netsurf/frontends/gtk/toolbar.c
2019-09-21 10:53:41 +01:00

3743 lines
89 KiB
C

/*
* Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
*
* 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
* implementatio 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/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)
/**
* toolbar item context
*/
struct nsgtk_toolbar_item {
GtkToolItem *button;
int location; /* in toolbar */
bool sensitivity;
/**
* button clicked handler
*/
gboolean (*bhandler)(GtkWidget *widget, gpointer data);
void *dataplus; /* customisation -> toolbar */
void *dataminus; /* customisation -> store */
};
/**
* 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 *buttons[PLACEHOLDER_BUTTON];
/** entry widget holding the url of the current displayed page */
GtkWidget *url_bar;
/** 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;
};
/**
* 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 */
};
static bool edit_mode = false;
/* the number of buttons that fit in the width of the store window */
#define NSGTK_STORE_WIDTH 6
/* 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
enum image_sets {
IMAGE_SET_MAIN_MENU = 0,
IMAGE_SET_RCLICK_MENU,
IMAGE_SET_POPUP_MENU,
IMAGE_SET_BUTTONS,
IMAGE_SET_COUNT
};
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];
};
/* forward declaration */
int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, struct nsgtk_scaffolding *g);
static nserror toolbar_item_create(nsgtk_toolbar_button id, struct nsgtk_toolbar_item **item_out);
/* define data plus and data minus handlers */
#define TOOLBAR_ITEM(identifier, name, sensitivity, clicked, activate) \
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
/**
* 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");
BUTTON_IMAGE(OPENMENU, NSGTK_STOCK_OPEN_MENU);
#undef BUTTON_IMAGE
case HISTORY_BUTTON:
image = GTK_IMAGE(gtk_image_new_from_pixbuf(arrow_down_pixbuf));
break;
default:
image = NULL;
break;
}
if (usedef && (image == NULL)) {
image = GTK_IMAGE(nsgtk_image_new_from_stock("gtk-missing-image", iconsize));
}
return image;
}
/**
* Get default image for search buttons / menu items from gtk stock items
*
* \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.
*/
static GtkImage *
nsgtk_theme_searchimage_default(nsgtk_search_buttons tbbutton,
GtkIconSize iconsize,
bool usedef)
{
GtkImage *image;
switch (tbbutton) {
case (SEARCH_BACK_BUTTON):
image = GTK_IMAGE(nsgtk_image_new_from_stock(
NSGTK_STOCK_GO_BACK, iconsize));
break;
case (SEARCH_FORWARD_BUTTON):
image = GTK_IMAGE(nsgtk_image_new_from_stock(
NSGTK_STOCK_GO_FORWARD, iconsize));
break;
case (SEARCH_CLOSE_BUTTON):
image = GTK_IMAGE(nsgtk_image_new_from_stock(
NSGTK_STOCK_CLOSE, iconsize));
break;
default:
image = NULL;
}
if (usedef && (image == NULL)) {
image = GTK_IMAGE(nsgtk_image_new_from_stock(
"gtk-missing-image", iconsize));
}
return image;
}
/**
* initialise a theme structure with gtk images
*
* \param iconsize The size of icon to load
* \param usedef use the default gtk icon if unset
*/
static struct nsgtk_theme *nsgtk_theme_load(GtkIconSize iconsize, bool usedef)
{
struct nsgtk_theme *theme;
int btnloop;
theme = malloc(sizeof(struct nsgtk_theme));
if (theme == NULL) {
return NULL;
}
for (btnloop = BACK_BUTTON;
btnloop < PLACEHOLDER_BUTTON ;
btnloop++) {
theme->image[btnloop] = nsgtk_theme_image_default(btnloop,
iconsize,
usedef);
}
for (btnloop = SEARCH_BACK_BUTTON;
btnloop < SEARCH_BUTTONS_COUNT;
btnloop++) {
theme->searchimage[btnloop] =
nsgtk_theme_searchimage_default(btnloop,
iconsize,
usedef);
}
return theme;
}
static struct nsgtk_toolbar_item *
nsgtk_scaffolding_button(struct nsgtk_scaffolding *g, int i)
{
return NULL;
}
/* exported function documented in gtk/toolbar.h */
void nsgtk_theme_implement(struct nsgtk_scaffolding *g)
{
struct nsgtk_theme *theme[IMAGE_SET_COUNT];
int i;
struct nsgtk_toolbar_item *button;
struct gtk_search *search;
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;
button = nsgtk_scaffolding_button(g, i);
if (button == NULL)
continue;
#if 0
/* 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));
}
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));
}
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));
}
#endif
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));
}
}
/* 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]));
}
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]));
}
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]));
}
}
for (i = 0; i < IMAGE_SET_COUNT; i++) {
if (theme[i] != NULL) {
free(theme[i]);
}
}
}
/**
* 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)
{
nserror res;
GtkToolItem *item;
GdkPixbuf *pixbuf;
GtkWidget *image;
res = nsgtk_throbber_get_frame(0, &pixbuf);
if (res != NSERROR_OK) {
return NULL;
}
if (edit_mode) {
item = gtk_tool_button_new(
GTK_WIDGET(gtk_image_new_from_pixbuf(pixbuf)),
"[throbber]");
} 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
*/
static GtkToolItem *
make_toolbar_item_url_bar(bool sensitivity)
{
GtkToolItem *item;
GtkWidget *entry;
GtkEntryCompletion *completion;
item = gtk_tool_item_new();
entry = nsgtk_entry_new();
completion = gtk_entry_completion_new();
if ((entry == NULL) || (completion == NULL) || (item == NULL)) {
return NULL;
}
gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity);
gtk_container_add(GTK_CONTAINER(item), entry);
gtk_tool_item_set_expand(item, TRUE);
if (edit_mode) {
gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE);
} else {
gtk_entry_set_completion(GTK_ENTRY(entry), completion);
}
return item;
}
/**
* create web search toolbar item widget
*/
static GtkToolItem *
make_toolbar_item_websearch(bool sensitivity)
{
GtkToolItem *item;
nserror res;
GtkWidget *entry;
struct bitmap *bitmap;
GdkPixbuf *pixbuf = NULL;
entry = nsgtk_entry_new();
item = gtk_tool_item_new();
if ((entry == NULL) || (item == NULL)) {
return NULL;
}
gtk_widget_set_sensitive(GTK_WIDGET(item), sensitivity);
gtk_widget_set_size_request(entry, NSGTK_WEBSEARCH_WIDTH, -1);
res = search_web_get_provider_bitmap(&bitmap);
if ((res == NSERROR_OK) && (bitmap != NULL)) {
pixbuf = nsgdk_pixbuf_get_from_surface(bitmap->surface,
16, 16);
}
if (pixbuf != NULL) {
nsgtk_entry_set_icon_from_pixbuf(entry,
GTK_ENTRY_ICON_PRIMARY,
pixbuf);
} else {
nsgtk_entry_set_icon_from_stock(entry,
GTK_ENTRY_ICON_PRIMARY,
NSGTK_STOCK_INFO);
}
gtk_container_add(GTK_CONTAINER(item), entry);
if (edit_mode) {
gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE);
}
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,
struct nsgtk_theme *theme,
bool sensitivity)
{
GtkToolItem *w = NULL;
switch(itemid) {
/* gtk_tool_button_new accepts NULL args */
#define MAKE_ITEM(p, q) \
case p##_BUTTON: { \
char *label = NULL; \
label = remove_underscores(messages_get(#q), false); \
w = gtk_tool_button_new(GTK_WIDGET(theme->image[p##_BUTTON]), \
label); \
gtk_widget_set_sensitive(GTK_WIDGET(w), sensitivity); \
if (label != NULL) { \
free(label); \
} \
break; \
}
MAKE_ITEM(HOME, gtkHome)
MAKE_ITEM(BACK, gtkBack)
MAKE_ITEM(FORWARD, gtkForward)
MAKE_ITEM(STOP, Stop)
MAKE_ITEM(RELOAD, Reload)
MAKE_ITEM(NEWWINDOW, gtkNewWindow)
MAKE_ITEM(NEWTAB, gtkNewTab)
MAKE_ITEM(OPENFILE, gtkOpenFile)
MAKE_ITEM(CLOSETAB, gtkCloseTab)
MAKE_ITEM(CLOSEWINDOW, gtkCloseWindow)
MAKE_ITEM(SAVEPAGE, gtkSavePage)
MAKE_ITEM(PRINTPREVIEW, gtkPrintPreview)
MAKE_ITEM(PRINT, gtkPrint)
MAKE_ITEM(QUIT, gtkQuitMenu)
MAKE_ITEM(CUT, gtkCut)
MAKE_ITEM(COPY, gtkCopy)
MAKE_ITEM(PASTE, gtkPaste)
MAKE_ITEM(DELETE, gtkDelete)
MAKE_ITEM(SELECTALL, gtkSelectAll)
MAKE_ITEM(PREFERENCES, gtkPreferences)
MAKE_ITEM(ZOOMPLUS, gtkZoomPlus)
MAKE_ITEM(ZOOMMINUS, gtkZoomMinus)
MAKE_ITEM(ZOOMNORMAL, gtkZoomNormal)
MAKE_ITEM(FULLSCREEN, gtkFullScreen)
MAKE_ITEM(VIEWSOURCE, gtkViewSource)
MAKE_ITEM(CONTENTS, gtkContents)
MAKE_ITEM(ABOUT, gtkAbout)
MAKE_ITEM(PDF, gtkPDF)
MAKE_ITEM(PLAINTEXT, gtkPlainText)
MAKE_ITEM(DRAWFILE, gtkDrawFile)
MAKE_ITEM(POSTSCRIPT, gtkPostScript)
MAKE_ITEM(FIND, gtkFind)
MAKE_ITEM(DOWNLOADS, gtkDownloads)
MAKE_ITEM(SAVEWINDOWSIZE, gtkSaveWindowSize)
MAKE_ITEM(TOGGLEDEBUGGING, gtkToggleDebugging)
MAKE_ITEM(SAVEBOXTREE, gtkDebugBoxTree)
MAKE_ITEM(SAVEDOMTREE, gtkDebugDomTree)
MAKE_ITEM(LOCALHISTORY, gtkLocalHistory)
MAKE_ITEM(GLOBALHISTORY, gtkGlobalHistory)
MAKE_ITEM(ADDBOOKMARKS, gtkAddBookMarks)
MAKE_ITEM(SHOWBOOKMARKS, gtkShowBookMarks)
MAKE_ITEM(SHOWCOOKIES, gtkShowCookies)
MAKE_ITEM(OPENLOCATION, gtkOpenLocation)
MAKE_ITEM(NEXTTAB, gtkNextTab)
MAKE_ITEM(PREVTAB, gtkPrevTab)
MAKE_ITEM(GUIDE, gtkGuide)
MAKE_ITEM(INFO, gtkUserInformation)
MAKE_ITEM(OPENMENU, gtkOpenMenu)
#undef MAKE_ITEM
case HISTORY_BUTTON:
w = gtk_tool_button_new(GTK_WIDGET(
theme->image[HISTORY_BUTTON]), "H");
/* set history widget minimum width */
gtk_widget_set_size_request(GTK_WIDGET(w), 20, -1);
gtk_widget_set_sensitive(GTK_WIDGET(w), sensitivity);
break;
case URL_BAR_ITEM:
w = make_toolbar_item_url_bar(sensitivity);
break;
case THROBBER_ITEM:
w = make_toolbar_item_throbber(sensitivity);
break;
case WEBSEARCH_ITEM:
w = make_toolbar_item_websearch(sensitivity);
break;
default:
break;
}
return w;
}
/**
* target entry for drag source
*/
static GtkTargetEntry target_entry = {
(char *)"nsgtk_button_data",
GTK_TARGET_SAME_APP,
0
};
/**
* save toolbar settings to file
*/
static nserror
nsgtk_toolbar_customisation_save(struct nsgtk_toolbar_customisation *tbc)
{
char *choices = NULL;
char *order;
int order_len;
int tbidx;
char *cur;
int plen;
order_len = PLACEHOLDER_BUTTON * 12; /* length of order buffer */
order = malloc(order_len);
if (order == NULL) {
return NSERROR_NOMEM;
}
cur = order;
for (tbidx = BACK_BUTTON; tbidx < PLACEHOLDER_BUTTON; tbidx++) {
plen = snprintf(cur,
order_len,
"%d;%d|",
tbidx,
tbc->toolbar.buttons[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;
}
nsoption_set_charp(toolbar_order, 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;
}
/**
* 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->buttons[iidx]->location == location) {
break;
}
}
return iidx;
}
/**
* 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->buttons[itemid]->button, TRUE);
gtk_drag_source_set(GTK_WIDGET(tb->buttons[itemid]->button),
GDK_BUTTON1_MASK,
&target_entry,
1,
GDK_ACTION_COPY);
g_signal_connect(tb->buttons[itemid]->button,
"drag-data-get",
G_CALLBACK(tb->buttons[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.buttons[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.buttons[tbc->dragitem]->location;
location < PLACEHOLDER_BUTTON;
location++) {
itemid = itemid_from_location(&tbc->toolbar, location);
if (itemid == PLACEHOLDER_BUTTON) {
break;
}
tbc->toolbar.buttons[itemid]->location--;
}
/* remove existing item */
tbc->toolbar.buttons[tbc->dragitem]->location = -1;
gtk_container_remove(GTK_CONTAINER(tbc->toolbar.widget),
GTK_WIDGET(tbc->toolbar.buttons[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 */
struct nsgtk_theme *theme;
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.buttons[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.buttons[itemid]->location--;
}
/* remove existing item */
dragitem->location = INACTIVE_LOCATION;
gtk_container_remove(GTK_CONTAINER(tbc->toolbar.widget),
GTK_WIDGET(dragitem->button));
}
/* add dropped item into toolbar */
theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, false);
if (theme == NULL) {
nsgtk_warning(messages_get("NoMemory"), 0);
return TRUE;
}
edit_mode = true;
dragitem->button = make_toolbar_item(tbc->dragitem, theme, true);
edit_mode = false;
free(theme);
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.buttons[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;
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;
}
/**
* \return toolbar item id when a widget is an element of the scaffolding
* else -1
*/
int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget,
struct nsgtk_scaffolding *g)
{
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;
}
}
return -1;
}
/**
* 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)
{
int i, ii;
char *buffer;
char *buffer1, *subbuffer, *ptr = NULL, *pter = NULL;
/* set all button locations to inactive */
for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
tb->buttons[i]->location = INACTIVE_LOCATION;
}
/* if no user config is present apply the defaults */
if (nsoption_charp(toolbar_order) == NULL) {
tb->buttons[BACK_BUTTON]->location = 0;
tb->buttons[HISTORY_BUTTON]->location = 1;
tb->buttons[FORWARD_BUTTON]->location = 2;
tb->buttons[STOP_BUTTON]->location = 3;
tb->buttons[RELOAD_BUTTON]->location = 4;
tb->buttons[URL_BAR_ITEM]->location = 5;
tb->buttons[WEBSEARCH_ITEM]->location = 6;
tb->buttons[THROBBER_ITEM]->location = 7;
return NSERROR_OK;
}
buffer = strdup(nsoption_charp(toolbar_order));
if (buffer == NULL) {
return NSERROR_NOMEM;
}
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)) {
tb->buttons[i]->location = ii;
}
}
}
buffer1 = strtok_r(NULL, "|", &ptr);
}
free(buffer);
return NSERROR_OK;
}
/**
* append item to gtk toolbar container
*
* \param tb toolbar
* \param theme in use
* \param location item location being appended
* \return NSERROR_OK on success else error code.
*/
static nserror
add_item_to_toolbar(struct nsgtk_toolbar *tb,
struct nsgtk_theme *theme,
int location)
{
int bidx; /* button index */
for (bidx = BACK_BUTTON; bidx < PLACEHOLDER_BUTTON; bidx++) {
if (tb->buttons[bidx]->location == location) {
tb->buttons[bidx]->button = make_toolbar_item(
bidx, theme, tb->buttons[bidx]->sensitivity);
gtk_toolbar_insert(tb->widget,
tb->buttons[bidx]->button,
location);
break;
}
}
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 the gtk toolbar container with widgets in correct order
*/
static nserror populate_gtk_toolbar_widget(struct nsgtk_toolbar *tb)
{
struct nsgtk_theme *theme; /* internal theme context */
int lidx; /* location index */
theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, false);
if (theme == NULL) {
return NSERROR_NOMEM;
}
/* clear the toolbar container of all widgets */
gtk_container_foreach(GTK_CONTAINER(tb->widget),
container_remove_widget,
tb->widget);
/* add widgets to toolbar */
for (lidx = 0; lidx < PLACEHOLDER_BUTTON; lidx++) {
add_item_to_toolbar(tb, theme, lidx);
}
gtk_widget_show_all(GTK_WIDGET(tb->widget));
free(theme);
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->buttons[iidx]->location != INACTIVE_LOCATION) &&
(tb->buttons[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;
}
/**
* 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->buttons[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;
}
//NSLOG(netsurf, ERROR, "w:%d h:%d", alloc->width, alloc->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.buttons[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], iidx - startitem);
}
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 */
struct nsgtk_theme *theme;
theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR, true);
if (theme == NULL) {
return NSERROR_NOMEM;
}
/* ensure there are a minimum number of items per row */
columns = width / NSGTK_BUTTON_WIDTH;
if (columns < NSGTK_STORE_WIDTH) {
columns = NSGTK_STORE_WIDTH;
}
edit_mode = true;
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_toolbar_item(iidx, theme, true);
if (tbc->items[iidx] != NULL) {
curcol++;
}
}
if (curcol > 0) {
add_toolbox_row(tbc, startidx, iidx);
}
edit_mode = false;
free(theme);
return NSERROR_OK;
}
/**
* update toolbar in customisation to user settings
*/
static nserror
customisation_toolbar_update(struct nsgtk_toolbar_customisation *tbc)
{
GtkEntry *entry;
nserror res;
res = apply_user_button_customisation(&tbc->toolbar);
if (res != NSERROR_OK) {
return res;
}
/* populate toolbar widget */
res = populate_gtk_toolbar_widget(&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;
}
if (tbc->toolbar.buttons[URL_BAR_ITEM]->location != INACTIVE_LOCATION) {
entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tbc->toolbar.buttons[URL_BAR_ITEM]->button)));
gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE);
}
if (tbc->toolbar.buttons[WEBSEARCH_ITEM]->location != INACTIVE_LOCATION) {
entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tbc->toolbar.buttons[WEBSEARCH_ITEM]->button)));
gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE);
}
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);
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;
}
/*
* 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.buttons[iidx]);
if (res != NSERROR_OK) {
for (iidx-- ; iidx >= BACK_BUTTON; iidx--) {
free(tbc->toolbar.buttons[iidx]);
}
goto cutomize_button_clicked_cb_error;
}
tbc->toolbar.buttons[iidx]->sensitivity = true;
}
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 destroy signal */
#if 0
g_signal_connect(tbc->container,
"delete-event",
G_CALLBACK(nsgtk_toolbar_delete),
g);
#endif
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->buttons[itemid]->location < tb->buttons[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->buttons[itemid]->location <= tb->buttons[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->buttons[BACK_BUTTON],
browser_window_history_back_available(bw));
set_item_sensitivity(tb->buttons[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->buttons[BACK_BUTTON],
browser_window_history_back_available(bw));
set_item_sensitivity(tb->buttons[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 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));
}
/**
* 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) {
temp_open_background = 0;
bw = tb->get_bw(tb->get_ctx);
res = browser_window_create(
BW_CREATE_HISTORY | BW_CREATE_TAB,
url,
NULL,
bw,
NULL);
temp_open_background = -1;
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->buttons[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;
}
/**
* 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_out)
{
struct nsgtk_toolbar_item *item;
item = calloc(1, sizeof(struct nsgtk_toolbar_item));
if (item == NULL) {
return NSERROR_NOMEM;
}
item->location = INACTIVE_LOCATION;
/* set item defaults from macro */
switch (id) {
#define TOOLBAR_ITEM_y(name) \
item->bhandler = name##_button_clicked_cb;
#define TOOLBAR_ITEM_n(name) \
item->bhandler = NULL;
#define TOOLBAR_ITEM(identifier, name, snstvty, clicked, activate) \
case identifier: \
item->sensitivity = snstvty; \
item->dataplus = nsgtk_toolbar_##name##_data_plus; \
item->dataminus = nsgtk_toolbar_##name##_data_minus; \
TOOLBAR_ITEM_ ## clicked(name) \
break;
#include "gtk/toolbar_items.h"
#undef TOOLBAR_ITEM_y
#undef TOOLBAR_ITEM_n
#undef TOOLBAR_ITEM
case PLACEHOLDER_BUTTON:
free(item);
return NSERROR_INVALID;
}
*item_out = item;
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->buttons[THROBBER_ITEM]->button,
tb->throb_frame);
if (res == NSERROR_BAD_SIZE) {
tb->throb_frame = 1;
res = set_throbber_frame(tb->buttons[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->buttons[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);
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->bhandler != NULL) && (item->button != NULL)) {
g_signal_connect(item->button,
"clicked",
G_CALLBACK(item->bhandler),
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;
}
/* exported interface documented in toolbar.h */
nserror
nsgtk_toolbar_create(GtkBuilder *builder,
struct browser_window *(*get_bw)(void *ctx),
void *get_ctx,
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;
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);
/* allocate button contexts */
for (bidx = BACK_BUTTON; bidx < PLACEHOLDER_BUTTON; bidx++) {
res = toolbar_item_create(bidx, &tb->buttons[bidx]);
if (res != NSERROR_OK) {
for (bidx-- ; bidx >= BACK_BUTTON; bidx--) {
free(tb->buttons[bidx]);
}
free(tb);
return res;
}
}
res = nsgtk_toolbar_update(tb);
if (res != NSERROR_OK) {
free(tb);
return res;
}
*tb_out = tb;
return NSERROR_OK;
}
/* exported interface documented in toolbar.h */
nserror nsgtk_toolbar_destroy(struct nsgtk_toolbar *tb)
{
/** \todo free buttons and destroy toolbar container (and widgets) */
free(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;
bw = tb->get_bw(tb->get_ctx);
/* 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->buttons[STOP_BUTTON], true);
set_item_sensitivity(tb->buttons[RELOAD_BUTTON], false);
return NSERROR_OK;
}
/* stopping the throbber */
nsgtk_schedule(-1, next_throbber_frame, tb);
tb->throb_frame = 0;
res = set_throbber_frame(tb->buttons[THROBBER_ITEM]->button,
tb->throb_frame);
/* adjust sensitivity of other items */
set_item_sensitivity(tb->buttons[STOP_BUTTON], false);
set_item_sensitivity(tb->buttons[RELOAD_BUTTON], true);
set_item_sensitivity(tb->buttons[BACK_BUTTON],
browser_window_history_back_available(bw));
set_item_sensitivity(tb->buttons[FORWARD_BUTTON],
browser_window_history_forward_available(bw));
nsgtk_local_history_hide();
return res;
}
/* 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->buttons[URL_BAR_ITEM]->button == NULL) {
/* no toolbar item */
return NSERROR_INVALID;
}
url_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tb->buttons[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);
}
gtk_entry_set_text(url_entry, url_text);
//gtk_editable_set_position(GTK_EDITABLE(url_entry), -1);
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->buttons[WEBSEARCH_ITEM]->button == NULL) {
/* no toolbar item */
return NSERROR_INVALID;
}
entry = gtk_bin_get_child(GTK_BIN(tb->buttons[WEBSEARCH_ITEM]->button));
if (pixbuf != NULL) {
nsgtk_entry_set_icon_from_pixbuf(entry,
GTK_ENTRY_ICON_PRIMARY,
pixbuf);
} else {
nsgtk_entry_set_icon_from_stock(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->buttons[itemid]->bhandler == 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->buttons[itemid]->button != NULL) {
widget = GTK_WIDGET(tb->buttons[itemid]->button);
} else {
widget = GTK_WIDGET(tb->widget);
}
tb->buttons[itemid]->bhandler(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;
}