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