1 /*
2 * Copyright 2019 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf 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, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <gtk/gtk.h>
20 #include <stdbool.h>
21 #include <string.h>
22
23 #include "utils/utils.h"
24 #include "utils/log.h"
25 #include "utils/messages.h"
26 #include "utils/nsurl.h"
27 #include "utils/nsoption.h"
28 #include "netsurf/browser_window.h"
29 #include "desktop/browser_history.h"
30 #include "desktop/hotlist.h"
31
32 #include "gtk/compat.h"
33 #include "gtk/toolbar_items.h"
34 #include "gtk/menu.h"
35 #include "gtk/local_history.h"
36 #include "gtk/gui.h"
37 #include "gtk/download.h"
38 #include "gtk/window.h"
39 #include "gtk/warn.h"
40 #include "gtk/tabs.h"
41 #include "gtk/resources.h"
42 #include "gtk/scaffolding.h"
43
44
45 /**
46 * menu entry context
47 */
48 struct nsgtk_menu {
49 GtkWidget *main; /* main menu entry */
50 GtkWidget *burger; /* right click menu */
51 GtkWidget *popup; /* popup menu entry */
52 /**
53 * menu item handler
54 */
55 gboolean (*mhandler)(GtkMenuItem *widget, gpointer data);
56 const char *iconname; /* name of the icon to use */
57 bool sensitivity; /* menu item is sensitive */
58 };
59
60 /**
61 * Core scaffolding structure.
62 */
63 struct nsgtk_scaffolding {
64 /** global linked list of scaffolding for gui interface adjustments */
65 struct nsgtk_scaffolding *next, *prev;
66
67 /** currently active gui browsing context */
68 struct gui_window *top_level;
69
70 /** Builder object scaffold was created from */
71 GtkBuilder *builder;
72
73 /** scaffold container window */
74 GtkWindow *window;
75
76 /** tab widget holding displayed pages */
77 GtkNotebook *notebook;
78
79 /** handler id for tabs remove callback */
80 gulong tabs_remove_handler_id;
81
82 /** menu bar hierarchy */
83 struct nsgtk_bar_submenu *menu_bar;
84
85 /** burger menu hierarchy */
86 struct nsgtk_burger_menu *burger_menu;
87
88 /** right click popup menu hierarchy */
89 struct nsgtk_popup_menu *popup_menu;
90
91 /** link popup menu */
92 struct nsgtk_link_menu *link_menu;
93
94 /** menu entries widgets for sensitivity adjustment */
95 struct nsgtk_menu menus[PLACEHOLDER_BUTTON];
96 };
97
98 /**
99 * current scaffold for model dialogue use
100 */
101 static struct nsgtk_scaffolding *scaf_current;
102
103 /**
104 * global list for interface changes
105 */
106 static struct nsgtk_scaffolding *scaf_list = NULL;
107
108 /**
109 * holds the context data for what's under the pointer, when the
110 * contextual menu is opened.
111 */
112 static struct browser_window_features current_menu_features;
113
114
115 /**
116 * Helper to hide popup menu entries by grouping.
117 *
118 * \param menu The popup menu to modify.
119 * \param nav flag to indicate if navigation entries should be hidden.
120 * \param cnp flag to indicate if cut and paste entries should be hidden.
121 * \param custom flag to indicate if menu customisation is hidden.
122 */
123 static void
popup_menu_hide(struct nsgtk_popup_menu * menu,bool nav,bool cnp)124 popup_menu_hide(struct nsgtk_popup_menu *menu, bool nav, bool cnp)
125 {
126 if (nav) {
127 gtk_widget_hide(GTK_WIDGET(menu->back_menuitem));
128 gtk_widget_hide(GTK_WIDGET(menu->forward_menuitem));
129 gtk_widget_hide(GTK_WIDGET(menu->stop_menuitem));
130 gtk_widget_hide(GTK_WIDGET(menu->reload_menuitem));
131
132 gtk_widget_hide(menu->first_separator);
133 }
134
135 if (cnp) {
136 gtk_widget_hide(GTK_WIDGET(menu->cut_menuitem));
137 gtk_widget_hide(GTK_WIDGET(menu->copy_menuitem));
138 gtk_widget_hide(GTK_WIDGET(menu->paste_menuitem));
139
140 gtk_widget_hide(menu->second_separator);
141 }
142
143
144 }
145
146
147 /**
148 * Helper to show popup menu entries by grouping.
149 *
150 * \param menu The popup menu to modify.
151 * \param nav flag to indicate if navigation entries should be visible.
152 * \param cnp flag to indicate if cut and paste entries should be visible.
153 * \param custom flag to indicate if menu customisation is visible.
154 */
155 static void
popup_menu_show(struct nsgtk_popup_menu * menu,bool nav,bool cnp)156 popup_menu_show(struct nsgtk_popup_menu *menu, bool nav, bool cnp)
157 {
158 if (nav) {
159 gtk_widget_show(GTK_WIDGET(menu->back_menuitem));
160 gtk_widget_show(GTK_WIDGET(menu->forward_menuitem));
161 gtk_widget_show(GTK_WIDGET(menu->stop_menuitem));
162 gtk_widget_show(GTK_WIDGET(menu->reload_menuitem));
163
164 gtk_widget_show(menu->first_separator);
165 }
166
167 if (cnp) {
168 gtk_widget_show(GTK_WIDGET(menu->cut_menuitem));
169 gtk_widget_show(GTK_WIDGET(menu->copy_menuitem));
170 gtk_widget_show(GTK_WIDGET(menu->paste_menuitem));
171
172 gtk_widget_show(menu->second_separator);
173 }
174
175 }
176
177
178 /**
179 * resource cleanup function for window destruction.
180 *
181 * gtk event called when window is being destroyed. Need to free any
182 * resources associated with this scaffold,
183 *
184 * \param widget the widget being destroyed
185 * \param data The context pointer passed when the connection was made.
186 */
scaffolding_window_destroy(GtkWidget * widget,gpointer data)187 static void scaffolding_window_destroy(GtkWidget *widget, gpointer data)
188 {
189 struct nsgtk_scaffolding *gs = data;
190
191 NSLOG(netsurf, INFO, "scaffold:%p", gs);
192
193 nsgtk_local_history_hide();
194
195 if (gs->prev != NULL) {
196 gs->prev->next = gs->next;
197 } else {
198 scaf_list = gs->next;
199 }
200 if (gs->next != NULL) {
201 gs->next->prev = gs->prev;
202 }
203
204 NSLOG(netsurf, INFO, "scaffold list head: %p", scaf_list);
205
206 /* ensure menu resources are freed */
207 nsgtk_menu_bar_destroy(gs->menu_bar);
208 nsgtk_burger_menu_destroy(gs->burger_menu);
209 nsgtk_popup_menu_destroy(gs->popup_menu);
210 nsgtk_link_menu_destroy(gs->link_menu);
211
212 g_signal_handler_disconnect(gs->notebook, gs->tabs_remove_handler_id);
213
214 free(gs);
215
216 if (scaf_list == NULL) {
217 /* no more open windows - stop the browser */
218 nsgtk_complete = true;
219 }
220 }
221
222
223 /**
224 * gtk event callback on window delete event.
225 *
226 * prevent window close if download is in progress
227 *
228 * \param widget The widget receiving the delete event
229 * \param event The event
230 * \param data The context pointer passed when the connection was made.
231 * \return TRUE to indicate message handled.
232 */
233 static gboolean
scaffolding_window_delete_event(GtkWidget * widget,GdkEvent * event,gpointer data)234 scaffolding_window_delete_event(GtkWidget *widget,
235 GdkEvent *event,
236 gpointer data)
237 {
238 struct nsgtk_scaffolding *g = data;
239
240 if (nsgtk_check_for_downloads(GTK_WINDOW(widget)) == false) {
241 gtk_widget_destroy(GTK_WIDGET(g->window));
242 }
243 return TRUE;
244 }
245
246
247 /**
248 * Update the scaffolding controls
249 *
250 * The button sensitivity, url bar and local history visibility are updated
251 *
252 * \param g The scaffolding context to update
253 */
scaffolding_update_context(struct nsgtk_scaffolding * g)254 static void scaffolding_update_context(struct nsgtk_scaffolding *g)
255 {
256 struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
257
258 g->menus[BACK_BUTTON].sensitivity =
259 browser_window_history_back_available(bw);
260 g->menus[FORWARD_BUTTON].sensitivity =
261 browser_window_history_forward_available(bw);
262
263 nsgtk_scaffolding_set_sensitivity(g);
264
265 nsgtk_local_history_hide();
266 }
267
268
269 /**
270 * edit the sensitivity of focused widget
271 *
272 * \todo this needs to update toolbar sensitivity
273 *
274 * \param g The scaffolding context.
275 */
276 static guint
nsgtk_scaffolding_update_edit_actions_sensitivity(struct nsgtk_scaffolding * g)277 nsgtk_scaffolding_update_edit_actions_sensitivity(struct nsgtk_scaffolding *g)
278 {
279 GtkWidget *widget = gtk_window_get_focus(g->window);
280
281 if (GTK_IS_EDITABLE(widget)) {
282 gboolean has_selection;
283 has_selection = gtk_editable_get_selection_bounds(
284 GTK_EDITABLE(widget), NULL, NULL);
285 g->menus[COPY_BUTTON].sensitivity = has_selection;
286 g->menus[CUT_BUTTON].sensitivity = has_selection;
287 g->menus[PASTE_BUTTON].sensitivity = true;
288 } else {
289 struct browser_window *bw =
290 nsgtk_get_browser_window(g->top_level);
291 browser_editor_flags edit_f =
292 browser_window_get_editor_flags(bw);
293
294 g->menus[COPY_BUTTON].sensitivity =
295 edit_f & BW_EDITOR_CAN_COPY;
296 g->menus[CUT_BUTTON].sensitivity =
297 edit_f & BW_EDITOR_CAN_CUT;
298 g->menus[PASTE_BUTTON].sensitivity =
299 edit_f & BW_EDITOR_CAN_PASTE;
300 }
301
302 nsgtk_scaffolding_set_sensitivity(g);
303
304 return ((g->menus[COPY_BUTTON].sensitivity) |
305 (g->menus[CUT_BUTTON].sensitivity) |
306 (g->menus[PASTE_BUTTON].sensitivity));
307 }
308
309
310 /**
311 * make edit actions sensitive
312 *
313 * \todo toolbar sensitivity
314 *
315 * \param g The scaffolding context.
316 */
317 static void
nsgtk_scaffolding_enable_edit_actions_sensitivity(struct nsgtk_scaffolding * g)318 nsgtk_scaffolding_enable_edit_actions_sensitivity(struct nsgtk_scaffolding *g)
319 {
320 g->menus[PASTE_BUTTON].sensitivity = true;
321 g->menus[COPY_BUTTON].sensitivity = true;
322 g->menus[CUT_BUTTON].sensitivity = true;
323 nsgtk_scaffolding_set_sensitivity(g);
324
325 popup_menu_show(g->popup_menu, false, true);
326 }
327
328 /* signal handling functions for the toolbar, URL bar, and menu bar */
329
330 /**
331 * gtk event for edit menu being show
332 *
333 * \param widget The menu widget
334 * \param g scaffolding handle
335 * \return TRUE to indicate event handled
336 */
337 static gboolean
nsgtk_window_edit_menu_shown(GtkWidget * widget,struct nsgtk_scaffolding * g)338 nsgtk_window_edit_menu_shown(GtkWidget *widget,
339 struct nsgtk_scaffolding *g)
340 {
341 nsgtk_scaffolding_update_edit_actions_sensitivity(g);
342
343 return TRUE;
344 }
345
346 /**
347 * gtk event handler for edit menu being hidden
348 *
349 * \param widget The menu widget
350 * \param g scaffolding handle
351 * \return TRUE to indicate event handled
352 */
353 static gboolean
nsgtk_window_edit_menu_hidden(GtkWidget * widget,struct nsgtk_scaffolding * g)354 nsgtk_window_edit_menu_hidden(GtkWidget *widget,
355 struct nsgtk_scaffolding *g)
356 {
357 nsgtk_scaffolding_enable_edit_actions_sensitivity(g);
358
359 return TRUE;
360 }
361
362
363 /**
364 * gtk event handler for popup menu being hidden.
365 *
366 * \param widget The menu widget
367 * \param g scaffolding handle
368 * \return TRUE to indicate event handled
369 */
370 static gboolean
nsgtk_window_popup_menu_hidden(GtkWidget * widget,struct nsgtk_scaffolding * g)371 nsgtk_window_popup_menu_hidden(GtkWidget *widget, struct nsgtk_scaffolding *g)
372 {
373 nsgtk_scaffolding_enable_edit_actions_sensitivity(g);
374 return TRUE;
375 }
376
377
378 /**
379 * Update the menus when the number of tabs changes.
380 *
381 * \todo toolbar sensitivity
382 * \todo next/previous tab ought to only be visible if there is such a tab
383 *
384 * \param notebook The notebook all the tabs are in
385 * \param page The newly added page container widget
386 * \param page_num The index of the newly added page
387 * \param g The scaffolding context containing the notebook
388 */
389 static void
nsgtk_window_tabs_add(GtkNotebook * notebook,GtkWidget * page,guint page_num,struct nsgtk_scaffolding * g)390 nsgtk_window_tabs_add(GtkNotebook *notebook,
391 GtkWidget *page,
392 guint page_num,
393 struct nsgtk_scaffolding *g)
394 {
395 gboolean visible = gtk_notebook_get_show_tabs(g->notebook);
396 g_object_set(g->menu_bar->view_submenu->tabs_menuitem,
397 "visible",
398 visible,
399 NULL);
400 g_object_set(g->burger_menu->view_submenu->tabs_menuitem,
401 "visible",
402 visible,
403 NULL);
404
405 g->menus[NEXTTAB_BUTTON].sensitivity = visible;
406 g->menus[PREVTAB_BUTTON].sensitivity = visible;
407 g->menus[CLOSETAB_BUTTON].sensitivity = visible;
408
409 nsgtk_scaffolding_set_sensitivity(g);
410 }
411
412
413 /**
414 * Update the menus when the number of tabs changes.
415 *
416 * \todo toolbar sensitivity
417 *
418 * \param notebook The notebook all the tabs are in
419 * \param page The page container widget being removed
420 * \param page_num The index of the removed page
421 * \param gs The scaffolding context containing the notebook
422 */
423 static void
nsgtk_window_tabs_remove(GtkNotebook * notebook,GtkWidget * page,guint page_num,struct nsgtk_scaffolding * gs)424 nsgtk_window_tabs_remove(GtkNotebook *notebook,
425 GtkWidget *page,
426 guint page_num,
427 struct nsgtk_scaffolding *gs)
428 {
429 gboolean visible;
430
431 /* if the scaffold is being destroyed it is not useful to
432 * update the state, further many of the widgets may have
433 * already been destroyed.
434 */
435 if (gtk_widget_in_destruction(GTK_WIDGET(gs->window)) == TRUE) {
436 return;
437 }
438
439 /* if this is the last tab destroy the scaffold in addition */
440 if (gtk_notebook_get_n_pages(notebook) == 1) {
441 gtk_widget_destroy(GTK_WIDGET(gs->window));
442 return;
443 }
444
445 visible = gtk_notebook_get_show_tabs(gs->notebook);
446 g_object_set(gs->menu_bar->view_submenu->tabs_menuitem,
447 "visible", visible, NULL);
448 g_object_set(gs->burger_menu->view_submenu->tabs_menuitem,
449 "visible", visible, NULL);
450
451 gs->menus[NEXTTAB_BUTTON].sensitivity = visible;
452 gs->menus[PREVTAB_BUTTON].sensitivity = visible;
453 gs->menus[CLOSETAB_BUTTON].sensitivity = visible;
454
455 nsgtk_scaffolding_set_sensitivity(gs);
456 }
457
458
459 /* signal handlers for menu entries */
460
461 /**
462 * handle menu activate signals by calling toolbar item activation
463 */
464 #define TOOLBAR_ITEM_p(identifier, name) \
465 static gboolean \
466 nsgtk_on_##name##_activate_menu(GtkMenuItem *widget, gpointer data) \
467 { \
468 struct nsgtk_scaffolding *gs = (struct nsgtk_scaffolding *)data;\
469 nsgtk_window_item_activate(gs->top_level, identifier); \
470 return TRUE; \
471 }
472 #define TOOLBAR_ITEM_y(identifier, name)
473 #define TOOLBAR_ITEM_n(identifier, name)
474 #define TOOLBAR_ITEM(identifier, name, sensitivity, clicked, activate, label, iconame) \
475 TOOLBAR_ITEM_ ## activate(identifier, name)
476 #include "gtk/toolbar_items.h"
477 #undef TOOLBAR_ITEM_y
478 #undef TOOLBAR_ITEM_n
479 #undef TOOLBAR_ITEM_p
480 #undef TOOLBAR_ITEM
481
482
483 static gboolean
nsgtk_on_savelink_activate_menu(GtkMenuItem * widget,gpointer data)484 nsgtk_on_savelink_activate_menu(GtkMenuItem *widget, gpointer data)
485 {
486 struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
487 struct gui_window *gui = g->top_level;
488 struct browser_window *bw = nsgtk_get_browser_window(gui);
489 nserror err;
490
491 if (current_menu_features.link == NULL)
492 return FALSE;
493
494 err = browser_window_navigate(bw,
495 current_menu_features.link,
496 NULL,
497 BW_NAVIGATE_DOWNLOAD,
498 NULL,
499 NULL,
500 NULL);
501 if (err != NSERROR_OK) {
502 nsgtk_warning(messages_get_errorcode(err), 0);
503 }
504
505 return TRUE;
506 }
507
508
509 /**
510 * Handler for opening new window from a link. attached to the popup menu.
511 */
512 static gboolean
nsgtk_on_link_openwin_activate_menu(GtkMenuItem * widget,gpointer data)513 nsgtk_on_link_openwin_activate_menu(GtkMenuItem *widget, gpointer data)
514 {
515 struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
516 struct gui_window *gui = g->top_level;
517 struct browser_window *bw = nsgtk_get_browser_window(gui);
518 nserror err;
519
520 if (current_menu_features.link == NULL)
521 return FALSE;
522
523 err = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
524 current_menu_features.link, NULL, bw, NULL);
525 if (err != NSERROR_OK) {
526 nsgtk_warning(messages_get_errorcode(err), 0);
527 }
528
529 return TRUE;
530 }
531
532
533 /**
534 * Handler for opening new tab from a link. attached to the popup menu.
535 */
536 static gboolean
nsgtk_on_link_opentab_activate_menu(GtkMenuItem * widget,gpointer data)537 nsgtk_on_link_opentab_activate_menu(GtkMenuItem *widget, gpointer data)
538 {
539 struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
540 struct gui_window *gui = g->top_level;
541 struct browser_window *bw = nsgtk_get_browser_window(gui);
542 nserror err;
543
544 if (current_menu_features.link == NULL)
545 return FALSE;
546
547 err = browser_window_create(BW_CREATE_CLONE |
548 BW_CREATE_HISTORY |
549 BW_CREATE_TAB,
550 current_menu_features.link, NULL, bw, NULL);
551 if (err != NSERROR_OK) {
552 nsgtk_warning(messages_get_errorcode(err), 0);
553 }
554
555 return TRUE;
556 }
557
558
559 /**
560 * Handler for bookmarking a link. attached to the popup menu.
561 */
562 static gboolean
nsgtk_on_link_bookmark_activate_menu(GtkMenuItem * widget,gpointer data)563 nsgtk_on_link_bookmark_activate_menu(GtkMenuItem *widget, gpointer data)
564 {
565 if (current_menu_features.link == NULL)
566 return FALSE;
567
568 hotlist_add_url(current_menu_features.link);
569
570 return TRUE;
571 }
572
573
574 /**
575 * Handler for copying a link. attached to the popup menu.
576 */
577 static gboolean
nsgtk_on_link_copy_activate_menu(GtkMenuItem * widget,gpointer data)578 nsgtk_on_link_copy_activate_menu(GtkMenuItem *widget, gpointer data)
579 {
580 GtkClipboard *clipboard;
581
582 if (current_menu_features.link == NULL)
583 return FALSE;
584
585 clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
586 gtk_clipboard_set_text(clipboard,
587 nsurl_access(current_menu_features.link), -1);
588
589 return TRUE;
590 }
591
592
nsgtk_on_find_activate_menu(GtkMenuItem * widget,gpointer data)593 static gboolean nsgtk_on_find_activate_menu(GtkMenuItem *widget, gpointer data)
594 {
595 struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
596
597 nsgtk_window_search_toggle(g->top_level);
598
599 return TRUE;
600 }
601
get_bar_show(bool * menu,bool * tool)602 static nserror get_bar_show(bool *menu, bool *tool)
603 {
604 const char *cur_bar_show;
605
606 *menu = false;
607 *tool = false;
608
609 cur_bar_show = nsoption_charp(bar_show);
610 if (cur_bar_show != NULL) {
611 if (strcmp(cur_bar_show, "menu/tool") == 0) {
612 *menu = true;
613 *tool = true;
614 } else if (strcmp(cur_bar_show, "menu") == 0) {
615 *menu = true;
616 } else if (strcmp(cur_bar_show, "tool") == 0) {
617 *tool = true;
618 }
619 }
620
621 return NSERROR_OK;
622 }
623
set_bar_show(const char * bar,bool show)624 static nserror set_bar_show(const char *bar, bool show)
625 {
626 bool menu;
627 bool tool;
628 const char *new_bar_show;
629
630 get_bar_show(&menu, &tool);
631
632 if (strcmp(bar, "menu") == 0) {
633 menu = show;
634 } else if (strcmp(bar, "tool") == 0) {
635 tool = show;
636 }
637
638 if ((menu == true) && (tool == true)) {
639 new_bar_show = "menu/tool";
640 } else if (menu == true) {
641 new_bar_show = "menu";
642 } else if (tool == true) {
643 new_bar_show = "tool";
644 } else {
645 new_bar_show = "none";
646 }
647 nsoption_set_charp(bar_show, strdup(new_bar_show));
648
649 return NSERROR_OK;
650 }
651
652 static gboolean
nsgtk_on_menubar_activate_menu(GtkMenuItem * widget,gpointer data)653 nsgtk_on_menubar_activate_menu(GtkMenuItem *widget, gpointer data)
654 {
655 struct nsgtk_scaffolding *gs = (struct nsgtk_scaffolding *)data;
656 GtkCheckMenuItem *bmcmi; /* burger menu check */
657 GtkCheckMenuItem *mbcmi; /* menu bar check */
658 GtkCheckMenuItem *tbcmi; /* popup menu check */
659
660 bmcmi = GTK_CHECK_MENU_ITEM(gs->burger_menu->view_submenu->toolbars_submenu->menubar_menuitem);
661 mbcmi = GTK_CHECK_MENU_ITEM(gs->menu_bar->view_submenu->toolbars_submenu->menubar_menuitem);
662 tbcmi = GTK_CHECK_MENU_ITEM(gs->popup_menu->toolbars_submenu->menubar_menuitem);
663
664 /* ensure menubar and burger menu checkboxes are both updated */
665 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
666 if (gtk_check_menu_item_get_active(bmcmi) == FALSE) {
667 gtk_check_menu_item_set_active(bmcmi, TRUE);
668 }
669
670 if (gtk_check_menu_item_get_active(mbcmi) == FALSE) {
671 gtk_check_menu_item_set_active(mbcmi, TRUE);
672 }
673
674 if (gtk_check_menu_item_get_active(tbcmi) == FALSE) {
675 gtk_check_menu_item_set_active(tbcmi, TRUE);
676 }
677
678 gtk_widget_show(GTK_WIDGET(gs->menu_bar->bar_menu));
679 set_bar_show("menu", true);
680 } else {
681 if (gtk_check_menu_item_get_active(bmcmi) == TRUE) {
682 gtk_check_menu_item_set_active(bmcmi, FALSE);
683 }
684
685 if (gtk_check_menu_item_get_active(mbcmi) == TRUE) {
686 gtk_check_menu_item_set_active(mbcmi, FALSE);
687 }
688
689 if (gtk_check_menu_item_get_active(tbcmi) == TRUE) {
690 gtk_check_menu_item_set_active(tbcmi, FALSE);
691 }
692
693 gtk_widget_hide(GTK_WIDGET(gs->menu_bar->bar_menu));
694 set_bar_show("menu", false);
695 }
696 return TRUE;
697 }
698
699
700 static gboolean
nsgtk_on_toolbar_activate_menu(GtkMenuItem * widget,gpointer data)701 nsgtk_on_toolbar_activate_menu(GtkMenuItem *widget, gpointer data)
702 {
703 struct nsgtk_scaffolding *gs = (struct nsgtk_scaffolding *)data;
704 GtkCheckMenuItem *bmcmi; /* burger menu check */
705 GtkCheckMenuItem *mbcmi; /* menu bar check */
706 GtkCheckMenuItem *tbcmi; /* popup menu check */
707
708 bmcmi = GTK_CHECK_MENU_ITEM(gs->burger_menu->view_submenu->toolbars_submenu->toolbar_menuitem);
709 mbcmi = GTK_CHECK_MENU_ITEM(gs->menu_bar->view_submenu->toolbars_submenu->toolbar_menuitem);
710 tbcmi = GTK_CHECK_MENU_ITEM(gs->popup_menu->toolbars_submenu->toolbar_menuitem);
711
712 /* ensure menubar and burger menu checkboxes are both updated */
713 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
714 if (gtk_check_menu_item_get_active(bmcmi) == FALSE) {
715 gtk_check_menu_item_set_active(bmcmi, TRUE);
716 }
717
718 if (gtk_check_menu_item_get_active(mbcmi) == FALSE) {
719 gtk_check_menu_item_set_active(mbcmi, TRUE);
720 }
721
722 if (gtk_check_menu_item_get_active(tbcmi) == FALSE) {
723 gtk_check_menu_item_set_active(tbcmi, TRUE);
724 }
725
726 nsgtk_window_toolbar_show(gs, true);
727 set_bar_show("tool", true);
728 } else {
729 if (gtk_check_menu_item_get_active(bmcmi) == TRUE) {
730 gtk_check_menu_item_set_active(bmcmi, FALSE);
731 }
732
733 if (gtk_check_menu_item_get_active(mbcmi) == TRUE) {
734 gtk_check_menu_item_set_active(mbcmi, FALSE);
735 }
736
737 if (gtk_check_menu_item_get_active(tbcmi) == TRUE) {
738 gtk_check_menu_item_set_active(tbcmi, FALSE);
739 }
740
741 nsgtk_window_toolbar_show(gs, false);
742 set_bar_show("tool", false);
743 }
744 return TRUE;
745 }
746
747
748 static gboolean
nsgtk_on_nexttab_activate_menu(GtkMenuItem * widget,gpointer data)749 nsgtk_on_nexttab_activate_menu(GtkMenuItem *widget, gpointer data)
750 {
751 struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
752
753 nsgtk_tab_next(g->notebook);
754
755 return TRUE;
756 }
757
758
759 static gboolean
nsgtk_on_prevtab_activate_menu(GtkMenuItem * widget,gpointer data)760 nsgtk_on_prevtab_activate_menu(GtkMenuItem *widget, gpointer data)
761 {
762 struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
763
764 nsgtk_tab_prev(g->notebook);
765
766 return TRUE;
767 }
768
769
770 /**
771 * menu signal handler for activation on close tab item
772 */
773 static gboolean
nsgtk_on_closetab_activate_menu(GtkMenuItem * widget,gpointer data)774 nsgtk_on_closetab_activate_menu(GtkMenuItem *widget, gpointer data)
775 {
776 struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
777
778 nsgtk_tab_close_current(g->notebook);
779
780 return TRUE;
781 }
782
783 /* end of menu callback handlers */
784
785 /**
786 * attach gtk signal handlers for menus
787 */
nsgtk_menu_connect_signals(struct nsgtk_scaffolding * g)788 static void nsgtk_menu_connect_signals(struct nsgtk_scaffolding *g)
789 {
790 int idx; /* item index */
791 for (idx = BACK_BUTTON; idx < PLACEHOLDER_BUTTON; idx++) {
792 if (g->menus[idx].main != NULL) {
793 g_signal_connect(g->menus[idx].main,
794 "activate",
795 G_CALLBACK(g->menus[idx].mhandler),
796 g);
797 }
798 if (g->menus[idx].burger != NULL) {
799 g_signal_connect(g->menus[idx].burger,
800 "activate",
801 G_CALLBACK(g->menus[idx].mhandler),
802 g);
803 }
804 if (g->menus[idx].popup != NULL) {
805 g_signal_connect(g->menus[idx].popup,
806 "activate",
807 G_CALLBACK(g->menus[idx].mhandler),
808 g);
809 }
810 }
811 }
812
813
814 /**
815 * Create and connect handlers to bar menu.
816 *
817 * \param gs scaffolding to attach popup menu to.
818 * \param group The accelerator group to use for the popup.
819 * \param showmenu if the bar menu should be shown
820 * \param showtool if the toolabar should be shown
821 * \return menu structure on success or NULL on error.
822 */
823 static struct nsgtk_bar_submenu *
create_scaffolding_bar_menu(struct nsgtk_scaffolding * gs,GtkAccelGroup * group,bool showmenu,bool showtool)824 create_scaffolding_bar_menu(struct nsgtk_scaffolding *gs,
825 GtkAccelGroup *group,
826 bool showmenu,
827 bool showtool)
828 {
829 GtkMenuShell *menushell;
830 struct nsgtk_bar_submenu *nmenu;
831
832 menushell = GTK_MENU_SHELL(gtk_builder_get_object(gs->builder,
833 "menubar"));
834
835 nmenu = nsgtk_menu_bar_create(menushell, group);
836 if (nmenu == NULL) {
837 return NULL;
838 }
839
840 /* set menu bar visibility */
841 if (showmenu) {
842 gtk_widget_show(GTK_WIDGET(nmenu->bar_menu));
843 } else {
844 gtk_widget_hide(GTK_WIDGET(nmenu->bar_menu));
845 }
846
847 /* set checks correct way on toolbar submenu */
848 gtk_check_menu_item_set_active(nmenu->view_submenu->toolbars_submenu->menubar_menuitem, showmenu);
849 gtk_check_menu_item_set_active(nmenu->view_submenu->toolbars_submenu->toolbar_menuitem, showtool);
850
851 /* bar menu signal handlers for edit controls */
852 g_signal_connect(nmenu->edit_submenu->edit,
853 "show",
854 G_CALLBACK(nsgtk_window_edit_menu_shown),
855 gs);
856
857 g_signal_connect(nmenu->edit_submenu->edit,
858 "hide",
859 G_CALLBACK(nsgtk_window_edit_menu_hidden),
860 gs);
861
862 /*
863 * attach signal handlers for menubar and toolbar visibility toggling
864 */
865 g_signal_connect(nmenu->view_submenu->toolbars_submenu->menubar_menuitem,
866 "toggled",
867 G_CALLBACK(nsgtk_on_menubar_activate_menu),
868 gs);
869
870 g_signal_connect(nmenu->view_submenu->toolbars_submenu->toolbar_menuitem,
871 "toggled",
872 G_CALLBACK(nsgtk_on_toolbar_activate_menu),
873 gs);
874
875
876 return nmenu;
877 }
878
879
880 /**
881 * Create and connect handlers to burger menu.
882 *
883 * \param g scaffolding to attach popup menu to.
884 * \param group The accelerator group to use for the popup.
885 * \param showbar if the bar menu should be shown
886 * \param showtool if the toolabar should be shown
887 * \return menu structure on success or NULL on error.
888 */
889 static struct nsgtk_burger_menu *
create_scaffolding_burger_menu(struct nsgtk_scaffolding * gs,GtkAccelGroup * group,bool showbar,bool showtool)890 create_scaffolding_burger_menu(struct nsgtk_scaffolding *gs,
891 GtkAccelGroup *group,
892 bool showbar,
893 bool showtool)
894 {
895 struct nsgtk_burger_menu *nmenu;
896
897 nmenu = nsgtk_burger_menu_create(group);
898
899 if (nmenu == NULL) {
900 return NULL;
901 }
902
903 /* set checks correct way on toolbar submenu */
904 gtk_check_menu_item_set_active(nmenu->view_submenu->toolbars_submenu->menubar_menuitem, showbar);
905 gtk_check_menu_item_set_active(nmenu->view_submenu->toolbars_submenu->toolbar_menuitem, showtool);
906
907 g_signal_connect(nmenu->view_submenu->toolbars_submenu->menubar_menuitem,
908 "toggled",
909 G_CALLBACK(nsgtk_on_menubar_activate_menu),
910 gs);
911 g_signal_connect(nmenu->view_submenu->toolbars_submenu->toolbar_menuitem,
912 "toggled",
913 G_CALLBACK(nsgtk_on_toolbar_activate_menu),
914 gs);
915 return nmenu;
916 }
917
918
919 /**
920 * Create and connect handlers to popup menu.
921 *
922 * \param gs scaffolding to attach popup menu to.
923 * \param group The accelerator group to use for the popup.
924 * \param showbar if the bar menu should be shown
925 * \param showtool if the toolabar should be shown
926 * \return menu structure on success or NULL on error.
927 */
928 static struct nsgtk_popup_menu *
create_scaffolding_popup_menu(struct nsgtk_scaffolding * gs,GtkAccelGroup * group,bool showbar,bool showtool)929 create_scaffolding_popup_menu(struct nsgtk_scaffolding *gs,
930 GtkAccelGroup *group,
931 bool showbar,
932 bool showtool)
933 {
934 struct nsgtk_popup_menu *nmenu;
935
936 nmenu = nsgtk_popup_menu_create(group);
937
938 if (nmenu == NULL) {
939 return NULL;
940 }
941 /* set checks correct way on toolbar submenu */
942 gtk_check_menu_item_set_active(nmenu->toolbars_submenu->menubar_menuitem, showbar);
943 gtk_check_menu_item_set_active(nmenu->toolbars_submenu->toolbar_menuitem, showtool);
944
945 g_signal_connect(nmenu->popup_menu,
946 "hide",
947 G_CALLBACK(nsgtk_window_popup_menu_hidden),
948 gs);
949
950 g_signal_connect(nmenu->toolbars_submenu->menubar_menuitem,
951 "toggled",
952 G_CALLBACK(nsgtk_on_menubar_activate_menu),
953 gs);
954 g_signal_connect(nmenu->toolbars_submenu->toolbar_menuitem,
955 "toggled",
956 G_CALLBACK(nsgtk_on_toolbar_activate_menu),
957 gs);
958
959 /* set initial popup menu visibility */
960 popup_menu_hide(nmenu, false, false);
961
962 return nmenu;
963 }
964
965
966 /**
967 * Create and connect handlers to link popup menu.
968 *
969 * \param g scaffolding to attach popup menu to.
970 * \param group The accelerator group to use for the popup.
971 * \return true on success or false on error.
972 */
973 static struct nsgtk_link_menu *
create_scaffolding_link_menu(struct nsgtk_scaffolding * g,GtkAccelGroup * group)974 create_scaffolding_link_menu(struct nsgtk_scaffolding *g, GtkAccelGroup *group)
975 {
976 struct nsgtk_link_menu *nmenu;
977
978 nmenu = nsgtk_link_menu_create(group);
979
980 if (nmenu == NULL) {
981 return NULL;
982 }
983
984 g_signal_connect(nmenu->save_menuitem,
985 "activate",
986 G_CALLBACK(nsgtk_on_savelink_activate_menu),
987 g);
988
989 g_signal_connect(nmenu->opentab_menuitem,
990 "activate",
991 G_CALLBACK(nsgtk_on_link_opentab_activate_menu),
992 g);
993
994 g_signal_connect(nmenu->openwin_menuitem,
995 "activate",
996 G_CALLBACK(nsgtk_on_link_openwin_activate_menu),
997 g);
998
999 g_signal_connect(nmenu->bookmark_menuitem,
1000 "activate",
1001 G_CALLBACK(nsgtk_on_link_bookmark_activate_menu),
1002 g);
1003
1004 g_signal_connect(nmenu->copy_menuitem,
1005 "activate",
1006 G_CALLBACK(nsgtk_on_link_copy_activate_menu),
1007 g);
1008
1009 return nmenu;
1010 }
1011
1012
1013 /**
1014 * initialiase the menu signal handlers ready for connection
1015 */
nsgtk_menu_initialise(struct nsgtk_scaffolding * g)1016 static nserror nsgtk_menu_initialise(struct nsgtk_scaffolding *g)
1017 {
1018 #define TOOLBAR_ITEM_p(identifier, name, iconame) \
1019 g->menus[identifier].mhandler = nsgtk_on_##name##_activate_menu; \
1020 g->menus[identifier].iconname = iconame;
1021 #define TOOLBAR_ITEM_y(identifier, name, iconame) \
1022 g->menus[identifier].mhandler = nsgtk_on_##name##_activate_menu; \
1023 g->menus[identifier].iconname = iconame;
1024 #define TOOLBAR_ITEM_n(identifier, name, iconame) \
1025 g->menus[identifier].mhandler = NULL; \
1026 g->menus[identifier].iconname = iconame;
1027 #define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate, label, iconame) \
1028 g->menus[identifier].sensitivity = snstvty; \
1029 TOOLBAR_ITEM_ ## activate(identifier, name, iconame)
1030 #include "gtk/toolbar_items.h"
1031 #undef TOOLBAR_ITEM_y
1032 #undef TOOLBAR_ITEM_n
1033 #undef TOOLBAR_ITEM
1034
1035 /* items on menubar, burger */
1036 #define ITEM_MB(p, q, r) \
1037 g->menus[p##_BUTTON].main = g->menu_bar->r##_submenu->q##_menuitem; \
1038 g->menus[p##_BUTTON].burger = g->burger_menu->r##_submenu->q##_menuitem
1039
1040 /* items on menubar, burger and context popup submenu */
1041 #define ITEM_MBP(p, q, r) \
1042 g->menus[p##_BUTTON].main = g->menu_bar->r##_submenu->q##_menuitem; \
1043 g->menus[p##_BUTTON].burger = g->burger_menu->r##_submenu->q##_menuitem; \
1044 g->menus[p##_BUTTON].popup = g->popup_menu->r##_submenu->q##_menuitem
1045
1046 /* items on menubar, burger and context popup */
1047 #define ITEM_MBp(p, q, r) \
1048 g->menus[p##_BUTTON].main = g->menu_bar->r##_submenu->q##_menuitem; \
1049 g->menus[p##_BUTTON].burger = g->burger_menu->r##_submenu->q##_menuitem; \
1050 g->menus[p##_BUTTON].popup = g->popup_menu->q##_menuitem
1051
1052
1053 /* file menu */
1054 ITEM_MB(NEWWINDOW, newwindow, file);
1055 ITEM_MB(NEWTAB, newtab, file);
1056 ITEM_MB(OPENFILE, openfile, file);
1057 ITEM_MB(CLOSEWINDOW, closewindow, file);
1058 ITEM_MB(PRINTPREVIEW, printpreview, file);
1059 ITEM_MB(PRINT, print, file);
1060 ITEM_MB(QUIT, quit, file);
1061 /* file - export submenu */
1062 ITEM_MB(SAVEPAGE, savepage, file_submenu->export);
1063 ITEM_MB(PLAINTEXT, plaintext, file_submenu->export);
1064 ITEM_MB(PDF, pdf, file_submenu->export);
1065
1066 /* edit menu */
1067 ITEM_MBp(CUT, cut, edit);
1068 ITEM_MBp(COPY, copy, edit);
1069 ITEM_MBp(PASTE, paste, edit);
1070 ITEM_MB(DELETE, delete, edit);
1071 ITEM_MB(SELECTALL, selectall, edit);
1072 ITEM_MB(FIND, find, edit);
1073 ITEM_MB(PREFERENCES, preferences, edit);
1074
1075 /* view menu */
1076 ITEM_MB(FULLSCREEN, fullscreen, view);
1077 ITEM_MB(SAVEWINDOWSIZE, savewindowsize, view);
1078 /* view - scale submenu */
1079 ITEM_MB(ZOOMPLUS, zoomplus, view_submenu->scaleview);
1080 ITEM_MB(ZOOMMINUS, zoomminus, view_submenu->scaleview);
1081 ITEM_MB(ZOOMNORMAL, zoomnormal, view_submenu->scaleview);
1082 /* view - tabs submenu */
1083 ITEM_MB(NEXTTAB, nexttab, view_submenu->tabs);
1084 ITEM_MB(PREVTAB, prevtab, view_submenu->tabs);
1085 ITEM_MB(CLOSETAB, closetab, view_submenu->tabs);
1086 /* view - toolbars submenu */
1087 ITEM_MB(CUSTOMIZE, customize, view_submenu->toolbars);
1088 g->menus[CUSTOMIZE_BUTTON].popup = g->popup_menu->toolbars_submenu->customize_menuitem;
1089
1090 /* navigation menu */
1091 ITEM_MBp(BACK, back, nav);
1092 ITEM_MBp(FORWARD, forward, nav);
1093 ITEM_MBp(STOP, stop, nav);
1094 ITEM_MBp(RELOAD, reload, nav);
1095 ITEM_MB(HOME, home, nav);
1096 ITEM_MB(LOCALHISTORY, localhistory, nav);
1097 ITEM_MB(GLOBALHISTORY, globalhistory, nav);
1098 ITEM_MB(ADDBOOKMARKS, addbookmarks, nav);
1099 ITEM_MB(SHOWBOOKMARKS, showbookmarks, nav);
1100 ITEM_MB(OPENLOCATION, openlocation, nav);
1101
1102 /* tools menu */
1103 ITEM_MBP(DOWNLOADS, downloads, tools);
1104 ITEM_MBP(SHOWCOOKIES, showcookies, tools);
1105 /* tools > developer submenu */
1106 ITEM_MBP(VIEWSOURCE, viewsource, tools_submenu->developer);
1107 ITEM_MBP(TOGGLEDEBUGGING, toggledebugging, tools_submenu->developer);
1108 ITEM_MBP(SAVEBOXTREE, debugboxtree, tools_submenu->developer);
1109 ITEM_MBP(SAVEDOMTREE, debugdomtree, tools_submenu->developer);
1110
1111 /* help menu */
1112 ITEM_MB(CONTENTS, contents, help);
1113 ITEM_MB(GUIDE, guide, help);
1114 ITEM_MB(INFO, info, help);
1115 ITEM_MB(ABOUT, about, help);
1116
1117
1118 #undef ITEM_MB
1119 #undef ITEM_MBp
1120 #undef ITEM_MBP
1121
1122 return NSERROR_OK;
1123 }
1124
1125
nsgtk_menu_set_sensitivity(struct nsgtk_scaffolding * g)1126 static void nsgtk_menu_set_sensitivity(struct nsgtk_scaffolding *g)
1127 {
1128
1129 for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
1130 if (g->menus[i].main != NULL) {
1131 gtk_widget_set_sensitive(GTK_WIDGET(
1132 g->menus[i].main),
1133 g->menus[i].sensitivity);
1134 }
1135 if (g->menus[i].burger != NULL) {
1136 gtk_widget_set_sensitive(GTK_WIDGET(
1137 g->menus[i].burger),
1138 g->menus[i].sensitivity);
1139 }
1140 if (g->menus[i].popup != NULL) {
1141 gtk_widget_set_sensitive(GTK_WIDGET(
1142 g->menus[i].popup),
1143 g->menus[i].sensitivity);
1144 }
1145 }
1146 }
1147
1148
1149 /* set menu items to have icons */
nsgtk_menu_set_icons(struct nsgtk_scaffolding * g)1150 static void nsgtk_menu_set_icons(struct nsgtk_scaffolding *g)
1151 {
1152 GtkWidget *img;
1153 for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
1154 /* ensure there is an icon name */
1155 if (g->menus[i].iconname == NULL) {
1156 continue;
1157 }
1158
1159 if (g->menus[i].main != NULL) {
1160 img = gtk_image_new_from_icon_name(g->menus[i].iconname,
1161 GTK_ICON_SIZE_MENU);
1162 nsgtk_image_menu_item_set_image(GTK_WIDGET(g->menus[i].main), img);
1163 }
1164 if (g->menus[i].burger != NULL) {
1165 img = gtk_image_new_from_icon_name(g->menus[i].iconname,
1166 GTK_ICON_SIZE_MENU);
1167 nsgtk_image_menu_item_set_image(GTK_WIDGET(g->menus[i].burger), img);
1168 }
1169 if (g->menus[i].popup != NULL) {
1170 img = gtk_image_new_from_icon_name(g->menus[i].iconname,
1171 GTK_ICON_SIZE_MENU);
1172 nsgtk_image_menu_item_set_image(GTK_WIDGET(g->menus[i].popup), img);
1173 }
1174 }
1175 }
1176
1177
1178 /**
1179 * create and initialise menus
1180 *
1181 * There are four menus held by the scaffolding:
1182 *
1183 * 1. Main menubar menu.
1184 * This can be hidden which causes the right click popup context menu
1185 * to use the burger menu.
1186 * 2. Burger menu.
1187 * This can be opened from a burger icon on the toolbar.
1188 * 3. popup context menu.
1189 * This is opened by right mouse clicking on the toolbar or browser area
1190 * 4. link context menu
1191 * Opened like the other popup menu when the mouse is over a link in the
1192 * browser area
1193 *
1194 * The cut, copy, paste, delete and back, forwards, stop, reload groups of
1195 * menu entries are context sensitive and must be updated as appropriate
1196 * when a menu is opened which contains those groups.
1197 */
nsgtk_menus_create(struct nsgtk_scaffolding * gs)1198 static nserror nsgtk_menus_create(struct nsgtk_scaffolding *gs)
1199 {
1200 GtkAccelGroup *group;
1201 bool showmenu; /* show menubar */
1202 bool showtool; /* show toolbar */
1203
1204 get_bar_show(&showmenu, &showtool);
1205
1206 group = gtk_accel_group_new();
1207
1208 gtk_window_add_accel_group(gs->window, group);
1209
1210 gs->menu_bar = create_scaffolding_bar_menu(gs, group, showmenu, showtool);
1211 gs->burger_menu = create_scaffolding_burger_menu(gs, group, showmenu, showtool);
1212 gs->popup_menu = create_scaffolding_popup_menu(gs, group, showmenu, showtool);
1213 gs->link_menu = create_scaffolding_link_menu(gs, group);
1214
1215 /* set up the menu signal handlers */
1216 nsgtk_menu_initialise(gs);
1217 nsgtk_menu_set_icons(gs);
1218 nsgtk_menu_connect_signals(gs);
1219 nsgtk_menu_set_sensitivity(gs);
1220
1221 return NSERROR_OK;
1222 }
1223
1224
1225 /* exported function documented in gtk/scaffolding.h */
nsgtk_scaffolding_set_title(struct gui_window * gw,const char * title)1226 void nsgtk_scaffolding_set_title(struct gui_window *gw, const char *title)
1227 {
1228 struct nsgtk_scaffolding *gs = nsgtk_get_scaffold(gw);
1229 int title_len;
1230 char *newtitle;
1231
1232 /* only set window title if top level window */
1233 if (gs->top_level != gw) {
1234 return;
1235 }
1236
1237 if (title == NULL || title[0] == '\0') {
1238 gtk_window_set_title(gs->window, "NetSurf");
1239 return;
1240 }
1241
1242 title_len = strlen(title) + SLEN(" - NetSurf") + 1;
1243 newtitle = malloc(title_len);
1244 if (newtitle == NULL) {
1245 return;
1246 }
1247
1248 snprintf(newtitle, title_len, "%s - NetSurf", title);
1249
1250 gtk_window_set_title(gs->window, newtitle);
1251
1252 free(newtitle);
1253
1254 }
1255
1256
1257 /* exported interface documented in scaffolding.h */
nsgtk_scaffolding_throbber(struct gui_window * gw,bool active)1258 nserror nsgtk_scaffolding_throbber(struct gui_window* gw, bool active)
1259 {
1260 struct nsgtk_scaffolding *gs = nsgtk_get_scaffold(gw);
1261 if (active) {
1262 gs->menus[STOP_BUTTON].sensitivity = true;
1263 gs->menus[RELOAD_BUTTON].sensitivity = false;
1264 } else {
1265 gs->menus[STOP_BUTTON].sensitivity = false;
1266 gs->menus[RELOAD_BUTTON].sensitivity = true;
1267 }
1268 scaffolding_update_context(gs);
1269
1270 return NSERROR_OK;
1271 }
1272
1273
1274 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_destroy_all(void)1275 nserror nsgtk_scaffolding_destroy_all(void)
1276 {
1277 struct nsgtk_scaffolding *gs;
1278
1279 gs = scaf_list;
1280 assert(gs != NULL);
1281
1282 if (nsgtk_check_for_downloads(gs->window) == true) {
1283 return NSERROR_INVALID;
1284 }
1285
1286 /* iterate all scaffolding windows and destroy them */
1287 while (gs != NULL) {
1288 struct nsgtk_scaffolding *next = gs->next;
1289 gtk_widget_destroy(GTK_WIDGET(gs->window));
1290 gs = next;
1291 }
1292 return NSERROR_OK;
1293 }
1294
1295
1296 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_window(struct nsgtk_scaffolding * g)1297 GtkWindow* nsgtk_scaffolding_window(struct nsgtk_scaffolding *g)
1298 {
1299 return g->window;
1300 }
1301
1302 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_notebook(struct nsgtk_scaffolding * g)1303 GtkNotebook* nsgtk_scaffolding_notebook(struct nsgtk_scaffolding *g)
1304 {
1305 return g->notebook;
1306 }
1307
1308
1309 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding * gs)1310 GtkMenuBar *nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding *gs)
1311 {
1312 if (gs == NULL) {
1313 return NULL;
1314 }
1315 return gs->menu_bar->bar_menu;
1316 }
1317
1318 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_iterate(struct nsgtk_scaffolding * g)1319 struct nsgtk_scaffolding *nsgtk_scaffolding_iterate(struct nsgtk_scaffolding *g)
1320 {
1321 if (g == NULL) {
1322 return scaf_list;
1323 }
1324 return g->next;
1325 }
1326
1327
1328 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_top_level(struct nsgtk_scaffolding * g)1329 struct gui_window *nsgtk_scaffolding_top_level(struct nsgtk_scaffolding *g)
1330 {
1331 return g->top_level;
1332 }
1333
1334
1335 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_set_top_level(struct gui_window * gw)1336 void nsgtk_scaffolding_set_top_level(struct gui_window *gw)
1337 {
1338 struct browser_window *bw;
1339 struct nsgtk_scaffolding *sc;
1340
1341 assert(gw != NULL);
1342
1343 bw = nsgtk_get_browser_window(gw);
1344
1345 assert(bw != NULL);
1346
1347 sc = nsgtk_get_scaffold(gw);
1348 assert(sc != NULL);
1349
1350 scaf_current = sc;
1351
1352 sc->top_level = gw;
1353
1354 /* Synchronise the history */
1355 scaffolding_update_context(sc);
1356
1357 /* Ensure the window's title bar is updated */
1358 nsgtk_scaffolding_set_title(gw, browser_window_get_title(bw));
1359 }
1360
1361
1362 /* exported interface documented in scaffolding.h */
nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding * g)1363 void nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding *g)
1364 {
1365 int i;
1366 #define SENSITIVITY(q) \
1367 i = q##_BUTTON; \
1368 if (g->menus[i].main != NULL) \
1369 gtk_widget_set_sensitive(GTK_WIDGET(g->menus[i].main), \
1370 g->menus[i].sensitivity); \
1371 if (g->menus[i].burger != NULL) \
1372 gtk_widget_set_sensitive(GTK_WIDGET(g->menus[i].burger), \
1373 g->menus[i].sensitivity); \
1374 if (g->menus[i].popup != NULL) \
1375 gtk_widget_set_sensitive(GTK_WIDGET(g->menus[i].popup), \
1376 g->menus[i].sensitivity);
1377
1378 SENSITIVITY(STOP)
1379 SENSITIVITY(RELOAD)
1380 SENSITIVITY(CUT)
1381 SENSITIVITY(COPY)
1382 SENSITIVITY(PASTE)
1383 SENSITIVITY(BACK)
1384 SENSITIVITY(FORWARD)
1385 SENSITIVITY(NEXTTAB)
1386 SENSITIVITY(PREVTAB)
1387 SENSITIVITY(CLOSETAB)
1388 #undef SENSITIVITY
1389
1390 }
1391
1392
1393 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_toolbar_context_menu(struct nsgtk_scaffolding * gs)1394 nserror nsgtk_scaffolding_toolbar_context_menu(struct nsgtk_scaffolding *gs)
1395 {
1396 /* set visibility for right-click popup menu */
1397 popup_menu_hide(gs->popup_menu, false, true);
1398
1399 nsgtk_menu_popup_at_pointer(gs->popup_menu->popup_menu, NULL);
1400
1401 return NSERROR_OK;
1402 }
1403
1404
1405 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_burger_menu(struct nsgtk_scaffolding * gs)1406 nserror nsgtk_scaffolding_burger_menu(struct nsgtk_scaffolding *gs)
1407 {
1408 nsgtk_menu_popup_at_pointer(gs->burger_menu->burger_menu, NULL);
1409
1410 return NSERROR_OK;
1411 }
1412
1413
1414 /* exported interface documented in gtk/scaffolding.h */
1415 void
nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding * g,gdouble x,gdouble y)1416 nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding *g,
1417 gdouble x,
1418 gdouble y)
1419 {
1420 GtkMenu *gtkmenu;
1421 struct browser_window *bw;
1422
1423 bw = nsgtk_get_browser_window(g->top_level);
1424
1425 /* update the global context menu features */
1426 browser_window_get_features(bw, x, y, ¤t_menu_features);
1427
1428 if (current_menu_features.link != NULL) {
1429 /* menu is opening over a link */
1430 gtkmenu = g->link_menu->link_menu;
1431 } else {
1432 gtkmenu = g->popup_menu->popup_menu;
1433
1434 nsgtk_scaffolding_update_edit_actions_sensitivity(g);
1435
1436 if (!(g->menus[COPY_BUTTON].sensitivity)) {
1437 gtk_widget_hide(GTK_WIDGET(g->popup_menu->copy_menuitem));
1438 } else {
1439 gtk_widget_show(GTK_WIDGET(g->popup_menu->copy_menuitem));
1440 }
1441
1442 if (!(g->menus[CUT_BUTTON].sensitivity)) {
1443 gtk_widget_hide(GTK_WIDGET(g->popup_menu->cut_menuitem));
1444 } else {
1445 gtk_widget_show(GTK_WIDGET(g->popup_menu->cut_menuitem));
1446 }
1447
1448 if (!(g->menus[PASTE_BUTTON].sensitivity)) {
1449 gtk_widget_hide(GTK_WIDGET(g->popup_menu->paste_menuitem));
1450 } else {
1451 gtk_widget_show(GTK_WIDGET(g->popup_menu->paste_menuitem));
1452 }
1453
1454 }
1455
1456 nsgtk_menu_popup_at_pointer(gtkmenu, NULL);
1457 }
1458
1459 /* exported interface documented in gtk/scaffolding.h */
nsgtk_current_scaffolding(void)1460 struct nsgtk_scaffolding *nsgtk_current_scaffolding(void)
1461 {
1462 if (scaf_current == NULL) {
1463 scaf_current = scaf_list;
1464 }
1465 return scaf_current;
1466 }
1467
1468 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_from_notebook(GtkNotebook * notebook)1469 struct nsgtk_scaffolding *nsgtk_scaffolding_from_notebook(GtkNotebook *notebook)
1470 {
1471 struct nsgtk_scaffolding *gs;
1472 gs = scaf_list;
1473 while (gs != NULL) {
1474 if (gs->notebook == notebook) {
1475 break;
1476 }
1477 gs = gs->next;
1478 }
1479 return gs;
1480 }
1481
1482 /* exported interface documented in gtk/scaffolding.h */
nsgtk_new_scaffolding(struct gui_window * toplevel)1483 struct nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
1484 {
1485 nserror res;
1486 struct nsgtk_scaffolding *gs;
1487
1488 gs = calloc(1, sizeof(*gs));
1489 if (gs == NULL) {
1490 return NULL;
1491 }
1492
1493 NSLOG(netsurf, INFO,
1494 "Constructing a scaffold of %p for gui_window %p", gs, toplevel);
1495
1496 gs->top_level = toplevel;
1497
1498 /* Construct UI widgets */
1499 if (nsgtk_builder_new_from_resname("netsurf", &gs->builder) != NSERROR_OK) {
1500 free(gs);
1501 return NULL;
1502 }
1503
1504 gtk_builder_connect_signals(gs->builder, NULL);
1505
1506 /* containing window setup */
1507 gs->window = GTK_WINDOW(gtk_builder_get_object(gs->builder,
1508 "wndBrowser"));
1509
1510 /**
1511 * set this window's size and position to what's in the options, or
1512 * some sensible default if they are not set yet.
1513 */
1514 if (nsoption_int(window_width) > 0) {
1515 gtk_window_move(gs->window,
1516 nsoption_int(window_x),
1517 nsoption_int(window_y));
1518 gtk_window_resize(gs->window,
1519 nsoption_int(window_width),
1520 nsoption_int(window_height));
1521 } else {
1522 /* Set to 1000x700, so we're very likely to fit even on
1523 * 1024x768 displays, not being able to take into account
1524 * window furniture or panels.
1525 */
1526 gtk_window_set_default_size(gs->window, 1000, 700);
1527 }
1528
1529 g_signal_connect(gs->window,
1530 "delete-event",
1531 G_CALLBACK(scaffolding_window_delete_event),
1532 gs);
1533
1534 g_signal_connect(gs->window,
1535 "destroy",
1536 G_CALLBACK(scaffolding_window_destroy),
1537 gs);
1538
1539
1540 /* notebook */
1541 res = nsgtk_notebook_create(gs->builder, &gs->notebook);
1542 if (res != NSERROR_OK) {
1543 free(gs);
1544 return NULL;
1545 }
1546
1547 g_signal_connect_after(gs->notebook,
1548 "page-added",
1549 G_CALLBACK(nsgtk_window_tabs_add),
1550 gs);
1551 gs->tabs_remove_handler_id = g_signal_connect_after(gs->notebook,
1552 "page-removed",
1553 G_CALLBACK(nsgtk_window_tabs_remove),
1554 gs);
1555
1556
1557 res = nsgtk_menus_create(gs);
1558 if (res != NSERROR_OK) {
1559 free(gs);
1560 return NULL;
1561 }
1562
1563 /* attach to the list */
1564 if (scaf_list) {
1565 scaf_list->prev = gs;
1566 }
1567 gs->next = scaf_list;
1568 gs->prev = NULL;
1569 scaf_list = gs;
1570
1571 /* finally, show the window. */
1572 gtk_widget_show(GTK_WIDGET(gs->window));
1573
1574 NSLOG(netsurf, INFO, "creation complete");
1575
1576 return gs;
1577 }
1578
1579 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_position_page_info(struct nsgtk_scaffolding * gs,struct nsgtk_pi_window * win)1580 nserror nsgtk_scaffolding_position_page_info(struct nsgtk_scaffolding *gs,
1581 struct nsgtk_pi_window *win)
1582 {
1583 return nsgtk_window_position_page_info(gs->top_level, win);
1584 }
1585
1586 /* exported interface documented in gtk/scaffolding.h */
nsgtk_scaffolding_position_local_history(struct nsgtk_scaffolding * gs)1587 nserror nsgtk_scaffolding_position_local_history(struct nsgtk_scaffolding *gs)
1588 {
1589 return nsgtk_window_position_local_history(gs->top_level);
1590 }
1591