this doesn't work yet, it's half done. don't try run it...
authorDana Jansens <danakj@orodu.net>
Mon, 7 May 2007 20:04:03 +0000 (20:04 +0000)
committerDana Jansens <danakj@orodu.net>
Mon, 7 May 2007 20:04:03 +0000 (20:04 +0000)
openbox/menu.c
openbox/menu.h
openbox/menuframe.c
openbox/menuframe.h
render/theme.c
render/theme.h

index 741e27c12be7a5fc79375d96c8aa64ee5d9f2a25..d342b385a4e9f84aad6406773bb29a0c788d5e4e 100644 (file)
@@ -314,7 +314,7 @@ parse_menu_fail:
 }
 
 ObMenu* menu_new(const gchar *name, const gchar *title,
-                 gboolean allow_shortcut, gpointer data)
+                 gboolean allow_shortcut_selection, gpointer data)
 {
     ObMenu *self;
 
@@ -322,8 +322,8 @@ ObMenu* menu_new(const gchar *name, const gchar *title,
     self->name = g_strdup(name);
     self->data = data;
 
-    self->shortcut = parse_shortcut(title, allow_shortcut, &self->title,
-                                    &self->shortcut_position);
+    self->shortcut = parse_shortcut(title, allow_shortcut_selection,
+                                    &self->title, &self->shortcut_position);
 
     g_hash_table_replace(menu_hash, self->name, self);
 
@@ -378,7 +378,7 @@ void menu_show(gchar *name, gint x, gint y, gint button, ObClient *client)
 
     menu_frame_hide_all();
 
-    frame = menu_frame_new(self, client);
+    frame = menu_frame_new(self, 0, client);
     if (!menu_frame_show_topmenu(frame, x, y, button))
         menu_frame_free(frame);
     else if (frame->entries) {
@@ -396,6 +396,7 @@ static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id)
     g_assert(menu);
 
     self = g_new0(ObMenuEntry, 1);
+    self->ref = 1;
     self->type = type;
     self->menu = menu;
     self->id = id;
@@ -412,9 +413,14 @@ static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id)
     return self;
 }
 
