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 /**
20 * \file
21 * implementation of toolbar to control browsing context
22 */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <gtk/gtk.h>
29
30 #include "utils/log.h"
31 #include "utils/messages.h"
32 #include "utils/nsoption.h"
33 #include "utils/file.h"
34 #include "utils/nsurl.h"
35 #include "utils/corestrings.h"
36 #include "desktop/browser_history.h"
37 #include "desktop/searchweb.h"
38 #include "desktop/search.h"
39 #include "desktop/save_complete.h"
40 #include "desktop/save_text.h"
41 #include "desktop/print.h"
42 #include "desktop/hotlist.h"
43 #include "netsurf/content.h"
44 #include "netsurf/browser_window.h"
45 #include "netsurf/keypress.h"
46
47 #include "gtk/toolbar_items.h"
48 #include "gtk/completion.h"
49 #include "gtk/gui.h"
50 #include "gtk/warn.h"
51 #include "gtk/search.h"
52 #include "gtk/throbber.h"
53 #include "gtk/scaffolding.h"
54 #include "gtk/window.h"
55 #include "gtk/compat.h"
56 #include "gtk/resources.h"
57 #include "gtk/schedule.h"
58 #include "gtk/local_history.h"
59 #include "gtk/global_history.h"
60 #include "gtk/viewsource.h"
61 #include "gtk/download.h"
62 #include "gtk/viewdata.h"
63 #include "gtk/tabs.h"
64 #include "gtk/print.h"
65 #include "gtk/layout_pango.h"
66 #include "gtk/preferences.h"
67 #include "gtk/hotlist.h"
68 #include "gtk/cookies.h"
69 #include "gtk/about.h"
70 #include "gtk/gdk.h"
71 #include "gtk/bitmap.h"
72 #include "gtk/page_info.h"
73 #include "gtk/toolbar.h"
74
75 /**
76 * button location indicating button is not to be shown
77 */
78 #define INACTIVE_LOCATION (-1)
79
80 /**
81 * time (in ms) between throbber animation frame updates
82 */
83 #define THROBBER_FRAME_TIME (100)
84
85 /**
86 * the minimum number of columns in the tool store
87 */
88 #define NSGTK_MIN_STORE_COLUMNS 4
89
90 /**
91 * the 'standard' width of a button that makes sufficient of its label visible
92 */
93 #define NSGTK_BUTTON_WIDTH 120
94
95 /**
96 * the 'standard' height of a button that fits as many toolbars as
97 * possible into the store
98 */
99 #define NSGTK_BUTTON_HEIGHT 70
100
101 /**
102 * the 'normal' width of the websearch bar
103 */
104 #define NSGTK_WEBSEARCH_WIDTH 150
105
106 /**
107 * toolbar item context
108 */
109 struct nsgtk_toolbar_item {
110
111 /**
112 * GTK widget in the toolbar
113 */
114 GtkToolItem *button;
115
116 /**
117 * location index in toolbar
118 */
119 int location;
120
121 /**
122 * if the item is currently sensitive in the toolbar
123 */
124 bool sensitivity;
125
126 /**
127 * textural name used in serialising items
128 */
129 const char *name;
130
131 /**
132 * button clicked on toolbar handler
133 */
134 gboolean (*clicked)(GtkWidget *widget, gpointer data);
135
136 /**
137 * handler when dragging from customisation toolbox to toolbar
138 */
139 void *dataplus;
140
141 /**
142 * handler when dragging from toolbar to customisation toolbox
143 */
144 void *dataminus;
145 };
146
147 /**
148 * Location focus state machine
149 *
150 * 1. If we don't care, we're in LFS_IDLE
151 * 2. When we create a new toolbar, we can put it into
152 * LFS_WANT which means that we want the url bar to focus
153 * 3. When we start throbbing if we're in LFS_WANT we move to LFS_THROB
154 * 4. When we stop throbbing, if we're in LFS_THROB we move to LFS_LAST
155 *
156 * While not in LFS_IDLE, if the url bar is updated and we previously had it
157 * fully selected then we reselect it all. If we're in LFS_LAST we move to
158 * LFS_IDLE at that point.
159 */
160 typedef enum {
161 LFS_IDLE, /**< Nothing to do */
162 LFS_WANT, /**< Want focus, will apply */
163 LFS_THROB, /**< Want focus, we have started throbbing */
164 LFS_LAST, /**< Last chance for a focus update */
165 } nsgtk_toolbar_location_focus_state;
166
167 /**
168 * control toolbar context
169 */
170 struct nsgtk_toolbar {
171 /** gtk toolbar widget */
172 GtkToolbar *widget;
173
174 /* toolbar size allocation context */
175 int offset;
176 int toolbarmem;
177 int toolbarbase;
178 int historybase;
179
180 /**
181 * Toolbar item contexts
182 */
183 struct nsgtk_toolbar_item items[PLACEHOLDER_BUTTON];
184
185 /**
186 * Current frame of throbber animation
187 */
188 int throb_frame;
189
190 /**
191 * Web search widget
192 */
193 GtkWidget *webSearchEntry;
194
195 /**
196 * callback to obtain a browser window for navigation
197 */
198 struct browser_window *(*get_bw)(void *ctx);
199
200 /**
201 * context passed to get_bw function
202 */
203 void *get_ctx;
204
205 /**
206 * Location focus state machine, current state
207 */
208 nsgtk_toolbar_location_focus_state loc_focus;
209 };
210
211
212 /**
213 * toolbar cusomisation context
214 */
215 struct nsgtk_toolbar_customisation {
216 /**
217 * first entry is a toolbar widget so a customisation widget
218 * can be cast to toolbar and back.
219 */
220 struct nsgtk_toolbar toolbar;
221
222 /**
223 * The top level container (tabBox)
224 */
225 GtkWidget *container;
226
227 /**
228 * The vertical box into which the available tools are shown
229 */
230 GtkBox *toolbox;
231
232 /**
233 * widget handles for items in the customisation toolbox area
234 */
235 GtkToolItem *items[PLACEHOLDER_BUTTON];
236
237 /**
238 * which item is being dragged
239 */
240 int dragitem; /* currentbutton */
241 /**
242 * true if item being dragged onto toolbar, false if from toolbar
243 */
244 bool dragfrom; /*fromstore */
245
246 };
247
248
249 /* forward declaration */
250 static nserror toolbar_item_create(nsgtk_toolbar_button id,
251 struct nsgtk_toolbar_item *item_out);
252
253
254 /**
255 * returns a string without its underscores
256 *
257 * \param s The string to change.
258 * \param replacespace true to insert a space where there was an underscore
259 * \return The altered string
260 */
remove_underscores(const char * s,bool replacespace)261 static char *remove_underscores(const char *s, bool replacespace)
262 {
263 size_t i, ii, len;
264 char *ret;
265 len = strlen(s);
266 ret = malloc(len + 1);
267 if (ret == NULL) {
268 return NULL;
269 }
270 for (i = 0, ii = 0; i < len; i++) {
271 if (s[i] != '_') {
272 ret[ii++] = s[i];
273 } else if (replacespace) {
274 ret[ii++] = ' ';
275 }
276 }
277 ret[ii] = '\0';
278 return ret;
279 }
280
281
282 /**
283 * create throbber toolbar item widget
284 *
285 * create a gtk entry widget with a completion attached
286 */
287 static GtkToolItem *
make_toolbar_item_throbber(bool sensitivity,bool edit)288 make_toolbar_item_throbber(bool sensitivity, bool edit)
289 {
290 nserror res;
291 GtkToolItem *item;
292 GdkPixbuf *pixbuf;
293 GtkWidget *image;
294
295 res = nsgtk_throbber_get_frame(0, &pixbuf);
296 if (res != NSERROR_OK) {
297 return NULL;
298 }
299
300 if (edit) {
301 const char *msg;
302 msg = messages_get("ToolThrob");
303 item = gtk_tool_button_new(
304 GTK_WIDGET(gtk_image_new_from_pixbuf(pixbuf)),
305 msg);
306 } else {
307 item = gtk_tool_item_new();
308
309 image = gtk_image_new_from_pixbuf(pixbuf);
310 if (image != NULL) {
311 nsgtk_widget_set_alignment(image,
312 GTK_ALIGN_CENTER,
313 GTK_ALIGN_CENTER);
314 nsgtk_widget_set_margins(image, 3, 0);
315
316 gtk_container_add(GTK_CONTAINER(item), image);
317 }
318 }
319 gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity);
320
321 return item;
322 }
323
324
325 /**
326 * create url bar toolbar item widget
327 *
328 * create a gtk entry widget with a completion attached
329 *
330 * \param sensitivity if the entry should be created sensitive to input
331 * \param edit if the entry should be editable
332 */
333 static GtkToolItem *
make_toolbar_item_url_bar(bool sensitivity,bool edit)334 make_toolbar_item_url_bar(bool sensitivity, bool edit)
335 {
336 GtkToolItem *item;
337 GtkWidget *entry;
338 GtkEntryCompletion *completion;
339
340 entry = nsgtk_entry_new();
341
342 if (entry == NULL) {
343 return NULL;
344 }
345 nsgtk_entry_set_icon_from_icon_name(entry,
346 GTK_ENTRY_ICON_PRIMARY,
347 "page-info-internal");
348
349 if (edit) {
350 gtk_entry_set_width_chars(GTK_ENTRY(entry), 9);
351
352 item = gtk_tool_button_new(NULL, "URL");
353 gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), entry);
354 } else {
355 completion = gtk_entry_completion_new();
356 if (completion != NULL) {
357 gtk_entry_set_completion(GTK_ENTRY(entry), completion);
358 }
359
360 item = gtk_tool_item_new();
361 if (item == NULL) {
362 return NULL;
363 }
364
365 gtk_container_add(GTK_CONTAINER(item), entry);
366 gtk_tool_item_set_expand(item, TRUE);
367
368 }
369 gtk_widget_set_sensitive(GTK_WIDGET(item), TRUE);
370 gtk_widget_set_sensitive(GTK_WIDGET(entry), sensitivity);
371
372 return item;
373 }
374
375
376 /**
377 * create web search toolbar item widget
378 */
379 static GtkToolItem *
make_toolbar_item_websearch(bool sensitivity,bool edit)380 make_toolbar_item_websearch(bool sensitivity, bool edit)
381 {
382 GtkToolItem *item;
383 nserror res;
384 GtkWidget *entry;
385 struct bitmap *bitmap;
386 GdkPixbuf *pixbuf = NULL;
387
388 res = search_web_get_provider_bitmap(&bitmap);
389 if ((res == NSERROR_OK) && (bitmap != NULL)) {
390 pixbuf = nsgdk_pixbuf_get_from_surface(bitmap->surface, 32, 32);
391 }
392
393 entry = nsgtk_entry_new();
394
395 if (entry == NULL) {
396 return NULL;
397 }
398
399 if (pixbuf != NULL) {
400 nsgtk_entry_set_icon_from_pixbuf(entry,
401 GTK_ENTRY_ICON_PRIMARY,
402 pixbuf);
403 g_object_unref(pixbuf);
404 } else {
405 nsgtk_entry_set_icon_from_icon_name(entry,
406 GTK_ENTRY_ICON_PRIMARY,
407 NSGTK_STOCK_INFO);
408 }
409
410 if (edit) {
411 gtk_entry_set_width_chars(GTK_ENTRY(entry), 9);
412
413 item = gtk_tool_button_new(NULL, "Web Search");
414 gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item),
415 entry);
416 } else {
417 gtk_widget_set_size_request(entry, NSGTK_WEBSEARCH_WIDTH, -1);
418
419 item = gtk_tool_item_new();
420 if (item == NULL) {
421 return NULL;
422 }
423
424 gtk_container_add(GTK_CONTAINER(item), entry);
425 }
426 gtk_widget_set_sensitive(GTK_WIDGET(item), TRUE);
427 gtk_widget_set_sensitive(GTK_WIDGET(entry), sensitivity);
428
429 return item;
430 }
431
432
433 /**
434 * create local history toolbar item widget
435 */
436 static GtkToolItem *
make_toolbar_item_history(bool sensitivity,bool edit)437 make_toolbar_item_history(bool sensitivity, bool edit)
438 {
439 GtkToolItem *item;
440 const char *msg = "H";
441 char *label = NULL;
442
443 if (edit) {
444 msg = messages_get("gtkLocalHistory");
445 }
446 label = remove_underscores(msg, false);
447 item = gtk_tool_button_new(NULL, label);
448 if (label != NULL) {
449 free(label);
450 }
451 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "local-history");
452
453 /* set history widget minimum width */
454 gtk_widget_set_size_request(GTK_WIDGET(item), 20, -1);
455 gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity);
456
457 return item;
458 }
459
460
461 /**
462 * create generic button toolbar item widget
463 */
464 static GtkToolItem *
make_toolbar_item_button(const char * labelmsg,const char * iconname,bool sensitivity,bool edit)465 make_toolbar_item_button(const char *labelmsg,
466 const char *iconname,
467 bool sensitivity,
468 bool edit)
469 {
470 GtkToolItem *item;
471 char *label = NULL;
472
473 label = remove_underscores(messages_get(labelmsg), false);
474
475 item = gtk_tool_button_new(NULL, label);
476 if (label != NULL) {
477 free(label);
478 }
479
480 if (item != NULL) {
481 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), iconname);
482
483 gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity);
484 if (edit) {
485 nsgtk_widget_set_margins(GTK_WIDGET(item), 0, 0);
486 }
487 }
488
489 return item;
490 }
491
492
493 /**
494 * widget factory for creation of toolbar item widgets
495 *
496 * \param i the id of the widget
497 * \param theme the theme to make the widgets from
498 * \return gtk widget
499 */
500 static GtkToolItem *
make_toolbar_item(nsgtk_toolbar_button itemid,bool sensitivity)501 make_toolbar_item(nsgtk_toolbar_button itemid, bool sensitivity)
502 {
503 GtkToolItem *toolitem = NULL;
504
505 switch(itemid) {
506 #define TOOLBAR_ITEM_y(identifier, label, iconame)
507 #define TOOLBAR_ITEM_n(identifier, label, iconame)
508 #define TOOLBAR_ITEM_t(identifier, label, iconame) \
509 case identifier: \
510 toolitem = make_toolbar_item_button(#label, iconame, sensitivity, false); \
511 break;
512 #define TOOLBAR_ITEM_b(identifier, label, iconame) \
513 case identifier: \
514 toolitem = make_toolbar_item_button(#label, iconame, sensitivity, false); \
515 break;
516 #define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate, label, iconame) \
517 TOOLBAR_ITEM_ ## clicked(identifier, label, iconame)
518
519 #include "gtk/toolbar_items.h"
520
521 #undef TOOLBAR_ITEM_t
522 #undef TOOLBAR_ITEM_b
523 #undef TOOLBAR_ITEM_n
524 #undef TOOLBAR_ITEM_y
525 #undef TOOLBAR_ITEM
526
527 case HISTORY_BUTTON:
528 toolitem = make_toolbar_item_history(sensitivity, false);
529 break;
530
531 case URL_BAR_ITEM:
532 toolitem = make_toolbar_item_url_bar(sensitivity, false);
533 break;
534
535 case THROBBER_ITEM:
536 toolitem = make_toolbar_item_throbber(sensitivity, false);
537 break;
538
539 case WEBSEARCH_ITEM:
540 toolitem = make_toolbar_item_websearch(sensitivity, false);
541 break;
542
543 default:
544 break;
545
546 }
547 return toolitem;
548 }
549
550
551 /**
552 * widget factory for creation of toolbar item widgets for the toolbox
553 *
554 * \param itemid the id of the widget
555 * \return gtk tool item widget
556 */
557 static GtkToolItem *
make_toolbox_item(nsgtk_toolbar_button itemid,bool bar)558 make_toolbox_item(nsgtk_toolbar_button itemid, bool bar)
559 {
560 GtkToolItem *toolitem = NULL;
561
562 switch(itemid) {
563 #define TOOLBAR_ITEM_y(identifier, label, iconame)
564 #define TOOLBAR_ITEM_n(identifier, label, iconame)
565 #define TOOLBAR_ITEM_t(identifier, label, iconame) \
566 case identifier: \
567 if (bar) { \
568 toolitem = make_toolbar_item_button(#label, iconame, true, true); \
569 } \
570 break;
571 #define TOOLBAR_ITEM_b(identifier, label, iconame) \
572 case identifier: \
573 toolitem = make_toolbar_item_button(#label, iconame, true, true); \
574 break;
575 #define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate, label, iconame) \
576 TOOLBAR_ITEM_ ## clicked(identifier, label, iconame)
577
578 #include "gtk/toolbar_items.h"
579
580 #undef TOOLBAR_ITEM_t
581 #undef TOOLBAR_ITEM_b
582 #undef TOOLBAR_ITEM_n
583 #undef TOOLBAR_ITEM_y
584 #undef TOOLBAR_ITEM
585
586 case HISTORY_BUTTON:
587 toolitem = make_toolbar_item_history(true, true);
588 break;
589
590 case URL_BAR_ITEM:
591 toolitem = make_toolbar_item_url_bar(false, true);
592 break;
593
594 case THROBBER_ITEM:
595 toolitem = make_toolbar_item_throbber(true, true);
596 break;
597
598 case WEBSEARCH_ITEM:
599 toolitem = make_toolbar_item_websearch(false, true);
600 break;
601
602 default:
603 break;
604
605 }
606 return toolitem;
607 }
608
609
610 /**
611 * target entry for drag source
612 */
613 static GtkTargetEntry target_entry = {
614 (char *)"nsgtk_button_data",
615 GTK_TARGET_SAME_APP,
616 0
617 };
618
619
620 /**
621 * find the toolbar item with a given location.
622 *
623 * \param tb the toolbar instance
624 * \param locaction the location to search for
625 * \return the item id for a location
626 */
627 static nsgtk_toolbar_button
itemid_from_location(struct nsgtk_toolbar * tb,int location)628 itemid_from_location(struct nsgtk_toolbar *tb, int location)
629 {
630 int iidx;
631 for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
632 if (tb->items[iidx].location == location) {
633 break;
634 }
635 }
636 return iidx;
637 }
638
639
640 /**
641 * save toolbar settings to file
642 */
643 static nserror
nsgtk_toolbar_customisation_save(struct nsgtk_toolbar * tb)644 nsgtk_toolbar_customisation_save(struct nsgtk_toolbar *tb)
645 {
646 int iidx; /* item index */
647 char *order; /* item ordering */
648 char *start; /* start of next item name to be output */
649 int orderlen = 0; /* length of item ordering */
650 nsgtk_toolbar_button itemid;
651 int location;
652 char *choices = NULL;
653
654 for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
655 if (tb->items[iidx].location != INACTIVE_LOCATION) {
656 orderlen += strlen(tb->items[iidx].name);
657 orderlen++; /* allow for separator */
658 }
659 }
660
661 /* ensure there are some items to store */
662 if (orderlen == 0) {
663 return NSERROR_INVALID;
664 }
665
666 order = malloc(orderlen);
667 if (order == NULL) {
668 return NSERROR_NOMEM;
669 }
670
671 start = order;
672
673 for (location = BACK_BUTTON;
674 location < PLACEHOLDER_BUTTON;
675 location++) {
676 itemid = itemid_from_location(tb, location);
677 if (itemid == PLACEHOLDER_BUTTON) {
678 /* no more filled locations */
679 break;
680 }
681 start += snprintf(start,
682 orderlen - (start - order),
683 "%s/",
684 tb->items[itemid].name);
685
686 if ((start - order) >= orderlen) {
687 break;
688 }
689 }
690
691 order[orderlen - 1] = 0;
692
693 nsoption_set_charp(toolbar_items, order);
694
695 /* ensure choices are saved */
696 netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
697 if (choices != NULL) {
698 nsoption_write(choices, NULL, NULL);
699 free(choices);
700 }
701
702 return NSERROR_OK;
703 }
704
705
706 /**
707 * connect signals to a toolbar item in a customisation toolbar
708 *
709 * \param tb The toolbar
710 * \param itemid The item id within to toolbar to connect
711 * \param NSERROR_OK on success
712 */
713 static nserror
toolbar_item_connect_signals(struct nsgtk_toolbar * tb,int itemid)714 toolbar_item_connect_signals(struct nsgtk_toolbar *tb, int itemid)
715 {
716 /* set toolbar items to be a drag source */
717 gtk_tool_item_set_use_drag_window(tb->items[itemid].button, TRUE);
718 gtk_drag_source_set(GTK_WIDGET(tb->items[itemid].button),
719 GDK_BUTTON1_MASK,
720 &target_entry,
721 1,
722 GDK_ACTION_COPY);
723 g_signal_connect(tb->items[itemid].button,
724 "drag-data-get",
725 G_CALLBACK(tb->items[itemid].dataminus),
726 tb);
727 return NSERROR_OK;
728 }
729
730
731 /**
732 * customisation container handler for drag drop signal
733 *
734 * called when a widget is dropped onto the store window
735 */
736 static gboolean
customisation_container_drag_drop_cb(GtkWidget * widget,GdkDragContext * gdc,gint x,gint y,guint time,gpointer data)737 customisation_container_drag_drop_cb(GtkWidget *widget,
738 GdkDragContext *gdc,
739 gint x, gint y,
740 guint time,
741 gpointer data)
742 {
743 struct nsgtk_toolbar_customisation *tbc;
744 tbc = (struct nsgtk_toolbar_customisation *)data;
745 int location;
746 int itemid;
747
748 if ((tbc->dragfrom) || (tbc->dragitem == -1)) {
749 tbc->dragitem = -1;
750 return FALSE;
751 }
752
753 if (tbc->toolbar.items[tbc->dragitem].location == INACTIVE_LOCATION) {
754 tbc->dragitem = -1;
755 gtk_drag_finish(gdc, TRUE, TRUE, time);
756 return FALSE;
757
758 }
759
760 /* update the locations for all the subsequent toolbar items */
761 for (location = tbc->toolbar.items[tbc->dragitem].location;
762 location < PLACEHOLDER_BUTTON;
763 location++) {
764 itemid = itemid_from_location(&tbc->toolbar, location);
765 if (itemid == PLACEHOLDER_BUTTON) {
766 break;
767 }
768 tbc->toolbar.items[itemid].location--;
769 }
770
771 /* remove existing item */
772 tbc->toolbar.items[tbc->dragitem].location = -1;
773 gtk_container_remove(GTK_CONTAINER(tbc->toolbar.widget),
774 GTK_WIDGET(tbc->toolbar.items[tbc->dragitem].button));
775
776 tbc->dragitem = -1;
777 gtk_drag_finish(gdc, TRUE, TRUE, time);
778 return FALSE;
779 }
780
781
782 /**
783 * customisation container handler for drag motion signal
784 *
785 * called when hovering above the store
786 */
787 static gboolean
customisation_container_drag_motion_cb(GtkWidget * widget,GdkDragContext * gdc,gint x,gint y,guint time,gpointer data)788 customisation_container_drag_motion_cb(GtkWidget *widget,
789 GdkDragContext *gdc,
790 gint x, gint y,
791 guint time,
792 gpointer data)
793 {
794 return FALSE;
795 }
796
797
798 /**
799 * customisation toolbar handler for drag drop signal
800 *
801 * called when a widget is dropped onto the toolbar
802 */
803 static gboolean
customisation_toolbar_drag_drop_cb(GtkWidget * widget,GdkDragContext * gdc,gint x,gint y,guint time,gpointer data)804 customisation_toolbar_drag_drop_cb(GtkWidget *widget,
805 GdkDragContext *gdc,
806 gint x,
807 gint y,
808 guint time,
809 gpointer data)
810 {
811 struct nsgtk_toolbar_customisation *tbc;
812 tbc = (struct nsgtk_toolbar_customisation *)data;
813 gint position; /* drop position in toolbar */
814 int location;
815 int itemid;
816 struct nsgtk_toolbar_item *dragitem; /* toolbar item being dragged */
817
818 position = gtk_toolbar_get_drop_index(tbc->toolbar.widget, x, y);
819 if (tbc->dragitem == -1) {
820 return TRUE;
821 }
822
823 /* pure conveiance variable */
824 dragitem = &tbc->toolbar.items[tbc->dragitem];
825
826 /* deal with replacing existing item in toolbar */
827 if (dragitem->location != INACTIVE_LOCATION) {
828 if (dragitem->location < position) {
829 position--;
830 }
831
832 /* update the locations for all the subsequent toolbar items */
833 for (location = dragitem->location;
834 location < PLACEHOLDER_BUTTON;
835 location++) {
836 itemid = itemid_from_location(&tbc->toolbar, location);
837 if (itemid == PLACEHOLDER_BUTTON) {
838 break;
839 }
840 tbc->toolbar.items[itemid].location--;
841 }
842
843 /* remove existing item */
844 dragitem->location = INACTIVE_LOCATION;
845 gtk_container_remove(GTK_CONTAINER(tbc->toolbar.widget),
846 GTK_WIDGET(dragitem->button));
847 }
848
849
850 dragitem->button = make_toolbox_item(tbc->dragitem, true);
851
852 if (dragitem->button == NULL) {
853 nsgtk_warning("NoMemory", 0);
854 return TRUE;
855 }
856
857 /* update locations */
858 for (location = PLACEHOLDER_BUTTON; location >= position; location--) {
859 itemid = itemid_from_location(&tbc->toolbar, location);
860 if (itemid != PLACEHOLDER_BUTTON) {
861 tbc->toolbar.items[itemid].location++;
862 }
863 }
864 dragitem->location = position;
865
866 gtk_toolbar_insert(tbc->toolbar.widget,
867 dragitem->button,
868 dragitem->location);
869
870 toolbar_item_connect_signals(&tbc->toolbar, tbc->dragitem);
871 gtk_widget_show_all(GTK_WIDGET(dragitem->button));
872 tbc->dragitem = -1;
873 return TRUE;
874 }
875
876
877 /**
878 * customisation toolbar handler for drag data received signal
879 *
880 * connected to toolbutton drop; perhaps one day it'll work properly
881 * so it may replace the global current_button
882 */
883 static gboolean
customisation_toolbar_drag_data_received_cb(GtkWidget * widget,GdkDragContext * gdc,gint x,gint y,GtkSelectionData * selection,guint info,guint time,gpointer data)884 customisation_toolbar_drag_data_received_cb(GtkWidget *widget,
885 GdkDragContext *gdc,
886 gint x,
887 gint y,
888 GtkSelectionData *selection,
889 guint info,
890 guint time,
891 gpointer data)
892 {
893 return FALSE;
894 }
895
896
897 /**
898 * customisation toolbar handler for drag motion signal
899 *
900 * called when hovering an item above the toolbar
901 */
902 static gboolean
customisation_toolbar_drag_motion_cb(GtkWidget * widget,GdkDragContext * gdc,gint x,gint y,guint time,gpointer data)903 customisation_toolbar_drag_motion_cb(GtkWidget *widget,
904 GdkDragContext *gdc,
905 gint x,
906 gint y,
907 guint time,
908 gpointer data)
909 {
910 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
911 GtkToolItem *item;
912 gint position; /* position in toolbar */
913
914 item = gtk_tool_button_new(NULL, NULL);
915 position = gtk_toolbar_get_drop_index(tb->widget, x, y);
916
917 gtk_toolbar_set_drop_highlight_item(tb->widget, item, position);
918
919 return FALSE; /* drag not in drop zone */
920 }
921
922
923 /**
924 * customisation toolbar handler for drag leave signal
925 *
926 * called when hovering stops
927 */
928 static void
customisation_toolbar_drag_leave_cb(GtkWidget * widget,GdkDragContext * gdc,guint time,gpointer data)929 customisation_toolbar_drag_leave_cb(GtkWidget *widget,
930 GdkDragContext *gdc,
931 guint time,
932 gpointer data)
933 {
934 gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(widget), NULL, 0);
935 }
936
937
938 /**
939 * create a new browser window
940 *
941 * creates a browser window with default url depending on user choices.
942 *
943 * \param bw The browser window to pass for existing window/
944 * \param intab true if the new window should be in a tab else false
945 * for new window.
946 * \return NSERROR_OK on success else error code.
947 */
948 static nserror
nsgtk_browser_window_create(struct browser_window * bw,bool intab)949 nsgtk_browser_window_create(struct browser_window *bw, bool intab)
950 {
951 nserror res = NSERROR_OK;
952 nsurl *url = NULL;
953 int flags = BW_CREATE_HISTORY | BW_CREATE_FOREGROUND | BW_CREATE_FOCUS_LOCATION;
954
955 if (intab) {
956 flags |= BW_CREATE_TAB;
957 }
958
959 if (!nsoption_bool(new_blank)) {
960 const char *addr;
961 if (nsoption_charp(homepage_url) != NULL) {
962 addr = nsoption_charp(homepage_url);
963 } else {
964 addr = NETSURF_HOMEPAGE;
965 }
966 res = nsurl_create(addr, &url);
967 }
968
969 if (res == NSERROR_OK) {
970 res = browser_window_create(flags, url, NULL, bw, NULL);
971 }
972
973 if (url != NULL) {
974 nsurl_unref(url);
975 }
976
977 return res;
978 }
979
980
981 /**
982 * Apply the user toolbar button settings from configuration
983 *
984 * GTK specific user option string is a set of fields arranged as
985 * [itemreference];[itemlocation]|[itemreference];[itemlocation]| etc
986 *
987 * \param tb The toolbar to apply customisation to
988 * \param NSERROR_OK on success else error code.
989 */
990 static nserror
apply_user_button_customisation(struct nsgtk_toolbar * tb)991 apply_user_button_customisation(struct nsgtk_toolbar *tb)
992 {
993 const char *tbitems; /* item order user config */
994 const char *start;
995 const char *end;
996 int iidx; /* item index */
997 int location = 0; /* location index */
998
999 /* set all button locations to inactive */
1000 for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
1001 tb->items[iidx].location = INACTIVE_LOCATION;
1002 }
1003
1004 tbitems = nsoption_charp(toolbar_items);
1005 if (tbitems == NULL) {
1006 tbitems = "";
1007 }
1008
1009 end = tbitems;
1010 while (*end != 0) {
1011 start = end;
1012 while ((*end != 0) && (*end !='/')) {
1013 end++;
1014 }
1015
1016 for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
1017 if (((ssize_t)strlen(tb->items[iidx].name) == (end - start)) &&
1018 (strncmp(tb->items[iidx].name, start, end - start) == 0)) {
1019 tb->items[iidx].location = location++;
1020 break;
1021 }
1022 }
1023
1024 if (*end == '/') {
1025 end++;
1026 }
1027 }
1028
1029 if (location == 0) {
1030 /* completely failed to create any buttons so use defaults */
1031 tb->items[BACK_BUTTON].location = location++;
1032 tb->items[HISTORY_BUTTON].location = location++;
1033 tb->items[FORWARD_BUTTON].location = location++;
1034 tb->items[RELOADSTOP_BUTTON].location = location++;
1035 tb->items[URL_BAR_ITEM].location = location++;
1036 tb->items[WEBSEARCH_ITEM].location = location++;
1037 tb->items[OPENMENU_BUTTON].location = location++;
1038 tb->items[THROBBER_ITEM].location = location++;
1039 }
1040
1041
1042 return NSERROR_OK;
1043 }
1044
1045
1046 /**
1047 * callback function to remove a widget from a container
1048 */
container_remove_widget(GtkWidget * widget,gpointer data)1049 static void container_remove_widget(GtkWidget *widget, gpointer data)
1050 {
1051 GtkContainer *container = GTK_CONTAINER(data);
1052 gtk_container_remove(container, widget);
1053 }
1054
1055
1056 /**
1057 * populates a toolbar with widgets in correct order
1058 *
1059 * \param tb toolbar
1060 * \return NSERROR_OK on success else error code.
1061 */
populate_gtk_toolbar_widget(struct nsgtk_toolbar * tb)1062 static nserror populate_gtk_toolbar_widget(struct nsgtk_toolbar *tb)
1063 {
1064 int location; /* location index */
1065 int itemid;
1066
1067 /* clear the toolbar container of all widgets */
1068 gtk_container_foreach(GTK_CONTAINER(tb->widget),
1069 container_remove_widget,
1070 tb->widget);
1071
1072 /* add widgets to toolbar */
1073 for (location = 0; location < PLACEHOLDER_BUTTON; location++) {
1074 itemid = itemid_from_location(tb, location);
1075 if (itemid == PLACEHOLDER_BUTTON) {
1076 break;
1077 }
1078 tb->items[itemid].button =
1079 make_toolbar_item(itemid,
1080 tb->items[itemid].sensitivity);
1081
1082 gtk_toolbar_insert(tb->widget,
1083 tb->items[itemid].button,
1084 location);
1085 }
1086
1087 gtk_widget_show_all(GTK_WIDGET(tb->widget));
1088
1089 return NSERROR_OK;
1090 }
1091
1092
1093 /**
1094 * populates the customization toolbar with widgets in correct order
1095 *
1096 * \param tb toolbar
1097 * \return NSERROR_OK on success else error code.
1098 */
customisation_toolbar_populate(struct nsgtk_toolbar * tb)1099 static nserror customisation_toolbar_populate(struct nsgtk_toolbar *tb)
1100 {
1101 int location; /* location index */
1102 int itemid;
1103
1104 /* clear the toolbar container of all widgets */
1105 gtk_container_foreach(GTK_CONTAINER(tb->widget),
1106 container_remove_widget,
1107 tb->widget);
1108
1109 /* add widgets to toolbar */
1110 for (location = 0; location < PLACEHOLDER_BUTTON; location++) {
1111 itemid = itemid_from_location(tb, location);
1112 if (itemid == PLACEHOLDER_BUTTON) {
1113 break;
1114 }
1115 tb->items[itemid].button = make_toolbox_item(itemid, true);
1116
1117 gtk_toolbar_insert(tb->widget,
1118 tb->items[itemid].button,
1119 location);
1120 }
1121
1122 gtk_widget_show_all(GTK_WIDGET(tb->widget));
1123
1124 return NSERROR_OK;
1125 }
1126
1127
1128 /**
1129 * find the toolbar item with a given gtk widget.
1130 *
1131 * \param tb the toolbar instance
1132 * \param toolitem the tool item widget to search for
1133 * \return the item id matching the widget
1134 */
1135 static nsgtk_toolbar_button
itemid_from_gtktoolitem(struct nsgtk_toolbar * tb,GtkToolItem * toolitem)1136 itemid_from_gtktoolitem(struct nsgtk_toolbar *tb, GtkToolItem *toolitem)
1137 {
1138 int iidx;
1139 for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
1140 if ((tb->items[iidx].location != INACTIVE_LOCATION) &&
1141 (tb->items[iidx].button == toolitem)) {
1142 break;
1143 }
1144 }
1145 return iidx;
1146 }
1147
1148
1149 /**
1150 * set a toolbar items sensitivity
1151 *
1152 * note this does not set menu items sensitivity
1153 */
1154 static nserror
set_item_sensitivity(struct nsgtk_toolbar_item * item,bool sensitivity)1155 set_item_sensitivity(struct nsgtk_toolbar_item *item, bool sensitivity)
1156 {
1157 if (item->sensitivity != sensitivity) {
1158 /* item requires sensitivity changing */
1159 item->sensitivity = sensitivity;
1160
1161 if ((item->location != -1) && (item->button != NULL)) {
1162 gtk_widget_set_sensitive(GTK_WIDGET(item->button),
1163 item->sensitivity);
1164 }
1165 }
1166
1167 return NSERROR_OK;
1168 }
1169
1170
1171 /**
1172 * set an item to its alternative action
1173 *
1174 * this is currently only used for the stop/reload button where we
1175 * also reuse the item sensitivity for the state indicator.
1176 *
1177 * \param tb the toolbar instance
1178 */
set_item_action(struct nsgtk_toolbar * tb,int itemid,bool alt)1179 static nserror set_item_action(struct nsgtk_toolbar *tb, int itemid, bool alt)
1180 {
1181 const char *iconname;
1182 char *label = NULL;
1183
1184 if (itemid != RELOADSTOP_BUTTON) {
1185 return NSERROR_INVALID;
1186 }
1187 if (tb->items[itemid].location == -1) {
1188 return NSERROR_OK;
1189 }
1190 tb->items[itemid].sensitivity = alt;
1191
1192 if (tb->items[itemid].button == NULL) {
1193 return NSERROR_INVALID;
1194 }
1195
1196 if (tb->items[itemid].sensitivity) {
1197 iconname = NSGTK_STOCK_REFRESH;
1198 label = remove_underscores(messages_get("Reload"), false);
1199
1200 } else {
1201 iconname = NSGTK_STOCK_STOP;
1202 label = remove_underscores(messages_get("gtkStop"), false);
1203
1204 }
1205 gtk_tool_button_set_label(GTK_TOOL_BUTTON(tb->items[itemid].button),
1206 label);
1207
1208 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tb->items[itemid].button),
1209 iconname);
1210
1211 gtk_widget_set_sensitive(GTK_WIDGET(tb->items[itemid].button), TRUE);
1212
1213 if (label != NULL) {
1214 free(label);
1215 }
1216
1217 return NSERROR_OK;
1218 }
1219
1220
1221 /**
1222 * cause the toolbar browsing context to navigate to a new url.
1223 *
1224 * \param tb the toolbar context.
1225 * \param urltxt The url string.
1226 * \return NSERROR_OK on success else appropriate error code.
1227 */
1228 static nserror
toolbar_navigate_to_url(struct nsgtk_toolbar * tb,const char * urltxt)1229 toolbar_navigate_to_url(struct nsgtk_toolbar *tb, const char *urltxt)
1230 {
1231 struct browser_window *bw;
1232 nsurl *url;
1233 nserror res;
1234
1235 res = nsurl_create(urltxt, &url);
1236 if (res != NSERROR_OK) {
1237 return res;
1238 }
1239
1240 bw = tb->get_bw(tb->get_ctx);
1241
1242 res = browser_window_navigate(bw,
1243 url,
1244 NULL,
1245 BW_NAVIGATE_HISTORY,
1246 NULL,
1247 NULL,
1248 NULL);
1249 nsurl_unref(url);
1250
1251 return res;
1252 }
1253
1254
1255 /**
1256 * run a gtk file chooser as a save dialog to obtain a path
1257 */
1258 static nserror
nsgtk_saveas_dialog(struct browser_window * bw,const char * title,GtkWindow * parent,bool folder,gchar ** path_out)1259 nsgtk_saveas_dialog(struct browser_window *bw,
1260 const char *title,
1261 GtkWindow *parent,
1262 bool folder,
1263 gchar **path_out)
1264 {
1265 nserror res;
1266 GtkWidget *fc; /* file chooser widget */
1267 GtkFileChooserAction action;
1268 char *path; /* proposed path */
1269
1270 if (!browser_window_has_content(bw)) {
1271 /* cannot save a page with no content */
1272 return NSERROR_INVALID;
1273 }
1274
1275 if (folder) {
1276 action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
1277 } else {
1278 action = GTK_FILE_CHOOSER_ACTION_SAVE;
1279 }
1280
1281 fc = gtk_file_chooser_dialog_new(title,
1282 parent,
1283 action,
1284 NSGTK_STOCK_CANCEL,
1285 GTK_RESPONSE_CANCEL,
1286 NSGTK_STOCK_SAVE,
1287 GTK_RESPONSE_ACCEPT,
1288 NULL);
1289
1290 /* set a default file name */
1291 res = nsurl_nice(browser_window_access_url(bw), &path, false);
1292 if (res != NSERROR_OK) {
1293 path = strdup(messages_get("SaveText"));
1294 if (path == NULL) {
1295 gtk_widget_destroy(fc);
1296 return NSERROR_NOMEM;
1297 }
1298 }
1299
1300 if ((!folder) || (access(path, F_OK) != 0)) {
1301 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), path);
1302 }
1303 free(path);
1304
1305 /* confirm overwriting */
1306 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc), TRUE);
1307
1308 /* run the dialog to let user select path */
1309 if (gtk_dialog_run(GTK_DIALOG(fc)) != GTK_RESPONSE_ACCEPT) {
1310 gtk_widget_destroy(fc);
1311 return NSERROR_NOT_FOUND;
1312 }
1313
1314 *path_out = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1315
1316 gtk_widget_destroy(fc);
1317
1318 return NSERROR_OK;
1319 }
1320
1321
1322 /**
1323 * connect all signals to widgets in a customisation
1324 */
1325 static nserror
toolbar_customisation_connect_signals(struct nsgtk_toolbar * tb)1326 toolbar_customisation_connect_signals(struct nsgtk_toolbar *tb)
1327 {
1328 int iidx;
1329
1330 for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
1331 /* skip inactive items in toolbar */
1332 if (tb->items[iidx].location != INACTIVE_LOCATION) {
1333 toolbar_item_connect_signals(tb, iidx);
1334 }
1335 }
1336
1337 /* add move button listeners */
1338 g_signal_connect(tb->widget,
1339 "drag-drop",
1340 G_CALLBACK(customisation_toolbar_drag_drop_cb),
1341 tb);
1342 g_signal_connect(tb->widget,
1343 "drag-data-received",
1344 G_CALLBACK(customisation_toolbar_drag_data_received_cb),
1345 tb);
1346 g_signal_connect(tb->widget,
1347 "drag-motion",
1348 G_CALLBACK(customisation_toolbar_drag_motion_cb),
1349 tb);
1350 g_signal_connect(tb->widget,
1351 "drag-leave",
1352 G_CALLBACK(customisation_toolbar_drag_leave_cb),
1353 tb);
1354
1355 /* set data types */
1356 gtk_drag_dest_set(GTK_WIDGET(tb->widget),
1357 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
1358 &target_entry,
1359 1,
1360 GDK_ACTION_COPY);
1361
1362 return NSERROR_OK;
1363 }
1364
1365
1366 static void
item_size_allocate_cb(GtkWidget * widget,GdkRectangle * alloc,gpointer user_data)1367 item_size_allocate_cb(GtkWidget *widget,
1368 GdkRectangle *alloc,
1369 gpointer user_data)
1370 {
1371 if (alloc->width > NSGTK_BUTTON_WIDTH) {
1372 alloc->width = NSGTK_BUTTON_WIDTH;
1373 }
1374 if (alloc->height > NSGTK_BUTTON_HEIGHT) {
1375 alloc->height = NSGTK_BUTTON_HEIGHT;
1376 }
1377 gtk_widget_set_allocation(widget, alloc);
1378 }
1379
1380
1381 /**
1382 * add a row to a toolbar customisation toolbox
1383 *
1384 * \param tbc The toolbar customisation context
1385 * \param startitem The item index of the beginning of the row
1386 * \param enditem The item index of the beginning of the next row
1387 * \return NSERROR_OK on successs else error
1388 */
1389 static nserror
add_toolbox_row(struct nsgtk_toolbar_customisation * tbc,int startitem,int enditem)1390 add_toolbox_row(struct nsgtk_toolbar_customisation *tbc,
1391 int startitem,
1392 int enditem)
1393 {
1394 GtkToolbar *rowbar;
1395 int iidx;
1396
1397 rowbar = GTK_TOOLBAR(gtk_toolbar_new());
1398 if (rowbar == NULL) {
1399 return NSERROR_NOMEM;
1400 }
1401
1402 gtk_toolbar_set_style(rowbar, GTK_TOOLBAR_BOTH);
1403 gtk_toolbar_set_icon_size(rowbar, GTK_ICON_SIZE_LARGE_TOOLBAR);
1404 gtk_box_pack_start(tbc->toolbox, GTK_WIDGET(rowbar), FALSE, FALSE, 0);
1405
1406 for (iidx = startitem; iidx < enditem; iidx++) {
1407 if (tbc->items[iidx] == NULL) {
1408 /* skip any widgets that failed to initialise */
1409 continue;
1410 }
1411 gtk_widget_set_size_request(GTK_WIDGET(tbc->items[iidx]),
1412 NSGTK_BUTTON_WIDTH,
1413 NSGTK_BUTTON_HEIGHT);
1414 gtk_tool_item_set_use_drag_window(tbc->items[iidx], TRUE);
1415 gtk_drag_source_set(GTK_WIDGET(tbc->items[iidx]),
1416 GDK_BUTTON1_MASK,
1417 &target_entry,
1418 1,
1419 GDK_ACTION_COPY);
1420 g_signal_connect(tbc->items[iidx],
1421 "drag-data-get",
1422 G_CALLBACK(tbc->toolbar.items[iidx].dataplus),
1423 &tbc->toolbar);
1424 g_signal_connect(tbc->items[iidx],
1425 "size-allocate",
1426 G_CALLBACK(item_size_allocate_cb),
1427 NULL);
1428 gtk_toolbar_insert(rowbar, tbc->items[iidx], -1);
1429 }
1430 return NSERROR_OK;
1431 }
1432
1433
1434 /**
1435 * creates widgets in customisation toolbox
1436 *
1437 * \param tbc The toolbar customisation context
1438 * \param width The width to layout the toolbox to
1439 * \return NSERROR_OK on success else error code.
1440 */
1441 static nserror
toolbar_customisation_create_toolbox(struct nsgtk_toolbar_customisation * tbc,int width)1442 toolbar_customisation_create_toolbox(struct nsgtk_toolbar_customisation *tbc,
1443 int width)
1444 {
1445 int columns; /* number of items in a single row */
1446 int curcol; /* current column in creation */
1447 int iidx; /* item index */
1448 int startidx; /* index of item at start of row */
1449
1450 /* ensure there are a minimum number of items per row */
1451 columns = width / NSGTK_BUTTON_WIDTH;
1452 if (columns < NSGTK_MIN_STORE_COLUMNS) {
1453 columns = NSGTK_MIN_STORE_COLUMNS;
1454 }
1455
1456 curcol = 0;
1457 for (iidx = startidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
1458 if (curcol >= columns) {
1459 add_toolbox_row(tbc, startidx, iidx);
1460 curcol = 0;
1461 startidx = iidx;
1462 }
1463 tbc->items[iidx] = make_toolbox_item(iidx, false);
1464 if (tbc->items[iidx] != NULL) {
1465 curcol++;
1466 }
1467 }
1468 if (curcol > 0) {
1469 add_toolbox_row(tbc, startidx, iidx);
1470 }
1471
1472 return NSERROR_OK;
1473 }
1474
1475
1476 /**
1477 * update toolbar in customisation to user settings
1478 */
1479 static nserror
customisation_toolbar_update(struct nsgtk_toolbar_customisation * tbc)1480 customisation_toolbar_update(struct nsgtk_toolbar_customisation *tbc)
1481 {
1482 nserror res;
1483
1484 res = apply_user_button_customisation(&tbc->toolbar);
1485 if (res != NSERROR_OK) {
1486 return res;
1487 }
1488
1489 /* populate toolbar widget */
1490 res = customisation_toolbar_populate(&tbc->toolbar);
1491 if (res != NSERROR_OK) {
1492 return res;
1493 }
1494
1495 /* ensure icon sizes and text labels on toolbar are set */
1496 res = nsgtk_toolbar_restyle(&tbc->toolbar);
1497 if (res != NSERROR_OK) {
1498 return res;
1499 }
1500
1501 /* attach handlers to toolbar widgets */
1502 res = toolbar_customisation_connect_signals(&tbc->toolbar);
1503 if (res != NSERROR_OK) {
1504 return res;
1505 }
1506
1507 return NSERROR_OK;
1508 }
1509
1510
1511 /**
1512 * customisation apply handler for clicked signal
1513 *
1514 * when 'save settings' button is clicked
1515 */
1516 static gboolean
customisation_apply_clicked_cb(GtkWidget * widget,gpointer data)1517 customisation_apply_clicked_cb(GtkWidget *widget, gpointer data)
1518 {
1519 struct nsgtk_toolbar_customisation *tbc;
1520 tbc = (struct nsgtk_toolbar_customisation *)data;
1521
1522 /* save state to file, update toolbars for all windows */
1523 nsgtk_toolbar_customisation_save(&tbc->toolbar);
1524 nsgtk_window_toolbar_update();
1525 gtk_widget_destroy(tbc->container);
1526
1527 return TRUE;
1528 }
1529
1530
1531 /**
1532 * customisation reset handler for clicked signal
1533 *
1534 * when 'reload defaults' button is clicked
1535 */
1536 static gboolean
customisation_reset_clicked_cb(GtkWidget * widget,gpointer data)1537 customisation_reset_clicked_cb(GtkWidget *widget, gpointer data)
1538 {
1539 struct nsgtk_toolbar_customisation *tbc;
1540 tbc = (struct nsgtk_toolbar_customisation *)data;
1541
1542 customisation_toolbar_update(tbc);
1543
1544 return TRUE;
1545 }
1546
1547
1548 /**
1549 * customisation container destroy handler
1550 */
customisation_container_destroy_cb(GtkWidget * widget,gpointer data)1551 static void customisation_container_destroy_cb(GtkWidget *widget, gpointer data)
1552 {
1553 struct nsgtk_toolbar_customisation *tbc;
1554 tbc = (struct nsgtk_toolbar_customisation *)data;
1555
1556 free(tbc);
1557 }
1558
1559 /*
1560 * Toolbar button clicked handlers
1561 */
1562
1563 /**
1564 * create a toolbar customisation tab
1565 *
1566 * this is completely different approach to previous implementation. it
1567 * is not modal and the toolbar configuration is performed completely
1568 * within the tab. once the user is happy they can apply the change or
1569 * cancel as they see fit while continuing to use the browser as usual.
1570 */
cutomize_button_clicked_cb(GtkWidget * widget,gpointer data)1571 static gboolean cutomize_button_clicked_cb(GtkWidget *widget, gpointer data)
1572 {
1573 struct nsgtk_toolbar_customisation *tbc;
1574 nserror res;
1575 GtkBuilder *builder;
1576 GtkNotebook *notebook; /* notebook containing widget */
1577 GtkAllocation notebook_alloc; /* notebook size allocation */
1578 int iidx; /* item index */
1579
1580 /* obtain the notebook being added to */
1581 notebook = GTK_NOTEBOOK(gtk_widget_get_ancestor(widget,
1582 GTK_TYPE_NOTEBOOK));
1583 if (notebook == NULL) {
1584 return TRUE;
1585 }
1586
1587 /* create builder */
1588 res = nsgtk_builder_new_from_resname("toolbar", &builder);
1589 if (res != NSERROR_OK) {
1590 NSLOG(netsurf, INFO, "Toolbar UI builder init failed");
1591 return TRUE;
1592 }
1593 gtk_builder_connect_signals(builder, NULL);
1594
1595 /* create nsgtk_toolbar_customisation which has nsgtk_toolbar
1596 * at the front so we can reuse functions that take
1597 * nsgtk_toolbar
1598 */
1599 tbc = calloc(1, sizeof(struct nsgtk_toolbar_customisation));
1600 if (tbc == NULL) {
1601 g_object_unref(builder);
1602 return TRUE;
1603 }
1604
1605 /* get container box widget which forms a page of the tabs */
1606 tbc->container = GTK_WIDGET(gtk_builder_get_object(builder, "customisation"));
1607 if (tbc->container == NULL) {
1608 goto cutomize_button_clicked_cb_error;
1609 }
1610
1611 /* vertical box for the toolbox to drag items into and out of */
1612 tbc->toolbox = GTK_BOX(gtk_builder_get_object(builder, "toolbox"));
1613 if (tbc->toolbox == NULL) {
1614 goto cutomize_button_clicked_cb_error;
1615 }
1616
1617 /* customisation toolbar container */
1618 tbc->toolbar.widget = GTK_TOOLBAR(gtk_builder_get_object(builder, "toolbar"));
1619 if (tbc->toolbar.widget == NULL) {
1620 goto cutomize_button_clicked_cb_error;
1621 }
1622
1623 /* build customisation toolbar */
1624 gtk_toolbar_set_show_arrow(tbc->toolbar.widget, TRUE);
1625
1626 for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
1627 res = toolbar_item_create(iidx, &tbc->toolbar.items[iidx]);
1628 if (res != NSERROR_OK) {
1629 goto cutomize_button_clicked_cb_error;
1630 }
1631 }
1632
1633 res = customisation_toolbar_update(tbc);
1634 if (res != NSERROR_OK) {
1635 goto cutomize_button_clicked_cb_error;
1636 }
1637
1638 /* use toolbox for widgets to drag to/from */
1639 gtk_widget_get_allocation(GTK_WIDGET(notebook), ¬ebook_alloc);
1640
1641 res = toolbar_customisation_create_toolbox(tbc, notebook_alloc.width);
1642 if (res != NSERROR_OK) {
1643 goto cutomize_button_clicked_cb_error;
1644 }
1645
1646 /* configure the container */
1647 gtk_drag_dest_set(GTK_WIDGET(tbc->container),
1648 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
1649 &target_entry,
1650 1,
1651 GDK_ACTION_COPY);
1652
1653 /* discard button calls destroy */
1654 g_signal_connect_swapped(GTK_WIDGET(gtk_builder_get_object(builder,
1655 "discard")),
1656 "clicked",
1657 G_CALLBACK(gtk_widget_destroy),
1658 tbc->container);
1659
1660 /* save and update on apply button */
1661 g_signal_connect(GTK_WIDGET(gtk_builder_get_object(builder, "apply")),
1662 "clicked",
1663 G_CALLBACK(customisation_apply_clicked_cb),
1664 tbc);
1665
1666 g_signal_connect(GTK_WIDGET(gtk_builder_get_object(builder, "reset")),
1667 "clicked",
1668 G_CALLBACK(customisation_reset_clicked_cb),
1669 tbc);
1670
1671 /* close and cleanup on delete signal */
1672 g_signal_connect(tbc->container,
1673 "destroy",
1674 G_CALLBACK(customisation_container_destroy_cb),
1675 tbc);
1676
1677
1678 g_signal_connect(tbc->container,
1679 "drag-drop",
1680 G_CALLBACK(customisation_container_drag_drop_cb),
1681 tbc);
1682
1683 g_signal_connect(tbc->container,
1684 "drag-motion",
1685 G_CALLBACK(customisation_container_drag_motion_cb),
1686 tbc);
1687
1688
1689 nsgtk_tab_add_page(notebook,
1690 tbc->container,
1691 false,
1692 messages_get("gtkCustomizeToolbarTitle"),
1693 favicon_pixbuf);
1694
1695
1696 /* safe to drop the reference to the builder as the container is
1697 * referenced by the notebook now.
1698 */
1699 g_object_unref(builder);
1700
1701 return TRUE;
1702
1703 cutomize_button_clicked_cb_error:
1704 free(tbc);
1705 g_object_unref(builder);
1706 return TRUE;
1707
1708 }
1709
1710
1711 /**
1712 * callback for all toolbar items widget size allocation
1713 *
1714 * handler connected to all toolbar items for the size-allocate signal
1715 *
1716 * \param widget The widget the signal is being delivered to.
1717 * \param alloc The size allocation being set.
1718 * \param data The toolbar context passed when the signal was connected
1719 */
1720 static void
toolbar_item_size_allocate_cb(GtkWidget * widget,GtkAllocation * alloc,gpointer data)1721 toolbar_item_size_allocate_cb(GtkWidget *widget,
1722 GtkAllocation *alloc,
1723 gpointer data)
1724 {
1725 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1726 nsgtk_toolbar_button itemid;
1727
1728 itemid = itemid_from_gtktoolitem(tb, GTK_TOOL_ITEM(widget));
1729
1730 if ((tb->toolbarmem == alloc->x) ||
1731 (tb->items[itemid].location < tb->items[HISTORY_BUTTON].location)) {
1732 /*
1733 * no reallocation after first adjustment,
1734 * no reallocation for buttons left of history button
1735 */
1736 return;
1737 }
1738
1739 if (itemid == HISTORY_BUTTON) {
1740 if (alloc->width == 20) {
1741 return;
1742 }
1743
1744 tb->toolbarbase = alloc->y + alloc->height;
1745 tb->historybase = alloc->x + 20;
1746 if (tb->offset == 0) {
1747 tb->offset = alloc->width - 20;
1748 }
1749 alloc->width = 20;
1750 } else if (tb->items[itemid].location <= tb->items[URL_BAR_ITEM].location) {
1751 alloc->x -= tb->offset;
1752 if (itemid == URL_BAR_ITEM) {
1753 alloc->width += tb->offset;
1754 }
1755 }
1756 tb->toolbarmem = alloc->x;
1757
1758 gtk_widget_size_allocate(widget, alloc);
1759 }
1760
1761
1762 /**
1763 * handler for back tool bar item clicked signal
1764 *
1765 * \param widget The widget the signal is being delivered to.
1766 * \param data The toolbar context passed when the signal was connected
1767 * \return TRUE
1768 */
1769 static gboolean
back_button_clicked_cb(GtkWidget * widget,gpointer data)1770 back_button_clicked_cb(GtkWidget *widget, gpointer data)
1771 {
1772 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1773 struct browser_window *bw;
1774
1775 bw = tb->get_bw(tb->get_ctx);
1776
1777 if ((bw != NULL) && browser_window_history_back_available(bw)) {
1778 /* clear potential search effects */
1779 browser_window_search_clear(bw);
1780
1781 browser_window_history_back(bw, false);
1782
1783 set_item_sensitivity(&tb->items[BACK_BUTTON],
1784 browser_window_history_back_available(bw));
1785 set_item_sensitivity(&tb->items[FORWARD_BUTTON],
1786 browser_window_history_forward_available(bw));
1787
1788 nsgtk_local_history_hide();
1789 }
1790 return TRUE;
1791 }
1792
1793
1794 /**
1795 * handler for forward tool bar item clicked signal
1796 *
1797 * \param widget The widget the signal is being delivered to.
1798 * \param data The toolbar context passed when the signal was connected
1799 * \return TRUE
1800 */
1801 static gboolean
forward_button_clicked_cb(GtkWidget * widget,gpointer data)1802 forward_button_clicked_cb(GtkWidget *widget, gpointer data)
1803 {
1804 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1805 struct browser_window *bw;
1806
1807 bw = tb->get_bw(tb->get_ctx);
1808
1809 if ((bw != NULL) && browser_window_history_forward_available(bw)) {
1810 /* clear potential search effects */
1811 browser_window_search_clear(bw);
1812
1813 browser_window_history_forward(bw, false);
1814
1815 set_item_sensitivity(&tb->items[BACK_BUTTON],
1816 browser_window_history_back_available(bw));
1817 set_item_sensitivity(&tb->items[FORWARD_BUTTON],
1818 browser_window_history_forward_available(bw));
1819 nsgtk_local_history_hide();
1820 }
1821 return TRUE;
1822 }
1823
1824
1825 /**
1826 * handler for stop tool bar item clicked signal
1827 *
1828 * \param widget The widget the signal is being delivered to.
1829 * \param data The toolbar context passed when the signal was connected
1830 * \return TRUE
1831 */
1832 static gboolean
stop_button_clicked_cb(GtkWidget * widget,gpointer data)1833 stop_button_clicked_cb(GtkWidget *widget, gpointer data)
1834 {
1835 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1836
1837 browser_window_stop(tb->get_bw(tb->get_ctx));
1838
1839 return TRUE;
1840 }
1841
1842
1843 /**
1844 * handler for reload tool bar item clicked signal
1845 *
1846 * \param widget The widget the signal is being delivered to.
1847 * \param data The toolbar context passed when the signal was connected
1848 * \return TRUE
1849 */
1850 static gboolean
reload_button_clicked_cb(GtkWidget * widget,gpointer data)1851 reload_button_clicked_cb(GtkWidget *widget, gpointer data)
1852 {
1853 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1854 struct browser_window *bw;
1855
1856 bw = tb->get_bw(tb->get_ctx);
1857
1858 /* clear potential search effects */
1859 browser_window_search_clear(bw);
1860
1861 browser_window_reload(bw, true);
1862
1863 return TRUE;
1864 }
1865
1866
1867 /**
1868 * handler for reload/stop tool bar item clicked signal
1869 *
1870 * \param widget The widget the signal is being delivered to.
1871 * \param data The toolbar context passed when the signal was connected
1872 * \return TRUE
1873 */
1874 static gboolean
reloadstop_button_clicked_cb(GtkWidget * widget,gpointer data)1875 reloadstop_button_clicked_cb(GtkWidget *widget, gpointer data)
1876 {
1877 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1878 struct browser_window *bw;
1879
1880 bw = tb->get_bw(tb->get_ctx);
1881
1882 /* clear potential search effects */
1883 browser_window_search_clear(bw);
1884
1885 if (tb->items[RELOADSTOP_BUTTON].sensitivity) {
1886 browser_window_reload(bw, true);
1887 } else {
1888 browser_window_stop(tb->get_bw(tb->get_ctx));
1889 }
1890
1891 return TRUE;
1892 }
1893
1894
1895 /**
1896 * handler for home tool bar item clicked signal
1897 *
1898 * \param widget The widget the signal is being delivered to.
1899 * \param data The toolbar context passed when the signal was connected
1900 * \return TRUE
1901 */
1902 static gboolean
home_button_clicked_cb(GtkWidget * widget,gpointer data)1903 home_button_clicked_cb(GtkWidget *widget, gpointer data)
1904 {
1905 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1906 nserror res;
1907 const char *addr;
1908
1909 if (nsoption_charp(homepage_url) != NULL) {
1910 addr = nsoption_charp(homepage_url);
1911 } else {
1912 addr = NETSURF_HOMEPAGE;
1913 }
1914
1915 res = toolbar_navigate_to_url(tb, addr);
1916 if (res != NSERROR_OK) {
1917 nsgtk_warning(messages_get_errorcode(res), 0);
1918 }
1919
1920 return TRUE;
1921 }
1922
1923
1924 /**
1925 * callback for url entry widget activation
1926 *
1927 * handler connected to url entry widget for the activate signal
1928 *
1929 * \param widget The widget the signal is being delivered to.
1930 * \param data The toolbar context passed when the signal was connected
1931 * \return TRUE to allow activation.
1932 */
url_entry_activate_cb(GtkWidget * widget,gpointer data)1933 static gboolean url_entry_activate_cb(GtkWidget *widget, gpointer data)
1934 {
1935 nserror res;
1936 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1937 struct browser_window *bw;
1938 nsurl *url;
1939
1940 res = search_web_omni(gtk_entry_get_text(GTK_ENTRY(widget)),
1941 SEARCH_WEB_OMNI_NONE,
1942 &url);
1943 if (res == NSERROR_OK) {
1944 bw = tb->get_bw(tb->get_ctx);
1945 res = browser_window_navigate(
1946 bw, url, NULL, BW_NAVIGATE_HISTORY, NULL, NULL, NULL);
1947 nsurl_unref(url);
1948 }
1949 if (res != NSERROR_OK) {
1950 nsgtk_warning(messages_get_errorcode(res), 0);
1951 }
1952
1953 return TRUE;
1954 }
1955
1956
1957 /**
1958 * callback for url entry widget changing
1959 *
1960 * handler connected to url entry widget for the change signal
1961 *
1962 * \param widget The widget the signal is being delivered to.
1963 * \param event The key change event that changed the entry.
1964 * \param data The toolbar context passed when the signal was connected
1965 * \return TRUE to allow activation.
1966 */
1967 static gboolean
url_entry_changed_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)1968 url_entry_changed_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1969 {
1970 return nsgtk_completion_update(GTK_ENTRY(widget));
1971 }
1972
1973
1974 /**
1975 * callback for url entry widget icon button release
1976 *
1977 * handler connected to url entry widget for the icon release signal
1978 *
1979 * \param widget The widget the signal is being delivered to.
1980 * \param event The key change event that changed the entry.
1981 * \param data The toolbar context passed when the signal was connected
1982 * \return TRUE to allow activation.
1983 */
1984 static void
url_entry_icon_release_cb(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkEvent * event,gpointer data)1985 url_entry_icon_release_cb(GtkEntry *entry,
1986 GtkEntryIconPosition icon_pos,
1987 GdkEvent *event,
1988 gpointer data)
1989 {
1990 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
1991 struct browser_window *bw;
1992
1993 bw = tb->get_bw(tb->get_ctx);
1994
1995 nsgtk_page_info(bw);
1996 }
1997
1998
1999 /**
2000 * handler for web search tool bar entry item activate signal
2001 *
2002 * handler connected to web search entry widget for the activate signal
2003 *
2004 * \todo make this user selectable to switch between opening in new
2005 * and navigating current window. Possibly improve core search_web interfaces
2006 *
2007 * \param widget The widget the signal is being delivered to.
2008 * \param data The toolbar context passed when the signal was connected
2009 * \return TRUE
2010 */
websearch_entry_activate_cb(GtkWidget * widget,gpointer data)2011 static gboolean websearch_entry_activate_cb(GtkWidget *widget, gpointer data)
2012 {
2013 nserror res;
2014 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2015 struct browser_window *bw;
2016 nsurl *url;
2017
2018 res = search_web_omni(gtk_entry_get_text(GTK_ENTRY(widget)),
2019 SEARCH_WEB_OMNI_SEARCHONLY,
2020 &url);
2021 if (res == NSERROR_OK) {
2022 bw = tb->get_bw(tb->get_ctx);
2023
2024 res = browser_window_create(
2025 BW_CREATE_HISTORY | BW_CREATE_TAB | BW_CREATE_FOREGROUND,
2026 url,
2027 NULL,
2028 bw,
2029 NULL);
2030 nsurl_unref(url);
2031 }
2032 if (res != NSERROR_OK) {
2033 nsgtk_warning(messages_get_errorcode(res), 0);
2034 }
2035
2036 return TRUE;
2037 }
2038
2039 /**
2040 * handler for web search tool bar item button press signal
2041 *
2042 * allows a click in the websearch entry field to clear the name of the
2043 * provider.
2044 *
2045 * \todo this does not work well, different behaviour wanted perhaps?
2046 *
2047 * \param widget The widget the signal is being delivered to.
2048 * \param data The toolbar context passed when the signal was connected
2049 * \return TRUE
2050 */
2051 static gboolean
websearch_entry_button_press_cb(GtkWidget * widget,GdkEventFocus * f,gpointer data)2052 websearch_entry_button_press_cb(GtkWidget *widget,
2053 GdkEventFocus *f,
2054 gpointer data)
2055 {
2056 gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
2057 gtk_widget_grab_focus(GTK_WIDGET(widget));
2058
2059 return TRUE;
2060 }
2061
2062
2063 /**
2064 * handler for new window tool bar item clicked signal
2065 *
2066 * \param widget The widget the signal is being delivered to.
2067 * \param data The toolbar context passed when the signal was connected
2068 * \return TRUE
2069 */
2070 static gboolean
newwindow_button_clicked_cb(GtkWidget * widget,gpointer data)2071 newwindow_button_clicked_cb(GtkWidget *widget, gpointer data)
2072 {
2073 nserror res;
2074 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2075
2076 res = nsgtk_browser_window_create(tb->get_bw(tb->get_ctx), false);
2077 if (res != NSERROR_OK) {
2078 nsgtk_warning(messages_get_errorcode(res), 0);
2079 }
2080
2081 return TRUE;
2082 }
2083
2084
2085 /**
2086 * handler for new tab tool bar item clicked signal
2087 *
2088 * \param widget The widget the signal is being delivered to.
2089 * \param data The toolbar context passed when the signal was connected
2090 * \return TRUE
2091 */
2092 static gboolean
newtab_button_clicked_cb(GtkWidget * widget,gpointer data)2093 newtab_button_clicked_cb(GtkWidget *widget, gpointer data)
2094 {
2095 nserror res;
2096 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2097
2098 res = nsgtk_browser_window_create(tb->get_bw(tb->get_ctx), true);
2099 if (res != NSERROR_OK) {
2100 nsgtk_warning(messages_get_errorcode(res), 0);
2101 }
2102 return TRUE;
2103 }
2104
2105
2106 /**
2107 * handler for open file tool bar item clicked signal
2108 *
2109 * \param widget The widget the signal is being delivered to.
2110 * \param data The toolbar context passed when the signal was connected
2111 * \return TRUE
2112 */
2113 static gboolean
openfile_button_clicked_cb(GtkWidget * widget,gpointer data)2114 openfile_button_clicked_cb(GtkWidget *widget, gpointer data)
2115 {
2116 GtkWidget *dlgOpen;
2117 gint response;
2118 GtkWidget *toplevel;
2119 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2120 struct browser_window *bw;
2121
2122 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2123
2124 dlgOpen = gtk_file_chooser_dialog_new("Open File",
2125 GTK_WINDOW(toplevel),
2126 GTK_FILE_CHOOSER_ACTION_OPEN,
2127 NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2128 NSGTK_STOCK_OPEN, GTK_RESPONSE_OK,
2129 NULL, NULL);
2130
2131 response = gtk_dialog_run(GTK_DIALOG(dlgOpen));
2132 if (response == GTK_RESPONSE_OK) {
2133 char *urltxt;
2134 gchar *filename;
2135 nserror res;
2136 nsurl *url;
2137
2138 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlgOpen));
2139
2140 urltxt = malloc(strlen(filename) + FILE_SCHEME_PREFIX_LEN + 1);
2141 if (urltxt != NULL) {
2142 sprintf(urltxt, FILE_SCHEME_PREFIX"%s", filename);
2143
2144 res = nsurl_create(urltxt, &url);
2145 if (res == NSERROR_OK) {
2146 bw = tb->get_bw(tb->get_ctx);
2147 res = browser_window_navigate(bw,
2148 url,
2149 NULL,
2150 BW_NAVIGATE_HISTORY,
2151 NULL,
2152 NULL,
2153 NULL);
2154 nsurl_unref(url);
2155 }
2156 if (res != NSERROR_OK) {
2157 nsgtk_warning(messages_get_errorcode(res), 0);
2158 }
2159 free(urltxt);
2160 }
2161
2162
2163 g_free(filename);
2164 }
2165
2166 gtk_widget_destroy(dlgOpen);
2167
2168 return TRUE;
2169 }
2170
2171
2172 /**
2173 * handler for close window tool bar item clicked signal
2174 *
2175 * \param widget The widget the signal is being delivered to.
2176 * \param data The toolbar context passed when the signal was connected
2177 * \return TRUE
2178 */
2179 static gboolean
closewindow_button_clicked_cb(GtkWidget * widget,gpointer data)2180 closewindow_button_clicked_cb(GtkWidget *widget, gpointer data)
2181 {
2182 GtkWidget *toplevel;
2183 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2184 gtk_widget_destroy(toplevel);
2185 return TRUE;
2186 }
2187
2188
2189 /**
2190 * handler for full save export tool bar item clicked signal
2191 *
2192 * \param widget The widget the signal is being delivered to.
2193 * \param data The toolbar context passed when the signal was connected
2194 * \return TRUE
2195 */
2196 static gboolean
savepage_button_clicked_cb(GtkWidget * widget,gpointer data)2197 savepage_button_clicked_cb(GtkWidget *widget, gpointer data)
2198 {
2199 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2200 struct browser_window *bw;
2201 DIR *d;
2202 gchar *path;
2203 nserror res;
2204 GtkWidget *toplevel;
2205
2206 bw = tb->get_bw(tb->get_ctx);
2207 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2208
2209 res = nsgtk_saveas_dialog(bw,
2210 messages_get("gtkcompleteSave"),
2211 GTK_WINDOW(toplevel),
2212 true,
2213 &path);
2214 if (res != NSERROR_OK) {
2215 return FALSE;
2216 }
2217
2218 d = opendir(path);
2219 if (d == NULL) {
2220 NSLOG(netsurf, INFO,
2221 "Unable to open directory %s for complete save: %s",
2222 path,
2223 strerror(errno));
2224 if (errno == ENOTDIR) {
2225 nsgtk_warning("NoDirError", path);
2226 } else {
2227 nsgtk_warning("gtkFileError", path);
2228 }
2229 g_free(path);
2230 return TRUE;
2231 }
2232 closedir(d);
2233
2234 save_complete(browser_window_get_content(bw), path, NULL);
2235 g_free(path);
2236
2237 return TRUE;
2238 }
2239
2240
2241 /**
2242 * handler for pdf export tool bar item clicked signal
2243 *
2244 * \param widget The widget the signal is being delivered to.
2245 * \param data The toolbar context passed when the signal was connected
2246 * \return TRUE
2247 */
2248 static gboolean
pdf_button_clicked_cb(GtkWidget * widget,gpointer data)2249 pdf_button_clicked_cb(GtkWidget *widget, gpointer data)
2250 {
2251 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2252 struct browser_window *bw;
2253 GtkWidget *toplevel;
2254 gchar *filename;
2255 nserror res;
2256
2257 bw = tb->get_bw(tb->get_ctx);
2258
2259 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2260
2261 res = nsgtk_saveas_dialog(bw,
2262 "Export to PDF",
2263 GTK_WINDOW(toplevel),
2264 false,
2265 &filename);
2266 if (res != NSERROR_OK) {
2267 return FALSE;
2268 }
2269
2270 #ifdef WITH_PDF_EXPORT
2271 struct print_settings *settings;
2272
2273 /* this way the scale used by PDF functions is synchronised with that
2274 * used by the all-purpose print interface
2275 */
2276 haru_nsfont_set_scale((float)option_export_scale / 100);
2277
2278 settings = print_make_settings(PRINT_OPTIONS,
2279 (const char *) filename,
2280 &haru_nsfont);
2281 g_free(filename);
2282 if (settings == NULL) {
2283 return TRUE;
2284 }
2285 /* This will clean up the print_settings object for us */
2286 print_basic_run(browser_window_get_content(bw), &pdf_printer, settings);
2287 #endif
2288 return TRUE;
2289
2290 }
2291
2292
2293 /**
2294 * handler for plain text export tool bar item clicked signal
2295 *
2296 * \param widget The widget the signal is being delivered to.
2297 * \param data The toolbar context passed when the signal was connected
2298 * \return TRUE
2299 */
2300 static gboolean
plaintext_button_clicked_cb(GtkWidget * widget,gpointer data)2301 plaintext_button_clicked_cb(GtkWidget *widget, gpointer data)
2302 {
2303 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2304 struct browser_window *bw;
2305 GtkWidget *toplevel;
2306 gchar *filename;
2307 nserror res;
2308
2309 bw = tb->get_bw(tb->get_ctx);
2310
2311 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2312
2313 res = nsgtk_saveas_dialog(bw,
2314 messages_get("gtkplainSave"),
2315 GTK_WINDOW(toplevel),
2316 false,
2317 &filename);
2318 if (res != NSERROR_OK) {
2319 return FALSE;
2320 }
2321
2322
2323 save_as_text(browser_window_get_content(bw), filename);
2324 g_free(filename);
2325
2326 return TRUE;
2327 }
2328
2329
2330 /**
2331 * handler for print tool bar item clicked signal
2332 *
2333 * \param widget The widget the signal is being delivered to.
2334 * \param data The toolbar context passed when the signal was connected
2335 * \return TRUE
2336 */
2337 static gboolean
print_button_clicked_cb(GtkWidget * widget,gpointer data)2338 print_button_clicked_cb(GtkWidget *widget, gpointer data)
2339 {
2340 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2341 struct browser_window *bw;
2342 GtkPrintOperation *print_op;
2343 GtkPageSetup *page_setup;
2344 GtkPrintSettings *print_settings;
2345 GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR;
2346 struct print_settings *nssettings;
2347 char *settings_fname = NULL;
2348 GtkWidget *toplevel;
2349
2350 bw = tb->get_bw(tb->get_ctx);
2351
2352 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2353
2354 print_op = gtk_print_operation_new();
2355 if (print_op == NULL) {
2356 nsgtk_warning(messages_get("NoMemory"), 0);
2357 return TRUE;
2358 }
2359
2360 /* use previously saved settings if any */
2361 netsurf_mkpath(&settings_fname, NULL, 2, nsgtk_config_home, "Print");
2362 if (settings_fname != NULL) {
2363 print_settings = gtk_print_settings_new_from_file(settings_fname, NULL);
2364 if (print_settings != NULL) {
2365 gtk_print_operation_set_print_settings(print_op,
2366 print_settings);
2367
2368 /* We're not interested in the settings any more */
2369 g_object_unref(print_settings);
2370 }
2371 }
2372
2373 content_to_print = browser_window_get_content(bw);
2374
2375 page_setup = gtk_print_run_page_setup_dialog(GTK_WINDOW(toplevel),
2376 NULL,
2377 NULL);
2378 if (page_setup == NULL) {
2379 nsgtk_warning(messages_get("NoMemory"), 0);
2380 free(settings_fname);
2381 g_object_unref(print_op);
2382 return TRUE;
2383 }
2384 gtk_print_operation_set_default_page_setup(print_op, page_setup);
2385
2386 nssettings = print_make_settings(PRINT_DEFAULT,
2387 NULL,
2388 nsgtk_layout_table);
2389
2390 g_signal_connect(print_op,
2391 "begin_print",
2392 G_CALLBACK(gtk_print_signal_begin_print),
2393 nssettings);
2394 g_signal_connect(print_op,
2395 "draw_page",
2396 G_CALLBACK(gtk_print_signal_draw_page),
2397 NULL);
2398 g_signal_connect(print_op,
2399 "end_print",
2400 G_CALLBACK(gtk_print_signal_end_print),
2401 nssettings);
2402
2403 if (content_get_type(browser_window_get_content(bw)) != CONTENT_TEXTPLAIN) {
2404 res = gtk_print_operation_run(print_op,
2405 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2406 GTK_WINDOW(toplevel),
2407 NULL);
2408 }
2409
2410 /* if the settings were used save them for future use */
2411 if (settings_fname != NULL) {
2412 if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
2413 /* Do not increment the settings reference */
2414 print_settings = gtk_print_operation_get_print_settings(print_op);
2415
2416 gtk_print_settings_to_file(print_settings,
2417 settings_fname,
2418 NULL);
2419 }
2420 free(settings_fname);
2421 }
2422
2423 /* Our print_settings object is destroyed by the end print handler */
2424 g_object_unref(page_setup);
2425 g_object_unref(print_op);
2426
2427 return TRUE;
2428 }
2429
2430 /**
2431 * handler for quit tool bar item clicked signal
2432 *
2433 * \param widget The widget the signal is being delivered to.
2434 * \param data The toolbar context passed when the signal was connected
2435 * \return TRUE
2436 */
2437 static gboolean
quit_button_clicked_cb(GtkWidget * widget,gpointer data)2438 quit_button_clicked_cb(GtkWidget *widget, gpointer data)
2439 {
2440 nsgtk_scaffolding_destroy_all();
2441 return TRUE;
2442 }
2443
2444
2445 /**
2446 * handler for cut tool bar item clicked signal
2447 *
2448 * \param widget The widget the signal is being delivered to.
2449 * \param data The toolbar context passed when the signal was connected
2450 * \return TRUE
2451 */
2452 static gboolean
cut_button_clicked_cb(GtkWidget * widget,gpointer data)2453 cut_button_clicked_cb(GtkWidget *widget, gpointer data)
2454 {
2455 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2456 struct browser_window *bw;
2457 GtkWidget *focused;
2458 GtkWidget *toplevel;
2459
2460 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2461
2462 focused = gtk_window_get_focus(GTK_WINDOW(toplevel));
2463
2464 /* let gtk handle it if focused widget is an editable */
2465 if (GTK_IS_EDITABLE(focused)) {
2466 gtk_editable_cut_clipboard(GTK_EDITABLE(focused));
2467 } else {
2468 bw = tb->get_bw(tb->get_ctx);
2469 browser_window_key_press(bw, NS_KEY_CUT_SELECTION);
2470 }
2471
2472 return TRUE;
2473 }
2474
2475
2476 /**
2477 * handler for copy tool bar item clicked signal
2478 *
2479 * \param widget The widget the signal is being delivered to.
2480 * \param data The toolbar context passed when the signal was connected
2481 * \return TRUE
2482 */
2483 static gboolean
copy_button_clicked_cb(GtkWidget * widget,gpointer data)2484 copy_button_clicked_cb(GtkWidget *widget, gpointer data)
2485 {
2486 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2487 struct browser_window *bw;
2488 GtkWidget *focused;
2489 GtkWidget *toplevel;
2490
2491 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2492
2493 focused = gtk_window_get_focus(GTK_WINDOW(toplevel));
2494
2495 /* let gtk handle it if focused widget is an editable */
2496 if (GTK_IS_EDITABLE(focused)) {
2497 gtk_editable_copy_clipboard(GTK_EDITABLE(focused));
2498 } else {
2499 bw = tb->get_bw(tb->get_ctx);
2500 browser_window_key_press(bw, NS_KEY_COPY_SELECTION);
2501 }
2502
2503 return TRUE;
2504 }
2505
2506
2507 /**
2508 * handler for paste tool bar item clicked signal
2509 *
2510 * \param widget The widget the signal is being delivered to.
2511 * \param data The toolbar context passed when the signal was connected
2512 * \return TRUE
2513 */
2514 static gboolean
paste_button_clicked_cb(GtkWidget * widget,gpointer data)2515 paste_button_clicked_cb(GtkWidget *widget, gpointer data)
2516 {
2517 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2518 struct browser_window *bw;
2519 GtkWidget *focused;
2520 GtkWidget *toplevel;
2521
2522 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2523
2524 focused = gtk_window_get_focus(GTK_WINDOW(toplevel));
2525
2526 /* let gtk handle it if focused widget is an editable */
2527 if (GTK_IS_EDITABLE(focused)) {
2528 gtk_editable_paste_clipboard(GTK_EDITABLE(focused));
2529 } else {
2530 bw = tb->get_bw(tb->get_ctx);
2531 browser_window_key_press(bw, NS_KEY_PASTE);
2532 }
2533
2534 return TRUE;
2535 }
2536
2537
2538 /**
2539 * handler for delete tool bar item clicked signal
2540 *
2541 * \param widget The widget the signal is being delivered to.
2542 * \param data The toolbar context passed when the signal was connected
2543 * \return TRUE
2544 */
2545 static gboolean
delete_button_clicked_cb(GtkWidget * widget,gpointer data)2546 delete_button_clicked_cb(GtkWidget *widget, gpointer data)
2547 {
2548 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2549 struct browser_window *bw;
2550 GtkWidget *focused;
2551 GtkWidget *toplevel;
2552
2553 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2554
2555 focused = gtk_window_get_focus(GTK_WINDOW(toplevel));
2556
2557 /* let gtk handle it if focused widget is an editable */
2558 if (GTK_IS_EDITABLE(focused)) {
2559 gtk_editable_delete_selection(GTK_EDITABLE(focused));
2560 } else {
2561 bw = tb->get_bw(tb->get_ctx);
2562 browser_window_key_press(bw, NS_KEY_CLEAR_SELECTION);
2563 }
2564
2565 return TRUE;
2566 }
2567
2568
2569 /**
2570 * handler for select all tool bar item clicked signal
2571 *
2572 * \param widget The widget the signal is being delivered to.
2573 * \param data The toolbar context passed when the signal was connected
2574 * \return TRUE
2575 */
2576 static gboolean
selectall_button_clicked_cb(GtkWidget * widget,gpointer data)2577 selectall_button_clicked_cb(GtkWidget *widget, gpointer data)
2578 {
2579 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2580 struct browser_window *bw;
2581 GtkWidget *focused;
2582 GtkWidget *toplevel;
2583
2584 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2585
2586 focused = gtk_window_get_focus(GTK_WINDOW(toplevel));
2587
2588 /* let gtk handle it if focused widget is an editable */
2589 if (GTK_IS_EDITABLE(focused)) {
2590 gtk_editable_select_region(GTK_EDITABLE(focused), 0, -1);
2591 } else {
2592 bw = tb->get_bw(tb->get_ctx);
2593 browser_window_key_press(bw, NS_KEY_SELECT_ALL);
2594 }
2595
2596 return TRUE;
2597 }
2598
2599
2600 /**
2601 * handler for preferences tool bar item clicked signal
2602 *
2603 * \param widget The widget the signal is being delivered to.
2604 * \param data The toolbar context passed when the signal was connected
2605 * \return TRUE
2606 */
2607 static gboolean
preferences_button_clicked_cb(GtkWidget * widget,gpointer data)2608 preferences_button_clicked_cb(GtkWidget *widget, gpointer data)
2609 {
2610 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2611 struct browser_window *bw;
2612 GtkWidget *toplevel;
2613 GtkWidget *wndpreferences;
2614
2615 bw = tb->get_bw(tb->get_ctx);
2616
2617 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2618
2619 wndpreferences = nsgtk_preferences(bw, GTK_WINDOW(toplevel));
2620 if (wndpreferences != NULL) {
2621 gtk_widget_show(wndpreferences);
2622 }
2623
2624 return TRUE;
2625 }
2626
2627
2628 /**
2629 * handler for zoom plus tool bar item clicked signal
2630 *
2631 * \param widget The widget the signal is being delivered to.
2632 * \param data The toolbar context passed when the signal was connected
2633 * \return TRUE
2634 */
2635 static gboolean
zoomplus_button_clicked_cb(GtkWidget * widget,gpointer data)2636 zoomplus_button_clicked_cb(GtkWidget *widget, gpointer data)
2637 {
2638 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2639 struct browser_window *bw;
2640
2641 bw = tb->get_bw(tb->get_ctx);
2642
2643 browser_window_set_scale(bw, 0.05, false);
2644
2645 return TRUE;
2646 }
2647
2648
2649 /**
2650 * handler for zoom minus tool bar item clicked signal
2651 *
2652 * \param widget The widget the signal is being delivered to.
2653 * \param data The toolbar context passed when the signal was connected
2654 * \return TRUE
2655 */
2656 static gboolean
zoomminus_button_clicked_cb(GtkWidget * widget,gpointer data)2657 zoomminus_button_clicked_cb(GtkWidget *widget, gpointer data)
2658 {
2659 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2660 struct browser_window *bw;
2661
2662 bw = tb->get_bw(tb->get_ctx);
2663
2664 browser_window_set_scale(bw, -0.05, false);
2665
2666 return TRUE;
2667
2668 }
2669
2670
2671 /**
2672 * handler for zoom normal tool bar item clicked signal
2673 *
2674 * \param widget The widget the signal is being delivered to.
2675 * \param data The toolbar context passed when the signal was connected
2676 * \return TRUE
2677 */
2678 static gboolean
zoomnormal_button_clicked_cb(GtkWidget * widget,gpointer data)2679 zoomnormal_button_clicked_cb(GtkWidget *widget, gpointer data)
2680 {
2681 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2682 struct browser_window *bw;
2683
2684 bw = tb->get_bw(tb->get_ctx);
2685
2686 browser_window_set_scale(bw, 1.0, true);
2687
2688 return TRUE;
2689 }
2690
2691
2692 /**
2693 * handler for full screen tool bar item clicked signal
2694 *
2695 * \param widget The widget the signal is being delivered to.
2696 * \param data The toolbar context passed when the signal was connected
2697 * \return TRUE
2698 */
2699 static gboolean
fullscreen_button_clicked_cb(GtkWidget * widget,gpointer data)2700 fullscreen_button_clicked_cb(GtkWidget *widget, gpointer data)
2701 {
2702 GtkWindow *gtkwindow; /* gtk window widget is in */
2703 GdkWindow *gdkwindow;
2704 GdkWindowState state;
2705
2706 gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW));
2707 gdkwindow = gtk_widget_get_window(GTK_WIDGET(gtkwindow));
2708 state = gdk_window_get_state(gdkwindow);
2709
2710 if (state & GDK_WINDOW_STATE_FULLSCREEN) {
2711 gtk_window_unfullscreen(gtkwindow);
2712 } else {
2713 gtk_window_fullscreen(gtkwindow);
2714 }
2715 return TRUE;
2716 }
2717
2718
2719 /**
2720 * handler for view source tool bar item clicked signal
2721 *
2722 * \param widget The widget the signal is being delivered to.
2723 * \param data The toolbar context passed when the signal was connected
2724 * \return TRUE
2725 */
2726 static gboolean
viewsource_button_clicked_cb(GtkWidget * widget,gpointer data)2727 viewsource_button_clicked_cb(GtkWidget *widget, gpointer data)
2728 {
2729 nserror res;
2730 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2731 struct browser_window *bw;
2732 GtkWindow *gtkwindow; /* gtk window widget is in */
2733
2734 bw = tb->get_bw(tb->get_ctx);
2735
2736 gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW));
2737
2738 res = nsgtk_viewsource(gtkwindow, bw);
2739 if (res != NSERROR_OK) {
2740 nsgtk_warning(messages_get_errorcode(res), 0);
2741 }
2742
2743 return TRUE;
2744 }
2745
2746
2747 /**
2748 * handler for show downloads tool bar item clicked signal
2749 *
2750 * \param widget The widget the signal is being delivered to.
2751 * \param data The toolbar context passed when the signal was connected
2752 * \return TRUE
2753 */
2754 static gboolean
downloads_button_clicked_cb(GtkWidget * widget,gpointer data)2755 downloads_button_clicked_cb(GtkWidget *widget, gpointer data)
2756 {
2757 GtkWindow *gtkwindow; /* gtk window widget is in */
2758 gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW));
2759 nsgtk_download_show(gtkwindow);
2760 return TRUE;
2761 }
2762
2763
2764 /**
2765 * handler for show downloads tool bar item clicked signal
2766 *
2767 * \param widget The widget the signal is being delivered to.
2768 * \param data The toolbar context passed when the signal was connected
2769 * \return TRUE
2770 */
2771 static gboolean
savewindowsize_button_clicked_cb(GtkWidget * widget,gpointer data)2772 savewindowsize_button_clicked_cb(GtkWidget *widget, gpointer data)
2773 {
2774 GtkWindow *gtkwindow; /* gtk window widget is in */
2775 int x,y,w,h;
2776 char *choices = NULL;
2777
2778 gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW));
2779
2780 gtk_window_get_position(gtkwindow, &x, &y);
2781 gtk_window_get_size(gtkwindow, &w, &h);
2782
2783 nsoption_set_int(window_width, w);
2784 nsoption_set_int(window_height, h);
2785 nsoption_set_int(window_x, x);
2786 nsoption_set_int(window_y, y);
2787
2788 netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
2789 if (choices != NULL) {
2790 nsoption_write(choices, NULL, NULL);
2791 free(choices);
2792 }
2793
2794 return TRUE;
2795 }
2796
2797
2798 /**
2799 * handler for show downloads tool bar item clicked signal
2800 *
2801 * \param widget The widget the signal is being delivered to.
2802 * \param data The toolbar context passed when the signal was connected
2803 * \return TRUE
2804 */
2805 static gboolean
toggledebugging_button_clicked_cb(GtkWidget * widget,gpointer data)2806 toggledebugging_button_clicked_cb(GtkWidget *widget, gpointer data)
2807 {
2808 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2809 struct browser_window *bw;
2810
2811 bw = tb->get_bw(tb->get_ctx);
2812
2813 browser_window_debug(bw, CONTENT_DEBUG_REDRAW);
2814
2815 nsgtk_window_update_all();
2816
2817 return TRUE;
2818 }
2819
2820
2821 /**
2822 * handler for debug box tree tool bar item clicked signal
2823 *
2824 * \param widget The widget the signal is being delivered to.
2825 * \param data The toolbar context passed when the signal was connected
2826 * \return TRUE
2827 */
2828 static gboolean
debugboxtree_button_clicked_cb(GtkWidget * widget,gpointer data)2829 debugboxtree_button_clicked_cb(GtkWidget *widget, gpointer data)
2830 {
2831 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2832 struct browser_window *bw;
2833 gchar *fname;
2834 gint handle;
2835 FILE *f;
2836
2837 handle = g_file_open_tmp("nsgtkboxtreeXXXXXX", &fname, NULL);
2838 if ((handle == -1) || (fname == NULL)) {
2839 return TRUE;
2840 }
2841 close(handle); /* in case it was binary mode */
2842
2843 /* save data to temporary file */
2844 f = fopen(fname, "w");
2845 if (f == NULL) {
2846 nsgtk_warning("Error saving box tree dump.",
2847 "Unable to open file for writing.");
2848 unlink(fname);
2849 return TRUE;
2850 }
2851
2852 bw = tb->get_bw(tb->get_ctx);
2853
2854 browser_window_debug_dump(bw, f, CONTENT_DEBUG_RENDER);
2855
2856 fclose(f);
2857
2858 nsgtk_viewfile("Box Tree Debug", "boxtree", fname);
2859
2860 g_free(fname);
2861
2862 return TRUE;
2863 }
2864
2865
2866 /**
2867 * handler for debug dom tree tool bar item clicked signal
2868 *
2869 * \param widget The widget the signal is being delivered to.
2870 * \param data The toolbar context passed when the signal was connected
2871 * \return TRUE
2872 */
2873 static gboolean
debugdomtree_button_clicked_cb(GtkWidget * widget,gpointer data)2874 debugdomtree_button_clicked_cb(GtkWidget *widget, gpointer data)
2875 {
2876 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2877 struct browser_window *bw;
2878 gchar *fname;
2879 gint handle;
2880 FILE *f;
2881
2882 handle = g_file_open_tmp("nsgtkdomtreeXXXXXX", &fname, NULL);
2883 if ((handle == -1) || (fname == NULL)) {
2884 return TRUE;
2885 }
2886 close(handle); /* in case it was binary mode */
2887
2888 /* save data to temporary file */
2889 f = fopen(fname, "w");
2890 if (f == NULL) {
2891 nsgtk_warning("Error saving box tree dump.",
2892 "Unable to open file for writing.");
2893 unlink(fname);
2894 return TRUE;
2895 }
2896
2897 bw = tb->get_bw(tb->get_ctx);
2898
2899 browser_window_debug_dump(bw, f, CONTENT_DEBUG_DOM);
2900
2901 fclose(f);
2902
2903 nsgtk_viewfile("DOM Tree Debug", "domtree", fname);
2904
2905 g_free(fname);
2906
2907 return TRUE;
2908
2909 }
2910
2911
2912 /**
2913 * handler for local history tool bar item clicked signal
2914 *
2915 * \param widget The widget the signal is being delivered to.
2916 * \param data The toolbar context passed when the signal was connected
2917 * \return TRUE
2918 */
2919 static gboolean
localhistory_button_clicked_cb(GtkWidget * widget,gpointer data)2920 localhistory_button_clicked_cb(GtkWidget *widget, gpointer data)
2921 {
2922 nserror res;
2923 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2924 struct browser_window *bw;
2925 GtkWidget *toplevel;
2926
2927 toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
2928 if (toplevel != NULL) {
2929 bw = tb->get_bw(tb->get_ctx);
2930
2931 res = nsgtk_local_history_present(GTK_WINDOW(toplevel), bw);
2932 if (res != NSERROR_OK) {
2933 NSLOG(netsurf, INFO,
2934 "Unable to present local history window.");
2935 }
2936 }
2937 return TRUE;
2938 }
2939
2940 /**
2941 * handler for history tool bar item clicked signal
2942 *
2943 * \param widget The widget the signal is being delivered to.
2944 * \param data The toolbar context passed when the signal was connected
2945 * \return TRUE
2946 */
2947 static gboolean
history_button_clicked_cb(GtkWidget * widget,gpointer data)2948 history_button_clicked_cb(GtkWidget *widget, gpointer data)
2949 {
2950 return localhistory_button_clicked_cb(widget, data);
2951 }
2952
2953
2954 /**
2955 * handler for global history tool bar item clicked signal
2956 *
2957 * \param widget The widget the signal is being delivered to.
2958 * \param data The toolbar context passed when the signal was connected
2959 * \return TRUE
2960 */
2961 static gboolean
globalhistory_button_clicked_cb(GtkWidget * widget,gpointer data)2962 globalhistory_button_clicked_cb(GtkWidget *widget, gpointer data)
2963 {
2964 nserror res;
2965 res = nsgtk_global_history_present();
2966 if (res != NSERROR_OK) {
2967 NSLOG(netsurf, INFO,
2968 "Unable to initialise global history window.");
2969 }
2970 return TRUE;
2971 }
2972
2973
2974 /**
2975 * handler for add bookmark tool bar item clicked signal
2976 *
2977 * \param widget The widget the signal is being delivered to.
2978 * \param data The toolbar context passed when the signal was connected
2979 * \return TRUE
2980 */
2981 static gboolean
addbookmarks_button_clicked_cb(GtkWidget * widget,gpointer data)2982 addbookmarks_button_clicked_cb(GtkWidget *widget, gpointer data)
2983 {
2984 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
2985 struct browser_window *bw;
2986
2987 bw = tb->get_bw(tb->get_ctx);
2988 if (browser_window_has_content(bw)) {
2989 hotlist_add_url(browser_window_access_url(bw));
2990 }
2991 return TRUE;
2992 }
2993
2994
2995 /**
2996 * handler for show bookmark tool bar item clicked signal
2997 *
2998 * \param widget The widget the signal is being delivered to.
2999 * \param data The toolbar context passed when the signal was connected
3000 * \return TRUE
3001 */
3002 static gboolean
showbookmarks_button_clicked_cb(GtkWidget * widget,gpointer data)3003 showbookmarks_button_clicked_cb(GtkWidget *widget, gpointer data)
3004 {
3005 nserror res;
3006 res = nsgtk_hotlist_present();
3007 if (res != NSERROR_OK) {
3008 NSLOG(netsurf, INFO, "Unable to initialise bookmark window.");
3009 }
3010 return TRUE;
3011 }
3012
3013
3014 /**
3015 * handler for show cookies tool bar item clicked signal
3016 *
3017 * \param widget The widget the signal is being delivered to.
3018 * \param data The toolbar context passed when the signal was connected
3019 * \return TRUE
3020 */
3021 static gboolean
showcookies_button_clicked_cb(GtkWidget * widget,gpointer data)3022 showcookies_button_clicked_cb(GtkWidget *widget, gpointer data)
3023 {
3024 nserror res;
3025 res = nsgtk_cookies_present(NULL);
3026 if (res != NSERROR_OK) {
3027 NSLOG(netsurf, INFO, "Unable to initialise cookies window.");
3028 }
3029 return TRUE;
3030 }
3031
3032
3033 /**
3034 * handler for open location tool bar item clicked signal
3035 *
3036 * \param widget The widget the signal is being delivered to.
3037 * \param data The toolbar context passed when the signal was connected
3038 * \return TRUE
3039 */
3040 static gboolean
openlocation_button_clicked_cb(GtkWidget * widget,gpointer data)3041 openlocation_button_clicked_cb(GtkWidget *widget, gpointer data)
3042 {
3043 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
3044 GtkToolItem *urltitem;
3045
3046 urltitem = tb->items[URL_BAR_ITEM].button;
3047 if (urltitem != NULL) {
3048 GtkEntry *entry;
3049 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(urltitem)));
3050 gtk_widget_grab_focus(GTK_WIDGET(entry));
3051 }
3052 return TRUE;
3053 }
3054
3055
3056 /**
3057 * handler for contents tool bar item clicked signal
3058 *
3059 * \param widget The widget the signal is being delivered to.
3060 * \param data The toolbar context passed when the signal was connected
3061 * \return TRUE
3062 */
3063 static gboolean
contents_button_clicked_cb(GtkWidget * widget,gpointer data)3064 contents_button_clicked_cb(GtkWidget *widget, gpointer data)
3065 {
3066 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
3067 nserror res;
3068
3069 res = toolbar_navigate_to_url(tb, "http://www.netsurf-browser.org/documentation/");
3070 if (res != NSERROR_OK) {
3071 nsgtk_warning(messages_get_errorcode(res), 0);
3072 }
3073
3074 return TRUE;
3075 }
3076
3077 /**
3078 * handler for contents tool bar item clicked signal
3079 *
3080 * \param widget The widget the signal is being delivered to.
3081 * \param data The toolbar context passed when the signal was connected
3082 * \return TRUE
3083 */
3084 static gboolean
guide_button_clicked_cb(GtkWidget * widget,gpointer data)3085 guide_button_clicked_cb(GtkWidget *widget, gpointer data)
3086 {
3087 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
3088 nserror res;
3089
3090 res = toolbar_navigate_to_url(tb, "http://www.netsurf-browser.org/documentation/guide");
3091 if (res != NSERROR_OK) {
3092 nsgtk_warning(messages_get_errorcode(res), 0);
3093 }
3094
3095 return TRUE;
3096 }
3097
3098
3099 /**
3100 * handler for contents tool bar item clicked signal
3101 *
3102 * \param widget The widget the signal is being delivered to.
3103 * \param data The toolbar context passed when the signal was connected
3104 * \return TRUE
3105 */
3106 static gboolean
info_button_clicked_cb(GtkWidget * widget,gpointer data)3107 info_button_clicked_cb(GtkWidget *widget, gpointer data)
3108 {
3109 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
3110 nserror res;
3111
3112 res = toolbar_navigate_to_url(tb, "http://www.netsurf-browser.org/documentation/info");
3113 if (res != NSERROR_OK) {
3114 nsgtk_warning(messages_get_errorcode(res), 0);
3115 }
3116
3117 return TRUE;
3118 }
3119
3120
3121 /**
3122 * handler for contents tool bar item clicked signal
3123 *
3124 * \param widget The widget the signal is being delivered to.
3125 * \param data The toolbar context passed when the signal was connected
3126 * \return TRUE
3127 */
about_button_clicked_cb(GtkWidget * widget,gpointer data)3128 static gboolean about_button_clicked_cb(GtkWidget *widget, gpointer data)
3129 {
3130 GtkWindow *parent; /* gtk window widget is in */
3131
3132 parent = GTK_WINDOW(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW));
3133
3134 nsgtk_about_dialog_init(parent);
3135 return TRUE;
3136 }
3137
3138 /**
3139 * handler for openmenu tool bar item clicked signal
3140 *
3141 * \param widget The widget the signal is being delivered to.
3142 * \param data The toolbar context passed when the signal was connected
3143 * \return TRUE to indicate signal handled.
3144 */
openmenu_button_clicked_cb(GtkWidget * widget,gpointer data)3145 static gboolean openmenu_button_clicked_cb(GtkWidget *widget, gpointer data)
3146 {
3147 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
3148 struct gui_window *gw;
3149 struct nsgtk_scaffolding *gs;
3150
3151 gw = tb->get_ctx; /** \todo stop assuming the context is a gui window */
3152
3153 gs = nsgtk_get_scaffold(gw);
3154
3155 nsgtk_scaffolding_burger_menu(gs);
3156
3157 return TRUE;
3158 }
3159
3160
3161 /* define data plus and data minus handlers */
3162 #define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate, label, iconame) \
3163 static gboolean \
3164 nsgtk_toolbar_##name##_data_plus(GtkWidget *widget, \
3165 GdkDragContext *cont, \
3166 GtkSelectionData *selection, \
3167 guint info, \
3168 guint time, \
3169 gpointer data) \
3170 { \
3171 struct nsgtk_toolbar_customisation *tbc; \
3172 tbc = (struct nsgtk_toolbar_customisation *)data; \
3173 tbc->dragitem = identifier; \
3174 tbc->dragfrom = true; \
3175 return TRUE; \
3176 } \
3177 static gboolean \
3178 nsgtk_toolbar_##name##_data_minus(GtkWidget *widget, \
3179 GdkDragContext *cont, \
3180 GtkSelectionData *selection, \
3181 guint info, \
3182 guint time, \
3183 gpointer data) \
3184 { \
3185 struct nsgtk_toolbar_customisation *tbc; \
3186 tbc = (struct nsgtk_toolbar_customisation *)data; \
3187 tbc->dragitem = identifier; \
3188 tbc->dragfrom = false; \
3189 return TRUE; \
3190 }
3191
3192 #include "gtk/toolbar_items.h"
3193
3194 #undef TOOLBAR_ITEM
3195
3196
3197 /**
3198 * create a toolbar item
3199 *
3200 * create a toolbar item and set up its default handlers
3201 */
3202 static nserror
toolbar_item_create(nsgtk_toolbar_button id,struct nsgtk_toolbar_item * item)3203 toolbar_item_create(nsgtk_toolbar_button id, struct nsgtk_toolbar_item *item)
3204 {
3205 item->location = INACTIVE_LOCATION;
3206
3207 /* set item defaults from macro */
3208 switch (id) {
3209 #define TOOLBAR_ITEM_t(name) \
3210 item->clicked = name##_button_clicked_cb;
3211 #define TOOLBAR_ITEM_b(name) \
3212 item->clicked = name##_button_clicked_cb;
3213 #define TOOLBAR_ITEM_y(name) \
3214 item->clicked = name##_button_clicked_cb;
3215 #define TOOLBAR_ITEM_n(name) \
3216 item->clicked = NULL;
3217 #define TOOLBAR_ITEM(identifier, iname, snstvty, clicked, activate, label, iconame) \
3218 case identifier: \
3219 item->name = #iname; \
3220 item->sensitivity = snstvty; \
3221 item->dataplus = nsgtk_toolbar_##iname##_data_plus; \
3222 item->dataminus = nsgtk_toolbar_##iname##_data_minus; \
3223 TOOLBAR_ITEM_ ## clicked(iname) \
3224 break;
3225
3226 #include "gtk/toolbar_items.h"
3227
3228 #undef TOOLBAR_ITEM_t
3229 #undef TOOLBAR_ITEM_y
3230 #undef TOOLBAR_ITEM_n
3231 #undef TOOLBAR_ITEM
3232
3233 case PLACEHOLDER_BUTTON:
3234 return NSERROR_INVALID;
3235 }
3236
3237 return NSERROR_OK;
3238 }
3239
3240
3241 /**
3242 * set a toolbar item to a throbber frame number
3243 *
3244 * \param toolbar_item The toolbar item to update
3245 * \param frame The animation frame number to update to
3246 * \return NSERROR_OK on success,
3247 * NSERROR_INVALID if the toolbar item does not contain an image,
3248 * NSERROR_BAD_SIZE if the frame is out of range.
3249 */
set_throbber_frame(GtkToolItem * toolbar_item,int frame)3250 static nserror set_throbber_frame(GtkToolItem *toolbar_item, int frame)
3251 {
3252 nserror res;
3253 GdkPixbuf *pixbuf;
3254 GtkImage *throbber;
3255
3256 if (toolbar_item == NULL) {
3257 /* no toolbar item */
3258 return NSERROR_INVALID;
3259 }
3260
3261 res = nsgtk_throbber_get_frame(frame, &pixbuf);
3262 if (res != NSERROR_OK) {
3263 return res;
3264 }
3265
3266 throbber = GTK_IMAGE(gtk_bin_get_child(GTK_BIN(toolbar_item)));
3267
3268 gtk_image_set_from_pixbuf(throbber, pixbuf);
3269
3270 return NSERROR_OK;
3271 }
3272
3273
3274 /**
3275 * Make the throbber run.
3276 *
3277 * scheduled callback to update the throbber
3278 *
3279 * \param p The context passed when scheduled.
3280 */
next_throbber_frame(void * p)3281 static void next_throbber_frame(void *p)
3282 {
3283 struct nsgtk_toolbar *tb = p;
3284 nserror res;
3285
3286 tb->throb_frame++; /* advance to next frame */
3287
3288 res = set_throbber_frame(tb->items[THROBBER_ITEM].button,
3289 tb->throb_frame);
3290 if (res == NSERROR_BAD_SIZE) {
3291 tb->throb_frame = 1;
3292 res = set_throbber_frame(tb->items[THROBBER_ITEM].button,
3293 tb->throb_frame);
3294 }
3295
3296 /* only schedule next frame if there are no errors */
3297 if (res == NSERROR_OK) {
3298 nsgtk_schedule(THROBBER_FRAME_TIME, next_throbber_frame, p);
3299 }
3300 }
3301
3302
3303 /**
3304 * connect signal handlers to a gtk toolbar item
3305 */
3306 static nserror
toolbar_connect_signal(struct nsgtk_toolbar * tb,nsgtk_toolbar_button itemid)3307 toolbar_connect_signal(struct nsgtk_toolbar *tb, nsgtk_toolbar_button itemid)
3308 {
3309 struct nsgtk_toolbar_item *item;
3310 GtkEntry *entry;
3311
3312 item = &tb->items[itemid];
3313
3314 if (item->button != NULL) {
3315 g_signal_connect(item->button,
3316 "size-allocate",
3317 G_CALLBACK(toolbar_item_size_allocate_cb),
3318 tb);
3319 }
3320
3321 switch (itemid) {
3322 case URL_BAR_ITEM:
3323 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(item->button)));
3324
3325 g_signal_connect(GTK_WIDGET(entry),
3326 "activate",
3327 G_CALLBACK(url_entry_activate_cb),
3328 tb);
3329 g_signal_connect(GTK_WIDGET(entry),
3330 "changed",
3331 G_CALLBACK(url_entry_changed_cb),
3332 tb);
3333 g_signal_connect(GTK_WIDGET(entry),
3334 "icon-release",
3335 G_CALLBACK(url_entry_icon_release_cb),
3336 tb);
3337
3338 nsgtk_completion_connect_signals(entry,
3339 tb->get_bw,
3340 tb->get_ctx);
3341 break;
3342
3343
3344 case WEBSEARCH_ITEM:
3345 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(item->button)));
3346
3347 g_signal_connect(GTK_WIDGET(entry),
3348 "activate",
3349 G_CALLBACK(websearch_entry_activate_cb),
3350 tb);
3351 g_signal_connect(GTK_WIDGET(entry),
3352 "button-press-event",
3353 G_CALLBACK(websearch_entry_button_press_cb),
3354 tb);
3355 break;
3356
3357 default:
3358 if ((item->clicked != NULL) && (item->button != NULL)) {
3359 g_signal_connect(item->button,
3360 "clicked",
3361 G_CALLBACK(item->clicked),
3362 tb);
3363 }
3364 break;
3365
3366 }
3367
3368 return NSERROR_OK;
3369 }
3370
3371 /**
3372 * connect all signals to widgets in a toolbar
3373 */
toolbar_connect_signals(struct nsgtk_toolbar * tb)3374 static nserror toolbar_connect_signals(struct nsgtk_toolbar *tb)
3375 {
3376 int location; /* location index */
3377 nsgtk_toolbar_button itemid; /* item id */
3378
3379 for (location = BACK_BUTTON; location < PLACEHOLDER_BUTTON; location++) {
3380 itemid = itemid_from_location(tb, location);
3381 if (itemid == PLACEHOLDER_BUTTON) {
3382 /* no more filled locations */
3383 break;
3384 }
3385 toolbar_connect_signal(tb, itemid);
3386 }
3387
3388 return NSERROR_OK;
3389 }
3390
3391
3392 /**
3393 * signal handler for toolbar context menu
3394 *
3395 * \param toolbar The toolbar event is being delivered to
3396 * \param x The x coordinate where the click happened
3397 * \param y The x coordinate where the click happened
3398 * \param button the buttons being pressed
3399 * \param data The context pointer passed when the connection was made.
3400 * \return TRUE to indicate signal handled.
3401 */
3402 static gboolean
toolbar_popup_context_menu_cb(GtkToolbar * toolbar,gint x,gint y,gint button,gpointer data)3403 toolbar_popup_context_menu_cb(GtkToolbar *toolbar,
3404 gint x,
3405 gint y,
3406 gint button,
3407 gpointer data)
3408 {
3409 struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
3410 struct gui_window *gw;
3411 struct nsgtk_scaffolding *gs;
3412
3413 gw = tb->get_ctx; /** \todo stop assuming the context is a gui window */
3414
3415 gs = nsgtk_get_scaffold(gw);
3416
3417 nsgtk_scaffolding_toolbar_context_menu(gs);
3418
3419 return TRUE;
3420 }
3421
3422
3423 /**
3424 * toolbar delete signal handler
3425 */
toolbar_destroy_cb(GtkWidget * widget,gpointer data)3426 static void toolbar_destroy_cb(GtkWidget *widget, gpointer data)
3427 {
3428 struct nsgtk_toolbar *tb;
3429 tb = (struct nsgtk_toolbar *)data;
3430
3431 /* ensure any throbber scheduled is stopped */
3432 nsgtk_schedule(-1, next_throbber_frame, tb);
3433
3434 free(tb);
3435 }
3436
3437
3438 /* exported interface documented in toolbar.h */
3439 nserror
nsgtk_toolbar_create(GtkBuilder * builder,struct browser_window * (* get_bw)(void * ctx),void * get_ctx,bool want_location_focus,struct nsgtk_toolbar ** tb_out)3440 nsgtk_toolbar_create(GtkBuilder *builder,
3441 struct browser_window *(*get_bw)(void *ctx),
3442 void *get_ctx,
3443 bool want_location_focus,
3444 struct nsgtk_toolbar **tb_out)
3445 {
3446 nserror res;
3447 struct nsgtk_toolbar *tb;
3448 int bidx; /* button index */
3449
3450 tb = calloc(1, sizeof(struct nsgtk_toolbar));
3451 if (tb == NULL) {
3452 return NSERROR_NOMEM;
3453 }
3454
3455 tb->get_bw = get_bw;
3456 tb->get_ctx = get_ctx;
3457 /* set the throbber start frame. */
3458 tb->throb_frame = 0;
3459 if (want_location_focus) {
3460 tb->loc_focus = LFS_WANT;
3461 } else {
3462 tb->loc_focus = LFS_IDLE;
3463 }
3464
3465 tb->widget = GTK_TOOLBAR(gtk_builder_get_object(builder, "toolbar"));
3466 gtk_toolbar_set_show_arrow(tb->widget, TRUE);
3467
3468 g_signal_connect(tb->widget,
3469 "popup-context-menu",
3470 G_CALLBACK(toolbar_popup_context_menu_cb),
3471 tb);
3472
3473 /* close and cleanup on delete signal */
3474 g_signal_connect(tb->widget,
3475 "destroy",
3476 G_CALLBACK(toolbar_destroy_cb),
3477 tb);
3478
3479 /* allocate button contexts */
3480 for (bidx = BACK_BUTTON; bidx < PLACEHOLDER_BUTTON; bidx++) {
3481 res = toolbar_item_create(bidx, &tb->items[bidx]);
3482 if (res != NSERROR_OK) {
3483 return res;
3484 }
3485 }
3486
3487 res = nsgtk_toolbar_update(tb);
3488 if (res != NSERROR_OK) {
3489 return res;
3490 }
3491
3492 *tb_out = tb;
3493 return NSERROR_OK;
3494 }
3495
3496
3497 /* exported interface documented in toolbar.h */
nsgtk_toolbar_restyle(struct nsgtk_toolbar * tb)3498 nserror nsgtk_toolbar_restyle(struct nsgtk_toolbar *tb)
3499 {
3500 /*
3501 * reset toolbar size allocation so icon size change affects
3502 * allocated widths.
3503 */
3504 tb->offset = 0;
3505
3506 switch (nsoption_int(button_type)) {
3507
3508 case 1: /* Small icons */
3509 gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget),
3510 GTK_TOOLBAR_ICONS);
3511 gtk_toolbar_set_icon_size(GTK_TOOLBAR(tb->widget),
3512 GTK_ICON_SIZE_SMALL_TOOLBAR);
3513 break;
3514
3515 case 2: /* Large icons */
3516 gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget),
3517 GTK_TOOLBAR_ICONS);
3518 gtk_toolbar_set_icon_size(GTK_TOOLBAR(tb->widget),
3519 GTK_ICON_SIZE_LARGE_TOOLBAR);
3520 break;
3521
3522 case 3: /* Large icons with text */
3523 gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget),
3524 GTK_TOOLBAR_BOTH);
3525 gtk_toolbar_set_icon_size(GTK_TOOLBAR(tb->widget),
3526 GTK_ICON_SIZE_LARGE_TOOLBAR);
3527 break;
3528
3529 case 4: /* Text icons only */
3530 gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget),
3531 GTK_TOOLBAR_TEXT);
3532 break;
3533
3534 default:
3535 break;
3536 }
3537
3538 return NSERROR_OK;
3539 }
3540
3541
3542 /* exported interface documented in toolbar.h */
nsgtk_toolbar_throbber(struct nsgtk_toolbar * tb,bool active)3543 nserror nsgtk_toolbar_throbber(struct nsgtk_toolbar *tb, bool active)
3544 {
3545 nserror res;
3546 struct browser_window *bw;
3547
3548 /* Manage the location focus state */
3549 switch (tb->loc_focus) {
3550 case LFS_IDLE:
3551 break;
3552 case LFS_WANT:
3553 if (active) {
3554 tb->loc_focus = LFS_THROB;
3555 }
3556 break;
3557 case LFS_THROB:
3558 if (!active) {
3559 tb->loc_focus = LFS_LAST;
3560 }
3561 break;
3562 case LFS_LAST:
3563 break;
3564 }
3565
3566 /* when activating the throbber simply schedule the next frame update */
3567 if (active) {
3568 nsgtk_schedule(THROBBER_FRAME_TIME, next_throbber_frame, tb);
3569
3570 set_item_sensitivity(&tb->items[STOP_BUTTON], true);
3571 set_item_sensitivity(&tb->items[RELOAD_BUTTON], false);
3572 set_item_action(tb, RELOADSTOP_BUTTON, false);
3573
3574 return NSERROR_OK;
3575 }
3576
3577 /* stopping the throbber */
3578 nsgtk_schedule(-1, next_throbber_frame, tb);
3579 tb->throb_frame = 0;
3580 res = set_throbber_frame(tb->items[THROBBER_ITEM].button,
3581 tb->throb_frame);
3582
3583 bw = tb->get_bw(tb->get_ctx);
3584
3585 /* adjust sensitivity of other items */
3586 set_item_sensitivity(&tb->items[STOP_BUTTON], false);
3587 set_item_sensitivity(&tb->items[RELOAD_BUTTON], true);
3588 set_item_action(tb, RELOADSTOP_BUTTON, true);
3589 set_item_sensitivity(&tb->items[BACK_BUTTON],
3590 browser_window_history_back_available(bw));
3591 set_item_sensitivity(&tb->items[FORWARD_BUTTON],
3592 browser_window_history_forward_available(bw));
3593 nsgtk_local_history_hide();
3594
3595 return res;
3596 }
3597
3598
3599 /* exported interface documented in toolbar.h */
nsgtk_toolbar_page_info_change(struct nsgtk_toolbar * tb)3600 nserror nsgtk_toolbar_page_info_change(struct nsgtk_toolbar *tb)
3601 {
3602 GtkEntry *url_entry;
3603 browser_window_page_info_state pistate;
3604 struct browser_window *bw;
3605 const char *icon_name;
3606
3607 if (tb->items[URL_BAR_ITEM].button == NULL) {
3608 /* no toolbar item */
3609 return NSERROR_INVALID;
3610 }
3611 url_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tb->items[URL_BAR_ITEM].button)));
3612
3613 bw = tb->get_bw(tb->get_ctx);
3614
3615 pistate = browser_window_get_page_info_state(bw);
3616
3617 switch (pistate) {
3618 case PAGE_STATE_INTERNAL:
3619 icon_name = "page-info-internal";
3620 break;
3621
3622 case PAGE_STATE_LOCAL:
3623 icon_name = "page-info-local";
3624 break;
3625
3626 case PAGE_STATE_INSECURE:
3627 icon_name = "page-info-insecure";
3628 break;
3629
3630 case PAGE_STATE_SECURE_OVERRIDE:
3631 icon_name = "page-info-warning";
3632 break;
3633
3634 case PAGE_STATE_SECURE_ISSUES:
3635 icon_name = "page-info-warning";
3636 break;
3637
3638 case PAGE_STATE_SECURE:
3639 icon_name = "page-info-secure";
3640 break;
3641
3642 default:
3643 icon_name = "page-info-internal";
3644 break;
3645 }
3646
3647 nsgtk_entry_set_icon_from_icon_name(GTK_WIDGET(url_entry),
3648 GTK_ENTRY_ICON_PRIMARY,
3649 icon_name);
3650 return NSERROR_OK;
3651 }
3652
3653
3654 /* exported interface documented in toolbar.h */
nsgtk_toolbar_set_url(struct nsgtk_toolbar * tb,nsurl * url)3655 nserror nsgtk_toolbar_set_url(struct nsgtk_toolbar *tb, nsurl *url)
3656 {
3657 size_t idn_url_l;
3658 char *idn_url_s = NULL;
3659 const char *url_text = NULL;
3660 GtkEntry *url_entry;
3661
3662 if (tb->items[URL_BAR_ITEM].button == NULL) {
3663 /* no toolbar item */
3664 return NSERROR_INVALID;
3665 }
3666 url_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tb->items[URL_BAR_ITEM].button)));
3667
3668 if (nsoption_bool(display_decoded_idn) == true) {
3669 if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) != NSERROR_OK) {
3670 idn_url_s = NULL;
3671 }
3672 url_text = idn_url_s;
3673 }
3674 if (url_text == NULL) {
3675 url_text = nsurl_access(url);
3676 }
3677
3678 if (strcmp(url_text, gtk_entry_get_text(url_entry)) != 0) {
3679 /* The URL bar content has changed, we need to update it */
3680 gint startpos, endpos;
3681 bool was_selected;
3682 gtk_editable_get_selection_bounds(GTK_EDITABLE(url_entry),
3683 &startpos, &endpos);
3684 was_selected = gtk_widget_is_focus(GTK_WIDGET(url_entry)) &&
3685 startpos == 0 &&
3686 endpos == gtk_entry_get_text_length(url_entry);
3687 gtk_entry_set_text(url_entry, url_text);
3688 if (was_selected && tb->loc_focus != LFS_IDLE) {
3689 gtk_widget_grab_focus(GTK_WIDGET(url_entry));
3690 if (tb->loc_focus == LFS_LAST) {
3691 tb->loc_focus = LFS_IDLE;
3692 }
3693 }
3694 }
3695
3696 if (idn_url_s != NULL) {
3697 free(idn_url_s);
3698 }
3699
3700 return NSERROR_OK;
3701 }
3702
3703
3704 /* exported interface documented in toolbar.h */
3705 nserror
nsgtk_toolbar_set_websearch_image(struct nsgtk_toolbar * tb,GdkPixbuf * pixbuf)3706 nsgtk_toolbar_set_websearch_image(struct nsgtk_toolbar *tb, GdkPixbuf *pixbuf)
3707 {
3708 GtkWidget *entry;
3709
3710 if (tb->items[WEBSEARCH_ITEM].button == NULL) {
3711 /* no toolbar item */
3712 return NSERROR_INVALID;
3713 }
3714
3715 entry = gtk_bin_get_child(GTK_BIN(tb->items[WEBSEARCH_ITEM].button));
3716
3717 if (pixbuf != NULL) {
3718 nsgtk_entry_set_icon_from_pixbuf(entry,
3719 GTK_ENTRY_ICON_PRIMARY,
3720 pixbuf);
3721 } else {
3722 nsgtk_entry_set_icon_from_icon_name(entry,
3723 GTK_ENTRY_ICON_PRIMARY,
3724 NSGTK_STOCK_INFO);
3725 }
3726
3727 return NSERROR_OK;
3728 }
3729
3730
3731 /* exported interface documented in toolbar.h */
3732 nserror
nsgtk_toolbar_item_activate(struct nsgtk_toolbar * tb,nsgtk_toolbar_button itemid)3733 nsgtk_toolbar_item_activate(struct nsgtk_toolbar *tb,
3734 nsgtk_toolbar_button itemid)
3735 {
3736 GtkWidget *widget;
3737
3738 /* ensure item id in range */
3739 if ((itemid < BACK_BUTTON) || (itemid >= PLACEHOLDER_BUTTON)) {
3740 return NSERROR_BAD_PARAMETER;
3741 }
3742
3743 if (tb->items[itemid].clicked == NULL) {
3744 return NSERROR_INVALID;
3745 }
3746
3747 /*
3748 * if item has a widget in the current toolbar use that as the
3749 * signal source otherwise use the toolbar widget itself.
3750 */
3751 if (tb->items[itemid].button != NULL) {
3752 widget = GTK_WIDGET(tb->items[itemid].button);
3753 } else {
3754 widget = GTK_WIDGET(tb->widget);
3755 }
3756
3757 tb->items[itemid].clicked(widget, tb);
3758
3759 return NSERROR_OK;
3760 }
3761
3762
3763 /* exported interface documented in toolbar.h */
nsgtk_toolbar_show(struct nsgtk_toolbar * tb,bool show)3764 nserror nsgtk_toolbar_show(struct nsgtk_toolbar *tb, bool show)
3765 {
3766 if (show) {
3767 gtk_widget_show(GTK_WIDGET(tb->widget));
3768 } else {
3769 gtk_widget_hide(GTK_WIDGET(tb->widget));
3770 }
3771 return NSERROR_OK;
3772 }
3773
3774
3775 /* exported interface documented in toolbar.h */
nsgtk_toolbar_update(struct nsgtk_toolbar * tb)3776 nserror nsgtk_toolbar_update(struct nsgtk_toolbar *tb)
3777 {
3778 nserror res;
3779
3780 /* setup item locations based on user config */
3781 res = apply_user_button_customisation(tb);
3782 if (res != NSERROR_OK) {
3783 return res;
3784 }
3785
3786 /* populate toolbar widget */
3787 res = populate_gtk_toolbar_widget(tb);
3788 if (res != NSERROR_OK) {
3789 return res;
3790 }
3791
3792 /* ensure icon sizes and text labels on toolbar are set */
3793 res = nsgtk_toolbar_restyle(tb);
3794 if (res != NSERROR_OK) {
3795 return res;
3796 }
3797
3798 res = toolbar_connect_signals(tb);
3799
3800 return res;
3801 }
3802
3803 /**
3804 * Find the correct location for popping up a window for the chosen item.
3805 *
3806 * \param tb The toolbar to select from
3807 * \param item_idx The toolbar item to select from
3808 * \param out_x Filled with an appropriate X coordinate
3809 * \param out_y Filled with an appropriate Y coordinate
3810 */
3811 static nserror
nsgtk_toolbar_get_icon_window_position(struct nsgtk_toolbar * tb,int item_idx,int * out_x,int * out_y)3812 nsgtk_toolbar_get_icon_window_position(struct nsgtk_toolbar *tb,
3813 int item_idx,
3814 int *out_x,
3815 int *out_y)
3816 {
3817 struct nsgtk_toolbar_item *item = &tb->items[item_idx];
3818 GtkWidget *widget = GTK_WIDGET(item->button);
3819 GtkAllocation alloc;
3820 gint rootx, rooty, x, y;
3821
3822 switch (item_idx) {
3823 case URL_BAR_ITEM:
3824 widget = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(item->button)));
3825 break;
3826 default:
3827 /* Nothing to do here */
3828 break;
3829 }
3830
3831 nsgtk_widget_get_allocation(widget, &alloc);
3832
3833 if (gtk_widget_translate_coordinates(widget,
3834 gtk_widget_get_toplevel(widget),
3835 0,
3836 alloc.height - 1,
3837 &x, &y) != TRUE) {
3838 return NSERROR_UNKNOWN;
3839 }
3840
3841 gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
3842 &rootx, &rooty);
3843
3844 *out_x = rootx + x + 4;
3845 *out_y = rooty + y + 4;
3846
3847 return NSERROR_OK;
3848 }
3849
nsgtk_toolbar_position_page_info(struct nsgtk_toolbar * tb,struct nsgtk_pi_window * win)3850 nserror nsgtk_toolbar_position_page_info(struct nsgtk_toolbar *tb,
3851 struct nsgtk_pi_window *win)
3852 {
3853 nserror res;
3854 int x, y;
3855
3856 res = nsgtk_toolbar_get_icon_window_position(tb, URL_BAR_ITEM, &x, &y);
3857 if (res != NSERROR_OK) {
3858 return res;
3859 }
3860
3861 nsgtk_page_info_set_position(win, x, y);
3862
3863 return NSERROR_OK;
3864 }
3865
3866 /* exported interface documented in toolbar.h */
nsgtk_toolbar_position_local_history(struct nsgtk_toolbar * tb)3867 nserror nsgtk_toolbar_position_local_history(struct nsgtk_toolbar *tb)
3868 {
3869 nserror res;
3870 int x, y;
3871
3872 res = nsgtk_toolbar_get_icon_window_position(tb, HISTORY_BUTTON, &x, &y);
3873 if (res != NSERROR_OK) {
3874 return res;
3875 }
3876
3877 nsgtk_local_history_set_position(x, y);
3878
3879 return NSERROR_OK;
3880 }
3881