From 1133bc243d2b8b2f770e8a7b0bb887821441d92b Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 7 May 2007 20:04:03 +0000 Subject: [PATCH] this doesn't work yet, it's half done. don't try run it... --- openbox/menu.c | 31 ++++++++--- openbox/menu.h | 10 +++- openbox/menuframe.c | 132 +++++++++++++++++++++++++++++++++++++++++--- openbox/menuframe.h | 12 +++- render/theme.c | 7 ++- render/theme.h | 3 +- 6 files changed, 171 insertions(+), 24 deletions(-) diff --git a/openbox/menu.c b/openbox/menu.c index 741e27c1..d342b385 100644 --- a/openbox/menu.c +++ b/openbox/menu.c @@ -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; diff --git a/openbox/menu.h b/openbox/menu.h index 64cbbd6a..7ee3697d 100644 --- a/openbox/menu.h +++ b/openbox/menu.h @@ -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 diff --git a/openbox/menuframe.c b/openbox/menuframe.c index f87c3c0b..a83cba06 100644 --- a/openbox/menuframe.c +++ b/openbox/menuframe.c @@ -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; diff --git a/openbox/menuframe.h b/openbox/menuframe.h index 15bae667..0ce96158 100644 --- a/openbox/menuframe.h +++ b/openbox/menuframe.h @@ -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); diff --git a/render/theme.c b/render/theme.c index 45896c0d..84561ef1 100644 --- a/render/theme.c +++ b/render/theme.c @@ -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; diff --git a/render/theme.h b/render/theme.h index 44581ddb..d96130d1 100644 --- a/render/theme.h +++ b/render/theme.h @@ -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; -- 2.34.1