1 /*
2     roxterm - VTE/GTK terminal emulator with tabs
3     Copyright (C) 2004-2015 Tony Houghton <h@realh.co.uk>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 
21 #include "defns.h"
22 
23 #include <stdarg.h>
24 #include <string.h>
25 
26 #include "menutree.h"
27 #include "shortcuts.h"
28 
29 static char const *menutree_labels[MENUTREE_NUM_IDS];
30 static gboolean filled_labels = FALSE;
31 
menutree_append_new_item_with_mnemonic(GtkMenuShell * shell,const char * label)32 static GtkWidget *menutree_append_new_item_with_mnemonic(
33         GtkMenuShell *shell, const char *label)
34 {
35     GtkWidget *item = gtk_menu_item_new_with_mnemonic(label);
36 
37     gtk_menu_shell_append(shell, item);
38     return item;
39 }
40 
strip_underscore(const char * in)41 static char *strip_underscore(const char *in)
42 {
43     char *out = g_new(char, strlen(in) + 1);
44     int n, m;
45 
46     for (n = 0, m = 0; in[n]; ++n)
47     {
48         if (in[n] != '_')
49             out[m++] = in[n];
50     }
51     out[m] = 0;
52     return out;
53 }
54 
55 /* Builds a menu; va_list is a series of pairs of labels and IDs, terminated
56  * by a NULL label; use "_" for a separator, and MENUTREE_NULL_ID for any item
57  * you don't want stored in item_widgets */
menutree_build_shell_ap(MenuTree * menu_tree,GtkMenuShell * shell,va_list ap)58 static void menutree_build_shell_ap(MenuTree *menu_tree, GtkMenuShell *shell,
59         va_list ap)
60 {
61     char *label;
62     MenuTreeID id;
63 
64     while ((label = va_arg(ap, char *)) != NULL)
65     {
66         GtkWidget *item = NULL;
67 
68         id = va_arg(ap, MenuTreeID);
69         if (!strcmp(label, "_"))
70         {
71             item = gtk_separator_menu_item_new();
72             gtk_menu_shell_append(shell, item);
73         }
74         else
75         {
76             if (!filled_labels &&
77                     id < MENUTREE_NUM_IDS && id != MENUTREE_NULL_ID)
78             {
79                 menutree_labels[id] = label;
80             }
81             if (menu_tree->disable_shortcuts)
82                 label = strip_underscore(label);
83             item = NULL;
84             switch (id)
85             {
86                 case MENUTREE_VIEW_SHOW_MENUBAR:
87                 case MENUTREE_VIEW_SHOW_TAB_BAR:
88                 case MENUTREE_VIEW_FULLSCREEN:
89                 case MENUTREE_VIEW_BORDERLESS:
90                     item = gtk_check_menu_item_new_with_mnemonic(label);
91                     gtk_menu_shell_append(shell, item);
92                     break;
93                 default:
94                     item = menutree_append_new_item_with_mnemonic(shell, label);
95                     break;
96             }
97             if (menu_tree->disable_shortcuts)
98                 g_free(label);
99         }
100         if (id != MENUTREE_NULL_ID)
101             menu_tree->item_widgets[id] = item;
102     }
103 }
104 
105 /* See above for description of variable args */
menutree_build_shell(MenuTree * menu_tree,GtkMenuShell * shell,...)106 static void menutree_build_shell(MenuTree *menu_tree, GtkMenuShell * shell, ...)
107 {
108     va_list ap;
109 
110     va_start(ap, shell);
111     menutree_build_shell_ap(menu_tree, shell, ap);
112     va_end(ap);
113 }
114 
115 #define URI_MENU_ITEMS \
116         _("_SSH to host"), MENUTREE_SSH_HOST, \
117         _("_Open link"), MENUTREE_OPEN_IN_BROWSER, \
118         _("Send e_mail"), MENUTREE_OPEN_IN_MAILER, \
119         _("_Open file/directory in filer"), MENUTREE_OPEN_IN_FILER, \
120         _("_Call"), MENUTREE_VOIP_CALL, \
121         _("_Copy address to clipboard"), MENUTREE_COPY_URI, \
122         _("_"), MENUTREE_URI_SEPARATOR
123 
124 #define MENUTREE_SEARCH_ITEM _("_Search"), MENUTREE_SEARCH,
125 #define TOP_LEVEL_MENU_ITEMS \
126         _("_File"), MENUTREE_FILE, \
127         _("_Edit"), MENUTREE_EDIT, \
128         _("_View"), MENUTREE_VIEW, \
129         MENUTREE_SEARCH_ITEM \
130         _("_Preferences"), MENUTREE_PREFERENCES, \
131         _("Ta_bs"), MENUTREE_TABS, \
132         _("_Help"), MENUTREE_HELP
133 
134 #define COPY_PASTE_MENU_ITEMS \
135         _("Select _All"), MENUTREE_EDIT_SELECT_ALL, \
136         _("_Copy"), MENUTREE_EDIT_COPY, \
137         _("_Paste"), MENUTREE_EDIT_PASTE, \
138         _("Cop_y & Paste"), MENUTREE_EDIT_COPY_AND_PASTE, \
139         "_", MENUTREE_NULL_ID
140 
141 #define RESET_MENU_ITEMS \
142         _("_Reset"), MENUTREE_EDIT_RESET, \
143         _("Reset And C_lear"), MENUTREE_EDIT_RESET_AND_CLEAR
144 
145 #define SHOW_MENU_BAR_ITEM \
146         _("Show Menu_bar"), MENUTREE_VIEW_SHOW_MENUBAR
147 
148 #define PREFS_ITEMS1 \
149         _("Select _Profile"), MENUTREE_PREFERENCES_SELECT_PROFILE, \
150         _("Select _Colour Scheme"), MENUTREE_PREFERENCES_SELECT_COLOUR_SCHEME, \
151         _("Select _Shortcuts Scheme"), MENUTREE_PREFERENCES_SELECT_SHORTCUTS, \
152         "_", MENUTREE_NULL_ID, \
153         _("_Edit Current Profile"), MENUTREE_PREFERENCES_EDIT_CURRENT_PROFILE, \
154         _("E_dit Current Colour Scheme"), \
155             MENUTREE_PREFERENCES_EDIT_CURRENT_COLOUR_SCHEME
156 
157 #define PREFS_ITEMS2 \
158         "_", MENUTREE_NULL_ID, \
159         _("Configuration _Manager"), MENUTREE_PREFERENCES_CONFIG_MANAGER, \
160         "_", MENUTREE_NULL_ID, \
161         NULL
162 
menutree_submenu_from_id(MenuTree * mtree,MenuTreeID id)163 GtkMenu *menutree_submenu_from_id(MenuTree *mtree, MenuTreeID id)
164 {
165     GtkWidget *item = menutree_get_widget_for_id(mtree, id);
166 
167     return item ?
168             GTK_MENU(gtk_menu_item_get_submenu(GTK_MENU_ITEM(item))) : NULL;
169 }
170 
get_accel_path(Options * shortcuts,const char * branch_name)171 static char *get_accel_path(Options *shortcuts, const char *branch_name)
172 {
173     return g_strjoin("/", ACCEL_PATH,
174             shortcuts_get_index_str(shortcuts), branch_name, NULL);
175 }
176 
177 static void
menutree_set_accel_path_for_submenu(MenuTree * mtree,MenuTreeID id,const char * menu_branch)178 menutree_set_accel_path_for_submenu(MenuTree *mtree, MenuTreeID id,
179         const char *menu_branch)
180 {
181     GtkMenu *menu = menutree_submenu_from_id(mtree, id);
182     char *accel_path;
183 
184     if (!menu)
185         return;
186     accel_path = get_accel_path(mtree->shortcuts, menu_branch);
187     gtk_menu_set_accel_path(menu, accel_path);
188     g_free(accel_path);
189     gtk_menu_set_accel_group(menu, mtree->accel_group);
190 }
191 
192 static void
menutree_set_accel_path_for_item(MenuTree * tree,MenuTreeID id,const char * path_leaf)193 menutree_set_accel_path_for_item(MenuTree * tree, MenuTreeID id,
194     const char *path_leaf)
195 {
196     GtkWidget *item = tree->item_widgets[id];
197     char *full_path;
198 
199     if (!item)
200         return;
201     full_path = get_accel_path(tree->shortcuts, path_leaf);
202     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), full_path);
203     g_free(full_path);
204 }
205 
206 static void
menutree_set_accel_path_for_tab(MenuTree * tree,int tab)207 menutree_set_accel_path_for_tab(MenuTree * tree, int tab)
208 {
209     char *leaf = NULL;
210     char *full_path = NULL;
211 
212     if (!tree->tabs || tab >= 10)
213         return;
214     if (!tree->disable_tab_shortcuts && tree->ntabs != 1)
215     {
216         leaf = g_strdup_printf("Tabs/Select_Tab_%d", tab);
217         full_path = get_accel_path(tree->shortcuts, leaf);
218     }
219     shortcuts_enable_signal_handler(FALSE);
220     gtk_menu_item_set_accel_path(
221             GTK_MENU_ITEM(g_list_nth_data(tree->tabs, tab)),
222             full_path);
223     g_free(full_path);
224     g_free(leaf);
225     shortcuts_enable_signal_handler(TRUE);
226 }
227 
menutree_apply_tab_shortcuts(MenuTree * mtree)228 static void menutree_apply_tab_shortcuts(MenuTree *mtree)
229 {
230     int n;
231 
232     for (n = 0; n < mtree->ntabs; ++n)
233         menutree_set_accel_path_for_tab(mtree, n);
234 }
235 
menutree_apply_shortcuts(MenuTree * tree,Options * shortcuts)236 void menutree_apply_shortcuts(MenuTree *tree, Options *shortcuts)
237 {
238     GtkMenu *submenu;
239     char *accel_path;
240 
241     tree->shortcuts = shortcuts;
242     shortcuts_enable_signal_handler(FALSE);
243     menutree_set_accel_path_for_submenu(tree, MENUTREE_FILE, "File");
244     menutree_set_accel_path_for_submenu(tree, MENUTREE_EDIT, "Edit");
245     menutree_set_accel_path_for_submenu(tree, MENUTREE_VIEW, "View");
246     menutree_set_accel_path_for_submenu(tree, MENUTREE_SEARCH, "Search");
247     menutree_set_accel_path_for_submenu(tree, MENUTREE_PREFERENCES,
248             "Preferences");
249     menutree_set_accel_path_for_submenu(tree, MENUTREE_HELP, "Help");
250 
251     submenu = GTK_MENU(tree->new_win_profiles_menu);
252     if (submenu)
253     {
254         accel_path = get_accel_path(tree->shortcuts,
255                 "File/New Window With Profile");
256         gtk_menu_set_accel_path(submenu, accel_path);
257         g_free(accel_path);
258         gtk_menu_set_accel_group(submenu, tree->accel_group);
259     }
260     submenu = GTK_MENU(tree->new_tab_profiles_menu);
261     if (submenu)
262     {
263         accel_path = get_accel_path(tree->shortcuts,
264                 "File/New Tab With Profile");
265         gtk_menu_set_accel_path(submenu, accel_path);
266         g_free(accel_path);
267         gtk_menu_set_accel_group(submenu, tree->accel_group);
268     }
269 
270     /* Tabs have shortcuts set dynamically so set paths
271      * for fixed items individually */
272     submenu = menutree_submenu_from_id(tree, MENUTREE_TABS);
273     if (submenu)
274         gtk_menu_set_accel_group(submenu, tree->accel_group);
275     menutree_set_accel_path_for_item(tree,
276             MENUTREE_FILE_NEW_WINDOW_WITH_PROFILE_HEADER,
277             "File/New Window With Profile/Profiles");
278     menutree_set_accel_path_for_item(tree,
279             MENUTREE_FILE_NEW_TAB_WITH_PROFILE_HEADER,
280             "File/New Tab With Profile/Profiles");
281     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_DETACH_TAB,
282             "Tabs/Detach Tab");
283     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_CLOSE_TAB,
284             "Tabs/Close Tab");
285     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_CLOSE_OTHER_TABS,
286             "Tabs/Close Other Tabs");
287     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_NAME_TAB,
288             "Tabs/Name Tab...");
289     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_NEXT_TAB,
290             "Tabs/Next Tab");
291     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_PREVIOUS_TAB,
292             "Tabs/Previous Tab");
293     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_MOVE_TAB_LEFT,
294             "Tabs/Move Tab Left");
295     menutree_set_accel_path_for_item(tree, MENUTREE_TABS_MOVE_TAB_RIGHT,
296             "Tabs/Move Tab Right");
297     menutree_set_accel_path_for_submenu(tree, MENUTREE_HELP, "Help");
298     menutree_apply_tab_shortcuts(tree);
299     shortcuts_enable_signal_handler(TRUE);
300 }
301 
302 /*
303 static void submenu_destroy_handler(GtkWidget *menu, gpointer handle)
304 {
305     g_debug("Destroying submenu %p", menu);
306 }
307 */
308 
309 /* Creates top-level menubar or popup menu with submenus */
menutree_build(MenuTree * menu_tree,Options * shortcuts,GType menu_type)310 static void menutree_build(MenuTree *menu_tree, Options *shortcuts,
311         GType menu_type)
312 {
313     GtkWidget *submenu;
314 
315     menu_tree->top_level = menu_type == GTK_TYPE_MENU_BAR ?
316         gtk_menu_bar_new() : gtk_menu_new();
317     if (menu_type == GTK_TYPE_MENU_BAR)
318     {
319         menutree_build_shell(menu_tree, GTK_MENU_SHELL(menu_tree->top_level),
320                 TOP_LEVEL_MENU_ITEMS, NULL);
321     }
322     else
323     {
324         menutree_build_shell(menu_tree, GTK_MENU_SHELL(menu_tree->top_level),
325             URI_MENU_ITEMS, TOP_LEVEL_MENU_ITEMS, NULL);
326     }
327 
328     submenu = gtk_menu_new();
329     /*
330     g_debug("Creating File submenu %p", submenu);
331     g_signal_connect(submenu, "destroy",
332             G_CALLBACK(submenu_destroy_handler), NULL);
333     */
334     menutree_build_shell(menu_tree, GTK_MENU_SHELL(submenu),
335         _("_New Window"), MENUTREE_FILE_NEW_WINDOW,
336         _("New _Tab"), MENUTREE_FILE_NEW_TAB,
337         _("New _Window With Profile"), MENUTREE_FILE_NEW_WINDOW_WITH_PROFILE,
338         _("New Tab With _Profile"), MENUTREE_FILE_NEW_TAB_WITH_PROFILE,
339         _("C_lose Tab"), MENUTREE_FILE_CLOSE_TAB,
340         _("_Close Window"), MENUTREE_FILE_CLOSE_WINDOW,
341         _("_Save Session..."), MENUTREE_FILE_SAVE_SESSION,
342         NULL);
343     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
344             [MENUTREE_FILE]), submenu);
345 
346     menu_tree->new_win_profiles_menu = gtk_menu_new();
347     menutree_build_shell(menu_tree,
348             GTK_MENU_SHELL(menu_tree->new_win_profiles_menu),
349             _("Profiles"), MENUTREE_FILE_NEW_WINDOW_WITH_PROFILE_HEADER,
350             "_", MENUTREE_NULL_ID,
351             NULL);
352     gtk_widget_set_sensitive(menutree_get_widget_for_id(menu_tree,
353                 MENUTREE_FILE_NEW_WINDOW_WITH_PROFILE_HEADER), FALSE);
354     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
355             [MENUTREE_FILE_NEW_WINDOW_WITH_PROFILE]),
356             menu_tree->new_win_profiles_menu);
357     menu_tree->new_tab_profiles_menu = gtk_menu_new();
358     menutree_build_shell(menu_tree,
359             GTK_MENU_SHELL(menu_tree->new_tab_profiles_menu),
360             _("Profiles"), MENUTREE_FILE_NEW_TAB_WITH_PROFILE_HEADER,
361             "_", MENUTREE_NULL_ID,
362             NULL);
363     gtk_widget_set_sensitive(menutree_get_widget_for_id(menu_tree,
364                 MENUTREE_FILE_NEW_TAB_WITH_PROFILE_HEADER), FALSE);
365     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
366             [MENUTREE_FILE_NEW_TAB_WITH_PROFILE]),
367             menu_tree->new_tab_profiles_menu);
368 
369     submenu = gtk_menu_new();
370     menutree_build_shell(menu_tree, GTK_MENU_SHELL(submenu),
371         COPY_PASTE_MENU_ITEMS,
372         _("_Set Window Title..."), MENUTREE_EDIT_SET_WINDOW_TITLE,
373         RESET_MENU_ITEMS,
374         "_", MENUTREE_NULL_ID,
375         _("Res_tart command"), MENUTREE_EDIT_RESPAWN,
376         NULL);
377     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
378             [MENUTREE_EDIT]), submenu);
379 
380     submenu = gtk_menu_new();
381     menutree_build_shell(menu_tree, GTK_MENU_SHELL(submenu),
382         SHOW_MENU_BAR_ITEM,
383         _("_Always Show Tab Bar"), MENUTREE_VIEW_SHOW_TAB_BAR,
384         _("_Full Screen"), MENUTREE_VIEW_FULLSCREEN,
385         _("_Borderless"), MENUTREE_VIEW_BORDERLESS,
386         "_", MENUTREE_NULL_ID,
387         _("Zoom _In"), MENUTREE_VIEW_ZOOM_IN,
388         _("Zoom _Out"), MENUTREE_VIEW_ZOOM_OUT,
389         _("_Normal Size"), MENUTREE_VIEW_ZOOM_NORM,
390         "_", MENUTREE_NULL_ID,
391         _("Scroll _Up One Line"), MENUTREE_VIEW_SCROLL_UP,
392         _("Scroll _Down One Line"), MENUTREE_VIEW_SCROLL_DOWN,
393         _("Scroll Up One _Page"), MENUTREE_VIEW_SCROLL_PAGE_UP,
394         _("Scroll Do_wn One Page"), MENUTREE_VIEW_SCROLL_PAGE_DOWN,
395         _("Scroll Up One _Half Page"), MENUTREE_VIEW_SCROLL_HALF_PAGE_UP,
396         _("Scroll Down One Hal_f Page"), MENUTREE_VIEW_SCROLL_HALF_PAGE_DOWN,
397         _("Scroll To _Top"), MENUTREE_VIEW_SCROLL_TO_TOP,
398         _("Scroll To _Bottom"), MENUTREE_VIEW_SCROLL_TO_BOTTOM,
399         NULL);
400     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
401             [MENUTREE_VIEW]), submenu);
402 
403     submenu = gtk_menu_new();
404     menutree_build_shell(menu_tree, GTK_MENU_SHELL(submenu),
405         _("_Find..."), MENUTREE_SEARCH_FIND,
406         _("Find _Next"), MENUTREE_SEARCH_FIND_NEXT,
407         _("Find _Previous"), MENUTREE_SEARCH_FIND_PREVIOUS,
408         NULL);
409     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
410             [MENUTREE_SEARCH]), submenu);
411 
412     submenu = gtk_menu_new();
413     menutree_build_shell(menu_tree, GTK_MENU_SHELL(submenu),
414         PREFS_ITEMS1,
415         _("Edi_t Current Shortcuts Scheme"),
416             MENUTREE_PREFERENCES_EDIT_CURRENT_SHORTCUTS_SCHEME,
417         PREFS_ITEMS2);
418     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
419             [MENUTREE_PREFERENCES]), submenu);
420 
421     submenu = gtk_menu_new();
422     menutree_build_shell(menu_tree, GTK_MENU_SHELL(submenu),
423         _("_Detach Tab"), MENUTREE_TABS_DETACH_TAB,
424         _("_Close Tab"), MENUTREE_TABS_CLOSE_TAB,
425         _("Close _Other Tabs"), MENUTREE_TABS_CLOSE_OTHER_TABS,
426         _("Na_me Tab..."), MENUTREE_TABS_NAME_TAB,
427         "_", MENUTREE_NULL_ID,
428         _("_Previous Tab"), MENUTREE_TABS_PREVIOUS_TAB,
429         _("_Next Tab"), MENUTREE_TABS_NEXT_TAB,
430         "_", MENUTREE_NULL_ID,
431         _("Move Tab _Left"), MENUTREE_TABS_MOVE_TAB_LEFT,
432         _("Move Tab _Right"), MENUTREE_TABS_MOVE_TAB_RIGHT,
433         "_", MENUTREE_NULL_ID, NULL);
434     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
435             [MENUTREE_TABS]), submenu);
436 
437     submenu = gtk_menu_new();
438     menutree_build_shell(menu_tree, GTK_MENU_SHELL(submenu),
439         _("Show _Manual"), MENUTREE_HELP_SHOW_MANUAL,
440         _("_About ROXTerm"), MENUTREE_HELP_ABOUT, NULL);
441     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tree->item_widgets
442             [MENUTREE_HELP]), submenu);
443     menutree_apply_shortcuts(menu_tree, shortcuts);
444 }
445 
menutree_build_short_popup(MenuTree * menu_tree,Options * shortcuts,GType menu_type)446 static void menutree_build_short_popup(MenuTree *menu_tree, Options *shortcuts,
447         GType menu_type)
448 {
449     (void) menu_type;
450     menu_tree->top_level = gtk_menu_new();
451     menutree_build_shell(menu_tree, GTK_MENU_SHELL(menu_tree->top_level),
452         URI_MENU_ITEMS,
453         COPY_PASTE_MENU_ITEMS,
454         RESET_MENU_ITEMS,
455         SHOW_MENU_BAR_ITEM,
456         NULL);
457     menutree_apply_shortcuts(menu_tree, shortcuts);
458 }
459 
menutree_destroy(MenuTree * tree)460 static void menutree_destroy(MenuTree * tree)
461 {
462     //g_debug("Destroying menu tree %p", tree);
463     if (tree->deleted_handler)
464         tree->deleted_handler(tree, tree->deleted_data);
465     if (tree->tabs)
466         g_list_free(tree->tabs);
467     g_free(tree);
468 }
469 
menutree_destroy_widget_handler(GObject * obj,MenuTree * tree)470 static void menutree_destroy_widget_handler(GObject * obj, MenuTree * tree)
471 {
472     (void) obj;
473 
474     //g_debug("Menu %p being destroyed", obj);
475     tree->top_level = NULL;
476     /* tree is no good any more so destroy it */
477     menutree_destroy(tree);
478 }
479 
menutree_new_common(Options * shortcuts,GtkAccelGroup * accel_group,GType menu_type,void (* builder)(MenuTree * menu_tree,Options * shortcuts,GType menu_type),gboolean disable_shortcuts,gboolean disable_tab_shortcuts,gpointer user_data)480 static MenuTree *menutree_new_common(Options *shortcuts,
481         GtkAccelGroup *accel_group,
482         GType menu_type,
483         void (*builder)(MenuTree *menu_tree, Options *shortcuts,
484                 GType menu_type),
485         gboolean disable_shortcuts,
486         gboolean disable_tab_shortcuts, gpointer user_data)
487 {
488     MenuTree *tree = g_new0(MenuTree, 1);
489     int n;
490 
491     for (n = 0; n < MENUTREE_NUM_IDS; ++n)
492     {
493         tree->item_widgets[n] = NULL;
494     }
495 
496     tree->user_data = user_data;
497     tree->accel_group = accel_group;
498     tree->disable_shortcuts = disable_shortcuts;
499     tree->disable_tab_shortcuts = disable_tab_shortcuts;
500 
501     builder(tree, shortcuts, menu_type);
502 
503     g_signal_connect(tree->top_level,
504             "destroy", G_CALLBACK(menutree_destroy_widget_handler), tree);
505 
506     gtk_widget_show_all(tree->top_level);
507     return tree;
508 }
509 
menutree_new(Options * shortcuts,GtkAccelGroup * accel_group,GType menu_type,gboolean disable_shortcuts,gboolean disable_tab_shortcuts,gpointer user_data)510 MenuTree *menutree_new(Options *shortcuts, GtkAccelGroup *accel_group,
511     GType menu_type, gboolean disable_shortcuts,
512     gboolean disable_tab_shortcuts, gpointer user_data)
513 {
514     MenuTree *tree = NULL;
515 
516     if (!filled_labels)
517     {
518         int n;
519 
520         for (n = 0; n < MENUTREE_NUM_IDS; ++n)
521             menutree_labels[n] = NULL;
522     }
523     tree = menutree_new_common(shortcuts, accel_group, menu_type,
524         menutree_build, disable_shortcuts, disable_tab_shortcuts, user_data);
525     /*
526     g_debug("Created menu %p of type %s", tree->top_level,
527             menu_type == GTK_TYPE_MENU_BAR ? "bar" : "popup");
528     */
529     filled_labels = TRUE;
530     return tree;
531 }
532 
menutree_new_short_popup(Options * shortcuts,GtkAccelGroup * accel_group,gboolean disable_shortcuts,gpointer user_data)533 MenuTree *menutree_new_short_popup(Options *shortcuts,
534         GtkAccelGroup *accel_group, gboolean disable_shortcuts,
535         gpointer user_data)
536 {
537     MenuTree *tree = menutree_new_common(shortcuts, accel_group, GTK_TYPE_MENU,
538         menutree_build_short_popup, disable_shortcuts, FALSE, user_data);
539     //g_debug("Created short popup menu %p", tree->top_level);
540     return tree;
541 }
542 
menutree_delete(MenuTree * tree)543 void menutree_delete(MenuTree * tree)
544 {
545     g_return_if_fail(tree != NULL);
546 
547     if (tree->top_level)
548     {
549         gtk_widget_destroy(tree->top_level);
550     }
551     else
552     {
553         menutree_destroy(tree);
554     }
555 }
556 
557 void
menutree_connect_destroyed(MenuTree * tree,GCallback callback,gpointer user_data)558 menutree_connect_destroyed(MenuTree * tree,
559     GCallback callback, gpointer user_data)
560 {
561     tree->deleted_handler = (void (*)(MenuTree *, gpointer)) callback;
562     tree->deleted_data = user_data;
563 }
564 
menutree_find_radio_group(GtkWidget * widget,gpointer data)565 void menutree_find_radio_group(GtkWidget *widget, gpointer data)
566 {
567     GSList **pgroup = data;
568 
569     if (g_type_is_a(G_OBJECT_TYPE(G_OBJECT(widget)), GTK_TYPE_RADIO_MENU_ITEM))
570         *pgroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(widget));
571 }
572 
menutree_get_submenu_at_id(MenuTree * tree,int id)573 inline static GtkMenuShell *menutree_get_submenu_at_id(MenuTree *tree, int id)
574 {
575     return GTK_MENU_SHELL(gtk_menu_item_get_submenu(
576                 GTK_MENU_ITEM(tree->item_widgets[id])));
577 }
578 
menutree_remove_tab_without_fixing_accels(MenuTree * tree,GtkWidget * menu_item)579 static void menutree_remove_tab_without_fixing_accels(MenuTree *tree,
580         GtkWidget *menu_item)
581 {
582     gtk_widget_destroy(menu_item);
583     tree->tabs = g_list_remove(tree->tabs, menu_item);
584     --tree->ntabs;
585 }
586 
menutree_remove_tab(MenuTree * tree,GtkWidget * menu_item)587 void menutree_remove_tab(MenuTree *tree, GtkWidget *menu_item)
588 {
589     menutree_remove_tab_without_fixing_accels(tree, menu_item);
590     menutree_apply_tab_shortcuts(tree);
591 }
592 
menutree_tab_menu_item_new(GtkMenuShell * menu,const char * title)593 static GtkWidget *menutree_tab_menu_item_new(GtkMenuShell *menu,
594         const char *title)
595 {
596     /* GtkCheckMenuItem is actually easier to manage than GtkRadioMenuItem due
597      * to difficulty of keeping track of group when deleting radio menu items
598      */
599     GtkWidget *menu_item;
600     GSList *group = NULL;
601 
602     gtk_container_foreach(GTK_CONTAINER(menu), menutree_find_radio_group,
603             &group);
604     menu_item = gtk_radio_menu_item_new_with_label(group, title);
605     gtk_widget_show(menu_item);
606     return menu_item;
607 }
608 
menutree_add_tab_at_position(MenuTree * tree,const char * title,int position)609 GtkWidget *menutree_add_tab_at_position(MenuTree * tree, const char *title,
610         int position)
611 {
612     GtkMenuShell *submenu = menutree_get_submenu_at_id(tree, MENUTREE_TABS);
613     GtkWidget *menu_item = menutree_tab_menu_item_new(submenu, title);
614 
615     if (position == -1)
616     {
617         gtk_menu_shell_append(submenu, menu_item);
618         tree->tabs = g_list_append(tree->tabs, menu_item);
619         position = tree->ntabs;
620     }
621     else
622     {
623         gtk_menu_shell_insert(submenu, menu_item,
624                 position + MENUTREE_TABS_FIRST_DYNAMIC);
625         tree->tabs = g_list_insert(tree->tabs, menu_item, position);
626     }
627     ++tree->ntabs;
628     menutree_apply_tab_shortcuts(tree);
629     return menu_item;
630 }
631 
menu_item_change_label(GtkMenuItem * item,const char * label)632 static void menu_item_change_label(GtkMenuItem *item, const char *label)
633 {
634     GtkWidget *inner_item = gtk_bin_get_child(GTK_BIN(item));
635 
636     if (GTK_IS_LABEL(inner_item))
637     {
638         gtk_label_set_text_with_mnemonic(GTK_LABEL(inner_item), label);
639     }
640     else if (GTK_IS_CONTAINER(inner_item))
641     {
642         GList *link;
643 
644         for (link = gtk_container_get_children(GTK_CONTAINER(inner_item));
645                 link; link = g_list_next(link))
646         {
647             GtkWidget *child = link->data;
648 
649             if (GTK_IS_LABEL(child))
650             {
651                 gtk_label_set_text_with_mnemonic(GTK_LABEL(child), label);
652                 return;
653             }
654         }
655     }
656 }
657 
menutree_change_label(MenuTree * tree,MenuTreeID id,const char * label)658 inline static void menutree_change_label(MenuTree *tree, MenuTreeID id,
659         const char *label)
660 {
661     menu_item_change_label(GTK_MENU_ITEM(menutree_get_widget_for_id(tree, id)),
662             label);
663 }
664 
menutree_change_tab_title(MenuTree * tree,GtkWidget * menu_item,const char * title)665 GtkWidget *menutree_change_tab_title(MenuTree * tree,
666     GtkWidget * menu_item, const char *title)
667 {
668     (void) tree;
669     menu_item_change_label(GTK_MENU_ITEM(menu_item), title);
670     return menu_item;
671 }
672 
menutree_change_move_tab_labels(MenuTree * tree)673 void menutree_change_move_tab_labels(MenuTree *tree)
674 {
675     menutree_change_label(tree, MENUTREE_TABS_MOVE_TAB_LEFT,
676             _("Move Tab _Up"));
677     menutree_change_label(tree, MENUTREE_TABS_MOVE_TAB_RIGHT,
678             _("Move Tab _Down"));
679 }
680 
menutree_set_toggle(MenuTree * tree,MenuTreeID id,gboolean active)681 static void menutree_set_toggle(MenuTree *tree, MenuTreeID id, gboolean active)
682 {
683     GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM
684         (menutree_get_widget_for_id(tree, id));
685 
686     if (active != gtk_check_menu_item_get_active(item))
687     {
688         gtk_check_menu_item_set_active(item, active);
689     }
690 }
691 
menutree_set_show_menu_bar_active(MenuTree * tree,gboolean active)692 void menutree_set_show_menu_bar_active(MenuTree *tree, gboolean active)
693 {
694     menutree_set_toggle(tree, MENUTREE_VIEW_SHOW_MENUBAR, active);
695 }
696 
menutree_set_show_tab_bar_active(MenuTree * tree,gboolean active)697 void menutree_set_show_tab_bar_active(MenuTree *tree, gboolean active)
698 {
699     menutree_set_toggle(tree, MENUTREE_VIEW_SHOW_TAB_BAR, active);
700 }
701 
menutree_set_fullscreen_active(MenuTree * tree,gboolean active)702 void menutree_set_fullscreen_active(MenuTree *tree, gboolean active)
703 {
704     menutree_set_toggle(tree, MENUTREE_VIEW_FULLSCREEN, active);
705 }
706 
menutree_set_borderless_active(MenuTree * tree,gboolean active)707 void menutree_set_borderless_active(MenuTree *tree, gboolean active)
708 {
709     menutree_set_toggle(tree, MENUTREE_VIEW_BORDERLESS, active);
710 }
711 
712 extern  void multi_win_new_tab_action(gpointer win);
713 
menutree_named_signal_connect_data(MenuTree * tree,MenuTreeID id,const char * signame,GCallback handler,gpointer user_data,GConnectFlags flags)714 gulong menutree_named_signal_connect_data(MenuTree * tree, MenuTreeID id,
715         const char *signame, GCallback handler, gpointer user_data,
716         GConnectFlags flags)
717 {
718     GtkWidget *menu_item;
719 
720     g_return_val_if_fail(tree, 0);
721     menu_item = menutree_get_widget_for_id(tree, id);
722     if (!menu_item)
723         return 0;
724     return g_signal_connect_data(menu_item, signame, handler, user_data,
725         NULL, flags);
726 }
727 
menutree_set_show_item(MenuTree * tree,MenuTreeID id,gboolean show)728 void menutree_set_show_item(MenuTree * tree, MenuTreeID id, gboolean show)
729 {
730     GtkWidget *item = menutree_get_widget_for_id(tree, id);
731 
732     if (show)
733         gtk_widget_show(item);
734     else
735         gtk_widget_hide(item);
736 }
737 
menutree_disable_shortcuts(MenuTree * tree,gboolean disable)738 void menutree_disable_shortcuts(MenuTree *tree, gboolean disable)
739 {
740     int n;
741 
742     if (tree->disable_shortcuts == disable)
743         return;
744     tree->disable_shortcuts = disable;
745     for (n = 0; n < MENUTREE_NUM_IDS; ++n)
746     {
747         if (tree->item_widgets[n] && menutree_labels[n])
748         {
749             GtkWidget *child;
750             child = gtk_bin_get_child(GTK_BIN(tree->item_widgets[n]));
751             if (child && GTK_IS_LABEL(child))
752             {
753                 if (disable)
754                 {
755                     char *stripped = strip_underscore(menutree_labels[n]);
756 
757                     gtk_label_set_text(GTK_LABEL(child), stripped);
758                     g_free(stripped);
759                 }
760                 else
761                 {
762                     gtk_label_set_text_with_mnemonic(GTK_LABEL(child),
763                             menutree_labels[n]);
764                 }
765             }
766         }
767     }
768 }
769 
menutree_disable_tab_shortcuts(MenuTree * tree,gboolean disable)770 void menutree_disable_tab_shortcuts(MenuTree *tree, gboolean disable)
771 {
772     if (tree->disable_tab_shortcuts == disable)
773         return;
774     tree->disable_tab_shortcuts = disable;
775     menutree_apply_tab_shortcuts(tree);
776 }
777 
778 /* vi:set sw=4 ts=4 et cindent cino= */
779