netsurf/frontends/gtk/toolbar.c
Daniel Silverstone 4b8ed9b777
GTK: Cause the page-info popup to appear in the right place
To position the page-info window we have to tunnel all the
way from the current scaffolding, via its top-level gui window,
through to the toolbar.  In the toolbar we look up the URL bar
and then determine the screen coordinates of the scaffolding
via the top level widget GTK semantics.

Finally we place the page-info window 4 pixels down and right of
the bottom-left of the entry box, which should look nice.

Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
2020-05-08 20:46:37 +01:00

3826 lines
90 KiB
C

/*
* Copyright 2019 Vincent Sanders <vince@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
* NetSurf is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* NetSurf is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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 "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"
#include "gtk/throbber.h"
#include "gtk/scaffolding.h"
#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"
/**
* button location indicating button is not to be shown
*/
#define INACTIVE_LOCATION (-1)
/**
* time (in ms) between throbber animation frame updates
*/
#define THROBBER_FRAME_TIME (100)
/**
* 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
*/
#define NSGTK_WEBSEARCH_WIDTH 150
/**
* 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;
/**
* 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;
};
/**
* 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
*
* \param s The string to change.
* \param replacespace true to insert a space where there was an underscore
* \return The altered string
*/
static char *remove_underscores(const char *s, bool replacespace)
{
size_t i, ii, len;
char *ret;
len = strlen(s);
ret = malloc(len + 1);
if (ret == NULL) {
return NULL;
}
for (i = 0, ii = 0; i < len; i++) {
if (s[i] != '_') {
ret[ii++] = s[i];
} else if (replacespace) {
ret[ii++] = ' ';
}
}
ret[ii] = '\0';
return ret;
}
/**
* 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;
res = nsgtk_throbber_get_frame(0, &pixbuf);
if (res != NSERROR_OK) {
return NULL;
}
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);
}
gtk_widget_set_sensitive(GTK_WIDGET(item), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(entry), sensitivity);
return item;
}
/**
* 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 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;
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)
{
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"
#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 URL_BAR_ITEM:
toolitem = make_toolbar_item_url_bar(false, true);
break;
case THROBBER_ITEM:
toolitem = make_toolbar_item_throbber(true, true);
break;
case WEBSEARCH_ITEM:
toolitem = make_toolbar_item_websearch(false, true);
break;
default:
break;
}
return toolitem;
}
/**
* target entry for drag source
*/
static GtkTargetEntry target_entry = {
(char *)"nsgtk_button_data",
GTK_TARGET_SAME_APP,
0
};
/**
* 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;
}
/**
* 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++) {
itemid = itemid_from_location(tb, location);
if (itemid == PLACEHOLDER_BUTTON) {
/* no more filled locations */
break;
}
start += snprintf(start,
orderlen - (start - order),
"%s/",
tb->items[itemid].name);
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;
}
/**
* connect signals to a toolbar item in a customisation toolbar
*
* \param tb The toolbar
* \param itemid The item id within to toolbar to connect
* \param NSERROR_OK on success
*/
static nserror
toolbar_item_connect_signals(struct nsgtk_toolbar *tb, int itemid)
{
/* 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;
}
/**
* 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;
}
if (tbc->toolbar.items[tbc->dragitem].location == INACTIVE_LOCATION) {
tbc->dragitem = -1;
gtk_drag_finish(gdc, TRUE, TRUE, time);
return FALSE;
}
/* 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--;
}
/* 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;
}
/**
* 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)
{
return FALSE;
}
/**
* 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;
}
/* pure conveiance variable */
dragitem = &tbc->toolbar.items[tbc->dragitem];
/* deal with replacing existing item in toolbar */
if (dragitem->location != INACTIVE_LOCATION) {
if (dragitem->location < position) {
position--;
}
/* 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--;
}
/* 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++;
}
}
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;
}
tbitems = nsoption_charp(toolbar_items);
if (tbitems == NULL) {
tbitems = "";
}
end = tbitems;
while (*end != 0) {
start = end;
while ((*end != 0) && (*end !='/')) {
end++;
}
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 (*end == '/') {
end++;
}
}
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 remove a widget from a container
*/
static void container_remove_widget(GtkWidget *widget, gpointer data)
{
GtkContainer *container = GTK_CONTAINER(data);
gtk_container_remove(container, widget);
}
/**
* populates a toolbar with widgets in correct order
*
* \param tb toolbar
* \return NSERROR_OK on success else error code.
*/
static nserror populate_gtk_toolbar_widget(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_toolbar_item(itemid,
tb->items[itemid].sensitivity);
gtk_toolbar_insert(tb->widget,
tb->items[itemid].button,
location);
}
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;
}
/**
* find the toolbar item with a given gtk widget.
*
* \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
itemid_from_gtktoolitem(struct nsgtk_toolbar *tb, GtkToolItem *toolitem)
{
int iidx;
for (iidx = BACK_BUTTON; iidx < PLACEHOLDER_BUTTON; iidx++) {
if ((tb->items[iidx].location != INACTIVE_LOCATION) &&
(tb->items[iidx].button == toolitem)) {
break;
}
}
return iidx;
}
/**
* set a toolbar items sensitivity
*
* note this does not set menu items sensitivity
*/
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);
}
}
return NSERROR_OK;
}
/**
* 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 (tb->items[itemid].button == NULL) {
return NSERROR_INVALID;
}
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_tool_button_set_label(GTK_TOOL_BUTTON(tb->items[itemid].button),
label);
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 ((!folder) || (access(path, F_OK) != 0)) {
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), path);
}
free(path);
/* confirm overwriting */
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc), TRUE);
/* 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;
}
*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);
}
}
/* 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);
}
/**
* 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;
rowbar = GTK_TOOLBAR(gtk_toolbar_new());
if (rowbar == NULL) {
return NSERROR_NOMEM;
}
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;
}
/**
* 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;
}
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);
}
return NSERROR_OK;
}
/**
* 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;
}
/* attach handlers to toolbar widgets */
res = toolbar_customisation_connect_signals(&tbc->toolbar);
if (res != NSERROR_OK) {
return res;
}
return NSERROR_OK;
}
/**
* 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
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;
}
/* create builder */
res = nsgtk_builder_new_from_resname("toolbar", &builder);
if (res != NSERROR_OK) {
NSLOG(netsurf, INFO, "Toolbar UI builder init failed");
return TRUE;
}
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;
}
/* 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;
}
/* 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;
}
/* 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;
}
/* build customisation toolbar */
gtk_toolbar_set_show_arrow(tbc->toolbar.widget, TRUE);
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), &notebook_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);
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);
}
/**
* 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
back_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_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;
}
/**
* 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
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;
}
/**
* 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 gboolean
stop_button_clicked_cb(GtkWidget *widget, gpointer data)
{
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));
}
/**
* callback for url entry widget icon button release
*
* handler connected to url entry widget for the icon release 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 void
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;
}
/**
* 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 gboolean
websearch_entry_button_press_cb(GtkWidget *widget,
GdkEventFocus *f,
gpointer data)
{
gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
gtk_widget_grab_focus(GTK_WIDGET(widget));
return TRUE;
}
/**
* 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);
}
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;
}
/**
* 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 gboolean
savepage_button_clicked_cb(GtkWidget *widget, gpointer data)
{
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);
}
g_free(path);
return TRUE;
}
closedir(d);
save_complete(browser_window_get_content(bw), path, NULL);
g_free(path);
return TRUE;
}
/**
* 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 gboolean
pdf_button_clicked_cb(GtkWidget *widget, gpointer data)
{
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);
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;
}
#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;
}
/**
* 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
*/
static gboolean
plaintext_button_clicked_cb(GtkWidget *widget, gpointer data)
{
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);
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;
}
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;
}
/* 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);
}
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);
}
return TRUE;
}
/**
* 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 gboolean
copy_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_copy_clipboard(GTK_EDITABLE(focused));
} else {
bw = tb->get_bw(tb->get_ctx);
browser_window_key_press(bw, NS_KEY_COPY_SELECTION);
}
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);
}
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);
netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
if (choices != NULL) {
nsoption_write(choices, NULL, NULL);
free(choices);
}
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
toggledebugging_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_debug(bw, CONTENT_DEBUG_REDRAW);
nsgtk_window_update_all();
return TRUE;
}
/**
* 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
debugboxtree_button_clicked_cb(GtkWidget *widget, gpointer data)
{
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;
}
/**
* 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
debugdomtree_button_clicked_cb(GtkWidget *widget, gpointer data)
{
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;
}
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;
}
/**
* 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
localhistory_button_clicked_cb(GtkWidget *widget, gpointer data)
{
nserror res;
struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
struct browser_window *bw;
GtkWidget *toplevel;
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.");
}
}
return TRUE;
}
/**
* 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
history_button_clicked_cb(GtkWidget *widget, gpointer data)
{
return localhistory_button_clicked_cb(widget, data);
}
/**
* 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 gboolean
globalhistory_button_clicked_cb(GtkWidget *widget, gpointer data)
{
nserror res;
res = nsgtk_global_history_present();
if (res != NSERROR_OK) {
NSLOG(netsurf, INFO,
"Unable to initialise global history window.");
}
return TRUE;
}
/**
* 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;
}
/**
* 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, "Unable to initialise bookmark window.");
}
return TRUE;
}
/**
* 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();
if (res != NSERROR_OK) {
NSLOG(netsurf, INFO, "Unable to initialise cookies window.");
}
return TRUE;
}
/**
* 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;
}
/**
* 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;
res = toolbar_navigate_to_url(tb, "http://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, "http://www.netsurf-browser.org/documentation/guide");
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
info_button_clicked_cb(GtkWidget *widget, gpointer data)
{
struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
nserror res;
res = toolbar_navigate_to_url(tb, "http://www.netsurf-browser.org/documentation/info");
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 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 */
gs = nsgtk_get_scaffold(gw);
nsgtk_scaffolding_burger_menu(gs);
return TRUE;
}
/* 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
/**
* create a toolbar item
*
* create a toolbar item and set up its default handlers
*/
static nserror
toolbar_item_create(nsgtk_toolbar_button id, struct nsgtk_toolbar_item *item)
{
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;
#include "gtk/toolbar_items.h"
#undef TOOLBAR_ITEM_t
#undef TOOLBAR_ITEM_y
#undef TOOLBAR_ITEM_n
#undef TOOLBAR_ITEM
case PLACEHOLDER_BUTTON:
return NSERROR_INVALID;
}
return NSERROR_OK;
}
/**
* 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);
}
}
/**
* connect signal handlers to a gtk toolbar item
*/
static nserror
toolbar_connect_signal(struct nsgtk_toolbar *tb, nsgtk_toolbar_button itemid)
{
struct nsgtk_toolbar_item *item;
GtkEntry *entry;
item = &tb->items[itemid];
if (item->button != NULL) {
g_signal_connect(item->button,
"size-allocate",
G_CALLBACK(toolbar_item_size_allocate_cb),
tb);
}
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;
}
return NSERROR_OK;
}
/**
* connect all signals to widgets in a toolbar
*/
static nserror toolbar_connect_signals(struct nsgtk_toolbar *tb)
{
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 NSERROR_OK;
}
/**
* 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 gboolean
toolbar_popup_context_menu_cb(GtkToolbar *toolbar,
gint x,
gint y,
gint button,
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 */
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 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 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:
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;
}
/* exported interface documented in toolbar.h */
nserror nsgtk_toolbar_position_page_info(struct nsgtk_toolbar *tb,
struct nsgtk_pi_window *win)
{
struct nsgtk_toolbar_item *item = &tb->items[URL_BAR_ITEM];
GtkWidget *widget = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(item->button)));
gint rootx, rooty, x, y;
if (gtk_widget_translate_coordinates(widget,
gtk_widget_get_toplevel(widget),
0,
gtk_widget_get_allocated_height(widget) - 1,
&x, &y) != TRUE) {
return NSERROR_UNKNOWN;
}
gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
&rootx, &rooty);
nsgtk_page_info_set_position(win, rootx + x + 4, rooty + y + 4);
return NSERROR_OK;
}