mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-11-26 00:09:41 +03:00
3881 lines
91 KiB
C
3881 lines
91 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), ¬ebook_alloc);
|
|
|
|
res = toolbar_customisation_create_toolbox(tbc, notebook_alloc.width);
|
|
if (res != NSERROR_OK) {
|
|
goto cutomize_button_clicked_cb_error;
|
|
}
|
|
|
|
/* configure the container */
|
|
gtk_drag_dest_set(GTK_WIDGET(tbc->container),
|
|
GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
|
|
&target_entry,
|
|
1,
|
|
GDK_ACTION_COPY);
|
|
|
|
/* discard button calls destroy */
|
|
g_signal_connect_swapped(GTK_WIDGET(gtk_builder_get_object(builder,
|
|
"discard")),
|
|
"clicked",
|
|
G_CALLBACK(gtk_widget_destroy),
|
|
tbc->container);
|
|
|
|
/* save and update on apply button */
|
|
g_signal_connect(GTK_WIDGET(gtk_builder_get_object(builder, "apply")),
|
|
"clicked",
|
|
G_CALLBACK(customisation_apply_clicked_cb),
|
|
tbc);
|
|
|
|
g_signal_connect(GTK_WIDGET(gtk_builder_get_object(builder, "reset")),
|
|
"clicked",
|
|
G_CALLBACK(customisation_reset_clicked_cb),
|
|
tbc);
|
|
|
|
/* close and cleanup on delete signal */
|
|
g_signal_connect(tbc->container,
|
|
"destroy",
|
|
G_CALLBACK(customisation_container_destroy_cb),
|
|
tbc);
|
|
|
|
|
|
g_signal_connect(tbc->container,
|
|
"drag-drop",
|
|
G_CALLBACK(customisation_container_drag_drop_cb),
|
|
tbc);
|
|
|
|
g_signal_connect(tbc->container,
|
|
"drag-motion",
|
|
G_CALLBACK(customisation_container_drag_motion_cb),
|
|
tbc);
|
|
|
|
|
|
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(NULL);
|
|
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, "https://www.netsurf-browser.org/documentation/");
|
|
if (res != NSERROR_OK) {
|
|
nsgtk_warning(messages_get_errorcode(res), 0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* handler for contents tool bar item clicked signal
|
|
*
|
|
* \param widget The widget the signal is being delivered to.
|
|
* \param data The toolbar context passed when the signal was connected
|
|
* \return TRUE
|
|
*/
|
|
static gboolean
|
|
guide_button_clicked_cb(GtkWidget *widget, gpointer data)
|
|
{
|
|
struct nsgtk_toolbar *tb = (struct nsgtk_toolbar *)data;
|
|
nserror res;
|
|
|
|
res = toolbar_navigate_to_url(tb, "https://www.netsurf-browser.org/documentation/guide");
|
|
if (res != NSERROR_OK) {
|
|
nsgtk_warning(messages_get_errorcode(res), 0);
|
|
}
|
|
|
|
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, "https://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;
|
|
}
|
|
|
|
/**
|
|
* Find the correct location for popping up a window for the chosen item.
|
|
*
|
|
* \param tb The toolbar to select from
|
|
* \param item_idx The toolbar item to select from
|
|
* \param out_x Filled with an appropriate X coordinate
|
|
* \param out_y Filled with an appropriate Y coordinate
|
|
*/
|
|
static nserror
|
|
nsgtk_toolbar_get_icon_window_position(struct nsgtk_toolbar *tb,
|
|
int item_idx,
|
|
int *out_x,
|
|
int *out_y)
|
|
{
|
|
struct nsgtk_toolbar_item *item = &tb->items[item_idx];
|
|
GtkWidget *widget = GTK_WIDGET(item->button);
|
|
GtkAllocation alloc;
|
|
gint rootx, rooty, x, y;
|
|
|
|
switch (item_idx) {
|
|
case URL_BAR_ITEM:
|
|
widget = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(item->button)));
|
|
break;
|
|
default:
|
|
/* Nothing to do here */
|
|
break;
|
|
}
|
|
|
|
nsgtk_widget_get_allocation(widget, &alloc);
|
|
|
|
if (gtk_widget_translate_coordinates(widget,
|
|
gtk_widget_get_toplevel(widget),
|
|
0,
|
|
alloc.height - 1,
|
|
&x, &y) != TRUE) {
|
|
return NSERROR_UNKNOWN;
|
|
}
|
|
|
|
gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
|
|
&rootx, &rooty);
|
|
|
|
*out_x = rootx + x + 4;
|
|
*out_y = rooty + y + 4;
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
nserror nsgtk_toolbar_position_page_info(struct nsgtk_toolbar *tb,
|
|
struct nsgtk_pi_window *win)
|
|
{
|
|
nserror res;
|
|
int x, y;
|
|
|
|
res = nsgtk_toolbar_get_icon_window_position(tb, URL_BAR_ITEM, &x, &y);
|
|
if (res != NSERROR_OK) {
|
|
return res;
|
|
}
|
|
|
|
nsgtk_page_info_set_position(win, x, y);
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/* exported interface documented in toolbar.h */
|
|
nserror nsgtk_toolbar_position_local_history(struct nsgtk_toolbar *tb)
|
|
{
|
|
nserror res;
|
|
int x, y;
|
|
|
|
res = nsgtk_toolbar_get_icon_window_position(tb, HISTORY_BUTTON, &x, &y);
|
|
if (res != NSERROR_OK) {
|
|
return res;
|
|
}
|
|
|
|
nsgtk_local_history_set_position(x, y);
|
|
|
|
return NSERROR_OK;
|
|
}
|