-void menu_entry_free(ObMenuEntry *self)
+void menu_entry_ref(ObMenuEntry *self)
 {
-    if (self) {
+    ++self->ref;
+}
+
+void menu_entry_unref(ObMenuEntry *self)
+{
+    if (self && --self->ref == 0) {
         switch (self->type) {
         case OB_MENU_ENTRY_TYPE_NORMAL:
             g_free(self->data.normal.label);
@@ -452,7 +458,7 @@ void menu_clear_entries(ObMenu *self)
 #endif
 
     while (self->entries) {
-        menu_entry_free(self->entries->data);
+        menu_entry_unref(self->entries->data);
         self->entries = g_list_delete_link(self->entries, self->entries);
     }
 }
@@ -460,7 +466,7 @@ void menu_clear_entries(ObMenu *self)
 void menu_entry_remove(ObMenuEntry *self)
 {
     self->menu->entries = g_list_remove(self->menu->entries, self);
-    menu_entry_free(self);
+    menu_entry_unref(self);
 }
 
 ObMenuEntry* menu_add_normal(ObMenu *self, gint id, const gchar *label,
@@ -477,6 +483,15 @@ ObMenuEntry* menu_add_normal(ObMenu *self, gint id, const gchar *label,
     return e;
 }
 
+ObMenuEntry* menu_get_more(ObMenu *self, guint show_from)
+{
+    ObMenuEntry *e;
+    e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, -1);
+    e->data.submenu.name = g_strdup(self->name); /* points to itself */
+    e->data.submenu.show_from = show_from;
+    return e;
+}
+
 ObMenuEntry* menu_add_submenu(ObMenu *self, gint id, const gchar *submenu)
 {
     ObMenuEntry *e;
index 64cbbd6a29c146dfe40dcfab3837c250b84bd58e..7ee3697dc53d3a11b39ec0b3dcd299321b62d1f3 100644 (file)
@@ -120,6 +120,7 @@ struct _ObNormalMenuEntry {
 struct _ObSubmenuMenuEntry {
     gchar *name;
     ObMenu *submenu;
+    guint show_from;
 };
 
 struct _ObSeparatorMenuEntry {
@@ -128,6 +129,8 @@ struct _ObSeparatorMenuEntry {
 
 struct _ObMenuEntry
 {
+    guint ref;
+
     ObMenuEntryType type;
     ObMenu *menu;
 
@@ -143,10 +146,13 @@ struct _ObMenuEntry
 void menu_startup(gboolean reconfig);
 void menu_shutdown(gboolean reconfig);
 
+void menu_entry_ref(ObMenuEntry *self);
+void menu_entry_unref(ObMenuEntry *self);
+
 /*! @param allow_shortcut this should be false when the label is coming from
            outside data like window or desktop titles */
 ObMenu* menu_new(const gchar *name, const gchar *title,
-                 gboolean allow_shortcut, gpointer data);
+                 gboolean allow_shortcut_selection, gpointer data);
 void menu_free(ObMenu *menu);
 
 /* Repopulate a pipe-menu by running its command */
@@ -181,4 +187,6 @@ ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id);
 /* fills in the submenus, for use when a menu is being shown */
 void menu_find_submenus(ObMenu *self);
 
+ObMenuEntry* menu_get_more(ObMenu *menu, guint show_from);
+
 #endif
index f87c3c0bb6da15404faa6dcf705207437d09218e..a83cba066bbf2360281b2aa8da727e92cf9a1317 100644 (file)
@@ -69,7 +69,7 @@ void menu_frame_shutdown(gboolean reconfig)
     g_hash_table_destroy(menu_frame_map);
 }
 
-ObMenuFrame* menu_frame_new(ObMenu *menu, ObClient *client)
+ObMenuFrame* menu_frame_new(ObMenu *menu, guint show_from, ObClient *client)
 {
     ObMenuFrame *self;
     XSetWindowAttributes attr;
@@ -80,14 +80,21 @@ ObMenuFrame* menu_frame_new(ObMenu *menu, ObClient *client)
     self->selected = NULL;
     self->client = client;
     self->direction_right = TRUE;
+    self->show_from = show_from;
 
     attr.event_mask = FRAME_EVENTMASK;
     self->window = createWindow(RootWindow(ob_display, ob_screen),
                                 CWEventMask, &attr);
 
+    XSetWindowBorderWidth(ob_display, self->window, ob_rr_theme->mbwidth);
+    XSetWindowBorder(ob_display, self->window,
+                     RrColorPixel(ob_rr_theme->menu_b_color));
+
     self->a_title = RrAppearanceCopy(ob_rr_theme->a_menu_title);
     self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu);
 
+    self->item_h = ob_rr_theme->menu_font_height + 2*PADDING;
+
     stacking_add(MENU_AS_WINDOW(self));
 
     return self;
@@ -122,6 +129,8 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
     self->entry = entry;
     self->frame = frame;
 
+    menu_entry_ref(entry);
+
     attr.event_mask = ENTRY_EVENTMASK;
     self->window = createWindow(self->frame->window, CWEventMask, &attr);
     self->text = createWindow(self->window, 0, NULL);
@@ -176,6 +185,8 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
 static void menu_entry_frame_free(ObMenuEntryFrame *self)
 {
     if (self) {
+        menu_entry_unref(self->entry);
+
         XDestroyWindow(ob_display, self->text);
         XDestroyWindow(ob_display, self->window);
         g_hash_table_remove(menu_frame_map, &self->text);
@@ -562,6 +573,50 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
     XFlush(ob_display);
 }
 
+/*! this code is taken from the menu_frame_render. if that changes, this won't
+  work.. */
+static gint menu_entry_frame_get_height(ObMenuEntryFrame *self,
+                                        gboolean first_entry,
+                                        gboolean last_entry)
+{
+    ObMenuEntryType t;
+    gint h = 0;
+
+    /* if the first entry is a labeled separator, then make its border
+       overlap with the menu's outside border */
+    if (first_entry)
+        h -= ob_rr_theme->mbwidth;
+    /* if the last entry is a labeled separator, then make its border
+       overlap with the menu's outside border */
+    if (last_entry)
+        h -= ob_rr_theme->mbwidth;
+
+    h += 2*PADDING;
+
+    if (self)
+        t = self->entry->type;
+    else
+        /* this is the More... entry, it's NORMAL type */
+        t = OB_MENU_ENTRY_TYPE_NORMAL;
+
+    switch (self->entry->type) {
+    case OB_MENU_ENTRY_TYPE_NORMAL:
+    case OB_MENU_ENTRY_TYPE_SUBMENU:
+        h += self->frame->item_h;
+        break;
+    case OB_MENU_ENTRY_TYPE_SEPARATOR:
+        if (self->entry->data.separator.label != NULL) {
+            h += ob_rr_theme->menu_title_height +
+                (ob_rr_theme->mbwidth - PADDING) * 2;
+        } else {
+            h += SEPARATOR_HEIGHT;
+        }
+        break;
+    }
+
+    return h;
+}
+
 static void menu_frame_render(ObMenuFrame *self)
 {
     gint w = 0, h = 0;
@@ -571,10 +626,6 @@ static void menu_frame_render(ObMenuFrame *self)
     ObMenu *sub;
     ObMenuEntryFrame *e;
 
-    XSetWindowBorderWidth(ob_display, self->window, ob_rr_theme->mbwidth);
-    XSetWindowBorder(ob_display, self->window,
-                     RrColorPixel(ob_rr_theme->menu_b_color));
-
     /* find text dimensions */
 
     STRUT_SET(self->item_margin, 0, 0, 0, 0);
@@ -584,10 +635,10 @@ static void menu_frame_render(ObMenuFrame *self)
         gint l, t, r, b;
 
         e->a_text_normal->texture[0].data.text.string = "";
-        RrMinSize(e->a_text_normal, &tw, &th);
+        tw = RrMinWidth(e->a_text_normal);
         tw += 2*PADDING;
-        th += 2*PADDING;
-        self->item_h = th;
+
+        th = self->item_h;
 
         RrMargins(e->a_normal, &l, &t, &r, &b);
         STRUT_SET(self->item_margin,
@@ -737,12 +788,19 @@ static void menu_frame_render(ObMenuFrame *self)
 static void menu_frame_update(ObMenuFrame *self)
 {
     GList *mit, *fit;
+    Rect *a;
+    gint h;
 
     menu_pipe_execute(self->menu);
     menu_find_submenus(self->menu);
 
     self->selected = NULL;
 
+    /* start at show_from */
+    mit = g_list_nth(self->menu->entries, self->show_from);
+
+    /* go through the menu's and frame's entries and connect the frame entries
+       to the menu entries */
     for (mit = self->menu->entries, fit = self->entries; mit && fit;
          mit = g_list_next(mit), fit = g_list_next(fit))
     {
@@ -750,12 +808,15 @@ static void menu_frame_update(ObMenuFrame *self)
         f->entry = mit->data;
     }
 
+    /* if there are more menu entries than in the frame, add them */
     while (mit) {
         ObMenuEntryFrame *e = menu_entry_frame_new(mit->data, self);
         self->entries = g_list_append(self->entries, e);
         mit = g_list_next(mit);
     }
-    
+
+    /* if there are more frame entries than menu entries then get rid of
+       them */
     while (fit) {
         GList *n = g_list_next(fit);
         menu_entry_frame_free(fit->data);
@@ -764,6 +825,58 @@ static void menu_frame_update(ObMenuFrame *self)
     }
 
     menu_frame_render(self);
+
+    /* make the menu fit on the screen. at most we call render twice, at least
+       not like n times or sometime */
+
+    a = screen_physical_area_monitor(self->monitor);
+    h = self->area.height;
+
+    if (h > a->height) {
+        GList *flast, *tmp;
+        gboolean last_entry = TRUE;
+
+        /* take the height of our More... entry into account */
+        h += menu_entry_frame_get_height(NULL, FALSE, TRUE);
+
+        /* start at the end of the entries */
+        flast = g_list_last(self->entries);
+
+        /* pull out all the entries from the frame that don't
+           fit on the screen, leaving at least 1 though */
+        while (h > a->height && g_list_previous(flast) != NULL) {
+            /* update the height, without this entry */
+            h -= menu_entry_frame_get_height(flast->data, FALSE, last_entry);
+
+            /* destroy the entry we're not displaying */
+            tmp = flast;
+            flast = g_list_previous(flast);
+            menu_entry_frame_free(tmp->data);
+            self->entries = g_list_delete_link(self->entries, tmp);
+
+            /* only the first one that we see is the last entry in the menu */
+            last_entry = FALSE;
+        };
+
+        {
+            ObMenuEntry *more_entry;
+            ObMenuEntryFrame *more_frame;
+            /* make the More... menu entry frame which will display in this
+               frame. */
+            more_entry = menu_get_more(self->menu,
+                                       self->show_from +
+                                       g_list_length(self->entries));
+            more_frame = menu_entry_frame_new(more_entry, self);
+            /* make it get deleted when the menu frame goes away */
+            menu_entry_unref(more_entry);
+                                       
+            /* add our More... entry to the frame */
+            self->entries = g_list_append(self->entries, more_frame);
+        }
+
+        /* render again */
+        menu_frame_render(self);
+    }
 }
 
 static gboolean menu_frame_is_visible(ObMenuFrame *self)
@@ -1021,6 +1134,7 @@ void menu_entry_frame_show_submenu(ObMenuEntryFrame *self)
     if (!self->entry->data.submenu.submenu) return;
 
     f = menu_frame_new(self->entry->data.submenu.submenu,
+                       self->entry->data.submenu.show_from,
                        self->frame->client);
     /* pass our direction on to our child */
     f->direction_right = self->frame->direction_right;
index 15bae6675cb413e0cbbbebb465fa49c259d4ec6e..0ce96158ae84fef0532c2cbf1f23374f232f4137 100644 (file)
@@ -54,6 +54,9 @@ struct _ObMenuFrame
     GList *entries;
     ObMenuEntryFrame *selected;
 
+    /* show entries from the menu starting at this index */
+    guint show_from;
+
     /* If the submenus are being drawn to the right or the left */
     gboolean direction_right;
 
@@ -61,10 +64,10 @@ struct _ObMenuFrame
     Rect area;
     Strut item_margin;
     gint inner_w; /* inside the borders */
-    gint title_h;  /* height of all title items */
     gint item_h;  /* height of all normal items */
     gint text_x;  /* offset at which the text appears in the items */
     gint text_w;  /* width of the text area in the items */
+    gint text_h;  /* height of the items */
 
     gint monitor; /* monitor on which to show the menu in xinerama */
 
@@ -74,6 +77,9 @@ struct _ObMenuFrame
 
 struct _ObMenuEntryFrame
 {
+    /* if this is true then it doesn't have an entry to point to */
+    gboolean more;
+
     struct _ObMenuEntry *entry;
     ObMenuFrame *frame;
 
@@ -109,7 +115,9 @@ extern GHashTable *menu_frame_map;
 void menu_frame_startup(gboolean reconfig);
 void menu_frame_shutdown(gboolean reconfig);
 
-ObMenuFrame* menu_frame_new(struct _ObMenu *menu, struct _ObClient *client);
+ObMenuFrame* menu_frame_new(struct _ObMenu *menu,
+                            guint show_from,
+                            struct _ObClient *client);
 void menu_frame_free(ObMenuFrame *self);
 
 void menu_frame_move(ObMenuFrame *self, gint x, gint y);
index 45896c0d611356b8018025bc86d0df38d9377fd2..84561ef11145eac5d212da052bfc7678e6001ee7 100644 (file)
@@ -1106,9 +1106,10 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name,
            MAX(theme->padding * 2, ut + ub));
         */
         theme->title_height = theme->label_height + theme->paddingy * 2;
-        /* this should match the above title_height given the same font size
-           for both. */
-        theme->menu_title_height = theme->menu_title_font_height +
+
+        RrMargins(theme->a_menu_title, &ul, &ut, &ur, &ub);
+        theme->menu_title_label_height = theme->menu_title_font_height+ut+ub;
+        theme->menu_title_height = theme->menu_title_label_height +
             theme->paddingy * 2;
     }
     theme->button_size = theme->label_height - 2;
index 44581ddbc1e13430f7a5fb91feb93715add88798..d96130d1edc717bcdfe4d5b0db954c88f4f6c120 100644 (file)
@@ -51,9 +51,10 @@ struct _RrTheme {
     gint menu_font_height;
     gint label_height;
     gint title_height;
-    gint menu_title_height;
     gint button_size;
     gint grip_width;
+    gint menu_title_label_height;
+    gint menu_title_height;
 
     /* style settings - colors */
     RrColor *menu_b_color;