diff options
Diffstat (limited to 'frontends/gtk/toolbar.c')
-rw-r--r-- | frontends/gtk/toolbar.c | 4564 |
1 files changed, 3479 insertions, 1085 deletions
diff --git a/frontends/gtk/toolbar.c b/frontends/gtk/toolbar.c index e93bd49f9..6ec41cc1d 100644 --- a/frontends/gtk/toolbar.c +++ b/frontends/gtk/toolbar.c @@ -1,5 +1,5 @@ /* - * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net> + * Copyright 2019 Vincent Sanders <vince@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,18 +16,36 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/** + * \file + * implementation of toolbar to control browsing context + */ + #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <errno.h> #include <gtk/gtk.h> -#include "netsurf/browser_window.h" -#include "desktop/searchweb.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/nsoption.h" #include "utils/file.h" +#include "utils/nsurl.h" +#include "utils/corestrings.h" +#include "desktop/browser_history.h" +#include "desktop/searchweb.h" +#include "desktop/search.h" +#include "desktop/save_complete.h" +#include "desktop/save_text.h" +#include "desktop/print.h" +#include "desktop/hotlist.h" +#include "netsurf/content.h" +#include "netsurf/browser_window.h" +#include "netsurf/keypress.h" +#include "gtk/toolbar_items.h" +#include "gtk/completion.h" #include "gtk/gui.h" #include "gtk/warn.h" #include "gtk/search.h" @@ -36,63 +54,203 @@ #include "gtk/window.h" #include "gtk/compat.h" #include "gtk/resources.h" +#include "gtk/schedule.h" +#include "gtk/local_history.h" +#include "gtk/global_history.h" +#include "gtk/viewsource.h" +#include "gtk/download.h" +#include "gtk/viewdata.h" +#include "gtk/tabs.h" +#include "gtk/print.h" +#include "gtk/layout_pango.h" +#include "gtk/preferences.h" +#include "gtk/hotlist.h" +#include "gtk/cookies.h" +#include "gtk/about.h" +#include "gtk/gdk.h" +#include "gtk/bitmap.h" +#include "gtk/page_info.h" #include "gtk/toolbar.h" -static GtkTargetEntry entry = {(char *)"nsgtk_button_data", - GTK_TARGET_SAME_APP, 0}; - -static bool edit_mode = false; - -struct nsgtk_toolbar_custom_store { - GtkWidget *window; - GtkWidget *store_buttons[PLACEHOLDER_BUTTON]; - GtkWidget *widgetvbox; - GtkWidget *currentbar; - char numberh; /* current horizontal location while adding */ - GtkBuilder *builder; /* button widgets to store */ - int buttonlocations[PLACEHOLDER_BUTTON]; - int currentbutton; - bool fromstore; -}; -/* the number of buttons that fit in the width of the store window */ -#define NSGTK_STORE_WIDTH 6 +/** + * button location indicating button is not to be shown + */ +#define INACTIVE_LOCATION (-1) -/* the 'standard' width of a button that makes sufficient of its label -visible */ -#define NSGTK_BUTTON_WIDTH 111 +/** + * time (in ms) between throbber animation frame updates + */ +#define THROBBER_FRAME_TIME (100) -/* the 'standard' height of a button that fits as many toolbars as -possible into the store */ +/** + * the minimum number of columns in the tool store + */ +#define NSGTK_MIN_STORE_COLUMNS 4 + +/** + * the 'standard' width of a button that makes sufficient of its label visible + */ +#define NSGTK_BUTTON_WIDTH 120 + +/** + * the 'standard' height of a button that fits as many toolbars as + * possible into the store + */ #define NSGTK_BUTTON_HEIGHT 70 -/* the 'normal' width of the websearch bar */ +/** + * the 'normal' width of the websearch bar + */ #define NSGTK_WEBSEARCH_WIDTH 150 -static struct nsgtk_toolbar_custom_store store; -static struct nsgtk_toolbar_custom_store *window = &store; +/** + * toolbar item context + */ +struct nsgtk_toolbar_item { + + /** + * GTK widget in the toolbar + */ + GtkToolItem *button; + + /** + * location index in toolbar + */ + int location; + + /** + * if the item is currently sensitive in the toolbar + */ + bool sensitivity; + + /** + * textural name used in serialising items + */ + const char *name; + + /** + * button clicked on toolbar handler + */ + gboolean (*clicked)(GtkWidget *widget, gpointer data); + + /** + * handler when dragging from customisation toolbox to toolbar + */ + void *dataplus; + + /** + * handler when dragging from toolbar to customisation toolbox + */ + void *dataminus; +}; +/** + * Location focus state machine + * + * 1. If we don't care, we're in LFS_IDLE + * 2. When we create a new toolbar, we can put it into + * LFS_WANT which means that we want the url bar to focus + * 3. When we start throbbing if we're in LFS_WANT we move to LFS_THROB + * 4. When we stop throbbing, if we're in LFS_THROB we move to LFS_LAST + * + * While not in LFS_IDLE, if the url bar is updated and we previously had it + * fully selected then we reselect it all. If we're in LFS_LAST we move to + * LFS_IDLE at that point. + */ +typedef enum { + LFS_IDLE, /**< Nothing to do */ + LFS_WANT, /**< Want focus, will apply */ + LFS_THROB, /**< Want focus, we have started throbbing */ + LFS_LAST, /**< Last chance for a focus update */ +} nsgtk_toolbar_location_focus_state; -enum image_sets { - IMAGE_SET_MAIN_MENU = 0, - IMAGE_SET_RCLICK_MENU, - IMAGE_SET_POPUP_MENU, - IMAGE_SET_BUTTONS, - IMAGE_SET_COUNT +/** + * control toolbar context + */ +struct nsgtk_toolbar { + /** gtk toolbar widget */ + GtkToolbar *widget; + + /* toolbar size allocation context */ + int offset; + int toolbarmem; + int toolbarbase; + int historybase; + + /** + * Toolbar item contexts + */ + struct nsgtk_toolbar_item items[PLACEHOLDER_BUTTON]; + + /** + * Current frame of throbber animation + */ + int throb_frame; + + /** + * Web search widget + */ + GtkWidget *webSearchEntry; + + /** + * callback to obtain a browser window for navigation + */ + struct browser_window *(*get_bw)(void *ctx); + + /** + * context passed to get_bw function + */ + void *get_ctx; + + /** + * Location focus state machine, current state + */ + nsgtk_toolbar_location_focus_state loc_focus; }; -typedef enum search_buttons { - SEARCH_BACK_BUTTON = 0, - SEARCH_FORWARD_BUTTON, - SEARCH_CLOSE_BUTTON, - SEARCH_BUTTONS_COUNT -} nsgtk_search_buttons; - -struct nsgtk_theme { - GtkImage *image[PLACEHOLDER_BUTTON]; - GtkImage *searchimage[SEARCH_BUTTONS_COUNT]; - /* apng throbber element */ + +/** + * toolbar cusomisation context + */ +struct nsgtk_toolbar_customisation { + /** + * first entry is a toolbar widget so a customisation widget + * can be cast to toolbar and back. + */ + struct nsgtk_toolbar toolbar; + + /** + * The top level container (tabBox) + */ + GtkWidget *container; + + /** + * The vertical box into which the available tools are shown + */ + GtkBox *toolbox; + + /** + * widget handles for items in the customisation toolbox area + */ + GtkToolItem *items[PLACEHOLDER_BUTTON]; + + /** + * which item is being dragged + */ + int dragitem; /* currentbutton */ + /** + * true if item being dragged onto toolbar, false if from toolbar + */ + bool dragfrom; /*fromstore */ + }; + +/* forward declaration */ +static nserror toolbar_item_create(nsgtk_toolbar_button id, + struct nsgtk_toolbar_item *item_out); + + /** * returns a string without its underscores * @@ -122,1372 +280,3608 @@ static char *remove_underscores(const char *s, bool replacespace) /** - * get default image for buttons / menu items from gtk stock items. - * - * \param tbbutton button reference - * \param iconsize The size of icons to select. - * \param usedef Use the default image if not found. - * \return default images. - */ -static GtkImage * -nsgtk_theme_image_default(nsgtk_toolbar_button tbbutton, - GtkIconSize iconsize, - bool usedef) -{ - GtkImage *image; /* The GTK image to return */ - - switch(tbbutton) { - -#define BUTTON_IMAGE(p, q) \ - case p##_BUTTON: \ - image = GTK_IMAGE(nsgtk_image_new_from_stock(q, iconsize)); \ - break - - BUTTON_IMAGE(BACK, NSGTK_STOCK_GO_BACK); - BUTTON_IMAGE(FORWARD, NSGTK_STOCK_GO_FORWARD); - BUTTON_IMAGE(STOP, NSGTK_STOCK_STOP); - BUTTON_IMAGE(RELOAD, NSGTK_STOCK_REFRESH); - BUTTON_IMAGE(HOME, NSGTK_STOCK_HOME); - BUTTON_IMAGE(NEWWINDOW, "gtk-new"); - BUTTON_IMAGE(NEWTAB, "gtk-new"); - BUTTON_IMAGE(OPENFILE, NSGTK_STOCK_OPEN); - BUTTON_IMAGE(CLOSETAB, NSGTK_STOCK_CLOSE); - BUTTON_IMAGE(CLOSEWINDOW, NSGTK_STOCK_CLOSE); - BUTTON_IMAGE(SAVEPAGE, NSGTK_STOCK_SAVE_AS); - BUTTON_IMAGE(PRINTPREVIEW, "gtk-print-preview"); - BUTTON_IMAGE(PRINT, "gtk-print"); - BUTTON_IMAGE(QUIT, "gtk-quit"); - BUTTON_IMAGE(CUT, "gtk-cut"); - BUTTON_IMAGE(COPY, "gtk-copy"); - BUTTON_IMAGE(PASTE, "gtk-paste"); - BUTTON_IMAGE(DELETE, "gtk-delete"); - BUTTON_IMAGE(SELECTALL, "gtk-select-all"); - BUTTON_IMAGE(FIND, NSGTK_STOCK_FIND); - BUTTON_IMAGE(PREFERENCES, "gtk-preferences"); - BUTTON_IMAGE(ZOOMPLUS, "gtk-zoom-in"); - BUTTON_IMAGE(ZOOMMINUS, "gtk-zoom-out"); - BUTTON_IMAGE(ZOOMNORMAL, "gtk-zoom-100"); - BUTTON_IMAGE(FULLSCREEN, "gtk-fullscreen"); - BUTTON_IMAGE(VIEWSOURCE, "gtk-index"); - BUTTON_IMAGE(CONTENTS, "gtk-help"); - BUTTON_IMAGE(ABOUT, "gtk-about"); -#undef BUTTON_IMAGE + * create throbber toolbar item widget + * + * create a gtk entry widget with a completion attached + */ +static GtkToolItem * +make_toolbar_item_throbber(bool sensitivity, bool edit) +{ + nserror res; + GtkToolItem *item; + GdkPixbuf *pixbuf; + GtkWidget *image; - case HISTORY_BUTTON: - image = GTK_IMAGE(gtk_image_new_from_pixbuf(arrow_down_pixbuf)); - break; + res = nsgtk_throbber_get_frame(0, &pixbuf); + if (res != NSERROR_OK) { + return NULL; + } - default: - image = NULL; - break; + if (edit) { + const char *msg; + msg = messages_get("ToolThrob"); + item = gtk_tool_button_new( + GTK_WIDGET(gtk_image_new_from_pixbuf(pixbuf)), + msg); + } else { + item = gtk_tool_item_new(); + + image = gtk_image_new_from_pixbuf(pixbuf); + if (image != NULL) { + nsgtk_widget_set_alignment(image, + GTK_ALIGN_CENTER, + GTK_ALIGN_CENTER); + nsgtk_widget_set_margins(image, 3, 0); + + gtk_container_add(GTK_CONTAINER(item), image); + } + } + gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity); + + return item; +} + + +/** + * create url bar toolbar item widget + * + * create a gtk entry widget with a completion attached + * + * \param sensitivity if the entry should be created sensitive to input + * \param edit if the entry should be editable + */ +static GtkToolItem * +make_toolbar_item_url_bar(bool sensitivity, bool edit) +{ + GtkToolItem *item; + GtkWidget *entry; + GtkEntryCompletion *completion; + + entry = nsgtk_entry_new(); + if (entry == NULL) { + return NULL; } + nsgtk_entry_set_icon_from_icon_name(entry, + GTK_ENTRY_ICON_PRIMARY, + "page-info-internal"); + + if (edit) { + gtk_entry_set_width_chars(GTK_ENTRY(entry), 9); + + item = gtk_tool_button_new(NULL, "URL"); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), entry); + } else { + completion = gtk_entry_completion_new(); + if (completion != NULL) { + gtk_entry_set_completion(GTK_ENTRY(entry), completion); + } + + item = gtk_tool_item_new(); + if (item == NULL) { + return NULL; + } + + gtk_container_add(GTK_CONTAINER(item), entry); + gtk_tool_item_set_expand(item, TRUE); - if (usedef && (image == NULL)) { - image = GTK_IMAGE(nsgtk_image_new_from_stock("gtk-missing-image", iconsize)); } + gtk_widget_set_sensitive(GTK_WIDGET(item), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(entry), sensitivity); - return image; + return item; } + /** - * Get default image for search buttons / menu items from gtk stock items + * create web search toolbar item widget + */ +static GtkToolItem * +make_toolbar_item_websearch(bool sensitivity, bool edit) +{ + GtkToolItem *item; + nserror res; + GtkWidget *entry; + struct bitmap *bitmap; + GdkPixbuf *pixbuf = NULL; + + res = search_web_get_provider_bitmap(&bitmap); + if ((res == NSERROR_OK) && (bitmap != NULL)) { + pixbuf = nsgdk_pixbuf_get_from_surface(bitmap->surface, 32, 32); + } + + entry = nsgtk_entry_new(); + + if (entry == NULL) { + return NULL; + } + + if (pixbuf != NULL) { + nsgtk_entry_set_icon_from_pixbuf(entry, + GTK_ENTRY_ICON_PRIMARY, + pixbuf); + g_object_unref(pixbuf); + } else { + nsgtk_entry_set_icon_from_icon_name(entry, + GTK_ENTRY_ICON_PRIMARY, + NSGTK_STOCK_INFO); + } + + if (edit) { + gtk_entry_set_width_chars(GTK_ENTRY(entry), 9); + + item = gtk_tool_button_new(NULL, "Web Search"); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), + entry); + } else { + gtk_widget_set_size_request(entry, NSGTK_WEBSEARCH_WIDTH, -1); + + item = gtk_tool_item_new(); + if (item == NULL) { + return NULL; + } + + gtk_container_add(GTK_CONTAINER(item), entry); + } + gtk_widget_set_sensitive(GTK_WIDGET(item), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(entry), sensitivity); + + return item; +} + + +/** + * create local history toolbar item widget + */ +static GtkToolItem * +make_toolbar_item_history(bool sensitivity, bool edit) +{ + GtkToolItem *item; + const char *msg = "H"; + char *label = NULL; + + if (edit) { + msg = messages_get("gtkLocalHistory"); + } + label = remove_underscores(msg, false); + item = gtk_tool_button_new(NULL, label); + if (label != NULL) { + free(label); + } + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "local-history"); + + /* set history widget minimum width */ + gtk_widget_set_size_request(GTK_WIDGET(item), 20, -1); + gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity); + + return item; +} + + +/** + * create generic button toolbar item widget + */ +static GtkToolItem * +make_toolbar_item_button(const char *labelmsg, + const char *iconname, + bool sensitivity, + bool edit) +{ + GtkToolItem *item; + char *label = NULL; + + label = remove_underscores(messages_get(labelmsg), false); + + item = gtk_tool_button_new(NULL, label); + if (label != NULL) { + free(label); + } + + if (item != NULL) { + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), iconname); + + gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity); + if (edit) { + nsgtk_widget_set_margins(GTK_WIDGET(item), 0, 0); + } + } + + return item; +} + + +/** + * widget factory for creation of toolbar item widgets * - * \param tbbutton search button reference - * \param iconsize The size of icons to select. - * \param usedef Use the default image if not found. - * \return default search image. + * \param i the id of the widget + * \param theme the theme to make the widgets from + * \return gtk widget */ +static GtkToolItem * +make_toolbar_item(nsgtk_toolbar_button itemid, bool sensitivity) +{ + GtkToolItem *toolitem = NULL; + + switch(itemid) { +#define TOOLBAR_ITEM_y(identifier, label, iconame) +#define TOOLBAR_ITEM_n(identifier, label, iconame) +#define TOOLBAR_ITEM_t(identifier, label, iconame) \ + case identifier: \ + toolitem = make_toolbar_item_button(#label, iconame, sensitivity, false); \ + break; +#define TOOLBAR_ITEM_b(identifier, label, iconame) \ + case identifier: \ + toolitem = make_toolbar_item_button(#label, iconame, sensitivity, false); \ + break; +#define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate, label, iconame) \ + TOOLBAR_ITEM_ ## clicked(identifier, label, iconame) + +#include "gtk/toolbar_items.h" + +#undef TOOLBAR_ITEM_t +#undef TOOLBAR_ITEM_b +#undef TOOLBAR_ITEM_n +#undef TOOLBAR_ITEM_y +#undef TOOLBAR_ITEM + + case HISTORY_BUTTON: + toolitem = make_toolbar_item_history(sensitivity, false); + break; + + case URL_BAR_ITEM: + toolitem = make_toolbar_item_url_bar(sensitivity, false); + break; -static GtkImage * -nsgtk_theme_searchimage_default(nsgtk_search_buttons tbbutton, - GtkIconSize iconsize, - bool usedef) + case THROBBER_ITEM: + toolitem = make_toolbar_item_throbber(sensitivity, false); + break; + + case WEBSEARCH_ITEM: + toolitem = make_toolbar_item_websearch(sensitivity, false); + break; + + default: + break; + + } + return toolitem; +} + + +/** + * widget factory for creation of toolbar item widgets for the toolbox + * + * \param itemid the id of the widget + * \return gtk tool item widget + */ +static GtkToolItem * +make_toolbox_item(nsgtk_toolbar_button itemid, bool bar) { - GtkImage *image; + GtkToolItem *toolitem = NULL; + + switch(itemid) { +#define TOOLBAR_ITEM_y(identifier, label, iconame) +#define TOOLBAR_ITEM_n(identifier, label, iconame) +#define TOOLBAR_ITEM_t(identifier, label, iconame) \ + case identifier: \ + if (bar) { \ + toolitem = make_toolbar_item_button(#label, iconame, true, true); \ + } \ + break; +#define TOOLBAR_ITEM_b(identifier, label, iconame) \ + case identifier: \ + toolitem = make_toolbar_item_button(#label, iconame, true, true); \ + break; +#define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate, label, iconame) \ + TOOLBAR_ITEM_ ## clicked(identifier, label, iconame) + +#include "gtk/toolbar_items.h" - switch (tbbutton) { +#undef TOOLBAR_ITEM_t +#undef TOOLBAR_ITEM_b +#undef TOOLBAR_ITEM_n +#undef TOOLBAR_ITEM_y +#undef TOOLBAR_ITEM + + case HISTORY_BUTTON: + toolitem = make_toolbar_item_history(true, true); + break; - case (SEARCH_BACK_BUTTON): - image = GTK_IMAGE(nsgtk_image_new_from_stock( - NSGTK_STOCK_GO_BACK, iconsize)); + case URL_BAR_ITEM: + toolitem = make_toolbar_item_url_bar(false, true); break; - case (SEARCH_FORWARD_BUTTON): - image = GTK_IMAGE(nsgtk_image_new_from_stock( - NSGTK_STOCK_GO_FORWARD, iconsize)); + case THROBBER_ITEM: + toolitem = make_toolbar_item_throbber(true, true); break; - case (SEARCH_CLOSE_BUTTON): - image = GTK_IMAGE(nsgtk_image_new_from_stock( - NSGTK_STOCK_CLOSE, iconsize)); + case WEBSEARCH_ITEM: + toolitem = make_toolbar_item_websearch(false, true); break; default: - image = NULL; + break; + } + return toolitem; +} + + +/** + * target entry for drag source + */ +static GtkTargetEntry target_entry = { + (char *)"nsgtk_button_data", + GTK_TARGET_SAME_APP, + 0 +}; + - if (usedef && (image == NULL)) { - image = GTK_IMAGE(nsgtk_image_new_from_stock( - "gtk-missing-image", iconsize)); +/** + * find the toolbar item with a given location. + * + * \param tb the toolbar instance + * \param locaction the location to search for + * \return the item id for a location + */ +static nsgtk_toolbar_button +itemid_from_location(struct nsgtk_toolbar *tb, int location) +{ + int iidx; + for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + if (tb->items[iidx].location == location) { + break; + } } + return iidx; +} + - return image; +/** + * save toolbar settings to file + */ +static nserror +nsgtk_toolbar_customisation_save(struct nsgtk_toolbar *tb) +{ + int iidx; /* item index */ + char *order; /* item ordering */ + char *start; /* start of next item name to be output */ + int orderlen = 0; /* length of item ordering */ + nsgtk_toolbar_button itemid; + int location; + char *choices = NULL; + + for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + if (tb->items[iidx].location != INACTIVE_LOCATION) { + orderlen += strlen(tb->items[iidx].name); + orderlen++; /* allow for separator */ + } + } + + /* ensure there are some items to store */ + if (orderlen == 0) { + return NSERROR_INVALID; + } + + order = malloc(orderlen); + if (order == NULL) { + return NSERROR_NOMEM; + } + + start = order; + + for (location = BACK_BUTTON; + location < PLACEHOLDER_BUTTON; + location++) { + int written; + itemid = itemid_from_location(tb, location); + if (itemid == PLACEHOLDER_BUTTON) { + /* no more filled locations */ + break; + } + written = snprintf(start, + orderlen - (start - order), + "%s/", + tb->items[itemid].name); + if ((written < 0) || + (written >= orderlen - (start - order))) { + free(order); + return NSERROR_UNKNOWN; + } + start += written; + + if ((start - order) >= orderlen) { + break; + } + } + + order[orderlen - 1] = 0; + + nsoption_set_charp(toolbar_items, order); + + /* ensure choices are saved */ + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } + + return NSERROR_OK; } + /** - * initialise a theme structure with gtk images + * connect signals to a toolbar item in a customisation toolbar * - * \param iconsize The size of icon to load - * \param usedef use the default gtk icon if unset + * \param tb The toolbar + * \param itemid The item id within to toolbar to connect + * \param NSERROR_OK on success */ -static struct nsgtk_theme *nsgtk_theme_load(GtkIconSize iconsize, bool usedef) +static nserror +toolbar_item_connect_signals(struct nsgtk_toolbar *tb, int itemid) { - struct nsgtk_theme *theme; - int btnloop; + /* set toolbar items to be a drag source */ + gtk_tool_item_set_use_drag_window(tb->items[itemid].button, TRUE); + gtk_drag_source_set(GTK_WIDGET(tb->items[itemid].button), + GDK_BUTTON1_MASK, + &target_entry, + 1, + GDK_ACTION_COPY); + g_signal_connect(tb->items[itemid].button, + "drag-data-get", + G_CALLBACK(tb->items[itemid].dataminus), + tb); + return NSERROR_OK; +} - theme = malloc(sizeof(struct nsgtk_theme)); - if (theme == NULL) { - return NULL; + +/** + * customisation container handler for drag drop signal + * + * called when a widget is dropped onto the store window + */ +static gboolean +customisation_container_drag_drop_cb(GtkWidget *widget, + GdkDragContext *gdc, + gint x, gint y, + guint time, + gpointer data) +{ + struct nsgtk_toolbar_customisation *tbc; + tbc = (struct nsgtk_toolbar_customisation *)data; + int location; + int itemid; + + if ((tbc->dragfrom) || (tbc->dragitem == -1)) { + tbc->dragitem = -1; + return FALSE; } - for (btnloop = BACK_BUTTON; - btnloop < PLACEHOLDER_BUTTON ; - btnloop++) { - theme->image[btnloop] = nsgtk_theme_image_default(btnloop, - iconsize, - usedef); + if (tbc->toolbar.items[tbc->dragitem].location == INACTIVE_LOCATION) { + tbc->dragitem = -1; + gtk_drag_finish(gdc, TRUE, TRUE, time); + return FALSE; + } - for (btnloop = SEARCH_BACK_BUTTON; - btnloop < SEARCH_BUTTONS_COUNT; - btnloop++) { - theme->searchimage[btnloop] = - nsgtk_theme_searchimage_default(btnloop, - iconsize, - usedef); + /* update the locations for all the subsequent toolbar items */ + for (location = tbc->toolbar.items[tbc->dragitem].location; + location < PLACEHOLDER_BUTTON; + location++) { + itemid = itemid_from_location(&tbc->toolbar, location); + if (itemid == PLACEHOLDER_BUTTON) { + break; + } + tbc->toolbar.items[itemid].location--; } - return theme; -} + /* remove existing item */ + tbc->toolbar.items[tbc->dragitem].location = -1; + gtk_container_remove(GTK_CONTAINER(tbc->toolbar.widget), + GTK_WIDGET(tbc->toolbar.items[tbc->dragitem].button)); + + tbc->dragitem = -1; + gtk_drag_finish(gdc, TRUE, TRUE, time); + return FALSE; +} -/* exported function documented in gtk/toolbar.h */ -void nsgtk_theme_implement(struct nsgtk_scaffolding *g) +/** + * customisation container handler for drag motion signal + * + * called when hovering above the store + */ +static gboolean +customisation_container_drag_motion_cb(GtkWidget *widget, + GdkDragContext *gdc, + gint x, gint y, + guint time, + gpointer data) { - struct nsgtk_theme *theme[IMAGE_SET_COUNT]; - int i; - struct nsgtk_button_connect *button; - struct gtk_search *search; + return FALSE; +} - theme[IMAGE_SET_MAIN_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU, false); - theme[IMAGE_SET_RCLICK_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU, false); - theme[IMAGE_SET_POPUP_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU, false); - theme[IMAGE_SET_BUTTONS] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, false); - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) || - (i == WEBSEARCH_ITEM)) - continue; +/** + * customisation toolbar handler for drag drop signal + * + * called when a widget is dropped onto the toolbar + */ +static gboolean +customisation_toolbar_drag_drop_cb(GtkWidget *widget, + GdkDragContext *gdc, + gint x, + gint y, + guint time, + gpointer data) +{ + struct nsgtk_toolbar_customisation *tbc; + tbc = (struct nsgtk_toolbar_customisation *)data; + gint position; /* drop position in toolbar */ + int location; + int itemid; + struct nsgtk_toolbar_item *dragitem; /* toolbar item being dragged */ + + position = gtk_toolbar_get_drop_index(tbc->toolbar.widget, x, y); + if (tbc->dragitem == -1) { + return TRUE; + } - button = nsgtk_scaffolding_button(g, i); - if (button == NULL) - continue; + /* pure conveiance variable */ + dragitem = &tbc->toolbar.items[tbc->dragitem]; - /* gtk_image_menu_item_set_image accepts NULL image */ - if ((button->main != NULL) && - (theme[IMAGE_SET_MAIN_MENU] != NULL)) { - nsgtk_image_menu_item_set_image( - GTK_WIDGET(button->main), - GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->image[i])); - gtk_widget_show_all(GTK_WIDGET(button->main)); + /* deal with replacing existing item in toolbar */ + if (dragitem->location != INACTIVE_LOCATION) { + if (dragitem->location < position) { + position--; } - if ((button->rclick != NULL) && - (theme[IMAGE_SET_RCLICK_MENU] != NULL)) { - nsgtk_image_menu_item_set_image(GTK_WIDGET(button->rclick), - GTK_WIDGET( - theme[IMAGE_SET_RCLICK_MENU]-> - image[i])); - gtk_widget_show_all(GTK_WIDGET(button->rclick)); + + /* update the locations for all the subsequent toolbar items */ + for (location = dragitem->location; + location < PLACEHOLDER_BUTTON; + location++) { + itemid = itemid_from_location(&tbc->toolbar, location); + if (itemid == PLACEHOLDER_BUTTON) { + break; + } + tbc->toolbar.items[itemid].location--; } - if ((button->popup != NULL) && - (theme[IMAGE_SET_POPUP_MENU] != NULL)) { - nsgtk_image_menu_item_set_image(GTK_WIDGET(button->popup), - GTK_WIDGET( - theme[IMAGE_SET_POPUP_MENU]-> - image[i])); - gtk_widget_show_all(GTK_WIDGET(button->popup)); + + /* remove existing item */ + dragitem->location = INACTIVE_LOCATION; + gtk_container_remove(GTK_CONTAINER(tbc->toolbar.widget), + GTK_WIDGET(dragitem->button)); + } + + + dragitem->button = make_toolbox_item(tbc->dragitem, true); + + if (dragitem->button == NULL) { + nsgtk_warning("NoMemory", 0); + return TRUE; + } + + /* update locations */ + for (location = PLACEHOLDER_BUTTON; location >= position; location--) { + itemid = itemid_from_location(&tbc->toolbar, location); + if (itemid != PLACEHOLDER_BUTTON) { + tbc->toolbar.items[itemid].location++; } - if ((button->location != -1) && (button->button != NULL) && - (theme[IMAGE_SET_BUTTONS] != NULL)) { - gtk_tool_button_set_icon_widget( - GTK_TOOL_BUTTON(button->button), - GTK_WIDGET( - theme[IMAGE_SET_BUTTONS]-> - image[i])); - gtk_widget_show_all(GTK_WIDGET(button->button)); + } + dragitem->location = position; + + gtk_toolbar_insert(tbc->toolbar.widget, + dragitem->button, + dragitem->location); + + toolbar_item_connect_signals(&tbc->toolbar, tbc->dragitem); + gtk_widget_show_all(GTK_WIDGET(dragitem->button)); + tbc->dragitem = -1; + return TRUE; +} + + +/** + * customisation toolbar handler for drag data received signal + * + * connected to toolbutton drop; perhaps one day it'll work properly + * so it may replace the global current_button + */ +static gboolean +customisation_toolbar_drag_data_received_cb(GtkWidget *widget, + GdkDragContext *gdc, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time, + gpointer data) +{ + return FALSE; +} + + +/** + * customisation toolbar handler for drag motion signal + * + * called when hovering an item above the toolbar + */ +static gboolean +customisation_toolbar_drag_motion_cb(GtkWidget *widget, + GdkDragContext *gdc, + gint x, + gint y, + guint time, + gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + GtkToolItem *item; + gint position; /* position in toolbar */ + + item = gtk_tool_button_new(NULL, NULL); + position = gtk_toolbar_get_drop_index(tb->widget, x, y); + + gtk_toolbar_set_drop_highlight_item(tb->widget, item, position); + + return FALSE; /* drag not in drop zone */ +} + + +/** + * customisation toolbar handler for drag leave signal + * + * called when hovering stops + */ +static void +customisation_toolbar_drag_leave_cb(GtkWidget *widget, + GdkDragContext *gdc, + guint time, + gpointer data) +{ + gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(widget), NULL, 0); +} + + +/** + * create a new browser window + * + * creates a browser window with default url depending on user choices. + * + * \param bw The browser window to pass for existing window/ + * \param intab true if the new window should be in a tab else false + * for new window. + * \return NSERROR_OK on success else error code. + */ +static nserror +nsgtk_browser_window_create(struct browser_window *bw, bool intab) +{ + nserror res = NSERROR_OK; + nsurl *url = NULL; + int flags = BW_CREATE_HISTORY | BW_CREATE_FOREGROUND | BW_CREATE_FOCUS_LOCATION; + + if (intab) { + flags |= BW_CREATE_TAB; + } + + if (!nsoption_bool(new_blank)) { + const char *addr; + if (nsoption_charp(homepage_url) != NULL) { + addr = nsoption_charp(homepage_url); + } else { + addr = NETSURF_HOMEPAGE; } + res = nsurl_create(addr, &url); + } + + if (res == NSERROR_OK) { + res = browser_window_create(flags, url, NULL, bw, NULL); + } + + if (url != NULL) { + nsurl_unref(url); + } + + return res; +} + + +/** + * Apply the user toolbar button settings from configuration + * + * GTK specific user option string is a set of fields arranged as + * [itemreference];[itemlocation]|[itemreference];[itemlocation]| etc + * + * \param tb The toolbar to apply customisation to + * \param NSERROR_OK on success else error code. + */ +static nserror +apply_user_button_customisation(struct nsgtk_toolbar *tb) +{ + const char *tbitems; /* item order user config */ + const char *start; + const char *end; + int iidx; /* item index */ + int location = 0; /* location index */ + + /* set all button locations to inactive */ + for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + tb->items[iidx].location = INACTIVE_LOCATION; } - /* set search bar images */ - search = nsgtk_scaffolding_search(g); - if ((search != NULL) && (theme[IMAGE_SET_MAIN_MENU] != NULL)) { - /* gtk_tool_button_set_icon_widget accepts NULL image */ - if (search->buttons[SEARCH_BACK_BUTTON] != NULL) { - gtk_tool_button_set_icon_widget( - search->buttons[SEARCH_BACK_BUTTON], - GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]-> - searchimage[SEARCH_BACK_BUTTON])); - gtk_widget_show_all(GTK_WIDGET( - search->buttons[SEARCH_BACK_BUTTON])); + tbitems = nsoption_charp(toolbar_items); + if (tbitems == NULL) { + tbitems = ""; + } + + end = tbitems; + while (*end != 0) { + start = end; + while ((*end != 0) && (*end !='/')) { + end++; } - if (search->buttons[SEARCH_FORWARD_BUTTON] != NULL) { - gtk_tool_button_set_icon_widget( - search->buttons[SEARCH_FORWARD_BUTTON], - GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]-> - searchimage[SEARCH_FORWARD_BUTTON])); - gtk_widget_show_all(GTK_WIDGET( - search->buttons[ - SEARCH_FORWARD_BUTTON])); + + for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + if (((ssize_t)strlen(tb->items[iidx].name) == (end - start)) && + (strncmp(tb->items[iidx].name, start, end - start) == 0)) { + tb->items[iidx].location = location++; + break; + } } - if (search->buttons[SEARCH_CLOSE_BUTTON] != NULL) { - gtk_tool_button_set_icon_widget( - search->buttons[SEARCH_CLOSE_BUTTON], - GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]-> - searchimage[SEARCH_CLOSE_BUTTON])); - gtk_widget_show_all(GTK_WIDGET( - search->buttons[SEARCH_CLOSE_BUTTON])); + + if (*end == '/') { + end++; } } - for (i = 0; i < IMAGE_SET_COUNT; i++) { - if (theme[i] != NULL) { - free(theme[i]); - } + if (location == 0) { + /* completely failed to create any buttons so use defaults */ + tb->items[BACK_BUTTON].location = location++; + tb->items[HISTORY_BUTTON].location = location++; + tb->items[FORWARD_BUTTON].location = location++; + tb->items[RELOADSTOP_BUTTON].location = location++; + tb->items[URL_BAR_ITEM].location = location++; + tb->items[WEBSEARCH_ITEM].location = location++; + tb->items[OPENMENU_BUTTON].location = location++; + tb->items[THROBBER_ITEM].location = location++; } + + + return NSERROR_OK; } /** - * callback function to iterate toolbar's widgets + * callback function to remove a widget from a container */ -static void nsgtk_toolbar_clear_toolbar(GtkWidget *widget, gpointer data) +static void container_remove_widget(GtkWidget *widget, gpointer data) { - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; - gtk_container_remove(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)), - widget); + GtkContainer *container = GTK_CONTAINER(data); + gtk_container_remove(container, widget); } + /** - * connect temporary handler for toolbar edit events + * populates a toolbar with widgets in correct order * - * \param g The scaffolding - * \param bi The button index + * \param tb toolbar + * \return NSERROR_OK on success else error code. */ -static void nsgtk_toolbar_temp_connect(struct nsgtk_scaffolding *g, - nsgtk_toolbar_button bi) +static nserror populate_gtk_toolbar_widget(struct nsgtk_toolbar *tb) { - struct nsgtk_button_connect *bc; + int location; /* location index */ + int itemid; + + /* clear the toolbar container of all widgets */ + gtk_container_foreach(GTK_CONTAINER(tb->widget), + container_remove_widget, + tb->widget); + + /* add widgets to toolbar */ + for (location = 0; location < PLACEHOLDER_BUTTON; location++) { + itemid = itemid_from_location(tb, location); + if (itemid == PLACEHOLDER_BUTTON) { + break; + } + tb->items[itemid].button = + make_toolbar_item(itemid, + tb->items[itemid].sensitivity); + + gtk_toolbar_insert(tb->widget, + tb->items[itemid].button, + location); + } - if (bi != URL_BAR_ITEM) { - bc = nsgtk_scaffolding_button(g, bi); - if ((bc->button != NULL) && (bc->dataminus != NULL)) { - g_signal_connect(bc->button, - "drag-data-get", - G_CALLBACK(bc->dataminus), - g); + gtk_widget_show_all(GTK_WIDGET(tb->widget)); + + return NSERROR_OK; +} + + +/** + * populates the customization toolbar with widgets in correct order + * + * \param tb toolbar + * \return NSERROR_OK on success else error code. + */ +static nserror customisation_toolbar_populate(struct nsgtk_toolbar *tb) +{ + int location; /* location index */ + int itemid; + + /* clear the toolbar container of all widgets */ + gtk_container_foreach(GTK_CONTAINER(tb->widget), + container_remove_widget, + tb->widget); + + /* add widgets to toolbar */ + for (location = 0; location < PLACEHOLDER_BUTTON; location++) { + itemid = itemid_from_location(tb, location); + if (itemid == PLACEHOLDER_BUTTON) { + break; } + tb->items[itemid].button = make_toolbox_item(itemid, true); + + gtk_toolbar_insert(tb->widget, + tb->items[itemid].button, + location); } + + gtk_widget_show_all(GTK_WIDGET(tb->widget)); + + return NSERROR_OK; } + /** - * get scaffolding button index of button at location + * find the toolbar item with a given gtk widget. * - * \return toolbar item id from location when there is an item at that logical - * location; else -1 + * \param tb the toolbar instance + * \param toolitem the tool item widget to search for + * \return the item id matching the widget */ static nsgtk_toolbar_button -nsgtk_toolbar_get_id_at_location(struct nsgtk_scaffolding *g, int i) +itemid_from_gtktoolitem(struct nsgtk_toolbar *tb, GtkToolItem *toolitem) { - int q; - for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++) { - if (nsgtk_scaffolding_button(g, q)->location == i) { - return q; + int iidx; + for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + if ((tb->items[iidx].location != INACTIVE_LOCATION) && + (tb->items[iidx].button == toolitem)) { + break; } } - return -1; + return iidx; } + /** - * widget factory for creation of toolbar item widgets - * \param g the reference scaffolding - * \param i the id of the widget - * \param theme the theme to make the widgets from + * set a toolbar items sensitivity + * + * note this does not set menu items sensitivity */ -static GtkWidget * -nsgtk_toolbar_make_widget(struct nsgtk_scaffolding *g, - nsgtk_toolbar_button i, - struct nsgtk_theme *theme) -{ - GtkWidget *w = NULL; - - switch(i) { - -/* gtk_tool_button_new() accepts NULL args */ -#define MAKE_STOCKBUTTON(p, q) \ - case p##_BUTTON: { \ - GtkStockItem item; \ - char *label = NULL; \ - if (nsgtk_stock_lookup(q, &item) && \ - (item.label != NULL) && \ - ((label = remove_underscores(item.label, false)) != NULL)) { \ - w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \ - theme->image[p##_BUTTON]), label)); \ - free(label); \ - } else { \ - w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \ - theme->image[p##_BUTTON]), q)); \ - } \ - break; \ +static nserror +set_item_sensitivity(struct nsgtk_toolbar_item *item, bool sensitivity) +{ + if (item->sensitivity != sensitivity) { + /* item requires sensitivity changing */ + item->sensitivity = sensitivity; + + if ((item->location != -1) && (item->button != NULL)) { + gtk_widget_set_sensitive(GTK_WIDGET(item->button), + item->sensitivity); + } } - MAKE_STOCKBUTTON(HOME, NSGTK_STOCK_HOME) - MAKE_STOCKBUTTON(BACK, NSGTK_STOCK_GO_BACK) - MAKE_STOCKBUTTON(FORWARD, NSGTK_STOCK_GO_FORWARD) - MAKE_STOCKBUTTON(STOP, NSGTK_STOCK_STOP) - MAKE_STOCKBUTTON(RELOAD, NSGTK_STOCK_REFRESH) -#undef MAKE_STOCKBUTTON + return NSERROR_OK; +} - case HISTORY_BUTTON: - w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( - theme->image[HISTORY_BUTTON]), "H")); - break; - case URL_BAR_ITEM: { - GtkWidget *entry = nsgtk_entry_new(); - w = GTK_WIDGET(gtk_tool_item_new()); +/** + * set an item to its alternative action + * + * this is currently only used for the stop/reload button where we + * also reuse the item sensitivity for the state indicator. + * + * \param tb the toolbar instance + */ +static nserror set_item_action(struct nsgtk_toolbar *tb, int itemid, bool alt) +{ + const char *iconname; + char *label = NULL; + + if (itemid != RELOADSTOP_BUTTON) { + return NSERROR_INVALID; + } + if (tb->items[itemid].location == -1) { + return NSERROR_OK; + } + tb->items[itemid].sensitivity = alt; - if ((entry == NULL) || (w == NULL)) { - nsgtk_warning(messages_get("NoMemory"), 0); - return NULL; - } + if (tb->items[itemid].button == NULL) { + return NSERROR_INVALID; + } - nsgtk_entry_set_icon_from_pixbuf(entry, - GTK_ENTRY_ICON_PRIMARY, - favicon_pixbuf); + if (tb->items[itemid].sensitivity) { + iconname = NSGTK_STOCK_REFRESH; + label = remove_underscores(messages_get("Reload"), false); + + } else { + iconname = NSGTK_STOCK_STOP; + label = remove_underscores(messages_get("gtkStop"), false); - gtk_container_add(GTK_CONTAINER(w), entry); - gtk_tool_item_set_expand(GTK_TOOL_ITEM(w), TRUE); - break; } + gtk_tool_button_set_label(GTK_TOOL_BUTTON(tb->items[itemid].button), + label); - case THROBBER_ITEM: { - if ((nsgtk_throbber == NULL) || - (nsgtk_throbber->framedata == NULL) || - (nsgtk_throbber->framedata[0] == NULL)) { - return NULL; + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tb->items[itemid].button), + iconname); + + gtk_widget_set_sensitive(GTK_WIDGET(tb->items[itemid].button), TRUE); + + if (label != NULL) { + free(label); + } + + return NSERROR_OK; +} + + +/** + * cause the toolbar browsing context to navigate to a new url. + * + * \param tb the toolbar context. + * \param urltxt The url string. + * \return NSERROR_OK on success else appropriate error code. + */ +static nserror +toolbar_navigate_to_url(struct nsgtk_toolbar *tb, const char *urltxt) +{ + struct browser_window *bw; + nsurl *url; + nserror res; + + res = nsurl_create(urltxt, &url); + if (res != NSERROR_OK) { + return res; + } + + bw = tb->get_bw(tb->get_ctx); + + res = browser_window_navigate(bw, + url, + NULL, + BW_NAVIGATE_HISTORY, + NULL, + NULL, + NULL); + nsurl_unref(url); + + return res; +} + + +/** + * run a gtk file chooser as a save dialog to obtain a path + */ +static nserror +nsgtk_saveas_dialog(struct browser_window *bw, + const char *title, + GtkWindow *parent, + bool folder, + gchar **path_out) +{ + nserror res; + GtkWidget *fc; /* file chooser widget */ + GtkFileChooserAction action; + char *path; /* proposed path */ + + if (!browser_window_has_content(bw)) { + /* cannot save a page with no content */ + return NSERROR_INVALID; + } + + if (folder) { + action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; + } else { + action = GTK_FILE_CHOOSER_ACTION_SAVE; + } + + fc = gtk_file_chooser_dialog_new(title, + parent, + action, + NSGTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, + GTK_RESPONSE_ACCEPT, + NULL); + + /* set a default file name */ + res = nsurl_nice(browser_window_access_url(bw), &path, false); + if (res != NSERROR_OK) { + path = strdup(messages_get("SaveText")); + if (path == NULL) { + gtk_widget_destroy(fc); + return NSERROR_NOMEM; } + } - if (edit_mode) { - w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( - gtk_image_new_from_pixbuf( - nsgtk_throbber->framedata[0])), - "[throbber]")); - } else { - GtkWidget *image; + if ((!folder) || (access(path, F_OK) != 0)) { + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), path); + } + free(path); - w = GTK_WIDGET(gtk_tool_item_new()); + /* confirm overwriting */ + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc), TRUE); - image = gtk_image_new_from_pixbuf(nsgtk_throbber->framedata[0]); - if (image != NULL) { - nsgtk_widget_set_alignment(image, - GTK_ALIGN_CENTER, - GTK_ALIGN_CENTER); - nsgtk_widget_set_margins(image, 3, 0); + /* run the dialog to let user select path */ + if (gtk_dialog_run(GTK_DIALOG(fc)) != GTK_RESPONSE_ACCEPT) { + gtk_widget_destroy(fc); + return NSERROR_NOT_FOUND; + } - gtk_container_add(GTK_CONTAINER(w), image); - } + *path_out = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc)); + + gtk_widget_destroy(fc); + + return NSERROR_OK; +} + + +/** + * connect all signals to widgets in a customisation + */ +static nserror +toolbar_customisation_connect_signals(struct nsgtk_toolbar *tb) +{ + int iidx; + + for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + /* skip inactive items in toolbar */ + if (tb->items[iidx].location != INACTIVE_LOCATION) { + toolbar_item_connect_signals(tb, iidx); } - break; } - case WEBSEARCH_ITEM: { - if (edit_mode) - return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( - nsgtk_image_new_from_stock(NSGTK_STOCK_FIND, - GTK_ICON_SIZE_LARGE_TOOLBAR)), - "[websearch]")); + /* add move button listeners */ + g_signal_connect(tb->widget, + "drag-drop", + G_CALLBACK(customisation_toolbar_drag_drop_cb), + tb); + g_signal_connect(tb->widget, + "drag-data-received", + G_CALLBACK(customisation_toolbar_drag_data_received_cb), + tb); + g_signal_connect(tb->widget, + "drag-motion", + G_CALLBACK(customisation_toolbar_drag_motion_cb), + tb); + g_signal_connect(tb->widget, + "drag-leave", + G_CALLBACK(customisation_toolbar_drag_leave_cb), + tb); + + /* set data types */ + gtk_drag_dest_set(GTK_WIDGET(tb->widget), + GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, + &target_entry, + 1, + GDK_ACTION_COPY); + + return NSERROR_OK; +} + + +static void +item_size_allocate_cb(GtkWidget *widget, + GdkRectangle *alloc, + gpointer user_data) +{ + if (alloc->width > NSGTK_BUTTON_WIDTH) { + alloc->width = NSGTK_BUTTON_WIDTH; + } + if (alloc->height > NSGTK_BUTTON_HEIGHT) { + alloc->height = NSGTK_BUTTON_HEIGHT; + } + gtk_widget_set_allocation(widget, alloc); +} + - GtkWidget *entry = nsgtk_entry_new(); +/** + * add a row to a toolbar customisation toolbox + * + * \param tbc The toolbar customisation context + * \param startitem The item index of the beginning of the row + * \param enditem The item index of the beginning of the next row + * \return NSERROR_OK on successs else error + */ +static nserror +add_toolbox_row(struct nsgtk_toolbar_customisation *tbc, + int startitem, + int enditem) +{ + GtkToolbar *rowbar; + int iidx; - w = GTK_WIDGET(gtk_tool_item_new()); + rowbar = GTK_TOOLBAR(gtk_toolbar_new()); + if (rowbar == NULL) { + return NSERROR_NOMEM; + } - if ((entry == NULL) || (w == NULL)) { - nsgtk_warning(messages_get("NoMemory"), 0); - return NULL; + gtk_toolbar_set_style(rowbar, GTK_TOOLBAR_BOTH); + gtk_toolbar_set_icon_size(rowbar, GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_box_pack_start(tbc->toolbox, GTK_WIDGET(rowbar), FALSE, FALSE, 0); + + for (iidx = startitem; iidx < enditem; iidx++) { + if (tbc->items[iidx] == NULL) { + /* skip any widgets that failed to initialise */ + continue; } + gtk_widget_set_size_request(GTK_WIDGET(tbc->items[iidx]), + NSGTK_BUTTON_WIDTH, + NSGTK_BUTTON_HEIGHT); + gtk_tool_item_set_use_drag_window(tbc->items[iidx], TRUE); + gtk_drag_source_set(GTK_WIDGET(tbc->items[iidx]), + GDK_BUTTON1_MASK, + &target_entry, + 1, + GDK_ACTION_COPY); + g_signal_connect(tbc->items[iidx], + "drag-data-get", + G_CALLBACK(tbc->toolbar.items[iidx].dataplus), + &tbc->toolbar); + g_signal_connect(tbc->items[iidx], + "size-allocate", + G_CALLBACK(item_size_allocate_cb), + NULL); + gtk_toolbar_insert(rowbar, tbc->items[iidx], -1); + } + return NSERROR_OK; +} - gtk_widget_set_size_request(entry, NSGTK_WEBSEARCH_WIDTH, -1); - nsgtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_PRIMARY, - NSGTK_STOCK_INFO); +/** + * creates widgets in customisation toolbox + * + * \param tbc The toolbar customisation context + * \param width The width to layout the toolbox to + * \return NSERROR_OK on success else error code. + */ +static nserror +toolbar_customisation_create_toolbox(struct nsgtk_toolbar_customisation *tbc, + int width) +{ + int columns; /* number of items in a single row */ + int curcol; /* current column in creation */ + int iidx; /* item index */ + int startidx; /* index of item at start of row */ + + /* ensure there are a minimum number of items per row */ + columns = width / NSGTK_BUTTON_WIDTH; + if (columns < NSGTK_MIN_STORE_COLUMNS) { + columns = NSGTK_MIN_STORE_COLUMNS; + } - gtk_container_add(GTK_CONTAINER(w), entry); - break; + curcol = 0; + for (iidx = startidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + if (curcol >= columns) { + add_toolbox_row(tbc, startidx, iidx); + curcol = 0; + startidx = iidx; + } + tbc->items[iidx] = make_toolbox_item(iidx, false); + if (tbc->items[iidx] != NULL) { + curcol++; + } + } + if (curcol > 0) { + add_toolbox_row(tbc, startidx, iidx); } -/* gtk_tool_button_new accepts NULL args */ -#define MAKE_MENUBUTTON(p, q) \ - case p##_BUTTON: { \ - char *label = NULL; \ - label = remove_underscores(messages_get(#q), false); \ - w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \ - theme->image[p##_BUTTON]), label)); \ - if (label != NULL) \ - free(label); \ - break; \ - } - - MAKE_MENUBUTTON(NEWWINDOW, gtkNewWindow) - MAKE_MENUBUTTON(NEWTAB, gtkNewTab) - MAKE_MENUBUTTON(OPENFILE, gtkOpenFile) - MAKE_MENUBUTTON(CLOSETAB, gtkCloseTab) - MAKE_MENUBUTTON(CLOSEWINDOW, gtkCloseWindow) - MAKE_MENUBUTTON(SAVEPAGE, gtkSavePage) - MAKE_MENUBUTTON(PRINTPREVIEW, gtkPrintPreview) - MAKE_MENUBUTTON(PRINT, gtkPrint) - MAKE_MENUBUTTON(QUIT, gtkQuitMenu) - MAKE_MENUBUTTON(CUT, gtkCut) - MAKE_MENUBUTTON(COPY, gtkCopy) - MAKE_MENUBUTTON(PASTE, gtkPaste) - MAKE_MENUBUTTON(DELETE, gtkDelete) - MAKE_MENUBUTTON(SELECTALL, gtkSelectAll) - MAKE_MENUBUTTON(PREFERENCES, gtkPreferences) - MAKE_MENUBUTTON(ZOOMPLUS, gtkZoomPlus) - MAKE_MENUBUTTON(ZOOMMINUS, gtkZoomMinus) - MAKE_MENUBUTTON(ZOOMNORMAL, gtkZoomNormal) - MAKE_MENUBUTTON(FULLSCREEN, gtkFullScreen) - MAKE_MENUBUTTON(VIEWSOURCE, gtkViewSource) - MAKE_MENUBUTTON(CONTENTS, gtkContents) - MAKE_MENUBUTTON(ABOUT, gtkAbout) - MAKE_MENUBUTTON(PDF, gtkPDF) - MAKE_MENUBUTTON(PLAINTEXT, gtkPlainText) - MAKE_MENUBUTTON(DRAWFILE, gtkDrawFile) - MAKE_MENUBUTTON(POSTSCRIPT, gtkPostScript) - MAKE_MENUBUTTON(FIND, gtkFind) - MAKE_MENUBUTTON(DOWNLOADS, gtkDownloads) - MAKE_MENUBUTTON(SAVEWINDOWSIZE, gtkSaveWindowSize) - MAKE_MENUBUTTON(TOGGLEDEBUGGING, gtkToggleDebugging) - MAKE_MENUBUTTON(SAVEBOXTREE, gtkDebugBoxTree) - MAKE_MENUBUTTON(SAVEDOMTREE, gtkDebugDomTree) - MAKE_MENUBUTTON(LOCALHISTORY, gtkLocalHistory) - MAKE_MENUBUTTON(GLOBALHISTORY, gtkGlobalHistory) - MAKE_MENUBUTTON(ADDBOOKMARKS, gtkAddBookMarks) - MAKE_MENUBUTTON(SHOWBOOKMARKS, gtkShowBookMarks) - MAKE_MENUBUTTON(SHOWCOOKIES, gtkShowCookies) - MAKE_MENUBUTTON(OPENLOCATION, gtkOpenLocation) - MAKE_MENUBUTTON(NEXTTAB, gtkNextTab) - MAKE_MENUBUTTON(PREVTAB, gtkPrevTab) - MAKE_MENUBUTTON(GUIDE, gtkGuide) - MAKE_MENUBUTTON(INFO, gtkUserInformation) -#undef MAKE_MENUBUTTON + return NSERROR_OK; +} - default: - break; +/** + * update toolbar in customisation to user settings + */ +static nserror +customisation_toolbar_update(struct nsgtk_toolbar_customisation *tbc) +{ + nserror res; + + res = apply_user_button_customisation(&tbc->toolbar); + if (res != NSERROR_OK) { + return res; + } + + /* populate toolbar widget */ + res = customisation_toolbar_populate(&tbc->toolbar); + if (res != NSERROR_OK) { + return res; + } + + /* ensure icon sizes and text labels on toolbar are set */ + res = nsgtk_toolbar_restyle(&tbc->toolbar); + if (res != NSERROR_OK) { + return res; } - return w; + + /* attach handlers to toolbar widgets */ + res = toolbar_customisation_connect_signals(&tbc->toolbar); + if (res != NSERROR_OK) { + return res; + } + + return NSERROR_OK; } + /** - * called when a widget is dropped onto the toolbar + * customisation apply handler for clicked signal + * + * when 'save settings' button is clicked + */ +static gboolean +customisation_apply_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar_customisation *tbc; + tbc = (struct nsgtk_toolbar_customisation *)data; + + /* save state to file, update toolbars for all windows */ + nsgtk_toolbar_customisation_save(&tbc->toolbar); + nsgtk_window_toolbar_update(); + gtk_widget_destroy(tbc->container); + + return TRUE; +} + + +/** + * customisation reset handler for clicked signal + * + * when 'reload defaults' button is clicked */ static gboolean -nsgtk_toolbar_data(GtkWidget *widget, - GdkDragContext *gdc, - gint x, - gint y, - guint time, - gpointer data) -{ - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; - int ind = gtk_toolbar_get_drop_index(nsgtk_scaffolding_toolbar(g), - x, y); - int q, i; - if (window->currentbutton == -1) +customisation_reset_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar_customisation *tbc; + tbc = (struct nsgtk_toolbar_customisation *)data; + + customisation_toolbar_update(tbc); + + return TRUE; +} + + +/** + * customisation container destroy handler + */ +static void customisation_container_destroy_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar_customisation *tbc; + tbc = (struct nsgtk_toolbar_customisation *)data; + + free(tbc); +} + +/* + * Toolbar button clicked handlers + */ + +/** + * create a toolbar customisation tab + * + * this is completely different approach to previous implementation. it + * is not modal and the toolbar configuration is performed completely + * within the tab. once the user is happy they can apply the change or + * cancel as they see fit while continuing to use the browser as usual. + */ +static gboolean cutomize_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar_customisation *tbc; + nserror res; + GtkBuilder *builder; + GtkNotebook *notebook; /* notebook containing widget */ + GtkAllocation notebook_alloc; /* notebook size allocation */ + int iidx; /* item index */ + + /* obtain the notebook being added to */ + notebook = GTK_NOTEBOOK(gtk_widget_get_ancestor(widget, + GTK_TYPE_NOTEBOOK)); + if (notebook == NULL) { return TRUE; - struct nsgtk_theme *theme = - nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, false); - if (theme == NULL) { - nsgtk_warning(messages_get("NoMemory"), 0); + } + + /* create builder */ + res = nsgtk_builder_new_from_resname("toolbar", &builder); + if (res != NSERROR_OK) { + NSLOG(netsurf, INFO, "Toolbar UI builder init failed"); return TRUE; } - if (nsgtk_scaffolding_button(g, window->currentbutton)->location - != -1) { - /* widget was already in the toolbar; so replace */ - if (nsgtk_scaffolding_button(g, window->currentbutton)-> - location < ind) - ind--; - gtk_container_remove(GTK_CONTAINER( - nsgtk_scaffolding_toolbar(g)), GTK_WIDGET( - nsgtk_scaffolding_button(g, - window->currentbutton)->button)); - /* 'move' all widgets further right than the original location, - * one place to the left in logical schema */ - for (i = nsgtk_scaffolding_button(g, window->currentbutton)-> - location + 1; i < PLACEHOLDER_BUTTON; i++) { - q = nsgtk_toolbar_get_id_at_location(g, i); - if (q == -1) - continue; - nsgtk_scaffolding_button(g, q)->location--; - } - nsgtk_scaffolding_button(g, window->currentbutton)-> - location = -1; - } - nsgtk_scaffolding_button(g, window->currentbutton)->button = - GTK_TOOL_ITEM(nsgtk_toolbar_make_widget(g, - window->currentbutton, theme)); - free(theme); - if (nsgtk_scaffolding_button(g, window->currentbutton)->button - == NULL) { - nsgtk_warning("NoMemory", 0); + gtk_builder_connect_signals(builder, NULL); + + /* create nsgtk_toolbar_customisation which has nsgtk_toolbar + * at the front so we can reuse functions that take + * nsgtk_toolbar + */ + tbc = calloc(1, sizeof(struct nsgtk_toolbar_customisation)); + if (tbc == NULL) { + g_object_unref(builder); return TRUE; } - /* update logical schema */ - nsgtk_scaffolding_reset_offset(g); - /* 'move' all widgets further right than the new location, one place to - * the right in logical schema */ - for (i = PLACEHOLDER_BUTTON - 1; i >= ind; i--) { - q = nsgtk_toolbar_get_id_at_location(g, i); - if (q == -1) - continue; - nsgtk_scaffolding_button(g, q)->location++; + + /* get container box widget which forms a page of the tabs */ + tbc->container = GTK_WIDGET(gtk_builder_get_object(builder, "customisation")); + if (tbc->container == NULL) { + goto cutomize_button_clicked_cb_error; } - nsgtk_scaffolding_button(g, window->currentbutton)->location = ind; - /* complete action */ - GtkToolItem *current_button; + /* vertical box for the toolbox to drag items into and out of */ + tbc->toolbox = GTK_BOX(gtk_builder_get_object(builder, "toolbox")); + if (tbc->toolbox == NULL) { + goto cutomize_button_clicked_cb_error; + } - current_button = GTK_TOOL_ITEM(nsgtk_scaffolding_button(g, window->currentbutton)->button); + /* customisation toolbar container */ + tbc->toolbar.widget = GTK_TOOLBAR(gtk_builder_get_object(builder, "toolbar")); + if (tbc->toolbar.widget == NULL) { + goto cutomize_button_clicked_cb_error; + } - gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g), current_button, ind); + /* build customisation toolbar */ + gtk_toolbar_set_show_arrow(tbc->toolbar.widget, TRUE); - gtk_tool_item_set_use_drag_window(current_button, TRUE); - gtk_drag_source_set(GTK_WIDGET(current_button), - GDK_BUTTON1_MASK, &entry, 1, - GDK_ACTION_COPY); - nsgtk_toolbar_temp_connect(g, window->currentbutton); - gtk_widget_show_all(GTK_WIDGET(current_button)); + for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) { + res = toolbar_item_create(iidx, &tbc->toolbar.items[iidx]); + if (res != NSERROR_OK) { + goto cutomize_button_clicked_cb_error; + } + } + + res = customisation_toolbar_update(tbc); + if (res != NSERROR_OK) { + goto cutomize_button_clicked_cb_error; + } + + /* use toolbox for widgets to drag to/from */ + gtk_widget_get_allocation(GTK_WIDGET(notebook), ¬ebook_alloc); + + res = toolbar_customisation_create_toolbox(tbc, notebook_alloc.width); + if (res != NSERROR_OK) { + goto cutomize_button_clicked_cb_error; + } + + /* configure the container */ + gtk_drag_dest_set(GTK_WIDGET(tbc->container), + GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, + &target_entry, + 1, + GDK_ACTION_COPY); + + /* discard button calls destroy */ + g_signal_connect_swapped(GTK_WIDGET(gtk_builder_get_object(builder, + "discard")), + "clicked", + G_CALLBACK(gtk_widget_destroy), + tbc->container); + + /* save and update on apply button */ + g_signal_connect(GTK_WIDGET(gtk_builder_get_object(builder, "apply")), + "clicked", + G_CALLBACK(customisation_apply_clicked_cb), + tbc); + + g_signal_connect(GTK_WIDGET(gtk_builder_get_object(builder, "reset")), + "clicked", + G_CALLBACK(customisation_reset_clicked_cb), + tbc); + + /* close and cleanup on delete signal */ + g_signal_connect(tbc->container, + "destroy", + G_CALLBACK(customisation_container_destroy_cb), + tbc); + + + g_signal_connect(tbc->container, + "drag-drop", + G_CALLBACK(customisation_container_drag_drop_cb), + tbc); + + g_signal_connect(tbc->container, + "drag-motion", + G_CALLBACK(customisation_container_drag_motion_cb), + tbc); - window->currentbutton = -1; + nsgtk_tab_add_page(notebook, + tbc->container, + false, + messages_get("gtkCustomizeToolbarTitle"), + favicon_pixbuf); + + /* safe to drop the reference to the builder as the container is + * referenced by the notebook now. + */ + g_object_unref(builder); + + return TRUE; + + cutomize_button_clicked_cb_error: + free(tbc); + g_object_unref(builder); return TRUE; + +} + + +/** + * callback for all toolbar items widget size allocation + * + * handler connected to all toolbar items for the size-allocate signal + * + * \param widget The widget the signal is being delivered to. + * \param alloc The size allocation being set. + * \param data The toolbar context passed when the signal was connected + */ +static void +toolbar_item_size_allocate_cb(GtkWidget *widget, + GtkAllocation *alloc, + gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + nsgtk_toolbar_button itemid; + + itemid = itemid_from_gtktoolitem(tb, GTK_TOOL_ITEM(widget)); + + if ((tb->toolbarmem == alloc->x) || + (tb->items[itemid].location < tb->items[HISTORY_BUTTON].location)) { + /* + * no reallocation after first adjustment, + * no reallocation for buttons left of history button + */ + return; + } + + if (itemid == HISTORY_BUTTON) { + if (alloc->width == 20) { + return; + } + + tb->toolbarbase = alloc->y + alloc->height; + tb->historybase = alloc->x + 20; + if (tb->offset == 0) { + tb->offset = alloc->width - 20; + } + alloc->width = 20; + } else if (tb->items[itemid].location <= tb->items[URL_BAR_ITEM].location) { + alloc->x -= tb->offset; + if (itemid == URL_BAR_ITEM) { + alloc->width += tb->offset; + } + } + tb->toolbarmem = alloc->x; + + gtk_widget_size_allocate(widget, alloc); } + /** - * connected to toolbutton drop; perhaps one day it'll work properly so it may - * replace the global current_button + * handler for back tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ static gboolean -nsgtk_toolbar_move_complete(GtkWidget *widget, - GdkDragContext *gdc, - gint x, - gint y, - GtkSelectionData *selection, - guint info, - guint time, - gpointer data) +back_button_clicked_cb(GtkWidget *widget, gpointer data) { - return FALSE; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + if ((bw != NULL) && browser_window_history_back_available(bw)) { + /* clear potential search effects */ + browser_window_search_clear(bw); + + browser_window_history_back(bw, false); + + set_item_sensitivity(&tb->items[BACK_BUTTON], + browser_window_history_back_available(bw)); + set_item_sensitivity(&tb->items[FORWARD_BUTTON], + browser_window_history_forward_available(bw)); + + nsgtk_local_history_hide(); + } + return TRUE; } + /** - * called when hovering an item above the toolbar + * handler for forward tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ static gboolean -nsgtk_toolbar_action(GtkWidget *widget, GdkDragContext *gdc, gint x, - gint y, guint time, gpointer data) -{ - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; - GtkToolItem *item = gtk_tool_button_new(NULL, NULL); - if (item != NULL) - gtk_toolbar_set_drop_highlight_item( - nsgtk_scaffolding_toolbar(g), - GTK_TOOL_ITEM(item), - gtk_toolbar_get_drop_index( - nsgtk_scaffolding_toolbar(g), x, y)); - return FALSE; +forward_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + if ((bw != NULL) && browser_window_history_forward_available(bw)) { + /* clear potential search effects */ + browser_window_search_clear(bw); + + browser_window_history_forward(bw, false); + + set_item_sensitivity(&tb->items[BACK_BUTTON], + browser_window_history_back_available(bw)); + set_item_sensitivity(&tb->items[FORWARD_BUTTON], + browser_window_history_forward_available(bw)); + nsgtk_local_history_hide(); + } + return TRUE; } + /** - * called when hovering stops + * handler for stop tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static void -nsgtk_toolbar_clear(GtkWidget *widget, GdkDragContext *gdc, guint time, - gpointer data) +static gboolean +stop_button_clicked_cb(GtkWidget *widget, gpointer data) { - gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(widget), NULL, 0); + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + + browser_window_stop(tb->get_bw(tb->get_ctx)); + + return TRUE; +} + + +/** + * handler for reload tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +reload_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + /* clear potential search effects */ + browser_window_search_clear(bw); + + browser_window_reload(bw, true); + + return TRUE; +} + + +/** + * handler for reload/stop tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +reloadstop_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + /* clear potential search effects */ + browser_window_search_clear(bw); + + if (tb->items[RELOADSTOP_BUTTON].sensitivity) { + browser_window_reload(bw, true); + } else { + browser_window_stop(tb->get_bw(tb->get_ctx)); + } + + return TRUE; +} + + +/** + * handler for home tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +home_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + nserror res; + const char *addr; + + if (nsoption_charp(homepage_url) != NULL) { + addr = nsoption_charp(homepage_url); + } else { + addr = NETSURF_HOMEPAGE; + } + + res = toolbar_navigate_to_url(tb, addr); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + + return TRUE; +} + + +/** + * callback for url entry widget activation + * + * handler connected to url entry widget for the activate signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE to allow activation. + */ +static gboolean url_entry_activate_cb(GtkWidget *widget, gpointer data) +{ + nserror res; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + nsurl *url; + + res = search_web_omni(gtk_entry_get_text(GTK_ENTRY(widget)), + SEARCH_WEB_OMNI_NONE, + &url); + if (res == NSERROR_OK) { + bw = tb->get_bw(tb->get_ctx); + res = browser_window_navigate( + bw, url, NULL, BW_NAVIGATE_HISTORY, NULL, NULL, NULL); + nsurl_unref(url); + } + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + + return TRUE; +} + + +/** + * callback for url entry widget changing + * + * handler connected to url entry widget for the change signal + * + * \param widget The widget the signal is being delivered to. + * \param event The key change event that changed the entry. + * \param data The toolbar context passed when the signal was connected + * \return TRUE to allow activation. + */ +static gboolean +url_entry_changed_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + return nsgtk_completion_update(GTK_ENTRY(widget)); } + /** - * add item to toolbar. + * callback for url entry widget icon button release * - * the function should be called, when multiple items are being added, - * in ascending order. + * handler connected to url entry widget for the icon release signal * - * \param g the scaffolding whose toolbar an item is added to. - * \param i the location in the toolbar. - * \param theme The theme in use. + * \param widget The widget the signal is being delivered to. + * \param event The key change event that changed the entry. + * \param data The toolbar context passed when the signal was connected + * \return TRUE to allow activation. */ static void -nsgtk_toolbar_add_item_to_toolbar(struct nsgtk_scaffolding *g, int i, - struct nsgtk_theme *theme) -{ - int q; - for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++) - if (nsgtk_scaffolding_button(g, q)->location == i) { - nsgtk_scaffolding_button(g, q)->button = GTK_TOOL_ITEM( - nsgtk_toolbar_make_widget(g, q, - theme)); - gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g), - nsgtk_scaffolding_button(g, q)->button, - i); - break; - } +url_entry_icon_release_cb(GtkEntry *entry, + GtkEntryIconPosition icon_pos, + GdkEvent *event, + gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + nsgtk_page_info(bw); +} + + +/** + * handler for web search tool bar entry item activate signal + * + * handler connected to web search entry widget for the activate signal + * + * \todo make this user selectable to switch between opening in new + * and navigating current window. Possibly improve core search_web interfaces + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean websearch_entry_activate_cb(GtkWidget *widget, gpointer data) +{ + nserror res; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + nsurl *url; + + res = search_web_omni(gtk_entry_get_text(GTK_ENTRY(widget)), + SEARCH_WEB_OMNI_SEARCHONLY, + &url); + if (res == NSERROR_OK) { + bw = tb->get_bw(tb->get_ctx); + + res = browser_window_create( + BW_CREATE_HISTORY | BW_CREATE_TAB | BW_CREATE_FOREGROUND, + url, + NULL, + bw, + NULL); + nsurl_unref(url); + } + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + + return TRUE; } /** - * cleanup code physical update of all toolbars; resensitize - * \param g the 'front' scaffolding that called customize + * handler for web search tool bar item button press signal + * + * allows a click in the websearch entry field to clear the name of the + * provider. + * + * \todo this does not work well, different behaviour wanted perhaps? + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static void nsgtk_toolbar_close(struct nsgtk_scaffolding *g) +static gboolean +websearch_entry_button_press_cb(GtkWidget *widget, + GdkEventFocus *f, + gpointer data) { - int i; + gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1); + gtk_widget_grab_focus(GTK_WIDGET(widget)); - struct nsgtk_scaffolding *list; - struct nsgtk_theme *theme; + return TRUE; +} - list = nsgtk_scaffolding_iterate(NULL); - while (list) { - theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, false); - if (theme == NULL) { - nsgtk_warning(messages_get("NoMemory"), 0); - continue; - } - /* clear toolbar */ - gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar( - list)), nsgtk_toolbar_clear_toolbar, list); - /* then add items */ - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - nsgtk_toolbar_add_item_to_toolbar(list, i, theme); + +/** + * handler for new window tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +newwindow_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + nserror res; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + + res = nsgtk_browser_window_create(tb->get_bw(tb->get_ctx), false); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + + return TRUE; +} + + +/** + * handler for new tab tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +newtab_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + nserror res; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + + res = nsgtk_browser_window_create(tb->get_bw(tb->get_ctx), true); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + return TRUE; +} + + +/** + * handler for open file tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +openfile_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + GtkWidget *dlgOpen; + gint response; + GtkWidget *toplevel; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + dlgOpen = gtk_file_chooser_dialog_new("Open File", + GTK_WINDOW(toplevel), + GTK_FILE_CHOOSER_ACTION_OPEN, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL, NULL); + + response = gtk_dialog_run(GTK_DIALOG(dlgOpen)); + if (response == GTK_RESPONSE_OK) { + char *urltxt; + gchar *filename; + nserror res; + nsurl *url; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlgOpen)); + + urltxt = malloc(strlen(filename) + FILE_SCHEME_PREFIX_LEN + 1); + if (urltxt != NULL) { + sprintf(urltxt, FILE_SCHEME_PREFIX"%s", filename); + + res = nsurl_create(urltxt, &url); + if (res == NSERROR_OK) { + bw = tb->get_bw(tb->get_ctx); + res = browser_window_navigate(bw, + url, + NULL, + BW_NAVIGATE_HISTORY, + NULL, + NULL, + NULL); + nsurl_unref(url); + } + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + free(urltxt); } - nsgtk_toolbar_connect_all(list); - gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar( - list))); - nsgtk_scaffolding_set_sensitivity(list); - nsgtk_widget_override_background_color(GTK_WIDGET(nsgtk_window_get_layout(nsgtk_scaffolding_top_level(list))), GTK_STATE_NORMAL, 0, 0xFFFF, 0xFFFF, 0xFFFF); - g_signal_handler_unblock(GTK_WIDGET( - nsgtk_window_get_layout( - nsgtk_scaffolding_top_level(list))), - nsgtk_window_get_signalhandler( - nsgtk_scaffolding_top_level(list), - NSGTK_WINDOW_SIGNAL_CLICK)); - g_signal_handler_unblock(GTK_WIDGET( - nsgtk_window_get_layout( - nsgtk_scaffolding_top_level(list))), - nsgtk_window_get_signalhandler( - nsgtk_scaffolding_top_level(list), - NSGTK_WINDOW_SIGNAL_REDRAW)); - browser_window_refresh_url_bar( - nsgtk_get_browser_window( - nsgtk_scaffolding_top_level(list))); - - if (list != g) - gtk_widget_set_sensitive(GTK_WIDGET( - nsgtk_scaffolding_window(list)), TRUE); - free(theme); - list = nsgtk_scaffolding_iterate(list); - } - gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)), - TRUE); - gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)), - TRUE); - /* update favicon etc */ - nsgtk_scaffolding_set_top_level(nsgtk_scaffolding_top_level(g)); - - search_web_select_provider(-1); -} - -/** - * when cancel button is clicked - */ -static gboolean nsgtk_toolbar_cancel_clicked(GtkWidget *widget, gpointer data) -{ - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; - - edit_mode = false; - /* reset g->buttons->location */ - for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - nsgtk_scaffolding_button(g, i)->location = - window->buttonlocations[i]; - } - nsgtk_toolbar_set_physical(g); - nsgtk_toolbar_connect_all(g); - nsgtk_toolbar_close(g); - nsgtk_scaffolding_set_sensitivity(g); - gtk_widget_destroy(window->window); + + + g_free(filename); + } + + gtk_widget_destroy(dlgOpen); + + return TRUE; +} + + +/** + * handler for close window tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +closewindow_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + GtkWidget *toplevel; + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + gtk_widget_destroy(toplevel); return TRUE; } + /** - * physically add widgets to store window + * handler for full save export tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static bool nsgtk_toolbar_add_store_widget(GtkWidget *widget) +static gboolean +savepage_button_clicked_cb(GtkWidget *widget, gpointer data) { - if (window->numberh >= NSGTK_STORE_WIDTH) { - window->currentbar = gtk_toolbar_new(); - if (window->currentbar == NULL) { - nsgtk_warning("NoMemory", 0); - return false; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + DIR *d; + gchar *path; + nserror res; + GtkWidget *toplevel; + + bw = tb->get_bw(tb->get_ctx); + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + res = nsgtk_saveas_dialog(bw, + messages_get("gtkcompleteSave"), + GTK_WINDOW(toplevel), + true, + &path); + if (res != NSERROR_OK) { + return FALSE; + } + + d = opendir(path); + if (d == NULL) { + NSLOG(netsurf, INFO, + "Unable to open directory %s for complete save: %s", + path, + strerror(errno)); + if (errno == ENOTDIR) { + nsgtk_warning("NoDirError", path); + } else { + nsgtk_warning("gtkFileError", path); } - gtk_toolbar_set_style(GTK_TOOLBAR(window->currentbar), - GTK_TOOLBAR_BOTH); - gtk_toolbar_set_icon_size(GTK_TOOLBAR(window->currentbar), - GTK_ICON_SIZE_LARGE_TOOLBAR); - gtk_box_pack_start(GTK_BOX(window->widgetvbox), - window->currentbar, FALSE, FALSE, 0); - window->numberh = 0; + g_free(path); + return TRUE; } - gtk_widget_set_size_request(widget, NSGTK_BUTTON_WIDTH, - NSGTK_BUTTON_HEIGHT); - gtk_toolbar_insert(GTK_TOOLBAR(window->currentbar), GTK_TOOL_ITEM( - widget), window->numberh++); - gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(widget), TRUE); - gtk_drag_source_set(widget, GDK_BUTTON1_MASK, &entry, 1, - GDK_ACTION_COPY); - gtk_widget_show_all(window->window); - return true; + closedir(d); + + save_complete(browser_window_get_content(bw), path, NULL); + g_free(path); + + return TRUE; } /** - * cast toolbar settings to all scaffoldings referenced from the global linked - * list of gui_windows + * handler for pdf export tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static void nsgtk_toolbar_cast(struct nsgtk_scaffolding *g) +static gboolean +pdf_button_clicked_cb(GtkWidget *widget, gpointer data) { - int i; - struct nsgtk_scaffolding *list; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *toplevel; + gchar *filename; + nserror res; - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - window->buttonlocations[i] = - ((nsgtk_scaffolding_button(g, i)->location - >= -1) && - (nsgtk_scaffolding_button(g, i)->location - < PLACEHOLDER_BUTTON)) ? - nsgtk_scaffolding_button(g, i)->location : -1; + bw = tb->get_bw(tb->get_ctx); + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + res = nsgtk_saveas_dialog(bw, + "Export to PDF", + GTK_WINDOW(toplevel), + false, + &filename); + if (res != NSERROR_OK) { + return FALSE; } - list = nsgtk_scaffolding_iterate(NULL); - while (list) { - if (list != g) - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) - nsgtk_scaffolding_button(list, i)->location = - window->buttonlocations[i]; - list = nsgtk_scaffolding_iterate(list); +#ifdef WITH_PDF_EXPORT + struct print_settings *settings; + + /* this way the scale used by PDF functions is synchronised with that + * used by the all-purpose print interface + */ + haru_nsfont_set_scale((float)option_export_scale / 100); + + settings = print_make_settings(PRINT_OPTIONS, + (const char *) filename, + &haru_nsfont); + g_free(filename); + if (settings == NULL) { + return TRUE; } + /* This will clean up the print_settings object for us */ + print_basic_run(browser_window_get_content(bw), &pdf_printer, settings); +#endif + return TRUE; + } /** - * load toolbar settings from file; file is a set of fields arranged as - * [itemreference];[itemlocation]|[itemreference];[itemlocation]| etc + * handler for plain text export tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -void nsgtk_toolbar_customization_load(struct nsgtk_scaffolding *g) +static gboolean +plaintext_button_clicked_cb(GtkWidget *widget, gpointer data) { - int i, ii; - char *buffer; - char *buffer1, *subbuffer, *ptr = NULL, *pter = NULL; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *toplevel; + gchar *filename; + nserror res; + + bw = tb->get_bw(tb->get_ctx); - /* default toolbar button order */ - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - nsgtk_scaffolding_button(g, i)->location = - (i <= THROBBER_ITEM) ? i : -1; + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + res = nsgtk_saveas_dialog(bw, + messages_get("gtkplainSave"), + GTK_WINDOW(toplevel), + false, + &filename); + if (res != NSERROR_OK) { + return FALSE; } - /* ensure the option is actually set */ - if (nsoption_charp(toolbar_order) == NULL) { - return; + + save_as_text(browser_window_get_content(bw), filename); + g_free(filename); + + return TRUE; +} + + +/** + * handler for print tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +print_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkPrintOperation *print_op; + GtkPageSetup *page_setup; + GtkPrintSettings *print_settings; + GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR; + struct print_settings *nssettings; + char *settings_fname = NULL; + GtkWidget *toplevel; + + bw = tb->get_bw(tb->get_ctx); + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + print_op = gtk_print_operation_new(); + if (print_op == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return TRUE; } - buffer = strdup(nsoption_charp(toolbar_order)); - - i = BACK_BUTTON; - ii = BACK_BUTTON; - buffer1 = strtok_r(buffer, "|", &ptr); - while (buffer1 != NULL) { - subbuffer = strtok_r(buffer1, ";", &pter); - if (subbuffer != NULL) { - i = atoi(subbuffer); - subbuffer = strtok_r(NULL, ";", &pter); - if (subbuffer != NULL) { - ii = atoi(subbuffer); - if ((i >= BACK_BUTTON) && - (i < PLACEHOLDER_BUTTON) && - (ii >= -1) && - (ii < PLACEHOLDER_BUTTON)) { - nsgtk_scaffolding_button(g, i)->location = ii; - } - } + + /* use previously saved settings if any */ + netsurf_mkpath(&settings_fname, NULL, 2, nsgtk_config_home, "Print"); + if (settings_fname != NULL) { + print_settings = gtk_print_settings_new_from_file(settings_fname, NULL); + if (print_settings != NULL) { + gtk_print_operation_set_print_settings(print_op, + print_settings); + + /* We're not interested in the settings any more */ + g_object_unref(print_settings); + } + } + + content_to_print = browser_window_get_content(bw); + + page_setup = gtk_print_run_page_setup_dialog(GTK_WINDOW(toplevel), + NULL, + NULL); + if (page_setup == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(settings_fname); + g_object_unref(print_op); + return TRUE; + } + gtk_print_operation_set_default_page_setup(print_op, page_setup); + + nssettings = print_make_settings(PRINT_DEFAULT, + NULL, + nsgtk_layout_table); + + g_signal_connect(print_op, + "begin_print", + G_CALLBACK(gtk_print_signal_begin_print), + nssettings); + g_signal_connect(print_op, + "draw_page", + G_CALLBACK(gtk_print_signal_draw_page), + NULL); + g_signal_connect(print_op, + "end_print", + G_CALLBACK(gtk_print_signal_end_print), + nssettings); + + if (content_get_type(browser_window_get_content(bw)) != CONTENT_TEXTPLAIN) { + res = gtk_print_operation_run(print_op, + GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + GTK_WINDOW(toplevel), + NULL); + } + + /* if the settings were used save them for future use */ + if (settings_fname != NULL) { + if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { + /* Do not increment the settings reference */ + print_settings = gtk_print_operation_get_print_settings(print_op); + + gtk_print_settings_to_file(print_settings, + settings_fname, + NULL); } - buffer1 = strtok_r(NULL, "|", &ptr); + free(settings_fname); + } + + /* Our print_settings object is destroyed by the end print handler */ + g_object_unref(page_setup); + g_object_unref(print_op); + + return TRUE; +} + +/** + * handler for quit tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +quit_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + nsgtk_scaffolding_destroy_all(); + return TRUE; +} + + +/** + * handler for cut tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +cut_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *focused; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + focused = gtk_window_get_focus(GTK_WINDOW(toplevel)); + + /* let gtk handle it if focused widget is an editable */ + if (GTK_IS_EDITABLE(focused)) { + gtk_editable_cut_clipboard(GTK_EDITABLE(focused)); + } else { + bw = tb->get_bw(tb->get_ctx); + browser_window_key_press(bw, NS_KEY_CUT_SELECTION); } - free(buffer); + return TRUE; } /** - * save toolbar settings to file + * handler for copy tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static nserror nsgtk_toolbar_customization_save(struct nsgtk_scaffolding *g) +static gboolean +copy_button_clicked_cb(GtkWidget *widget, gpointer data) { - char *choices = NULL; - char *order; - int order_len = PLACEHOLDER_BUTTON * 12; /* length of order buffer */ - int tbidx; - char *cur; - int plen; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *focused; + GtkWidget *toplevel; - order = malloc(order_len); + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); - if (order == NULL) { - return NSERROR_NOMEM; + focused = gtk_window_get_focus(GTK_WINDOW(toplevel)); + + /* let gtk handle it if focused widget is an editable */ + if (GTK_IS_EDITABLE(focused)) { + gtk_editable_copy_clipboard(GTK_EDITABLE(focused)); + } else { + bw = tb->get_bw(tb->get_ctx); + browser_window_key_press(bw, NS_KEY_COPY_SELECTION); } - cur = order; - for (tbidx = BACK_BUTTON; tbidx < PLACEHOLDER_BUTTON; tbidx++) { - plen = snprintf(cur, - order_len, - "%d;%d|", - tbidx, - nsgtk_scaffolding_button(g, tbidx)->location); - if (plen == order_len) { - /* ran out of space, bail early */ - NSLOG(netsurf, INFO, - "toolbar ordering exceeded available space"); - break; - } - cur += plen; - order_len -= plen; + return TRUE; +} + + +/** + * handler for paste tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +paste_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *focused; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + focused = gtk_window_get_focus(GTK_WINDOW(toplevel)); + + /* let gtk handle it if focused widget is an editable */ + if (GTK_IS_EDITABLE(focused)) { + gtk_editable_paste_clipboard(GTK_EDITABLE(focused)); + } else { + bw = tb->get_bw(tb->get_ctx); + browser_window_key_press(bw, NS_KEY_PASTE); } - nsoption_set_charp(toolbar_order, order); + return TRUE; +} + + +/** + * handler for delete tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +delete_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *focused; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + focused = gtk_window_get_focus(GTK_WINDOW(toplevel)); + + /* let gtk handle it if focused widget is an editable */ + if (GTK_IS_EDITABLE(focused)) { + gtk_editable_delete_selection(GTK_EDITABLE(focused)); + } else { + bw = tb->get_bw(tb->get_ctx); + browser_window_key_press(bw, NS_KEY_CLEAR_SELECTION); + } + + return TRUE; +} + + +/** + * handler for select all tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +selectall_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *focused; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + focused = gtk_window_get_focus(GTK_WINDOW(toplevel)); + + /* let gtk handle it if focused widget is an editable */ + if (GTK_IS_EDITABLE(focused)) { + gtk_editable_select_region(GTK_EDITABLE(focused), 0, -1); + } else { + bw = tb->get_bw(tb->get_ctx); + browser_window_key_press(bw, NS_KEY_SELECT_ALL); + } + + return TRUE; +} + + +/** + * handler for preferences tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +preferences_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *toplevel; + GtkWidget *wndpreferences; + + bw = tb->get_bw(tb->get_ctx); + + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + + wndpreferences = nsgtk_preferences(bw, GTK_WINDOW(toplevel)); + if (wndpreferences != NULL) { + gtk_widget_show(wndpreferences); + } + + return TRUE; +} + + +/** + * handler for zoom plus tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +zoomplus_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + browser_window_set_scale(bw, 0.05, false); + + return TRUE; +} + + +/** + * handler for zoom minus tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +zoomminus_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + browser_window_set_scale(bw, -0.05, false); + + return TRUE; + +} + + +/** + * handler for zoom normal tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +zoomnormal_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + browser_window_set_scale(bw, 1.0, true); + + return TRUE; +} + + +/** + * handler for full screen tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +fullscreen_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + GtkWindow *gtkwindow; /* gtk window widget is in */ + GdkWindow *gdkwindow; + GdkWindowState state; + + gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW)); + gdkwindow = gtk_widget_get_window(GTK_WIDGET(gtkwindow)); + state = gdk_window_get_state(gdkwindow); + + if (state & GDK_WINDOW_STATE_FULLSCREEN) { + gtk_window_unfullscreen(gtkwindow); + } else { + gtk_window_fullscreen(gtkwindow); + } + return TRUE; +} + + +/** + * handler for view source tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +viewsource_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + nserror res; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWindow *gtkwindow; /* gtk window widget is in */ + + bw = tb->get_bw(tb->get_ctx); + + gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW)); + + res = nsgtk_viewsource(gtkwindow, bw); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + + return TRUE; +} + + +/** + * handler for show downloads tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +downloads_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + GtkWindow *gtkwindow; /* gtk window widget is in */ + gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW)); + nsgtk_download_show(gtkwindow); + return TRUE; +} + + +/** + * handler for show downloads tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +savewindowsize_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + GtkWindow *gtkwindow; /* gtk window widget is in */ + int x,y,w,h; + char *choices = NULL; + + gtkwindow = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW)); + + gtk_window_get_position(gtkwindow, &x, &y); + gtk_window_get_size(gtkwindow, &w, &h); + + nsoption_set_int(window_width, w); + nsoption_set_int(window_height, h); + nsoption_set_int(window_x, x); + nsoption_set_int(window_y, y); - /* ensure choices are saved */ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); if (choices != NULL) { nsoption_write(choices, NULL, NULL); free(choices); } - return NSERROR_OK; + return TRUE; } /** - * when 'save settings' button is clicked + * handler for show downloads tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static gboolean nsgtk_toolbar_persist(GtkWidget *widget, gpointer data) +static gboolean +toggledebugging_button_clicked_cb(GtkWidget *widget, gpointer data) { - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + + browser_window_debug(bw, CONTENT_DEBUG_REDRAW); + + nsgtk_window_update_all(); - edit_mode = false; - /* save state to file, update toolbars for all windows */ - nsgtk_toolbar_customization_save(g); - nsgtk_toolbar_cast(g); - nsgtk_toolbar_set_physical(g); - nsgtk_toolbar_close(g); - gtk_widget_destroy(window->window); return TRUE; } + /** - * when 'reload defaults' button is clicked + * handler for debug box tree tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static gboolean nsgtk_toolbar_reset(GtkWidget *widget, gpointer data) +static gboolean +debugboxtree_button_clicked_cb(GtkWidget *widget, gpointer data) { - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; - int i; - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) - nsgtk_scaffolding_button(g, i)->location = - (i <= THROBBER_ITEM) ? i : -1; - nsgtk_toolbar_set_physical(g); - for (i = BACK_BUTTON; i <= THROBBER_ITEM; i++) { - if (i == URL_BAR_ITEM) - continue; - gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM( - nsgtk_scaffolding_button(g, i)->button), TRUE); - gtk_drag_source_set(GTK_WIDGET( - nsgtk_scaffolding_button(g, i)->button), - GDK_BUTTON1_MASK, &entry, 1, GDK_ACTION_COPY); - nsgtk_toolbar_temp_connect(g, i); + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + gchar *fname; + gint handle; + FILE *f; + + handle = g_file_open_tmp("nsgtkboxtreeXXXXXX", &fname, NULL); + if ((handle == -1) || (fname == NULL)) { + return TRUE; } + close(handle); /* in case it was binary mode */ + + /* save data to temporary file */ + f = fopen(fname, "w"); + if (f == NULL) { + nsgtk_warning("Error saving box tree dump.", + "Unable to open file for writing."); + unlink(fname); + return TRUE; + } + + bw = tb->get_bw(tb->get_ctx); + + browser_window_debug_dump(bw, f, CONTENT_DEBUG_RENDER); + + fclose(f); + + nsgtk_viewfile("Box Tree Debug", "boxtree", fname); + + g_free(fname); + return TRUE; } + /** - * when titlebar / alt-F4 window close event happens + * handler for debug dom tree tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static gboolean nsgtk_toolbar_delete(GtkWidget *widget, GdkEvent *event, - gpointer data) +static gboolean +debugdomtree_button_clicked_cb(GtkWidget *widget, gpointer data) { - edit_mode = false; - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; - /* reset g->buttons->location */ - for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - nsgtk_scaffolding_button(g, i)->location = - window->buttonlocations[i]; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + gchar *fname; + gint handle; + FILE *f; + + handle = g_file_open_tmp("nsgtkdomtreeXXXXXX", &fname, NULL); + if ((handle == -1) || (fname == NULL)) { + return TRUE; } - nsgtk_toolbar_set_physical(g); - nsgtk_toolbar_connect_all(g); - nsgtk_toolbar_close(g); - nsgtk_scaffolding_set_sensitivity(g); - gtk_widget_destroy(window->window); + close(handle); /* in case it was binary mode */ + + /* save data to temporary file */ + f = fopen(fname, "w"); + if (f == NULL) { + nsgtk_warning("Error saving box tree dump.", + "Unable to open file for writing."); + unlink(fname); + return TRUE; + } + + bw = tb->get_bw(tb->get_ctx); + + browser_window_debug_dump(bw, f, CONTENT_DEBUG_DOM); + + fclose(f); + + nsgtk_viewfile("DOM Tree Debug", "domtree", fname); + + g_free(fname); + return TRUE; + } + /** - * called when a widget is dropped onto the store window + * handler for local history tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ static gboolean -nsgtk_toolbar_store_return(GtkWidget *widget, GdkDragContext *gdc, - gint x, gint y, guint time, gpointer data) +localhistory_button_clicked_cb(GtkWidget *widget, gpointer data) { - struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; - int q, i; + nserror res; + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + GtkWidget *toplevel; - if ((window->fromstore) || (window->currentbutton == -1)) { - window->currentbutton = -1; - return FALSE; - } - if (nsgtk_scaffolding_button(g, window->currentbutton)->location - != -1) { - /* 'move' all widgets further right, one place to the left - * in logical schema */ - for (i = nsgtk_scaffolding_button(g, window->currentbutton)-> - location + 1; i < PLACEHOLDER_BUTTON; i++) { - q = nsgtk_toolbar_get_id_at_location(g, i); - if (q == -1) - continue; - nsgtk_scaffolding_button(g, q)->location--; + toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); + if (toplevel != NULL) { + bw = tb->get_bw(tb->get_ctx); + + res = nsgtk_local_history_present(GTK_WINDOW(toplevel), bw); + if (res != NSERROR_OK) { + NSLOG(netsurf, INFO, + "Unable to present local history window."); } - gtk_container_remove(GTK_CONTAINER( - nsgtk_scaffolding_toolbar(g)), GTK_WIDGET( - nsgtk_scaffolding_button(g, - window->currentbutton)->button)); - nsgtk_scaffolding_button(g, window->currentbutton)->location - = -1; - } - window->currentbutton = -1; - gtk_drag_finish(gdc, TRUE, TRUE, time); - return FALSE; + } + return TRUE; } /** - * called when hovering above the store + * handler for history tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ static gboolean -nsgtk_toolbar_store_action(GtkWidget *widget, GdkDragContext *gdc, - gint x, gint y, guint time, gpointer data) +history_button_clicked_cb(GtkWidget *widget, gpointer data) { - return FALSE; + return localhistory_button_clicked_cb(widget, data); } + /** - * create store window + * handler for global history tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE */ -static void nsgtk_toolbar_window_open(struct nsgtk_scaffolding *g) +static gboolean +globalhistory_button_clicked_cb(GtkWidget *widget, gpointer data) { - struct nsgtk_theme *theme; nserror res; + res = nsgtk_global_history_present(); + if (res != NSERROR_OK) { + NSLOG(netsurf, INFO, + "Unable to initialise global history window."); + } + return TRUE; +} - theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, true); - if (theme == NULL) { - nsgtk_warning(messages_get("NoMemory"), 0); - nsgtk_toolbar_cancel_clicked(NULL, g); - return; + +/** + * handler for add bookmark tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +addbookmarks_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct browser_window *bw; + + bw = tb->get_bw(tb->get_ctx); + if (browser_window_has_content(bw)) { + hotlist_add_url(browser_window_access_url(bw)); } + return TRUE; +} + - res = nsgtk_builder_new_from_resname("toolbar", &window->builder); +/** + * handler for show bookmark tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +showbookmarks_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + nserror res; + res = nsgtk_hotlist_present(); if (res != NSERROR_OK) { - NSLOG(netsurf, INFO, "Toolbar UI builder init failed"); - nsgtk_warning("Toolbar UI builder init failed", 0); - nsgtk_toolbar_cancel_clicked(NULL, g); - free(theme); - return; + NSLOG(netsurf, INFO, "Unable to initialise bookmark window."); } + return TRUE; +} - gtk_builder_connect_signals(window->builder, NULL); - window->window = GTK_WIDGET(gtk_builder_get_object( - window->builder, "dialogToolbar")); - if (window->window == NULL) { - nsgtk_warning(messages_get("NoMemory"), 0); - nsgtk_toolbar_cancel_clicked(NULL, g); - free(theme); - return; +/** + * handler for show cookies tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +showcookies_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + nserror res; + res = nsgtk_cookies_present(NULL); + if (res != NSERROR_OK) { + NSLOG(netsurf, INFO, "Unable to initialise cookies window."); } + return TRUE; +} - gtk_window_set_transient_for(GTK_WINDOW(window->window), - nsgtk_scaffolding_window(g)); - window->widgetvbox = GTK_WIDGET(gtk_builder_get_object( - window->builder, "widgetvbox")); - if (window->widgetvbox == NULL) { - nsgtk_warning(messages_get("NoMemory"), 0); - nsgtk_toolbar_cancel_clicked(NULL, g); - free(theme); - return; +/** + * handler for open location tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +openlocation_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + GtkToolItem *urltitem; + + urltitem = tb->items[URL_BAR_ITEM].button; + if (urltitem != NULL) { + GtkEntry *entry; + entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(urltitem))); + gtk_widget_grab_focus(GTK_WIDGET(entry)); } + return TRUE; +} - /* preset to width [in buttons] of */ - window->numberh = NSGTK_STORE_WIDTH; - /* store to cause creation of a new toolbar */ - window->currentbutton = -1; +/** + * handler for contents tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +contents_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + nserror res; - /* load toolbuttons */ - /* add toolbuttons to window */ - /* set event handlers */ - for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - if (i == URL_BAR_ITEM) - continue; - window->store_buttons[i] = - nsgtk_toolbar_make_widget(g, i, theme); - if (window->store_buttons[i] == NULL) { - nsgtk_warning(messages_get("NoMemory"), 0); - continue; - } - nsgtk_toolbar_add_store_widget(window->store_buttons[i]); - g_signal_connect(window->store_buttons[i], "drag-data-get", - G_CALLBACK( - nsgtk_scaffolding_button(g, i)->dataplus), g); + res = toolbar_navigate_to_url(tb, "https://www.netsurf-browser.org/documentation/"); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } + + return TRUE; +} + +/** + * handler for contents tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +guide_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + nserror res; + + res = toolbar_navigate_to_url(tb, "https://www.netsurf-browser.org/documentation/guide"); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); } - free(theme); + return TRUE; +} + + +/** + * handler for contents tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean +info_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + nserror res; - gtk_window_set_accept_focus(GTK_WINDOW(window->window), FALSE); + res = toolbar_navigate_to_url(tb, "https://www.netsurf-browser.org/documentation/info"); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + } - gtk_drag_dest_set(GTK_WIDGET(window->window), GTK_DEST_DEFAULT_MOTION | - GTK_DEST_DEFAULT_DROP, &entry, 1, GDK_ACTION_COPY); + return TRUE; +} - g_signal_connect(GTK_WIDGET(gtk_builder_get_object( - window->builder, "close")), - "clicked", - G_CALLBACK(nsgtk_toolbar_persist), - g); - g_signal_connect(GTK_WIDGET(gtk_builder_get_object( - window->builder, "reset")), - "clicked", - G_CALLBACK(nsgtk_toolbar_reset), - g); +/** + * handler for contents tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE + */ +static gboolean about_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + GtkWindow *parent; /* gtk window widget is in */ + + parent = GTK_WINDOW(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)); + + nsgtk_about_dialog_init(parent); + return TRUE; +} + +/** + * handler for openmenu tool bar item clicked signal + * + * \param widget The widget the signal is being delivered to. + * \param data The toolbar context passed when the signal was connected + * \return TRUE to indicate signal handled. + */ +static gboolean openmenu_button_clicked_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct gui_window *gw; + struct nsgtk_scaffolding *gs; + + gw = tb->get_ctx; /** \todo stop assuming the context is a gui window */ - g_signal_connect(window->window, "delete-event", - G_CALLBACK(nsgtk_toolbar_delete), g); + gs = nsgtk_get_scaffold(gw); - g_signal_connect(window->window, "drag-drop", - G_CALLBACK(nsgtk_toolbar_store_return), g); + nsgtk_scaffolding_burger_menu(gs); + + return TRUE; +} - g_signal_connect(window->window, "drag-motion", - G_CALLBACK(nsgtk_toolbar_store_action), g); - gtk_widget_show_all(window->window); +/* define data plus and data minus handlers */ +#define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate, label, iconame) \ +static gboolean \ +nsgtk_toolbar_##name##_data_plus(GtkWidget *widget, \ + GdkDragContext *cont, \ + GtkSelectionData *selection, \ + guint info, \ + guint time, \ + gpointer data) \ +{ \ + struct nsgtk_toolbar_customisation *tbc; \ + tbc = (struct nsgtk_toolbar_customisation *)data; \ + tbc->dragitem = identifier; \ + tbc->dragfrom = true; \ + return TRUE; \ +} \ +static gboolean \ +nsgtk_toolbar_##name##_data_minus(GtkWidget *widget, \ + GdkDragContext *cont, \ + GtkSelectionData *selection, \ + guint info, \ + guint time, \ + gpointer data) \ +{ \ + struct nsgtk_toolbar_customisation *tbc; \ + tbc = (struct nsgtk_toolbar_customisation *)data; \ + tbc->dragitem = identifier; \ + tbc->dragfrom = false; \ + return TRUE; \ } +#include "gtk/toolbar_items.h" + +#undef TOOLBAR_ITEM + + /** - * change behaviour of scaffoldings while editing toolbar + * create a toolbar item * - * All buttons as well as window clicks are desensitized; then buttons - * in the front window are changed to movable buttons + * create a toolbar item and set up its default handlers */ -void nsgtk_toolbar_customization_init(struct nsgtk_scaffolding *g) +static nserror +toolbar_item_create(nsgtk_toolbar_button id, struct nsgtk_toolbar_item *item) { - int i; - struct nsgtk_scaffolding *list; - edit_mode = true; + item->location = INACTIVE_LOCATION; + + /* set item defaults from macro */ + switch (id) { +#define TOOLBAR_ITEM_t(name) \ + item->clicked = name##_button_clicked_cb; +#define TOOLBAR_ITEM_b(name) \ + item->clicked = name##_button_clicked_cb; +#define TOOLBAR_ITEM_y(name) \ + item->clicked = name##_button_clicked_cb; +#define TOOLBAR_ITEM_n(name) \ + item->clicked = NULL; +#define TOOLBAR_ITEM(identifier, iname, snstvty, clicked, activate, label, iconame) \ + case identifier: \ + item->name = #iname; \ + item->sensitivity = snstvty; \ + item->dataplus = nsgtk_toolbar_##iname##_data_plus; \ + item->dataminus = nsgtk_toolbar_##iname##_data_minus; \ + TOOLBAR_ITEM_ ## clicked(iname) \ + break; - list = nsgtk_scaffolding_iterate(NULL); - while (list) { - g_signal_handler_block(GTK_WIDGET( - nsgtk_window_get_layout( - nsgtk_scaffolding_top_level(list))), - nsgtk_window_get_signalhandler( - nsgtk_scaffolding_top_level(list), - NSGTK_WINDOW_SIGNAL_CLICK)); - g_signal_handler_block(GTK_WIDGET( - nsgtk_window_get_layout( - nsgtk_scaffolding_top_level(list))), - nsgtk_window_get_signalhandler( - nsgtk_scaffolding_top_level(list), - NSGTK_WINDOW_SIGNAL_REDRAW)); - nsgtk_widget_override_background_color( - GTK_WIDGET(nsgtk_window_get_layout( - nsgtk_scaffolding_top_level(list))), - GTK_STATE_NORMAL, 0, 0xEEEE, 0xEEEE, 0xEEEE); +#include "gtk/toolbar_items.h" - if (list == g) { - list = nsgtk_scaffolding_iterate(list); - continue; - } - /* set sensitive for all gui_windows save g */ - gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_window( - list)), FALSE); - list = nsgtk_scaffolding_iterate(list); - } - /* set sensitive for all of g save toolbar */ - gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)), - FALSE); - gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)), - FALSE); - - /* set editable aspect for toolbar */ - gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)), - nsgtk_toolbar_clear_toolbar, g); - nsgtk_toolbar_set_physical(g); - /* memorize button locations, set editable */ - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - window->buttonlocations[i] = nsgtk_scaffolding_button(g, i) - ->location; - if ((window->buttonlocations[i] == -1) || (i == URL_BAR_ITEM)) - continue; - gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM( - nsgtk_scaffolding_button(g, i)->button), TRUE); - gtk_drag_source_set(GTK_WIDGET(nsgtk_scaffolding_button( - g, i)->button), GDK_BUTTON1_MASK, &entry, 1, - GDK_ACTION_COPY); - nsgtk_toolbar_temp_connect(g, i); +#undef TOOLBAR_ITEM_t +#undef TOOLBAR_ITEM_y +#undef TOOLBAR_ITEM_n +#undef TOOLBAR_ITEM + + case PLACEHOLDER_BUTTON: + return NSERROR_INVALID; } - /* add move button listeners */ - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), - "drag-drop", G_CALLBACK(nsgtk_toolbar_data), g); - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), - "drag-data-received", G_CALLBACK( - nsgtk_toolbar_move_complete), g); - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), - "drag-motion", G_CALLBACK(nsgtk_toolbar_action), g); - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), - "drag-leave", G_CALLBACK( - nsgtk_toolbar_clear), g); + return NSERROR_OK; +} - /* set data types */ - gtk_drag_dest_set(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), - GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, - &entry, 1, GDK_ACTION_COPY); - /* open toolbar window */ - nsgtk_toolbar_window_open(g); +/** + * set a toolbar item to a throbber frame number + * + * \param toolbar_item The toolbar item to update + * \param frame The animation frame number to update to + * \return NSERROR_OK on success, + * NSERROR_INVALID if the toolbar item does not contain an image, + * NSERROR_BAD_SIZE if the frame is out of range. + */ +static nserror set_throbber_frame(GtkToolItem *toolbar_item, int frame) +{ + nserror res; + GdkPixbuf *pixbuf; + GtkImage *throbber; + + if (toolbar_item == NULL) { + /* no toolbar item */ + return NSERROR_INVALID; + } + + res = nsgtk_throbber_get_frame(frame, &pixbuf); + if (res != NSERROR_OK) { + return res; + } + + throbber = GTK_IMAGE(gtk_bin_get_child(GTK_BIN(toolbar_item))); + + gtk_image_set_from_pixbuf(throbber, pixbuf); + + return NSERROR_OK; +} + + +/** + * Make the throbber run. + * + * scheduled callback to update the throbber + * + * \param p The context passed when scheduled. + */ +static void next_throbber_frame(void *p) +{ + struct nsgtk_toolbar *tb = p; + nserror res; + + tb->throb_frame++; /* advance to next frame */ + + res = set_throbber_frame(tb->items[THROBBER_ITEM].button, + tb->throb_frame); + if (res == NSERROR_BAD_SIZE) { + tb->throb_frame = 1; + res = set_throbber_frame(tb->items[THROBBER_ITEM].button, + tb->throb_frame); + } + + /* only schedule next frame if there are no errors */ + if (res == NSERROR_OK) { + nsgtk_schedule(THROBBER_FRAME_TIME, next_throbber_frame, p); + } } + /** - * set toolbar logical -> physical; physically visible toolbar buttons are made - * to correspond to the logically stored schema in terms of location - * visibility etc + * connect signal handlers to a gtk toolbar item */ -void nsgtk_toolbar_set_physical(struct nsgtk_scaffolding *g) +static nserror +toolbar_connect_signal(struct nsgtk_toolbar *tb, nsgtk_toolbar_button itemid) { - int i; - struct nsgtk_theme *theme; + struct nsgtk_toolbar_item *item; + GtkEntry *entry; - theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, false); - if (theme == NULL) { - nsgtk_warning(messages_get("NoMemory"), 0); - return; + item = &tb->items[itemid]; + + if (item->button != NULL) { + g_signal_connect(item->button, + "size-allocate", + G_CALLBACK(toolbar_item_size_allocate_cb), + tb); } - /* simplest is to clear the toolbar then reload it from memory */ - gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)), - nsgtk_toolbar_clear_toolbar, g); - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - nsgtk_toolbar_add_item_to_toolbar(g, i, theme); + + switch (itemid) { + case URL_BAR_ITEM: + entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(item->button))); + + g_signal_connect(GTK_WIDGET(entry), + "activate", + G_CALLBACK(url_entry_activate_cb), + tb); + g_signal_connect(GTK_WIDGET(entry), + "changed", + G_CALLBACK(url_entry_changed_cb), + tb); + g_signal_connect(GTK_WIDGET(entry), + "icon-release", + G_CALLBACK(url_entry_icon_release_cb), + tb); + + nsgtk_completion_connect_signals(entry, + tb->get_bw, + tb->get_ctx); + break; + + + case WEBSEARCH_ITEM: + entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(item->button))); + + g_signal_connect(GTK_WIDGET(entry), + "activate", + G_CALLBACK(websearch_entry_activate_cb), + tb); + g_signal_connect(GTK_WIDGET(entry), + "button-press-event", + G_CALLBACK(websearch_entry_button_press_cb), + tb); + break; + + default: + if ((item->clicked != NULL) && (item->button != NULL)) { + g_signal_connect(item->button, + "clicked", + G_CALLBACK(item->clicked), + tb); + } + break; + } - gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(g))); - free(theme); + + return NSERROR_OK; } /** - * \return toolbar item id when a widget is an element of the scaffolding - * else -1 + * connect all signals to widgets in a toolbar */ -int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, - struct nsgtk_scaffolding *g) +static nserror toolbar_connect_signals(struct nsgtk_toolbar *tb) { - int i; - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - if ((nsgtk_scaffolding_button(g, i)->location != -1) - && (widget == GTK_WIDGET( - nsgtk_scaffolding_button(g, i)->button))) { - return i; + int location; /* location index */ + nsgtk_toolbar_button itemid; /* item id */ + + for (location = BACK_BUTTON; location < PLACEHOLDER_BUTTON; location++) { + itemid = itemid_from_location(tb, location); + if (itemid == PLACEHOLDER_BUTTON) { + /* no more filled locations */ + break; } + toolbar_connect_signal(tb, itemid); } - return -1; + + return NSERROR_OK; } /** - * add handlers to factory widgets - * \param g the scaffolding to attach handlers to - * \param i the toolbar item id + * signal handler for toolbar context menu + * + * \param toolbar The toolbar event is being delivered to + * \param x The x coordinate where the click happened + * \param y The x coordinate where the click happened + * \param button the buttons being pressed + * \param data The context pointer passed when the connection was made. + * \return TRUE to indicate signal handled. */ -static void -nsgtk_toolbar_set_handler(struct nsgtk_scaffolding *g, nsgtk_toolbar_button i) +static gboolean +toolbar_popup_context_menu_cb(GtkToolbar *toolbar, + gint x, + gint y, + gint button, + gpointer data) { - switch(i){ - case URL_BAR_ITEM: - nsgtk_scaffolding_update_url_bar_ref(g); - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)), - "activate", G_CALLBACK( - nsgtk_window_url_activate_event), g); - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)), - "changed", G_CALLBACK( - nsgtk_window_url_changed), g); + struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data; + struct gui_window *gw; + struct nsgtk_scaffolding *gs; + + gw = tb->get_ctx; /** \todo stop assuming the context is a gui window */ + + gs = nsgtk_get_scaffold(gw); + + nsgtk_scaffolding_toolbar_context_menu(gs); + + return TRUE; +} + + +/** + * toolbar delete signal handler + */ +static void toolbar_destroy_cb(GtkWidget *widget, gpointer data) +{ + struct nsgtk_toolbar *tb; + tb = (struct nsgtk_toolbar *)data; + + /* ensure any throbber scheduled is stopped */ + nsgtk_schedule(-1, next_throbber_frame, tb); + + free(tb); +} + + +/* exported interface documented in toolbar.h */ +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) +{ + nserror res; + struct nsgtk_toolbar *tb; + int bidx; /* button index */ + + tb = calloc(1, sizeof(struct nsgtk_toolbar)); + if (tb == NULL) { + return NSERROR_NOMEM; + } + + tb->get_bw = get_bw; + tb->get_ctx = get_ctx; + /* set the throbber start frame. */ + tb->throb_frame = 0; + if (want_location_focus) { + tb->loc_focus = LFS_WANT; + } else { + tb->loc_focus = LFS_IDLE; + } + + tb->widget = GTK_TOOLBAR(gtk_builder_get_object(builder, "toolbar")); + gtk_toolbar_set_show_arrow(tb->widget, TRUE); + + g_signal_connect(tb->widget, + "popup-context-menu", + G_CALLBACK(toolbar_popup_context_menu_cb), + tb); + + /* close and cleanup on delete signal */ + g_signal_connect(tb->widget, + "destroy", + G_CALLBACK(toolbar_destroy_cb), + tb); + + /* allocate button contexts */ + for (bidx = BACK_BUTTON; bidx < PLACEHOLDER_BUTTON; bidx++) { + res = toolbar_item_create(bidx, &tb->items[bidx]); + if (res != NSERROR_OK) { + return res; + } + } + + res = nsgtk_toolbar_update(tb); + if (res != NSERROR_OK) { + return res; + } + + *tb_out = tb; + return NSERROR_OK; +} + + +/* exported interface documented in toolbar.h */ +nserror nsgtk_toolbar_restyle(struct nsgtk_toolbar *tb) +{ + /* + * reset toolbar size allocation so icon size change affects + * allocated widths. + */ + tb->offset = 0; + + switch (nsoption_int(button_type)) { + + case 1: /* Small icons */ + gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(tb->widget), + GTK_ICON_SIZE_SMALL_TOOLBAR); break; - case THROBBER_ITEM: - nsgtk_scaffolding_update_throbber_ref(g); + case 2: /* Large icons */ + gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(tb->widget), + GTK_ICON_SIZE_LARGE_TOOLBAR); break; - case WEBSEARCH_ITEM: - nsgtk_scaffolding_update_websearch_ref(g); - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)), - "activate", G_CALLBACK( - nsgtk_websearch_activate), g); - g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)), - "button-press-event", G_CALLBACK( - nsgtk_websearch_clear), g); + case 3: /* Large icons with text */ + gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget), + GTK_TOOLBAR_BOTH); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(tb->widget), + GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + + case 4: /* Text icons only */ + gtk_toolbar_set_style(GTK_TOOLBAR(tb->widget), + GTK_TOOLBAR_TEXT); break; default: - if ((nsgtk_scaffolding_button(g, i)->bhandler != NULL) && - (nsgtk_scaffolding_button(g, i)->button != NULL)) { - g_signal_connect( - nsgtk_scaffolding_button(g, i)->button, - "clicked", - G_CALLBACK(nsgtk_scaffolding_button( - g, i)->bhandler), g); + break; + } + + return NSERROR_OK; +} + + +/* exported interface documented in toolbar.h */ +nserror nsgtk_toolbar_throbber(struct nsgtk_toolbar *tb, bool active) +{ + nserror res; + struct browser_window *bw; + + /* Manage the location focus state */ + switch (tb->loc_focus) { + case LFS_IDLE: + break; + case LFS_WANT: + if (active) { + tb->loc_focus = LFS_THROB; + } + break; + case LFS_THROB: + if (!active) { + tb->loc_focus = LFS_LAST; } break; + case LFS_LAST: + break; + } + + /* when activating the throbber simply schedule the next frame update */ + if (active) { + nsgtk_schedule(THROBBER_FRAME_TIME, next_throbber_frame, tb); + + set_item_sensitivity(&tb->items[STOP_BUTTON], true); + set_item_sensitivity(&tb->items[RELOAD_BUTTON], false); + set_item_action(tb, RELOADSTOP_BUTTON, false); + + return NSERROR_OK; + } + + /* stopping the throbber */ + nsgtk_schedule(-1, next_throbber_frame, tb); + tb->throb_frame = 0; + res = set_throbber_frame(tb->items[THROBBER_ITEM].button, + tb->throb_frame); + + bw = tb->get_bw(tb->get_ctx); + + /* adjust sensitivity of other items */ + set_item_sensitivity(&tb->items[STOP_BUTTON], false); + set_item_sensitivity(&tb->items[RELOAD_BUTTON], true); + set_item_action(tb, RELOADSTOP_BUTTON, true); + set_item_sensitivity(&tb->items[BACK_BUTTON], + browser_window_history_back_available(bw)); + set_item_sensitivity(&tb->items[FORWARD_BUTTON], + browser_window_history_forward_available(bw)); + nsgtk_local_history_hide(); + + return res; +} + + +/* exported interface documented in toolbar.h */ +nserror nsgtk_toolbar_page_info_change(struct nsgtk_toolbar *tb) +{ + GtkEntry *url_entry; + browser_window_page_info_state pistate; + struct browser_window *bw; + const char *icon_name; + + if (tb->items[URL_BAR_ITEM].button == NULL) { + /* no toolbar item */ + return NSERROR_INVALID; + } + url_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tb->items[URL_BAR_ITEM].button))); + + bw = tb->get_bw(tb->get_ctx); + + pistate = browser_window_get_page_info_state(bw); + + switch (pistate) { + case PAGE_STATE_INTERNAL: + icon_name = "page-info-internal"; + break; + + case PAGE_STATE_LOCAL: + icon_name = "page-info-local"; + break; + + case PAGE_STATE_INSECURE: + icon_name = "page-info-insecure"; + break; + + case PAGE_STATE_SECURE_OVERRIDE: + icon_name = "page-info-warning"; + break; + + case PAGE_STATE_SECURE_ISSUES: + icon_name = "page-info-warning"; + break; + + case PAGE_STATE_SECURE: + icon_name = "page-info-secure"; + break; + + default: + icon_name = "page-info-internal"; + break; + } + + nsgtk_entry_set_icon_from_icon_name(GTK_WIDGET(url_entry), + GTK_ENTRY_ICON_PRIMARY, + icon_name); + return NSERROR_OK; +} + + +/* exported interface documented in toolbar.h */ +nserror nsgtk_toolbar_set_url(struct nsgtk_toolbar *tb, nsurl *url) +{ + size_t idn_url_l; + char *idn_url_s = NULL; + const char *url_text = NULL; + GtkEntry *url_entry; + + if (tb->items[URL_BAR_ITEM].button == NULL) { + /* no toolbar item */ + return NSERROR_INVALID; + } + url_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tb->items[URL_BAR_ITEM].button))); + + if (nsoption_bool(display_decoded_idn) == true) { + if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) != NSERROR_OK) { + idn_url_s = NULL; + } + url_text = idn_url_s; + } + if (url_text == NULL) { + url_text = nsurl_access(url); + } + + if (strcmp(url_text, gtk_entry_get_text(url_entry)) != 0) { + /* The URL bar content has changed, we need to update it */ + gint startpos, endpos; + bool was_selected; + gtk_editable_get_selection_bounds(GTK_EDITABLE(url_entry), + &startpos, &endpos); + was_selected = gtk_widget_is_focus(GTK_WIDGET(url_entry)) && + startpos == 0 && + endpos == gtk_entry_get_text_length(url_entry); + gtk_entry_set_text(url_entry, url_text); + if (was_selected && tb->loc_focus != LFS_IDLE) { + gtk_widget_grab_focus(GTK_WIDGET(url_entry)); + if (tb->loc_focus == LFS_LAST) { + tb->loc_focus = LFS_IDLE; + } + } } + + if (idn_url_s != NULL) { + free(idn_url_s); + } + + return NSERROR_OK; +} + + +/* exported interface documented in toolbar.h */ +nserror +nsgtk_toolbar_set_websearch_image(struct nsgtk_toolbar *tb, GdkPixbuf *pixbuf) +{ + GtkWidget *entry; + + if (tb->items[WEBSEARCH_ITEM].button == NULL) { + /* no toolbar item */ + return NSERROR_INVALID; + } + + entry = gtk_bin_get_child(GTK_BIN(tb->items[WEBSEARCH_ITEM].button)); + + if (pixbuf != NULL) { + nsgtk_entry_set_icon_from_pixbuf(entry, + GTK_ENTRY_ICON_PRIMARY, + pixbuf); + } else { + nsgtk_entry_set_icon_from_icon_name(entry, + GTK_ENTRY_ICON_PRIMARY, + NSGTK_STOCK_INFO); + } + + return NSERROR_OK; +} + + +/* exported interface documented in toolbar.h */ +nserror +nsgtk_toolbar_item_activate(struct nsgtk_toolbar *tb, + nsgtk_toolbar_button itemid) +{ + GtkWidget *widget; + + /* ensure item id in range */ + if ((itemid < BACK_BUTTON) || (itemid >= PLACEHOLDER_BUTTON)) { + return NSERROR_BAD_PARAMETER; + } + + if (tb->items[itemid].clicked == NULL) { + return NSERROR_INVALID; + } + + /* + * if item has a widget in the current toolbar use that as the + * signal source otherwise use the toolbar widget itself. + */ + if (tb->items[itemid].button != NULL) { + widget = GTK_WIDGET(tb->items[itemid].button); + } else { + widget = GTK_WIDGET(tb->widget); + } + + tb->items[itemid].clicked(widget, tb); + + return NSERROR_OK; +} + + +/* exported interface documented in toolbar.h */ +nserror nsgtk_toolbar_show(struct nsgtk_toolbar *tb, bool show) +{ + if (show) { + gtk_widget_show(GTK_WIDGET(tb->widget)); + } else { + gtk_widget_hide(GTK_WIDGET(tb->widget)); + } + return NSERROR_OK; +} + + +/* exported interface documented in toolbar.h */ +nserror nsgtk_toolbar_update(struct nsgtk_toolbar *tb) +{ + nserror res; + + /* setup item locations based on user config */ + res = apply_user_button_customisation(tb); + if (res != NSERROR_OK) { + return res; + } + + /* populate toolbar widget */ + res = populate_gtk_toolbar_widget(tb); + if (res != NSERROR_OK) { + return res; + } + + /* ensure icon sizes and text labels on toolbar are set */ + res = nsgtk_toolbar_restyle(tb); + if (res != NSERROR_OK) { + return res; + } + + res = toolbar_connect_signals(tb); + + return res; } /** - * connect 'normal' handlers to toolbar buttons + * Find the correct location for popping up a window for the chosen item. + * + * \param tb The toolbar to select from + * \param item_idx The toolbar item to select from + * \param out_x Filled with an appropriate X coordinate + * \param out_y Filled with an appropriate Y coordinate */ -void nsgtk_toolbar_connect_all(struct nsgtk_scaffolding *g) +static nserror +nsgtk_toolbar_get_icon_window_position(struct nsgtk_toolbar *tb, + int item_idx, + int *out_x, + int *out_y) { - int q, i; - for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { - q = nsgtk_toolbar_get_id_at_location(g, i); - if (q == -1) - continue; - if (nsgtk_scaffolding_button(g, q)->button != NULL) - g_signal_connect( - nsgtk_scaffolding_button(g, q)->button, - "size-allocate", G_CALLBACK( - nsgtk_scaffolding_toolbar_size_allocate - ), g); - nsgtk_toolbar_set_handler(g, q); - } -} - - -#define DATAHANDLER(p, q, r)\ -gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\ - *cont, GtkSelectionData *selection, guint info, guint time,\ - gpointer data)\ -{\ - r->currentbutton = q##_BUTTON;\ - r->fromstore = true;\ - return TRUE;\ -}\ -gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\ - GdkDragContext *cont, GtkSelectionData *selection, guint info,\ - guint time, gpointer data)\ -{\ - r->currentbutton = q##_BUTTON;\ - r->fromstore = false;\ - return TRUE;\ -} - -DATAHANDLER(home, HOME, window) -DATAHANDLER(forward, FORWARD, window) -DATAHANDLER(back, BACK, window) -DATAHANDLER(stop, STOP, window) -DATAHANDLER(reload, RELOAD, window) -DATAHANDLER(history, HISTORY, window) -DATAHANDLER(newwindow, NEWWINDOW, window) -DATAHANDLER(newtab, NEWTAB, window) -DATAHANDLER(openfile, OPENFILE, window) -DATAHANDLER(closetab, CLOSETAB, window) -DATAHANDLER(closewindow, CLOSEWINDOW, window) -DATAHANDLER(savepage, SAVEPAGE, window) -DATAHANDLER(printpreview, PRINTPREVIEW, window) -DATAHANDLER(print, PRINT, window) -DATAHANDLER(quit, QUIT, window) -DATAHANDLER(cut, CUT, window) -DATAHANDLER(copy, COPY, window) -DATAHANDLER(paste, PASTE, window) -DATAHANDLER(delete, DELETE, window) -DATAHANDLER(selectall, SELECTALL, window) -DATAHANDLER(preferences, PREFERENCES, window) -DATAHANDLER(zoomplus, ZOOMPLUS, window) -DATAHANDLER(zoomminus, ZOOMMINUS, window) -DATAHANDLER(zoomnormal, ZOOMNORMAL, window) -DATAHANDLER(fullscreen, FULLSCREEN, window) -DATAHANDLER(viewsource, VIEWSOURCE, window) -DATAHANDLER(contents, CONTENTS, window) -DATAHANDLER(about, ABOUT, window) -DATAHANDLER(pdf, PDF, window) -DATAHANDLER(plaintext, PLAINTEXT, window) -DATAHANDLER(drawfile, DRAWFILE, window) -DATAHANDLER(postscript, POSTSCRIPT, window) -DATAHANDLER(find, FIND, window) -DATAHANDLER(downloads, DOWNLOADS, window) -DATAHANDLER(savewindowsize, SAVEWINDOWSIZE, window) -DATAHANDLER(toggledebugging, TOGGLEDEBUGGING, window) -DATAHANDLER(debugboxtree, SAVEBOXTREE, window) -DATAHANDLER(debugdomtree, SAVEDOMTREE, window) -DATAHANDLER(localhistory, LOCALHISTORY, window) -DATAHANDLER(globalhistory, GLOBALHISTORY, window) -DATAHANDLER(addbookmarks, ADDBOOKMARKS, window) -DATAHANDLER(showbookmarks, SHOWBOOKMARKS, window) -DATAHANDLER(showcookies, SHOWCOOKIES, window) -DATAHANDLER(openlocation, OPENLOCATION, window) -DATAHANDLER(nexttab, NEXTTAB, window) -DATAHANDLER(prevtab, PREVTAB, window) -DATAHANDLER(guide, GUIDE, window) -DATAHANDLER(info, INFO, window) -#undef DATAHANDLER - -#define DATAHANDLER(p, q, r) \ -gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\ - *cont, GtkSelectionData *selection, guint info, guint time,\ - gpointer data)\ -{\ - r->currentbutton = q##_ITEM;\ - r->fromstore = true;\ - return TRUE;\ -}\ -gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\ - GdkDragContext *cont, GtkSelectionData *selection, guint info,\ - guint time, gpointer data)\ -{\ - r->currentbutton = q##_ITEM;\ - r->fromstore = false;\ - return TRUE;\ -} - -DATAHANDLER(throbber, THROBBER, window) -DATAHANDLER(websearch, WEBSEARCH, window) -#undef DATAHANDLER + struct nsgtk_toolbar_item *item = &tb->items[item_idx]; + GtkWidget *widget = GTK_WIDGET(item->button); + GtkAllocation alloc; + gint rootx, rooty, x, y; + + switch (item_idx) { + case URL_BAR_ITEM: + widget = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(item->button))); + break; + default: + /* Nothing to do here */ + break; + } + + nsgtk_widget_get_allocation(widget, &alloc); + + if (gtk_widget_translate_coordinates(widget, + gtk_widget_get_toplevel(widget), + 0, + alloc.height - 1, + &x, &y) != TRUE) { + return NSERROR_UNKNOWN; + } + + gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(widget)), + &rootx, &rooty); + + *out_x = rootx + x + 4; + *out_y = rooty + y + 4; + + return NSERROR_OK; +} + +nserror nsgtk_toolbar_position_page_info(struct nsgtk_toolbar *tb, + struct nsgtk_pi_window *win) +{ + nserror res; + int x, y; + + res = nsgtk_toolbar_get_icon_window_position(tb, URL_BAR_ITEM, &x, &y); + if (res != NSERROR_OK) { + return res; + } + + nsgtk_page_info_set_position(win, x, y); + + return NSERROR_OK; +} + +/* exported interface documented in toolbar.h */ +nserror nsgtk_toolbar_position_local_history(struct nsgtk_toolbar *tb) +{ + nserror res; + int x, y; + + res = nsgtk_toolbar_get_icon_window_position(tb, HISTORY_BUTTON, &x, &y); + if (res != NSERROR_OK) { + return res; + } + + nsgtk_local_history_set_position(x, y); + + return NSERROR_OK; +} |