From d21447d096a320a08b3efb2b8768fad0dcdcfd64 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Thu, 5 May 2016 22:28:51 +0100 Subject: move frontends into sub directory --- frontends/gtk/Makefile | 205 ++ frontends/gtk/Makefile.defaults | 42 + frontends/gtk/about.c | 157 ++ frontends/gtk/about.h | 24 + frontends/gtk/bitmap.c | 540 ++++++ frontends/gtk/bitmap.h | 35 + frontends/gtk/compat.c | 640 +++++++ frontends/gtk/compat.h | 296 +++ frontends/gtk/completion.c | 157 ++ frontends/gtk/completion.h | 46 + frontends/gtk/cookies.c | 219 +++ frontends/gtk/cookies.h | 37 + frontends/gtk/download.c | 865 +++++++++ frontends/gtk/download.h | 41 + frontends/gtk/fetch.c | 260 +++ frontends/gtk/fetch.h | 28 + frontends/gtk/gdk.c | 124 ++ frontends/gtk/gdk.h | 35 + frontends/gtk/gettext.c | 43 + frontends/gtk/gettext.h | 32 + frontends/gtk/gui.c | 1161 ++++++++++++ frontends/gtk/gui.h | 45 + frontends/gtk/history.c | 273 +++ frontends/gtk/history.h | 39 + frontends/gtk/hotlist.c | 280 +++ frontends/gtk/hotlist.h | 40 + frontends/gtk/layout_pango.c | 309 +++ frontends/gtk/layout_pango.h | 43 + frontends/gtk/login.c | 232 +++ frontends/gtk/login.h | 31 + frontends/gtk/menu.c | 585 ++++++ frontends/gtk/menu.h | 227 +++ frontends/gtk/options.h | 72 + frontends/gtk/plotters.c | 538 ++++++ frontends/gtk/plotters.h | 40 + frontends/gtk/preferences.c | 1030 ++++++++++ frontends/gtk/preferences.h | 28 + frontends/gtk/print.c | 613 ++++++ frontends/gtk/print.h | 50 + frontends/gtk/res/Messages | 1 + frontends/gtk/res/SearchEngines | 1 + frontends/gtk/res/adblock.css | 1 + frontends/gtk/res/arrow_down_8x32.png | Bin 0 -> 206 bytes frontends/gtk/res/ca-bundle.txt | 1 + frontends/gtk/res/cookies.gtk2.ui | 174 ++ frontends/gtk/res/cookies.gtk3.ui | 206 ++ frontends/gtk/res/credits.html | 1 + frontends/gtk/res/de/welcome.html | 1 + frontends/gtk/res/default.css | 1 + frontends/gtk/res/default.ico | Bin 0 -> 1406 bytes frontends/gtk/res/downloads.gtk2.ui | 175 ++ frontends/gtk/res/downloads.gtk3.ui | 175 ++ frontends/gtk/res/en/credits.html | 1 + frontends/gtk/res/en/licence.html | 1 + frontends/gtk/res/en/maps.html | 1 + frontends/gtk/res/en/welcome.html | 1 + frontends/gtk/res/favicon.png | 1 + frontends/gtk/res/history.gtk2.ui | 242 +++ frontends/gtk/res/history.gtk3.ui | 238 +++ frontends/gtk/res/hotlist.gtk2.ui | 217 +++ frontends/gtk/res/hotlist.gtk3.ui | 255 +++ frontends/gtk/res/icons | 1 + frontends/gtk/res/internal.css | 1 + frontends/gtk/res/it/credits.html | 1 + frontends/gtk/res/it/licence.html | 1 + frontends/gtk/res/it/welcome.html | 1 + frontends/gtk/res/ja/welcome.html | 1 + frontends/gtk/res/languages | 261 +++ frontends/gtk/res/licence.html | 1 + frontends/gtk/res/login.gtk2.ui | 223 +++ frontends/gtk/res/login.gtk3.ui | 223 +++ frontends/gtk/res/maps.html | 1 + frontends/gtk/res/menu_cursor.png | Bin 0 -> 255 bytes frontends/gtk/res/menu_cursor.xbm | 6 + frontends/gtk/res/menu_cursor_mask.xbm | 6 + frontends/gtk/res/menu_cursor_mask.xpm | 22 + frontends/gtk/res/messages.gresource.xml | 10 + frontends/gtk/res/netsurf-16x16.xpm | 211 +++ frontends/gtk/res/netsurf-gtk.desktop | 68 + frontends/gtk/res/netsurf.gresource.xml | 70 + frontends/gtk/res/netsurf.gtk2.ui | 212 +++ frontends/gtk/res/netsurf.gtk3.ui | 207 ++ frontends/gtk/res/netsurf.png | 1 + frontends/gtk/res/netsurf.xpm | 317 ++++ frontends/gtk/res/nl/credits.html | 1 + frontends/gtk/res/nl/licence.html | 1 + frontends/gtk/res/nl/welcome.html | 1 + frontends/gtk/res/options.gtk2.ui | 3004 +++++++++++++++++++++++++++++ frontends/gtk/res/options.gtk3.ui | 3057 ++++++++++++++++++++++++++++++ frontends/gtk/res/password.gtk2.ui | 415 ++++ frontends/gtk/res/password.gtk3.ui | 415 ++++ frontends/gtk/res/quirks.css | 1 + frontends/gtk/res/ssl.gtk2.ui | 202 ++ frontends/gtk/res/ssl.gtk3.ui | 181 ++ frontends/gtk/res/tabcontents.gtk2.ui | 92 + frontends/gtk/res/tabcontents.gtk3.ui | 92 + frontends/gtk/res/throbber/throbber0.png | Bin 0 -> 507 bytes frontends/gtk/res/throbber/throbber1.png | Bin 0 -> 802 bytes frontends/gtk/res/throbber/throbber2.png | Bin 0 -> 790 bytes frontends/gtk/res/throbber/throbber3.png | Bin 0 -> 808 bytes frontends/gtk/res/throbber/throbber4.png | Bin 0 -> 797 bytes frontends/gtk/res/throbber/throbber5.png | Bin 0 -> 797 bytes frontends/gtk/res/throbber/throbber6.png | Bin 0 -> 819 bytes frontends/gtk/res/throbber/throbber7.png | Bin 0 -> 792 bytes frontends/gtk/res/throbber/throbber8.png | Bin 0 -> 814 bytes frontends/gtk/res/toolbar.gtk2.ui | 189 ++ frontends/gtk/res/toolbar.gtk3.ui | 189 ++ frontends/gtk/res/viewdata.gtk2.ui | 204 ++ frontends/gtk/res/viewdata.gtk3.ui | 239 +++ frontends/gtk/res/warning.gtk2.ui | 77 + frontends/gtk/res/warning.gtk3.ui | 77 + frontends/gtk/res/welcome.html | 1 + frontends/gtk/resources.c | 600 ++++++ frontends/gtk/resources.h | 109 ++ frontends/gtk/scaffolding.c | 2811 +++++++++++++++++++++++++++ frontends/gtk/scaffolding.h | 252 +++ frontends/gtk/schedule.c | 136 ++ frontends/gtk/schedule.h | 26 + frontends/gtk/search.c | 245 +++ frontends/gtk/search.h | 36 + frontends/gtk/selection.c | 96 + frontends/gtk/selection.h | 24 + frontends/gtk/sexy_icon_entry.c | 982 ++++++++++ frontends/gtk/sexy_icon_entry.h | 100 + frontends/gtk/ssl_cert.c | 135 ++ frontends/gtk/ssl_cert.h | 36 + frontends/gtk/tabs.c | 416 ++++ frontends/gtk/tabs.h | 42 + frontends/gtk/throbber.c | 91 + frontends/gtk/throbber.h | 35 + frontends/gtk/toolbar.c | 1416 ++++++++++++++ frontends/gtk/toolbar.h | 96 + frontends/gtk/treeview.c | 586 ++++++ frontends/gtk/treeview.h | 38 + frontends/gtk/viewdata.c | 990 ++++++++++ frontends/gtk/viewdata.h | 48 + frontends/gtk/viewsource.c | 82 + frontends/gtk/viewsource.h | 25 + frontends/gtk/warn.h | 32 + frontends/gtk/window.c | 1337 +++++++++++++ frontends/gtk/window.h | 48 + 141 files changed, 32309 insertions(+) create mode 100644 frontends/gtk/Makefile create mode 100644 frontends/gtk/Makefile.defaults create mode 100644 frontends/gtk/about.c create mode 100644 frontends/gtk/about.h create mode 100644 frontends/gtk/bitmap.c create mode 100644 frontends/gtk/bitmap.h create mode 100644 frontends/gtk/compat.c create mode 100644 frontends/gtk/compat.h create mode 100644 frontends/gtk/completion.c create mode 100644 frontends/gtk/completion.h create mode 100644 frontends/gtk/cookies.c create mode 100644 frontends/gtk/cookies.h create mode 100644 frontends/gtk/download.c create mode 100644 frontends/gtk/download.h create mode 100644 frontends/gtk/fetch.c create mode 100644 frontends/gtk/fetch.h create mode 100644 frontends/gtk/gdk.c create mode 100644 frontends/gtk/gdk.h create mode 100644 frontends/gtk/gettext.c create mode 100644 frontends/gtk/gettext.h create mode 100644 frontends/gtk/gui.c create mode 100644 frontends/gtk/gui.h create mode 100644 frontends/gtk/history.c create mode 100644 frontends/gtk/history.h create mode 100644 frontends/gtk/hotlist.c create mode 100644 frontends/gtk/hotlist.h create mode 100644 frontends/gtk/layout_pango.c create mode 100644 frontends/gtk/layout_pango.h create mode 100644 frontends/gtk/login.c create mode 100644 frontends/gtk/login.h create mode 100644 frontends/gtk/menu.c create mode 100644 frontends/gtk/menu.h create mode 100644 frontends/gtk/options.h create mode 100644 frontends/gtk/plotters.c create mode 100644 frontends/gtk/plotters.h create mode 100644 frontends/gtk/preferences.c create mode 100644 frontends/gtk/preferences.h create mode 100644 frontends/gtk/print.c create mode 100644 frontends/gtk/print.h create mode 120000 frontends/gtk/res/Messages create mode 120000 frontends/gtk/res/SearchEngines create mode 120000 frontends/gtk/res/adblock.css create mode 100644 frontends/gtk/res/arrow_down_8x32.png create mode 120000 frontends/gtk/res/ca-bundle.txt create mode 100644 frontends/gtk/res/cookies.gtk2.ui create mode 100644 frontends/gtk/res/cookies.gtk3.ui create mode 120000 frontends/gtk/res/credits.html create mode 120000 frontends/gtk/res/de/welcome.html create mode 120000 frontends/gtk/res/default.css create mode 100644 frontends/gtk/res/default.ico create mode 100644 frontends/gtk/res/downloads.gtk2.ui create mode 100644 frontends/gtk/res/downloads.gtk3.ui create mode 120000 frontends/gtk/res/en/credits.html create mode 120000 frontends/gtk/res/en/licence.html create mode 120000 frontends/gtk/res/en/maps.html create mode 120000 frontends/gtk/res/en/welcome.html create mode 120000 frontends/gtk/res/favicon.png create mode 100644 frontends/gtk/res/history.gtk2.ui create mode 100644 frontends/gtk/res/history.gtk3.ui create mode 100644 frontends/gtk/res/hotlist.gtk2.ui create mode 100644 frontends/gtk/res/hotlist.gtk3.ui create mode 120000 frontends/gtk/res/icons create mode 120000 frontends/gtk/res/internal.css create mode 120000 frontends/gtk/res/it/credits.html create mode 120000 frontends/gtk/res/it/licence.html create mode 120000 frontends/gtk/res/it/welcome.html create mode 120000 frontends/gtk/res/ja/welcome.html create mode 100644 frontends/gtk/res/languages create mode 120000 frontends/gtk/res/licence.html create mode 100644 frontends/gtk/res/login.gtk2.ui create mode 100644 frontends/gtk/res/login.gtk3.ui create mode 120000 frontends/gtk/res/maps.html create mode 100644 frontends/gtk/res/menu_cursor.png create mode 100644 frontends/gtk/res/menu_cursor.xbm create mode 100644 frontends/gtk/res/menu_cursor_mask.xbm create mode 100644 frontends/gtk/res/menu_cursor_mask.xpm create mode 100644 frontends/gtk/res/messages.gresource.xml create mode 100644 frontends/gtk/res/netsurf-16x16.xpm create mode 100644 frontends/gtk/res/netsurf-gtk.desktop create mode 100644 frontends/gtk/res/netsurf.gresource.xml create mode 100644 frontends/gtk/res/netsurf.gtk2.ui create mode 100644 frontends/gtk/res/netsurf.gtk3.ui create mode 120000 frontends/gtk/res/netsurf.png create mode 100644 frontends/gtk/res/netsurf.xpm create mode 120000 frontends/gtk/res/nl/credits.html create mode 120000 frontends/gtk/res/nl/licence.html create mode 120000 frontends/gtk/res/nl/welcome.html create mode 100644 frontends/gtk/res/options.gtk2.ui create mode 100644 frontends/gtk/res/options.gtk3.ui create mode 100644 frontends/gtk/res/password.gtk2.ui create mode 100644 frontends/gtk/res/password.gtk3.ui create mode 120000 frontends/gtk/res/quirks.css create mode 100644 frontends/gtk/res/ssl.gtk2.ui create mode 100644 frontends/gtk/res/ssl.gtk3.ui create mode 100644 frontends/gtk/res/tabcontents.gtk2.ui create mode 100644 frontends/gtk/res/tabcontents.gtk3.ui create mode 100644 frontends/gtk/res/throbber/throbber0.png create mode 100644 frontends/gtk/res/throbber/throbber1.png create mode 100644 frontends/gtk/res/throbber/throbber2.png create mode 100644 frontends/gtk/res/throbber/throbber3.png create mode 100644 frontends/gtk/res/throbber/throbber4.png create mode 100644 frontends/gtk/res/throbber/throbber5.png create mode 100644 frontends/gtk/res/throbber/throbber6.png create mode 100644 frontends/gtk/res/throbber/throbber7.png create mode 100644 frontends/gtk/res/throbber/throbber8.png create mode 100644 frontends/gtk/res/toolbar.gtk2.ui create mode 100644 frontends/gtk/res/toolbar.gtk3.ui create mode 100644 frontends/gtk/res/viewdata.gtk2.ui create mode 100644 frontends/gtk/res/viewdata.gtk3.ui create mode 100644 frontends/gtk/res/warning.gtk2.ui create mode 100644 frontends/gtk/res/warning.gtk3.ui create mode 120000 frontends/gtk/res/welcome.html create mode 100644 frontends/gtk/resources.c create mode 100644 frontends/gtk/resources.h create mode 100644 frontends/gtk/scaffolding.c create mode 100644 frontends/gtk/scaffolding.h create mode 100644 frontends/gtk/schedule.c create mode 100644 frontends/gtk/schedule.h create mode 100644 frontends/gtk/search.c create mode 100644 frontends/gtk/search.h create mode 100644 frontends/gtk/selection.c create mode 100644 frontends/gtk/selection.h create mode 100644 frontends/gtk/sexy_icon_entry.c create mode 100644 frontends/gtk/sexy_icon_entry.h create mode 100644 frontends/gtk/ssl_cert.c create mode 100644 frontends/gtk/ssl_cert.h create mode 100644 frontends/gtk/tabs.c create mode 100644 frontends/gtk/tabs.h create mode 100644 frontends/gtk/throbber.c create mode 100644 frontends/gtk/throbber.h create mode 100644 frontends/gtk/toolbar.c create mode 100644 frontends/gtk/toolbar.h create mode 100644 frontends/gtk/treeview.c create mode 100644 frontends/gtk/treeview.h create mode 100644 frontends/gtk/viewdata.c create mode 100644 frontends/gtk/viewdata.h create mode 100644 frontends/gtk/viewsource.c create mode 100644 frontends/gtk/viewsource.h create mode 100644 frontends/gtk/warn.h create mode 100644 frontends/gtk/window.c create mode 100644 frontends/gtk/window.h (limited to 'frontends/gtk') diff --git a/frontends/gtk/Makefile b/frontends/gtk/Makefile new file mode 100644 index 000000000..7f8ffc16a --- /dev/null +++ b/frontends/gtk/Makefile @@ -0,0 +1,205 @@ +# +# Makefile for NetSurf GTK target +# +# This file is part of NetSurf +# +# ---------------------------------------------------------------------------- +# GTK flag setup (using pkg-config) +# ---------------------------------------------------------------------------- + +# define additional CFLAGS and LDFLAGS requirements for pkg-configed libs here +NETSURF_FEATURE_RSVG_CFLAGS := -DWITH_RSVG +NETSURF_FEATURE_VIDEO_CFLAGS := -DWITH_VIDEO + +$(eval $(call pkg_config_find_and_add_enabled,RSVG,librsvg-2.0,SVG)) +$(eval $(call pkg_config_find_and_add_enabled,VIDEO,gstreamer-0.10,Video)) + +# GTK and GLIB flags to disable depricated usage +GTKDEPFLAGS := -DG_DISABLE_SINGLE_INCLUDES \ + -DG_DISABLE_DEPRECATED \ + -DGTK_DISABLE_SINGLE_INCLUDES \ + -DGTK_MULTIHEAD_SAFE \ + -DPANGO_DISABLE_DEPRECATED + +# later editions of gtk 2 deprecate interfaces we rely upon for cursors +# -DGDK_PIXBUF_DISABLE_DEPRECATED + +# libsexy currently means we cannot enable this +# -DGDK_DISABLE_DEPRECATED + +# gtk3 is depricating interfaces we use a lot +ifeq ($(NETSURF_GTK_MAJOR),2) +GTKDEPFLAGS += -DGTK_DISABLE_DEPRECATED +endif + + +GTKCFLAGS := -std=c99 -Dgtk -Dnsgtk -g \ + $(GTKDEPFLAGS) \ + -D_BSD_SOURCE \ + -D_DEFAULT_SOURCE \ + -D_XOPEN_SOURCE=600 \ + -D_POSIX_C_SOURCE=200809L \ + -D_NETBSD_SOURCE \ + -DGTK_RESPATH=\"$(NETSURF_GTK_RESOURCES)\" + +# non optional pkg-configed libs +$(eval $(call pkg_config_find_and_add,gtk+-$(NETSURF_GTK_MAJOR).0,GTK-$(NETSURF_GTK_MAJOR))) +$(eval $(call pkg_config_find_and_add,gthread-2.0,GThread2)) +$(eval $(call pkg_config_find_and_add,gmodule-2.0,GModule2)) + + +CFLAGS += $(GTKCFLAGS) +LDFLAGS += -lm + +# --------------------------------------------------------------------------- +# Target setup +# --------------------------------------------------------------------------- + +# Path to GTK resources +NSGTK_RESOURCES_DIR := $(FRONTEND_RESOURCES_DIR) + +# The gtk binary target +EXETARGET := nsgtk + +# The filter and target for split messages +MESSAGES_FILTER=gtk +MESSAGES_TARGET=$(NSGTK_RESOURCES_DIR) + +# --------------------------------------------------------------------------- +# Windows flag setup +# --------------------------------------------------------------------------- + +ifeq ($(HOST),Windows_NT) + CFLAGS += -U__STRICT_ANSI__ +endif + +# ---------------------------------------------------------------------------- +# Builtin resource handling +# ---------------------------------------------------------------------------- + +# builtin resource sources +S_RESOURCE := + +# Glib prior to 2.32 does not have GResource handling. +# +# This uses pkg-config to check for the minimum required version for +# this feature in a way similar to the pkg_config_find_and_add_enabled +# macro. Note we check for gmodule-2.0 which is a specific part of +# glib we require. +# +# It would be nice if we could check for this functionality rather +# than "knowing" the version but there does not appear to be a simple +# way to implement that. +# +NETSURF_FEATURE_GRESOURCE_AVAILABLE := $(shell $(PKG_CONFIG) --atleast-version=2.32 gmodule-2.0 && echo yes) +ifneq (,$(filter $(NETSURF_USE_GRESOURCE),AUTO YES)) +ifeq ($(NETSURF_FEATURE_GRESOURCE_AVAILABLE),yes) + +# Gresource use has been enabled +NETSURF_FEATURE_GRESOURCE_ENABLED := yes + +#resource compiler tool +GLIB_COMPILE_RESOURCES := glib-compile-resources +CFLAGS += -DWITH_GRESOURCE + +NETSURF_GRESOURCE_XML := $(NSGTK_RESOURCES_DIR)/netsurf.gresource.xml +MESSAGES_GRESOURCE_XML := $(NSGTK_RESOURCES_DIR)/messages.gresource.xml + +# generate the netsurf gresource source files +$(OBJROOT)/netsurf_gresource.c: $(NETSURF_GRESOURCE_XML) $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir $(NSGTK_RESOURCES_DIR) --generate-dependencies $(NETSURF_GRESOURCE_XML)) + $(VQ)echo "GRESORCE: $<" + $(Q)$(GLIB_COMPILE_RESOURCES) --generate-source --sourcedir $(NSGTK_RESOURCES_DIR) --target=$@ $< + +S_RESOURCE += $(OBJROOT)/netsurf_gresource.c + +# generate the messages gresource source file +$(OBJROOT)/messages_gresource.c: $(MESSAGES_GRESOURCE_XML) $(addsuffix /Messages,$(addprefix $(MESSAGES_TARGET)/,$(MESSAGES_LANGUAGES))) + $(VQ)echo "GRESORCE: $<" + $(Q)$(GLIB_COMPILE_RESOURCES) --generate-source --sourcedir $(NSGTK_RESOURCES_DIR) --target=$@ $< + +S_RESOURCE += $(OBJROOT)/messages_gresource.c + + +endif +endif + +# Build pixbufs as inlines if enabled +ifneq (,$(filter $(NETSURF_USE_INLINE_PIXBUF),AUTO YES)) +ifneq ($(NETSURF_FEATURE_GRESOURCE_ENABLED),yes) + +CFLAGS += -DWITH_BUILTIN_PIXBUF + +GTK_IMAGE_favicon := favicon.png +GTK_IMAGE_netsurf := netsurf.xpm +GTK_IMAGE_menu_cursor := menu_cursor.png + +# 1: input file +# 2: output file +# 3: bitmap name +define convert_image + +# add converted pixbuf to builtin resource sources +S_RESOURCE += $(2) + +$(2): $(1) + $(VQ)echo " INLINE: ${3}" + $(Q)echo "#include " > $(2) + $(Q)gdk-pixbuf-csource --extern --raw --name=$(3) $(1) >> $(2) || \ + ( rm -f $(2) && false ) + +endef + +$(eval $(foreach V,$(filter GTK_IMAGE_%,$(.VARIABLES)),$(call convert_image,$(addprefix $(NSGTK_RESOURCES_DIR)/,$($(V))),$(OBJROOT)/$(patsubst GTK_IMAGE_%,%,$(V)).c,$(patsubst GTK_IMAGE_%,%,$(V))_pixdata))) +endif +endif + +# ---------------------------------------------------------------------------- +# Source file setup +# ---------------------------------------------------------------------------- + +# S_FRONTEND are sources purely for the GTK frontend +S_FRONTEND := gui.c schedule.c layout_pango.c bitmap.c plotters.c \ + treeview.c scaffolding.c gdk.c completion.c login.c throbber.c \ + selection.c history.c window.c fetch.c download.c menu.c \ + print.c search.c tabs.c toolbar.c gettext.c \ + compat.c cookies.c hotlist.c viewdata.c viewsource.c \ + preferences.c about.c ssl_cert.c resources.c + +# This is the final source build list +# Note this is deliberately *not* expanded here as common and image +# are not yet available +SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_RESOURCE) $(S_FRONTEND) + +# ---------------------------------------------------------------------------- +# Install target +# ---------------------------------------------------------------------------- + +GTK_RESOURCES_LIST := \ + languages SearchEngines toolbarIndices ca-bundle.txt \ + default.css adblock.css quirks.css internal.css \ + credits.html licence.html welcome.html maps.html Messages \ + default.ico favicon.png netsurf.png netsurf.xpm netsurf-16x16.xpm \ + arrow_down_8x32.png + +GTK_RESOURCES_LIST := \ + $(addprefix $(NSGTK_RESOURCES_DIR)/, $(GTK_RESOURCES_LIST)) \ + $(wildcard $(NSGTK_RESOURCES_DIR)/*.gtk$(NETSURF_GTK_MAJOR).ui) + +# translations with more than just Messages files +GTK_TRANSLATIONS_HTML := de en fr it ja nl + +install-gtk: + $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_BIN) + $(Q)install nsgtk $(DESTDIR)$(NETSURF_GTK_BIN)netsurf + $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_RESOURCES)icons + $(Q)install -m 0644 $(NSGTK_RESOURCES_DIR)/icons/*.png $(DESTDIR)$(NETSURF_GTK_RESOURCES)/icons + $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_RESOURCES)throbber + $(Q)install -m 0644 $(NSGTK_RESOURCES_DIR)/throbber/*.png $(DESTDIR)$(NETSURF_GTK_RESOURCES)/throbber + $(Q)tar -c -h -C $(NSGTK_RESOURCES_DIR) -f - $(GTK_TRANSLATIONS_HTML) | tar -xv -C $(DESTDIR)$(NETSURF_GTK_RESOURCES) -f - + $(Q)install -m 0644 $(GTK_RESOURCES_LIST) $(DESTDIR)$(NETSURF_GTK_RESOURCES) + +# ---------------------------------------------------------------------------- +# Package target +# ---------------------------------------------------------------------------- + +package-gtk: diff --git a/frontends/gtk/Makefile.defaults b/frontends/gtk/Makefile.defaults new file mode 100644 index 000000000..fc352a020 --- /dev/null +++ b/frontends/gtk/Makefile.defaults @@ -0,0 +1,42 @@ +# ---------------------------------------------------------------------------- +# GTK-specific options +# ---------------------------------------------------------------------------- + +# Where to search for NetSurf's resources after looking in ~/.netsurf and +# $NETSURFRES. It must have a trailing / +NETSURF_GTK_RESOURCES := $(PREFIX)/share/netsurf/:./frontends/gtk/res/ + +# Where to install the netsurf binary +NETSURF_GTK_BIN := $(PREFIX)/bin/ + +# Enable NetSurf's use of librsvg in conjunction with Cairo to display SVGs +# Valid options: YES, NO, AUTO +NETSURF_USE_RSVG := AUTO + +# Enable NetSurf's use of libsvgtiny for displaying SVGs +# Valid options: YES, NO, AUTO +NETSURF_USE_NSSVG := AUTO + +# Enable NetSurf's use of librosprite for displaying RISC OS Sprites +# Valid options: YES, NO, AUTO +NETSURF_USE_ROSPRITE := AUTO + +# Enable the use of GLib compiled in resource handling. This requires +# GLib 2.32 or later +# Valid options: YES, NO, AUTO +NETSURF_USE_GRESOURCE := AUTO + +# Enable the use of compiled in inline pixbuf. This is depricated +# since GLib 2.32. The automatic selection is disabled if GRESOURCE +# handling is enabled +# Valid options: YES, NO, AUTO +NETSURF_USE_INLINE_PIXBUF := AUTO + +# Enable building the source object cache filesystem based backing store. +NETSURF_FS_BACKING_STORE := YES + +# Set default GTK version to build for (2 or 3) +NETSURF_GTK_MAJOR ?= 2 + +# Optimisation levels +CFLAGS += -O2 diff --git a/frontends/gtk/about.c b/frontends/gtk/about.c new file mode 100644 index 000000000..d57afea7f --- /dev/null +++ b/frontends/gtk/about.c @@ -0,0 +1,157 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +/** + * \file gtk/about.c + * + * Implementation of gtk about dialog. + */ + +#include +#include + +#include "utils/messages.h" +#include "utils/nsoption.h" +#include "utils/nsurl.h" +#include "desktop/browser.h" +#include "desktop/version.h" + +#include "gtk/warn.h" +#include "gtk/compat.h" +#include "gtk/about.h" + +#define ABOUT_RESPONSE_ID_LICENCE 1 +#define ABOUT_RESPONSE_ID_CREDITS 2 + + +/** + * Open a url and a browser window/tab + * + * \param url_text The text of the url to open + */ +static void about_open(const char *url_text) +{ + nsurl *url; + nserror ret; + enum browser_window_create_flags flags = BW_CREATE_HISTORY; + + if (nsoption_bool(show_single_tab) == true) { + flags |= BW_CREATE_TAB; + } + + ret = nsurl_create(url_text, &url); + if (ret == NSERROR_OK) { + ret = browser_window_create(flags, url, NULL, NULL, NULL); + nsurl_unref(url); + } + + if (ret != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(ret), 0); + } +} + +/** + * About dialog response handling. + * + * \param dialog The dialog widget + * \param response_id The response ID from the user clicking. + * \param user_data The value from the signal connection. + */ +static void +nsgtk_about_dialog_response(GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + switch (response_id) { + + case ABOUT_RESPONSE_ID_LICENCE: + about_open("about:credits"); + break; + + case ABOUT_RESPONSE_ID_CREDITS: + about_open("about:licence"); + break; + } + + /* close about dialog */ + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + +void nsgtk_about_dialog_init(GtkWindow *parent) +{ + GtkWidget *dialog, *vbox, *label; + gchar *name_string; + GList *pixbufs; + + /* Create the dialog */ + dialog = gtk_dialog_new_with_buttons("About NetSurf", + parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + "Licence", ABOUT_RESPONSE_ID_LICENCE, + "Credits", ABOUT_RESPONSE_ID_CREDITS, + "Close", GTK_RESPONSE_CANCEL, + NULL, NULL); + + vbox = nsgtk_vbox_new(FALSE, 8); + + gtk_box_pack_start(GTK_BOX(nsgtk_dialog_get_content_area(GTK_DIALOG(dialog))), vbox, TRUE, TRUE, 0); + + /* NetSurf icon */ + pixbufs = gtk_window_get_default_icon_list(); + if (pixbufs != NULL) { + GtkWidget *image; + + image = nsgtk_image_new_from_pixbuf_icon(GDK_PIXBUF(g_list_nth_data(pixbufs, 0)), + GTK_ICON_SIZE_DIALOG); + g_list_free(pixbufs); + + gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 0); + } + + /* version string */ + label = gtk_label_new (NULL); + name_string = g_markup_printf_escaped("NetSurf %s", netsurf_version); + gtk_label_set_markup (GTK_LABEL (label), name_string); + g_free(name_string); + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + label = gtk_label_new(messages_get("AboutDesc")); + gtk_label_set_selectable(GTK_LABEL (label), TRUE); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + label = gtk_label_new(messages_get("NetSurfCopyright")); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); + gtk_box_pack_start(GTK_BOX (vbox), label, FALSE, FALSE, 0); + + /* Remove separator */ + nsgtk_dialog_set_has_separator(GTK_DIALOG (dialog), FALSE); + + /* Ensure that the dialog box response is processed. */ + g_signal_connect_swapped(dialog, + "response", + G_CALLBACK(nsgtk_about_dialog_response), + dialog); + + /* Add the label, and show everything we've added to the dialog. */ + gtk_widget_show_all(dialog); +} diff --git a/frontends/gtk/about.h b/frontends/gtk/about.h new file mode 100644 index 000000000..bf3c9f58d --- /dev/null +++ b/frontends/gtk/about.h @@ -0,0 +1,24 @@ +/* + * Copyright 2008 Rob Kendrick + * + * 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 . + */ + +#ifndef NETSURF_GTK_ABOUT_H +#define NETSURF_GTK_ABOUT_H + +void nsgtk_about_dialog_init(GtkWindow *parent); + +#endif diff --git a/frontends/gtk/bitmap.c b/frontends/gtk/bitmap.c new file mode 100644 index 000000000..f8dcf1d17 --- /dev/null +++ b/frontends/gtk/bitmap.c @@ -0,0 +1,540 @@ +/* + * Copyright 2004 James Bursa + * + * 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 . + */ + +/** + * \file + * Generic bitmap handling (GDK / GTK+ implementation). + * + * This implements the interface given by desktop/bitmap.h using GdkPixbufs. + */ + +#include +#include +#include +#include +#include +#include + +#include "utils/utils.h" +#include "utils/log.h" +#include "content/content.h" +#include "image/bitmap.h" +#include "desktop/plotters.h" + +#include "gtk/scaffolding.h" +#include "gtk/plotters.h" +#include "gtk/bitmap.h" + + +/** + * Create a bitmap. + * + * \param width width of image in pixels + * \param height width of image in pixels + * \param state a flag word indicating the initial state + * \return an opaque struct bitmap, or NULL on memory exhaustion + */ +static void *bitmap_create(int width, int height, unsigned int state) +{ + struct bitmap *gbitmap; + + gbitmap = calloc(1, sizeof(struct bitmap)); + if (gbitmap != NULL) { + if ((state & BITMAP_OPAQUE) != 0) { + gbitmap->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); + } else { + gbitmap->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + } + + if (cairo_surface_status(gbitmap->surface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(gbitmap->surface); + free(gbitmap); + gbitmap = NULL; + } + } + + return gbitmap; +} + + +/** + * Sets whether a bitmap should be plotted opaque + * + * \param vbitmap a bitmap, as returned by bitmap_create() + * \param opaque whether the bitmap should be plotted opaque + */ +static void bitmap_set_opaque(void *vbitmap, bool opaque) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + cairo_format_t fmt; + cairo_surface_t *nsurface = NULL; + + assert(gbitmap); + + fmt = cairo_image_surface_get_format(gbitmap->surface); + if (fmt == CAIRO_FORMAT_RGB24) { + if (opaque == false) { + /* opaque to transparent */ + nsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + cairo_image_surface_get_width(gbitmap->surface), + cairo_image_surface_get_height(gbitmap->surface)); + + } + + } else { + if (opaque == true) { + /* transparent to opaque */ + nsurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, + cairo_image_surface_get_width(gbitmap->surface), + cairo_image_surface_get_height(gbitmap->surface)); + + } + } + + if (nsurface != NULL) { + if (cairo_surface_status(nsurface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(nsurface); + } else { + memcpy(cairo_image_surface_get_data(nsurface), + cairo_image_surface_get_data(gbitmap->surface), + cairo_image_surface_get_stride(gbitmap->surface) * cairo_image_surface_get_height(gbitmap->surface)); + cairo_surface_destroy(gbitmap->surface); + gbitmap->surface = nsurface; + + cairo_surface_mark_dirty(gbitmap->surface); + + } + + } +} + + +/** + * Tests whether a bitmap has an opaque alpha channel + * + * \param vbitmap a bitmap, as returned by bitmap_create() + * \return whether the bitmap is opaque + */ +static bool bitmap_test_opaque(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + unsigned char *pixels; + int pcount; + int ploop; + + assert(gbitmap); + + pixels = cairo_image_surface_get_data(gbitmap->surface); + + pcount = cairo_image_surface_get_stride(gbitmap->surface) * + cairo_image_surface_get_height(gbitmap->surface); + + for (ploop = 3; ploop < pcount; ploop += 4) { + if (pixels[ploop] != 0xff) { + return false; + } + } + + return true; +} + + +/** + * Gets whether a bitmap should be plotted opaque + * + * \param vbitmap a bitmap, as returned by bitmap_create() + */ +static bool bitmap_get_opaque(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + cairo_format_t fmt; + + assert(gbitmap); + + fmt = cairo_image_surface_get_format(gbitmap->surface); + if (fmt == CAIRO_FORMAT_RGB24) { + return true; + } + + return false; +} + + +/** + * Return a pointer to the pixel data in a bitmap. + * + * \param vbitmap a bitmap, as returned by bitmap_create() + * \return pointer to the pixel buffer + * + * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end + * of rows. The width of a row in bytes is given by bitmap_get_rowstride(). + */ +static unsigned char *bitmap_get_buffer(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + int pixel_loop; + int pixel_count; + uint8_t *pixels; + uint32_t t, r, g, b; + cairo_format_t fmt; + + assert(gbitmap); + + cairo_surface_flush(gbitmap->surface); + pixels = cairo_image_surface_get_data(gbitmap->surface); + + if (!gbitmap->converted) + return pixels; + + fmt = cairo_image_surface_get_format(gbitmap->surface); + pixel_count = cairo_image_surface_get_width(gbitmap->surface) * + cairo_image_surface_get_height(gbitmap->surface); + + if (fmt == CAIRO_FORMAT_RGB24) { + /* Opaque image */ + for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) { + /* Cairo surface is ARGB, written in native endian */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + b = pixels[4 * pixel_loop + 0]; + g = pixels[4 * pixel_loop + 1]; + r = pixels[4 * pixel_loop + 2]; + t = pixels[4 * pixel_loop + 3]; +#else + t = pixels[4 * pixel_loop + 0]; + r = pixels[4 * pixel_loop + 1]; + g = pixels[4 * pixel_loop + 2]; + b = pixels[4 * pixel_loop + 3]; +#endif + + /* Core bitmaps always have a component order of rgba, + * regardless of system endianness */ + pixels[4 * pixel_loop + 0] = r; + pixels[4 * pixel_loop + 1] = g; + pixels[4 * pixel_loop + 2] = b; + pixels[4 * pixel_loop + 3] = t; + } + } else { + /* Alpha image: de-multiply alpha */ + for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + b = pixels[4 * pixel_loop + 0]; + g = pixels[4 * pixel_loop + 1]; + r = pixels[4 * pixel_loop + 2]; + t = pixels[4 * pixel_loop + 3]; +#else + t = pixels[4 * pixel_loop + 0]; + r = pixels[4 * pixel_loop + 1]; + g = pixels[4 * pixel_loop + 2]; + b = pixels[4 * pixel_loop + 3]; +#endif + + if (t != 0) { + r = (r << 8) / t; + g = (g << 8) / t; + b = (b << 8) / t; + + r = (r > 255) ? 255 : r; + g = (g > 255) ? 255 : g; + b = (b > 255) ? 255 : b; + } else { + r = g = b = 0; + } + + pixels[4 * pixel_loop + 0] = r; + pixels[4 * pixel_loop + 1] = g; + pixels[4 * pixel_loop + 2] = b; + pixels[4 * pixel_loop + 3] = t; + } + } + + gbitmap->converted = false; + + return (unsigned char *) pixels; +} + + +/** + * Find the width of a pixel row in bytes. + * + * \param vbitmap a bitmap, as returned by bitmap_create() + * \return width of a pixel row in the bitmap + */ +static size_t bitmap_get_rowstride(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + assert(gbitmap); + + return cairo_image_surface_get_stride(gbitmap->surface); +} + + +/** + * Find the bytes per pixel of a bitmap + * + * \param vbitmap a bitmap, as returned by bitmap_create() + * \return bytes per pixel + */ +static size_t bitmap_get_bpp(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + assert(gbitmap); + + return 4; +} + + + +/** + * Free a bitmap. + * + * \param vbitmap a bitmap, as returned by bitmap_create() + */ +static void bitmap_destroy(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + assert(gbitmap); + + if (gbitmap->surface != NULL) { + cairo_surface_destroy(gbitmap->surface); + } + if (gbitmap->scsurface != NULL) { + cairo_surface_destroy(gbitmap->scsurface); + } + free(gbitmap); +} + + +/** + * Save a bitmap in the platform's native format. + * + * \param vbitmap a bitmap, as returned by bitmap_create() + * \param path pathname for file + * \param flags modify the behaviour of the save + * \return true on success, false on error and error reported + */ +static bool bitmap_save(void *vbitmap, const char *path, unsigned flags) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + assert(gbitmap); + + return false; +} + + +/** + * The bitmap image has changed, so flush any persistant cache. + * + * \param vbitmap a bitmap, as returned by bitmap_create() + */ +static void bitmap_modified(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + int pixel_loop; + int pixel_count; + uint8_t *pixels; + uint32_t t, r, g, b; + cairo_format_t fmt; + + assert(gbitmap); + + fmt = cairo_image_surface_get_format(gbitmap->surface); + + pixel_count = cairo_image_surface_get_width(gbitmap->surface) * + cairo_image_surface_get_height(gbitmap->surface); + pixels = cairo_image_surface_get_data(gbitmap->surface); + + if (gbitmap->converted) { + cairo_surface_mark_dirty(gbitmap->surface); + return; + } + + if (fmt == CAIRO_FORMAT_RGB24) { + /* Opaque image */ + for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) { + /* Core bitmaps always have a component order of rgba, + * regardless of system endianness */ + r = pixels[4 * pixel_loop + 0]; + g = pixels[4 * pixel_loop + 1]; + b = pixels[4 * pixel_loop + 2]; + t = pixels[4 * pixel_loop + 3]; + + /* Cairo surface is ARGB, written in native endian */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + pixels[4 * pixel_loop + 0] = b; + pixels[4 * pixel_loop + 1] = g; + pixels[4 * pixel_loop + 2] = r; + pixels[4 * pixel_loop + 3] = t; +#else + pixels[4 * pixel_loop + 0] = t; + pixels[4 * pixel_loop + 1] = r; + pixels[4 * pixel_loop + 2] = g; + pixels[4 * pixel_loop + 3] = b; +#endif + } + } else { + /* Alpha image: pre-multiply alpha */ + for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) { + r = pixels[4 * pixel_loop + 0]; + g = pixels[4 * pixel_loop + 1]; + b = pixels[4 * pixel_loop + 2]; + t = pixels[4 * pixel_loop + 3]; + + if (t != 0) { + r = ((r * (t + 1)) >> 8) & 0xff; + g = ((g * (t + 1)) >> 8) & 0xff; + b = ((b * (t + 1)) >> 8) & 0xff; + } else { + r = g = b = 0; + } + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + pixels[4 * pixel_loop + 0] = b; + pixels[4 * pixel_loop + 1] = g; + pixels[4 * pixel_loop + 2] = r; + pixels[4 * pixel_loop + 3] = t; +#else + pixels[4 * pixel_loop + 0] = t; + pixels[4 * pixel_loop + 1] = r; + pixels[4 * pixel_loop + 2] = g; + pixels[4 * pixel_loop + 3] = b; +#endif + } + } + + cairo_surface_mark_dirty(gbitmap->surface); + + gbitmap->converted = true; +} + +/* exported interface documented in gtk/bitmap.h */ +int nsgtk_bitmap_get_width(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + assert(gbitmap); + + return cairo_image_surface_get_width(gbitmap->surface); +} + +/* exported interface documented in gtk/bitmap.h */ +int nsgtk_bitmap_get_height(void *vbitmap) +{ + struct bitmap *gbitmap = (struct bitmap *)vbitmap; + assert(gbitmap); + + return cairo_image_surface_get_height(gbitmap->surface); +} + +/** + * Render content into a bitmap. + * + * \param bitmap The bitmap to draw to + * \param content The content to render + * \return true on success and bitmap updated else false + */ +static nserror +bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content) +{ + cairo_surface_t *dsurface = bitmap->surface; + cairo_surface_t *surface; + cairo_t *old_cr; + gint dwidth, dheight; + int cwidth, cheight; + struct redraw_context ctx = { + .interactive = false, + .background_images = true, + .plot = &nsgtk_plotters + }; + + assert(content); + assert(bitmap); + + dwidth = cairo_image_surface_get_width(dsurface); + dheight = cairo_image_surface_get_height(dsurface); + + /* Calculate size of buffer to render the content into */ + /* Get the width from the content width, unless it exceeds 1024, + * in which case we use 1024. This means we never create excessively + * large render buffers for huge contents, which would eat memory and + * cripple performance. + */ + cwidth = min(max(content_get_width(content), dwidth), 1024); + + /* The height is set in proportion with the width, according to the + * aspect ratio of the required thumbnail. */ + cheight = ((cwidth * dheight) + (dwidth / 2)) / dwidth; + + /* Create surface to render into */ + surface = cairo_surface_create_similar(dsurface, CAIRO_CONTENT_COLOR_ALPHA, cwidth, cheight); + + if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(surface); + return false; + } + + old_cr = current_cr; + current_cr = cairo_create(surface); + + /* render the content */ + content_scaled_redraw(content, cwidth, cheight, &ctx); + + cairo_destroy(current_cr); + current_cr = old_cr; + + cairo_t *cr = cairo_create(dsurface); + + /* Scale *before* setting the source surface (1) */ + cairo_scale (cr, (double)dwidth / cwidth, (double)dheight / cheight); + cairo_set_source_surface (cr, surface, 0, 0); + + /* To avoid getting the edge pixels blended with 0 alpha, + * which would occur with the default EXTEND_NONE. Use + * EXTEND_PAD for 1.2 or newer (2) + */ + cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REFLECT); + + /* Replace the destination with the source instead of overlaying */ + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + /* Do the actual drawing */ + cairo_paint(cr); + + cairo_destroy(cr); + + cairo_surface_destroy(surface); + + return NSERROR_OK; +} + + +static struct gui_bitmap_table bitmap_table = { + .create = bitmap_create, + .destroy = bitmap_destroy, + .set_opaque = bitmap_set_opaque, + .get_opaque = bitmap_get_opaque, + .test_opaque = bitmap_test_opaque, + .get_buffer = bitmap_get_buffer, + .get_rowstride = bitmap_get_rowstride, + .get_width = nsgtk_bitmap_get_width, + .get_height = nsgtk_bitmap_get_height, + .get_bpp = bitmap_get_bpp, + .save = bitmap_save, + .modified = bitmap_modified, + .render = bitmap_render, +}; + +struct gui_bitmap_table *nsgtk_bitmap_table = &bitmap_table; diff --git a/frontends/gtk/bitmap.h b/frontends/gtk/bitmap.h new file mode 100644 index 000000000..0f46d19a8 --- /dev/null +++ b/frontends/gtk/bitmap.h @@ -0,0 +1,35 @@ +/* + * Copyright 2006 Daniel Silverstone + * + * 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 . + */ + +#ifndef NS_GTK_BITMAP_H +#define NS_GTK_BITMAP_H + +#include + +extern struct gui_bitmap_table *nsgtk_bitmap_table; + +struct bitmap { + cairo_surface_t *surface; /* original cairo surface */ + cairo_surface_t *scsurface; /* scaled surface */ + bool converted; /** set if the surface data has been converted */ +}; + +int nsgtk_bitmap_get_width(void *vbitmap); +int nsgtk_bitmap_get_height(void *vbitmap); + +#endif /* NS_GTK_BITMAP_H */ diff --git a/frontends/gtk/compat.c b/frontends/gtk/compat.c new file mode 100644 index 000000000..4c5524b0e --- /dev/null +++ b/frontends/gtk/compat.c @@ -0,0 +1,640 @@ +/* + * Copyright 2010 Rob Kendrick + * + * 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 . + */ + +/** + * \file + * Compatibility functions for older GTK versions implementation + */ + +#include + +#include "gtk/compat.h" + +#ifdef _SEXY_ICON_ENTRY_H_ +#include "gtk/sexy_icon_entry.c" + +/* + * exported interface documented in gtk/compat.h + * + * Only required for the lib sexy interface before 2.16 + */ +GtkStateType nsgtk_widget_get_state(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(2,18,0) + return gtk_widget_get_state(widget); +#else + return GTK_WIDGET_STATE(widget); +#endif +} + + +#endif + +void nsgtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus) +{ +#if GTK_CHECK_VERSION(2,22,0) + gtk_widget_set_can_focus(widget, can_focus); +#else + if (can_focus == TRUE) + GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS(widget, GTK_CAN_FOCUS); +#endif +} + +gboolean nsgtk_widget_has_focus(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(2,20,0) + return gtk_widget_has_focus(widget); +#else + return GTK_WIDGET_HAS_FOCUS(widget); +#endif +} + +gboolean nsgtk_widget_get_visible(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(2,20,0) + return gtk_widget_get_visible(widget); +#else + return GTK_WIDGET_VISIBLE(widget); +#endif +} + +gboolean nsgtk_widget_get_realized(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(2,20,0) + return gtk_widget_get_realized(widget); +#else + return GTK_WIDGET_REALIZED(widget); +#endif +} + +gboolean nsgtk_widget_get_mapped(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(2,20,0) + return gtk_widget_get_mapped(widget); +#else + return GTK_WIDGET_MAPPED(widget); +#endif +} + +gboolean nsgtk_widget_is_drawable(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(2,18,0) + return gtk_widget_is_drawable(widget); +#else + return GTK_WIDGET_DRAWABLE(widget); +#endif +} + +void nsgtk_dialog_set_has_separator(GtkDialog *dialog, gboolean setting) +{ +#if GTK_CHECK_VERSION(2,21,8) + /* Deprecated */ +#else + gtk_dialog_set_has_separator(dialog, setting); +#endif +} + +GtkWidget *nsgtk_combo_box_text_new(void) +{ +#if GTK_CHECK_VERSION(2,24,0) + return gtk_combo_box_text_new(); +#else + return gtk_combo_box_new_text(); +#endif +} + +void nsgtk_combo_box_text_append_text(GtkWidget *combo_box, const gchar *text) +{ +#if GTK_CHECK_VERSION(2,24,0) + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), text); +#else + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), text); +#endif + +} + +gchar *nsgtk_combo_box_text_get_active_text(GtkWidget *combo_box) +{ +#if GTK_CHECK_VERSION(2,24,0) + return gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo_box)); +#else + return gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo_box)); +#endif +} + +GtkWidget *nsgtk_entry_new(void) +{ +#if GTK_CHECK_VERSION(2,16,0) + return gtk_entry_new(); +#else + return GTK_WIDGET(sexy_icon_entry_new()); +#endif +} + +void nsgtk_entry_set_icon_from_pixbuf(GtkWidget *entry, + GtkEntryIconPosition icon_pos, + GdkPixbuf *pixbuf) +{ +#if GTK_CHECK_VERSION(2,16,0) + gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(entry), icon_pos, pixbuf); +#else + GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixbuf(pixbuf)); + + if (image != NULL) { + sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(entry), + (SexyIconEntryPosition)icon_pos, + image); + + g_object_unref(image); + } + +#endif +} + + +/* exported interface documented in gtk/compat.h */ +void nsgtk_entry_set_icon_from_stock(GtkWidget *entry, + GtkEntryIconPosition icon_pos, + const gchar *id) +{ +#ifdef NSGTK_USE_ICON_NAME + gtk_entry_set_icon_from_icon_name(GTK_ENTRY(entry), icon_pos, id); +#else +#if GTK_CHECK_VERSION(2,16,0) + gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), icon_pos, id); +#else + GtkImage *image = GTK_IMAGE(gtk_image_new_from_stock(id, + GTK_ICON_SIZE_LARGE_TOOLBAR)); + + if (image != NULL) { + sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(entry), + (SexyIconEntryPosition)icon_pos, + image); + g_object_unref(image); + } +#endif +#endif +} + + +/* exported interface documented in gtk/compat.h */ +GtkWidget *nsgtk_image_new_from_stock(const gchar *id, GtkIconSize size) +{ +#ifdef NSGTK_USE_ICON_NAME + return gtk_image_new_from_icon_name(id, size); +#else + return gtk_image_new_from_stock(id, size); +#endif +} + + +/* exported interface documented in gtk/compat.h */ +GtkWidget *nsgtk_button_new_from_stock(const gchar *stock_id) +{ +#ifdef NSGTK_USE_ICON_NAME + return gtk_button_new_with_label(stock_id); +#else + return gtk_button_new_from_stock(stock_id); +#endif +} + +/* exported interface documented in gtk/compat.h */ +gboolean nsgtk_stock_lookup(const gchar *stock_id, GtkStockItem *item) +{ +#ifdef NSGTK_USE_ICON_NAME + return FALSE; +#else + return gtk_stock_lookup(stock_id, item); +#endif +} + + +void nsgtk_widget_override_background_color(GtkWidget *widget, + GtkStateFlags state, + uint16_t a, + uint16_t r, + uint16_t g, + uint16_t b) +{ +#if GTK_CHECK_VERSION(3,0,0) + GdkRGBA colour; + colour.alpha = (double)a / 0xffff; + colour.red = (double)r / 0xffff; + colour.green = (double)g / 0xffff; + colour.blue = (double)b / 0xffff; + gtk_widget_override_background_color(widget, state, &colour); +#else + GdkColor colour; + colour.pixel = a; + colour.red = r; + colour.green = g; + colour.blue = b; + gtk_widget_modify_bg(widget, state, &colour ); +#endif +} + +GtkAdjustment *nsgtk_layout_get_vadjustment(GtkLayout *layout) +{ +#if GTK_CHECK_VERSION(3,0,0) + return gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(layout)); +#else + return gtk_layout_get_vadjustment(layout); +#endif +} + +GtkAdjustment *nsgtk_layout_get_hadjustment(GtkLayout *layout) +{ +#if GTK_CHECK_VERSION(3,0,0) + return gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(layout)); +#else + return gtk_layout_get_hadjustment(layout); +#endif +} + +static void nsgtk_layout_set_adjustment_step_increment(GtkAdjustment *adj, + int value) +{ +#if GTK_CHECK_VERSION(2,14,0) + gtk_adjustment_set_step_increment(adj, value); +#else + adj->step_increment = value; +#endif +} + +void nsgtk_layout_set_hadjustment(GtkLayout *layout, GtkAdjustment *adj) +{ +#if GTK_CHECK_VERSION(3,0,0) + gtk_scrollable_set_hadjustment(GTK_SCROLLABLE(layout), adj); +#else + gtk_layout_set_hadjustment(layout, adj); +#endif + nsgtk_layout_set_adjustment_step_increment(adj, 8); +} + +void nsgtk_layout_set_vadjustment(GtkLayout *layout, GtkAdjustment *adj) +{ +#if GTK_CHECK_VERSION(3,0,0) + gtk_scrollable_set_vadjustment(GTK_SCROLLABLE(layout), adj); +#else + gtk_layout_set_vadjustment(layout, adj); +#endif + nsgtk_layout_set_adjustment_step_increment(adj, 8); +} + +GtkWidget *nsgtk_hbox_new(gboolean homogeneous, gint spacing) +{ +#if GTK_CHECK_VERSION(3,0,0) + return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing); +#else + return gtk_hbox_new(homogeneous, spacing); +#endif +} + +GtkWidget *nsgtk_vbox_new(gboolean homogeneous, gint spacing) +{ +#if GTK_CHECK_VERSION(3,0,0) + return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing); +#else + return gtk_vbox_new(homogeneous, spacing); +#endif +} + +GtkStateFlags nsgtk_widget_get_state_flags(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(3,0,0) + return gtk_widget_get_state_flags(widget); +#else +#if GTK_CHECK_VERSION(2,18,0) + return gtk_widget_get_state(widget); +#else + return 0; /* FIXME */ +#endif +#endif +} + +GtkStyleContext *nsgtk_widget_get_style_context(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(3,0,0) + return gtk_widget_get_style_context(widget); +#else + return widget->style; +#endif +} + +const PangoFontDescription* nsgtk_style_context_get_font(GtkStyleContext *style, + GtkStateFlags state) +{ +#if GTK_CHECK_VERSION(3,8,0) + const PangoFontDescription* fontdesc = NULL; + gtk_style_context_get(style, state, GTK_STYLE_PROPERTY_FONT, &fontdesc, NULL); + return fontdesc; +#else +#if GTK_CHECK_VERSION(3,0,0) + return gtk_style_context_get_font(style, state); +#else + return style->font_desc; +#endif +#endif +} + +gulong nsgtk_connect_draw_event(GtkWidget *widget, + GCallback callback, + gpointer g) +{ +#if GTK_CHECK_VERSION(3,0,0) + return g_signal_connect(G_OBJECT(widget), "draw", callback, g); +#else + return g_signal_connect(G_OBJECT(widget), "expose_event", callback, g); +#endif +} + +void nsgdk_cursor_unref(GdkCursor *cursor) +{ +#if GTK_CHECK_VERSION(3,0,0) + g_object_unref(cursor); +#else + gdk_cursor_unref(cursor); +#endif +} + +void nsgtk_widget_modify_font(GtkWidget *widget, + PangoFontDescription *font_desc) +{ +#if GTK_CHECK_VERSION(3,0,0) +/* FIXME */ + return; +#else + gtk_widget_modify_font(widget, font_desc); +#endif +} + +GdkWindow *nsgtk_widget_get_window(GtkWidget *widget) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_widget_get_window(widget); +#else + return widget->window; +#endif +} + +GtkWidget *nsgtk_dialog_get_content_area(GtkDialog *dialog) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_dialog_get_content_area(dialog); +#else + return dialog->vbox; +#endif +} + +gboolean nsgtk_show_uri(GdkScreen *screen, + const gchar *uri, + guint32 timestamp, + GError **error) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_show_uri(screen, uri, timestamp, error); +#else + return FALSE; /* FIXME */ +#endif +} + +GdkWindow *nsgtk_layout_get_bin_window(GtkLayout *layout) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_layout_get_bin_window(layout); +#else + return layout->bin_window; +#endif +} + +gdouble nsgtk_adjustment_get_step_increment(GtkAdjustment *adjustment) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_adjustment_get_step_increment(adjustment); +#else + return adjustment->step_increment; +#endif +} + +gdouble nsgtk_adjustment_get_upper(GtkAdjustment *adjustment) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_adjustment_get_upper(adjustment); +#else + return adjustment->upper; +#endif +} + +gdouble nsgtk_adjustment_get_lower(GtkAdjustment *adjustment) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_adjustment_get_lower(adjustment); +#else + return adjustment->lower; +#endif +} + +gdouble nsgtk_adjustment_get_page_increment(GtkAdjustment *adjustment) +{ +#if GTK_CHECK_VERSION(2,14,0) + return gtk_adjustment_get_page_increment(adjustment); +#else + return adjustment->page_increment; +#endif +} + +void nsgtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation) +{ +#if GTK_CHECK_VERSION(2,18,0) + gtk_widget_get_allocation(widget, allocation); +#else + allocation->x = widget->allocation.x; + allocation->y = widget->allocation.y; + allocation->width = widget->allocation.width; + allocation->height = widget->allocation.height; +#endif +} + +/* exported interface documented in gtk/compat.h */ +GtkWidget *nsgtk_image_new_from_pixbuf_icon(GdkPixbuf *pixbuf, GtkIconSize size) +{ +#if GTK_CHECK_VERSION(3,10,0) + return gtk_image_new_from_pixbuf(pixbuf); +#else + GtkIconSet *icon_set; + GtkWidget *image; + + icon_set = gtk_icon_set_new_from_pixbuf(pixbuf); + + image = gtk_image_new_from_icon_set(icon_set, size); + + gtk_icon_set_unref(icon_set); + + return image; +#endif +} + + +/* exported interface documented in gtk/compat.h */ +void nsgtk_window_set_opacity(GtkWindow *window, gdouble opacity) +{ +#if GTK_CHECK_VERSION(3,8,0) + gtk_widget_set_opacity(GTK_WIDGET(window), opacity); +#else + gtk_window_set_opacity(window, opacity); +#endif +} + +/* exported interface documented in gtk/compat.h */ +void nsgtk_scrolled_window_add_with_viewport(GtkScrolledWindow *window, + GtkWidget *child) +{ +#if GTK_CHECK_VERSION(3,8,0) + gtk_container_add(GTK_CONTAINER(window), child); +#else + gtk_scrolled_window_add_with_viewport(window, child); +#endif +} + +/* exported interface documented in gtk/compat.h */ +GtkWidget *nsgtk_image_menu_item_new_with_mnemonic(const gchar *label) +{ +#if GTK_CHECK_VERSION(3,10,0) + return gtk_menu_item_new_with_mnemonic(label); +#else + return gtk_image_menu_item_new_with_mnemonic(label); +#endif +} + +/* exported interface documented in gtk/compat.h */ +void nsgtk_image_menu_item_set_image(GtkWidget *image_menu_item, GtkWidget *image) +{ +#if !GTK_CHECK_VERSION(3,10,0) + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(image_menu_item), image); +#endif +} + +/* exported interface documented in gtk/compat.h */ +gboolean nsgtk_icon_size_lookup_for_settings(GtkSettings *settings, + GtkIconSize size, + gint *width, + gint *height) +{ +#if GTK_CHECK_VERSION(3,10,0) + return gtk_icon_size_lookup(size, width, height); +#else + return gtk_icon_size_lookup_for_settings(settings, size, width, height); +#endif +} + +/* exported interface documented in gtk/compat.h */ +void nsgtk_widget_set_alignment(GtkWidget *widget, GtkAlign halign, GtkAlign valign) +{ +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_set_halign(widget, halign); + gtk_widget_set_valign(widget, valign); +#else + gfloat x, y; + switch(halign) { + case GTK_ALIGN_START: + x = 0.0; + break; + + case GTK_ALIGN_END: + x = 1.0; + break; + + default: + x = 0.5; + break; + } + + switch(valign) { + case GTK_ALIGN_START: + y = 0.0; + break; + + case GTK_ALIGN_END: + y = 1.0; + break; + + default: + y = 0.5; + break; + } + + gtk_misc_set_alignment(GTK_MISC(widget), x, y); +#endif +} + +/* exported interface documented in gtk/compat.h */ +void nsgtk_widget_set_margins(GtkWidget *widget, gint hmargin, gint vmargin) +{ +#if GTK_CHECK_VERSION(3,0,0) +#if GTK_CHECK_VERSION(3,12,0) + gtk_widget_set_margin_start(widget, hmargin); + gtk_widget_set_margin_end(widget, hmargin); +#else + gtk_widget_set_margin_left(widget, hmargin); + gtk_widget_set_margin_right(widget, hmargin); +#endif + gtk_widget_set_margin_top(widget, vmargin); + gtk_widget_set_margin_bottom(widget, vmargin); +#else + gtk_misc_set_padding(GTK_MISC(widget), hmargin, vmargin); +#endif +} + +/* exported interface documented in gtk/compat.h */ +guint +nsgtk_builder_add_from_resource(GtkBuilder *builder, + const gchar *resource_path, + GError **error) +{ + guint ret; + +#ifdef WITH_GRESOURCE +#if GTK_CHECK_VERSION(3,4,0) + ret = gtk_builder_add_from_resource(builder, resource_path, error); +#else + GBytes *data; + const gchar *buffer; + gsize buffer_length; + + g_assert(error && *error == NULL); + + data = g_resources_lookup_data(resource_path, 0, error); + if (data == NULL) { + return 0; + } + + buffer_length = 0; + buffer = g_bytes_get_data(data, &buffer_length); + g_assert(buffer != NULL); + + ret = gtk_builder_add_from_string(builder, buffer, buffer_length, error); + + g_bytes_unref(data); +#endif +#else + ret = 0; /* return an error as GResource not supported before GLIB 2.32 */ +#endif + return ret; +} diff --git a/frontends/gtk/compat.h b/frontends/gtk/compat.h new file mode 100644 index 000000000..9554b0cba --- /dev/null +++ b/frontends/gtk/compat.h @@ -0,0 +1,296 @@ +/* + * Copyright 2010 Rob Kendrick + * + * 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 . + */ + +/** \file + * Compatibility functions for older GTK versions (interface) + */ + +#ifndef NETSURF_GTK_COMPAT_H_ +#define NETSURF_GTK_COMPAT_H_ + +#include + +#include + +/* gtk 3.10 depricated the use of stock names */ +#if GTK_CHECK_VERSION(3,10,0) +#define NSGTK_USE_ICON_NAME +#else +#undef NSGTK_USE_ICON_NAME +#endif + +/* icon names instead of stock */ +#ifdef NSGTK_USE_ICON_NAME +#define NSGTK_STOCK_ADD "list-add" +#define NSGTK_STOCK_CANCEL "_Cancel" +#define NSGTK_STOCK_CLEAR "edit-clear" +#define NSGTK_STOCK_CLOSE "window-close" +#define NSGTK_STOCK_FIND "edit-find" +#define NSGTK_STOCK_GO_BACK "go-previous" +#define NSGTK_STOCK_GO_FORWARD "go-next" +#define NSGTK_STOCK_HOME "go-home" +#define NSGTK_STOCK_INFO "dialog-information" +#define NSGTK_STOCK_REFRESH "view-refresh" +#define NSGTK_STOCK_SAVE "document-save" +#define NSGTK_STOCK_SAVE_AS "document-save-as" +#define NSGTK_STOCK_STOP "process-stop" +#define NSGTK_STOCK_OK "_OK" +#define NSGTK_STOCK_OPEN "_Open" +#else +#define NSGTK_STOCK_ADD GTK_STOCK_ADD +#define NSGTK_STOCK_CANCEL GTK_STOCK_CANCEL +#define NSGTK_STOCK_CLEAR GTK_STOCK_CLEAR +#define NSGTK_STOCK_CLOSE GTK_STOCK_CLOSE +#define NSGTK_STOCK_FIND GTK_STOCK_FIND +#define NSGTK_STOCK_GO_BACK GTK_STOCK_GO_BACK +#define NSGTK_STOCK_GO_FORWARD GTK_STOCK_GO_FORWARD +#define NSGTK_STOCK_HOME GTK_STOCK_HOME +#define NSGTK_STOCK_INFO GTK_STOCK_INFO +#define NSGTK_STOCK_REFRESH GTK_STOCK_REFRESH +#define NSGTK_STOCK_SAVE GTK_STOCK_SAVE +#define NSGTK_STOCK_SAVE_AS GTK_STOCK_SAVE_AS +#define NSGTK_STOCK_STOP GTK_STOCK_STOP +#define NSGTK_STOCK_OK GTK_STOCK_OK +#define NSGTK_STOCK_OPEN GTK_STOCK_OPEN +#endif + +/* widget alignment only available since 3.0 */ +#if !GTK_CHECK_VERSION(3,0,0) +typedef enum { + GTK_ALIGN_FILL, + GTK_ALIGN_START, + GTK_ALIGN_END, + GTK_ALIGN_CENTER, + GTK_ALIGN_BASELINE +} GtkAlign; +#endif + +/** + * Set the alignment of a widget. + * + * sets both the horizontal and vertical alignement of a widget + * + * @note this type of alignemnt was not available prior to GTK 3.0 so + * we emulate it using gtk_misc_set_alignment. + * + * \param widget The widget to set alignent on. + * \param halign The horizontal alignment to set. + * \param valign The vertical alignment to set + */ +void nsgtk_widget_set_alignment(GtkWidget *widget, GtkAlign halign, GtkAlign valign); + +/** + * Set the margins of a widget + * + * Sets the margin all round a widget. + * + * @note this type of margin was not available prior to GTK 3.0 so + * we emulate it using gtk_misc_set_padding. + * + * \param widget The widget to set alignent on. + * \param hmargin The horizontal margin. + * \param vmargin The vertical margin. + */ +void nsgtk_widget_set_margins(GtkWidget *widget, gint hmargin, gint vmargin); + +void nsgtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus); +gboolean nsgtk_widget_has_focus(GtkWidget *widget); +gboolean nsgtk_widget_get_visible(GtkWidget *widget); +gboolean nsgtk_widget_get_realized(GtkWidget *widget); +gboolean nsgtk_widget_get_mapped(GtkWidget *widget); +gboolean nsgtk_widget_is_drawable(GtkWidget *widget); +void nsgtk_dialog_set_has_separator(GtkDialog *dialog, gboolean setting); +GtkWidget *nsgtk_combo_box_text_new(void); +void nsgtk_combo_box_text_append_text(GtkWidget *combo_box, const gchar *text); +gchar *nsgtk_combo_box_text_get_active_text(GtkWidget *combo_box); + +/** + * creates a new image widget of an appropriate icon size from a pixbuf. + * + * \param pixbuf The pixbuf to use as a source. + * \param size The size of icon to create + * \return An image widget. + */ +GtkWidget *nsgtk_image_new_from_pixbuf_icon(GdkPixbuf *pixbuf, GtkIconSize size); + +/* GTK prior to 2.16 needs the sexy interface for icons */ +#if !GTK_CHECK_VERSION(2,16,0) + +#include "gtk/sexy_icon_entry.h" + +typedef enum { + GTK_ENTRY_ICON_PRIMARY = SEXY_ICON_ENTRY_PRIMARY, + GTK_ENTRY_ICON_SECONDARY = SEXY_ICON_ENTRY_SECONDARY +} GtkEntryIconPosition; + +GtkStateType nsgtk_widget_get_state(GtkWidget *widget); + +#endif + +#if GTK_CHECK_VERSION (2, 90, 7) +#define GDK_KEY(symbol) GDK_KEY_##symbol +#else +#include +#define GDK_KEY(symbol) GDK_##symbol +#endif + +#if !GTK_CHECK_VERSION(3,0,0) +typedef GtkStateType GtkStateFlags; +typedef GtkStyle GtkStyleContext; + +#if GTK_CHECK_VERSION(2,22,0) +enum { + GTK_IN_DESTRUCTION = 1 << 0, +}; +#define GTK_OBJECT_FLAGS(obj) (GTK_OBJECT (obj)->flags) +#endif + +#define gtk_widget_in_destruction(widget) \ + (GTK_OBJECT_FLAGS(GTK_OBJECT(widget)) & GTK_IN_DESTRUCTION) + +#endif + + +/** + * Sets the icon shown in the entry at the specified position from a + * stock image. + * + * Compatability interface for original deprecated in GTK 3.10 + * + * \param entry The entry widget to set the icon on. + * \param icon_pos The position of the icon. + * \param stock_id the name of the stock item. + */ +void nsgtk_entry_set_icon_from_stock(GtkWidget *entry, GtkEntryIconPosition icon_pos, const gchar *stock_id); + +/** + * Creates a GtkImage displaying a stock icon. + * + * Compatability interface for original deprecated in GTK 3.10 + * + * \param stock_id the name of the stock item. + * \param size The size of icon to create. + * \return The created image widget or NULL on error + */ +GtkWidget *nsgtk_image_new_from_stock(const gchar *stock_id, GtkIconSize size); + +/** + * Creates a new GtkButton containing the image and text from a stock item. + * + * Compatability interface for original deprecated in GTK 3.10 + * + * \param stock_id the name of the stock item + */ +GtkWidget *nsgtk_button_new_from_stock(const gchar *stock_id); + +/** + * Fills item with the registered values for stock_id. + * + * Compatability interface for original deprecated in GTK 3.10 + * + * \param stock_id the name of the stock item. + * \param item The structure to update if the stock_id was known. + * \return TRUE if stock_id was known. + */ +gboolean nsgtk_stock_lookup(const gchar *stock_id, GtkStockItem *item); + +void nsgtk_window_set_opacity(GtkWindow *window, gdouble opacity); + +void nsgtk_scrolled_window_add_with_viewport(GtkScrolledWindow *window, GtkWidget *child); + +GtkWidget *nsgtk_entry_new(void); + +void nsgtk_entry_set_icon_from_pixbuf(GtkWidget *entry, GtkEntryIconPosition icon_pos, GdkPixbuf *pixbuf); + +void nsgtk_widget_override_background_color(GtkWidget *widget, GtkStateFlags state, uint16_t a, uint16_t r, uint16_t g, uint16_t b); +GtkWidget* nsgtk_hbox_new(gboolean homogeneous, gint spacing); +GtkWidget* nsgtk_vbox_new(gboolean homogeneous, gint spacing); +GtkStateFlags nsgtk_widget_get_state_flags(GtkWidget *widget); +GtkStyleContext* nsgtk_widget_get_style_context(GtkWidget *widget); +const PangoFontDescription* nsgtk_style_context_get_font(GtkStyleContext *style, GtkStateFlags state); +gulong nsgtk_connect_draw_event(GtkWidget *widget, GCallback callback, gpointer g); +void nsgdk_cursor_unref(GdkCursor *cursor); +void nsgtk_widget_modify_font(GtkWidget *widget, PangoFontDescription *font_desc); +GdkWindow *nsgtk_widget_get_window(GtkWidget *widget); +GtkWidget *nsgtk_dialog_get_content_area(GtkDialog *dialog); +gboolean nsgtk_show_uri(GdkScreen *screen, const gchar *uri, guint32 timestamp, GError **error); +GdkWindow *nsgtk_layout_get_bin_window(GtkLayout *layout); +void nsgtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation); + +gboolean nsgtk_icon_size_lookup_for_settings (GtkSettings *settings, GtkIconSize size, gint *width, gint *height); + +GtkAdjustment *nsgtk_layout_get_vadjustment(GtkLayout *layout); +GtkAdjustment *nsgtk_layout_get_hadjustment(GtkLayout *layout); +void nsgtk_layout_set_hadjustment(GtkLayout *layout, GtkAdjustment *adj); +void nsgtk_layout_set_vadjustment(GtkLayout *layout, GtkAdjustment *adj); +gdouble nsgtk_adjustment_get_step_increment(GtkAdjustment *adjustment); +gdouble nsgtk_adjustment_get_upper(GtkAdjustment *adjustment); +gdouble nsgtk_adjustment_get_lower(GtkAdjustment *adjustment); +gdouble nsgtk_adjustment_get_page_increment(GtkAdjustment *adjustment); + +/* menu compatability */ + +/** + * Creates a new GtkImageMenuItem containing a label. + * + * Compatability interface for original deprecated in GTK 3.10. + * @note post 3.10 this creates a GtkMenuItem. + * + * \param label The text of the button, with an underscore in front of + * the mnemonic character. + * \return a new GtkMenuItem + */ +GtkWidget *nsgtk_image_menu_item_new_with_mnemonic(const gchar *label); + +/** + * Sets the image of image_menu_item to the given widget. + * + * Compatability interface for original deprecated in GTK 3.10. + * @note post 3.10 this is empty as menu creation generates GtkMenuItem. + * + * \param image_menu_item The image menu entry item. + * \param image The image to set. + */ +void nsgtk_image_menu_item_set_image(GtkWidget *image_menu_item, GtkWidget *image); + + +/** + * Parses a resource file containing a GtkBuilder UI definition and + * merges it with the current contents of builder. + * + * Compatability interface as this did not exist prior to GTK 3.4 + * + * GTK prior to 3.4 can have the resources in a GResource but + * gtk_builder cannot directly instantiate from them + * + * GTK 3.4 onwards can use gtk_builder_add_from_resource() to add + * directly from resources. The gtk_builder_new_ type operations + * cannot be used because they are only available post 3.10 and handle + * all errors by aborting the application + * + * @note prior to GLIB 2.32 resources did not exist and this wrapper + * returns the error code. + * + * \param builder a GtkBuilder + * \param resource_path the path of the resource file to parse + * \param error return location for an error, or NULL. + * \return A positive value on success, 0 if an error occurred. + */ +guint nsgtk_builder_add_from_resource(GtkBuilder *builder, const gchar *resource_path, GError **error); + +#endif /* NETSURF_GTK_COMPAT_H */ diff --git a/frontends/gtk/completion.c b/frontends/gtk/completion.c new file mode 100644 index 000000000..6dde728b3 --- /dev/null +++ b/frontends/gtk/completion.c @@ -0,0 +1,157 @@ +/* + * Copyright 2006 Rob Kendrick + * + * 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 . + */ + +/** + * \file + * Implementation of url entry completion. + */ + +#include + +#include "utils/log.h" +#include "utils/messages.h" +#include "utils/nsoption.h" +#include "content/urldb.h" +#include "desktop/searchweb.h" +#include "desktop/browser.h" + +#include "gtk/warn.h" +#include "gtk/window.h" +#include "gtk/completion.h" + +GtkListStore *nsgtk_completion_list; + +/** + * completion row matcher + */ +static gboolean nsgtk_completion_match(GtkEntryCompletion *completion, + const gchar *key, + GtkTreeIter *iter, + gpointer user_data) +{ + /* the completion list is modified to only contain valid + * entries so this simply returns TRUE to indicate all rows + * are in the list should be shown. + */ + return TRUE; + +} + + +/** + * callback for each entry to add to completion list + */ +static bool +nsgtk_completion_udb_callback(nsurl *url, const struct url_data *data) +{ + GtkTreeIter iter; + + if (data->visits != 0) { + gtk_list_store_append(nsgtk_completion_list, &iter); + gtk_list_store_set(nsgtk_completion_list, &iter, 0, + nsurl_access(url), -1); + } + return true; +} + +/** + * event handler for when a completion suggestion is selected. + */ +static gboolean +nsgtk_completion_match_select(GtkEntryCompletion *widget, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + GValue value = {0, }; + struct nsgtk_scaffolding *g = user_data; + struct browser_window *bw = nsgtk_get_browser_window(nsgtk_scaffolding_top_level(g)); + nserror ret; + nsurl *url; + + gtk_tree_model_get_value(model, iter, 0, &value); + + ret = search_web_omni(g_value_get_string(&value), + SEARCH_WEB_OMNI_NONE, + &url); + + g_value_unset(&value); + + if (ret == NSERROR_OK) { + ret = browser_window_navigate(bw, + url, NULL, BW_NAVIGATE_HISTORY, + NULL, NULL, NULL); + nsurl_unref(url); + } + if (ret != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(ret), 0); + } + + return TRUE; +} + +/* exported interface documented in completion.h */ +void nsgtk_completion_init(void) +{ + nsgtk_completion_list = gtk_list_store_new(1, G_TYPE_STRING); + +} + +/* exported interface documented in completion.h */ +gboolean nsgtk_completion_update(GtkEntry *entry) +{ + gtk_list_store_clear(nsgtk_completion_list); + + if (nsoption_bool(url_suggestion) == true) { + urldb_iterate_partial(gtk_entry_get_text(entry), + nsgtk_completion_udb_callback); + } + + return TRUE; +} + +/* exported interface documented in completion.h */ +GtkEntryCompletion *nsgtk_url_entry_completion_new(struct nsgtk_scaffolding *gs) +{ + GtkEntryCompletion *completion; + + completion = gtk_entry_completion_new(); + gtk_entry_completion_set_match_func(completion, + nsgtk_completion_match, NULL, NULL); + + gtk_entry_completion_set_model(completion, + GTK_TREE_MODEL(nsgtk_completion_list)); + + gtk_entry_completion_set_text_column(completion, 0); + + gtk_entry_completion_set_minimum_key_length(completion, 1); + + /* enable popup for completion */ + gtk_entry_completion_set_popup_completion(completion, TRUE); + + /* when selected callback */ + g_signal_connect(G_OBJECT(completion), "match-selected", + G_CALLBACK(nsgtk_completion_match_select), gs); + + g_object_set(G_OBJECT(completion), + "popup-set-width", TRUE, + "popup-single-match", TRUE, + NULL); + + return completion; +} diff --git a/frontends/gtk/completion.h b/frontends/gtk/completion.h new file mode 100644 index 000000000..9a1db293d --- /dev/null +++ b/frontends/gtk/completion.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 Rob Kendrick + * + * 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 . + */ + +/** + * \file + * Interface to url entry completion. + */ + +#ifndef _NETSURF_GTK_COMPLETION_H_ +#define _NETSURF_GTK_COMPLETION_H_ + +struct nsgtk_scaffolding; + +/** + * initialise completion list store + */ +void nsgtk_completion_init(void); + +/** + * update completion list store. + */ +gboolean nsgtk_completion_update(GtkEntry *entry); + +/** + * create a new entry completion on a scaffold. + * + * \param gs The scaffoliding which the url entry is in. + */ +GtkEntryCompletion *nsgtk_url_entry_completion_new(struct nsgtk_scaffolding *gs); + +#endif diff --git a/frontends/gtk/cookies.c b/frontends/gtk/cookies.c new file mode 100644 index 000000000..f8f989347 --- /dev/null +++ b/frontends/gtk/cookies.c @@ -0,0 +1,219 @@ +/* + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +/** \file + * Cookies (implementation). + */ + +#include + +#include "utils/log.h" +#include "desktop/cookie_manager.h" +#include "desktop/plot_style.h" +#include "desktop/tree.h" +#include "desktop/textinput.h" + +#include "gtk/cookies.h" +#include "gtk/plotters.h" +#include "gtk/scaffolding.h" +#include "gtk/treeview.h" +#include "gtk/resources.h" + +#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \ + GtkMenuItem *widget, gpointer g) +#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) } +#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \ + gpointer g) + +struct menu_events { + const char *widget; + GCallback handler; +}; + +/* edit menu */ +MENUPROTO(delete_selected); +MENUPROTO(delete_all); +MENUPROTO(select_all); +MENUPROTO(clear_selection); + +/* view menu*/ +MENUPROTO(expand_all); +MENUPROTO(expand_domains); +MENUPROTO(expand_cookies); +MENUPROTO(collapse_all); +MENUPROTO(collapse_domains); +MENUPROTO(collapse_cookies); + + +static struct menu_events menu_events[] = { + + /* edit menu */ + MENUEVENT(delete_selected), + MENUEVENT(delete_all), + MENUEVENT(select_all), + MENUEVENT(clear_selection), + + /* view menu*/ + MENUEVENT(expand_all), + MENUEVENT(expand_domains), + MENUEVENT(expand_cookies), + MENUEVENT(collapse_all), + MENUEVENT(collapse_domains), + MENUEVENT(collapse_cookies), + + {NULL, NULL} +}; + +static struct nsgtk_treeview *cookies_treeview; +static GtkBuilder *cookie_builder; +GtkWindow *wndCookies; + +/** + * Connects menu events in the cookies window. + */ +static void nsgtk_cookies_init_menu(void) +{ + struct menu_events *event = menu_events; + GtkWidget *w; + + while (event->widget != NULL) { + w = GTK_WIDGET(gtk_builder_get_object(cookie_builder, event->widget)); + if (w == NULL) { + LOG("Unable to connect menu widget ""%s""", event->widget); } else { + g_signal_connect(G_OBJECT(w), "activate", event->handler, cookies_treeview); + } + event++; + } +} + +/* exported interface documented in gtk/cookies.h */ +nserror nsgtk_cookies_init(void) +{ + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + nserror res; + + res = nsgtk_builder_new_from_resname("cookies", &cookie_builder); + if (res != NSERROR_OK) { + LOG("Cookie UI builder init failed"); + return res; + } + + gtk_builder_connect_signals(cookie_builder, NULL); + + wndCookies = GTK_WINDOW(gtk_builder_get_object(cookie_builder, + "wndCookies")); + + scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(cookie_builder, + "cookiesScrolled")); + + drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(cookie_builder, + "cookiesDrawingArea")); + + cookies_treeview = nsgtk_treeview_create(TREE_COOKIES, + wndCookies, + scrolled, + drawing_area); + if (cookies_treeview == NULL) { + return NSERROR_INIT_FAILED; + } + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(wndCookies, "delete_event", gtk_widget_hide_on_delete, NULL); + CONNECT(wndCookies, "hide", nsgtk_tree_window_hide, cookies_treeview); + + nsgtk_cookies_init_menu(); + + return NSERROR_OK; +} + + +/** + * Destroys the cookies window and performs any other necessary cleanup actions. + */ +void nsgtk_cookies_destroy(void) +{ + /** \todo what about cookie_builder? */ + nsgtk_treeview_destroy(cookies_treeview); +} + + +/* edit menu */ +MENUHANDLER(delete_selected) +{ + cookie_manager_keypress(NS_KEY_DELETE_LEFT); + return TRUE; +} + +MENUHANDLER(delete_all) +{ + cookie_manager_keypress(NS_KEY_SELECT_ALL); + cookie_manager_keypress(NS_KEY_DELETE_LEFT); + return TRUE; +} + +MENUHANDLER(select_all) +{ + cookie_manager_keypress(NS_KEY_SELECT_ALL); + return TRUE; +} + +MENUHANDLER(clear_selection) +{ + cookie_manager_keypress(NS_KEY_CLEAR_SELECTION); + return TRUE; +} + +/* view menu*/ +MENUHANDLER(expand_all) +{ + cookie_manager_expand(false); + return TRUE; +} + +MENUHANDLER(expand_domains) +{ + cookie_manager_expand(true); + return TRUE; +} + +MENUHANDLER(expand_cookies) +{ + cookie_manager_expand(false); + return TRUE; +} + +MENUHANDLER(collapse_all) +{ + cookie_manager_contract(true); + return TRUE; +} + +MENUHANDLER(collapse_domains) +{ + cookie_manager_contract(true); + return TRUE; +} + +MENUHANDLER(collapse_cookies) +{ + cookie_manager_contract(false); + return TRUE; +} diff --git a/frontends/gtk/cookies.h b/frontends/gtk/cookies.h new file mode 100644 index 000000000..2d5c56d52 --- /dev/null +++ b/frontends/gtk/cookies.h @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +/** \file + * Cookies (interface). + */ + +#ifndef __NSGTK_COOKIES_H__ +#define __NSGTK_COOKIES_H__ + +extern GtkWindow *wndCookies; + +/** + * Creates the window for the cookies tree. + * + * \return NSERROR_OK on success else appropriate error code on faliure. + */ +nserror nsgtk_cookies_init(void); + +void nsgtk_cookies_destroy(void); + +#endif /* __NSGTK_COOKIES_H__ */ diff --git a/frontends/gtk/download.c b/frontends/gtk/download.c new file mode 100644 index 000000000..b7eea2584 --- /dev/null +++ b/frontends/gtk/download.c @@ -0,0 +1,865 @@ +/* + * Copyright 2008 Michael Lester + * + * 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 . + */ + +#include +#include +#include + +#include +#include + +#include "utils/log.h" +#include "utils/utils.h" +#include "utils/nsurl.h" +#include "utils/messages.h" +#include "utils/nsoption.h" +#include "utils/string.h" +#include "desktop/download.h" +#include "desktop/gui_download.h" + +#include "gtk/warn.h" +#include "gtk/scaffolding.h" +#include "gtk/window.h" +#include "gtk/compat.h" +#include "gtk/resources.h" +#include "gtk/download.h" + +#define UPDATE_RATE 500 /* In milliseconds */ + +struct download_context; + +enum { + NSGTK_DOWNLOAD_PROGRESS, + NSGTK_DOWNLOAD_INFO, + NSGTK_DOWNLOAD_REMAINING, + NSGTK_DOWNLOAD_SPEED, + NSGTK_DOWNLOAD_PULSE, + NSGTK_DOWNLOAD_STATUS, + NSGTK_DOWNLOAD, + + NSGTK_DOWNLOAD_N_COLUMNS +}; + +typedef enum { + NSGTK_DOWNLOAD_NONE, + NSGTK_DOWNLOAD_WORKING, + NSGTK_DOWNLOAD_ERROR, + NSGTK_DOWNLOAD_COMPLETE, + NSGTK_DOWNLOAD_CANCELED +} nsgtk_download_status; + +typedef enum { + NSGTK_DOWNLOAD_PAUSE = 1 << 0, + NSGTK_DOWNLOAD_RESUME = 1 << 1, + NSGTK_DOWNLOAD_CANCEL = 1 << 2, + NSGTK_DOWNLOAD_CLEAR = 1 << 3 +} nsgtk_download_actions; + +struct gui_download_window { + struct download_context *ctx; + nsgtk_download_actions sensitivity; + nsgtk_download_status status; + + GString *name; + GString *time_left; + gint size_total; + gint size_downloaded; + gint progress; + gfloat time_remaining; + gfloat start_time; + gfloat speed; + + GtkTreeRowReference *row; + GIOChannel *write; + GError *error; +}; + +typedef void (*nsgtk_download_selection_action)(struct gui_download_window *dl); + +static GtkWindow *nsgtk_download_window, *nsgtk_download_parent; +static GtkProgressBar *nsgtk_download_progress_bar; + +static GtkTreeView *nsgtk_download_tree; +static GtkListStore *nsgtk_download_store; +static GtkTreeSelection *nsgtk_download_selection; +static GtkTreeIter nsgtk_download_iter; + +static GTimer *nsgtk_downloads_timer; +static GList *nsgtk_downloads_list; +static GtkButton *nsgtk_download_button_pause; +static GtkButton *nsgtk_download_button_clear; +static GtkButton *nsgtk_download_button_cancel; +static GtkButton *nsgtk_download_button_resume; +static gint nsgtk_downloads_num_active; +static const gchar* status_messages[] = { NULL, "gtkWorking", "gtkError", + "gtkComplete", "gtkCanceled" }; + + + +static GtkTreeView* nsgtk_download_tree_view_new(GtkBuilder *gladeFile) +{ + GtkTreeView *treeview; + GtkCellRenderer *renderer; + + treeview = GTK_TREE_VIEW(gtk_builder_get_object(gladeFile, "treeDownloads")); + + /* Progress column */ + renderer = gtk_cell_renderer_progress_new(); + gtk_tree_view_insert_column_with_attributes (treeview, -1, + messages_get("gtkProgress"), renderer, "value", + NSGTK_DOWNLOAD_PROGRESS, "pulse", NSGTK_DOWNLOAD_PULSE, + "text", NSGTK_DOWNLOAD_STATUS, NULL); + + /* Information column */ + renderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR, + "wrap-width", 300, NULL); + gtk_tree_view_insert_column_with_attributes (treeview, -1, + messages_get("gtkDetails"), renderer, "text", + NSGTK_DOWNLOAD_INFO, NULL); + gtk_tree_view_column_set_expand(gtk_tree_view_get_column(treeview, + NSGTK_DOWNLOAD_INFO), TRUE); + + /* Time remaining column */ + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes (treeview, -1, + messages_get("gtkRemaining"), renderer, "text", + NSGTK_DOWNLOAD_REMAINING, NULL); + + /* Speed column */ + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes (treeview, -1, + messages_get("gtkSpeed"), renderer, "text", + NSGTK_DOWNLOAD_SPEED, NULL); + + return treeview; +} + +static gint +nsgtk_download_sort(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer userdata) +{ + struct gui_download_window *dl1, *dl2; + + gtk_tree_model_get(model, a, NSGTK_DOWNLOAD, &dl1, -1); + gtk_tree_model_get(model, b, NSGTK_DOWNLOAD, &dl2, -1); + + return dl1->status - dl2->status; +} + +static void +nsgtk_download_sensitivity_update_buttons(nsgtk_download_actions sensitivity) +{ + /* Glade seems to pack the buttons in an arbitrary order */ + enum { PAUSE_BUTTON, CLEAR_BUTTON, CANCEL_BUTTON, RESUME_BUTTON }; + + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_pause), + sensitivity & NSGTK_DOWNLOAD_PAUSE); + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_clear), + sensitivity & NSGTK_DOWNLOAD_CLEAR); + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_cancel), + sensitivity & NSGTK_DOWNLOAD_CANCEL); + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_resume), + sensitivity & NSGTK_DOWNLOAD_RESUME); +} + +static void nsgtk_download_sensitivity_evaluate(GtkTreeSelection *selection) +{ + GtkTreeIter iter; + GList *rows; + gboolean selected = gtk_tree_selection_count_selected_rows(selection); + GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store); + nsgtk_download_actions sensitivity = 0; + struct gui_download_window *dl; + + if (selected) { + rows = gtk_tree_selection_get_selected_rows(selection, &model); + while (rows != NULL) { + gtk_tree_model_get_iter(model, &iter, + (GtkTreePath*)rows->data); + gtk_tree_model_get(model, &iter, NSGTK_DOWNLOAD, + &dl, -1); + sensitivity |= dl->sensitivity; + rows = rows->next; + } + } else { + rows = nsgtk_downloads_list; + while (rows != NULL) { + dl = rows->data; + sensitivity |= (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR); + rows = rows->next; + } + } + + + nsgtk_download_sensitivity_update_buttons(sensitivity); +} + +static void nsgtk_download_do(nsgtk_download_selection_action action) +{ + GList *rows, *dls = NULL; + GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store); + gboolean selection_exists = gtk_tree_selection_count_selected_rows( + nsgtk_download_selection); + + if (selection_exists) { + rows = gtk_tree_selection_get_selected_rows( + nsgtk_download_selection, &model); + while (rows != NULL) { + struct gui_download_window *dl; + gtk_tree_model_get_iter(GTK_TREE_MODEL( + nsgtk_download_store), + &nsgtk_download_iter, + (GtkTreePath*)rows->data); + gtk_tree_model_get(GTK_TREE_MODEL(nsgtk_download_store), + &nsgtk_download_iter, NSGTK_DOWNLOAD, + &dl, -1); + dls = g_list_prepend(dls, dl); + + rows = rows->next; + } + g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); + g_list_foreach(rows, (GFunc)g_free, NULL); + g_list_free(rows); + } else + dls = g_list_copy(nsgtk_downloads_list); + + g_list_foreach(dls, (GFunc)action, NULL); + g_list_free(dls); +} + +static gchar* nsgtk_download_info_to_string(struct gui_download_window *dl) +{ + gchar *size_info = g_strdup_printf(messages_get("gtkSizeInfo"), + human_friendly_bytesize(dl->size_downloaded), + dl->size_total == 0 ? messages_get("gtkUnknownSize") : + human_friendly_bytesize(dl->size_total)); + + gchar *r; + + if (dl->status != NSGTK_DOWNLOAD_ERROR) + r = g_strdup_printf("%s\n%s", dl->name->str, size_info); + else + r = g_strdup_printf("%s\n%s", dl->name->str, + dl->error->message); + + g_free(size_info); + + return r; +} + +static gchar* nsgtk_download_time_to_string(gint seconds) +{ + gint hours, minutes; + + if (seconds < 0) + return g_strdup("-"); + + hours = seconds / 3600; + seconds -= hours * 3600; + minutes = seconds / 60; + seconds -= minutes * 60; + + if (hours > 0) + return g_strdup_printf("%u:%02u:%02u", hours, minutes, + seconds); + else + return g_strdup_printf("%u:%02u", minutes, seconds); +} + +static void nsgtk_download_store_update_item (struct gui_download_window *dl) +{ + gchar *info = nsgtk_download_info_to_string(dl); + char *human = human_friendly_bytesize(dl->speed); + char speed[strlen(human) + SLEN("/s") + 1]; + sprintf(speed, "%s/s", human); + gchar *time = nsgtk_download_time_to_string(dl->time_remaining); + gboolean pulse = dl->status == NSGTK_DOWNLOAD_WORKING; + + /* Updates iter (which is needed to set and get data) with the dl row */ + gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store), + &nsgtk_download_iter, + gtk_tree_row_reference_get_path(dl->row)); + + gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter, + NSGTK_DOWNLOAD_PULSE, pulse ? dl->progress : -1, + NSGTK_DOWNLOAD_PROGRESS, pulse ? 0 : dl->progress, + NSGTK_DOWNLOAD_INFO, info, + NSGTK_DOWNLOAD_SPEED, dl->speed == 0 ? "-" : speed, + NSGTK_DOWNLOAD_REMAINING, time, + NSGTK_DOWNLOAD, dl, + -1); + + g_free(info); + g_free(time); +} + +static gboolean nsgtk_download_update(gboolean force_update) +{ + /* Be sure we need to update */ + if (!nsgtk_widget_get_visible(GTK_WIDGET(nsgtk_download_window))) + return TRUE; + + GList *list; + gchar *text; + gboolean update, pulse_mode = FALSE; + gint downloaded = 0, total = 0, dls = 0; + gfloat percent, elapsed = g_timer_elapsed(nsgtk_downloads_timer, NULL); + nsgtk_downloads_num_active = 0; + + for (list = nsgtk_downloads_list; list != NULL; list = list->next) { + struct gui_download_window *dl = list->data; + update = force_update; + + switch (dl->status) { + case NSGTK_DOWNLOAD_WORKING: + pulse_mode = TRUE; + + case NSGTK_DOWNLOAD_NONE: + dl->speed = dl->size_downloaded / + (elapsed - dl->start_time); + if (dl->status == NSGTK_DOWNLOAD_NONE) { + dl->time_remaining = (dl->size_total - + dl->size_downloaded)/ + dl->speed; + dl->progress = (gfloat) + dl->size_downloaded / + dl->size_total * 100; + } else + dl->progress++; + + nsgtk_downloads_num_active++; + update = TRUE; + + case NSGTK_DOWNLOAD_COMPLETE: + downloaded += dl->size_downloaded; + total += dl->size_total; + dls++; + + default: + ;//Do nothing + + } + if (update) + nsgtk_download_store_update_item(dl); + } + + if (pulse_mode) { + text = g_strdup_printf( + messages_get(nsgtk_downloads_num_active > 1 ? + "gtkProgressBarPulse" : + "gtkProgressBarPulseSingle"), + nsgtk_downloads_num_active); + gtk_progress_bar_pulse(nsgtk_download_progress_bar); + gtk_progress_bar_set_text(nsgtk_download_progress_bar, text); + } else { + percent = total != 0 ? (gfloat)downloaded / total : 0; + text = g_strdup_printf(messages_get("gtkProgressBar"), + floor(percent*100), dls); + gtk_progress_bar_set_fraction(nsgtk_download_progress_bar, + percent); + gtk_progress_bar_set_text(nsgtk_download_progress_bar, text); + } + + g_free(text); + + if (nsgtk_downloads_num_active == 0) + return FALSE; /* Returning FALSE here cancels the g_timeout */ + else + return TRUE; +} + +static void nsgtk_download_store_clear_item(struct gui_download_window *dl) +{ + if (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR) { + nsgtk_downloads_list = g_list_remove(nsgtk_downloads_list, dl); + + gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store), + &nsgtk_download_iter, + gtk_tree_row_reference_get_path(dl->row)); + gtk_list_store_remove(nsgtk_download_store, + &nsgtk_download_iter); + + download_context_destroy(dl->ctx); + g_string_free(dl->name, TRUE); + g_string_free(dl->time_left, TRUE); + g_free(dl); + + nsgtk_download_sensitivity_evaluate(nsgtk_download_selection); + nsgtk_download_update(FALSE); + } +} + +static void nsgtk_download_tree_view_row_activated(GtkTreeView *tree, + GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model(tree); + + if (gtk_tree_model_get_iter(model, &iter, path)) { + /* TODO: This will be a context action (pause, start, clear) */ + nsgtk_download_do(nsgtk_download_store_clear_item); + } +} + +static void nsgtk_download_change_sensitivity(struct gui_download_window *dl, + nsgtk_download_actions sensitivity) +{ + dl->sensitivity = sensitivity; + nsgtk_download_sensitivity_evaluate(nsgtk_download_selection); +} + +static void nsgtk_download_change_status ( + struct gui_download_window *dl, nsgtk_download_status status) +{ + dl->status = status; + if (status != NSGTK_DOWNLOAD_NONE) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store), + &nsgtk_download_iter, + gtk_tree_row_reference_get_path(dl->row)); + + gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter, + NSGTK_DOWNLOAD_STATUS, + messages_get(status_messages[status]), -1); + } +} + +static void nsgtk_download_store_cancel_item (struct gui_download_window *dl) +{ + if (dl->sensitivity & NSGTK_DOWNLOAD_CANCEL) { + dl->speed = 0; + dl->size_downloaded = 0; + dl->progress = 0; + dl->time_remaining = -1; + nsgtk_download_change_sensitivity(dl, NSGTK_DOWNLOAD_CLEAR); + nsgtk_download_change_status(dl, NSGTK_DOWNLOAD_CANCELED); + + download_context_abort(dl->ctx); + + g_unlink(download_context_get_filename(dl->ctx)); + + nsgtk_download_update(TRUE); + } +} + +static gboolean nsgtk_download_hide(GtkWidget *window) +{ + gtk_widget_hide(window); + return TRUE; +} + +/* exported interface documented in gtk/download.h */ +nserror nsgtk_download_init(void) +{ + GtkBuilder* builder; + nserror res; + + res = nsgtk_builder_new_from_resname("downloads", &builder); + if (res != NSERROR_OK) { + LOG("Download UI builder init failed"); + return res; + } + + gtk_builder_connect_signals(builder, NULL); + + nsgtk_download_button_pause = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPause")); + nsgtk_download_button_clear = GTK_BUTTON(gtk_builder_get_object(builder, "buttonClear")); + nsgtk_download_button_cancel = GTK_BUTTON(gtk_builder_get_object(builder, "buttonCancel")); + nsgtk_download_button_resume = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPlay")); + + nsgtk_download_progress_bar = GTK_PROGRESS_BAR(gtk_builder_get_object(builder, "progressBar")); + nsgtk_download_window = GTK_WINDOW(gtk_builder_get_object(builder, "wndDownloads")); + nsgtk_download_parent = NULL; + + gtk_window_set_transient_for(GTK_WINDOW(nsgtk_download_window), + nsgtk_download_parent); + gtk_window_set_destroy_with_parent(GTK_WINDOW(nsgtk_download_window), + FALSE); + + nsgtk_downloads_timer = g_timer_new(); + + nsgtk_download_tree = nsgtk_download_tree_view_new(builder); + + nsgtk_download_store = gtk_list_store_new(NSGTK_DOWNLOAD_N_COLUMNS, + G_TYPE_INT, /* % complete */ + G_TYPE_STRING, /* Description */ + G_TYPE_STRING, /* Time remaining */ + G_TYPE_STRING, /* Speed */ + G_TYPE_INT, /* Pulse */ + G_TYPE_STRING, /* Status */ + G_TYPE_POINTER /* Download structure */ + ); + + + gtk_tree_view_set_model(nsgtk_download_tree, + GTK_TREE_MODEL(nsgtk_download_store)); + + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(nsgtk_download_store), + NSGTK_DOWNLOAD_STATUS, + (GtkTreeIterCompareFunc) nsgtk_download_sort, NULL, NULL); + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(nsgtk_download_store), + NSGTK_DOWNLOAD_STATUS, GTK_SORT_ASCENDING); + + g_object_unref(nsgtk_download_store); + + nsgtk_download_selection = + gtk_tree_view_get_selection(nsgtk_download_tree); + gtk_tree_selection_set_mode(nsgtk_download_selection, + GTK_SELECTION_MULTIPLE); + + g_signal_connect(G_OBJECT(nsgtk_download_selection), "changed", + G_CALLBACK(nsgtk_download_sensitivity_evaluate), NULL); + g_signal_connect(nsgtk_download_tree, "row-activated", + G_CALLBACK(nsgtk_download_tree_view_row_activated), + NULL); + g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonClear"), + "clicked", + G_CALLBACK(nsgtk_download_do), + nsgtk_download_store_clear_item); + g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonCancel"), + "clicked", + G_CALLBACK(nsgtk_download_do), + nsgtk_download_store_cancel_item); + g_signal_connect(G_OBJECT(nsgtk_download_window), "delete-event", + G_CALLBACK(nsgtk_download_hide), NULL); + + return NSERROR_OK; +} + +void nsgtk_download_destroy () +{ + nsgtk_download_do(nsgtk_download_store_cancel_item); +} + +bool nsgtk_check_for_downloads (GtkWindow *parent) +{ + if (nsgtk_downloads_num_active != 0) { + GtkWidget *dialog; + dialog = gtk_message_dialog_new_with_markup(parent, + GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + "%s\n\n" + "%s", messages_get("gtkQuit"), + messages_get("gtkDownloadsRunning")); + gtk_dialog_add_buttons(GTK_DIALOG(dialog), "gtk-cancel", + GTK_RESPONSE_CANCEL, "gtk-quit", + GTK_RESPONSE_CLOSE, NULL); + + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + if (response == GTK_RESPONSE_CANCEL) + return true; + } + + return false; +} + +void nsgtk_download_show(GtkWindow *parent) +{ + gtk_window_set_transient_for(nsgtk_download_window, + nsgtk_download_parent); + gtk_window_present(nsgtk_download_window); +} + +static gchar* nsgtk_download_dialog_show (const gchar *filename, const gchar *domain, + const gchar *size) +{ + enum { GTK_RESPONSE_DOWNLOAD, GTK_RESPONSE_SAVE_AS }; + GtkWidget *dialog; + char *destination = NULL; + gchar *message = g_strdup(messages_get("gtkStartDownload")); + gchar *info = g_strdup_printf(messages_get("gtkInfo"), filename, + domain, size); + + dialog = gtk_message_dialog_new_with_markup(nsgtk_download_parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, + "%s" + "\n\n%s", + message, info); + + gtk_dialog_add_buttons(GTK_DIALOG(dialog), NSGTK_STOCK_SAVE, + GTK_RESPONSE_DOWNLOAD, NSGTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, NSGTK_STOCK_SAVE_AS, + GTK_RESPONSE_SAVE_AS, NULL); + + gint result = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + g_free(message); + g_free(info); + + switch (result) { + case GTK_RESPONSE_SAVE_AS: { + dialog = gtk_file_chooser_dialog_new + (messages_get("gtkSave"), + nsgtk_download_parent, + GTK_FILE_CHOOSER_ACTION_SAVE, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_current_name + (GTK_FILE_CHOOSER(dialog), filename); + gtk_file_chooser_set_current_folder + (GTK_FILE_CHOOSER(dialog), + nsoption_charp(downloads_directory)); + gtk_file_chooser_set_do_overwrite_confirmation + (GTK_FILE_CHOOSER(dialog), + nsoption_bool(request_overwrite)); + + gint result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_ACCEPT) + destination = gtk_file_chooser_get_filename + (GTK_FILE_CHOOSER(dialog)); + gtk_widget_destroy(dialog); + break; + } + case GTK_RESPONSE_DOWNLOAD: { + destination = malloc(strlen(nsoption_charp(downloads_directory)) + + strlen(filename) + SLEN("/") + 1); + if (destination == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + break; + } + sprintf(destination, "%s/%s", + nsoption_charp(downloads_directory), filename); + /* Test if file already exists and display overwrite + * confirmation if needed */ + if (g_file_test(destination, G_FILE_TEST_EXISTS) && + nsoption_bool(request_overwrite)) { + message = g_strdup_printf(messages_get( + "gtkOverwrite"), filename); + info = g_strdup_printf(messages_get( + "gtkOverwriteInfo"), + nsoption_charp(downloads_directory)); + + dialog = gtk_message_dialog_new_with_markup( + nsgtk_download_parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + "%s",message); + gtk_message_dialog_format_secondary_markup( + GTK_MESSAGE_DIALOG(dialog), + "%s", info); + + GtkWidget *button = gtk_dialog_add_button( + GTK_DIALOG(dialog), + "_Replace", + GTK_RESPONSE_DOWNLOAD); + gtk_button_set_image(GTK_BUTTON(button), + nsgtk_image_new_from_stock( + NSGTK_STOCK_SAVE, + GTK_ICON_SIZE_BUTTON)); + + gint result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_CANCEL) + destination = NULL; + + gtk_widget_destroy(dialog); + g_free(message); + g_free(info); + } + break; + } + } + return destination; +} + + +static gboolean nsgtk_download_handle_error (GError *error) +{ + if (error != NULL) { + GtkWidget*dialog; + gchar *message = g_strdup_printf(messages_get("gtkFileError"), + error->message); + + dialog = gtk_message_dialog_new_with_markup + (nsgtk_download_parent, + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s\n\n" + "%s", messages_get("gtkFailed"), + message); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return TRUE; + } + return FALSE; +} + +static void nsgtk_download_store_create_item (struct gui_download_window *dl) +{ + nsgtk_download_store_update_item(dl); + /* The iter has already been updated to this row */ + gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter, + NSGTK_DOWNLOAD, dl, -1); +} + +static struct gui_download_window * +gui_download_window_create(download_context *ctx, struct gui_window *gui) +{ + nsurl *url = download_context_get_url(ctx); + unsigned long total_size = download_context_get_total_length(ctx); + gchar *domain; + gchar *destination; + gboolean unknown_size = total_size == 0; + const char *size = (total_size == 0 ? + messages_get("gtkUnknownSize") : + human_friendly_bytesize(total_size)); + + nsgtk_download_parent = + nsgtk_scaffolding_window(nsgtk_get_scaffold(gui)); + + struct gui_download_window *download = malloc(sizeof *download); + if (download == NULL) { + return NULL; + } + + /* set the domain to the host component of the url if it exists */ + if (nsurl_has_component(url, NSURL_HOST)) { + domain = g_strdup(lwc_string_data(nsurl_get_component(url, NSURL_HOST))); + } else { + domain = g_strdup(messages_get("gtkUnknownHost")); + } + if (domain == NULL) { + free(download); + return NULL; + } + + /* show the dialog */ + destination = nsgtk_download_dialog_show( + download_context_get_filename(ctx), domain, size); + if (destination == NULL) { + g_free(domain); + free(download); + return NULL; + } + + /* Add the new row and store the reference to it (which keeps track of + * the tree changes) */ + gtk_list_store_prepend(nsgtk_download_store, &nsgtk_download_iter); + download->row = gtk_tree_row_reference_new( + GTK_TREE_MODEL(nsgtk_download_store), + gtk_tree_model_get_path( + GTK_TREE_MODEL(nsgtk_download_store), + &nsgtk_download_iter)); + + download->ctx = ctx; + download->name = g_string_new(download_context_get_filename(ctx)); + download->time_left = g_string_new(""); + download->size_total = total_size; + download->size_downloaded = 0; + download->speed = 0; + download->start_time = g_timer_elapsed(nsgtk_downloads_timer, NULL); + download->time_remaining = -1; + download->status = NSGTK_DOWNLOAD_NONE; + download->progress = 0; + download->error = NULL; + download->write = + g_io_channel_new_file(destination, "w", &download->error); + + if (nsgtk_download_handle_error(download->error)) { + g_string_free(download->name, TRUE); + g_string_free(download->time_left, TRUE); + free(download); + return NULL; + } + g_io_channel_set_encoding(download->write, NULL, &download->error); + + nsgtk_download_change_sensitivity(download, NSGTK_DOWNLOAD_CANCEL); + + nsgtk_download_store_create_item(download); + nsgtk_download_show(nsgtk_download_parent); + + if (unknown_size) + nsgtk_download_change_status(download, NSGTK_DOWNLOAD_WORKING); + + if (nsgtk_downloads_num_active == 0) { + g_timeout_add(UPDATE_RATE, + (GSourceFunc) nsgtk_download_update, FALSE); + } + + nsgtk_downloads_list = g_list_prepend(nsgtk_downloads_list, download); + + return download; +} + + +static nserror gui_download_window_data(struct gui_download_window *dw, + const char *data, unsigned int size) +{ + g_io_channel_write_chars(dw->write, data, size, NULL, &dw->error); + if (dw->error != NULL) { + dw->speed = 0; + dw->time_remaining = -1; + + nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR); + nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_ERROR); + + nsgtk_download_update(TRUE); + + gtk_window_present(nsgtk_download_window); + + return NSERROR_SAVE_FAILED; + } + dw->size_downloaded += size; + + return NSERROR_OK; +} + + +static void gui_download_window_error(struct gui_download_window *dw, + const char *error_msg) +{ +} + + +static void gui_download_window_done(struct gui_download_window *dw) +{ + g_io_channel_shutdown(dw->write, TRUE, &dw->error); + g_io_channel_unref(dw->write); + + dw->speed = 0; + dw->time_remaining = -1; + dw->progress = 100; + dw->size_total = dw->size_downloaded; + nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR); + nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_COMPLETE); + + if (nsoption_bool(downloads_clear)) + nsgtk_download_store_clear_item(dw); + else + nsgtk_download_update(TRUE); +} + + +static struct gui_download_table download_table = { + .create = gui_download_window_create, + .data = gui_download_window_data, + .error = gui_download_window_error, + .done = gui_download_window_done, +}; + +struct gui_download_table *nsgtk_download_table = &download_table; diff --git a/frontends/gtk/download.h b/frontends/gtk/download.h new file mode 100644 index 000000000..0b1097655 --- /dev/null +++ b/frontends/gtk/download.h @@ -0,0 +1,41 @@ +/* + * Copyright 2008 Michael Lester + * + * 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 . + */ + +#ifndef GTK_DOWNLOAD_H +#define GTK_DOWNLOAD_H + +#include + +/** + * download operation table for gtk + */ +struct gui_download_table *nsgtk_download_table; + +/** + * Initialise download window ready for use. + * + * \return NSERROR_OK on success else appropriate error code on faliure. + */ +nserror nsgtk_download_init(void); + +void nsgtk_download_destroy (void); +bool nsgtk_check_for_downloads(GtkWindow *parent); +void nsgtk_download_show(GtkWindow *parent); +void nsgtk_download_add(gchar *url, gchar *destination); + +#endif diff --git a/frontends/gtk/fetch.c b/frontends/gtk/fetch.c new file mode 100644 index 000000000..06770b6b2 --- /dev/null +++ b/frontends/gtk/fetch.c @@ -0,0 +1,260 @@ +/* + * Copyright 2007, 2014 Vincent Sanders + * Copyright 2007 Rob Kendrick + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/hashtable.h" +#include "utils/log.h" +#include "utils/filepath.h" +#include "utils/file.h" +#include "utils/nsurl.h" +#include "desktop/gui_fetch.h" + +#include "gtk/gui.h" +#include "gtk/resources.h" +#include "gtk/fetch.h" + +#define HASH_SIZE 117 +#define MAX_LINE_LEN 256 + +static struct hash_table *mime_hash = NULL; + +void gtk_fetch_filetype_init(const char *mimefile) +{ + struct stat statbuf; + FILE *fh = NULL; + + mime_hash = hash_create(HASH_SIZE); + + /* first, check to see if /etc/mime.types in preference */ + + if ((stat("/etc/mime.types", &statbuf) == 0) && + S_ISREG(statbuf.st_mode)) { + mimefile = "/etc/mime.types"; + } + + fh = fopen(mimefile, "r"); + + /* Some OSes (mentioning no Solarises) have a worthlessly tiny + * /etc/mime.types that don't include essential things, so we + * pre-seed our hash with the essentials. These will get + * over-ridden if they are mentioned in the mime.types file. + */ + + hash_add(mime_hash, "css", "text/css"); + hash_add(mime_hash, "htm", "text/html"); + hash_add(mime_hash, "html", "text/html"); + hash_add(mime_hash, "jpg", "image/jpeg"); + hash_add(mime_hash, "jpeg", "image/jpeg"); + hash_add(mime_hash, "gif", "image/gif"); + hash_add(mime_hash, "png", "image/png"); + hash_add(mime_hash, "jng", "image/jng"); + hash_add(mime_hash, "mng", "image/mng"); + hash_add(mime_hash, "webp", "image/webp"); + hash_add(mime_hash, "spr", "image/x-riscos-sprite"); + + if (fh == NULL) { + LOG("Unable to open a mime.types file, so using a minimal one for you."); + return; + } + + while (feof(fh) == 0) { + char line[MAX_LINE_LEN], *ptr, *type, *ext; + + if (fgets(line, sizeof(line), fh) == NULL) + break; + + if ((feof(fh) == 0) && line[0] != '#') { + ptr = line; + + /* search for the first non-whitespace character */ + while (isspace(*ptr)) { + ptr++; + } + + /* is this line empty other than leading whitespace? */ + if (*ptr == '\n' || *ptr == '\0') { + continue; + } + + type = ptr; + + /* search for the first non-whitespace char or NUL or + * NL */ + while (*ptr && (!isspace(*ptr)) && *ptr != '\n') { + ptr++; + } + + if (*ptr == '\0' || *ptr == '\n') { + /* this mimetype has no extensions - read next + * line. + */ + continue; + } + + *ptr++ = '\0'; + + /* search for the first non-whitespace character which + * will be the first filename extenion */ + while (isspace(*ptr)) { + ptr++; + } + + while (true) { + ext = ptr; + + /* search for the first whitespace char or + * NUL or NL which is the end of the ext. + */ + while (*ptr && + (!isspace(*ptr)) && + *ptr != '\n') { + ptr++; + } + + if (*ptr == '\0' || *ptr == '\n') { + /* special case for last extension on + * the line + */ + *ptr = '\0'; + hash_add(mime_hash, ext, type); + break; + } + + *ptr++ = '\0'; + hash_add(mime_hash, ext, type); + + /* search for the first non-whitespace char or + * NUL or NL, to find start of next ext. + */ + while (*ptr && + (isspace(*ptr)) && + *ptr != '\n') { + ptr++; + } + } + } + } + + fclose(fh); +} + +void gtk_fetch_filetype_fin(void) +{ + hash_destroy(mime_hash); +} + +const char *fetch_filetype(const char *unix_path) +{ + struct stat statbuf; + char *ext; + const char *ptr; + char *lowerchar; + const char *type; + int l; + + /* stat the path to attempt to determine if the file is special */ + if (stat(unix_path, &statbuf) == 0) { + /* stat suceeded so can check for directory */ + + if (S_ISDIR(statbuf.st_mode)) { + return "application/x-netsurf-directory"; + } + } + + l = strlen(unix_path); + + /* Hacky RISC OS compatibility */ + if ((3 < l) && (strcasecmp(unix_path + l - 4, ",f79") == 0)) { + return "text/css"; + } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",faf") == 0)) { + return "text/html"; + } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",b60") == 0)) { + return "image/png"; + } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",ff9") == 0)) { + return "image/x-riscos-sprite"; + } + + if (strchr(unix_path, '.') == NULL) { + /* no extension anywhere! */ + return "text/plain"; + } + + ptr = unix_path + strlen(unix_path); + while (*ptr != '.' && *ptr != '/') { + ptr--; + } + + if (*ptr != '.') { + return "text/plain"; + } + + ext = strdup(ptr + 1); /* skip the . */ + + /* the hash table only contains lower-case versions - make sure this + * copy is lower case too. + */ + lowerchar = ext; + while (*lowerchar) { + *lowerchar = tolower(*lowerchar); + lowerchar++; + } + + type = hash_get(mime_hash, ext); + free(ext); + + if (type == NULL) { + type = "text/plain"; + } + + return type; +} + + +static nsurl *nsgtk_get_resource_url(const char *path) +{ + char buf[PATH_MAX]; + nsurl *url = NULL; + + /* favicon.ico -> favicon.png */ + if (strcmp(path, "favicon.ico") == 0) { + nsurl_create("resource:favicon.png", &url); + } else { + netsurf_path_to_nsurl(filepath_sfind(respaths, buf, path), &url); + } + + return url; +} + +static struct gui_fetch_table fetch_table = { + .filetype = fetch_filetype, + + .get_resource_url = nsgtk_get_resource_url, + .get_resource_data = nsgtk_data_from_resname, +}; + +struct gui_fetch_table *nsgtk_fetch_table = &fetch_table; diff --git a/frontends/gtk/fetch.h b/frontends/gtk/fetch.h new file mode 100644 index 000000000..a095adbf9 --- /dev/null +++ b/frontends/gtk/fetch.h @@ -0,0 +1,28 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +#ifndef NETSURF_GTK_FETCH_H +#define NETSURF_GTK_FETCH_H + +struct gui_fetch_table *nsgtk_fetch_table; + +void gtk_fetch_filetype_init(const char *mimefile); +void gtk_fetch_filetype_fin(void); +const char *fetch_filetype(const char *unix_path); + +#endif diff --git a/frontends/gtk/gdk.c b/frontends/gtk/gdk.c new file mode 100644 index 000000000..9ed90bd8e --- /dev/null +++ b/frontends/gtk/gdk.c @@ -0,0 +1,124 @@ +/* + * Copyright 2011 Vincent Sanders + * + * 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 . + */ + +#include + +#include "utils/log.h" + +#include "gtk/gdk.h" + +static void +convert_alpha(guchar *dest_data, + int dest_stride, + guchar *src_data, + int src_stride, + int width, + int height) +{ + int x, y; + + for (y = 0; y < height; y++) { + guint32 *src = (guint32 *) src_data; + + for (x = 0; x < width; x++) { + guint alpha = src[x] >> 24; + + if (alpha == 0) { + dest_data[x * 4 + 0] = 0; + dest_data[x * 4 + 1] = 0; + dest_data[x * 4 + 2] = 0; + } else { + dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; + dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + } + dest_data[x * 4 + 3] = alpha; + } + + src_data += src_stride; + dest_data += dest_stride; + } +} + + +GdkPixbuf * +nsgdk_pixbuf_get_from_surface(cairo_surface_t *surface, int scwidth, int scheight) +{ + int width, height; /* source width and height */ + cairo_surface_t *scsurface; /* scaled surface */ + cairo_t *cr; /* cairo context for scaled surface */ + GdkPixbuf *pixbuf; /* The result pixel buffer */ + + /* create pixmap */ + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, scwidth, scheight); + if (pixbuf == NULL) { + return NULL; + } + + memset(gdk_pixbuf_get_pixels(pixbuf), + 0xff, + gdk_pixbuf_get_rowstride(pixbuf) * scheight); + + /* scale cairo surface into new surface the target size */ + cairo_surface_flush(surface); /* ensure source surface is ready */ + + /* get source surface dimensions */ + width = cairo_image_surface_get_width(surface); + height = cairo_image_surface_get_height(surface); + + /* scaled surface always has an alpha chanel for ease */ + scsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, scwidth, scheight); + if (cairo_surface_status(scsurface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(scsurface); + g_object_unref(pixbuf); + LOG("Surface creation failed"); + return NULL; + } + + cr = cairo_create(scsurface); + + /* Scale *before* setting the source surface */ + cairo_scale(cr, (double)scwidth / width, (double)scheight / height); + cairo_set_source_surface(cr, surface, 0, 0); + + /* To avoid getting the edge pixels blended with 0 + * alpha, which would occur with the default + * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer + */ + cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REFLECT); + + /* Replace the destination with the source instead of overlaying */ + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + /* Do the actual drawing */ + cairo_paint(cr); + + cairo_destroy(cr); + + /* copy data from surface into pixmap */ + convert_alpha(gdk_pixbuf_get_pixels(pixbuf), + gdk_pixbuf_get_rowstride(pixbuf), + cairo_image_surface_get_data(scsurface), + cairo_image_surface_get_stride(scsurface), + scwidth, scheight); + + cairo_surface_destroy(scsurface); + + return pixbuf; +} + diff --git a/frontends/gtk/gdk.h b/frontends/gtk/gdk.h new file mode 100644 index 000000000..2fcee07f1 --- /dev/null +++ b/frontends/gtk/gdk.h @@ -0,0 +1,35 @@ +/* + * Copyright 2011 Vincent Sanders + * + * 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 . + */ + +/** \file + * GDK support functions for missing interfaces + */ + +#ifndef NETSURF_GTK_GDK_H_ +#define NETSURF_GTK_GDK_H_ + +#include + +/** obtain a pixbuf of the specified size from a cairo surface. + * + * This is the same as the GTK+ 3 gdk_pixbuf_get_from_surface but + * actually works and is available on gtk 2 + */ +GdkPixbuf *nsgdk_pixbuf_get_from_surface(cairo_surface_t *surface, int width, int height); + +#endif /* NETSURF_GTK_GDK_H */ diff --git a/frontends/gtk/gettext.c b/frontends/gtk/gettext.c new file mode 100644 index 000000000..a9f6f48be --- /dev/null +++ b/frontends/gtk/gettext.c @@ -0,0 +1,43 @@ +/* + * Copyright 2013 Vincent Sanders + * + * 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 . + */ + +/** \file + * Localised gettext message support (implementation). + * + * Wrappers for gettext to the internal native language message support. + */ + +#include + +#include "utils/messages.h" +#include "gtk/gettext.h" + +char *gettext(const char *msgid) +{ + return dcgettext(NULL, msgid, 0); +} + +char *dgettext(const char *domainname, const char *msgid) +{ + return dcgettext(domainname, msgid, 0); +} + +char *dcgettext(const char *domainname, const char *msgid, int category) +{ + return (void *)messages_get(msgid); +} diff --git a/frontends/gtk/gettext.h b/frontends/gtk/gettext.h new file mode 100644 index 000000000..726ba356e --- /dev/null +++ b/frontends/gtk/gettext.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012 Vincent Sanders + * + * 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 . + */ + +/** \file + * Localised message support (interface). + * + * Provide gettext interface to the utility localisation routines + */ + +#ifndef _NETSURF_GTK_GETTEXT_MESSAGES_H_ +#define _NETSURF_GTK_GETTEXT_MESSAGES_H_ + +char *gettext(const char *msgid); +char *dgettext(const char *domainname, const char *msgid); +char *dcgettext(const char *domainname, const char *msgid, int category); + +#endif diff --git a/frontends/gtk/gui.c b/frontends/gtk/gui.c new file mode 100644 index 000000000..e705918bc --- /dev/null +++ b/frontends/gtk/gui.c @@ -0,0 +1,1161 @@ +/* + * Copyright 2004-2010 James Bursa + * Copyright 2010-2016 Vincent Sanders + * Copyright 2004-2009 John-Mark Bell + * Copyright 2009 Paul Blokus + * Copyright 2006-2009 Daniel Silverstone + * Copyright 2006-2008 Rob Kendrick + * Copyright 2008 John Tytgat + * Copyright 2008 Adam Blokus + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/filepath.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "utils/utils.h" +#include "utils/file.h" +#include "utils/nsoption.h" +#include "content/fetchers.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "content/backing_store.h" +#include "desktop/browser.h" +#include "desktop/save_complete.h" +#include "desktop/save_pdf.h" +#include "desktop/searchweb.h" +#include "desktop/textinput.h" +#include "desktop/tree.h" +#include "desktop/gui_misc.h" +#include "desktop/netsurf.h" + +#include "gtk/compat.h" +#include "gtk/warn.h" +#include "gtk/completion.h" +#include "gtk/cookies.h" +#include "gtk/download.h" +#include "gtk/fetch.h" +#include "gtk/gui.h" +#include "gtk/history.h" +#include "gtk/hotlist.h" +#include "gtk/throbber.h" +#include "gtk/treeview.h" +#include "gtk/window.h" +#include "gtk/schedule.h" +#include "gtk/selection.h" +#include "gtk/search.h" +#include "gtk/ssl_cert.h" +#include "gtk/bitmap.h" +#include "gtk/resources.h" +#include "gtk/login.h" +#include "gtk/layout_pango.h" + +bool nsgtk_complete = false; + +char *toolbar_indices_file_location; + +char *nsgtk_config_home; /* exported global defined in gtk/gui.h */ + +GdkPixbuf *favicon_pixbuf; /** favicon default pixbuf */ +GdkPixbuf *win_default_icon_pixbuf; /** default window icon pixbuf */ +GdkPixbuf *arrow_down_pixbuf; /** arrow down pixbuf */ + +GtkBuilder *warning_builder; + +char **respaths; /** resource search path vector */ + +/** + * Cause an abnormal program termination. + * + * \note This never returns and is intended to terminate without any cleanup. + * + * \param error The message to display to the user. + */ +static void die(const char * const error) +{ + fprintf(stderr, "%s", error); + exit(EXIT_FAILURE); +} + +/** + * Create an array of valid paths to search for resources. + * + * The idea is that all the complex path computation to find resources + * is performed here, once, rather than every time a resource is + * searched for. + */ +static char ** +nsgtk_init_resource_path(const char *config_home) +{ + char *resource_path; + int resource_path_len; + const gchar * const *langv; + char **pathv; /* resource path string vector */ + char **respath; /* resource paths vector */ + + if (config_home != NULL) { + resource_path_len = snprintf(NULL, 0, + "%s:${NETSURFRES}:%s", + config_home, + GTK_RESPATH); + resource_path = malloc(resource_path_len + 1); + if (resource_path == NULL) { + return NULL; + } + snprintf(resource_path, resource_path_len + 1, + "%s:${NETSURFRES}:%s", + config_home, + GTK_RESPATH); + } else { + resource_path_len = snprintf(NULL, 0, + "${NETSURFRES}:%s", + GTK_RESPATH); + resource_path = malloc(resource_path_len + 1); + if (resource_path == NULL) { + return NULL; + } + snprintf(resource_path, + resource_path_len + 1, + "${NETSURFRES}:%s", + GTK_RESPATH); + } + + pathv = filepath_path_to_strvec(resource_path); + + langv = g_get_language_names(); + + respath = filepath_generate(pathv, langv); + + filepath_free_strvec(pathv); + + free(resource_path); + + return respath; +} + + +/** + * Set option defaults for gtk frontend. + * + * @param defaults The option table to update. + * @return error status. + */ +static nserror set_defaults(struct nsoption_s *defaults) +{ + char *fname; + + /* cookie file default */ + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies"); + if (fname != NULL) { + nsoption_setnull_charp(cookie_file, fname); + } + + /* cookie jar default */ + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies"); + if (fname != NULL) { + nsoption_setnull_charp(cookie_jar, fname); + } + + /* url database default */ + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "URLs"); + if (fname != NULL) { + nsoption_setnull_charp(url_file, fname); + } + + /* bookmark database default */ + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Hotlist"); + if (fname != NULL) { + nsoption_setnull_charp(hotlist_path, fname); + } + + /* download directory default */ + fname = getenv("HOME"); + if (fname != NULL) { + nsoption_setnull_charp(downloads_directory, strdup(fname)); + } + + /* default path to certificates */ + nsoption_setnull_charp(ca_path, strdup("/etc/ssl/certs")); + + if ((nsoption_charp(cookie_file) == NULL) || + (nsoption_charp(cookie_jar) == NULL) || + (nsoption_charp(url_file) == NULL) || + (nsoption_charp(hotlist_path) == NULL) || + (nsoption_charp(downloads_directory) == NULL) || + (nsoption_charp(ca_path) == NULL)) { + LOG("Failed initialising default resource paths"); + return NSERROR_BAD_PARAMETER; + } + + /* set default font names */ + nsoption_set_charp(font_sans, strdup("Sans")); + nsoption_set_charp(font_serif, strdup("Serif")); + nsoption_set_charp(font_mono, strdup("Monospace")); + nsoption_set_charp(font_cursive, strdup("Serif")); + nsoption_set_charp(font_fantasy, strdup("Serif")); + + return NSERROR_OK; +} + + + + +/** + * Initialize GTK interface. + */ +static nserror nsgtk_init(int argc, char** argv, char **respath) +{ + char buf[PATH_MAX]; + char *resource_filename; + char *addr = NULL; + nsurl *url; + nserror error; + + error = nsgtk_builder_new_from_resname("warning", &warning_builder); + if (error != NSERROR_OK) { + LOG("Unable to initialise warning dialog"); + return error; + } + + gtk_builder_connect_signals(warning_builder, NULL); + + /* set default icon if its available */ + error = nsgdk_pixbuf_new_from_resname("netsurf.xpm", + &win_default_icon_pixbuf); + if (error == NSERROR_OK) { + LOG("Seting default window icon"); + gtk_window_set_default_icon(win_default_icon_pixbuf); + } + + /* Search engine sources */ + resource_filename = filepath_find(respath, "SearchEngines"); + search_web_init(resource_filename); + if (resource_filename != NULL) { + LOG("Using '%s' as Search Engines file", resource_filename); + free(resource_filename); + } + + /* Default favicon */ + error = nsgdk_pixbuf_new_from_resname("favicon.png", &favicon_pixbuf); + if (error != NSERROR_OK) { + favicon_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, + false, 8, 16, 16); + } + + /* arrow down icon */ + error = nsgdk_pixbuf_new_from_resname("arrow_down_8x32.png", + &arrow_down_pixbuf); + if (error != NSERROR_OK) { + arrow_down_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, + false, 8, 8, 32); + } + + /* Toolbar inicies file */ + toolbar_indices_file_location = filepath_find(respath, + "toolbarIndices"); + LOG("Using '%s' as custom toolbar settings file", + toolbar_indices_file_location); + + /* initialise throbber */ + error = nsgtk_throbber_init(); + if (error != NSERROR_OK) { + LOG("Unable to initialise throbber."); + return error; + } + + /* Initialise completions - cannot fail */ + nsgtk_completion_init(); + + filepath_sfinddef(respath, buf, "mime.types", "/etc/"); + gtk_fetch_filetype_init(buf); + + save_complete_init(); + + urldb_load(nsoption_charp(url_file)); + urldb_load_cookies(nsoption_charp(cookie_file)); + + /* The tree view system needs to know the screen's DPI, so we + * find that out here, rather than when we create a first browser + * window. + */ + browser_set_dpi(gdk_screen_get_resolution(gdk_screen_get_default())); + LOG("Set CSS DPI to %d", browser_get_dpi()); + + /* Initialise top level UI elements */ + error = nsgtk_history_init(); + if (error != NSERROR_OK) { + LOG("Unable to initialise global history window."); + return error; + } + + error = nsgtk_download_init(); + if (error != NSERROR_OK) { + LOG("Unable to initialise download window."); + return error; + } + + error = nsgtk_cookies_init(); + if (error != NSERROR_OK) { + LOG("Unable to initialise cookies window."); + return error; + } + + error = nsgtk_hotlist_init(); + if (error != NSERROR_OK) { + LOG("Unable to initialise hotlist window."); + return error; + } + + /* If there is a url specified on the command line use it */ + if (argc > 1) { + struct stat fs; + if (stat(argv[1], &fs) == 0) { + size_t addrlen; + char *rp = realpath(argv[1], NULL); + assert(rp != NULL); + + /* calculate file url length including terminator */ + addrlen = SLEN("file://") + strlen(rp) + 1; + addr = malloc(addrlen); + assert(addr != NULL); + snprintf(addr, addrlen, "file://%s", rp); + free(rp); + } else { + addr = strdup(argv[1]); + } + } + if (addr != NULL) { + /* managed to set up based on local launch */ + } else if (nsoption_charp(homepage_url) != NULL) { + addr = strdup(nsoption_charp(homepage_url)); + } else { + addr = strdup(NETSURF_HOMEPAGE); + } + + /* create an initial browser window */ + error = nsurl_create(addr, &url); + if (error == NSERROR_OK) { + error = browser_window_create(BW_CREATE_HISTORY, + url, + NULL, + NULL, + NULL); + nsurl_unref(url); + } + + free(addr); + + return error; +} + + + +/** + * Ensures output logging stream is correctly configured + */ +static bool nslog_stream_configure(FILE *fptr) +{ + /* set log stream to be non-buffering */ + setbuf(fptr, NULL); + + return true; +} + + +/** + * Run the gtk event loop. + * + * The same as the standard gtk_main loop except this ensures active + * FD are added to the gtk poll event set. + */ +static void nsgtk_main(void) +{ + fd_set read_fd_set, write_fd_set, exc_fd_set; + int max_fd; + GPollFD *fd_list[1000]; + unsigned int fd_count; + + while (!nsgtk_complete) { + max_fd = -1; + fd_count = 0; + FD_ZERO(&read_fd_set); + FD_ZERO(&write_fd_set); + FD_ZERO(&exc_fd_set); + + fetcher_fdset(&read_fd_set, &write_fd_set, &exc_fd_set, &max_fd); + for (int i = 0; i <= max_fd; i++) { + if (FD_ISSET(i, &read_fd_set)) { + GPollFD *fd = malloc(sizeof *fd); + fd->fd = i; + fd->events = G_IO_IN | G_IO_HUP | G_IO_ERR; + g_main_context_add_poll(0, fd, 0); + fd_list[fd_count++] = fd; + } + if (FD_ISSET(i, &write_fd_set)) { + GPollFD *fd = malloc(sizeof *fd); + fd->fd = i; + fd->events = G_IO_OUT | G_IO_ERR; + g_main_context_add_poll(0, fd, 0); + fd_list[fd_count++] = fd; + } + if (FD_ISSET(i, &exc_fd_set)) { + GPollFD *fd = malloc(sizeof *fd); + fd->fd = i; + fd->events = G_IO_ERR; + g_main_context_add_poll(0, fd, 0); + fd_list[fd_count++] = fd; + } + } + + schedule_run(); + + gtk_main_iteration(); + + for (unsigned int i = 0; i != fd_count; i++) { + g_main_context_remove_poll(0, fd_list[i]); + free(fd_list[i]); + } + } +} + + +static void gui_quit(void) +{ + LOG("Quitting GUI"); + + /* Ensure all scaffoldings are destroyed before we go into exit */ + nsgtk_download_destroy(); + urldb_save_cookies(nsoption_charp(cookie_jar)); + urldb_save(nsoption_charp(url_file)); + nsgtk_cookies_destroy(); + nsgtk_history_destroy(); + nsgtk_hotlist_destroy(); + + free(toolbar_indices_file_location); + + free(nsgtk_config_home); + + gtk_fetch_filetype_fin(); +} + +static nserror gui_launch_url(struct nsurl *url) +{ + gboolean ok; + GError *error = NULL; + + ok = nsgtk_show_uri(NULL, nsurl_access(url), GDK_CURRENT_TIME, &error); + if (ok == TRUE) { + return NSERROR_OK; + } + + if (error) { + nsgtk_warning(messages_get("URIOpenError"), error->message); + g_error_free(error); + } + return NSERROR_NO_FETCH_HANDLER; +} + +/* exported function documented in gtk/warn.h */ +nserror nsgtk_warning(const char *warning, const char *detail) +{ + char buf[300]; /* 300 is the size the RISC OS GUI uses */ + static GtkWindow *nsgtk_warning_window; + GtkLabel *WarningLabel; + + LOG("%s %s", warning, detail ? detail : ""); + fflush(stdout); + + nsgtk_warning_window = GTK_WINDOW(gtk_builder_get_object(warning_builder, "wndWarning")); + WarningLabel = GTK_LABEL(gtk_builder_get_object(warning_builder, + "labelWarning")); + + snprintf(buf, sizeof(buf), "%s %s", messages_get(warning), + detail ? detail : ""); + buf[sizeof(buf) - 1] = 0; + + gtk_label_set_text(WarningLabel, buf); + + gtk_widget_show_all(GTK_WIDGET(nsgtk_warning_window)); + + return NSERROR_OK; +} + + +static void nsgtk_PDF_set_pass(GtkButton *w, gpointer data) +{ + char **owner_pass = ((void **)data)[0]; + char **user_pass = ((void **)data)[1]; + GtkWindow *wnd = ((void **)data)[2]; + GtkBuilder *password_builder = ((void **)data)[3]; + char *path = ((void **)data)[4]; + + char *op, *op1; + char *up, *up1; + + op = strdup(gtk_entry_get_text( + GTK_ENTRY(gtk_builder_get_object(password_builder, + "entryPDFOwnerPassword")))); + op1 = strdup(gtk_entry_get_text( + GTK_ENTRY(gtk_builder_get_object(password_builder, + "entryPDFOwnerPassword1")))); + up = strdup(gtk_entry_get_text( + GTK_ENTRY(gtk_builder_get_object(password_builder, + "entryPDFUserPassword")))); + up1 = strdup(gtk_entry_get_text( + GTK_ENTRY(gtk_builder_get_object(password_builder, + "entryPDFUserPassword1")))); + + + if (op[0] == '\0') { + gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder, + "labelInfo")), + "Owner password must be at least 1 character long:"); + free(op); + free(up); + } else if (!strcmp(op, up)) { + gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder, + "labelInfo")), + "User and owner passwords must be different:"); + free(op); + free(up); + } else if (!strcmp(op, op1) && !strcmp(up, up1)) { + + *owner_pass = op; + if (up[0] == '\0') + free(up); + else + *user_pass = up; + + free(data); + gtk_widget_destroy(GTK_WIDGET(wnd)); + g_object_unref(G_OBJECT(password_builder)); + + save_pdf(path); + + free(path); + } else { + gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder, + "labelInfo")), "Passwords not confirmed:"); + free(op); + free(up); + } + + free(op1); + free(up1); +} + +static void nsgtk_PDF_no_pass(GtkButton *w, gpointer data) +{ + GtkWindow *wnd = ((void **)data)[2]; + GtkBuilder *password_builder = ((void **)data)[3]; + char *path = ((void **)data)[4]; + + free(data); + + gtk_widget_destroy(GTK_WIDGET(wnd)); + g_object_unref(G_OBJECT(password_builder)); + + save_pdf(path); + + free(path); +} + +static void nsgtk_pdf_password(char **owner_pass, char **user_pass, char *path) +{ + GtkButton *ok, *no; + GtkWindow *wnd; + void **data; + GtkBuilder *password_builder; + nserror res; + + res = nsgtk_builder_new_from_resname("password", &password_builder); + if (res != NSERROR_OK) { + LOG("Password UI builder init failed"); + return; + } + + gtk_builder_connect_signals(password_builder, NULL); + + wnd = GTK_WINDOW(gtk_builder_get_object(password_builder, + "wndPDFPassword")); + + data = malloc(5 * sizeof(void *)); + + *owner_pass = NULL; + *user_pass = NULL; + + data[0] = owner_pass; + data[1] = user_pass; + data[2] = wnd; + data[3] = password_builder; + data[4] = path; + + ok = GTK_BUTTON(gtk_builder_get_object(password_builder, + "buttonPDFSetPassword")); + no = GTK_BUTTON(gtk_builder_get_object(password_builder, + "buttonPDFNoPassword")); + + g_signal_connect(G_OBJECT(ok), "clicked", + G_CALLBACK(nsgtk_PDF_set_pass), (gpointer)data); + g_signal_connect(G_OBJECT(no), "clicked", + G_CALLBACK(nsgtk_PDF_no_pass), (gpointer)data); + + gtk_widget_show(GTK_WIDGET(wnd)); +} + + +uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key) +{ + /* this function will need to become much more complex to support + * everything that the RISC OS version does. But this will do for + * now. I hope. + */ + switch (key->keyval) { + + case GDK_KEY(Tab): + return NS_KEY_TAB; + + case GDK_KEY(BackSpace): + if (key->state & GDK_SHIFT_MASK) + return NS_KEY_DELETE_LINE_START; + else + return NS_KEY_DELETE_LEFT; + case GDK_KEY(Delete): + if (key->state & GDK_SHIFT_MASK) + return NS_KEY_DELETE_LINE_END; + else + return NS_KEY_DELETE_RIGHT; + case GDK_KEY(Linefeed): return 13; + case GDK_KEY(Return): return 10; + case GDK_KEY(Left): return NS_KEY_LEFT; + case GDK_KEY(Right): return NS_KEY_RIGHT; + case GDK_KEY(Up): return NS_KEY_UP; + case GDK_KEY(Down): return NS_KEY_DOWN; + case GDK_KEY(Home): + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_TEXT_START; + else + return NS_KEY_LINE_START; + case GDK_KEY(End): + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_TEXT_END; + else + return NS_KEY_LINE_END; + case GDK_KEY(Page_Up): + return NS_KEY_PAGE_UP; + case GDK_KEY(Page_Down): + return NS_KEY_PAGE_DOWN; + case 'a': + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_SELECT_ALL; + return gdk_keyval_to_unicode(key->keyval); + case 'u': + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_DELETE_LINE; + return gdk_keyval_to_unicode(key->keyval); + case 'c': + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_COPY_SELECTION; + return gdk_keyval_to_unicode(key->keyval); + case 'v': + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_PASTE; + return gdk_keyval_to_unicode(key->keyval); + case 'x': + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_CUT_SELECTION; + return gdk_keyval_to_unicode(key->keyval); + case 'Z': + case 'y': + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_REDO; + return gdk_keyval_to_unicode(key->keyval); + case 'z': + if (key->state & GDK_CONTROL_MASK) + return NS_KEY_UNDO; + return gdk_keyval_to_unicode(key->keyval); + case GDK_KEY(Escape): + return NS_KEY_ESCAPE; + + /* Modifiers - do nothing for now */ + case GDK_KEY(Shift_L): + case GDK_KEY(Shift_R): + case GDK_KEY(Control_L): + case GDK_KEY(Control_R): + case GDK_KEY(Caps_Lock): + case GDK_KEY(Shift_Lock): + case GDK_KEY(Meta_L): + case GDK_KEY(Meta_R): + case GDK_KEY(Alt_L): + case GDK_KEY(Alt_R): + case GDK_KEY(Super_L): + case GDK_KEY(Super_R): + case GDK_KEY(Hyper_L): + case GDK_KEY(Hyper_R): + return 0; + + default: + return gdk_keyval_to_unicode(key->keyval); + } +} + + +/** + * create directory name and check it is acessible and a directory. + */ +static nserror +check_dirname(const char *path, const char *leaf, char **dirname_out) +{ + nserror ret; + char *dirname = NULL; + struct stat dirname_stat; + + ret = netsurf_mkpath(&dirname, NULL, 2, path, leaf); + if (ret != NSERROR_OK) { + return ret; + } + + /* ensure access is possible and the entry is actualy + * a directory. + */ + if (stat(dirname, &dirname_stat) == 0) { + if (S_ISDIR(dirname_stat.st_mode)) { + if (access(dirname, R_OK | W_OK) == 0) { + *dirname_out = dirname; + return NSERROR_OK; + } else { + ret = NSERROR_PERMISSION; + } + } else { + ret = NSERROR_NOT_DIRECTORY; + } + } else { + ret = NSERROR_NOT_FOUND; + } + + free(dirname); + + return ret; +} + +/** + * Get the path to the config directory. + * + * @param config_home_out Path to configuration directory. + * @return NSERROR_OK on sucess and \a config_home_out updated else error code. + */ +static nserror get_config_home(char **config_home_out) +{ + nserror ret; + char *home_dir; + char *xdg_config_dir; + char *config_home; + + home_dir = getenv("HOME"); + + /* The old $HOME/.netsurf/ directory should be used if it + * exists and is accessible. + */ + if (home_dir != NULL) { + ret = check_dirname(home_dir, ".netsurf", &config_home); + if (ret == NSERROR_OK) { + LOG("\"%s\"", config_home); + *config_home_out = config_home; + return ret; + } + } + + /* $XDG_CONFIG_HOME defines the base directory + * relative to which user specific configuration files + * should be stored. + */ + xdg_config_dir = getenv("XDG_CONFIG_HOME"); + + if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) { + /* If $XDG_CONFIG_HOME is either not set or empty, a + * default equal to $HOME/.config should be used. + */ + + /** @todo the meaning of empty is never defined so I + * am assuming it is a zero length string but is it + * supposed to mean "whitespace" and if so what counts + * as whitespace? (are tabs etc. counted or should + * isspace() be used) + */ + + /* the HOME envvar is required */ + if (home_dir == NULL) { + return NSERROR_NOT_DIRECTORY; + } + + ret = check_dirname(home_dir, ".config/netsurf", &config_home); + if (ret != NSERROR_OK) { + return ret; + } + } else { + ret = check_dirname(xdg_config_dir, "netsurf", &config_home); + if (ret != NSERROR_OK) { + return ret; + } + } + + LOG("\"%s\"", config_home); + + *config_home_out = config_home; + return NSERROR_OK; +} + +static nserror create_config_home(char **config_home_out) +{ + char *config_home = NULL; + char *home_dir; + char *xdg_config_dir; + nserror ret; + + LOG("Attempting to create configuration directory"); + + /* $XDG_CONFIG_HOME defines the base directory + * relative to which user specific configuration files + * should be stored. + */ + xdg_config_dir = getenv("XDG_CONFIG_HOME"); + + if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) { + home_dir = getenv("HOME"); + + if ((home_dir == NULL) || (*home_dir == 0)) { + return NSERROR_NOT_DIRECTORY; + } + + ret = netsurf_mkpath(&config_home, NULL, 4, home_dir, ".config","netsurf", "/"); + if (ret != NSERROR_OK) { + return ret; + } + } else { + ret = netsurf_mkpath(&config_home, NULL, 3, xdg_config_dir, "netsurf", "/"); + if (ret != NSERROR_OK) { + return ret; + } + } + + /* ensure all elements of path exist (the trailing / is required) */ + ret = netsurf_mkdir_all(config_home); + if (ret != NSERROR_OK) { + free(config_home); + return ret; + } + + /* strip the trailing separator */ + config_home[strlen(config_home) - 1] = 0; + + LOG("\"%s\"", config_home); + + *config_home_out = config_home; + + return NSERROR_OK; +} + +/** + * Get the path to the cache directory. + * + * @param cache_home_out Path to cache directory. + * @return NSERROR_OK on sucess and \a cache_home_out updated else error code. + */ +static nserror get_cache_home(char **cache_home_out) +{ + nserror ret; + char *xdg_cache_dir; + char *cache_home; + char *home_dir; + + /* $XDG_CACHE_HOME defines the base directory relative to + * which user specific non-essential data files should be + * stored. + */ + xdg_cache_dir = getenv("XDG_CACHE_HOME"); + + if ((xdg_cache_dir == NULL) || (*xdg_cache_dir == 0)) { + /* If $XDG_CACHE_HOME is either not set or empty, a + * default equal to $HOME/.cache should be used. + */ + + home_dir = getenv("HOME"); + + /* the HOME envvar is required */ + if (home_dir == NULL) { + return NSERROR_NOT_DIRECTORY; + } + + ret = check_dirname(home_dir, ".cache/netsurf", &cache_home); + if (ret != NSERROR_OK) { + return ret; + } + } else { + ret = check_dirname(xdg_cache_dir, "netsurf", &cache_home); + if (ret != NSERROR_OK) { + return ret; + } + } + + LOG("\"%s\"", cache_home); + + *cache_home_out = cache_home; + return NSERROR_OK; +} + +static nserror create_cache_home(char **cache_home_out) +{ + char *cache_home = NULL; + char *home_dir; + char *xdg_cache_dir; + nserror ret; + + LOG("Attempting to create configuration directory"); + + /* $XDG_CACHE_HOME defines the base directory + * relative to which user specific cache files + * should be stored. + */ + xdg_cache_dir = getenv("XDG_CACHE_HOME"); + + if ((xdg_cache_dir == NULL) || (*xdg_cache_dir == 0)) { + home_dir = getenv("HOME"); + + if ((home_dir == NULL) || (*home_dir == 0)) { + return NSERROR_NOT_DIRECTORY; + } + + ret = netsurf_mkpath(&cache_home, NULL, 4, home_dir, ".cache", "netsurf", "/"); + if (ret != NSERROR_OK) { + return ret; + } + } else { + ret = netsurf_mkpath(&cache_home, NULL, 3, xdg_cache_dir, "netsurf", "/"); + if (ret != NSERROR_OK) { + return ret; + } + } + + /* ensure all elements of path exist (the trailing / is required) */ + ret = netsurf_mkdir_all(cache_home); + if (ret != NSERROR_OK) { + free(cache_home); + return ret; + } + + /* strip the trailing separator */ + cache_home[strlen(cache_home) - 1] = 0; + + LOG("\"%s\"", cache_home); + + *cache_home_out = cache_home; + + return NSERROR_OK; +} + +static nserror nsgtk_option_init(int *pargc, char** argv) +{ + nserror ret; + char *choices = NULL; + + /* user options setup */ + ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default); + if (ret != NSERROR_OK) { + return ret; + } + + /* Attempt to load the user choices */ + ret = netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); + if (ret == NSERROR_OK) { + nsoption_read(choices, nsoptions); + free(choices); + } + + /* overide loaded options with those from commandline */ + nsoption_commandline(pargc, argv, nsoptions); + + /* ensure all options fall within sensible bounds */ + + /* Attempt to handle nonsense status bar widths. These may exist + * in people's Choices as the GTK front end used to abuse the + * status bar width option by using it for an absolute value in px. + * The GTK front end now correctly uses it as a proportion of window + * width. Here we assume that a value of less than 15% is wrong + * and set to the default two thirds. */ + if (nsoption_int(toolbar_status_size) < 1500) { + nsoption_set_int(toolbar_status_size, 6667); + } + + return NSERROR_OK; +} + +static struct gui_misc_table nsgtk_misc_table = { + .schedule = nsgtk_schedule, + .warning = nsgtk_warning, + + .quit = gui_quit, + .launch_url = gui_launch_url, + .cert_verify = gtk_cert_verify, + .login = gui_401login_open, + .pdf_password = nsgtk_pdf_password, +}; + + +static nserror nsgtk_messages_init(char **respaths) +{ + const char *messages; + nserror ret; + const uint8_t *data; + size_t data_size; + + ret = nsgtk_data_from_resname("Messages", &data, &data_size); + if (ret == NSERROR_OK) { + ret = messages_add_from_inline(data, data_size); + } else { + /* Obtain path to messages */ + ret = nsgtk_path_from_resname("Messages", &messages); + if (ret == NSERROR_OK) { + ret = messages_add_from_file(messages); + } + } + return ret; +} + +/** + * Main entry point from OS. + */ +int main(int argc, char** argv) +{ + char *cache_home = NULL; + nserror ret; + struct netsurf_table nsgtk_table = { + .misc = &nsgtk_misc_table, + .window = nsgtk_window_table, + .clipboard = nsgtk_clipboard_table, + .download = nsgtk_download_table, + .fetch = nsgtk_fetch_table, + .llcache = filesystem_llcache_table, + .search = nsgtk_search_table, + .search_web = nsgtk_search_web_table, + .bitmap = nsgtk_bitmap_table, + .layout = nsgtk_layout_table, + }; + + ret = netsurf_register(&nsgtk_table); + if (ret != NSERROR_OK) { + die("NetSurf operation table failed registration\n"); + } + + /* Locate the correct user configuration directory path */ + ret = get_config_home(&nsgtk_config_home); + if (ret == NSERROR_NOT_FOUND) { + /* no config directory exists yet so try to create one */ + ret = create_config_home(&nsgtk_config_home); + } + if (ret != NSERROR_OK) { + LOG("Unable to locate a configuration directory."); + nsgtk_config_home = NULL; + } + + /* Initialise gtk */ + gtk_init(&argc, &argv); + + /* initialise logging. Not fatal if it fails but not much we + * can do about it either. + */ + nslog_init(nslog_stream_configure, &argc, argv); + + /* build the common resource path list */ + respaths = nsgtk_init_resource_path(nsgtk_config_home); + if (respaths == NULL) { + fprintf(stderr, "Unable to locate resources\n"); + return 1; + } + + /* initialise the gtk resource handling */ + ret = nsgtk_init_resources(respaths); + if (ret != NSERROR_OK) { + fprintf(stderr, "GTK resources failed to initialise (%s)\n", + messages_get_errorcode(ret)); + return 1; + } + + /* Initialise user options */ + ret = nsgtk_option_init(&argc, argv); + if (ret != NSERROR_OK) { + fprintf(stderr, "Options failed to initialise (%s)\n", + messages_get_errorcode(ret)); + return 1; + } + + /* Initialise translated messages */ + ret = nsgtk_messages_init(respaths); + if (ret != NSERROR_OK) { + fprintf(stderr, "Unable to load translated messages (%s)\n", + messages_get_errorcode(ret)); + LOG("Unable to load translated messages"); + /** \todo decide if message load faliure should be fatal */ + } + + /* Locate the correct user cache directory path */ + ret = get_cache_home(&cache_home); + if (ret == NSERROR_NOT_FOUND) { + /* no cache directory exists yet so try to create one */ + ret = create_cache_home(&cache_home); + } + if (ret != NSERROR_OK) { + LOG("Unable to locate a cache directory."); + } + + /* core initialisation */ + ret = netsurf_init(cache_home); + free(cache_home); + if (ret != NSERROR_OK) { + fprintf(stderr, "NetSurf core failed to initialise (%s)\n", + messages_get_errorcode(ret)); + return 1; + } + + /* run the browser */ + ret = nsgtk_init(argc, argv, respaths); + if (ret != NSERROR_OK) { + fprintf(stderr, "NetSurf gtk initialise failed (%s)\n", + messages_get_errorcode(ret)); + } else { + nsgtk_main(); + } + + /* common finalisation */ + netsurf_exit(); + + /* finalise options */ + nsoption_finalise(nsoptions, nsoptions_default); + + return 0; +} diff --git a/frontends/gtk/gui.h b/frontends/gtk/gui.h new file mode 100644 index 000000000..b6a6dc994 --- /dev/null +++ b/frontends/gtk/gui.h @@ -0,0 +1,45 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +#ifndef GTK_GUI_H +#define GTK_GUI_H + +struct nsurl; + +/** toolbar arrangement file path. */ +extern char *toolbar_indices_file_location; + +/** Directory where all configuration files are held. */ +extern char *nsgtk_config_home; + +/** favicon default pixbuf */ +extern GdkPixbuf *favicon_pixbuf; + +/** arrow down pixbuf */ +extern GdkPixbuf *arrow_down_pixbuf; + +/** resource search path vector */ +extern char **respaths; + +/** input conversion. */ +uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *eventkey); + +/** set when no windows remain open. */ +extern bool nsgtk_complete; + +#endif /* GTK_GUI_H */ diff --git a/frontends/gtk/history.c b/frontends/gtk/history.c new file mode 100644 index 000000000..9c5c0b5e4 --- /dev/null +++ b/frontends/gtk/history.c @@ -0,0 +1,273 @@ +/* + * Copyright 2006 Rob Kendrick + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +#include +#include + +#include "utils/log.h" +#include "desktop/global_history.h" +#include "desktop/plot_style.h" +#include "desktop/tree.h" +#include "desktop/textinput.h" + +#include "gtk/plotters.h" +#include "gtk/scaffolding.h" +#include "gtk/treeview.h" +#include "gtk/compat.h" +#include "gtk/resources.h" +#include "gtk/history.h" + +#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \ + GtkMenuItem *widget, gpointer g) +#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) } +#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \ + gpointer g) + +struct menu_events { + const char *widget; + GCallback handler; +}; + +/* file menu*/ +MENUPROTO(export); + +/* edit menu */ +MENUPROTO(delete_selected); +MENUPROTO(delete_all); +MENUPROTO(select_all); +MENUPROTO(clear_selection); + +/* view menu*/ +MENUPROTO(expand_all); +MENUPROTO(expand_directories); +MENUPROTO(expand_addresses); +MENUPROTO(collapse_all); +MENUPROTO(collapse_directories); +MENUPROTO(collapse_addresses); + +MENUPROTO(launch); + +static struct menu_events menu_events[] = { + + /* file menu*/ + MENUEVENT(export), + + /* edit menu */ + MENUEVENT(delete_selected), + MENUEVENT(delete_all), + MENUEVENT(select_all), + MENUEVENT(clear_selection), + + /* view menu*/ + MENUEVENT(expand_all), + MENUEVENT(expand_directories), + MENUEVENT(expand_addresses), + MENUEVENT(collapse_all), + MENUEVENT(collapse_directories), + MENUEVENT(collapse_addresses), + + MENUEVENT(launch), + {NULL, NULL} +}; + +static struct nsgtk_treeview *global_history_window; +static GtkBuilder *history_builder; +GtkWindow *wndHistory; + +/** + * Connects menu events in the global history window. + */ +static void nsgtk_history_init_menu(void) +{ + struct menu_events *event = menu_events; + GtkWidget *w; + + while (event->widget != NULL) { + w = GTK_WIDGET(gtk_builder_get_object(history_builder, + event->widget)); + if (w == NULL) { + LOG("Unable to connect menu widget ""%s""", + event->widget); + } else { + g_signal_connect(G_OBJECT(w), + "activate", + event->handler, + global_history_window); + } + event++; + } +} + +/* exported interface, documented in gtk/history.h */ +nserror nsgtk_history_init(void) +{ + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + nserror res; + + res = nsgtk_builder_new_from_resname("history", &history_builder); + if (res != NSERROR_OK) { + LOG("History UI builder init failed"); + return res; + } + gtk_builder_connect_signals(history_builder, NULL); + + wndHistory = GTK_WINDOW(gtk_builder_get_object(history_builder, + "wndHistory")); + + window = wndHistory; + + scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(history_builder, + "globalHistoryScrolled")); + + drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(history_builder, + "globalHistoryDrawingArea")); + + global_history_window = nsgtk_treeview_create(TREE_HISTORY, + window, + scrolled, + drawing_area); + if (global_history_window == NULL) { + return NSERROR_INIT_FAILED; + } + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL); + CONNECT(window, "hide", nsgtk_tree_window_hide, global_history_window); + + nsgtk_history_init_menu(); + + return NSERROR_OK; +} + + + + +/** + * Destroys the global history window and performs any other necessary cleanup + * actions. + */ +void nsgtk_history_destroy(void) +{ + /** \todo what about history_builder? */ + nsgtk_treeview_destroy(global_history_window); +} + + +/* file menu */ +MENUHANDLER(export) +{ + GtkWidget *save_dialog; + save_dialog = gtk_file_chooser_dialog_new("Save File", + wndHistory, + GTK_FILE_CHOOSER_ACTION_SAVE, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog), + getenv("HOME") ? getenv("HOME") : "/"); + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog), + "history.html"); + + if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(save_dialog)); + + global_history_export(filename, NULL); + g_free(filename); + } + + gtk_widget_destroy(save_dialog); + + return TRUE; +} + +/* edit menu */ +MENUHANDLER(delete_selected) +{ + global_history_keypress(NS_KEY_DELETE_LEFT); + return TRUE; +} + +MENUHANDLER(delete_all) +{ + global_history_keypress(NS_KEY_SELECT_ALL); + global_history_keypress(NS_KEY_DELETE_LEFT); + return TRUE; +} + +MENUHANDLER(select_all) +{ + global_history_keypress(NS_KEY_SELECT_ALL); + return TRUE; +} + +MENUHANDLER(clear_selection) +{ + global_history_keypress(NS_KEY_CLEAR_SELECTION); + return TRUE; +} + +/* view menu*/ +MENUHANDLER(expand_all) +{ + global_history_expand(false); + return TRUE; +} + +MENUHANDLER(expand_directories) +{ + global_history_expand(true); + return TRUE; +} + +MENUHANDLER(expand_addresses) +{ + global_history_expand(false); + return TRUE; +} + +MENUHANDLER(collapse_all) +{ + global_history_contract(true); + return TRUE; +} + +MENUHANDLER(collapse_directories) +{ + global_history_contract(true); + return TRUE; +} + +MENUHANDLER(collapse_addresses) +{ + global_history_contract(false); + return TRUE; +} + +MENUHANDLER(launch) +{ + global_history_keypress(NS_KEY_CR); + return TRUE; +} diff --git a/frontends/gtk/history.h b/frontends/gtk/history.h new file mode 100644 index 000000000..c0f7db2bd --- /dev/null +++ b/frontends/gtk/history.h @@ -0,0 +1,39 @@ +/* + * Copyright 2006 Rob Kendrick + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +#ifndef __NSGTK_HISTORY_H__ +#define __NSGTK_HISTORY_H__ + +#include + +extern GtkWindow *wndHistory; + +/** + * Creates the window for the global history tree. + * + * \return NSERROR_OK on sucess else appropriate error code. + */ +nserror nsgtk_history_init(void); + +/** + * Free global resources associated with the gtk history window. + */ +void nsgtk_history_destroy(void); + +#endif /* __NSGTK_HISTORY_H__ */ diff --git a/frontends/gtk/hotlist.c b/frontends/gtk/hotlist.c new file mode 100644 index 000000000..06fd5cd69 --- /dev/null +++ b/frontends/gtk/hotlist.c @@ -0,0 +1,280 @@ +/* + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +#include +#include + +#include "utils/log.h" +#include "utils/nsoption.h" +#include "desktop/hotlist.h" +#include "desktop/tree.h" + +#include "gtk/plotters.h" +#include "gtk/scaffolding.h" +#include "gtk/treeview.h" +#include "gtk/compat.h" +#include "gtk/resources.h" +#include "gtk/hotlist.h" + +#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \ + GtkMenuItem *widget, gpointer g) +#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) } +#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \ + gpointer g) + +struct menu_events { + const char *widget; + GCallback handler; +}; + + +/* file menu*/ +MENUPROTO(export); +MENUPROTO(new_folder); +MENUPROTO(new_entry); + +/* edit menu */ +MENUPROTO(edit_selected); +MENUPROTO(delete_selected); +MENUPROTO(select_all); +MENUPROTO(clear_selection); + +/* view menu*/ +MENUPROTO(expand_all); +MENUPROTO(expand_directories); +MENUPROTO(expand_addresses); +MENUPROTO(collapse_all); +MENUPROTO(collapse_directories); +MENUPROTO(collapse_addresses); + +MENUPROTO(launch); + +static struct menu_events menu_events[] = { + + /* file menu*/ + MENUEVENT(export), + MENUEVENT(new_folder), + MENUEVENT(new_entry), + + /* edit menu */ + MENUEVENT(edit_selected), + MENUEVENT(delete_selected), + MENUEVENT(select_all), + MENUEVENT(clear_selection), + + /* view menu*/ + MENUEVENT(expand_all), + MENUEVENT(expand_directories), + MENUEVENT(expand_addresses), + MENUEVENT(collapse_all), + MENUEVENT(collapse_directories), + MENUEVENT(collapse_addresses), + + MENUEVENT(launch), + {NULL, NULL} +}; + +static struct nsgtk_treeview *hotlist_treeview; +static GtkBuilder *hotlist_builder; +GtkWindow *wndHotlist; + +/** + * Connects menu events in the hotlist window. + */ +static void nsgtk_hotlist_init_menu(void) +{ + struct menu_events *event = menu_events; + GtkWidget *w; + + while (event->widget != NULL) { + w = GTK_WIDGET(gtk_builder_get_object(hotlist_builder, event->widget)); + if (w == NULL) { + LOG("Unable to connect menu widget ""%s""", event->widget); } else { + g_signal_connect(G_OBJECT(w), "activate", event->handler, hotlist_treeview); + } + event++; + } +} + +/* exported interface docuemnted in gtk/hotlist.h */ +nserror nsgtk_hotlist_init(void) +{ + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + nserror res; + + res = nsgtk_builder_new_from_resname("hotlist", &hotlist_builder); + if (res != NSERROR_OK) { + LOG("Cookie UI builder init failed"); + return res; + } + + gtk_builder_connect_signals(hotlist_builder, NULL); + + wndHotlist = GTK_WINDOW(gtk_builder_get_object(hotlist_builder, "wndHotlist")); + window = wndHotlist; + + scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(hotlist_builder, + "hotlistScrolled")); + + drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(hotlist_builder, + "hotlistDrawingArea")); + + + tree_hotlist_path = nsoption_charp(hotlist_path); + hotlist_treeview = nsgtk_treeview_create(TREE_HOTLIST, window, + scrolled, drawing_area); + + if (hotlist_treeview == NULL) { + return NSERROR_INIT_FAILED; + } + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL); + CONNECT(window, "hide", nsgtk_tree_window_hide, hotlist_treeview); + + nsgtk_hotlist_init_menu(); + + return NSERROR_OK; +} + + + + +/** + * Destroys the hotlist window and performs any other necessary cleanup actions. + */ +void nsgtk_hotlist_destroy(void) +{ + /** \todo what about hotlist_builder? */ + nsgtk_treeview_destroy(hotlist_treeview); +} + + +/* file menu*/ +MENUHANDLER(export) +{ + GtkWidget *save_dialog; + save_dialog = gtk_file_chooser_dialog_new("Save File", + wndHotlist, + GTK_FILE_CHOOSER_ACTION_SAVE, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog), + getenv("HOME") ? getenv("HOME") : "/"); + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog), + "hotlist.html"); + + if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(save_dialog)); + + hotlist_export(filename, NULL); + g_free(filename); + } + + gtk_widget_destroy(save_dialog); + + return TRUE; +} + +MENUHANDLER(new_folder) +{ + hotlist_add_folder(NULL, false, 0); + return TRUE; +} + +MENUHANDLER(new_entry) +{ + hotlist_add_entry(NULL, NULL, false, 0); + return TRUE; +} + +/* edit menu */ +MENUHANDLER(edit_selected) +{ + hotlist_edit_selection(); + return TRUE; +} + +MENUHANDLER(delete_selected) +{ + hotlist_keypress(NS_KEY_DELETE_LEFT); + return TRUE; +} + +MENUHANDLER(select_all) +{ + hotlist_keypress(NS_KEY_SELECT_ALL); + return TRUE; +} + +MENUHANDLER(clear_selection) +{ + hotlist_keypress(NS_KEY_CLEAR_SELECTION); + return TRUE; +} + +/* view menu*/ +MENUHANDLER(expand_all) +{ + hotlist_expand(false); + return TRUE; +} + +MENUHANDLER(expand_directories) +{ + hotlist_expand(true); + return TRUE; +} + +MENUHANDLER(expand_addresses) +{ + hotlist_expand(false); + return TRUE; +} + +MENUHANDLER(collapse_all) +{ + hotlist_contract(true); + return TRUE; +} + +MENUHANDLER(collapse_directories) +{ + hotlist_contract(true); + return TRUE; +} + +MENUHANDLER(collapse_addresses) +{ + hotlist_contract(false); + return TRUE; +} + +MENUHANDLER(launch) +{ + hotlist_keypress(NS_KEY_CR); + return TRUE; +} diff --git a/frontends/gtk/hotlist.h b/frontends/gtk/hotlist.h new file mode 100644 index 000000000..01e5a86c5 --- /dev/null +++ b/frontends/gtk/hotlist.h @@ -0,0 +1,40 @@ +/* + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +/** \file + * GTK hotlist (interface). + */ + +#ifndef __NSGTK_HOTLIST_H__ +#define __NSGTK_HOTLIST_H__ + +#include + +extern GtkWindow *wndHotlist; + +/** + * Initialise the gtk specific hotlist (bookmarks) display. + * + * \return NSERROR_OK on success else appropriate error code on faliure. + */ +nserror nsgtk_hotlist_init(void); + + +void nsgtk_hotlist_destroy(void); + +#endif /* __NSGTK_HOTLIST_H__ */ diff --git a/frontends/gtk/layout_pango.c b/frontends/gtk/layout_pango.c new file mode 100644 index 000000000..49b629399 --- /dev/null +++ b/frontends/gtk/layout_pango.c @@ -0,0 +1,309 @@ +/* + * Copyright 2005 James Bursa + * + * 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 . + */ + +/** + * \file + * GTK implementation of layout handling using pango. + * + * Pango is used handle and render fonts. + */ + + +#include +#include +#include + +#include "utils/log.h" +#include "utils/nsoption.h" +#include "desktop/gui_layout.h" + +#include "gtk/layout_pango.h" +#include "gtk/plotters.h" + +static PangoContext *nsfont_pango_context = NULL; +static PangoLayout *nsfont_pango_layout = NULL; + +static inline void nsfont_pango_check(void) +{ + if (nsfont_pango_context == NULL) { + LOG("Creating nsfont_pango_context."); + nsfont_pango_context = gdk_pango_context_get(); + } + + if (nsfont_pango_layout == NULL) { + LOG("Creating nsfont_pango_layout."); + nsfont_pango_layout = pango_layout_new(nsfont_pango_context); + } +} + +/** + * Measure the width of a string. + * + * \param[in] fstyle plot style for this text + * \param[in] string UTF-8 string to measure + * \param[in] length length of string, in bytes + * \param[out] width updated to width of string[0..length) + * \return NSERROR_OK and width updated or appropriate error code on faliure + */ +static nserror +nsfont_width(const plot_font_style_t *fstyle, + const char *string, + size_t length, + int *width) +{ + PangoFontDescription *desc; + + if (length == 0) { + *width = 0; + return NSERROR_OK; + } + + nsfont_pango_check(); + + desc = nsfont_style_to_description(fstyle); + pango_layout_set_font_description(nsfont_pango_layout, desc); + pango_font_description_free(desc); + + pango_layout_set_text(nsfont_pango_layout, string, length); + + pango_layout_get_pixel_size(nsfont_pango_layout, width, 0); + + /* LOG("fstyle: %p string:\"%.*s\", length: %u, width: %dpx", + fstyle, length, string, length, *width); + */ + + return NSERROR_OK; +} + + +/** + * Find the position in a string where an x coordinate falls. + * + * \param[in] fstyle style for this text + * \param[in] string UTF-8 string to measure + * \param[in] length length of string, in bytes + * \param[in] x coordinate to search for + * \param[out] char_offset updated to offset in string of actual_x, [0..length] + * \param[out] actual_x updated to x coordinate of character closest to x + * \return NSERROR_OK and char_offset and actual_x updated or appropriate + * error code on faliure + */ +static nserror +nsfont_position_in_string(const plot_font_style_t *fstyle, + const char *string, + size_t length, + int x, + size_t *char_offset, + int *actual_x) +{ + int index; + PangoFontDescription *desc; + PangoRectangle pos; + + nsfont_pango_check(); + + desc = nsfont_style_to_description(fstyle); + pango_layout_set_font_description(nsfont_pango_layout, desc); + pango_font_description_free(desc); + + pango_layout_set_text(nsfont_pango_layout, string, length); + + if (pango_layout_xy_to_index(nsfont_pango_layout, + x * PANGO_SCALE, + 0, &index, 0) == FALSE) { + index = length; + } + + pango_layout_index_to_pos(nsfont_pango_layout, index, &pos); + + *char_offset = index; + *actual_x = PANGO_PIXELS(pos.x); + + return NSERROR_OK; +} + + +/** + * Find where to split a string to make it fit a width. + * + * \param[in] fstyle style for this text + * \param[in] string UTF-8 string to measure + * \param[in] length length of string, in bytes + * \param[in] x width available + * \param[out] char_offset updated to offset in string of actual_x, [1..length] + * \param[out] actual_x updated to x coordinate of character closest to x + * \return NSERROR_OK or appropriate error code on faliure + * + * On exit, char_offset indicates first character after split point. + * + * \note char_offset of 0 must never be returned. + * + * Returns: + * char_offset giving split point closest to x, where actual_x <= x + * else + * char_offset giving split point closest to x, where actual_x > x + * + * Returning char_offset == length means no split possible + */ +static nserror +nsfont_split(const plot_font_style_t *fstyle, + const char *string, + size_t length, + int x, + size_t *char_offset, + int *actual_x) +{ + int index = length; + PangoFontDescription *desc; + PangoContext *context; + PangoLayout *layout; + PangoLayoutLine *line; + + context = gdk_pango_context_get(); + layout = pango_layout_new(context); + + desc = nsfont_style_to_description(fstyle); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + pango_layout_set_text(layout, string, length); + + /* Limit width of layout to the available width */ + pango_layout_set_width(layout, x * PANGO_SCALE); + + /* Request word wrapping */ + pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + + /* Prevent pango treating linebreak characters as line breaks */ + pango_layout_set_single_paragraph_mode(layout, TRUE); + + /* Obtain the second line of the layout (if there is one) */ + line = pango_layout_get_line(layout, 1); + if (line != NULL) { + /* Pango split the text. The line's start_index indicates the + * start of the character after the line break. */ + index = line->start_index; + } + + g_object_unref(layout); + g_object_unref(context); + + *char_offset = index; + /* Obtain the pixel offset of the split character */ + nsfont_width(fstyle, string, index, actual_x); + + return NSERROR_OK; +} + + +/** + * Render a string. + * + * \param x x coordinate + * \param y y coordinate + * \param string UTF-8 string to measure + * \param length length of string + * \param fstyle plot style for this text + * \return true on success, false on error and error reported + */ +bool nsfont_paint(int x, int y, const char *string, size_t length, + const plot_font_style_t *fstyle) +{ + PangoFontDescription *desc; + PangoLayout *layout; + PangoLayoutLine *line; + + if (length == 0) + return true; + + layout = pango_cairo_create_layout(current_cr); + + desc = nsfont_style_to_description(fstyle); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + pango_layout_set_text(layout, string, length); + + line = pango_layout_get_line_readonly(layout, 0); + cairo_move_to(current_cr, x, y); + nsgtk_set_colour(fstyle->foreground); + pango_cairo_show_layout_line(current_cr, line); + + g_object_unref(layout); + + return true; +} + + +/* exported interface documented in gtk/layout_pango.h */ +PangoFontDescription * +nsfont_style_to_description(const plot_font_style_t *fstyle) +{ + unsigned int size; + PangoFontDescription *desc; + PangoStyle style = PANGO_STYLE_NORMAL; + + switch (fstyle->family) { + case PLOT_FONT_FAMILY_SERIF: + desc = pango_font_description_from_string(nsoption_charp(font_serif)); + break; + case PLOT_FONT_FAMILY_MONOSPACE: + desc = pango_font_description_from_string(nsoption_charp(font_mono)); + break; + case PLOT_FONT_FAMILY_CURSIVE: + desc = pango_font_description_from_string(nsoption_charp(font_cursive)); + break; + case PLOT_FONT_FAMILY_FANTASY: + desc = pango_font_description_from_string(nsoption_charp(font_fantasy)); + break; + case PLOT_FONT_FAMILY_SANS_SERIF: + default: + desc = pango_font_description_from_string(nsoption_charp(font_sans)); + break; + } + + size = (fstyle->size * PANGO_SCALE) / FONT_SIZE_SCALE; + + if (fstyle->flags & FONTF_ITALIC) + style = PANGO_STYLE_ITALIC; + else if (fstyle->flags & FONTF_OBLIQUE) + style = PANGO_STYLE_OBLIQUE; + + pango_font_description_set_style(desc, style); + + pango_font_description_set_weight(desc, (PangoWeight) fstyle->weight); + + pango_font_description_set_size(desc, size); + + if (fstyle->flags & FONTF_SMALLCAPS) { + pango_font_description_set_variant(desc, + PANGO_VARIANT_SMALL_CAPS); + } else { + pango_font_description_set_variant(desc, PANGO_VARIANT_NORMAL); + } + + return desc; +} + +static struct gui_layout_table layout_table = { + .width = nsfont_width, + .position = nsfont_position_in_string, + .split = nsfont_split, +}; + +struct gui_layout_table *nsgtk_layout_table = &layout_table; diff --git a/frontends/gtk/layout_pango.h b/frontends/gtk/layout_pango.h new file mode 100644 index 000000000..137cebe68 --- /dev/null +++ b/frontends/gtk/layout_pango.h @@ -0,0 +1,43 @@ +/* + * Copyright 2005 James Bursa + * + * 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 . + */ + +/** + * \file + * Interface to GTK layout handling using pango. + */ + +#ifndef _NETSURF_GTK_LAYOUT_PANGO_H_ +#define _NETSURF_GTK_LAYOUT_PANGO_H_ + +#include + +struct plot_font_style; + +extern struct gui_layout_table *nsgtk_layout_table; + +bool nsfont_paint(int x, int y, const char *string, size_t length, const struct plot_font_style *fstyle); + +/** + * Convert a plot style to a PangoFontDescription. + * + * \param fstyle plot style for this text + * \return A new Pango font description + */ +PangoFontDescription *nsfont_style_to_description(const struct plot_font_style *fstyle); + +#endif diff --git a/frontends/gtk/login.c b/frontends/gtk/login.c new file mode 100644 index 000000000..ee77052d7 --- /dev/null +++ b/frontends/gtk/login.c @@ -0,0 +1,232 @@ +/* + * Copyright 2006 Rob Kendrick + * + * 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 . + */ + +#include +#include +#include + +#include "utils/log.h" +#include "utils/nsurl.h" +#include "content/urldb.h" + +#include "gtk/resources.h" +#include "gtk/login.h" + +/** login window session data */ +struct session_401 { + nsurl *url; /**< URL being fetched */ + lwc_string *host; /**< Host for user display */ + char *realm; /**< Authentication realm */ + nserror (*cb)(bool proceed, void *pw); /**< Continuation callback */ + void *cbpw; /**< Continuation data */ + GtkBuilder *x; /**< Our builder windows */ + GtkWindow *wnd; /**< The login window itself */ + GtkEntry *user; /**< Widget with username */ + GtkEntry *pass; /**< Widget with password */ +}; + +/** + * Destroy login window and free all associated resources + * + * \param session The login window session to destroy. + */ +static void destroy_login_window(struct session_401 *session) +{ + nsurl_unref(session->url); + lwc_string_unref(session->host); + free(session->realm); + gtk_widget_destroy(GTK_WIDGET(session->wnd)); + g_object_unref(G_OBJECT(session->x)); + free(session); +} + + +/** + * process next signal in entry widgets. + * + * \param w current widget + * \param data next widget + */ +static void nsgtk_login_next(GtkWidget *w, gpointer data) +{ + gtk_widget_grab_focus(GTK_WIDGET(data)); +} + + +/** + * handler called when navigation is continued + * + * \param w current widget + * \param data login window session + */ +static void nsgtk_login_ok_clicked(GtkButton *w, gpointer data) +{ + /* close the window and destroy it, having continued the fetch + * assoicated with it. + */ + + struct session_401 *session = (struct session_401 *)data; + const gchar *user = gtk_entry_get_text(session->user); + const gchar *pass = gtk_entry_get_text(session->pass); + char *auth; + + auth = malloc(strlen(user) + strlen(pass) + 2); + sprintf(auth, "%s:%s", user, pass); + urldb_set_auth_details(session->url, session->realm, auth); + free(auth); + + session->cb(true, session->cbpw); + + destroy_login_window(session); +} + + +/** + * handler called when navigation is cancelled + * + * \param w widget + * \param data login window session + */ +static void nsgtk_login_cancel_clicked(GtkButton *w, gpointer data) +{ + struct session_401 *session = (struct session_401 *) data; + + session->cb(false, session->cbpw); + + /* close and destroy the window */ + destroy_login_window(session); +} + + +/** + * create a new instance of the login window + * + * creates login window and handles to all the widgets we're + * interested in. + * + * \param url The url causing the login. + * \param host the host being logged into + * \param realm realmm the login relates to + * \param cb callback when complete + * \param cbpw data to pass to callback + * \return NSERROR_OK on sucessful window creation or error code on faliure. + */ +static nserror +create_login_window(nsurl *url, + lwc_string *host, + const char *realm, + nserror (*cb)(bool proceed, void *pw), + void *cbpw) +{ + struct session_401 *session; + GtkWindow *wnd; + GtkLabel *lhost, *lrealm; + GtkEntry *euser, *epass; + GtkButton *bok, *bcan; + GtkBuilder* builder; + nserror res; + + session = calloc(1, sizeof(struct session_401)); + if (session == NULL) { + return NSERROR_NOMEM; + } + + res = nsgtk_builder_new_from_resname("login", &builder); + if (res != NSERROR_OK) { + free(session); + return res; + } + + gtk_builder_connect_signals(builder, NULL); + + wnd = GTK_WINDOW(gtk_builder_get_object(builder, "wndLogin")); + lhost = GTK_LABEL(gtk_builder_get_object(builder, "labelLoginHost")); + lrealm = GTK_LABEL(gtk_builder_get_object(builder, "labelLoginRealm")); + euser = GTK_ENTRY(gtk_builder_get_object(builder, "entryLoginUser")); + epass = GTK_ENTRY(gtk_builder_get_object(builder, "entryLoginPass")); + bok = GTK_BUTTON(gtk_builder_get_object(builder, "buttonLoginOK")); + bcan = GTK_BUTTON(gtk_builder_get_object(builder, "buttonLoginCan")); + + /* create and fill in our session structure */ + session->url = nsurl_ref(url); + session->host = lwc_string_ref(host); + session->realm = strdup(realm ? realm : "Secure Area"); + session->cb = cb; + session->cbpw = cbpw; + session->x = builder; + session->wnd = wnd; + session->user = euser; + session->pass = epass; + + /* fill in our new login window */ + + gtk_label_set_text(GTK_LABEL(lhost), lwc_string_data(host)); + gtk_label_set_text(lrealm, realm); + gtk_entry_set_text(euser, ""); + gtk_entry_set_text(epass, ""); + + /* attach signal handlers to the Login and Cancel buttons in our new + * window to call functions in this file to process the login + */ + g_signal_connect(G_OBJECT(bok), "clicked", + G_CALLBACK(nsgtk_login_ok_clicked), (gpointer)session); + g_signal_connect(G_OBJECT(bcan), "clicked", + G_CALLBACK(nsgtk_login_cancel_clicked), + (gpointer)session); + + /* attach signal handlers to the entry boxes such that pressing + * enter in one progresses the focus onto the next widget. + */ + + g_signal_connect(G_OBJECT(euser), "activate", + G_CALLBACK(nsgtk_login_next), (gpointer)epass); + g_signal_connect(G_OBJECT(epass), "activate", + G_CALLBACK(nsgtk_login_next), (gpointer)bok); + + /* make sure the username entry box currently has the focus */ + gtk_widget_grab_focus(GTK_WIDGET(euser)); + + /* finally, show the window */ + gtk_widget_show(GTK_WIDGET(wnd)); + + return NSERROR_OK; +} + + +/* exported function documented in gtk/login.h */ +void gui_401login_open(nsurl *url, + const char *realm, + nserror (*cb)(bool proceed, void *pw), + void *cbpw) +{ + lwc_string *host; + nserror res; + + host = nsurl_get_component(url, NSURL_HOST); + assert(host != NULL); + + res = create_login_window(url, host, realm, cb, cbpw); + if (res != NSERROR_OK) { + LOG("Login init failed"); + + /* creating login failed so cancel navigation */ + cb(false, cbpw); + } + + lwc_string_unref(host); +} diff --git a/frontends/gtk/login.h b/frontends/gtk/login.h new file mode 100644 index 000000000..00c29000c --- /dev/null +++ b/frontends/gtk/login.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Vincent Sanders + * + * 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 . + */ + +/** \file + * Login interfaces. + */ + +#ifndef __NSGTK_LOGIN_H__ +#define __NSGTK_LOGIN_H__ + +/** + * login window request. + */ +extern void gui_401login_open(struct nsurl *url, const char *realm, nserror (*cb)(bool proceed, void *pw), void *cbpw); + +#endif diff --git a/frontends/gtk/menu.c b/frontends/gtk/menu.c new file mode 100644 index 000000000..a93ef9385 --- /dev/null +++ b/frontends/gtk/menu.c @@ -0,0 +1,585 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + +#include +#include + +#include +#include + +#include "utils/messages.h" + +#include "gtk/compat.h" +#include "gtk/menu.h" +#include "gtk/warn.h" + +/** + * Adds image menu item to a menu. + * + * \param menu the menu to add the item to + * \param item_out a pointer to the item's location in the menu struct + * \param message the menu item I18n lookup value + * \param messageAccel the menu item accelerator I18n lookup value + * \param group the 'global' in a gtk sense accelerator group + * \return true if sucessful and \a item_out updated else false. + */ + +static bool nsgtk_menu_add_image_item(GtkMenu *menu, + GtkWidget **item_out, const char *message, + const char *messageAccel, GtkAccelGroup *group) +{ + unsigned int key; + GdkModifierType mod; + GtkWidget *item; + + item = nsgtk_image_menu_item_new_with_mnemonic(messages_get(message)); + if (item == NULL) { + return false; + } + + gtk_accelerator_parse(messages_get(messageAccel), &key, &mod); + if (key > 0) { + gtk_widget_add_accelerator(item, "activate", group, key, mod, + GTK_ACCEL_VISIBLE); + } + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + *item_out = item; + + return true; +} + +#define NEW_MENU(n, m) \ + n = malloc(sizeof(*n)); \ + if (n == NULL) { \ + return NULL; \ + } \ + n->m##_menu = GTK_MENU(gtk_menu_new()) + +#define IMAGE_ITEM(p, q, r, s, t)\ + nsgtk_menu_add_image_item(s->p##_menu, &(s->q##_menuitem), #r,\ + #r "Accel", t) + +#define CHECK_ITEM(p, q, r, s)\ + s->q##_menuitem = GTK_CHECK_MENU_ITEM(\ + gtk_check_menu_item_new_with_mnemonic(\ + messages_get(#r)));\ + if ((s->q##_menuitem != NULL) && (s->p##_menu != NULL)) {\ + gtk_menu_shell_append(GTK_MENU_SHELL(s->p##_menu),\ + GTK_WIDGET(s->q##_menuitem));\ + gtk_widget_show(GTK_WIDGET(s->q##_menuitem));\ + } + +#define SET_SUBMENU(q, r) \ + do { \ + r->q##_submenu = nsgtk_menu_##q##_submenu(group); \ + if ((r->q##_submenu != NULL) && \ + (r->q##_submenu->q##_menu != NULL) && \ + (r->q##_menuitem != NULL)) { \ + gtk_menu_item_set_submenu(GTK_MENU_ITEM(r->q##_menuitem), \ + GTK_WIDGET(r->q##_submenu->q##_menu)); \ + } \ + } while(0) + +#define ADD_NAMED_SEP(q, r, s) \ + do { \ + s->r##_separator = gtk_separator_menu_item_new(); \ + if ((s->r##_separator != NULL) && (s->q##_menu != NULL)) { \ + gtk_menu_shell_append(GTK_MENU_SHELL(s->q##_menu), s->r##_separator); \ + gtk_widget_show(s->r##_separator); \ + } \ + } while(0) + +#define ADD_SEP(q, r) \ + do { \ + GtkWidget *w = gtk_separator_menu_item_new(); \ + if ((w != NULL) && (r->q##_menu != NULL)) { \ + gtk_menu_shell_append(GTK_MENU_SHELL(r->q##_menu), w); \ + gtk_widget_show(w); \ + } \ + } while(0) + +#define ATTACH_PARENT(parent, msgname, menuv, group) \ + do { \ + /* create top level menu entry and attach to parent */ \ + menuv = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(messages_get(#msgname))); \ + gtk_menu_shell_append(parent, GTK_WIDGET(menuv)); \ + gtk_widget_show(GTK_WIDGET(menuv)); \ + /* attach submenu to parent */ \ + gtk_menu_item_set_submenu(menuv, GTK_WIDGET(menuv##_menu)); \ + gtk_menu_set_accel_group(menuv##_menu, group); \ + } while(0) + +/** +* creates an export submenu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_export_submenu *nsgtk_menu_export_submenu(GtkAccelGroup *group) +{ + struct nsgtk_export_submenu *ret = malloc(sizeof(struct + nsgtk_export_submenu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->export_menu = GTK_MENU(gtk_menu_new()); + if (ret->export_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + IMAGE_ITEM(export, plaintext, gtkPlainText, ret, group); + IMAGE_ITEM(export, drawfile, gtkDrawFile, ret, group); + IMAGE_ITEM(export, postscript, gtkPostScript, ret, group); + IMAGE_ITEM(export, pdf, gtkPDF, ret, group); + return ret; +} + +/** +* creates a scaleview submenu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_scaleview_submenu *nsgtk_menu_scaleview_submenu( + GtkAccelGroup *group) +{ + struct nsgtk_scaleview_submenu *ret = + malloc(sizeof(struct nsgtk_scaleview_submenu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->scaleview_menu = GTK_MENU(gtk_menu_new()); + if (ret->scaleview_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + IMAGE_ITEM(scaleview, zoomplus, gtkZoomPlus, ret, group); + IMAGE_ITEM(scaleview, zoomnormal, gtkZoomNormal, ret, group); + IMAGE_ITEM(scaleview, zoomminus, gtkZoomMinus, ret, group); + return ret; +} + +/** +* creates a tab navigation submenu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_tabs_submenu *nsgtk_menu_tabs_submenu(GtkAccelGroup *group) +{ + struct nsgtk_tabs_submenu *ret = malloc(sizeof(struct nsgtk_tabs_submenu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->tabs_menu = GTK_MENU(gtk_menu_new()); + if (ret->tabs_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + IMAGE_ITEM(tabs, nexttab, gtkNextTab, ret, group); + IMAGE_ITEM(tabs, prevtab, gtkPrevTab, ret, group); + IMAGE_ITEM(tabs, closetab, gtkCloseTab, ret, group); + + return ret; +} + +/** +* creates an images submenu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_images_submenu *nsgtk_menu_images_submenu(GtkAccelGroup *group) +{ + struct nsgtk_images_submenu *ret = + malloc(sizeof(struct nsgtk_images_submenu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->images_menu = GTK_MENU(gtk_menu_new()); + if (ret->images_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + CHECK_ITEM(images, foregroundimages, gtkForegroundImages, ret) + CHECK_ITEM(images, backgroundimages, gtkBackgroundImages, ret) + return ret; +} + +/** +* creates a toolbars submenu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_toolbars_submenu *nsgtk_menu_toolbars_submenu( + GtkAccelGroup *group) +{ + struct nsgtk_toolbars_submenu *ret = + malloc(sizeof(struct nsgtk_toolbars_submenu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->toolbars_menu = GTK_MENU(gtk_menu_new()); + if (ret->toolbars_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + CHECK_ITEM(toolbars, menubar, gtkMenuBar, ret) + if (ret->menubar_menuitem != NULL) + gtk_check_menu_item_set_active(ret->menubar_menuitem, TRUE); + CHECK_ITEM(toolbars, toolbar, gtkToolBar, ret) + if (ret->toolbar_menuitem != NULL) + gtk_check_menu_item_set_active(ret->toolbar_menuitem, TRUE); + return ret; +} + +/** +* creates a debugging submenu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_developer_submenu *nsgtk_menu_developer_submenu( + GtkAccelGroup *group) +{ + struct nsgtk_developer_submenu *dmenu = + malloc(sizeof(struct nsgtk_developer_submenu)); + if (dmenu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + dmenu->developer_menu = GTK_MENU(gtk_menu_new()); + if (dmenu->developer_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(dmenu); + return NULL; + } + + IMAGE_ITEM(developer, viewsource, gtkPageSource, dmenu, group); + IMAGE_ITEM(developer, toggledebugging, gtkToggleDebugging, dmenu, group); + IMAGE_ITEM(developer, debugboxtree, gtkDebugBoxTree, dmenu, group); + IMAGE_ITEM(developer, debugdomtree, gtkDebugDomTree, dmenu, group); + + return dmenu; +} + +/** + * creates the file menu + * + * \param group The gtk 'global' accelerator reference + * \return The new file menu or NULL on error + */ +static struct nsgtk_file_menu *nsgtk_menu_file_submenu(GtkAccelGroup *group) +{ + struct nsgtk_file_menu *fmenu; + + fmenu = malloc(sizeof(struct nsgtk_file_menu)); + if (fmenu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + + fmenu->file_menu = GTK_MENU(gtk_menu_new()); + if (fmenu->file_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(fmenu); + return NULL; + } + + IMAGE_ITEM(file, newwindow, gtkNewWindow, fmenu, group); + IMAGE_ITEM(file, newtab, gtkNewTab, fmenu, group); + IMAGE_ITEM(file, openfile, gtkOpenFile, fmenu, group); + IMAGE_ITEM(file, closewindow, gtkCloseWindow, fmenu, group); + ADD_SEP(file, fmenu); + IMAGE_ITEM(file, savepage, gtkSavePage, fmenu, group); + IMAGE_ITEM(file, export, gtkExport, fmenu, group); + ADD_SEP(file, fmenu); + IMAGE_ITEM(file, printpreview, gtkPrintPreview, fmenu, group); + IMAGE_ITEM(file, print, gtkPrint, fmenu, group); + ADD_SEP(file, fmenu); + IMAGE_ITEM(file, quit, gtkQuitMenu, fmenu, group); + SET_SUBMENU(export, fmenu); + + return fmenu; +} + +/** +* creates an edit menu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_edit_menu *nsgtk_menu_edit_submenu(GtkAccelGroup *group) +{ + struct nsgtk_edit_menu *ret = malloc(sizeof(struct nsgtk_edit_menu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->edit_menu = GTK_MENU(gtk_menu_new()); + if (ret->edit_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + IMAGE_ITEM(edit, cut, gtkCut, ret, group); + IMAGE_ITEM(edit, copy, gtkCopy, ret, group); + IMAGE_ITEM(edit, paste, gtkPaste, ret, group); + IMAGE_ITEM(edit, delete, gtkDelete, ret, group); + ADD_SEP(edit, ret); + IMAGE_ITEM(edit, selectall, gtkSelectAll, ret, group); + ADD_SEP(edit, ret); + IMAGE_ITEM(edit, find, gtkFind, ret, group); + ADD_SEP(edit, ret); + IMAGE_ITEM(edit, preferences, gtkPreferences, ret, group); + + return ret; +} + +/** +* creates a view menu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_view_menu *nsgtk_menu_view_submenu(GtkAccelGroup *group) +{ + struct nsgtk_view_menu *ret = malloc(sizeof(struct nsgtk_view_menu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->view_menu = GTK_MENU(gtk_menu_new()); + if (ret->view_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + IMAGE_ITEM(view, stop, gtkStop, ret, group); + IMAGE_ITEM(view, reload, gtkReload, ret, group); + ADD_SEP(view, ret); + IMAGE_ITEM(view, scaleview, gtkScaleView, ret, group); + IMAGE_ITEM(view, fullscreen, gtkFullScreen, ret, group); + ADD_SEP(view, ret); + IMAGE_ITEM(view, images, gtkImages, ret, group); + IMAGE_ITEM(view, toolbars, gtkToolbars, ret, group); + IMAGE_ITEM(view, tabs, gtkTabs, ret, group); + ADD_SEP(view, ret); + IMAGE_ITEM(view, savewindowsize, gtkSaveWindowSize, ret, group); + SET_SUBMENU(scaleview, ret); + SET_SUBMENU(images, ret); + SET_SUBMENU(toolbars, ret); + SET_SUBMENU(tabs, ret); + + + return ret; +} + +/** +* creates a nav menu +* \param group the 'global' in a gtk sense accelerator reference +*/ + +static struct nsgtk_nav_menu *nsgtk_menu_nav_submenu(GtkAccelGroup *group) +{ + struct nsgtk_nav_menu *ret = malloc(sizeof(struct nsgtk_nav_menu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->nav_menu = GTK_MENU(gtk_menu_new()); + if (ret->nav_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + + IMAGE_ITEM(nav, back, gtkBack, ret, group); + IMAGE_ITEM(nav, forward, gtkForward, ret, group); + IMAGE_ITEM(nav, home, gtkHome, ret, group); + ADD_SEP(nav, ret); + IMAGE_ITEM(nav, localhistory, gtkLocalHistory, ret, group); + IMAGE_ITEM(nav, globalhistory, gtkGlobalHistory, ret, group); + ADD_SEP(nav, ret); + IMAGE_ITEM(nav, addbookmarks, gtkAddBookMarks, ret, group); + IMAGE_ITEM(nav, showbookmarks, gtkShowBookMarks, ret, group); + ADD_SEP(nav, ret); + IMAGE_ITEM(nav, openlocation, gtkOpenLocation, ret, group); + + + return ret; +} + +/** + * creates the tools menu + * \param group the 'global' in a gtk sense accelerator reference + */ +static struct nsgtk_tools_menu *nsgtk_menu_tools_submenu(GtkAccelGroup *group) +{ + struct nsgtk_tools_menu *ret = malloc(sizeof(struct nsgtk_tools_menu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->tools_menu = GTK_MENU(gtk_menu_new()); + if (ret->tools_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + + IMAGE_ITEM(tools, downloads, gtkDownloads, ret, group); + IMAGE_ITEM(tools, showcookies, gtkShowCookies, ret, group); + IMAGE_ITEM(tools, developer, gtkDeveloper, ret, group); + SET_SUBMENU(developer, ret); + + return ret; +} + +/** + * creates a help menu + * \param group the 'global' in a gtk sense accelerator reference + */ +static struct nsgtk_help_menu *nsgtk_menu_help_submenu(GtkAccelGroup *group) +{ + struct nsgtk_help_menu *ret = malloc(sizeof(struct nsgtk_help_menu)); + if (ret == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + ret->help_menu = GTK_MENU(gtk_menu_new()); + if (ret->help_menu == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + free(ret); + return NULL; + } + IMAGE_ITEM(help, contents, gtkContents, ret, group); + IMAGE_ITEM(help, guide, gtkGuide, ret, group); + IMAGE_ITEM(help, info, gtkUserInformation, ret, group); + ADD_SEP(help, ret); + IMAGE_ITEM(help, about, gtkAbout, ret, group); + + return ret; +} + + +/** + * Generate menubar menus. + * + * Generate the main menu structure and attach it to a menubar widget. + */ +struct nsgtk_bar_submenu * +nsgtk_menu_bar_create(GtkMenuShell *menubar, GtkAccelGroup *group) +{ + struct nsgtk_bar_submenu *nmenu; + + nmenu = calloc(1, sizeof(struct nsgtk_bar_submenu)); + if (nmenu == NULL) { + return NULL; + } + + /* create sub menus */ + nmenu->file_submenu = nsgtk_menu_file_submenu(group); + nmenu->edit_submenu = nsgtk_menu_edit_submenu(group); + nmenu->view_submenu = nsgtk_menu_view_submenu(group); + nmenu->nav_submenu = nsgtk_menu_nav_submenu(group); + nmenu->tools_submenu = nsgtk_menu_tools_submenu(group); + nmenu->help_submenu = nsgtk_menu_help_submenu(group); + + if (menubar != NULL) { + nmenu->bar_menu = GTK_MENU_BAR(menubar); + + /* attach menus to menubar */ + ATTACH_PARENT(menubar, gtkFile, nmenu->file_submenu->file, group); + ATTACH_PARENT(menubar, gtkEdit, nmenu->edit_submenu->edit, group); + ATTACH_PARENT(menubar, gtkView, nmenu->view_submenu->view, group); + ATTACH_PARENT(menubar, gtkNavigate, nmenu->nav_submenu->nav, group); + ATTACH_PARENT(menubar, gtkTools, nmenu->tools_submenu->tools, group); + ATTACH_PARENT(menubar, gtkHelp, nmenu->help_submenu->help, group); + } + + return nmenu; +} + +/* exported function documented in gtk/menu.h */ +struct nsgtk_popup_menu *nsgtk_popup_menu_create(GtkAccelGroup *group) +{ + struct nsgtk_popup_menu *nmenu; + + NEW_MENU(nmenu, popup); + + IMAGE_ITEM(popup, file, gtkFile, nmenu, group); + SET_SUBMENU(file, nmenu); + + IMAGE_ITEM(popup, edit, gtkEdit, nmenu, group); + SET_SUBMENU(edit, nmenu); + + IMAGE_ITEM(popup, view, gtkView, nmenu, group); + SET_SUBMENU(view, nmenu); + + IMAGE_ITEM(popup, nav, gtkNavigate, nmenu, group); + SET_SUBMENU(nav, nmenu); + + IMAGE_ITEM(popup, tools, gtkTools, nmenu, group); + SET_SUBMENU(tools, nmenu); + + IMAGE_ITEM(popup, help, gtkHelp, nmenu, group); + SET_SUBMENU(help, nmenu); + + ADD_NAMED_SEP(popup, first, nmenu); + + IMAGE_ITEM(popup, back, gtkBack, nmenu, group); + IMAGE_ITEM(popup, forward, gtkForward, nmenu, group); + + ADD_NAMED_SEP(popup, third, nmenu); + + IMAGE_ITEM(popup, stop, gtkStop, nmenu, group); + IMAGE_ITEM(popup, reload, gtkReload, nmenu, group); + IMAGE_ITEM(popup, cut, gtkCut, nmenu, group); + IMAGE_ITEM(popup, copy, gtkCopy, nmenu, group); + IMAGE_ITEM(popup, paste, gtkPaste, nmenu, group); + IMAGE_ITEM(popup, customize, gtkCustomize, nmenu, group); + + return nmenu; +} + + +/* exported function documented in gtk/menu.h */ +struct nsgtk_link_menu * +nsgtk_link_menu_create(GtkAccelGroup *group) +{ + struct nsgtk_link_menu *nmenu; + + NEW_MENU(nmenu, link); + + IMAGE_ITEM(link, opentab, gtkOpentab, nmenu, group); + IMAGE_ITEM(link, openwin, gtkOpenwin, nmenu, group); + + ADD_SEP(link, nmenu); + + IMAGE_ITEM(link, save, gtkSavelink, nmenu, group); + IMAGE_ITEM(link, bookmark, gtkBookmarklink, nmenu, group); + IMAGE_ITEM(link, copy, gtkCopylink, nmenu, group); + + return nmenu; +} diff --git a/frontends/gtk/menu.h b/frontends/gtk/menu.h new file mode 100644 index 000000000..5da5cb1b2 --- /dev/null +++ b/frontends/gtk/menu.h @@ -0,0 +1,227 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ +#ifndef _NETSURF_GTK_MENU_H_ +#define _NETSURF_GTK_MENU_H_ + +#include + +struct nsgtk_file_menu { + GtkMenuItem *file; /* File menu item on menubar */ + GtkMenu *file_menu; + GtkWidget *newwindow_menuitem; + GtkWidget *newtab_menuitem; + GtkWidget *openfile_menuitem; + GtkWidget *closewindow_menuitem; + GtkWidget *savepage_menuitem; + GtkWidget *export_menuitem; + struct nsgtk_export_submenu *export_submenu; + GtkWidget *printpreview_menuitem; + GtkWidget *print_menuitem; + GtkWidget *quit_menuitem; +}; + +struct nsgtk_edit_menu { + GtkMenuItem *edit; /* Edit menu item on menubar */ + GtkMenu *edit_menu; + GtkWidget *cut_menuitem; + GtkWidget *copy_menuitem; + GtkWidget *paste_menuitem; + GtkWidget *delete_menuitem; + GtkWidget *selectall_menuitem; + GtkWidget *find_menuitem; + GtkWidget *preferences_menuitem; +}; + +struct nsgtk_view_menu { + GtkMenuItem *view; /* View menu item on menubar */ + GtkMenu *view_menu; /* gtk menu attached to menu item */ + GtkWidget *stop_menuitem; + GtkWidget *reload_menuitem; + GtkWidget *scaleview_menuitem; + struct nsgtk_scaleview_submenu *scaleview_submenu; + GtkWidget *fullscreen_menuitem; + GtkWidget *images_menuitem; + struct nsgtk_images_submenu *images_submenu; + GtkWidget *toolbars_menuitem; + struct nsgtk_toolbars_submenu *toolbars_submenu; + GtkWidget *tabs_menuitem; + struct nsgtk_tabs_submenu *tabs_submenu; + GtkWidget *savewindowsize_menuitem; +}; + +struct nsgtk_nav_menu { + GtkMenuItem *nav; /* Nav menu item on menubar */ + GtkMenu *nav_menu; + GtkWidget *back_menuitem; + GtkWidget *forward_menuitem; + GtkWidget *home_menuitem; + GtkWidget *localhistory_menuitem; + GtkWidget *globalhistory_menuitem; + GtkWidget *addbookmarks_menuitem; + GtkWidget *showbookmarks_menuitem; + GtkWidget *openlocation_menuitem; +}; + +struct nsgtk_tools_menu { + GtkMenuItem *tools; /* Tools menu item on menubar */ + GtkMenu *tools_menu; + + GtkWidget *showcookies_menuitem; + GtkWidget *downloads_menuitem; + GtkWidget *developer_menuitem; + struct nsgtk_developer_submenu *developer_submenu; +}; + +struct nsgtk_help_menu { + GtkMenuItem *help; /* Help menu item on menubar */ + GtkMenu *help_menu; + GtkWidget *contents_menuitem; + GtkWidget *guide_menuitem; + GtkWidget *info_menuitem; + GtkWidget *about_menuitem; +}; + + +struct nsgtk_export_submenu { + GtkMenu *export_menu; + GtkWidget *plaintext_menuitem; + GtkWidget *drawfile_menuitem; + GtkWidget *postscript_menuitem; + GtkWidget *pdf_menuitem; +}; + +struct nsgtk_scaleview_submenu { + GtkMenu *scaleview_menu; + GtkWidget *zoomplus_menuitem; + GtkWidget *zoomminus_menuitem; + GtkWidget *zoomnormal_menuitem; +}; + +struct nsgtk_tabs_submenu { + GtkMenu *tabs_menu; + GtkWidget *nexttab_menuitem; + GtkWidget *prevtab_menuitem; + GtkWidget *closetab_menuitem; +}; + +struct nsgtk_images_submenu { + GtkMenu *images_menu; + GtkCheckMenuItem *foregroundimages_menuitem; + GtkCheckMenuItem *backgroundimages_menuitem; +}; + +struct nsgtk_toolbars_submenu { + GtkMenu *toolbars_menu; + GtkCheckMenuItem *menubar_menuitem; + GtkCheckMenuItem *toolbar_menuitem; +}; + +struct nsgtk_developer_submenu { + GtkMenu *developer_menu; + + GtkWidget *viewsource_menuitem; + GtkWidget *toggledebugging_menuitem; + GtkWidget *debugboxtree_menuitem; + GtkWidget *debugdomtree_menuitem; +}; + + +struct nsgtk_bar_submenu { + GtkMenuBar *bar_menu; + struct nsgtk_file_menu *file_submenu; + struct nsgtk_edit_menu *edit_submenu; + struct nsgtk_view_menu *view_submenu; + struct nsgtk_nav_menu *nav_submenu; + struct nsgtk_tabs_submenu *tabs_submenu; + struct nsgtk_tools_menu *tools_submenu; + struct nsgtk_help_menu *help_submenu; +}; + +struct nsgtk_popup_menu { + GtkMenu *popup_menu; + + GtkWidget *file_menuitem; + struct nsgtk_file_menu *file_submenu; + + GtkWidget *edit_menuitem; + struct nsgtk_edit_menu *edit_submenu; + + GtkWidget *view_menuitem; + struct nsgtk_view_menu *view_submenu; + + GtkWidget *nav_menuitem; + struct nsgtk_nav_menu *nav_submenu; + + GtkWidget *tabs_menuitem; + struct nsgtk_tabs_submenu *tabs_submenu; + + GtkWidget *tools_menuitem; + struct nsgtk_tools_menu *tools_submenu; + + GtkWidget *help_menuitem; + struct nsgtk_help_menu *help_submenu; + + GtkWidget *first_separator; + + /* navigation entries */ + GtkWidget *back_menuitem; + GtkWidget *forward_menuitem; + + GtkWidget *third_separator; + + /* view entries */ + GtkWidget *stop_menuitem; + GtkWidget *reload_menuitem; + + GtkWidget *cut_menuitem; + GtkWidget *copy_menuitem; + GtkWidget *paste_menuitem; + GtkWidget *customize_menuitem; + +}; + +struct nsgtk_link_menu { + GtkMenu *link_menu; + + GtkWidget *opentab_menuitem; + GtkWidget *openwin_menuitem; + + GtkWidget *save_menuitem; + GtkWidget *bookmark_menuitem; + GtkWidget *copy_menuitem; +}; + +/** + * Create main menu bar. + */ +struct nsgtk_bar_submenu *nsgtk_menu_bar_create(GtkMenuShell *menubar, GtkAccelGroup *group); + +/** + * Generate right click menu menu. + * + */ +struct nsgtk_popup_menu *nsgtk_popup_menu_create(GtkAccelGroup *group); + +/** + * Generate context sensitive popup menu for link. + * + */ +struct nsgtk_link_menu *nsgtk_link_menu_create(GtkAccelGroup *group); + + +#endif diff --git a/frontends/gtk/options.h b/frontends/gtk/options.h new file mode 100644 index 000000000..ac642c153 --- /dev/null +++ b/frontends/gtk/options.h @@ -0,0 +1,72 @@ +/* + * Copyright 2012 Vincent Sanders + * + * 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 . + */ + +#ifndef _NETSURF_GTK_OPTIONS_H_ +#define _NETSURF_GTK_OPTIONS_H_ + +/* currently nothing here */ + +#endif + +/* High quality image scaling */ +NSOPTION_BOOL(render_resample, true) + +/* clear downloads */ +NSOPTION_BOOL(downloads_clear, false) + +/* prompt before overwriting downloads */ +NSOPTION_BOOL(request_overwrite, true) + +/* location to download files to */ +NSOPTION_STRING(downloads_directory, NULL) + +/* where to store URL database */ +NSOPTION_STRING(url_file, NULL) + +/* Always show tabs even if there is only one */ +NSOPTION_BOOL(show_single_tab, false) + +/* size of buttons */ +NSOPTION_INTEGER(button_type, 0) + +/* disallow popup windows */ +NSOPTION_BOOL(disable_popups, false) + +/* disable content plugins */ +NSOPTION_BOOL(disable_plugins, false) + +/* number of days to keep history data */ +NSOPTION_INTEGER(history_age, 0) + +/* show urls in local history browser */ +NSOPTION_BOOL(hover_urls, false) + +/* bring new tabs to front */ +NSOPTION_BOOL(focus_new, false) + +/* new tabs are blank instead of homepage */ +NSOPTION_BOOL(new_blank, false) + +/* path to save hotlist file */ +NSOPTION_STRING(hotlist_path, NULL) + +/* Developer information viewer display method */ +NSOPTION_INTEGER(developer_view, 0) + +/* where tabs are positioned */ +NSOPTION_INTEGER(position_tab, 0) diff --git a/frontends/gtk/plotters.c b/frontends/gtk/plotters.c new file mode 100644 index 000000000..1d8c19827 --- /dev/null +++ b/frontends/gtk/plotters.c @@ -0,0 +1,538 @@ +/* + * Copyright 2006 Rob Kendrick + * Copyright 2005 James Bursa + * + * 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 . + */ + +/** + * \file + * GTK and Cairo plotter implementations. + * + * Uses Cairo drawing primitives to render browser output. + * \todo remove the use of the gdk structure for clipping + */ + +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utils.h" +#include "desktop/plotters.h" +#include "utils/nsoption.h" + +#include "gtk/layout_pango.h" +#include "gtk/plotters.h" +#include "gtk/scaffolding.h" +#include "gtk/bitmap.h" + +GtkWidget *current_widget; +cairo_t *current_cr; + +static GdkRectangle cliprect; + +struct plotter_table plot; + +/** Set cairo context colour to nsgtk colour. */ +void nsgtk_set_colour(colour c) +{ + cairo_set_source_rgba(current_cr, + (c & 0xff) / 255.0, + ((c & 0xff00) >> 8) / 255.0, + ((c & 0xff0000) >> 16) / 255.0, + 1.0); +} + +/** Set cairo context to solid plot operation. */ +static inline void nsgtk_set_solid(void) +{ + double dashes = 0; + cairo_set_dash(current_cr, &dashes, 0, 0); +} + +/** Set cairo context to dotted plot operation. */ +static inline void nsgtk_set_dotted(void) +{ + double cdashes[] = { 1.0, 2.0 }; + cairo_set_dash(current_cr, cdashes, 2, 0); +} + +/** Set cairo context to dashed plot operation. */ +static inline void nsgtk_set_dashed(void) +{ + double cdashes[] = { 8.0, 2.0 }; + cairo_set_dash(current_cr, cdashes, 2, 0); +} + +/** Set clipping area for subsequent plot operations. */ +static bool nsgtk_plot_clip(const struct rect *clip) +{ + cairo_reset_clip(current_cr); + cairo_rectangle(current_cr, clip->x0, clip->y0, + clip->x1 - clip->x0, clip->y1 - clip->y0); + cairo_clip(current_cr); + + cliprect.x = clip->x0; + cliprect.y = clip->y0; + cliprect.width = clip->x1 - clip->x0; + cliprect.height = clip->y1 - clip->y0; + + return true; +} + + +static bool nsgtk_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style) +{ + nsgtk_set_colour(style->fill_colour); + nsgtk_set_solid(); + + cairo_set_line_width(current_cr, 1); + cairo_arc(current_cr, x, y, radius, + (angle1 + 90) * (M_PI / 180), + (angle2 + 90) * (M_PI / 180)); + cairo_stroke(current_cr); + + return true; +} + +static bool nsgtk_plot_disc(int x, int y, int radius, const plot_style_t *style) +{ + if (style->fill_type != PLOT_OP_TYPE_NONE) { + nsgtk_set_colour(style->fill_colour); + nsgtk_set_solid(); + cairo_set_line_width(current_cr, 0); + cairo_arc(current_cr, x, y, radius, 0, M_PI * 2); + cairo_fill(current_cr); + cairo_stroke(current_cr); + } + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + nsgtk_set_colour(style->stroke_colour); + + switch (style->stroke_type) { + case PLOT_OP_TYPE_SOLID: /**< Solid colour */ + default: + nsgtk_set_solid(); + break; + + case PLOT_OP_TYPE_DOT: /**< Doted plot */ + nsgtk_set_dotted(); + break; + + case PLOT_OP_TYPE_DASH: /**< dashed plot */ + nsgtk_set_dashed(); + break; + } + + if (style->stroke_width == 0) + cairo_set_line_width(current_cr, 1); + else + cairo_set_line_width(current_cr, style->stroke_width); + + cairo_arc(current_cr, x, y, radius, 0, M_PI * 2); + + cairo_stroke(current_cr); + } + + return true; +} + +static bool +nsgtk_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style) +{ + nsgtk_set_colour(style->stroke_colour); + + switch (style->stroke_type) { + case PLOT_OP_TYPE_SOLID: /**< Solid colour */ + default: + nsgtk_set_solid(); + break; + + case PLOT_OP_TYPE_DOT: /**< Doted plot */ + nsgtk_set_dotted(); + break; + + case PLOT_OP_TYPE_DASH: /**< dashed plot */ + nsgtk_set_dashed(); + break; + } + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + nsgtk_set_colour(style->stroke_colour); + } + + if (style->stroke_width == 0) + cairo_set_line_width(current_cr, 1); + else + cairo_set_line_width(current_cr, style->stroke_width); + + /* core expects horizontal and vertical lines to be on pixels, not + * between pixels */ + cairo_move_to(current_cr, (x0 == x1) ? x0 + 0.5 : x0, + (y0 == y1) ? y0 + 0.5 : y0); + cairo_line_to(current_cr, (x0 == x1) ? x1 + 0.5 : x1, + (y0 == y1) ? y1 + 0.5 : y1); + cairo_stroke(current_cr); + + return true; +} + +/** Plot a caret. + * + * @note It is assumed that the plotters have been set up. + */ +void nsgtk_plot_caret(int x, int y, int h) +{ + nsgtk_set_solid(); /* solid line */ + nsgtk_set_colour(0); /* black */ + cairo_set_line_width(current_cr, 1); /* thin line */ + + /* core expects horizontal and vertical lines to be on pixels, not + * between pixels */ + cairo_move_to(current_cr, x + 0.5, y); + cairo_line_to(current_cr, x + 0.5, y + h - 1); + cairo_stroke(current_cr); +} + +static bool nsgtk_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style) +{ + if (style->fill_type != PLOT_OP_TYPE_NONE) { + nsgtk_set_colour(style->fill_colour); + nsgtk_set_solid(); + + cairo_set_line_width(current_cr, 0); + cairo_rectangle(current_cr, x0, y0, x1 - x0, y1 - y0); + cairo_fill(current_cr); + cairo_stroke(current_cr); + } + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + nsgtk_set_colour(style->stroke_colour); + + switch (style->stroke_type) { + case PLOT_OP_TYPE_SOLID: /**< Solid colour */ + default: + nsgtk_set_solid(); + break; + + case PLOT_OP_TYPE_DOT: /**< Doted plot */ + nsgtk_set_dotted(); + break; + + case PLOT_OP_TYPE_DASH: /**< dashed plot */ + nsgtk_set_dashed(); + break; + } + + if (style->stroke_width == 0) + cairo_set_line_width(current_cr, 1); + else + cairo_set_line_width(current_cr, style->stroke_width); + + cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, x1 - x0, y1 - y0); + cairo_stroke(current_cr); + } + return true; +} + +static bool nsgtk_plot_polygon(const int *p, unsigned int n, const plot_style_t *style) +{ + unsigned int i; + + nsgtk_set_colour(style->fill_colour); + nsgtk_set_solid(); + + cairo_set_line_width(current_cr, 0); + cairo_move_to(current_cr, p[0], p[1]); + for (i = 1; i != n; i++) { + cairo_line_to(current_cr, p[i * 2], p[i * 2 + 1]); + } + cairo_fill(current_cr); + cairo_stroke(current_cr); + + return true; +} + + + + +static bool nsgtk_plot_text(int x, int y, const char *text, size_t length, + const struct plot_font_style *fstyle) +{ + return nsfont_paint(x, y, text, length, fstyle); +} + + + +static bool nsgtk_plot_pixbuf(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg) +{ + int x0, y0, x1, y1; + int dsrcx, dsrcy, dwidth, dheight; + int bmwidth, bmheight; + + cairo_surface_t *bmsurface = bitmap->surface; + + /* Bail early if we can */ + if (width == 0 || height == 0) + /* Nothing to plot */ + return true; + if ((x > (cliprect.x + cliprect.width)) || + ((x + width) < cliprect.x) || + (y > (cliprect.y + cliprect.height)) || + ((y + height) < cliprect.y)) { + /* Image completely outside clip region */ + return true; + } + + /* Get clip rectangle / image rectangle edge differences */ + x0 = cliprect.x - x; + y0 = cliprect.y - y; + x1 = (x + width) - (cliprect.x + cliprect.width); + y1 = (y + height) - (cliprect.y + cliprect.height); + + /* Set initial draw geometry */ + dsrcx = x; + dsrcy = y; + dwidth = width; + dheight = height; + + /* Manually clip draw coordinates to area of image to be rendered */ + if (x0 > 0) { + /* Clip left */ + dsrcx += x0; + dwidth -= x0; + } + if (y0 > 0) { + /* Clip top */ + dsrcy += y0; + dheight -= y0; + } + if (x1 > 0) { + /* Clip right */ + dwidth -= x1; + } + if (y1 > 0) { + /* Clip bottom */ + dheight -= y1; + } + + if (dwidth == 0 || dheight == 0) + /* Nothing to plot */ + return true; + + bmwidth = cairo_image_surface_get_width(bmsurface); + bmheight = cairo_image_surface_get_height(bmsurface); + + /* Render the bitmap */ + if ((bmwidth == width) && (bmheight == height)) { + /* Bitmap is not scaled */ + /* Plot the bitmap */ + cairo_set_source_surface(current_cr, bmsurface, x, y); + cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight); + cairo_fill(current_cr); + + } else { + /* Bitmap is scaled */ + if ((bitmap->scsurface != NULL) && + ((cairo_image_surface_get_width(bitmap->scsurface) != width) || + (cairo_image_surface_get_height(bitmap->scsurface) != height))){ + cairo_surface_destroy(bitmap->scsurface); + bitmap->scsurface = NULL; + } + + if (bitmap->scsurface == NULL) { + bitmap->scsurface = cairo_surface_create_similar(bmsurface,CAIRO_CONTENT_COLOR_ALPHA, width, height); + cairo_t *cr = cairo_create(bitmap->scsurface); + + /* Scale *before* setting the source surface (1) */ + cairo_scale(cr, (double)width / bmwidth, (double)height / bmheight); + cairo_set_source_surface(cr, bmsurface, 0, 0); + + /* To avoid getting the edge pixels blended with 0 + * alpha, which would occur with the default + * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer (2) + */ + cairo_pattern_set_extend(cairo_get_source(cr), + CAIRO_EXTEND_REFLECT); + + /* Replace the destination with the source instead of + * overlaying + */ + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + /* Do the actual drawing */ + cairo_paint(cr); + + cairo_destroy(cr); + + } + /* Plot the scaled bitmap */ + cairo_set_source_surface(current_cr, bitmap->scsurface, x, y); + cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight); + cairo_fill(current_cr); + + + } + + return true; +} + +static bool nsgtk_plot_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bitmap_flags_t flags) +{ + int doneheight = 0, donewidth = 0; + bool repeat_x = (flags & BITMAPF_REPEAT_X); + bool repeat_y = (flags & BITMAPF_REPEAT_Y); + + /* Bail early if we can */ + if (width == 0 || height == 0) + /* Nothing to plot */ + return true; + + if (!(repeat_x || repeat_y)) { + /* Not repeating at all, so just pass it on */ + return nsgtk_plot_pixbuf(x, y, width, height, bitmap, bg); + } + + if (y > cliprect.y) { + doneheight = (cliprect.y - height) + ((y - cliprect.y) % height); + } else { + doneheight = y; + } + + while (doneheight < (cliprect.y + cliprect.height)) { + if (x > cliprect.x) { + donewidth = (cliprect.x - width) + ((x - cliprect.x) % width); + } else { + donewidth = x; + } + + while (donewidth < (cliprect.x + cliprect.width)) { + nsgtk_plot_pixbuf(donewidth, doneheight, + width, height, bitmap, bg); + donewidth += width; + if (!repeat_x) + break; + } + doneheight += height; + + if (!repeat_y) + break; + } + + return true; +} + +static bool nsgtk_plot_path(const float *p, unsigned int n, colour fill, float width, + colour c, const float transform[6]) +{ + unsigned int i; + cairo_matrix_t old_ctm, n_ctm; + + if (n == 0) + return true; + + if (p[0] != PLOTTER_PATH_MOVE) { + LOG("Path does not start with move"); + return false; + } + + + /* Save CTM */ + cairo_get_matrix(current_cr, &old_ctm); + + /* Set up line style and width */ + cairo_set_line_width(current_cr, 1); + nsgtk_set_solid(); + + /* Load new CTM */ + n_ctm.xx = transform[0]; + n_ctm.yx = transform[1]; + n_ctm.xy = transform[2]; + n_ctm.yy = transform[3]; + n_ctm.x0 = transform[4]; + n_ctm.y0 = transform[5]; + + cairo_set_matrix(current_cr, &n_ctm); + + /* Construct path */ + for (i = 0; i < n; ) { + if (p[i] == PLOTTER_PATH_MOVE) { + cairo_move_to(current_cr, p[i+1], p[i+2]); + i += 3; + } else if (p[i] == PLOTTER_PATH_CLOSE) { + cairo_close_path(current_cr); + i++; + } else if (p[i] == PLOTTER_PATH_LINE) { + cairo_line_to(current_cr, p[i+1], p[i+2]); + i += 3; + } else if (p[i] == PLOTTER_PATH_BEZIER) { + cairo_curve_to(current_cr, p[i+1], p[i+2], + p[i+3], p[i+4], + p[i+5], p[i+6]); + i += 7; + } else { + LOG("bad path command %f", p[i]); + /* Reset matrix for safety */ + cairo_set_matrix(current_cr, &old_ctm); + return false; + } + } + + /* Restore original CTM */ + cairo_set_matrix(current_cr, &old_ctm); + + /* Now draw path */ + if (fill != NS_TRANSPARENT) { + nsgtk_set_colour(fill); + + if (c != NS_TRANSPARENT) { + /* Fill & Stroke */ + cairo_fill_preserve(current_cr); + nsgtk_set_colour(c); + cairo_stroke(current_cr); + } else { + /* Fill only */ + cairo_fill(current_cr); + } + } else if (c != NS_TRANSPARENT) { + /* Stroke only */ + nsgtk_set_colour(c); + cairo_stroke(current_cr); + } + + return true; +} + +/** GTK plotter table */ +const struct plotter_table nsgtk_plotters = { + .clip = nsgtk_plot_clip, + .arc = nsgtk_plot_arc, + .disc = nsgtk_plot_disc, + .line = nsgtk_plot_line, + .rectangle = nsgtk_plot_rectangle, + .polygon = nsgtk_plot_polygon, + .path = nsgtk_plot_path, + .bitmap = nsgtk_plot_bitmap, + .text = nsgtk_plot_text, + .option_knockout = true +}; + + + diff --git a/frontends/gtk/plotters.h b/frontends/gtk/plotters.h new file mode 100644 index 000000000..c88a8da0c --- /dev/null +++ b/frontends/gtk/plotters.h @@ -0,0 +1,40 @@ +/* + * Copyright 2005 James Bursa + * + * 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 . + */ + +/** \file + * Target independent plotting (GDK / GTK+ interface). + */ + +#ifndef NETSURF_GTK_PLOTTERS_H +#define NETSURF_GTK_PLOTTERS_H 1 + +#include + +struct plotter_table; + +extern const struct plotter_table nsgtk_plotters; + +/* make sure this is NULL if no redraw is in progress */ +extern GtkWidget *current_widget; +extern cairo_t *current_cr; + +void nsgtk_set_colour(colour c); +void nsgtk_plot_caret(int x, int y, int h); + +#endif /* NETSURF_GTK_PLOTTERS_H */ + diff --git a/frontends/gtk/preferences.c b/frontends/gtk/preferences.c new file mode 100644 index 000000000..3efb9eddd --- /dev/null +++ b/frontends/gtk/preferences.c @@ -0,0 +1,1030 @@ +/* + * Copyright 2012 Vincent Sanders + * + * 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 . + */ + +#include +#include +#include +#include + +#include "utils/messages.h" +#include "utils/nsoption.h" +#include "utils/file.h" +#include "utils/log.h" +#include "utils/nsurl.h" +#include "desktop/browser.h" +#include "desktop/searchweb.h" + +#include "gtk/compat.h" +#include "gtk/window.h" +#include "gtk/gui.h" +#include "gtk/scaffolding.h" +#include "gtk/resources.h" +#include "gtk/preferences.h" + +/* private prefs */ +struct ppref { + /** dialog handle created when window first accessed */ + GObject *dialog; + + struct browser_window *bw; + + /* widgets which are accessed from outside their own signal handlers */ + GtkEntry *entryHomePageURL; + GtkEntry *entryProxyHost; + GtkEntry *entryProxyUser; + GtkEntry *entryProxyPassword; + GtkEntry *entryProxyNoproxy; + GtkSpinButton *spinProxyPort; + + /* dynamic list stores */ + GtkListStore *content_language; + GtkListStore *search_providers; +}; + +static struct ppref ppref; + + +/* Set netsurf option based on toggle button state + * + * This works for any widget which subclasses togglebutton (checkbox, + * radiobutton etc.) + */ +#define TOGGLEBUTTON_SIGNALS(WIDGET, OPTION) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \ + struct ppref *priv) \ +{ \ + nsoption_set_bool(OPTION, \ + gtk_toggle_button_get_active(togglebutton)); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \ + struct ppref *priv) \ +{ \ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), \ + nsoption_bool(OPTION)); \ +} + +#define SPINBUTTON_SIGNALS(WIDGET, OPTION, MULTIPLIER) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv) \ +{ \ + nsoption_set_int(OPTION, \ + round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \ +{ \ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \ + ((gdouble)nsoption_int(OPTION)) / MULTIPLIER); \ +} + +#define SPINBUTTON_UINT_SIGNALS(WIDGET, OPTION, MULTIPLIER) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv) \ +{ \ + nsoption_set_uint(OPTION, \ + round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \ +{ \ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \ + ((gdouble)nsoption_uint(OPTION)) / MULTIPLIER); \ +} + +#define ENTRY_SIGNALS(WIDGET, OPTION) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv)\ +{ \ + nsoption_set_charp(OPTION, \ + strdup(gtk_entry_get_text(GTK_ENTRY(editable)))); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \ +{ \ + const char *OPTION; \ + OPTION = nsoption_charp(OPTION); \ + if (OPTION != NULL) { \ + gtk_entry_set_text(GTK_ENTRY(widget), OPTION); \ + } \ +} + +/* GTK module requires these to be exported symbols so these all need + * forward declaring to avoid warnings + */ +G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid); +G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv); + + +/********* PDF **********/ + +/* Appearance */ + +/* no images in output */ +TOGGLEBUTTON_SIGNALS(checkSuppressImages, suppress_images) + +/* no background images */ +TOGGLEBUTTON_SIGNALS(checkRemoveBackgrounds, remove_backgrounds) + +/* scale to fit page */ +TOGGLEBUTTON_SIGNALS(checkFitPage, enable_loosening) + +/* port */ +SPINBUTTON_SIGNALS(spinExportScale, export_scale, 1.0) + +/* Margins */ +SPINBUTTON_SIGNALS(spinMarginTop, margin_top, 1.0) +SPINBUTTON_SIGNALS(spinMarginBottom, margin_bottom, 1.0) +SPINBUTTON_SIGNALS(spinMarginLeft, margin_left, 1.0) +SPINBUTTON_SIGNALS(spinMarginRight, margin_right, 1.0) + + +/* Generation */ + +/* output is compressed */ +TOGGLEBUTTON_SIGNALS(checkCompressPDF, enable_PDF_compression) + +/* output has a password */ +TOGGLEBUTTON_SIGNALS(checkPasswordPDF, enable_PDF_password) + +/********* Network **********/ + +/* HTTP proxy */ +static void set_proxy_widgets_sensitivity(int proxyval, struct ppref *priv) +{ + gboolean host; + gboolean port; + gboolean user; + gboolean pass; + gboolean noproxy; + + switch (proxyval) { + case 0: /* no proxy */ + host = FALSE; + port = FALSE; + user = FALSE; + pass = FALSE; + noproxy = FALSE; + break; + + case 1: /* proxy with no auth */ + host = TRUE; + port = TRUE; + user = FALSE; + pass = FALSE; + noproxy = TRUE; + break; + + case 2: /* proxy with basic auth */ + host = TRUE; + port = TRUE; + user = TRUE; + pass = TRUE; + noproxy = TRUE; + break; + + case 3: /* proxy with ntlm auth */ + host = TRUE; + port = TRUE; + user = TRUE; + pass = TRUE; + noproxy = TRUE; + break; + + case 4: /* system proxy */ + host = FALSE; + port = FALSE; + user = FALSE; + pass = FALSE; + noproxy = FALSE; + break; + + default: + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyHost), host); + gtk_widget_set_sensitive(GTK_WIDGET(priv->spinProxyPort), port); + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyUser), user); + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyPassword), pass); + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyNoproxy), noproxy); + +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv) +{ + int proxy_sel; + proxy_sel = gtk_combo_box_get_active(combo); + + switch (proxy_sel) { + case 0: /* no proxy */ + nsoption_set_bool(http_proxy, false); + break; + + case 1: /* proxy with no auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); + break; + + case 2: /* proxy with basic auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC); + break; + + case 3: /* proxy with ntlm auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM); + break; + + case 4: /* system proxy */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); + break; + } + + set_proxy_widgets_sensitivity(proxy_sel, priv); +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv) +{ + int proxytype = 0; /* no proxy by default */ + + if (nsoption_bool(http_proxy) == true) { + /* proxy type combo box starts with disabled, to allow + * for this the http_proxy option needs combining with + * the http_proxy_auth option + */ + proxytype = nsoption_int(http_proxy_auth) + 1; + if (nsoption_charp(http_proxy_host) == NULL) { + /* set to use a proxy without a host, turn proxy off */ + proxytype = 0; + } else if (((proxytype == 2) || + (proxytype == 3)) && + ((nsoption_charp(http_proxy_auth_user) == NULL) || + (nsoption_charp(http_proxy_auth_pass) == NULL))) { + /* authentication selected with empty credentials, turn proxy off */ + proxytype = 0; + } + } + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), proxytype); + + set_proxy_widgets_sensitivity(proxytype, priv); +} + +/* host */ +ENTRY_SIGNALS(entryProxyHost, http_proxy_host) + +/* port */ +SPINBUTTON_SIGNALS(spinProxyPort, http_proxy_port, 1.0) + +/* user */ +ENTRY_SIGNALS(entryProxyUser, http_proxy_auth_user) + +/* password */ +ENTRY_SIGNALS(entryProxyPassword, http_proxy_auth_pass) + +/* no proxy */ +ENTRY_SIGNALS(entryProxyNoproxy, http_proxy_noproxy) + +/* Fetching */ + +/* maximum fetchers */ +SPINBUTTON_SIGNALS(spinMaxFetchers, max_fetchers, 1.0) + +/* fetches per host */ +SPINBUTTON_SIGNALS(spinFetchesPerHost, max_fetchers_per_host, 1.0) + +/* cached connections */ +SPINBUTTON_SIGNALS(spinCachedConnections, max_cached_fetch_handles, 1.0) + + +/********* Privacy **********/ + +/* General */ + +/* enable referral submission */ +TOGGLEBUTTON_SIGNALS(checkSendReferer, send_referer) + +/* send do not track */ +TOGGLEBUTTON_SIGNALS(checkSendDNT, do_not_track) + +/* History */ + +/* local history shows url tooltips */ +TOGGLEBUTTON_SIGNALS(checkHoverURLs, hover_urls) + +/* remember browsing history */ +SPINBUTTON_SIGNALS(spinHistoryAge, history_age, 1.0) + +/* Cache */ + +/* memory cache size */ +SPINBUTTON_SIGNALS(spinMemoryCacheSize, memory_cache_size, (1024*1024)) + +/* disc cache size */ +SPINBUTTON_UINT_SIGNALS(spinDiscCacheSize, disc_cache_size, (1024*1024)) + + +/* disc cache age */ +SPINBUTTON_SIGNALS(spinDiscCacheAge, disc_cache_age, 1.0) + + +/********* Content **********/ + +/* Control */ + + +/* prevent popups */ +TOGGLEBUTTON_SIGNALS(checkDisablePopups, disable_popups) + +/* hide adverts */ +TOGGLEBUTTON_SIGNALS(checkHideAdverts, block_advertisements) + +/* enable javascript */ +TOGGLEBUTTON_SIGNALS(checkEnableJavascript, enable_javascript) + +/* disable plugins */ +TOGGLEBUTTON_SIGNALS(checkDisablePlugins, disable_plugins) + +/* high quality image scaling */ +TOGGLEBUTTON_SIGNALS(checkResampleImages, render_resample) + +/* load and display of images */ +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, + struct ppref *priv) +{ + int img_sel; + /* get the row number for the selection */ + img_sel = gtk_combo_box_get_active(combo); + switch (img_sel) { + case 0: + /* background and foreground */ + nsoption_set_bool(foreground_images, true); + nsoption_set_bool(background_images, true); + break; + + case 1: + /* foreground only */ + nsoption_set_bool(foreground_images, true); + nsoption_set_bool(background_images, false); + break; + + case 2: + /* background only */ + nsoption_set_bool(foreground_images, false); + nsoption_set_bool(background_images, true); + break; + + case 3: + /* no images */ + nsoption_set_bool(foreground_images, false); + nsoption_set_bool(background_images, false); + break; + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, + struct ppref *priv) +{ + if (nsoption_bool(foreground_images)) { + if (nsoption_bool(background_images)) { + /* background and foreground */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); + } else { + /* foreground only */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 1); + } + } else { + if (nsoption_bool(background_images)) { + /* background only */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 2); + } else { + /* no images */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 3); + } + } +} + +/* Animation */ + +/* enable animation */ +TOGGLEBUTTON_SIGNALS(checkEnableAnimations, animate_images) + +/* frame time */ +SPINBUTTON_SIGNALS(spinAnimationSpeed, minimum_gif_delay, 100.0) + +/* Fonts */ + +/* default font */ +G_MODULE_EXPORT void +nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv) +{ + int font_sel; + /* get the row number for the selection */ + font_sel = gtk_combo_box_get_active(combo); + if ((font_sel >= 0) && (font_sel <= 4)) { + nsoption_set_int(font_default, font_sel); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(font_default)); +} + +/* default font size */ +SPINBUTTON_SIGNALS(spinDefaultSize, font_size, 10.0) + +/* preview - actually reflow all views */ +G_MODULE_EXPORT void +nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv) +{ + nsgtk_reflow_all_windows(); +} + + +/* Language */ + +/* accept language */ +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, + struct ppref *priv) +{ + gchar *lang = NULL; + GtkTreeIter iter; + GtkTreeModel *model; + + /* Obtain currently selected item from combo box. + * If nothing is selected, do nothing. + */ + if (gtk_combo_box_get_active_iter(combo, &iter)) { + /* Obtain data model from combo box. */ + model = gtk_combo_box_get_model(combo); + + /* Obtain string from model. */ + gtk_tree_model_get(model, &iter, 0, &lang, -1); + } + + if (lang != NULL) { + nsoption_set_charp(accept_language, strdup(lang)); + g_free(lang); + } +} + +/** + * populate language combo from data + */ +static nserror +comboboxLanguage_add_from_data(GtkListStore *liststore, + GtkComboBox *combobox, + const char *accept_language, + const uint8_t *data, + size_t data_size) +{ + int active_language = -1; + GtkTreeIter iter; + int combo_row_count = 0; + const uint8_t *s; + const uint8_t *nl; + char buf[50]; + int bufi = 0; + + gtk_list_store_clear(liststore); + s = data; + + while (s < (data + data_size)) { + /* find nl and copy buffer */ + for (nl = s; nl < data + data_size; nl++) { + if ((*nl == '\n') || (bufi == (sizeof(buf) - 2))) { + buf[bufi] = 0; /* null terminate */ + break; + } + buf[bufi++] = *nl; + } + if (bufi > 0) { + gtk_list_store_append(liststore, &iter); + gtk_list_store_set(liststore, &iter, 0, buf, -1 ); + + if (strcmp(buf, accept_language) == 0) { + active_language = combo_row_count; + } + + combo_row_count++; + } + bufi = 0; + s = nl + 1; /* skip newline */ + } + + /* if configured language was not in list, add it */ + if (active_language == -1) { + gtk_list_store_append(liststore, &iter); + gtk_list_store_set(liststore, &iter, 0, accept_language, -1); + active_language = combo_row_count; + } + + gtk_combo_box_set_active(combobox, active_language); + + return NSERROR_OK; +} + +/** + * populate language combo from file + */ +static nserror +comboboxLanguage_add_from_file(GtkListStore *liststore, + GtkComboBox *combobox, + const char *accept_language, + const char *file_location) +{ + int active_language = -1; + GtkTreeIter iter; + int combo_row_count = 0; + FILE *fp; + char buf[50]; + + fp = fopen(file_location, "r"); + if (fp == NULL) { + return NSERROR_NOT_FOUND; + } + + gtk_list_store_clear(liststore); + + LOG("Used %s for languages", file_location); + while (fgets(buf, sizeof(buf), fp)) { + /* Ignore blank lines */ + if (buf[0] == '\0') + continue; + + /* Remove trailing \n */ + buf[strlen(buf) - 1] = '\0'; + + gtk_list_store_append(liststore, &iter); + gtk_list_store_set(liststore, &iter, 0, buf, -1 ); + + if (strcmp(buf, accept_language) == 0) { + active_language = combo_row_count; + } + + combo_row_count++; + } + + /* if configured language was not in list, add it */ + if (active_language == -1) { + gtk_list_store_append(liststore, &iter); + gtk_list_store_set(liststore, &iter, 0, accept_language, -1); + active_language = combo_row_count; + } + + fclose(fp); + + gtk_combo_box_set_active(combobox, active_language); + + return NSERROR_OK; +} + +/** + * Fill content language list store. + */ +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, + struct ppref *priv) +{ + nserror res; + const uint8_t *data; + size_t data_size; + const char *languages_file; + const char *accept_language; + + if (priv->content_language == NULL) { + LOG("content language list store unavailable"); + return; + } + + /* get current accept language */ + accept_language = nsoption_charp(accept_language); + if (accept_language == NULL) { + accept_language = "en"; + } + + /* attempt to read languages from inline resource */ + res = nsgtk_data_from_resname("languages", &data, &data_size); + if (res == NSERROR_OK) { + res = comboboxLanguage_add_from_data(priv->content_language, + GTK_COMBO_BOX(widget), + accept_language, + data, + data_size); + } else { + /* attempt to read languages from file */ + res = nsgtk_path_from_resname("languages", &languages_file); + if (res == NSERROR_OK) { + res = comboboxLanguage_add_from_file(priv->content_language, + GTK_COMBO_BOX(widget), + accept_language, + languages_file); + } + } + if (res != NSERROR_OK) { + LOG("error populatiung languages combo"); + } +} + + +/********* Apperance **********/ + +/* Tabs */ + +/* always show tab bar */ +G_MODULE_EXPORT void +nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, + struct ppref *priv) +{ + nsoption_set_bool(show_single_tab, + gtk_toggle_button_get_active(togglebutton)); + nsgtk_reflow_all_windows(); +} + +G_MODULE_EXPORT void +nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + nsoption_bool(show_single_tab)); +} + +/* switch to newly opened tabs immediately */ +TOGGLEBUTTON_SIGNALS(checkFocusNew, focus_new) + +/* newly opened tabs are blank */ +TOGGLEBUTTON_SIGNALS(checkNewBlank, new_blank) + +/* tab position */ +G_MODULE_EXPORT void +nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, + struct ppref *priv) +{ + struct nsgtk_scaffolding *current; + + /* set the option */ + nsoption_set_int(position_tab, gtk_combo_box_get_active(widget)); + + /* update all notebooks in all scaffolds */ + current = nsgtk_scaffolding_iterate(NULL); + while (current) { + nsgtk_scaffolding_reset_offset(current); + + nsgtk_reflow_all_windows(); + + current = nsgtk_scaffolding_iterate(current); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(position_tab)); +} + +/* Tools */ + +/* developer view opening */ +G_MODULE_EXPORT void +nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget, + struct ppref *priv) +{ + /* set the option */ + nsoption_set_int(developer_view, gtk_combo_box_get_active(widget)); +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(developer_view)); +} + + +/* URLbar */ + +/* show recently visited urls as you type */ +TOGGLEBUTTON_SIGNALS(checkDisplayRecentURLs, url_suggestion) + +/* Toolbar */ + +/* button position */ +G_MODULE_EXPORT void +nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, + struct ppref *priv) +{ + struct nsgtk_scaffolding *current; + + nsoption_set_int(button_type, gtk_combo_box_get_active(widget) + 1); + + current = nsgtk_scaffolding_iterate(NULL); + while (current != NULL) { + nsgtk_scaffolding_reset_offset(current); + + nsgtk_scaffolding_toolbars(current, nsoption_int(button_type)); + + current = nsgtk_scaffolding_iterate(current); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(button_type) - 1); +} + + + +/************ Main ************/ + +/* Startup */ + +/* entry HomePageURL widget */ +ENTRY_SIGNALS(entryHomePageURL, homepage_url) + +/* put current page into homepage url */ +G_MODULE_EXPORT void +nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv) +{ + const gchar *url = nsurl_access(browser_window_get_url(priv->bw)); + + if (priv->entryHomePageURL != NULL) { + gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url); + nsoption_set_charp(homepage_url, strdup(url)); + } +} + +/* put default page into homepage */ +G_MODULE_EXPORT void +nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv) +{ + const gchar *url = NETSURF_HOMEPAGE; + + if (priv->entryHomePageURL != NULL) { + gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url); + nsoption_set_charp(homepage_url, strdup(url)); + } +} + +/* Search */ + +/* Url Search widget */ +TOGGLEBUTTON_SIGNALS(checkUrlSearch, search_url_bar) + +/* provider combo */ +G_MODULE_EXPORT void +nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv) +{ + int provider; + + provider = gtk_combo_box_get_active(widget); + + /* set the option */ + nsoption_set_int(search_provider, provider); + + /* set search provider */ + search_web_select_provider(provider); +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv) +{ + int iter; + const char *name; + int provider = nsoption_int(search_provider); + + if (priv->search_providers != NULL) { + gtk_list_store_clear(priv->search_providers); + for (iter = search_web_iterate_providers(0, &name); + iter != -1; + iter = search_web_iterate_providers(iter, &name)) { + gtk_list_store_insert_with_values(priv->search_providers, + NULL, -1, + 0, name, -1); + } + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), provider); +} + + +/* Downloads */ + +/* clear downloads */ +TOGGLEBUTTON_SIGNALS(checkClearDownloads, downloads_clear) + +/* request overwite */ +TOGGLEBUTTON_SIGNALS(checkRequestOverwrite, request_overwrite) + +/* download location + * + * note selection-changed is used instead of file-set as the returned + * filename when that signal are used is incorrect. Though this signal + * does update frequently often with the same data. + */ +G_MODULE_EXPORT void +nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, + struct ppref *priv) +{ + gchar *dir; + dir = gtk_file_chooser_get_filename(chooser); + nsoption_set_charp(downloads_directory, strdup(dir)); + g_free(dir); +} + +G_MODULE_EXPORT void +nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget), + nsoption_charp(downloads_directory)); +} + + +/************* Dialog window ***********/ + +/* dialog close and destroy events */ +G_MODULE_EXPORT void +nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid) +{ + char *choices = NULL; + + if (resid == GTK_RESPONSE_CLOSE) { + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } + gtk_widget_hide(GTK_WIDGET(dlg)); + } +} + +G_MODULE_EXPORT gboolean +nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, + struct ppref *priv) +{ + char *choices = NULL; + + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } + + gtk_widget_hide(GTK_WIDGET(dlg)); + + /* Delt with it by hiding window, no need to destory widget by + * default. + */ + return TRUE; +} + +G_MODULE_EXPORT void +nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv) +{ + char *choices = NULL; + + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } +} + + +/* exported interface documented in gtk/preferences.h */ +GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent) +{ + GtkBuilder *preferences_builder; + struct ppref *priv = &ppref; + nserror res; + + priv->bw = bw; /* for setting "current" page */ + + /* memoised dialog creation */ + if (priv->dialog != NULL) { + gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent); + return GTK_WIDGET(priv->dialog); + } + + res = nsgtk_builder_new_from_resname("options", &preferences_builder); + if (res != NSERROR_OK) { + LOG("Preferences UI builder init failed"); + return NULL; + } + + priv->dialog = gtk_builder_get_object(preferences_builder, + "dialogPreferences"); + if (priv->dialog == NULL) { + LOG("Unable to get object for preferences dialog"); + /* release builder as were done with it */ + g_object_unref(G_OBJECT(preferences_builder)); + return NULL; + } + + /* need to explicitly obtain handles for some widgets enabling + * updates by other widget events + */ +#define GB(TYPE, NAME) GTK_##TYPE(gtk_builder_get_object(preferences_builder, #NAME)) + priv->entryHomePageURL = GB(ENTRY, entryHomePageURL); + priv->content_language = GB(LIST_STORE, liststore_content_language); + priv->search_providers = GB(LIST_STORE, liststore_search_provider); + priv->entryProxyHost = GB(ENTRY, entryProxyHost); + priv->spinProxyPort = GB(SPIN_BUTTON, spinProxyPort); + priv->entryProxyUser = GB(ENTRY, entryProxyUser); + priv->entryProxyPassword = GB(ENTRY, entryProxyPassword); + priv->entryProxyNoproxy = GB(ENTRY, entryProxyNoproxy); +#undef GB + + /* connect all signals ready to use */ + gtk_builder_connect_signals(preferences_builder, priv); + + /* release builder as were done with it */ + g_object_unref(G_OBJECT(preferences_builder)); + + /* mark dialog as transient on parent */ + gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent); + + return GTK_WIDGET(priv->dialog); +} + diff --git a/frontends/gtk/preferences.h b/frontends/gtk/preferences.h new file mode 100644 index 000000000..9fe469e24 --- /dev/null +++ b/frontends/gtk/preferences.h @@ -0,0 +1,28 @@ +/* + * Copyright 2012 Vincent Sanders + * + * 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 . + */ + +#ifndef NETSURF_GTK_PREFERENCES_H +#define NETSURF_GTK_PREFERENCES_H + +#include + +/** Initialise prefernces window + */ +GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent); + +#endif diff --git a/frontends/gtk/print.c b/frontends/gtk/print.c new file mode 100644 index 000000000..a6e639996 --- /dev/null +++ b/frontends/gtk/print.c @@ -0,0 +1,613 @@ +/* + * Copyright 2006 Rob Kendrick + * Copyright 2005 James Bursa + * Copyright 2008 Adam Blokus + * + * 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 . + */ + /** \file + * GTK printing (implementation). + * All the functions and structures necessary for printing( signal handlers, + * plotters, printer) are here. + * Most of the plotters have been copied from the gtk_plotters.c file. + */ + +#include "utils/config.h" + +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utils.h" +#include "content/content.h" +#include "content/hlcache.h" +#include "utils/nsoption.h" +#include "desktop/plotters.h" +#include "desktop/print.h" +#include "desktop/printer.h" + +#include "gtk/layout_pango.h" +#include "gtk/bitmap.h" +#include "gtk/print.h" +#include "gtk/scaffolding.h" + +/* Globals */ +cairo_t *gtk_print_current_cr; +static struct print_settings* settings; +hlcache_handle *content_to_print; +static GdkRectangle cliprect; + +static inline void nsgtk_print_set_colour(colour c) +{ + int r, g, b; + + r = c & 0xff; + g = (c & 0xff00) >> 8; + b = (c & 0xff0000) >> 16; + +#ifdef FIXME + GdkColor colour; + colour.red = r | (r << 8); + colour.green = g | (g << 8); + colour.blue = b | (b << 8); + colour.pixel = (r << 16) | (g << 8) | b; + gdk_colormap_alloc_color(gdk_colormap_get_system(), &colour, true, true); +#endif + cairo_set_source_rgba(gtk_print_current_cr, r / 255.0, + g / 255.0, b / 255.0, 1.0); +} + + + +static bool gtk_print_font_paint(int x, int y, + const char *string, size_t length, + const plot_font_style_t *fstyle) +{ + PangoFontDescription *desc; + PangoLayout *layout; + gint size; + PangoLayoutLine *line; + + if (length == 0) + return true; + + desc = nsfont_style_to_description(fstyle); + size = (gint) ((double) pango_font_description_get_size(desc) * + settings->scale); + + if (pango_font_description_get_size_is_absolute(desc)) + pango_font_description_set_absolute_size(desc, size); + else + pango_font_description_set_size(desc, size); + + layout = pango_cairo_create_layout(gtk_print_current_cr); + + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, string, length); + + line = pango_layout_get_line(layout, 0); + + cairo_move_to(gtk_print_current_cr, x, y); + nsgtk_print_set_colour(fstyle->foreground); + pango_cairo_show_layout_line(gtk_print_current_cr, line); + + g_object_unref(layout); + pango_font_description_free(desc); + + return true; +} + + +/** Set cairo context to solid plot operation. */ +static inline void nsgtk_print_set_solid(void) +{ + double dashes = 0; + cairo_set_dash(gtk_print_current_cr, &dashes, 0, 0); +} + +/** Set cairo context to dotted plot operation. */ +static inline void nsgtk_print_set_dotted(void) +{ + double cdashes[] = { 1.0, 2.0 }; + cairo_set_dash(gtk_print_current_cr, cdashes, 1, 0); +} + +/** Set cairo context to dashed plot operation. */ +static inline void nsgtk_print_set_dashed(void) +{ + double cdashes[] = { 8.0, 2.0 }; + cairo_set_dash(gtk_print_current_cr, cdashes, 1, 0); +} + +/** Set clipping area for subsequent plot operations. */ +static bool nsgtk_print_plot_clip(const struct rect *clip) +{ + LOG("Clipping. x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i", clip->x0, clip->y0, clip->x1, clip->y1); + + /* Normalize cllipping area - to prevent overflows. + * See comment in pdf_plot_fill. */ + int clip_x0 = min(max(clip->x0, 0), settings->page_width); + int clip_y0 = min(max(clip->y0, 0), settings->page_height); + int clip_x1 = min(max(clip->x1, 0), settings->page_width); + int clip_y1 = min(max(clip->y1, 0), settings->page_height); + + cairo_reset_clip(gtk_print_current_cr); + cairo_rectangle(gtk_print_current_cr, clip_x0, clip_y0, + clip_x1 - clip_x0, clip_y1 - clip_y0); + cairo_clip(gtk_print_current_cr); + + cliprect.x = clip_x0; + cliprect.y = clip_y0; + cliprect.width = clip_x1 - clip_x0; + cliprect.height = clip_y1 - clip_y0; + + return true; +} + +static bool nsgtk_print_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style) +{ + nsgtk_print_set_colour(style->fill_colour); + nsgtk_print_set_solid(); + + cairo_set_line_width(gtk_print_current_cr, 1); + cairo_arc(gtk_print_current_cr, x, y, radius, + (angle1 + 90) * (M_PI / 180), + (angle2 + 90) * (M_PI / 180)); + cairo_stroke(gtk_print_current_cr); + + return true; +} + +static bool nsgtk_print_plot_disc(int x, int y, int radius, const plot_style_t *style) +{ + if (style->fill_type != PLOT_OP_TYPE_NONE) { + nsgtk_print_set_colour(style->fill_colour); + nsgtk_print_set_solid(); + cairo_set_line_width(gtk_print_current_cr, 0); + cairo_arc(gtk_print_current_cr, x, y, radius, 0, M_PI * 2); + cairo_fill(gtk_print_current_cr); + cairo_stroke(gtk_print_current_cr); + } + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + nsgtk_print_set_colour(style->stroke_colour); + + switch (style->stroke_type) { + case PLOT_OP_TYPE_SOLID: /**< Solid colour */ + default: + nsgtk_print_set_solid(); + break; + + case PLOT_OP_TYPE_DOT: /**< Doted plot */ + nsgtk_print_set_dotted(); + break; + + case PLOT_OP_TYPE_DASH: /**< dashed plot */ + nsgtk_print_set_dashed(); + break; + } + + if (style->stroke_width == 0) + cairo_set_line_width(gtk_print_current_cr, 1); + else + cairo_set_line_width(gtk_print_current_cr, style->stroke_width); + + cairo_arc(gtk_print_current_cr, x, y, radius, 0, M_PI * 2); + + cairo_stroke(gtk_print_current_cr); + } + return true; +} + +static bool nsgtk_print_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style) +{ + nsgtk_print_set_colour(style->stroke_colour); + + switch (style->stroke_type) { + case PLOT_OP_TYPE_SOLID: /**< Solid colour */ + default: + nsgtk_print_set_solid(); + break; + + case PLOT_OP_TYPE_DOT: /**< Doted plot */ + nsgtk_print_set_dotted(); + break; + + case PLOT_OP_TYPE_DASH: /**< dashed plot */ + nsgtk_print_set_dashed(); + break; + } + + if (style->stroke_width == 0) + cairo_set_line_width(gtk_print_current_cr, 1); + else + cairo_set_line_width(gtk_print_current_cr, style->stroke_width); + + cairo_move_to(gtk_print_current_cr, x0 + 0.5, y0 + 0.5); + cairo_line_to(gtk_print_current_cr, x1 + 0.5, y1 + 0.5); + cairo_stroke(gtk_print_current_cr); + + return true; +} + +static bool nsgtk_print_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style) +{ + LOG("x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i", x0, y0, x1, y1); + + if (style->fill_type != PLOT_OP_TYPE_NONE) { + + nsgtk_print_set_colour(style->fill_colour); + nsgtk_print_set_solid(); + + /* Normalize boundaries of the area - to prevent overflows. + * See comment in pdf_plot_fill. */ + x0 = min(max(x0, 0), settings->page_width); + y0 = min(max(y0, 0), settings->page_height); + x1 = min(max(x1, 0), settings->page_width); + y1 = min(max(y1, 0), settings->page_height); + + cairo_set_line_width(gtk_print_current_cr, 0); + cairo_rectangle(gtk_print_current_cr, x0, y0, x1 - x0, y1 - y0); + cairo_fill(gtk_print_current_cr); + cairo_stroke(gtk_print_current_cr); + } + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + nsgtk_print_set_colour(style->stroke_colour); + + switch (style->stroke_type) { + case PLOT_OP_TYPE_SOLID: /**< Solid colour */ + default: + nsgtk_print_set_solid(); + break; + + case PLOT_OP_TYPE_DOT: /**< Doted plot */ + nsgtk_print_set_dotted(); + break; + + case PLOT_OP_TYPE_DASH: /**< dashed plot */ + nsgtk_print_set_dashed(); + break; + } + + if (style->stroke_width == 0) + cairo_set_line_width(gtk_print_current_cr, 1); + else + cairo_set_line_width(gtk_print_current_cr, style->stroke_width); + + cairo_rectangle(gtk_print_current_cr, x0, y0, x1 - x0, y1 - y0); + cairo_stroke(gtk_print_current_cr); + } + + return true; +} + +static bool nsgtk_print_plot_polygon(const int *p, unsigned int n, const plot_style_t *style) +{ + unsigned int i; + + LOG("Plotting polygon."); + + nsgtk_print_set_colour(style->fill_colour); + nsgtk_print_set_solid(); + + cairo_set_line_width(gtk_print_current_cr, 0); + cairo_move_to(gtk_print_current_cr, p[0], p[1]); + + LOG("Starting line at: %i\t%i", p[0], p[1]); + + for (i = 1; i != n; i++) { + cairo_line_to(gtk_print_current_cr, p[i * 2], p[i * 2 + 1]); + LOG("Drawing line to: %i\t%i", p[i * 2], p[i * 2 + 1]); + } + + cairo_fill(gtk_print_current_cr); + cairo_stroke(gtk_print_current_cr); + + return true; +} + + +static bool nsgtk_print_plot_path(const float *p, unsigned int n, colour fill, + float width, colour c, const float transform[6]) +{ + /* Only the internal SVG renderer uses this plot call currently, + * and the GTK version uses librsvg. Thus, we ignore this complexity, + * and just return true obliviously. */ + + return true; +} + + +static bool nsgtk_print_plot_pixbuf(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg) +{ + int x0, y0, x1, y1; + int dsrcx, dsrcy, dwidth, dheight; + int bmwidth, bmheight; + + cairo_surface_t *bmsurface = bitmap->surface; + + /* Bail early if we can */ + if (width == 0 || height == 0) + /* Nothing to plot */ + return true; + if ((x > (cliprect.x + cliprect.width)) || + ((x + width) < cliprect.x) || + (y > (cliprect.y + cliprect.height)) || + ((y + height) < cliprect.y)) { + /* Image completely outside clip region */ + return true; + } + + /* Get clip rectangle / image rectangle edge differences */ + x0 = cliprect.x - x; + y0 = cliprect.y - y; + x1 = (x + width) - (cliprect.x + cliprect.width); + y1 = (y + height) - (cliprect.y + cliprect.height); + + /* Set initial draw geometry */ + dsrcx = x; + dsrcy = y; + dwidth = width; + dheight = height; + + /* Manually clip draw coordinates to area of image to be rendered */ + if (x0 > 0) { + /* Clip left */ + dsrcx += x0; + dwidth -= x0; + } + if (y0 > 0) { + /* Clip top */ + dsrcy += y0; + dheight -= y0; + } + if (x1 > 0) { + /* Clip right */ + dwidth -= x1; + } + if (y1 > 0) { + /* Clip bottom */ + dheight -= y1; + } + + if (dwidth == 0 || dheight == 0) + /* Nothing to plot */ + return true; + + bmwidth = cairo_image_surface_get_width(bmsurface); + bmheight = cairo_image_surface_get_height(bmsurface); + + /* Render the bitmap */ + if ((bmwidth == width) && (bmheight == height)) { + /* Bitmap is not scaled */ + /* Plot the bitmap */ + cairo_set_source_surface(gtk_print_current_cr, bmsurface, x, y); + cairo_rectangle(gtk_print_current_cr, dsrcx, dsrcy, dwidth, dheight); + cairo_fill(gtk_print_current_cr); + + } else { + /* Bitmap is scaled */ + if ((bitmap->scsurface != NULL) && + ((cairo_image_surface_get_width(bitmap->scsurface) != width) || + (cairo_image_surface_get_height(bitmap->scsurface) != height))){ + cairo_surface_destroy(bitmap->scsurface); + bitmap->scsurface = NULL; + } + + if (bitmap->scsurface == NULL) { + bitmap->scsurface = cairo_surface_create_similar(bmsurface,CAIRO_CONTENT_COLOR_ALPHA, width, height); + cairo_t *cr = cairo_create(bitmap->scsurface); + + /* Scale *before* setting the source surface (1) */ + cairo_scale(cr, (double)width / bmwidth, (double)height / bmheight); + cairo_set_source_surface(cr, bmsurface, 0, 0); + + /* To avoid getting the edge pixels blended with 0 + * alpha, which would occur with the default + * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer (2) + */ + cairo_pattern_set_extend(cairo_get_source(cr), + CAIRO_EXTEND_REFLECT); + + /* Replace the destination with the source instead of + * overlaying + */ + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + /* Do the actual drawing */ + cairo_paint(cr); + + cairo_destroy(cr); + + } + /* Plot the scaled bitmap */ + cairo_set_source_surface(gtk_print_current_cr, bitmap->scsurface, x, y); + cairo_rectangle(gtk_print_current_cr, dsrcx, dsrcy, dwidth, dheight); + cairo_fill(gtk_print_current_cr); + + } + + return true; +} + + +static bool nsgtk_print_plot_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bitmap_flags_t flags) +{ + int doneheight = 0, donewidth = 0; + bool repeat_x = (flags & BITMAPF_REPEAT_X); + bool repeat_y = (flags & BITMAPF_REPEAT_Y); + + if (!(repeat_x || repeat_y)) { + /* Not repeating at all, so just pass it on */ + return nsgtk_print_plot_pixbuf(x, y, width, height, bitmap, bg); + } + + width = nsgtk_bitmap_get_width(bitmap); + height = nsgtk_bitmap_get_height(bitmap); + + /* Bail early if we can */ + if (width == 0 || height == 0) + /* Nothing to plot */ + return true; + + if (y > cliprect.y) { + doneheight = (cliprect.y - height) + ((y - cliprect.y) % height); + } else { + doneheight = y; + } + + while (doneheight < (cliprect.y + cliprect.height)) { + if (x > cliprect.x) { + donewidth = (cliprect.x - width) + ((x - cliprect.x) % width); + } else { + donewidth = x; + } + + while (donewidth < (cliprect.x + cliprect.width)) { + nsgtk_print_plot_pixbuf(donewidth, doneheight, + width, height, bitmap, bg); + donewidth += width; + if (!repeat_x) + break; + } + doneheight += height; + + if (!repeat_y) + break; + } + + return true; +} + +static bool nsgtk_print_plot_text(int x, int y, const char *text, size_t length, + const plot_font_style_t *fstyle) +{ + return gtk_print_font_paint(x, y, text, length, fstyle); +} + +/** GTK print plotter table */ +static const struct plotter_table nsgtk_print_plotters = { + .clip = nsgtk_print_plot_clip, + .arc = nsgtk_print_plot_arc, + .disc = nsgtk_print_plot_disc, + .line = nsgtk_print_plot_line, + .rectangle = nsgtk_print_plot_rectangle, + .polygon = nsgtk_print_plot_polygon, + .path = nsgtk_print_plot_path, + .bitmap = nsgtk_print_plot_bitmap, + .text = nsgtk_print_plot_text, + .option_knockout = false, +}; + +static bool gtk_print_begin(struct print_settings* settings) +{ + return true; +} + +static bool gtk_print_next_page(void) +{ + return true; +} + +static void gtk_print_end(void) +{ +} + +static const struct printer gtk_printer = { + &nsgtk_print_plotters, + gtk_print_begin, + gtk_print_next_page, + gtk_print_end +}; + +/** + * Handle the begin_print signal from the GtkPrintOperation + * + * \param operation the operation which emited the signal + * \param context the print context used to set up the pages + * \param user_data nothing in here + */ +void gtk_print_signal_begin_print (GtkPrintOperation *operation, + GtkPrintContext *context, gpointer user_data) +{ + int page_number; + double height_on_page, height_to_print; + + LOG("Begin print"); + + settings = user_data; + + settings->margins[MARGINTOP] = 0; + settings->margins[MARGINLEFT] = 0; + settings->margins[MARGINBOTTOM] = 0; + settings->margins[MARGINRIGHT] = 0; + settings->page_width = gtk_print_context_get_width(context); + settings->page_height = gtk_print_context_get_height(context); + settings->scale = 0.7; /* at 0.7 the pages look the best */ + settings->font_func = nsgtk_layout_table; + + if (print_set_up(content_to_print, >k_printer, + settings, &height_to_print) == false) { + gtk_print_operation_cancel(operation); + + } else { + + LOG("page_width: %f ;page_height: %f; content height: %lf", settings->page_width, settings->page_height, height_to_print); + + height_on_page = settings->page_height; + height_on_page = height_on_page - + FIXTOFLT(FSUB(settings->margins[MARGINTOP], + settings->margins[MARGINBOTTOM])); + height_to_print *= settings->scale; + + page_number = height_to_print / height_on_page; + + if (height_to_print - page_number * height_on_page > 0) + page_number += 1; + + gtk_print_operation_set_n_pages(operation, page_number); + } +} + +/** + * Handle the draw_page signal from the GtkPrintOperation. + * This function changes only the cairo context to print on. + */ +void gtk_print_signal_draw_page(GtkPrintOperation *operation, + GtkPrintContext *context, gint page_nr, gpointer user_data) +{ + LOG("Draw Page"); + gtk_print_current_cr = gtk_print_context_get_cairo_context(context); + print_draw_next_page(>k_printer, settings); +} + +/** + * Handle the end_print signal from the GtkPrintOperation. + * This functions calls only the print_cleanup function from the print interface + */ +void gtk_print_signal_end_print(GtkPrintOperation *operation, + GtkPrintContext *context, gpointer user_data) +{ + LOG("End print"); + print_cleanup(content_to_print, >k_printer, user_data); +} + + diff --git a/frontends/gtk/print.h b/frontends/gtk/print.h new file mode 100644 index 000000000..d44fad31f --- /dev/null +++ b/frontends/gtk/print.h @@ -0,0 +1,50 @@ +/* + * Copyright 2008 Adam Blokus + * + * 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 . + */ + + /** \file + * GTK printing (interface). + */ + +#ifndef NETSURF_GTK_PRINT_PLOTTERS_H +#define NETSURF_GTK_PRINT_PLOTTERS_H + + +#include + +struct hlcache_handle; + +extern cairo_t *gtk_print_current_cr; + +extern struct hlcache_handle *content_to_print; + + +/*handlers for signals from the GTK print operation*/ +void gtk_print_signal_begin_print(GtkPrintOperation *operation, + GtkPrintContext *context, + gpointer user_data); + +void gtk_print_signal_draw_page(GtkPrintOperation *operation, + GtkPrintContext *context, + gint page_nr, + gpointer user_data); + +void gtk_print_signal_end_print(GtkPrintOperation *operation, + GtkPrintContext *context, + gpointer user_data); + +#endif diff --git a/frontends/gtk/res/Messages b/frontends/gtk/res/Messages new file mode 120000 index 000000000..75bfdf53d --- /dev/null +++ b/frontends/gtk/res/Messages @@ -0,0 +1 @@ +en/Messages \ No newline at end of file diff --git a/frontends/gtk/res/SearchEngines b/frontends/gtk/res/SearchEngines new file mode 120000 index 000000000..3dc819c2a --- /dev/null +++ b/frontends/gtk/res/SearchEngines @@ -0,0 +1 @@ +../../../resources/SearchEngines \ No newline at end of file diff --git a/frontends/gtk/res/adblock.css b/frontends/gtk/res/adblock.css new file mode 120000 index 000000000..ff2485622 --- /dev/null +++ b/frontends/gtk/res/adblock.css @@ -0,0 +1 @@ +../../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file diff --git a/frontends/gtk/res/arrow_down_8x32.png b/frontends/gtk/res/arrow_down_8x32.png new file mode 100644 index 000000000..475b4ff61 Binary files /dev/null and b/frontends/gtk/res/arrow_down_8x32.png differ diff --git a/frontends/gtk/res/ca-bundle.txt b/frontends/gtk/res/ca-bundle.txt new file mode 120000 index 000000000..0b0e416ad --- /dev/null +++ b/frontends/gtk/res/ca-bundle.txt @@ -0,0 +1 @@ +../../../!NetSurf/Resources/ca-bundle \ No newline at end of file diff --git a/frontends/gtk/res/cookies.gtk2.ui b/frontends/gtk/res/cookies.gtk2.ui new file mode 100644 index 000000000..86f15c765 --- /dev/null +++ b/frontends/gtk/res/cookies.gtk2.ui @@ -0,0 +1,174 @@ + + + + + + Cookies - NetSurf + mouse + 600 + 500 + + + True + 2 + vertical + 2 + + + True + + + True + _Edit + True + + + True + + + True + _Delete + True + + + + + + True + D_elete all + True + + + + + True + _Select all + True + + + + + + True + _Clear selection + True + + + + + + + + + + True + _View + True + + + True + + + True + _Expand + True + + + True + + + True + _All + True + + + + + True + _Domains + True + + + + + True + _Cookies + True + + + + + + + + + True + Collapse + True + + + True + + + True + _All + True + + + + + True + _Domains + True + + + + + True + _Cookies + True + + + + + + + + + + + + + False + 0 + + + + + True + True + automatic + automatic + + + True + queue + + + True + True + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + + + 1 + + + + + + diff --git a/frontends/gtk/res/cookies.gtk3.ui b/frontends/gtk/res/cookies.gtk3.ui new file mode 100644 index 000000000..44dcb80b8 --- /dev/null +++ b/frontends/gtk/res/cookies.gtk3.ui @@ -0,0 +1,206 @@ + + + + + False + NetSurf Cookies + mouse + 600 + 500 + + + True + False + vertical + + + True + False + + + False + True + False + _Edit + True + + + True + False + + + False + True + False + _Delete + True + + + + + False + True + False + D_elete all + True + + + + + False + True + False + _Select all + True + + + + + False + True + False + _Clear selection + True + + + + + + + + + False + True + False + _View + True + + + True + False + + + False + True + False + _Expand + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Domains + True + + + + + False + True + False + _Cookies + True + + + + + + + + + False + True + False + Collapse + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Domains + True + + + + + False + True + False + _Cookies + True + + + + + + + + + + + + + False + True + 0 + + + + + True + True + never + in + + + True + False + + + True + True + False + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + + + True + True + 1 + + + + + + diff --git a/frontends/gtk/res/credits.html b/frontends/gtk/res/credits.html new file mode 120000 index 000000000..ca85d3d27 --- /dev/null +++ b/frontends/gtk/res/credits.html @@ -0,0 +1 @@ +en/credits.html \ No newline at end of file diff --git a/frontends/gtk/res/de/welcome.html b/frontends/gtk/res/de/welcome.html new file mode 120000 index 000000000..98a53b215 --- /dev/null +++ b/frontends/gtk/res/de/welcome.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/de/welcome.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/default.css b/frontends/gtk/res/default.css new file mode 120000 index 000000000..a8579eb7c --- /dev/null +++ b/frontends/gtk/res/default.css @@ -0,0 +1 @@ +../../../!NetSurf/Resources/CSS,f79 \ No newline at end of file diff --git a/frontends/gtk/res/default.ico b/frontends/gtk/res/default.ico new file mode 100644 index 000000000..1cb432828 Binary files /dev/null and b/frontends/gtk/res/default.ico differ diff --git a/frontends/gtk/res/downloads.gtk2.ui b/frontends/gtk/res/downloads.gtk2.ui new file mode 100644 index 000000000..1e71328a4 --- /dev/null +++ b/frontends/gtk/res/downloads.gtk2.ui @@ -0,0 +1,175 @@ + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Downloads - NetSurf + mouse + 500 + 300 + True + + + True + + + True + True + never + automatic + + + True + True + 2 + False + True + + + + + 0 + + + + + True + + + False + 1 + + + + + True + + + True + 2 + 2 + 2 + 2 + + + True + True + 0% of 0 files + + + + + 0 + + + + + True + True + end + + + gtk-media-pause + True + False + True + True + none + True + + + False + False + 0 + + + + + True + False + True + True + none + + + True + + + True + gtk-media-play + 2 + + + 0 + + + + + True + Resume + + + 1 + + + + + + + False + False + 1 + + + + + gtk-cancel + True + False + True + True + none + True + + + False + False + 2 + + + + + gtk-clear + True + False + True + True + none + True + + + False + False + 3 + + + + + False + False + end + 1 + + + + + False + False + 2 + + + + + + diff --git a/frontends/gtk/res/downloads.gtk3.ui b/frontends/gtk/res/downloads.gtk3.ui new file mode 100644 index 000000000..1e71328a4 --- /dev/null +++ b/frontends/gtk/res/downloads.gtk3.ui @@ -0,0 +1,175 @@ + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Downloads - NetSurf + mouse + 500 + 300 + True + + + True + + + True + True + never + automatic + + + True + True + 2 + False + True + + + + + 0 + + + + + True + + + False + 1 + + + + + True + + + True + 2 + 2 + 2 + 2 + + + True + True + 0% of 0 files + + + + + 0 + + + + + True + True + end + + + gtk-media-pause + True + False + True + True + none + True + + + False + False + 0 + + + + + True + False + True + True + none + + + True + + + True + gtk-media-play + 2 + + + 0 + + + + + True + Resume + + + 1 + + + + + + + False + False + 1 + + + + + gtk-cancel + True + False + True + True + none + True + + + False + False + 2 + + + + + gtk-clear + True + False + True + True + none + True + + + False + False + 3 + + + + + False + False + end + 1 + + + + + False + False + 2 + + + + + + diff --git a/frontends/gtk/res/en/credits.html b/frontends/gtk/res/en/credits.html new file mode 120000 index 000000000..252516fd7 --- /dev/null +++ b/frontends/gtk/res/en/credits.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/en/credits.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/en/licence.html b/frontends/gtk/res/en/licence.html new file mode 120000 index 000000000..79f73669b --- /dev/null +++ b/frontends/gtk/res/en/licence.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/en/licence.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/en/maps.html b/frontends/gtk/res/en/maps.html new file mode 120000 index 000000000..bb3ffcbe7 --- /dev/null +++ b/frontends/gtk/res/en/maps.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/en/maps.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/en/welcome.html b/frontends/gtk/res/en/welcome.html new file mode 120000 index 000000000..601099223 --- /dev/null +++ b/frontends/gtk/res/en/welcome.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/en/welcome.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/favicon.png b/frontends/gtk/res/favicon.png new file mode 120000 index 000000000..5a8b3433c --- /dev/null +++ b/frontends/gtk/res/favicon.png @@ -0,0 +1 @@ +../../../resources/favicon.png \ No newline at end of file diff --git a/frontends/gtk/res/history.gtk2.ui b/frontends/gtk/res/history.gtk2.ui new file mode 100644 index 000000000..2b89ecb4b --- /dev/null +++ b/frontends/gtk/res/history.gtk2.ui @@ -0,0 +1,242 @@ + + + + + False + NetSurf Global History + center + 600 + 500 + utility + + + True + False + + + True + False + + + False + True + False + _File + True + + + True + False + + + False + True + False + _Export + True + + + + + + + + + + False + True + False + _Edit + True + + + True + False + + + False + True + False + _Delete + True + + + + + + False + True + False + D_elete all + True + + + + + False + True + False + _Select all + True + + + + + + False + True + False + _Clear selection + True + + + + + + + + + + False + True + False + _View + True + + + True + False + + + False + True + False + _Expand + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Directories + True + + + + + False + True + False + Add_resses + True + + + + + + + + + False + True + False + _Collapse + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Directories + True + + + + + False + True + False + Add_resses + True + + + + + + + + + + + + + False + True + False + _Launch + True + + + + + False + True + 0 + + + + + True + True + + + True + False + queue + + + True + True + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + + + True + True + 1 + + + + + + diff --git a/frontends/gtk/res/history.gtk3.ui b/frontends/gtk/res/history.gtk3.ui new file mode 100644 index 000000000..7fa598f1e --- /dev/null +++ b/frontends/gtk/res/history.gtk3.ui @@ -0,0 +1,238 @@ + + + + + False + NetSurf Global History + center + 600 + 500 + utility + + + True + False + vertical + + + True + False + + + False + True + False + _File + True + + + True + False + + + False + True + False + _Export + True + + + + + + + + + False + True + False + _Edit + True + + + True + False + + + False + True + False + _Delete + True + + + + + False + True + False + D_elete all + True + + + + + False + True + False + _Select all + True + + + + + False + True + False + _Clear selection + True + + + + + + + + + False + True + False + _View + True + + + True + False + + + False + True + False + _Expand + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Directories + True + + + + + False + True + False + Add_resses + True + + + + + + + + + False + True + False + _Collapse + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Directories + True + + + + + False + True + False + Add_resses + True + + + + + + + + + + + + + False + True + False + _Launch + True + + + + + False + True + 0 + + + + + True + True + in + + + True + False + + + True + False + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + + + True + True + 1 + + + + + + diff --git a/frontends/gtk/res/hotlist.gtk2.ui b/frontends/gtk/res/hotlist.gtk2.ui new file mode 100644 index 000000000..af0fd5696 --- /dev/null +++ b/frontends/gtk/res/hotlist.gtk2.ui @@ -0,0 +1,217 @@ + + + + + + Bookmarks - NetSurf + mouse + 600 + 500 + + + True + 2 + vertical + 2 + + + True + + + True + _File + True + + + True + + + True + _Export + True + + + + + + True + New _folder + True + + + + + + True + New _entry + True + + + + + + + + + + True + _Edit + True + + + True + + + True + _Edit + True + + + + + True + _Delete + True + + + + + + True + _Select all + True + + + + + + True + _Clear selection + True + + + + + + + + + + True + _View + True + + + True + + + True + _Expand + True + + + True + + + True + _All + True + + + + + True + _Directories + True + + + + + True + Add_resses + True + + + + + + + + + True + _Collapse + True + + + True + + + True + _All + True + + + + + True + _Directories + True + + + + + True + Add_resses + True + + + + + + + + + + + + + True + _Launch + True + + + + + False + 0 + + + + + True + True + automatic + automatic + + + True + queue + + + True + True + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + + + 1 + + + + + + diff --git a/frontends/gtk/res/hotlist.gtk3.ui b/frontends/gtk/res/hotlist.gtk3.ui new file mode 100644 index 000000000..b0e075c4b --- /dev/null +++ b/frontends/gtk/res/hotlist.gtk3.ui @@ -0,0 +1,255 @@ + + + + + False + NetSurf Bookmarks + mouse + 600 + 500 + + + True + False + vertical + + + True + False + + + False + True + False + _File + True + + + True + False + + + False + True + False + _Export + True + + + + + False + True + False + New _folder + True + + + + + False + True + False + New _entry + True + + + + + + + + + False + True + False + _Edit + True + + + True + False + + + False + True + False + _Edit + True + + + + + False + True + False + _Delete + True + + + + + False + True + False + _Select all + True + + + + + False + True + False + _Clear selection + True + + + + + + + + + False + True + False + _View + True + + + True + False + + + False + True + False + _Expand + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Directories + True + + + + + False + True + False + Add_resses + True + + + + + + + + + False + True + False + _Collapse + True + + + True + False + + + False + True + False + _All + True + + + + + False + True + False + _Directories + True + + + + + False + True + False + Add_resses + True + + + + + + + + + + + + + False + True + False + _Launch + True + + + + + False + True + 0 + + + + + True + True + in + + + True + False + + + True + False + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + + + True + True + 1 + + + + + + diff --git a/frontends/gtk/res/icons b/frontends/gtk/res/icons new file mode 120000 index 000000000..187efd6f9 --- /dev/null +++ b/frontends/gtk/res/icons @@ -0,0 +1 @@ +../../../!NetSurf/Resources/Icons/ \ No newline at end of file diff --git a/frontends/gtk/res/internal.css b/frontends/gtk/res/internal.css new file mode 120000 index 000000000..17f9f1504 --- /dev/null +++ b/frontends/gtk/res/internal.css @@ -0,0 +1 @@ +../../../!NetSurf/Resources/internal.css,f79 \ No newline at end of file diff --git a/frontends/gtk/res/it/credits.html b/frontends/gtk/res/it/credits.html new file mode 120000 index 000000000..64b78982e --- /dev/null +++ b/frontends/gtk/res/it/credits.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/it/credits.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/it/licence.html b/frontends/gtk/res/it/licence.html new file mode 120000 index 000000000..4abc825d3 --- /dev/null +++ b/frontends/gtk/res/it/licence.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/it/licence.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/it/welcome.html b/frontends/gtk/res/it/welcome.html new file mode 120000 index 000000000..59cef0551 --- /dev/null +++ b/frontends/gtk/res/it/welcome.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/it/welcome.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/ja/welcome.html b/frontends/gtk/res/ja/welcome.html new file mode 120000 index 000000000..a2556ee4e --- /dev/null +++ b/frontends/gtk/res/ja/welcome.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/ja/welcome.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/languages b/frontends/gtk/res/languages new file mode 100644 index 000000000..4927e03a0 --- /dev/null +++ b/frontends/gtk/res/languages @@ -0,0 +1,261 @@ +aa +ab +ae +af +ak +am +an +ar +ar-ae +ar-bh +ar-dz +ar-eg +ar-iq +ar-jo +ar-kw +ar-lb +ar-ly +ar-ma +ar-om +ar-qa +ar-sa +ar-sy +ar-tn +ar-ye +as +ast +av +az +ba +be +bg +bh +bi +bm +bn +bo +br +bs +ca +ce +ch +co +cr +cs +cu +cv +cy +da +de +de-at +de-ch +de-de +de-li +de-lu +dv +dz +ee +el +en +en-au +en-bz +en-ca +en-gb +en-ie +en-jm +en-nz +en-ph +en-tt +en-us +en-za +en-zw +eo +es +es-ar +es-bo +es-cl +es-co +es-cr +es-do +es-ec +es-es +es-gt +es-hn +es-mx +es-ni +es-pa +es-pe +es-pr +es-py +es-sv +es-uy +es-ve +et +eu +fa +fa-ir +ff +fi +fj +fo +fr +fr-be +fr-ca +fr-ch +fr-fr +fr-lu +fr-mc +fur +fy +ga +gd +gl +gn +gu +gv +ha +he +hi +ho +hsb +hr +ht +hu +hy +hz +ia +id +ie +ig +ii +ik +io +is +it +it-ch +iu +ja +jv +ka +kg +ki +kk +kl +km +kn +ko +ko-kp +ko-kr +kok +kr +ks +ku +kv +kw +ky +la +lb +lg +li +ln +lo +lt +lu +lv +mg +mh +mi +mk +mk-mk +ml +mn +mo +mr +ms +mt +my +na +nb +nd +ne +ng +nl +nl-be +nn +no +nr +nso +nv +ny +oc +oj +om +or +os +pa +pa-in +pa-pk +pi +pl +ps +pt +pt-br +qu +rm +rn +ro +ro-mo +ru +ru-mo +sa +sc +sd +sg +si +sk +sl +so +sq +sr +ss +st +su +sv +sv-fi +sv-se +sw +ta +te +tg +th +ti +tig +tk +tl +tlh +tn +to +tr +ts +tt +tw +ty +ug +uk +ur +uz +ve +vi +vo +wa +wo +xh +yi +yo +za +zh +zh-cn +zh-hk +zh-sg +zh-tw +zu diff --git a/frontends/gtk/res/licence.html b/frontends/gtk/res/licence.html new file mode 120000 index 000000000..86f8c54bf --- /dev/null +++ b/frontends/gtk/res/licence.html @@ -0,0 +1 @@ +en/licence.html \ No newline at end of file diff --git a/frontends/gtk/res/login.gtk2.ui b/frontends/gtk/res/login.gtk2.ui new file mode 100644 index 000000000..552b173ed --- /dev/null +++ b/frontends/gtk/res/login.gtk2.ui @@ -0,0 +1,223 @@ + + + + + Site Authentication + GTK_WIN_POS_CENTER_ALWAYS + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + + + True + 3 + + + True + 0.10000000149011612 + 12 + 6 + gtk-dialog-authentication + + + False + False + + + + + True + 1 + 4 + 2 + 11 + 10 + + + True + 0 + moo.yoo.com + + + 1 + 2 + + + + + + True + 0 + Password + + + 3 + 4 + + + + + + True + 0 + Username + + + 2 + 3 + + + + + + True + 0 + Host + + + + + + + + True + 0 + Realm + + + 1 + 2 + + + + + + True + 0 + my sekr3t area + + + 1 + 2 + 1 + 2 + + + + + + True + True + False + True + opensesame + + + 1 + 2 + 3 + 4 + + + + + + True + True + True + sesame + + + 1 + 2 + 2 + 3 + + + + + + 1 + 1 + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + gtk-cancel + True + + + + + True + True + True + True + + + True + True + True + 0 + 0 + + + True + 2 + + + True + gtk-ok + + + False + False + + + + + True + Login + True + + + False + False + 1 + + + + + + + + + 1 + + + + + False + GTK_PACK_END + + + + + + buttonLoginCan + buttonLoginOK + + + diff --git a/frontends/gtk/res/login.gtk3.ui b/frontends/gtk/res/login.gtk3.ui new file mode 100644 index 000000000..552b173ed --- /dev/null +++ b/frontends/gtk/res/login.gtk3.ui @@ -0,0 +1,223 @@ + + + + + Site Authentication + GTK_WIN_POS_CENTER_ALWAYS + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + + + True + 3 + + + True + 0.10000000149011612 + 12 + 6 + gtk-dialog-authentication + + + False + False + + + + + True + 1 + 4 + 2 + 11 + 10 + + + True + 0 + moo.yoo.com + + + 1 + 2 + + + + + + True + 0 + Password + + + 3 + 4 + + + + + + True + 0 + Username + + + 2 + 3 + + + + + + True + 0 + Host + + + + + + + + True + 0 + Realm + + + 1 + 2 + + + + + + True + 0 + my sekr3t area + + + 1 + 2 + 1 + 2 + + + + + + True + True + False + True + opensesame + + + 1 + 2 + 3 + 4 + + + + + + True + True + True + sesame + + + 1 + 2 + 2 + 3 + + + + + + 1 + 1 + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + gtk-cancel + True + + + + + True + True + True + True + + + True + True + True + 0 + 0 + + + True + 2 + + + True + gtk-ok + + + False + False + + + + + True + Login + True + + + False + False + 1 + + + + + + + + + 1 + + + + + False + GTK_PACK_END + + + + + + buttonLoginCan + buttonLoginOK + + + diff --git a/frontends/gtk/res/maps.html b/frontends/gtk/res/maps.html new file mode 120000 index 000000000..a32f725fb --- /dev/null +++ b/frontends/gtk/res/maps.html @@ -0,0 +1 @@ +en/maps.html \ No newline at end of file diff --git a/frontends/gtk/res/menu_cursor.png b/frontends/gtk/res/menu_cursor.png new file mode 100644 index 000000000..ccbbbd2d4 Binary files /dev/null and b/frontends/gtk/res/menu_cursor.png differ diff --git a/frontends/gtk/res/menu_cursor.xbm b/frontends/gtk/res/menu_cursor.xbm new file mode 100644 index 000000000..1257ac1f4 --- /dev/null +++ b/frontends/gtk/res/menu_cursor.xbm @@ -0,0 +1,6 @@ +#define menu_cursor_width 16 +#define menu_cursor_height 16 +static char menu_cursor_bits[] = { + 0x00, 0x00, 0x80, 0x7F, 0x88, 0x40, 0x9E, 0x5E, 0x88, 0x40, 0x80, 0x56, + 0x80, 0x40, 0x80, 0x5A, 0x80, 0x40, 0x80, 0x5E, 0x80, 0x40, 0x80, 0x56, + 0x80, 0x40, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00 }; diff --git a/frontends/gtk/res/menu_cursor_mask.xbm b/frontends/gtk/res/menu_cursor_mask.xbm new file mode 100644 index 000000000..09789d51b --- /dev/null +++ b/frontends/gtk/res/menu_cursor_mask.xbm @@ -0,0 +1,6 @@ +#define menu_cursor_mask_width 16 +#define menu_cursor_mask_height 16 +static char menu_cursor_mask_bits[] = { + 0xC0, 0xFF, 0xC8, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xC8, 0xFF, + 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, + 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0x00, 0x00 }; diff --git a/frontends/gtk/res/menu_cursor_mask.xpm b/frontends/gtk/res/menu_cursor_mask.xpm new file mode 100644 index 000000000..985d46cc6 --- /dev/null +++ b/frontends/gtk/res/menu_cursor_mask.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * menu_cursor_mask_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ..........", +" . .++++++++.", +"...+. .+......+.", +".++++..+.++++.+.", +"...+. .+......+.", +" . .+.++.+.+.", +" .+......+.", +" .+.+.++.+.", +" .+......+.", +" .+.++++.+.", +" .+......+.", +" .+.++.+.+.", +" .+......+.", +" .++++++++.", +" ..........", +" "}; diff --git a/frontends/gtk/res/messages.gresource.xml b/frontends/gtk/res/messages.gresource.xml new file mode 100644 index 000000000..684a10862 --- /dev/null +++ b/frontends/gtk/res/messages.gresource.xml @@ -0,0 +1,10 @@ + + + + Messages + nl/Messages + de/Messages + fr/Messages + it/Messages + + diff --git a/frontends/gtk/res/netsurf-16x16.xpm b/frontends/gtk/res/netsurf-16x16.xpm new file mode 100644 index 000000000..7880f7aae --- /dev/null +++ b/frontends/gtk/res/netsurf-16x16.xpm @@ -0,0 +1,211 @@ +/* XPM */ +static char *netsurf___x__[] = { +/* columns rows colors chars-per-pixel */ +"16 16 189 2", +" c #02530B521130", +". c #02C50E711619", +"X c #02AF106B198E", +"o c #02D913641E8A", +"O c #04D913E91D9D", +"+ c #042B14C01F86", +"@ c #124A1BC61F1D", +"# c #097F17941FC4", +"$ c #03CE193E2759", +"% c #04451CED2D68", +"& c #0BDC1D8727E6", +"* c #10D2247A2F43", +"= c #069C27663D09", +"- c #180C2AED33E3", +"; c #1AEC2D37353A", +": c #16F12D9C3927", +"> c #20AD32C039B7", +", c #0A2A2E04451D", +"< c #0C1A306A47D4", +"1 c #1D6E39A1482F", +"2 c #060A341B5355", +"3 c #0B773D945E4D", +"4 c #20B93C4949C5", +"5 c #07B03F0A6460", +"6 c #2332407A4E57", +"7 c #253040AD4CF8", +"8 c #33854FAB5A6E", +"9 c #15494CF97003", +"0 c #19F855EA7B34", +"q c #1E7F57237923", +"w c #21344D986676", +"e c #422B60856A1E", +"r c #09DA58208D29", +"t c #11AC562C82BA", +"y c #0A6A5CB99496", +"u c #18A265959712", +"i c #17B8662E9805", +"p c #11A66358990E", +"a c #224A65BB8E98", +"s c #342C75079947", +"d c #3487766A9B6F", +"f c #35B478C99F4B", +"g c #0BFA6509A06D", +"h c #0F9175B8BA3A", +"j c #1AAA7C1DBB21", +"k c #289374DBA2C1", +"l c #200E76DFADCA", +"z c #269D7B1FAF00", +"x c #32E57D4FA875", +"c c #299F805AB558", +"v c #2DF28A1CC027", +"b c #345A83F3B216", +"n c #3151843FB58D", +"m c #59C882138E75", +"M c #5F5E8AAC9842", +"N c #51998AF6A4C9", +"B c #5F5A96AEACF5", +"V c #43048E07B597", +"C c #65129940AC1C", +"Z c #6C1AA2A1B60E", +"A c #7A87AEC9BD67", +"S c #125084AAD039", +"D c #163388A1D450", +"F c #1BF88E3BD899", +"G c #17BD8EB6DCB2", +"H c #220A83D0C214", +"J c #29678B9EC6ED", +"K c #3F59A336DDC8", +"L c #198792ECE247", +"P c #11B295CDED75", +"I c #132A98BFF1CB", +"U c #25099D91EB08", +"Y c #29299E0AE8BB", +"T c #1412A137FE21", +"R c #191EA60AFFFF", +"E c #1C73A663FFFF", +"W c #2E68A1C2E7B3", +"Q c #2C05A14AEAC1", +"! c #2E32A597F03F", +"~ c #39BBA7F4E929", +"^ c #23A0A4D1F872", +"/ c #25C6A4E4F6A5", +"( c #2965A4CCF3B1", +") c #236CA4BAF891", +"_ c #23A1A6A2FA96", +"` c #2018A8FDFFFF", +"' c #25F5AB89FFFF", +"] c #2B0BACCDFECB", +"[ c #2AEBAD69FFF4", +"{ c #2E7EAC78FC88", +"} c #37FCADAFF4EB", +"| c #3B7FAF4EF4E9", +" . c #303DAB56F8E3", +".. c #3354B0E8FFFF", +"X. c #3607B294FFCE", +"o. c #3978B4A2FFF7", +"O. c #414EA3E7DD4D", +"+. c #5643A8B7D325", +"@. c #52B4AADCD9F0", +"#. c #60F0B145D82C", +"$. c #6F40B9C2DA92", +"%. c #71EFBCB5DD40", +"&. c #482FB26FEEA5", +"*. c #5E59B526E061", +"=. c #413CB50EFA06", +"-. c #40FAB7C7FF64", +";. c #411FB840FF4A", +":. c #43C4B9CAFFFA", +">. c #4AB5BB61FFFF", +",. c #4A0ABCF3FFFF", +"<. c #4BB1BD9AFFB4", +"1. c #4D02BE43FFE0", +"2. c #542DBA39F2D5", +"3. c #5682BD17F583", +"4. c #6545BFABECE0", +"5. c #7AD1C155DCC5", +"6. c #512AC23DFFFF", +"7. c #5584C279FF9A", +"8. c #5642C44BFFFF", +"9. c #57E0C1FCF97C", +"0. c #5C81C618FFB3", +"q. c #5F65C55DFFFF", +"w. c #5FA0C7BFFFCB", +"e. c #6BD0C37FEDBC", +"r. c #6FB7C6C0EF7B", +"t. c #7055C5DCEDC7", +"y. c #7A3ECB76EEA8", +"u. c #63C4C2A1F626", +"i. c #64A9C5C0F7C3", +"p. c #6B3AC4CBF902", +"a. c #621EC925FFE9", +"s. c #6557C9F1FE6D", +"d. c #67BACC2FFFFE", +"f. c #66B4CD66FFFF", +"g. c #69E9CD27FFFC", +"h. c #6E23CD02FFBA", +"j. c #6E50CFA0FFFF", +"k. c #7093CC80FFBB", +"l. c #6E47D0E6FFFF", +"z. c #7269D1B2FFFF", +"x. c #73B7D323FFFF", +"c. c #7383D389FFFF", +"v. c #7F08D0B1FFA4", +"b. c #7A4CD5C8FFFF", +"n. c #7C1BD6A7FFFF", +"m. c #7CBAD75AFFFF", +"M. c #8CB9CF6EE5B6", +"N. c #9678D359E3DC", +"B. c #8911D5A7F360", +"V. c #829DD7A7FFFF", +"C. c #8092DA47FFFF", +"Z. c #814DDA85FFFF", +"A. c #839BDA7BFFFF", +"S. c #85D6DB65FFFF", +"D. c #86DFDAB4FFFD", +"F. c #8EE1DDADFC14", +"G. c #8CC8DC55FFFE", +"H. c #894DDD3BFFFE", +"J. c #89CCDDD9FFFF", +"K. c #8AFCDE12FFFC", +"L. c #8D41DF48FFF9", +"P. c #8D5FE05DFFFF", +"I. c #8FB7E05EFFEA", +"U. c #9808DD11F202", +"Y. c #A445E007FF52", +"T. c #96ADE3E2FFB6", +"R. c #9705E548FFFF", +"E. c #9DB5E153FFFD", +"W. c #9914E507FFB9", +"Q. c #9A08E660FFF3", +"!. c #9A40E858FFFF", +"~. c #A7C7E568F44C", +"^. c #A7C5E6F4F48B", +"/. c #A3A6EB6AFFFB", +"(. c #A652EC53FFFA", +"). c #A408ED75FFFF", +"_. c #B2C9E411FFBB", +"`. c #B047E69AFFFB", +"'. c #B8E2E8BFFFF5", +"]. c #D6CBF231FFFF", +"[. c #DE35F506FFFC", +"{. c #E029F512FFFF", +"}. c #E57AF80BFFFE", +"|. c #E6C5F71DFFE9", +" X c #E735F794FFF7", +".X c #E895F7DEFFEE", +"XX c #E9B0F91EFFFE", +"oX c gray100", +"OX c None", +/* pixels */ +"OXOXOXOXOXOX; 7 6 : OXOXOXOXOXOX", +"OXOXOXOX> C 5.y.r.*.V w OXOXOXOX", +"OXOX@ e M ).R.J.m.l.V.u.f & OXOX", +"OXOXm ^.~.(.W.K.D.`.[.].Y.k OXOX", +"OX8 U.A N./.T.H.G.}.oXoX{.p.9 OX", +"OXB !.Z M.Q.I.A.E.XXoXoX|.q.H O ", +"- %.P.B.F.L.S.n.z.'. X.X_.>.U , ", +"4 t.C.N $.Z.b.z.g.h.k.v.-...^ 3 ", +"1 4.c.#.e.x.j.d.w.7.<.;.X.[ F = ", +"* @.f.i.+.s.a.0.7.1.:.o.{ _ p . ", +"OXd 9.3.s 2.8.6.,.=.} ! u G h X ", +"OX# b &.x O.K n | z l / L I 5 OX", +"OXOXq ~ v a c J .( ) E T y OXOX", +"OXOXOX0 W Q Y ] ' ` R P r o OXOX", +"OXOXOXOX< i j t D S g 2 OXOXOXOX", +"OXOXOXOXOXOX+ % $ OXOXOXOXOXOX" +}; diff --git a/frontends/gtk/res/netsurf-gtk.desktop b/frontends/gtk/res/netsurf-gtk.desktop new file mode 100644 index 000000000..4c21d5537 --- /dev/null +++ b/frontends/gtk/res/netsurf-gtk.desktop @@ -0,0 +1,68 @@ +[Desktop Entry] +Name=NetSurf Web Browser +Name[ca]=Navegador web NetSurf +Name[cs]=NetSurf Webový prohlížeč +Name[es]=Navegador web NetSurf +Name[fa]=مرورگر اینترنتی NetSurf +Name[fi]=NetSurf-selain +Name[fr]=Navigateur Web NetSurf +Name[hu]=NetSurf webböngésző +Name[it]=NetSurf Browser Web +Name[ja]=NetSurf ウェブ・ブラウザ +Name[ko]=NetSurf 웹 브라우저 +Name[nb]=NetSurf Nettleser +Name[nl]=NetSurf webbrowser +Name[nn]=NetSurf Nettlesar +Name[no]=NetSurf Nettleser +Name[pl]=Przeglądarka WWW NetSurf +Name[pt]=NetSurf Navegador Web +Name[pt_BR]=Navegador Web NetSurf +Name[sk]=Internetový prehliadač NetSurf +Comment=Browse the World Wide Web +Comment[ca]=Navegueu per el web +Comment[cs]=Prohlížení stránek World Wide Webu +Comment[de]=Im Internet surfen +Comment[es]=Navegue por la web +Comment[fa]=صفحات شبکه جهانی اینترنت را مرور نمایید +Comment[fi]=Selaa Internetin WWW-sivuja +Comment[fr]=Navigue sur Internet +Comment[hu]=A világháló böngészése +Comment[it]=Esplora il web +Comment[ja]=ウェブを閲覧します +Comment[ko]=웹을 돌아 다닙니다 +Comment[nb]=Surf på nettet +Comment[nl]=Verken het internet +Comment[nn]=Surf på nettet +Comment[no]=Surf på nettet +Comment[pl]=Przeglądanie stron WWW +Comment[pt]=Navegue na Internet +Comment[pt_BR]=Navegue na Internet +Comment[sk]=Prehliadanie internetu +GenericName=Web Browser +GenericName[ca]=Navegador web +GenericName[cs]=Webový prohlížeč +GenericName[es]=Navegador web +GenericName[fa]=مرورگر اینترنتی +GenericName[fi]=WWW-selain +GenericName[fr]=Navigateur Web +GenericName[hu]=Webböngésző +GenericName[it]=Browser Web +GenericName[ja]=ウェブ・ブラウザ +GenericName[ko]=웹 브라우저 +GenericName[nb]=Nettleser +GenericName[nl]=Webbrowser +GenericName[nn]=Nettlesar +GenericName[no]=Nettleser +GenericName[pl]=Przeglądarka WWW +GenericName[pt]=Navegador Web +GenericName[pt_BR]=Navegador Web +GenericName[sk]=Internetový prehliadač +Exec=netsurf-gtk %u +Terminal=false +X-MultipleArgs=false +Type=Application +Icon=netsurf.png +Categories=Network; +MimeType=text/html;text/xml;application/xhtml+xml;application/xml;image/gif;image/jpeg;image/png +StartupWMClass=NetSurf-bin +StartupNotify=true diff --git a/frontends/gtk/res/netsurf.gresource.xml b/frontends/gtk/res/netsurf.gresource.xml new file mode 100644 index 000000000..c7626b053 --- /dev/null +++ b/frontends/gtk/res/netsurf.gresource.xml @@ -0,0 +1,70 @@ + + + + cookies.gtk2.ui + history.gtk3.ui + netsurf.gtk2.ui + password.gtk3.ui + toolbar.gtk2.ui + warning.gtk3.ui + cookies.gtk3.ui + hotlist.gtk2.ui + netsurf.gtk3.ui + ssl.gtk2.ui + toolbar.gtk3.ui + downloads.gtk2.ui + hotlist.gtk3.ui + options.gtk2.ui + ssl.gtk3.ui + viewdata.gtk2.ui + downloads.gtk3.ui + login.gtk2.ui + options.gtk3.ui + tabcontents.gtk2.ui + viewdata.gtk3.ui + history.gtk2.ui + login.gtk3.ui + password.gtk2.ui + tabcontents.gtk3.ui + warning.gtk2.ui + favicon.png + netsurf.xpm + menu_cursor.png + throbber/throbber0.png + throbber/throbber1.png + throbber/throbber2.png + throbber/throbber3.png + throbber/throbber4.png + throbber/throbber5.png + throbber/throbber6.png + throbber/throbber7.png + throbber/throbber8.png + credits.html + it/credits.html + nl/credits.html + licence.html + it/licence.html + nl/licence.html + welcome.html + de/welcome.html + it/welcome.html + ja/welcome.html + nl/welcome.html + maps.html + adblock.css + default.css + internal.css + quirks.css + netsurf.png + default.ico + arrow_down_8x32.png + icons/arrow-l.png + icons/content.png + icons/directory2.png + icons/directory.png + icons/hotlist-add.png + icons/hotlist-rmv.png + icons/search.png + languages + + diff --git a/frontends/gtk/res/netsurf.gtk2.ui b/frontends/gtk/res/netsurf.gtk2.ui new file mode 100644 index 000000000..68812b364 --- /dev/null +++ b/frontends/gtk/res/netsurf.gtk2.ui @@ -0,0 +1,212 @@ + + + + + 100 + 0 + 10 + 26 + 10 + 0.5357142857142857 + + + 100 + 0 + 10 + 26 + 10 + 0 + + + + + + + + NetSurf + GTK_WIN_POS_CENTER + + + True + + + True + + + False + False + + + + + True + GTK_TOOLBAR_BOTH_HORIZ + + + False + False + 1 + + + + + GTK_TOOLBAR_BOTH + + + True + gtk-close + + + False + + + + + True + + + True + 4 + Match + + + + + False + False + + + + + True + + + True + True + + + + + False + False + + + + + True + Search _Back + True + gtk-go-back + + + False + + + + + True + Search _Forward + True + gtk-go-forward + + + False + + + + + True + + + True + True + show all matches + All + True + True + + + + + False + False + + + + + True + + + True + True + Match case when searching + Case + GTK_RELIEF_NONE + True + True + + + + + False + False + + + + + False + False + 2 + + + + + True + False + False + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + + + diff --git a/frontends/gtk/res/netsurf.gtk3.ui b/frontends/gtk/res/netsurf.gtk3.ui new file mode 100644 index 000000000..ce47c6370 --- /dev/null +++ b/frontends/gtk/res/netsurf.gtk3.ui @@ -0,0 +1,207 @@ + + + + + False + + + True + False + vertical + + + True + False + + + False + True + 0 + + + + + True + False + both + + + False + False + 1 + + + + + False + both + + + False + True + False + False + gtk-close + gtk-close + + + False + True + + + + + False + True + False + False + + + True + False + 4 + Match + + + + + False + + + + + False + True + False + False + + + True + True + + + + + + False + + + + + False + True + False + False + Search _Back + True + gtk-go-back + + + False + True + + + + + False + True + False + False + Search _Forward + True + gtk-go-forward + + + False + True + + + + + False + True + False + False + + + All + False + True + True + False + False + 0 + True + + + + + False + + + + + False + True + False + False + + + Case + False + True + True + False + False + 0 + True + + + + + False + + + + + False + False + 2 + + + + + True + True + False + False + True + + + + + + + + + + + + + + + + + + + + + True + True + 3 + + + + + + diff --git a/frontends/gtk/res/netsurf.png b/frontends/gtk/res/netsurf.png new file mode 120000 index 000000000..905512c25 --- /dev/null +++ b/frontends/gtk/res/netsurf.png @@ -0,0 +1 @@ +../../../!NetSurf/Resources/netsurf.png,b60 \ No newline at end of file diff --git a/frontends/gtk/res/netsurf.xpm b/frontends/gtk/res/netsurf.xpm new file mode 100644 index 000000000..7061727d6 --- /dev/null +++ b/frontends/gtk/res/netsurf.xpm @@ -0,0 +1,317 @@ +/* XPM */ +static char *netsurf[] = { +/* columns rows colors chars-per-pixel */ +"132 135 176 2", +" c black", +". c #010101", +"X c #010202", +"o c #030406", +"O c #040A0D", +"+ c #060E14", +"@ c #08141A", +"# c #081720", +"$ c #091B27", +"% c #0F2430", +"& c #142E3D", +"* c #143548", +"= c #153C53", +"- c #194662", +"; c #1E516E", +": c #1D577A", +"> c #175B87", +", c #1A6595", +"< c #1E6B9C", +"1 c #26709C", +"2 c #33789E", +"3 c #3188BD", +"4 c #3E89B3", +"5 c #1F92DD", +"6 c #0E9CFD", +"7 c #0E9DFF", +"8 c #0F9EFF", +"9 c #1A95E7", +"0 c #1898ED", +"q c #1299F4", +"w c #119BF9", +"e c #119FFF", +"r c #14A0FF", +"t c #15A1FF", +"y c #17A2FF", +"u c #18A3FF", +"i c #1AA3FE", +"p c #1BA4FE", +"a c #1CA5FE", +"s c #1EA5FE", +"d c #1FA6FE", +"f c #268AC9", +"g c #218ED3", +"h c #20A6FE", +"j c #21A7FE", +"k c #23A8FE", +"l c #24A8FE", +"z c #25A8FE", +"x c #26A9FE", +"c c #27AAFE", +"v c #28AAFE", +"b c #2AABFF", +"n c #2BACFF", +"m c #2CADFF", +"M c #2EAEFF", +"N c #2FAEFF", +"B c #31AFFE", +"V c #31AFFF", +"C c #32B0FF", +"Z c #34B1FF", +"A c #35B2FF", +"S c #36B2FF", +"D c #37B2FF", +"F c #3AB2FB", +"G c #38B3FE", +"H c #39B3FE", +"J c #3AB3FC", +"K c #3BB4FE", +"L c #3CB5FE", +"P c #3DB5FE", +"I c #3EB6FF", +"U c #3FB7FF", +"Y c #4182A4", +"T c #4789AB", +"R c #43B7FB", +"E c #40B7FF", +"W c #44B6FA", +"Q c #42B8FE", +"! c #44B9FE", +"~ c #46BAFF", +"^ c #47BAFF", +"/ c #48BBFF", +"( c #49BCFF", +") c #4ABCFF", +"_ c #4CBDFF", +"` c #4EBEFF", +"' c #50BFFE", +"] c #50BFFF", +"[ c #51BFFF", +"{ c #5ABFF7", +"} c #52C0FE", +"| c #54C1FE", +" . c #56C2FF", +".. c #57C3FF", +"X. c #58C3FE", +"o. c #58C3FF", +"O. c #5CC1F8", +"+. c #5DC4FD", +"@. c #5CC5FE", +"#. c #5DC5FE", +"$. c #5DC6FF", +"%. c #5EC6FF", +"&. c #5FC7FF", +"*. c #61C8FF", +"=. c #62C8FF", +"-. c #63C9FF", +";. c #65CAFF", +":. c #66CBFF", +">. c #67CBFF", +",. c #68CCFF", +"<. c #69CDFF", +"1. c #6BCDFF", +"2. c #6CCEFF", +"3. c #6DCEFF", +"4. c #6FCFFF", +"5. c #70D0FF", +"6. c #72D1FF", +"7. c #73D1FF", +"8. c #74D2FF", +"9. c #76D3FF", +"0. c #77D3FF", +"q. c #78D4FF", +"w. c #79D5FF", +"e. c #7AD5FF", +"r. c #7CD6FF", +"t. c #7ED7FF", +"y. c #7FD8FF", +"u. c #81CBE7", +"i. c #82CBE7", +"p. c #83CEEB", +"a. c #81D4F7", +"s. c #84D3F4", +"d. c #80D7FE", +"f. c #80D8FF", +"g. c #84DAFF", +"h. c #85DAFF", +"j. c #86DBFF", +"k. c #87DBFF", +"l. c #89DCFF", +"z. c #8ADDFF", +"x. c #8BDEFF", +"c. c #8DDEFF", +"v. c #8EDEFF", +"b. c #8FDFFF", +"n. c #91E0FF", +"m. c #92E1FF", +"M. c #93E2FF", +"N. c #95E3FF", +"B. c #97E3FF", +"V. c #98E4FF", +"C. c #99E4FE", +"Z. c #9BE5FE", +"A. c #9CE6FE", +"S. c #9EE6FF", +"D. c #9FE7FF", +"F. c #A1E8FF", +"G. c #A3E9FF", +"H. c #A5EBFF", +"J. c #A6ECFF", +"K. c #A8EDFF", +"L. c #ABEEFF", +"P. c #ADEFFF", +"I. c #B0F0FF", +"U. c #B3F1FF", +"Y. c #B7F2FF", +"T. c #C7F3FF", +"R. c #DCF6FF", +"E. c #EAF8FF", +"W. c #F1FAFF", +"Q. c #F8FCFF", +"!. c #FCFEFF", +"~. c #FDFEFF", +"^. c #FEFEFF", +"/. c #FEFFFF", +"(. c gray100", +"). c None", +/* pixels */ +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O & ; 2 T O.O.a.r.e.e.e.q.9.9.4.{ { T 2 ; % O ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). @ - T r.l.k.k.k.y.t.t.t.t.r.e.e.q.9.8.8.8.8.4.4.1.1.1.{ T ; # ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). = Y p.v.x.x.s.v.k.k.k.a.g.t.y.t.r.e.e.q.9.8.8.8.2.4.4.4.4.,.,.;.;.;.O.4 = o ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % 2 u.m.m.v.v.c.x.x.l.k.k.k.k.g.t.y.t.t.r.e.e.q.9.8.8.8.8.4.4.1.1.,.,.;.;.-.-.-.{ 2 & ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O ; u.B.m.m.m.m.m.v.c.c.x.x.x.k.k.k.g.g.g.t.t.r.e.e.q.9.9.8.8.2.2.8.2.1.1.,.,.;.;.-.*.*.$.$.! : + ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O : p.C.C.B.B.B.m.m.m.m.c.c.c.c.x.k.l.k.k.g.y.g.t.t.r.e.e.q.q.8.9.2.2.4.2.1.1.1.,.;.;.-.-.-.&.$.$.+.O.2 @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).). + 2 s.A.A.C.C.C.B.m.N.m.M.m.m.m.c.l.x.x.l.k.k.g.g.t.f.t.r.r.e.q.9.9.8.8.8.8.8.1.2.,.,.,.;.-.-.&.&.&.$.+... .| 2 @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).). = p.F.F.A.A.A.C.C.C.C.N.N.m.m.m.m.v.v.c.x.l.l.g.k.g.t.f.f.t.r.e.e.q.9.8.8.8.2.8.2.2.1.,.,.,.;.-.&.-.&.+.+....... .` 1 O ).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).). ; A.F.F.A.A.A.A.C.C.C.N.N.N.m.m.m.v.c.x.x.l.k.k.g.g.f.f.t.r.e.e.q.9.q.8.8.8.2.2.2.1.,.,.;.;.-.-.&.&.$.+.$..... .| | W = ).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).). o T F.F.F.A.F.S.A.A.C.C.C.N.M.M.m.m.c.c.c.x.l.k.k.g.g.f.f.f.t.t.e.e.q.9.9.8.5.5.4.1.1.1.;.,.-.-.-.-.$.*.$.+.o. . . .| | ` T # ).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).). * & p.F.F.F.A.F.F.A.A.A.C.C.N.N.M.M.m.m.c.c.x.x.k.k.k.g.h.f.f.t.r.r.e.q.9.9.8.5.5.4.4.4.1.1.;.,.;.-.-.&.$.$.+. . . . .| | ` ` W ; ).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).). 2 A. % J.J.F.J.F.F.S.F.F.A.A.C.C.C.N.M.m.m.c.c.c.x.x.l.k.k.f.t.t.t.t.r.e.q.0.9.9.8.5.4.4.1.1.,.,.;.-.-.&.-.$.$.$.o.+... .| | ` ) ) ) 2 O ).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).). @ u.J.T ; J.J.J.F.J.F.F.F.A.A.D.C.N.C.N.M.M.m.m.m.x.x.x.l.k.k.k.k.f.f.t.r.r.q.0.9.5.5.5.5.4.4.1.,.,.;.;.-.-.&.$.$.+.o. ... .| | ` | [ ) ( 5 $ ).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).). & p.K.K.: T J.J.J.J.F.G.F.F.F.F.A.A.N.C.C.N.M.m.m.c.c.c.x.x.l.k.g.g.t.t.t.r.r.q.0.0.9.8.5.5.4.3.1.,.,.,.;.-.-.&.h.k.+.$.o. . . .| ` ` ` | ( ( W - ).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).). = F.K.P.K.& p.K.J.J.J.J.F.F.F.A.F.D.A.A.C.C.N.M.M.m.m.c.c.x.l.l.k.k.g.g.g.t.r.r.r.e.0.9.9.5.5.4.4.1.1.,.;.,.-.-.,.Q./.8.o.o.o. .[ ` | | ` ) ) ( ( ( ; ).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).). ; J.K.P.P.F. % K.P.K.J.J.J.G.G.G.F.A.D.A.A.C.C.N.N.M.m.m.m.v.x.x.l.k.g.g.t.y.t.t.r.q.0.0.9.7.9.5.4.4.2.1.,.,.;.;.-.F././.T.$.o.o. . .| ) ` ) ) ) ( ( ! ! , ).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).). : K.K.P.P.P.u. ; P.K.P.K.K.J.J.J.G.F.F.D.D.D.C.C.N.N.M.m.m.c.v.c.x.l.k.k.g.k.y.t.r.r.r.0.0.9.9.5.5.4.4.2.1.,.;.,.;.,.W./././.l.o. . . . .| | ` ) ) ( ( ^ ! Q 1 ).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).). Y K.K.P.P.P.I.Y T P.P.P.K.K.J.J.J.F.G.F.D.D.A.A.N.N.N.N.M.m.m.c.c.c.x.l.k.g.g.y.t.t.t.r.w.q.q.8.7.5.5.4.2.1.,.,.;.;.F././././.E.+. . . .| | ) ` ) ) ) ( / ! ! W 2 ).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).). 2 K.P.P.P.P.P.I.P.u.T ; % i.I.I.P.P.P.K.J.J.J.G.F.D.D.D.A.C.C.N.N.m.m.m.c.c.x.l.l.g.k.g.y.t.t.r.r.w.q.8.8.7.5.5.5.2.1.,.,.;.;.E./././././.P. . . .| | | ) ) ` ( 1.K.0.! E Q 1 ).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).). ; J.K.P.P.P.I.I.U.U.U.U.U.U.U.I.I.P.P.P.K.J.J.J.G.G.D.D.D.A.D.C.C.N.N.m.N.c.c.x.l.l.g.k.g.y.g.t.r.r.r.q.q.8.7.5.5.5.2.2.,.,.;.C./././././././.4.o.o.| | ) | $.C.R././.U.! ! U L > ).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).). * F.J.K.K.P.I.I.U.U.U.U.U.U.U.U.U.I.I.P.K.K.J.J.J.G.G.D.D.D.D.C.N.C.N.m.m.m.c.c.x.x.l.k.k.k.y.t.t.r.r.q.q.8.8.9.5.5.5.2.2.,.,.;.E./././././././.T.o.[ | .v.T./.!././.!.k.! E Q L L - ).).).).).).).).).).).).).).", +").).).).).).).).).).).).).). & C.J.K.K.P.P.I.I.U.U.U.Y.Y.Y.U.U.U.I.I.P.P.P.K.J.G.J.F.D.D.D.A.C.C.N.N.M.m.m.m.c.x.l.l.k.k.g.y.y.y.r.r.r.M.R.U.C.9.5.5.2.2.1.,.v.!././././././././.k.d.T././././././././.+.! ! E E L G * ).).).).).).).).).).).).).", +").).).).).).).).).).).).). + p.J.J.K.P.P.P.I.I.U.U.Y.Y.Y.Y.Y.U.U.U.I.P.P.K.K.J.J.J.F.F.D.D.A.D.N.C.N.M.m.m.c.c.x.x.l.z.g.g.y.g.y.r.r.r.L./.!././.R.U.m.2.1.>.R./././././././././././././././././././.W.! ! E E E L L F $ ).).).).).).).).).).).).", +").).).).).).).).).).).).). T G.J.J.K.K.P.P.I.I.U.U.Y.Y.Y.Y.Y.U.U.I.I.P.P.P.K.J.J.J.G.F.D.D.A.D.C.C.N.M.m.M.c.c.x.x.l.k.k.g.y.t.y.t.r.q.r.!./././././././.R.T././././././././././././././././././././.Y.! ! ! E L L L K g o ).).).).).).).).).).).).", +").).).).).).).).).).).). ; J.G.J.J.K.K.P.P.I.I.U.U.U.Y.Y.Y.U.U.U.I.I.P.P.P.K.J.G.J.G.F.D.D.D.C.N.C.N.M.m.M.c.c.x.l.l.k.g.g.y.t.y.r.r.r.q.T.!././././././././.!././././././././././././././././././.Q.v.! ! E E E L L K G , ).).).).).).).).).).).", +").).).).).).).).).).). & A.J.F.J.J.J.J.L.P.P.I.U.U.U.U.U.U.U.U.U.I.I.P.P.P.K.J.J.F.F.F.D.D.A.A.N.C.N.M.m.m.c.c.x.l.l.k.k.h.f.f.f.r.r.r.0.A.^././././././././././././././././././././././././././././.*.! ! E E E L L L G D * ).).).).).).).).).).", +").).).).).).).).).).). O u.A.F.F.J.J.J.K.K.P.P.I.I.U.U.U.U.U.U.U.I.I.P.P.K.K.J.J.J.F.J.D.D.D.A.A.C.N.N.M.m.m.m.c.x.x.l.k.k.f.f.f.r.r.r.0.0.0.Q./././././././././././././././././././././././././././.W.! ! ! E E L L L P G D B @ ).).).).).).).).).).", +").).).).).).).).).). Y F.D.F.J.J.J.J.K.L.P.P.P.I.I.I.U.U.U.U.I.I.I.P.P.K.K.J.J.J.G.F.D.D.D.A.C.C.C.N.m.m.m.c.c.x.l.l.k.k.h.f.f.r.r.r.e.0.9.T.^././././././././././././././././././././././././././.T./ ! ! ! E E L L G G D D 1 ).).).).).).).).).", +").).).).).).).).).). $ N.D.D.F.F.G.J.J.K.K.L.L.P.P.I.I.I.I.I.I.I.I.P.P.P.K.J.J.J.G.G.F.D.D.A.D.C.N.N.M.N.m.m.c.c.x.x.l.k.f.h.f.f.f.r.e.e.e.9.n.^./././././././././././././././././././././././././.Q.K.! ! ! E E L L L G D D B V & ).).).).).).).).).", +").).).).).).).).). T A.F.D.D.F.F.J.J.J.J.K.L.L.T : T i.J.I.I.P.P.P.K.K.K.J.J.G.G.G.F.D.D.A.C.C.N.N.M.m.m.m.v.v.l.x.k.k.g.g.t.f.f.r.e.e.e.9.9.W./././././././././././././././././././././././././.^.^.R.g.W E E P P P G G D D V g ).).).).).).).).", +").).).).).).).).). ; C.A.A.D.D.F.F.F.J.J.J.K.K.L.& K.P.P.P.P.K.K.J.J.J.F.G.F.A.D.D.A.C.C.C.N.M.m.m.c.c.x.x.k.k.k.g.f.f.f.r.r.e.0.0.9.8.Y./././././././././././././././././././././././././././././.E.v.W P P G G G D V V N - ).).).).).).).).", +").).).).).).).). o u.C.C.A.D.D.F.F.F.J.G.J.J.J.K.+ % P.K.K.P.K.K.K.J.J.G.F.F.F.F.D.A.D.C.C.N.M.M.m.m.m.l.x.x.l.k.k.g.f.f.f.r.r.e.9.9.9.8.h./././././././././././././././././././././././././././././././.W.n.^ P G G D D V V b + ).).).).).).).", +").).).).).).).). ; B.C.C.A.A.D.D.D.F.F.G.J.G.K.A. = K.K.K.K.J.J.K.G.J.G.G.F.F.A.A.A.C.C.N.N.M.m.m.c.v.v.x.l.l.k.k.k.f.f.r.r.r.e.0.9.8.8.B.^./././././././././././././././././././././././././././././././.!.Q.F./ G D V V V N : ).).).).).).).", +").).).).).).). p.B.N.C.C.D.A.D.D.F.F.G.G.G.J.r. ; J.J.J.J.J.J.J.F.G.G.F.F.S.F.A.C.C.C.N.N.M.m.N.c.x.x.z.z.z.g.g.g.g.t.t.r.r.q.0.0.h.R./././././././././././././././././././././././././././././././././././.Q./.2.A C C V N 0 O ).).).).).).", +").).).).).).). ; N.N.N.N.C.C.D.D.D.D.F.F.G.G.K.i. 2 J.J.G.J.J.J.G.G.F.F.F.A.A.A.A.C.C.N.N.N.M.m.m.c.c.x.z.z.g.k.g.t.y.y.t.r.r.q.0.Y./././././././././././././././././././././././././././././././././././././.Q.E.&.A C C N N m ; ).).).).).).", +").).).).).). r.m.B.B.N.C.C.A.A.D.D.D.F.F.G.G.Y X Y J.G.J.J.G.G.G.F.F.S.F.S.A.A.C.C.C.N.M.M.m.M.c.c.x.x.z.k.k.g.g.g.t.t.r.r.q.D.W././././././././././././././././././././././././././././././././././././.^./.G.U A A C C V N m 9 o ).).).).).", +").).).).).). = n.m.m.m.N.C.C.C.A.A.D.D.A.F.G.G.Y X T G.G.G.F.G.F.F.F.A.F.S.A.A.A.C.C.N.N.M.m.m.c.c.c.x.z.z.k.g.g.g.t.t.t.r.k.R././././././././././././././././././././././././././././././././././././././.T.o.G K A C C V V N m N - ).).).).).", +").).).).).). X X T n.n.n.n.N.N.C.C.C.A.A.A.F.A.F.A.: u.F.F.F.F.F.A.F.A.A.F.A.A.C.C.N.C.N.M.M.m.m.c.c.x.x.x.k.k.k.g.f.f.t.t.N./.!./././././././././././././././././././././././././././././././././././.Q.W.r.P G G A A D V V V N m b f ).).).).).", +").).).).). X @ s.n.n.C.n.m.B.N.N.C.C.A.A.Z.F.S.F.; u.F.F.F.F.A.F.F.F.A.A.A.C.C.C.C.N.m.M.m.M.c.c.c.x.l.l.k.k.g.g.f.f.t.r.m.W././././././././././././././././././././././././././././././././././.^.^.G./ L P P G A A D V V N m m b b # ).).).).", +").).).).). - n.b.M.n.n.m.m.B.m.C.B.C.C.A.A.A.F.= B.A.F.F.A.A.F.A.A.A.C.C.B.C.B.N.M.M.m.m.c.c.c.x.l.l.k.k.g.g.g.f.f.t.r.e.e.J.E././././././././././././././././././././././././././././././././.^.*.E E L L P G A A V V V N m N N b : ).).).).", +").).).).). e.b.b.s.n.n.m.m.m.B.B.B.C.C.C.C.A.A.* F.F.A.A.A.A.A.A.C.C.B.C.C.m.B.M.M.m.m.c.m.c.x.x.x.l.k.k.g.t.t.f.t.e.e.e.e.9.9.A.R././././././././././././././././././././././././././././././.Q.r.E L L L P G K A A V N N m b b b g ).).).).", +").).).). % x.l.x.c.c.c.M.m.m.M.M.N.N.C.C.C.C.C.C.D.D.A.A.A.A.A.A.C.A.C.C.C.N.C.N.N.M.M.M.m.m.c.m.l.c.x.x.k.k.k.g.g.g.t.t.t.r.e.e.q.9.8.8.8.m.R./././././././././././././././././././././././././././././.T.Q L L L K K D V V C V N m m b c c % ).).).", +").).).). 2 l.x.x.x.c.c.c.m.m.m.M.M.N.N.N.C.N.C.N.C.C.C.C.C.C.C.C.C.C.C.C.C.C.N.N.M.m.m.m.m.m.c.v.v.c.l.k.k.k.g.g.y.y.t.t.r.e.e.q.9.9.8.8.4.2.4.l.T././././././././././././././././././././././././././././.) E L J A A D V C V N N m m b c c > ).).).", +").).).). T k.k.x.x.x.c.c.c.m.m.m.M.M.M.N.N.N.N.C.C.C.C.C.N.C.C.N.C.N.C.N.N.N.M.M.M.m.m.m.c.m.c.c.x.x.l.k.k.k.g.y.g.t.t.r.e.e.e.q.9.8.8.2.8.4.4.1.1.J.!./././././././././././././././././././././././././.!.N.L L L J D A A C V N m m m b c c f ).).).", +").).).). O a.k.k.l.l.x.x.c.c.c.m.m.m.m.M.N.m.N.N.N.N.N.N.B.B.N.C.N.N.N.N.M.M.M.M.m.m.m.m.c.v.v.l.x.l.l.k.k.g.g.g.y.y.t.t.r.e.e.q.8.8.8.8.8.2.4.1.1.,.T./././././././././././././././././././././././././././.R.L L L A D A C C V N m b b b c c k + ).).", +").).). * g.k.g.k.l.x.l.x.c.c.c.c.m.m.m.m.m.m.M.M.M.M.M.N.N.N.m.B.M.M.M.M.M.m.m.m.m.c.c.c.x.x.x.x.l.k.k.k.g.g.g.f.t.t.r.r.e.q.9.9.8.8.8.2.4.4.4.1.,.W././././././././././././././././././././././././././././.;.K K A D A V V V N N b m b c c k * ).).", +").).). 2 k.g.k.k.k.l.l.l.x.x.c.c.m.v.m.m.m.m.m.m.M.M.M.M.M.m.m.m.m.m.m.m.M.m.m.c.m.c.c.c.x.x.x.l.k.k.k.g.k.t.y.t.t.r.r.e.e.q.9.8.8.8.2.8.4.1.1.1.e././././././././././././././././././././././././././././.W.P.K A G D A V V V N m m b c c c k > ).).", +").).). O.t.g.g.k.k.k.l.l.x.x.x.c.c.v.c.c.m.m.m.m.m.M.M.m.m.m.m.m.m.m.m.m.c.m.c.c.c.c.x.x.l.x.k.k.k.k.g.g.t.f.t.t.t.e.e.e.q.9.9.8.8.4.2.4.4.1.1.;.A.!./././././././././././././././././././.Q././././././././.Q.U K A A A V V V m m m b c c k k g ).).", +").).). X a.f.t.t.g.g.k.k.k.l.x.l.x.l.v.v.c.c.c.c.m.c.c.M.m.m.m.m.m.c.m.c.c.c.c.c.c.x.x.x.x.l.k.g.k.g.g.g.t.g.t.t.r.r.e.e.q.q.9.8.8.8.4.4.4.1.4.;.1.T././././././././././././././././././././.C.U.W./.W././././.Q.g.K A A V V V N m b b b c c k k k o ).).", +").).). $ d.f.f.f.f.h.h.k.k.k.k.l.x.z.x.x.x.x.x.c.c.c.c.c.c.c.c.m.c.c.c.c.c.c.x.x.x.x.x.k.l.k.k.k.g.g.g.g.t.y.t.r.e.e.e.q.9.9.8.8.8.8.2.8.1.1.,.,.,.W./././././././././.W.^./././././././.^.T./ ! ! *.C.T./.Q.E.!.T.D A A V V V N b N v v c c k k j $ ).).", +").). = r.r.f.f.f.f.h.f.k.k.k.k.z.k.z.z.z.x.x.x.x.c.c.c.v.v.v.v.c.c.c.x.x.x.l.x.x.k.k.k.k.k.g.g.k.t.t.y.t.t.r.r.e.q.q.q.9.8.8.8.4.2.8.2.4.1.,.,.9./././././././.E.K.1.8.^./././././././.^.9./ / ! ! ! L Q 8.F.R.R.A A A V V N b N v N v c j k j j * ).", +").). : r.r.t.t.f.f.f.f.g.g.k.k.k.k.x.k.l.x.x.l.x.x.x.x.x.x.l.x.x.x.x.l.l.x.l.l.z.k.g.k.k.g.g.g.g.t.t.t.r.r.r.r.q.q.9.q.8.8.8.4.2.2.2.2.1.,.,.,.A./././.!.Q.U.w.$. .o. .P./././././././.R./ / ! ! E E E U K K K J D V C C V N b b b v v c j k j d ; ).", +").). Y e.r.r.t.t.f.f.f.t.g.g.g.k.g.k.k.k.l.l.l.Y ; ; = & s.x.l.l.l.l.l.l.l.k.k.g.k.k.g.g.k.t.t.y.y.t.t.r.r.r.w.q.q.8.8.8.8.8.2.4.8.1.1.,.,.,.;.T././.T.x.&.&.+.+. .o. .+.W././././././.d.! / W ! ! E L U K K A D D C V V V N N b b v c c j k j d , ).", +").). T e.e.e.e.r.r.t.f.t.g.t.k.g.g.g.k.k.k.k.g.; r.l.l.l.h.k.k.k.k.k.k.k.g.g.g.t.y.y.g.t.t.r.r.r.r.q.q.q.8.8.8.8.4.2.8.4.4.1.,.>.1.;.;.9.A.;.*.&.$.$.$.o.o. . .[ k./././././.E.( / ! ! ! ! L E L L K K A A C C V N N m v v v v j c j d d f ).", +").). { q.e.e.e.r.r.t.r.t.t.t.g.t.g.g.g.g.g.k.k.; i.h.h.k.k.k.k.k.g.g.g.g.g.g.y.t.g.y.t.t.r.r.w.w.w.q.q.q.8.8.8.4.4.4.4.4.1.1.,.1.>.;.;.*.*.*.*.$.$.+...o. . . . .[ T././././.v./ / ! ! E E E L K K K A A C C V V N m b N v v c z j j d d g ).", +").). { 9.9.e.e.e.r.r.r.t.t.y.y.y.g.t.t.g.k.g.k.2 T h.h.h.h.f.k.g.g.g.t.t.y.g.t.t.t.t.r.r.r.r.w.q.q.q.8.8.8.8.8.2.4.4.1.1.1.,.,.;.;.;.-.-.*.$.$.+.+.+.o. . . . .[ [ ;./././.Q.) / / ! Q ! L L L U K K A A A C V V m m m v v c c j c j d d 9 ).", +").). 5.8.9.9.q.e.e.e.r.r.r.t.t.y.y.t.g.t.t.t.t.T Y h.h.f.k.f.f.t.t.y.g.y.y.t.t.t.r.r.r.r.w.q.q.q.8.q.8.8.8.2.2.8.4.4.1.1.,.,.1.;.;.-.-.*.*.*.$.+.+.+.o. . . .[ [ [ [ K.!.!.B./ ! ! ! Q E E E L K A K A A V C V N m m b v c c z j j d d d 0 ).", +").). 8.8.8.9.8.q.q.q.e.e.r.r.r.r.t.r.f.f.f.f.f.{ ; f.f.f.f.f.f.f.f.t.f.t.t.r.r.r.e.e.e.e.q.q.9.q.8.8.8.8.2.5.5.4.4.1.1.,.,.,.,.-.-.-.-.&.&.$.$.+....... . .| | ) | ) | E.!.[ / / ! Q Q E L L L K A K A A V V V N m b m v c c k k j d d p p ).", +").). O 8.8.8.8.8.9.9.q.q.e.e.e.e.r.r.r.r.t.t.t.t.u. = f.f.f.t.f.f.t.t.t.t.r.r.r.e.e.e.e.q.q.9.9.8.8.8.8.8.8.8.2.2.1.1.1.,.,.;.;.;.-.-.&.-.&.$.$.$.+. ..... .| | | | ) ) ( ;.-.! ! ! ! Q E L E L L K A A C C V N N m m b b c c c k j d d d i i O ).", +").). @ 2.4.8.8.8.8.9.9.9.q.q.e.e.e.e.e.r.r.r.r.r.r.O % t.t.t.t.r.r.r.r.r.r.r.e.e.e.e.q.q.9.9.9.8.8.8.8.8.2.2.2.5.2.1.1.,.,.;.,.;.-.-.-.&.&.$.$.$.+...+. . .| ` | ` ` | ) ) ( / / ! ! Q Q E E L L L A K A A C V V N m m m b c c k k j j d p p y + ).", +").). @ 4.4.4.8.8.8.8.8.8.9.q.q.q.e.e.e.e.e.e.r.r.r.% + r.r.r.r.r.e.r.e.e.e.e.e.q.q.e.9.9.9.8.8.8.8.8.2.8.2.2.8.2.2.1.,.1.;.,.;.-.-.-.&.&.$.$.+.$. ... . . .| | ` ` ) ` ) ) / ( ! ! ! Q E L L L L G K A C C C V V N m b b c c c k k j d d p p i @ ).", +").). O 4.4.4.2.8.8.8.8.8.8.9.9.9.9.q.q.q.e.e.e.e.e.* r.e.e.e.e.e.e.e.q.q.q.q.9.9.9.9.8.8.9.8.8.8.2.2.8.2.2.2.2.2.,.,.,.;.-.-.-.&.&.&.$.$.+.+..... . . .| | | | ) ) ` ( ( ( ! ! ! W E E E L L P G K A C C V N N m b m b c c k k d d d d p p y O ).", +").). 4.1.4.8.2.2.8.8.8.8.8.8.8.9.8.9.9.q.q.q.q.e.: Y e.q.e.q.q.e.e.q.q.9.9.9.8.8.8.8.8.8.2.4.2.2.8.2.4.2.2.:.:.,.,.,.;.-.-.-.-.&.&.$.+.+....... . . .| [ ) | [ ) ) ( ( / / ! Q Q E L L L P G G A C C C V N m m b b c c c k k k d d p p y y ).", +").). O.1.4.4.4.2.8.2.8.8.8.8.8.8.8.8.9.9.9.9.9.9.T O * : Y O.e.9.9.9.9.9.9.9.9.8.8.8.9.8.8.8.4.2.8.2.8.2.2.4.1.:.2.:.:.;.,.-.-.-.-.&.&.&.$.+.+......... .| | | ) | | ) ` ) ( ( ! ! ! Q E L L L P G G G A A C V V N m b m b c c k k k d d d p p p q ).", +").). { 1.1.1.4.2.8.2.8.2.2.4.8.8.8.8.8.8.8.8.8.9.9.9.9.e.q.q.q.9.9.9.8.8.8.8.8.8.8.8.8.8.8.2.4.4.8.2.2.2.1.1.1.2.:.:.;.-.;.-.-.-.&.&.$.$.$.+...+... ... .| ) [ | ) ) ) ) ( ( ! ! ! ! Q E E P P G G D D C C C V N m m m b c c c k k d d d p p y 9 9 ).", +").). { ,.,.1.1.2.2.2.2.2.8.2.8.2.8.8.2.9.8.8.8.8.8.8.8.9.9.9.6.9.6.6.9.8.8.8.8.2.8.8.2.4.4.8.2.2.2.2.2.2.,.,.,.,.;.;.;.;.;.;.&.&.&.$.$.$.$...........| | | ` | ) ) ) ) ( ^ ^ ! Q Q Q Q L E L L K A A V V V V V N m b b v c c c j j d d d i p w $ % ).", +").). T ,.,.,.1.1.2.2.2.4.4.4.8.2.8.2.8.2.8.8.8.8.2.8.8.7.7.6.9.7.7.6.6.8.8.4.4.8.2.2.8.4.4.2.2.2.2.4.1.,.,.,.,.;.;.;.;.-.;.&.&.&.$.$.+.+...........| | ` | ` ` ` ` ( ( ( ( ^ ! ! Q Q L L L L L K A A D V V V V m m m b v c z j j j d d p p y = ).", +").). Y ,.;.,.,.,.1.1.2.1.1.4.2.4.4.2.8.8.2.2.8.2.2.8.2.7.7.3.3.3.7.3.6.8.2.8.2.4.4.4.2.1.1.4.1.1.1.,.,.,.,.;.;.;.;.&.;.&.&.&.&.&.$.+.+......... .| | | | ` ` ` ) ( ( ( ^ ^ ! ! Q Q Q L L L G G K A V D V V V N N b b b c c j z j j d d p i - ).", +").). : -.,.;.,.,.,.,.,.1.1.1.1.1.1.4.4.2.8.2.2.8.4.4.4.3.3.3.7.3.3.7.3.2.8.2.2.4.1.1.1.1.1.1.,.,.,.,.,.;.,.;.;.;.;.;.&.-.&.&.$.$.$.+. .......| | | | ` ` | ` ( ` ( ( ( ^ Q Q Q Q U U L L L G G A A A V V V N b b b b c c c c j j d d p p : ).", +").). = -.;.;.,.,.,.,.,.,.,.,.,.1.1.4.1.2.2.2.2.2.8.2.2.4.4.4.4.7.3.6.3.2.2.2.2.2.1.1.1.,.,.,.,.,.;.,.-.;.;.-.;.;.&.&.&.&.&.$.$.$.+...+... . .| | | ` ` ` ` ` ` ( ( ^ ^ ! ! Q Q Q L U L P P D D D V V V V N m m b b c c z z j j d d d p - ).", +").).). % -.-.-.;.-.-.,.,.,.,.,.,.,.,.1.1.2.2.2.2.2.2.2.2.2.2.2.1.1.1.3.1.2.1.1.1.1.,.,.,.,.,.,.;.;.,.;.;.-.;.-.&.&.;.&.&.$.$.$.+......... . .| | | ` ` | ` _ _ ( ( ^ ^ ^ ! Q Q Q L U L L P G D D D V V V V N m m m b c c z j c j d d i = ).).", +").).). o $.&.-.-.-.-.;.;.,.-.,.,.,.,.,.,.,.,.2.:.2.,.:.2.1.1.1.1.,.1.1.,.,.,.,.,.,.,.,.,.,.-.,.,.;.-.-.-.;.&.-.&.&.&.$.$.$.+.+......... . .| | ` ` | ) ` _ _ ( ( ( ^ ^ Q Q Q U U U L K K G G D D V V V V N m m b b c c c z j j d d 0 & O o ).).", +").).). g -.&.-.-.-.-.-.;.;.-.,.-.,.;.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.;.,.,.-.,.,.;.;.-.-.-.-.-.-.&.&.&.&.$.$.$.$.$....... . . .| | | ` | ) | ) ` ( ( ^ ^ ^ ! Q Q Q U L L K K K G D D B V V V V m m m b b c c k j j d d d - O , ).).", +").).). 2 $.*.*.*.*.*.;.;.-.-.;.;.;.;.;.,.;.;.,.,.;.,.,.,.,.,.;.;.1.;.;.,.;.;.,.;.;.;.;.;.;.&.;.&.&.&.&.&.&.&.$.$.+.+.$........... .| | | [ [ | ) ) ) ) ) ) / / / ! ! E Q U U U K K K A A A C C V V N N N n N v c c c k c j d d p 0 $ + g , ).).", +").).). * $.$.$.$.*.*.*.-.&.-.-.-.-.;.;.;.;.;.;.;.;.;.;.,.;.T : # 2 ;.;.;.;.;.;.-.-.-.-.&.;.&.;.&.;.&.&.&.&.$.+.+.+.| ........| .| | | | ) ) [ [ ) ( ( ) ( ! / ! ! E ! U U K K K K K A A A C C V N N m v n v v c c j k j d d p p i g O # 5 e = ).).", +").).). + $.$.$.$.$.$.*.&.-.-.&.&.;.-.;.;.;.;.&.;.;.;.;.O.O $ -.-.-.-.-.-.-.-.-.&.&.;.&.&.&.&.&.$.$.$.+.$...+...........| | ` ` ` | | ) [ ) ) ) ( ( / ! ! ! Q ! E U L L L K A K A A V C V V N N b N v v v c j k j j d d p i y y < * 0 y e O ).).", +").).).). T $.$.$.$.$.$.&.&.&.&.&.-.&.&.&.&.&.;.&.;.;.;.-.$ T -.-.-.-.&.&.-.&.-.&.&.&.$.$.$.$.$.+.+........... ...| | | | | ` | ) | ) ) ) ) ( ( / ! ! ! Q Q E L U L L K A K A A A C V V N m b N N v c c c j k j d d d i i y y y y e 8 g ).).).", +").).).). > o.$.$.+.$.$.$.$.$.$.&.&.&.&.&.;.&.&.;.&.&.&.-.2 - -.&.-.&.&.&.&.&.$.$.$.$.$.$.+.+.+.| ............ .| | ) | ` | ` ) [ ) ) ) ( / ( ! ! ! ! Q Q Q L L L L K K K A A C V C V V N b b b v v c c k k j j d p p i y y y y y e e > ).).).", +").).).). & o.o.o.o.$.+.+.$.$.$.$.$.$.&.$.&.$.$.&.&.&.&.$.{ o O.&.&.&.$.$.$.$.$.$.+.$.+.+.............| ....| | | | | | | ) | ) ) ) ) ) / ( ! ! ! Q Q Q E L E L L K A J A A A C C V N N b N b b v c z > 5 j d d p d p y y y y e e e 8 & ).).).", +").).).).). W .o.o. .o...+.$.+.+.+.+.$.$.$.$.$.$.$.&.$.$.$.= 2 &.$.$.$.$.+.$.+.+.$.+.............| ....| | | | ) | | ` ) | ( ) ) ) / ( / ! ! ! Q Q Q L L L L L K K K A A C C C V V N m N b v c c , @ = j d d d i i i y y e y e e 5 ).).).", +").).).).). : . . .o. ...........$.+.+.$.$.+.$.$.$.+.$.$.$.4 % $.+.+.+.+.$................. . .....| | ` | ` ` | ) ) | ) ` ) ( ( / / ! / ! ! Q Q Q Q L E L L L K A A A A C C V V N m m v v c > + g d d i i i y y y e e e 8 > ).).).).", +").).).).). # | . . . . . .....+. ......... .$.+.$.+.$.+.+.+.@ 4 ....+...............| ...... .| | | | ` } } ` ) | ) ) ) ) ) / / / / ! ! ! Q Q Q L Q L K K K A K D V C C C V V N m m m 5 ; + * d p i y i i y y e e 8 8 $ ).).).).", +").).).).).). 4 .| | . . . . .........+. .+............... .: * ..............| ......| | | | | | ` ` } ` ` _ ) ) ) ) ( ( ( / / ! ! ! Q Q Q E L L L L K K A K D D A V C V V N m m 3 = $ 0 p i i y e e e e e 8 g ).).).).).", +").).).).).). - | | | | | | . . . . . .| .......... . ..... .W o 4 .. . . . . ...| | | | | ` ` ` ` ` } ` ` _ _ ) ) ) ) / / ( ! ! ! ! E W E E L L L L L L K A D A A V C N V N m m b % @ , i p i y y i y e e 8 8 8 - ).).).).).", +").).).).).). W | ) [ [ | | | | | | . . . . . . . .| ... . .; O : R . . .| | | | | ` | ` | ` | | ` ` ` _ _ _ _ ) ( ( ( / / ! ! ! ! E ! E L E E L L L G A D D D A C C V N N m m b m , $ < d d p i y y y e i e e 8 7 9 O ).).).).).", +").).).).).).). + 4 ) | | [ [ | | | | | | | | | | | | | | | | | W + : W | .| | | | | | | | ` | | ` ` _ ` ` _ _ _ ( _ ( ( ( ( ! / ! ! ! E ! E E L L L L L L G G G D D V V V V V N m m m b b 0 & < d d d p i y y y y e y e e 8 7 : ).).).).).).", +").).).).).).). % 2 ( [ ) | ) | ) | | ` | | | | [ | | ` | | .) | | | | ) | ) | | ) ` ` ` ` ` ` ` _ _ _ _ _ _ _ ^ ^ ( / ! ! ! ! Q Q E E E E E L L G P G G G D B 1 3 C V V N m m b m b c c * o - g k d d d p p y y y y e e e 8 8 8 q O ).).).).).).", +").).).).).).).). + ; 4 | | ) | ` ` ` ) | ) | [ | ) | ` | | [ | ) ) | | | ) | ) | ` ` ` _ _ _ _ _ ( _ ^ _ ^ ^ ^ ^ ! ! ! ! Q Q Q Q E E L L L L P G G G 3 : % * V V N m m m b b b c c 5 0 k k d d d p p i y y y y e e e 8 8 8 > ).).).).).).).", +").).).).).).).). $ : W ` ` ` | ` ` | | ) ` | ` | ) | ) [ | | ) ) | ) | ) ) _ _ _ _ _ _ _ ( ( ( ^ ^ ^ ^ ^ ! ! ! ! Q Q E E L E L L L P 5 1 ; & o + N N m m m b b c c c k k k k d d d p p i i y i y e e e 8 8 8 w + ).).).).).).).", +").).).).).).).).). o W ) ) [ ) [ [ ) ) | ) [ ) | [ ) | | ) | [ ) ) ) ) [ ) ) ) ( ( ) ( / / ^ ( ^ ^ ^ ! ^ Q Q Q Q Q Q L L U L K U K , f b N v n v c c c k k k k d d d d i i i y y y e e e e e 8 8 : ).).).).).).).", +").).).).).).).).). * ( ) ) [ ) ) ) ) [ ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ( ( ) ) ) ( / / / / / ^ ^ ^ Q ! ! Q Q Q G U L L U L L L K K A f > N v N v c c c k k k k d d d p i i y y y y y e e e 8 7 8 5 o ).).).).).).).).", +").).).).).).).).).). O 1 ( ( ( ( ) ) ) ) ) ) W $ % - ; 1 1 2 f f f W ( ( / / ( / / / ! / 5 f 1 1 > - - % % # : L Q L L K K K K K K V X # - < v b v n v v z z k k d d d d p p i y y y e y e e e 8 7 7 8 * ).).).).).).).).", +").).).).).).).).).). > : % F / / / / / / / ( ) ) g P / / / ( / / ! / ! ! - - L L L L K J K A A A A % X # = < 5 N N b N b v c c c c j k k d d d p p i i y y i y e e e 8 7 7 7 < ).).).).).).).).).", +").).).).).).).).).).). O G ! F ! / ! / ! / / ( ( ( ( 3 W / / ! ! ! ! ! ! ! ! ; % L L L K A A A D V A C < > 3 v V N b b N b b v v c c c z j z k d d d p p i y y y e e e e 8 8 7 7 8 q # ).).).).).).).).).).", +").).).).).).).).).).). & ! ! ! ! ! ! ! ! / ! / ! ! , W ! 2 # 3 ! ! Q Q ! E ; + L G A K K D A D A V C V V V N N b N N b b b v c c c j z j j d d d p p i y y i y e y e e 8 7 7 7 8 - ).).).).).).).).).).", +").).).).).).).).).).).). : ! Q Q Q ! ! ! ! ! ! ! ! 2 % @ g & @ G ! Q E E E ; % % - F G A A A A A C V V V V V N m N N b N v v c c c j j j j j d d d p p p y y y y e e e e 8 7 7 7 7 , ).).).).).).).).).).).", +").).).).).).).).).).).).). 3 E Q Q Q Q ! Q Q Q Q Q ! Q Q Q G f 2 1 1 : - - O = E Q L L E 2 : 1 1 f f 9 L J A G G D D A A A C V V V V V N N m m b b b v v c c z z j c j j d j d p p p y y y y e y e e 8 8 7 7 7 g O ).).).).).).).).).).).", +").).).).).).).).).).).).). + F E L E L Q E E E E ! Q Q Q Q Q Q Q Q Q U Q U 1 1 L L L L L L L K K K K A K K D D A D D V V V V V V N N m m m b m b b b c c c c j j j j d d p p p i y y y e e i e e e 7 7 7 7 w $ ).).).).).).).).).).).).", +").).).).).).).).).).).).).). & F E E E L L E E L E L U L L Q L L L L L L U U * + G L L L L L K K K D K A A A V D A V V V V V V N N b m m m b b b c c c c k k j j j j j p d i p y y y y y y e e e 7 7 7 7 7 7 - ).).).).).).).).).).).).).", +").).).).).).).).).).).).).).). * G L L L E L E L L E L U U L Q L L Q U L L L J + * L L K K A A K D D A A A C D V V V V V V V N b N b b m b b b c c c c k k k d j d d p p p i y y y y y e e e e 8 7 7 8 8 8 > ).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).). - L L L L L L L L L L L L K L L L L L L K U K 1 < A K A K A A V D A A C C V V V V V N N N N b b N b b b b c c c k k k k d d d d d d p i i y y y y e y e e 8 7 7 7 8 8 , ).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).). : K K K K L G L P K K K K K K K L L K K K K K = o g D A A A D A A V V V C V V N N N m m m N b N v v v c c c c k k k d k d d d p p i i y i y y y e y e 8 8 7 7 7 7 7 < ).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).). , G G G G G G G K K K D K A K A K D A D A K N @ $ V V D V V V V A V V V V N N m m m b m v N v v v v z c j k k k k d d d d d p p y i y i y e e y e e 8 7 7 7 8 8 f o ).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).). : G D D D G D A A K A D D A K A K D D A A A 3 > D V V V V V V V V N N N m N m m b m b v v v v c c c j j c j j d d d d p p i y y y i e e e e e 8 8 7 7 7 7 7 < o ).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).). - V D V D D A A A A V D A V D V D V A C A C : + 3 V V V V V V V V N N N b m m b m b b b c v c c z c j j j j j d d d d p i p p y y y y i e y 8 e e 8 8 7 7 7 7 , ).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).). = B D V D A C A A A C A V C C C C C C C C V ; 5 N V N V V N N N b b N N v m b m b v c c c c z c z j j j j d d d d d i p p y y y y i y e e e 8 8 7 7 7 8 8 8 > ).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).). & N V V C V V C C V C V C C C V V V V V V V V N N N N N m N b N b b b N b b b c c c c c z j j j c j j d d d d i p p y p y y y y e e e e e 8 7 7 7 7 7 8 - ).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).). + 3 V V V V V V V V V V V V V N V N N N N N m b N N b m b b b N b v v c c c c c c k k j c j j j j d d d p p i i y y y y y e y e e e 8 8 8 7 7 7 7 9 % ).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).). > N N N N V N N N N N N N N m m m m m m m m b b b m b b b b c c c c k c c k k k k j j j j d d d p p p i i i y y y y e y e e e 8 8 7 7 7 7 7 < + ).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).). * 0 N m b N N b m m m m m m N m m b m b b N v b b b c c c c c c c k k k k k d k d d d d p j p p i y y y y i e i e y e e e 8 7 7 7 7 7 7 : ).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).). O , m b b b N b m m b m b b b b m b b v v v v c c c c c c j k k k k k k d d d d d d d p p p i i y y y e i e e e 8 e 8 8 7 7 7 7 7 f $ ).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).). % g b b b m b m b b b b b b c c c c c c c c c c k g k k k k k d k d d d d p d i p i i i i y y y i e y e e e 8 8 7 7 7 7 7 q - ).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).). X ; c v b b c c c b c c c c c c c c c k k k d = o f j k k d d d d d d p i i i i i y y y y i e e e e e e e 8 8 8 7 7 7 , + ).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).). O : 0 c c c c c c c c c c k k k k k k 0 & + 5 d d d p d d d p i i y y y y y y e y e e e e e 8 8 7 7 8 8 8 , $ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O ; 0 c z j j c k k k k k k k k d @ # 9 d d p p i p i y i y y y y y e y y e e e 8 e 7 7 7 8 7 , $ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). o - f j k k k k d d j k d d d : % 0 i p p y y i y y y y y y e y e e e e 8 8 7 7 7 9 > # ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). $ > 9 k d d k d d d d d d = % w y p y y y y y e e y e y e e e 8 8 8 7 w < * ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). & > g d d d d p d p i & * w y y y y y y e y e e e e 8 8 8 0 < - O ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). + = , 5 i p p i 6 % * w y e e e e e 8 e e 8 q < : % ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % = : < f # = y e y g f < , - & @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). o ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", +").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).)." +}; diff --git a/frontends/gtk/res/nl/credits.html b/frontends/gtk/res/nl/credits.html new file mode 120000 index 000000000..9c983987a --- /dev/null +++ b/frontends/gtk/res/nl/credits.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/nl/credits.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/nl/licence.html b/frontends/gtk/res/nl/licence.html new file mode 120000 index 000000000..8a10d2073 --- /dev/null +++ b/frontends/gtk/res/nl/licence.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/nl/licence.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/nl/welcome.html b/frontends/gtk/res/nl/welcome.html new file mode 120000 index 000000000..6b2ba7742 --- /dev/null +++ b/frontends/gtk/res/nl/welcome.html @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/nl/welcome.html,faf \ No newline at end of file diff --git a/frontends/gtk/res/options.gtk2.ui b/frontends/gtk/res/options.gtk2.ui new file mode 100644 index 000000000..d5542ba5d --- /dev/null +++ b/frontends/gtk/res/options.gtk2.ui @@ -0,0 +1,3004 @@ + + + + + + False + 5 + preferencesTitle + center-on-parent + True + dialog + + + + + + True + False + 2 + + + True + False + edge + + + gtk-help + True + True + False + True + + + False + False + 0 + + + + + gtk-close + True + True + False + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + True + True + + + True + False + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 12 + + + True + False + preferencesStartupPage + + + False + False + 0 + + + + + True + True + + False + False + True + True + + + + + True + True + end + 1 + + + + + True + True + 0 + + + + + True + False + 6 + + + + + + preferencesStartupPageDefault + True + True + True + + + + False + False + end + 1 + + + + + preferencesStartupPageCurrent + True + True + True + + + + False + False + end + 2 + + + + + True + False + 1 + + + + + + + + + True + False + preferencesStartup + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesSearchURLBar + True + True + False + True + + + + + True + True + 0 + + + + + True + False + 12 + + + True + False + preferencesSearchProvider + + + False + False + 0 + + + + + True + False + liststore_search_provider + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesSearch + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesDownloadsRemove + True + True + False + True + + + + + False + True + 0 + + + + + preferencesDownloadsConfirm + True + True + False + True + + + + + False + True + 1 + + + + + True + False + 12 + + + True + False + preferencesDownloadsLocation + + + False + True + 0 + + + + + True + False + select-folder + + + + + True + True + 1 + + + + + False + True + 2 + + + + + + + + + True + False + preferencesDownloads + True + + + + + False + True + 6 + 2 + + + + + + + True + False + preferencesMainTabtitle + + + False + + + + + True + False + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesTabsAlways + True + True + False + True + + + + + True + True + 0 + + + + + preferencesTabsSwitch + True + True + False + True + + + + + True + True + 1 + + + + + preferencesTabsNewly + True + True + False + True + + + + + True + True + 2 + + + + + True + False + 12 + + + True + False + preferencesTabsPosition + + + False + False + 0 + + + + + True + False + liststore_tab_position + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 3 + + + + + + + + + True + False + preferencesTabs + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 6 + + + True + False + preferencesDeveloperView + + + False + True + 0 + + + + + True + False + liststore_developer_view + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 0 + + + + + + + + + True + False + preferencesTools + True + + + + + False + True + 6 + 2 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesURLbarDisplay + True + True + False + True + + + + + True + True + 0 + + + + + + + + + True + False + preferencesURLbar + True + + + + + False + True + 7 + 3 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 6 + + + True + False + preferencesToolbarButtons + + + False + True + 0 + + + + + True + False + liststore_toolbar_buttontype + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 0 + + + + + + + + + True + False + preferencesToolbar + True + + + + + False + True + 6 + 4 + + + + + 1 + + + + + True + False + preferencesAppearanceTabtitle + + + 1 + False + + + + + True + False + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesControlPrevent + True + True + False + True + + + + + False + False + 0 + + + + + preferencesControlHide + True + True + False + True + + + + + False + False + 1 + + + + + preferencesControlEnable + True + True + False + True + + + + + False + False + 2 + + + + + preferencesControlDisable + True + True + False + True + + + + + False + False + 3 + + + + + preferencesControlHigh + True + True + False + True + + + + + True + True + 4 + + + + + True + False + 6 + + + True + False + preferencesControlLoad + + + False + True + 0 + + + + + True + False + liststore_image_loading + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 5 + + + + + + + + + True + False + preferencesControl + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesAnimationEnable + True + True + False + True + + + + + True + True + 0 + + + + + True + False + 12 + + + True + False + preferencesAnimationMinimum + + + False + True + 0 + + + + + True + True + True + preferencesAnimationMinimumTooltip + + False + False + True + True + adjustment_animation_time + 1 + 1 + True + if-valid + + + + + False + True + 1 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesAnimation + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 12 + + + True + False + 6 + + + True + False + preferencesFontsDefault + + + False + True + 0 + + + + + True + False + liststore_defaultfont + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 0 + + + + + True + False + 6 + + + True + False + preferencesFontsSize + + + False + True + 0 + + + + + True + True + True + preferencesFontsSizeTooltip + 4 + + 4 + False + False + True + True + adjustment_font_default_size + 1 + 1 + True + + + + + False + True + 1 + + + + + False + True + 1 + + + + + preferencesFontsPreview + True + True + True + image1 + True + + + + False + True + 2 + + + + + + + + + True + False + preferencesFonts + True + + + + + False + True + 2 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 12 + + + True + False + preferencesLanguagePreferred + + + False + True + 0 + + + + + True + False + True + preferencesLanguagePreferredTooltip + liststore_content_language + + + + + 0 + + + 1 + + + + + 1 + + + 0 + + + + + True + True + 1 + + + + + True + True + 0 + + + + + + + + + True + False + preferencesLanguage + True + + + + + False + True + 3 + + + + + 2 + + + + + True + False + preferencesContentTabtitle + + + 2 + False + + + + + True + False + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesGeneralReferral + True + True + False + True + + + + + True + True + 0 + + + + + preferencesGeneralDNT + True + True + False + True + + + + + True + True + 1 + + + + + + + + + True + False + preferencesGeneral + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesHistoryShow + True + True + False + True + + + + + True + True + 0 + + + + + True + False + 6 + + + True + False + preferencesHistoryRemember + + + False + True + 0 + + + + + True + True + 4 + + 4 + False + False + True + True + adjustment_history_age + 1 + True + if-valid + + + + + False + True + 1 + + + + + True + False + preferencesHistoryDays + + + False + True + 2 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesHistory + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 3 + 3 + 6 + 6 + + + True + False + 0 + preferencesCacheMemory + + + GTK_FILL + + + + + True + False + 0 + preferencesCacheDisc + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesCacheExpire + + + 2 + 3 + GTK_FILL + + + + + True + True + + 5 + False + False + True + True + adjustment_cache_memory_size + 1 + True + + + + + 1 + 2 + GTK_FILL + + + + + True + True + + 5 + False + False + True + True + adjustment_cache_disc_size + 1 + True + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + True + + 3 + False + False + True + True + adjustment_disc_cache_age + 1 + True + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + False + 0 + MB + + + 2 + 3 + GTK_FILL + + + + + True + False + 0 + MB + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesHistoryDays + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + True + True + 0 + + + + + True + False + + + + + + preferencesCacheMaintenance + True + True + True + + + False + True + 1 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesCache + True + + + + + False + True + 6 + 2 + + + + + 3 + + + + + True + False + preferencesPrivacyTabtitle + + + 3 + False + + + + + True + False + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 5 + 2 + 6 + 6 + + + True + False + 0 + preferencesProxyType + + + GTK_FILL + + + + + True + False + 0 + preferencesProxyHost + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesProxyUsername + + + 2 + 3 + GTK_FILL + + + + + True + False + 0 + preferencesProxyPassword + + + 3 + 4 + GTK_FILL + + + + + True + False + True + preferencesProxyTypeTooltip + liststore_proxy_type + + + + + + 0 + + + + + 1 + 2 + + + + + True + False + 6 + + + True + True + True + preferencesProxyHostTooltip + + False + False + True + True + + + + + True + True + 0 + + + + + True + False + : + + + False + True + 1 + + + + + True + True + True + preferencesProxyPortTooltip + 5 + + 5 + False + False + True + True + adjustment_proxy_port + 1 + True + if-valid + + + + + False + True + 2 + + + + + 1 + 2 + 1 + 2 + + + + + True + True + True + preferencesProxyUsernameTooltip + + False + False + True + True + + + + + 1 + 2 + 2 + 3 + + + + + True + True + True + preferencesProxyPasswordTooltip + False + + False + False + True + True + + + + + 1 + 2 + 3 + 4 + + + + + True + False + 0 + preferencesProxyNoproxy + + + 4 + 5 + GTK_FILL + + + + + True + True + True + preferencesProxyNoproxyTooltip + False + False + False + True + True + + + + + 1 + 2 + 4 + 5 + + + + + + + + + True + False + preferencesProxy + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 3 + 2 + 6 + 6 + + + True + False + 0 + preferencesFetchingMax + + + GTK_FILL + + + + + True + False + 0 + preferencesFetchingPerhost + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesFetchingCached + + + 2 + 3 + GTK_FILL + + + + + True + True + True + preferencesFetchingMaxTooltip + + 3 + False + False + True + True + adjustment_fetching_max + 1 + True + + + + + 1 + 2 + GTK_FILL + + + + + True + True + True + preferencesFetchingPerhostTooltip + + 3 + False + False + True + True + adjustment_fetching_perhost + 1 + True + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + True + True + preferencesFetchingCachedTooltip. + + 3 + False + False + True + True + adjustment_fetching_cached + 1 + True + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + + + + True + False + preferencesFetching + True + + + + + False + True + 6 + 1 + + + + + 4 + + + + + True + False + preferencesNetworkTabtitle + + + 4 + False + + + + + True + False + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesAppearanceImages + True + True + False + True + + + + + True + True + 0 + + + + + preferencesAppearanceBackground + True + True + False + True + + + + + True + True + 1 + + + + + preferencesAppearanceScalefit + True + True + False + True + + + + + True + True + 2 + + + + + True + False + 6 + + + True + False + preferencesAppearanceScale + + + False + True + 0 + + + + + True + True + 4 + + 4 + 1 + False + False + True + True + adjustment_pdf_scale + 1 + True + + + + + False + True + 1 + + + + + True + False + % + + + False + True + 2 + + + + + True + True + 3 + + + + + + + + + True + False + preferencesAppearance + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + + + True + False + preferencesMarginsMeasurements + + + False + True + 0 + + + + + + + + True + True + 0 + + + + + True + False + 3 + 3 + + + + + + + + + + + + True + False + 6 + + + True + False + Top + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + False + False + True + True + 1 + 1 + True + + + + + False + True + 1 + + + + + 1 + 2 + GTK_EXPAND + + + + + True + False + 6 + + + True + False + Left + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + False + False + True + True + adjustment_pdf_lmargin + 1 + 1 + True + + + + + False + True + 1 + + + + + 1 + 2 + GTK_EXPAND + + + + + True + False + 6 + + + True + False + Bottom + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + False + False + True + True + 1 + 1 + True + + + + + False + True + 1 + + + + + 1 + 2 + 2 + 3 + GTK_EXPAND + + + + + True + False + 6 + + + True + False + Right + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + False + False + True + True + 1 + 1 + True + + + + + False + True + 1 + + + + + 2 + 3 + 1 + 2 + GTK_EXPAND + + + + + True + True + 1 + + + + + + + + + True + False + preferencesMargins + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 7 + + + preferencesGenerationCompressed + True + True + False + True + + + + + True + True + 0 + + + + + preferencesGenerationPassword + True + True + False + True + + + + + True + True + 1 + + + + + + + + + True + False + preferencesGeneration + True + + + + + False + True + 6 + 2 + + + + + 5 + + + + + True + False + preferencesPDFTabtitle + + + 5 + False + + + + + True + True + 1 + + + + + + help + close + + + + + + + + + + Google + + + Yahoo! + + + Microsoft Live + + + Buisiness.com + + + Omgili + + + BBC News + + + Ubuntu Packages + + + Creative Commons + + + Ask + + + Answers + + + Dictionary.com + + + YouTube + + + AeroMP3 + + + AOL + + + Baidu + + + Amazon + + + Ebay + + + IMBD + + + ESPN + + + Wikipedia + + + DuckDuckGo + + + + + + + + + + + preferencesTabLocTop + + + preferencesTabLocLeft + + + preferencesTabLocRight + + + preferencesTabLocBottom + + + + + + + + + + + preferencesButtonTypeSmall + + + preferencesButtonTypeLarge + + + preferencesButtonTypeLargeText + + + preferencesButtonTypeText + + + + + + + + + + + preferencesImageLoadBoth + + + preferencesImageLoadFore + + + preferencesImageLoadBack + + + preferencesImageLoadNone + + + + + + + + + + + preferencesFonttypeSans + + + preferencesFonttypeSerif + + + preferencesFonttypeMonospace + + + preferencesFonttypeCursive + + + preferencesFonttypeFantasy + + + + + True + False + 3 + gtk-apply + + + + + + + + + preferencesProxyTypeDirect + + + preferencesProxyTypeManual + + + preferencesProxyTypeBasic + + + preferencesProxyTypeNLTM + + + preferencesProxyTypeSystem + + + + + 0.10000000000000001 + 0.10000000000000001 + 10 + 0.10000000000000001 + 1 + + + 16 + 1 + 99.900000000000006 + 0.10000000000000001 + 2 + + + 28 + 999 + 1 + 28 + + + 16 + 2048 + 4 + 16 + + + 1024 + 4096 + 32 + 256 + + + 28 + 999 + 1 + 10 + + + 3128 + 1 + 65535 + 1 + 10 + + + 10 + 1 + 100 + 1 + 10 + + + 1 + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 100 + 1 + 1000 + 1 + 10 + + + 999 + 1 + 10 + + + + + + + + + + + en + English + + + + + + + + + + + preferencesDeveloperViewWindow + + + preferencesDeveloperViewTab + + + preferencesDeveloperViewEditor + + + + diff --git a/frontends/gtk/res/options.gtk3.ui b/frontends/gtk/res/options.gtk3.ui new file mode 100644 index 000000000..2a3516f09 --- /dev/null +++ b/frontends/gtk/res/options.gtk3.ui @@ -0,0 +1,3057 @@ + + + + + 0.10000000000000001 + 10 + 0.10000000000000001 + 0.10000000000000001 + 1 + + + 4096 + 1024 + 4 + 16 + + + 1024 + 16 + 4 + 16 + + + 999 + 28 + 1 + 10 + + + 100 + 1 + 1 + 10 + + + 1 + 100 + 10 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1 + 99.900000000000006 + 16 + 0.10000000000000001 + 2 + + + 999 + 28 + 1 + 28 + + + 999 + 1 + 10 + + + 1 + 1000 + 100 + 1 + 10 + + + 1 + 65535 + 3128 + 1 + 10 + + + False + 5 + preferencesTitle + center-on-parent + True + dialog + + + + + + True + False + vertical + 2 + + + True + False + + + gtk-help + False + True + True + False + False + True + + + False + False + 0 + + + + + gtk-close + False + True + True + False + False + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + True + True + + + True + False + start + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 12 + + + True + False + preferencesStartupPage + + + False + False + 0 + + + + + True + True + preferencesStartupPageTooltip + + + + + + True + True + end + 1 + + + + + True + True + 0 + + + + + True + False + 6 + + + + + + preferencesStartupPageDefault + False + True + True + True + False + + + + False + False + end + 1 + + + + + preferencesStartupPageCurrent + False + True + True + True + False + + + + False + False + end + 2 + + + + + True + False + 1 + + + + + + + + + True + False + preferencesStartup + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesSearchURLBar + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + True + False + 12 + + + True + False + preferencesSearchProvider + + + False + False + 0 + + + + + True + False + preferencesSearchProviderTooltip + start + liststore_search_provider + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesSearch + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesDownloadsRemove + False + True + True + False + start + False + 0.5 + True + + + + + False + True + 0 + + + + + preferencesDownloadsConfirm + False + True + True + False + start + False + 0.5 + True + + + + + False + True + 1 + + + + + True + False + 12 + + + True + False + preferencesDownloadsLocation + + + False + True + 0 + + + + + True + False + preferencesDownloadsLocationTooltip + start + select-folder + + + + + True + True + 1 + + + + + False + True + 2 + + + + + + + + + True + False + preferencesDownloads + True + + + + + False + True + 6 + 2 + + + + + + + True + False + preferencesMainTabtitle + + + False + + + + + True + False + start + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesTabsAlways + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + preferencesTabsSwitch + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 1 + + + + + preferencesTabsNewly + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 2 + + + + + True + False + 12 + + + True + False + preferencesTabsPosition + + + False + False + 0 + + + + + True + False + start + liststore_tab_position + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 3 + + + + + + + + + True + False + preferencesTabs + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 12 + + + True + False + preferencesDeveloperView + + + False + True + 0 + + + + + True + False + start + liststore_developer_view + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesTools + True + + + + + False + True + 6 + 2 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesURLbarDisplay + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + + + + + True + False + preferencesURLbar + True + + + + + False + True + 7 + 3 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 6 + + + True + False + preferencesToolbarButtons + + + False + True + 0 + + + + + True + False + start + liststore_toolbar_buttontype + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 0 + + + + + + + + + True + False + preferencesToolbar + True + + + + + False + True + 6 + 4 + + + + + 1 + + + + + True + False + preferencesAppearanceTabtitle + + + 1 + False + + + + + True + False + start + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesControlPrevent + False + True + True + False + start + False + 0.5 + True + + + + + False + False + 0 + + + + + preferencesControlHide + False + True + True + False + start + False + 0.5 + True + + + + + False + False + 1 + + + + + preferencesControlEnable + False + True + True + False + start + False + 0.5 + True + + + + + False + False + 2 + + + + + preferencesControlDisable + False + True + True + False + start + False + 0.5 + True + + + + + False + False + 3 + + + + + preferencesControlHigh + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 4 + + + + + True + False + 6 + + + True + False + preferencesControlLoad + + + False + True + 0 + + + + + True + False + start + liststore_image_loading + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 5 + + + + + + + + + True + False + preferencesControl + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesAnimationEnable + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + True + False + 12 + + + True + False + preferencesAnimationMinimum + + + False + True + 0 + + + + + True + True + True + preferencesAnimationMinimumTooltip + + adjustment_animation_time + 1 + 1 + True + if-valid + + + + + False + True + 1 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesAnimation + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 12 + + + True + False + 6 + + + True + False + preferencesFontsDefault + + + False + True + 0 + + + + + True + False + liststore_defaultfont + + + + + + 0 + + + + + True + True + 1 + + + + + True + True + 0 + + + + + True + False + 6 + + + True + False + preferencesFontsSize + + + False + True + 0 + + + + + True + True + True + preferencesFontsSizeTooltip + 4 + + 4 + adjustment_font_default_size + 1 + 1 + True + + + + + False + True + 1 + + + + + False + True + 1 + + + + + preferencesFontsPreview + False + True + True + True + False + image1 + True + + + + False + True + 2 + + + + + + + + + True + False + preferencesFonts + True + + + + + False + True + 2 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 12 + + + True + False + preferencesLanguagePreferred + + + False + True + 0 + + + + + True + False + True + preferencesLanguagePreferredTooltip + start + liststore_content_language + + + + + 0 + + + 1 + + + + + 1 + + + 0 + + + + + True + True + 1 + + + + + True + True + 0 + + + + + + + + + True + False + preferencesLanguage + True + + + + + False + True + 3 + + + + + 2 + + + + + True + False + preferencesContentTabtitle + + + 2 + False + + + + + True + False + start + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesGeneralReferral + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + preferencesGeneralDNT + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 1 + + + + + + + + + True + False + preferencesGeneral + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesHistoryShow + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + True + False + 6 + + + True + False + preferencesHistoryRemember + + + False + True + 0 + + + + + True + True + 4 + + 4 + adjustment_history_age + 1 + True + if-valid + + + + + False + True + 1 + + + + + True + False + preferencesHistoryDays + + + False + True + 2 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesHistory + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + 3 + 3 + 6 + 6 + + + True + False + 0 + preferencesCacheMemory + + + GTK_FILL + + + + + True + False + 0 + preferencesCacheDisc + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesCacheExpire + + + 2 + 3 + GTK_FILL + + + + + True + True + + 5 + adjustment_cache_memory_size + 1 + True + + + + + 1 + 2 + GTK_FILL + + + + + True + True + + 5 + adjustment_cache_disc_size + 1 + True + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + True + + 3 + adjustment_disc_cache_age + 1 + True + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + False + 0 + MB + + + 2 + 3 + GTK_FILL + + + + + True + False + 0 + MB + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesCacheDays + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + True + True + 0 + + + + + True + False + + + + + + preferencesCacheMaintenance + False + True + True + True + False + + + False + True + end + 1 + + + + + True + True + 1 + + + + + + + + + True + False + preferencesCache + True + + + + + False + True + 6 + 2 + + + + + 3 + + + + + True + False + preferencesPrivacyTabtitle + + + 3 + False + + + + + True + False + start + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 5 + 2 + 6 + 6 + + + True + False + 0 + preferencesProxyType + + + GTK_FILL + + + + + True + False + 0 + preferencesProxyHost + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesProxyUsername + + + 2 + 3 + GTK_FILL + + + + + True + False + 0 + preferencesProxyPassword + + + 3 + 4 + GTK_FILL + + + + + True + False + True + preferencesProxyTypeTooltip + liststore_proxy_type + + + + + + 0 + + + + + 1 + 2 + + + + + True + False + 6 + + + True + True + True + preferencesProxyHostTooltip + + + + + + True + True + 0 + + + + + True + False + : + + + False + True + 1 + + + + + True + True + True + preferencesProxyPortTooltip + 5 + + 5 + adjustment_proxy_port + 1 + True + if-valid + + + + + False + True + 2 + + + + + 1 + 2 + 1 + 2 + + + + + True + True + True + preferencesProxyUsernameTooltip + + + + + + 1 + 2 + 2 + 3 + + + + + True + True + True + preferencesProxyPasswordTooltip + False + + + + + + 1 + 2 + 3 + 4 + + + + + True + False + 0 + preferencesProxyNoproxy + + + 4 + 5 + GTK_FILL + + + + + True + True + True + preferencesProxyNoproxyTooltip + False + + + + + 1 + 2 + 4 + 5 + + + + + + + + + True + False + preferencesProxy + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 3 + 2 + 6 + 6 + + + True + False + 0 + preferencesFetchingMax + + + GTK_FILL + + + + + True + False + 0 + preferencesFetchingPerhost + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + preferencesFetchingCached + + + 2 + 3 + GTK_FILL + + + + + True + True + True + preferencesFetchingMaxTooltip + + 3 + adjustment_fetching_max + 1 + True + + + + + 1 + 2 + GTK_FILL + + + + + True + True + True + preferencesFetchingPerhostTooltip + + 3 + adjustment_fetching_perhost + 1 + True + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + True + True + preferencesFetchingCachedTooltip + + 3 + adjustment_fetching_cached + 1 + True + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + + + + True + False + preferencesFetching + True + + + + + False + True + 6 + 1 + + + + + 4 + + + + + True + False + preferencesNetworkTabtitle + + + 4 + False + + + + + True + False + start + 6 + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + preferencesAppearanceImages + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + preferencesAppearanceBackground + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 1 + + + + + preferencesAppearanceScalefit + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 2 + + + + + True + False + 6 + + + True + False + preferencesAppearanceScale + + + False + True + 0 + + + + + True + True + 4 + + 4 + 1 + adjustment_pdf_scale + 1 + True + + + + + False + True + 1 + + + + + True + False + % + + + False + True + 2 + + + + + True + True + 3 + + + + + + + + + True + False + preferencesAppearance + True + + + + + False + True + 6 + 0 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 6 + + + True + False + + + True + False + preferencesMarginsMeasurements + + + False + True + 0 + + + + + + + + True + True + 0 + + + + + True + False + 3 + 3 + + + + + + + + + + + + True + False + 6 + + + True + False + Top + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + 1 + 1 + True + + + + + False + True + 1 + + + + + 1 + 2 + GTK_EXPAND + + + + + True + False + 6 + + + True + False + Left + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + adjustment_pdf_lmargin + 1 + 1 + True + + + + + False + True + 1 + + + + + 1 + 2 + GTK_EXPAND + + + + + True + False + 6 + + + True + False + Bottom + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + 1 + 1 + True + + + + + False + True + 1 + + + + + 1 + 2 + 2 + 3 + GTK_EXPAND + + + + + True + False + 6 + + + True + False + Right + + + False + True + 0 + + + + + True + True + 4 + + 5 + 1 + 1 + 1 + True + + + + + False + True + 1 + + + + + 2 + 3 + 1 + 2 + GTK_EXPAND + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + 1 + + + + + + + + + True + False + preferencesMargins + True + + + + + False + True + 6 + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 12 + 12 + + + True + False + 7 + + + preferencesGenerationCompressed + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 0 + + + + + preferencesGenerationPassword + False + True + True + False + start + False + 0.5 + True + + + + + True + True + 1 + + + + + + + + + True + False + preferencesGeneration + True + + + + + False + True + 6 + 2 + + + + + 5 + + + + + True + False + preferencesPDFTabtitle + + + 5 + False + + + + + False + True + 1 + + + + + + help + close + + + + True + False + 3 + gtk-apply + + + + + + + + + + + en + English + + + + + + + + + + + preferencesFonttypeSans + + + preferencesFonttypeSerif + + + preferencesFonttypeMonospace + + + preferencesFonttypeCursive + + + preferencesFonttypeFantasy + + + + + + + + + + + preferencesDeveloperViewWindow + + + preferencesDeveloperViewTab + + + preferencesDeveloperViewEditor + + + + + + + + + + + preferencesImageLoadBoth + + + preferencesImageLoadFore + + + preferencesImageLoadBack + + + preferencesImageLoadNone + + + + + + + + + + + preferencesProxyTypeDirect + + + preferencesProxyTypeManual + + + preferencesProxyTypeBasic + + + preferencesProxyTypeNLTM + + + preferencesProxyTypeSystem + + + + + + + + + + + Google + + + Yahoo! + + + Microsoft Live + + + Buisiness.com + + + Omgili + + + BBC News + + + Ubuntu Packages + + + Creative Commons + + + Ask + + + Answers + + + Dictionary.com + + + YouTube + + + AeroMP3 + + + AOL + + + Baidu + + + Amazon + + + Ebay + + + IMBD + + + ESPN + + + Wikipedia + + + DuckDuckGo + + + + + + + + + + + preferencesTabLocTop + + + preferencesTabLocLeft + + + preferencesTabLocRight + + + preferencesTabLocBottom + + + + + + + + + + + preferencesButtonTypeSmall + + + preferencesButtonTypeLarge + + + preferencesButtonTypeLargeText + + + preferencesButtonTypeText + + + + diff --git a/frontends/gtk/res/password.gtk2.ui b/frontends/gtk/res/password.gtk2.ui new file mode 100644 index 000000000..eb51e4f8f --- /dev/null +++ b/frontends/gtk/res/password.gtk2.ui @@ -0,0 +1,415 @@ + + + + + PDF Password + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER + True + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + True + False + 0 + + + True + 6 + gtk-dialog-authentication + 0.5 + 0.10000000149 + 12 + 0 + + + 0 + False + False + + + + + 5 + True + False + 0 + + + 5 + True + True + 0 + + + True + Owner password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + True + 0 + + + True + Repeat password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + True + 0 + + + True + User password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + True + 0 + + + True + Repeat password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + GTK_BUTTONBOX_END + 10 + + + True + True + GTK_RELIEF_NORMAL + True + + + True + False + 0 + + + True + gtk-dialog-authentication + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + True + Use these as passwords + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 0 + 0 + + + True + False + 0 + + + True + gtk-cancel + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + True + Do not set any passwords + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + diff --git a/frontends/gtk/res/password.gtk3.ui b/frontends/gtk/res/password.gtk3.ui new file mode 100644 index 000000000..eb51e4f8f --- /dev/null +++ b/frontends/gtk/res/password.gtk3.ui @@ -0,0 +1,415 @@ + + + + + PDF Password + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER + True + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + True + False + 0 + + + True + 6 + gtk-dialog-authentication + 0.5 + 0.10000000149 + 12 + 0 + + + 0 + False + False + + + + + 5 + True + False + 0 + + + 5 + True + True + 0 + + + True + Owner password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + True + 0 + + + True + Repeat password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + True + 0 + + + True + User password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + True + 0 + + + True + Repeat password + False + False + GTK_JUSTIFY_LEFT + False + False + 0.899999976158 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 15 + False + 0 + + + 0 + False + True + + + + + True + True + True + False + 20 + + True + + False + 20 + + + 0 + False + False + + + + + 0 + False + False + + + + + 5 + True + GTK_BUTTONBOX_END + 10 + + + True + True + GTK_RELIEF_NORMAL + True + + + True + False + 0 + + + True + gtk-dialog-authentication + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + True + Use these as passwords + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 0 + 0 + + + True + False + 0 + + + True + gtk-cancel + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + True + Do not set any passwords + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + diff --git a/frontends/gtk/res/quirks.css b/frontends/gtk/res/quirks.css new file mode 120000 index 000000000..88aabe48c --- /dev/null +++ b/frontends/gtk/res/quirks.css @@ -0,0 +1 @@ +../../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file diff --git a/frontends/gtk/res/ssl.gtk2.ui b/frontends/gtk/res/ssl.gtk2.ui new file mode 100644 index 000000000..90f449ddd --- /dev/null +++ b/frontends/gtk/res/ssl.gtk2.ui @@ -0,0 +1,202 @@ + + + + + 1 + SSL certificate problem + True + 500 + 250 + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + + + True + + + True + 0 + 6 + gtk-dialog-warning + + + False + False + + + + + True + + + True + NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below. + GTK_JUSTIFY_CENTER + True + + + False + False + + + + + True + 5 + 0 + + + True + 12 + + + True + True + GTK_SHADOW_IN + + + True + GTK_RESIZE_QUEUE + + + True + True + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + + + + + + + True + <b>Certificate chain</b> + True + + + + + 1 + + + + + 1 + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + + + True + 0 + 0 + + + True + 2 + + + True + gtk-cancel + + + False + False + + + + + True + Reject + True + + + False + False + 1 + + + + + + + + + + + True + True + True + + + True + 0 + 0 + + + True + 2 + + + True + gtk-apply + + + False + False + + + + + True + Accept + True + + + False + False + 1 + + + + + + + + + 1 + + + + + False + GTK_PACK_END + + + + + + sslreject + sslaccept + + + diff --git a/frontends/gtk/res/ssl.gtk3.ui b/frontends/gtk/res/ssl.gtk3.ui new file mode 100644 index 000000000..dace2a49e --- /dev/null +++ b/frontends/gtk/res/ssl.gtk3.ui @@ -0,0 +1,181 @@ + + + + + True + False + gtk-apply + + + True + False + gtk-cancel + + + False + 5 + 440 + 260 + True + dialog + + + False + vertical + 2 + + + False + end + + + _Reject + False + True + True + True + False + image3 + True + + + False + True + 0 + + + + + _Accept + False + True + True + True + False + image2 + True + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + False + vertical + + + True + False + + + True + False + 8 + gtk-dialog-warning + 6 + + + False + True + 0 + + + + + True + False + NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below. + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + True + in + + + True + False + + + True + True + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + start + True + True + + + + + + + + + + + True + False + <b>Certificate Chain</b> + True + + + + + False + True + 1 + + + + + False + True + 1 + + + + + + sslreject + sslaccept + + + diff --git a/frontends/gtk/res/tabcontents.gtk2.ui b/frontends/gtk/res/tabcontents.gtk2.ui new file mode 100644 index 000000000..63e290e8b --- /dev/null +++ b/frontends/gtk/res/tabcontents.gtk2.ui @@ -0,0 +1,92 @@ + + + + + + True + 2 + 2 + + + True + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK + layouthadjustment + layoutvadjustment + + + + + 1 + True + 2 + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + True + + + 1 + True + 0 + 4 + Status + + + False + False + + + + + True + layouthadjustment + + + True + True + + + + + 1 + 2 + GTK_FILL + + + + + True + vertical + layoutvadjustment + + + 1 + 2 + + + + + + 100 + 30 + 10 + 10 + + + 100 + 30 + 10 + 10 + + diff --git a/frontends/gtk/res/tabcontents.gtk3.ui b/frontends/gtk/res/tabcontents.gtk3.ui new file mode 100644 index 000000000..23328b3b7 --- /dev/null +++ b/frontends/gtk/res/tabcontents.gtk3.ui @@ -0,0 +1,92 @@ + + + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + True + False + 2 + 2 + + + True + True + False + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK + layouthadjustment + layoutvadjustment + + + 0 + 0 + 1 + 1 + + + + + True + False + True + vertical + layoutvadjustment + + + 1 + 0 + 1 + 1 + + + + + True + True + True + + + True + False + 0 + 4 + Status + True + + + False + True + + + + + True + False + layouthadjustment + + + True + True + + + + + 0 + 1 + 1 + 1 + + + + + + + diff --git a/frontends/gtk/res/throbber/throbber0.png b/frontends/gtk/res/throbber/throbber0.png new file mode 100644 index 000000000..bfcb5d37f Binary files /dev/null and b/frontends/gtk/res/throbber/throbber0.png differ diff --git a/frontends/gtk/res/throbber/throbber1.png b/frontends/gtk/res/throbber/throbber1.png new file mode 100644 index 000000000..a44b70d64 Binary files /dev/null and b/frontends/gtk/res/throbber/throbber1.png differ diff --git a/frontends/gtk/res/throbber/throbber2.png b/frontends/gtk/res/throbber/throbber2.png new file mode 100644 index 000000000..1bbdd8b4d Binary files /dev/null and b/frontends/gtk/res/throbber/throbber2.png differ diff --git a/frontends/gtk/res/throbber/throbber3.png b/frontends/gtk/res/throbber/throbber3.png new file mode 100644 index 000000000..a62488ab8 Binary files /dev/null and b/frontends/gtk/res/throbber/throbber3.png differ diff --git a/frontends/gtk/res/throbber/throbber4.png b/frontends/gtk/res/throbber/throbber4.png new file mode 100644 index 000000000..4e685dcfb Binary files /dev/null and b/frontends/gtk/res/throbber/throbber4.png differ diff --git a/frontends/gtk/res/throbber/throbber5.png b/frontends/gtk/res/throbber/throbber5.png new file mode 100644 index 000000000..72adf67d9 Binary files /dev/null and b/frontends/gtk/res/throbber/throbber5.png differ diff --git a/frontends/gtk/res/throbber/throbber6.png b/frontends/gtk/res/throbber/throbber6.png new file mode 100644 index 000000000..f7dcc2c0c Binary files /dev/null and b/frontends/gtk/res/throbber/throbber6.png differ diff --git a/frontends/gtk/res/throbber/throbber7.png b/frontends/gtk/res/throbber/throbber7.png new file mode 100644 index 000000000..da9d8aee3 Binary files /dev/null and b/frontends/gtk/res/throbber/throbber7.png differ diff --git a/frontends/gtk/res/throbber/throbber8.png b/frontends/gtk/res/throbber/throbber8.png new file mode 100644 index 000000000..8505d1030 Binary files /dev/null and b/frontends/gtk/res/throbber/throbber8.png differ diff --git a/frontends/gtk/res/toolbar.gtk2.ui b/frontends/gtk/res/toolbar.gtk2.ui new file mode 100644 index 000000000..d84db5c8c --- /dev/null +++ b/frontends/gtk/res/toolbar.gtk2.ui @@ -0,0 +1,189 @@ + + + + + 700 + 450 + + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + True + False + 0 + + + True + Move items from store to toolbar Rearrange items in toolbar Move items from toolbar to store + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + True + + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + True + GTK_SHADOW_IN + + + True + False + 0 + + + + + + + + + + 0 + True + True + + + + + True + True + False + 0 + + + True + True + GTK_RELIEF_NORMAL + True + + + True + False + 0 + + + True + gtk-refresh + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + True + + + + + True + Reset to defaults + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + + 10 + False + True + + + + + + + + True + True + True + gtk-apply + True + GTK_RELIEF_NORMAL + True + + + 10 + False + True + + + + + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + 0 + False + True + + + + + 0 + False + True + + + + + + diff --git a/frontends/gtk/res/toolbar.gtk3.ui b/frontends/gtk/res/toolbar.gtk3.ui new file mode 100644 index 000000000..d84db5c8c --- /dev/null +++ b/frontends/gtk/res/toolbar.gtk3.ui @@ -0,0 +1,189 @@ + + + + + 700 + 450 + + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + True + False + 0 + + + True + Move items from store to toolbar Rearrange items in toolbar Move items from toolbar to store + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + True + + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + True + GTK_SHADOW_IN + + + True + False + 0 + + + + + + + + + + 0 + True + True + + + + + True + True + False + 0 + + + True + True + GTK_RELIEF_NORMAL + True + + + True + False + 0 + + + True + gtk-refresh + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + True + + + + + True + Reset to defaults + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + + 10 + False + True + + + + + + + + True + True + True + gtk-apply + True + GTK_RELIEF_NORMAL + True + + + 10 + False + True + + + + + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + 0 + False + True + + + + + 0 + False + True + + + + + + diff --git a/frontends/gtk/res/viewdata.gtk2.ui b/frontends/gtk/res/viewdata.gtk2.ui new file mode 100644 index 000000000..7589022ca --- /dev/null +++ b/frontends/gtk/res/viewdata.gtk2.ui @@ -0,0 +1,204 @@ + + + + + + 640 + 480 + + + True + vertical + + + True + + + True + _File + True + + + True + + + gtk-save-as + True + True + True + + + + + + gtk-print + True + True + True + + + + + + True + + + + + gtk-close + True + True + True + + + + + + + + + True + _Edit + True + + + True + + + gtk-select-all + True + True + True + + + + + + gtk-cut + True + True + True + + + + + gtk-copy + True + True + True + + + + + gtk-paste + True + True + True + + + + + gtk-delete + True + True + True + + + + + + + + + + True + _View + True + + + True + + + gtk-zoom-in + True + True + True + + + + + + gtk-zoom-out + True + True + True + + + + + + gtk-zoom-100 + True + True + True + + + + + + + + + + True + _Help + True + + + True + + + gtk-about + True + True + True + + + + + + + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + 1 + 1 + False + word + 3 + 3 + False + + + + + 1 + + + + + + diff --git a/frontends/gtk/res/viewdata.gtk3.ui b/frontends/gtk/res/viewdata.gtk3.ui new file mode 100644 index 000000000..b742d5f6b --- /dev/null +++ b/frontends/gtk/res/viewdata.gtk3.ui @@ -0,0 +1,239 @@ + + + + + False + 640 + 480 + + + True + False + vertical + + + True + False + + + False + True + False + _File + True + + + True + False + + + gtk-save-as + False + True + False + True + True + + + + + gtk-print + False + True + False + True + True + + + + + False + True + False + + + + + gtk-close + False + True + False + True + True + + + + + + + + + False + True + False + _Edit + True + + + True + False + + + gtk-select-all + False + True + False + True + True + + + + + gtk-cut + False + True + False + True + True + + + + + gtk-copy + False + True + False + True + True + + + + + gtk-paste + False + True + False + True + True + + + + + gtk-delete + False + True + False + True + True + + + + + + + + + False + True + False + _View + True + + + True + False + + + gtk-zoom-in + False + True + False + True + True + + + + + gtk-zoom-out + False + True + False + True + True + + + + + gtk-zoom-100 + False + True + False + True + True + + + + + + + + + False + True + False + _Help + True + + + True + False + + + gtk-about + False + True + False + True + True + + + + + + + + + False + True + 0 + + + + + True + True + bottom-right + + + True + True + 1 + 1 + False + word + 3 + 3 + False + + + + + True + True + 1 + + + + + + diff --git a/frontends/gtk/res/warning.gtk2.ui b/frontends/gtk/res/warning.gtk2.ui new file mode 100644 index 000000000..e4fb4e662 --- /dev/null +++ b/frontends/gtk/res/warning.gtk2.ui @@ -0,0 +1,77 @@ + + + + + Warning from NetSurf + GTK_WIN_POS_CENTER + gtk-dialog-warning + GDK_WINDOW_TYPE_HINT_DIALOG + True + + + True + 2 + + + True + 3 + + + True + 12 + 6 + gtk-dialog-warning + + + False + False + + + + + True + Help help help! I'm being held prisoner by a bunch of RISC OS zealots! + True + + + 1 + 1 + + + + + + + True + + + False + 3 + 1 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-ok + True + + + + + + False + False + 2 + + + + + + diff --git a/frontends/gtk/res/warning.gtk3.ui b/frontends/gtk/res/warning.gtk3.ui new file mode 100644 index 000000000..e4fb4e662 --- /dev/null +++ b/frontends/gtk/res/warning.gtk3.ui @@ -0,0 +1,77 @@ + + + + + Warning from NetSurf + GTK_WIN_POS_CENTER + gtk-dialog-warning + GDK_WINDOW_TYPE_HINT_DIALOG + True + + + True + 2 + + + True + 3 + + + True + 12 + 6 + gtk-dialog-warning + + + False + False + + + + + True + Help help help! I'm being held prisoner by a bunch of RISC OS zealots! + True + + + 1 + 1 + + + + + + + True + + + False + 3 + 1 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-ok + True + + + + + + False + False + 2 + + + + + + diff --git a/frontends/gtk/res/welcome.html b/frontends/gtk/res/welcome.html new file mode 120000 index 000000000..1abdc5e8a --- /dev/null +++ b/frontends/gtk/res/welcome.html @@ -0,0 +1 @@ +en/welcome.html \ No newline at end of file diff --git a/frontends/gtk/resources.c b/frontends/gtk/resources.c new file mode 100644 index 000000000..dfe3d3dad --- /dev/null +++ b/frontends/gtk/resources.c @@ -0,0 +1,600 @@ +/* + * Copyright 2015 Vincent Sanders + * + * 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 . + */ + +/** + * \file + * Implementation of gtk builtin resource handling. + * + * This presents a unified interface to the rest of the codebase to + * obtain resources. Note this is not anything to do with the resource + * scheme handling beyond possibly providing the underlying data. + * + */ + +#include +#include +#include + +#include "utils/log.h" +#include "utils/filepath.h" + +#include "gtk/compat.h" +#include "gtk/resources.h" + +/** log contents of gresource /org/netsource */ +#ifdef WITH_GRESOURCE +#define SHOW_GRESOURCE +#undef SHOW_GRESOURCE +#endif + +#ifdef WITH_BUILTIN_PIXBUF +#ifdef __GNUC__ +extern const guint8 menu_cursor_pixdata[] __attribute__ ((__aligned__ (4))); +extern const guint8 favicon_pixdata[] __attribute__ ((__aligned__ (4))); +extern const guint8 netsurf_pixdata[] __attribute__ ((__aligned__ (4))); +#else +extern const guint8 menu_cursor_pixdata[]; +extern const guint8 favicon_pixdata[]; +extern const guint8 netsurf_pixdata[]; +#endif +#endif + +/** type of resource entry */ +enum nsgtk_resource_type_e { + NSGTK_RESOURCE_FILE, /**< entry is a file on disc */ + NSGTK_RESOURCE_GLIB, /**< entry is a gresource accessed by path */ + NSGTK_RESOURCE_DIRECT, /**< entry is a gresource accesed by gbytes */ + NSGTK_RESOURCE_INLINE, /**< entry is compiled in accessed by pointer */ +}; + +/** resource entry */ +struct nsgtk_resource_s { + const char *name; + unsigned int len; + enum nsgtk_resource_type_e type; + char *path; +}; + +#define RES_ENTRY(name) { name, sizeof((name)) - 1, NSGTK_RESOURCE_FILE, NULL } + +/** resources that are used for gtk builder */ +static struct nsgtk_resource_s ui_resource[] = { + RES_ENTRY("netsurf"), + RES_ENTRY("tabcontents"), + RES_ENTRY("password"), + RES_ENTRY("login"), + RES_ENTRY("ssl"), + RES_ENTRY("toolbar"), + RES_ENTRY("downloads"), + RES_ENTRY("history"), + RES_ENTRY("options"), + RES_ENTRY("hotlist"), + RES_ENTRY("cookies"), + RES_ENTRY("viewdata"), + RES_ENTRY("warning"), + { NULL, 0, NSGTK_RESOURCE_FILE, NULL }, +}; + +/** resources that are used as pixbufs */ +static struct nsgtk_resource_s pixbuf_resource[] = { + RES_ENTRY("favicon.png"), + RES_ENTRY("netsurf.xpm"), + RES_ENTRY("menu_cursor.png"), + RES_ENTRY("arrow_down_8x32.png"), + RES_ENTRY("throbber/throbber0.png"), + RES_ENTRY("throbber/throbber1.png"), + RES_ENTRY("throbber/throbber2.png"), + RES_ENTRY("throbber/throbber3.png"), + RES_ENTRY("throbber/throbber4.png"), + RES_ENTRY("throbber/throbber5.png"), + RES_ENTRY("throbber/throbber6.png"), + RES_ENTRY("throbber/throbber7.png"), + RES_ENTRY("throbber/throbber8.png"), + { NULL, 0, NSGTK_RESOURCE_FILE, NULL }, +}; + +/** resources that are used for direct data access */ +static struct nsgtk_resource_s direct_resource[] = { + RES_ENTRY("welcome.html"), + RES_ENTRY("credits.html"), + RES_ENTRY("licence.html"), + RES_ENTRY("maps.html"), + RES_ENTRY("default.css"), + RES_ENTRY("adblock.css"), + RES_ENTRY("internal.css"), + RES_ENTRY("quirks.css"), + RES_ENTRY("netsurf.png"), + RES_ENTRY("default.ico"), + RES_ENTRY("icons/arrow-l.png"), + RES_ENTRY("icons/content.png"), + RES_ENTRY("icons/directory2.png"), + RES_ENTRY("icons/directory.png"), + RES_ENTRY("icons/hotlist-add.png"), + RES_ENTRY("icons/hotlist-rmv.png"), + RES_ENTRY("icons/search.png"), + RES_ENTRY("languages"), + RES_ENTRY("Messages"), + { NULL, 0, NSGTK_RESOURCE_FILE, NULL }, +}; + + +/* exported interface documented in gtk/resources.h */ +GdkCursor *nsgtk_create_menu_cursor(void) +{ + GdkCursor *cursor = NULL; + GdkPixbuf *pixbuf; + nserror res; + + res = nsgdk_pixbuf_new_from_resname("menu_cursor.png", &pixbuf); + if (res == NSERROR_OK) { + cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), + pixbuf, 0, 3); + g_object_unref(pixbuf); + } + + return cursor; +} + + +/** + * locate a resource + * + * The way GTK accesses resource files has changed greatly between + * releases. This initilises the interface that hides all the + * implementation details from the rest of the code. + * + * If the GResource is not enabled or the item cannot be found in the + * compiled in resources the files will be loaded directly from disc + * instead. + * + * \param respath A string vector containing the valid resource search paths + * \param resource A resource entry to initialise + */ +static nserror +init_resource(char **respath, struct nsgtk_resource_s *resource) +{ + char *resname; +#ifdef WITH_GRESOURCE + int resnamelen; + gboolean present; + const gchar * const *langv; + int langc = 0; + + langv = g_get_language_names(); + + while (langv[langc] != NULL) { + resnamelen = snprintf(NULL, 0, + "/org/netsurf/%s/%s", + langv[langc], resource->name); + + resname = malloc(resnamelen + 1); + if (resname == NULL) { + return NSERROR_NOMEM; + } + snprintf(resname, resnamelen + 1, + "/org/netsurf/%s/%s", + langv[langc], resource->name); + + present = g_resources_get_info(resname, + G_RESOURCE_LOOKUP_FLAGS_NONE, + NULL, NULL, NULL); + if (present == TRUE) { + /* found an entry in the resources */ + resource->path = resname; + resource->type = NSGTK_RESOURCE_GLIB; + LOG("Found gresource path %s", resource->path); + return NSERROR_OK; + } + /*LOG("gresource \"%s\" not found", resname);*/ + free(resname); + + langc++; + } + resnamelen = snprintf(NULL, 0, "/org/netsurf/%s", resource->name); + + resname = malloc(resnamelen + 1); + if (resname == NULL) { + return NSERROR_NOMEM; + } + snprintf(resname, resnamelen + 1, "/org/netsurf/%s", resource->name); + + present = g_resources_get_info(resname, + G_RESOURCE_LOOKUP_FLAGS_NONE, + NULL, NULL, NULL); + if (present == TRUE) { + /* found an entry in the resources */ + resource->path = resname; + resource->type = NSGTK_RESOURCE_GLIB; + LOG("Found gresource path %s", resource->path); + return NSERROR_OK; + } + /*LOG("gresource \"%s\" not found", resname);*/ + free(resname); + +#endif + + resname = filepath_find(respath, resource->name); + if (resname == NULL) { + LOG("Unable to find resource %s on resource path", + resource->name); + return NSERROR_NOT_FOUND; + } + + /* found an entry on the path */ + resource->path = resname; + resource->type = NSGTK_RESOURCE_FILE; + + LOG("Found file resource path %s", resource->path); + return NSERROR_OK; +} + +/** + * locate and setup a direct resource + * + * Direct resources have general type of NSGTK_RESOURCE_GLIB but have + * g_resources_lookup_data() applied and the result stored so the data + * can be directly accessed without additional processing. + * + * \param respath A string vector containing the valid resource search paths + * \param resource A resource entry to initialise + */ +static nserror +init_direct_resource(char **respath, struct nsgtk_resource_s *resource) +{ + nserror res; + + res = init_resource(respath, resource); + +#ifdef WITH_GRESOURCE + if ((res == NSERROR_OK) && + (resource->type == NSGTK_RESOURCE_GLIB)) { + /* found gresource we can convert */ + GBytes *data; + + data = g_resources_lookup_data(resource->path, + G_RESOURCE_LOOKUP_FLAGS_NONE, + NULL); + if (data != NULL) { + resource->type = NSGTK_RESOURCE_DIRECT; + resource->path = (char *)data; + } + } +#endif + + return res; +} + +/** + * locate a pixbuf resource + * + * Pixbuf resources can be compiled inline + * + * \param respath A string vector containing the valid resource search paths + * \param resource A resource entry to initialise + */ +static nserror +init_pixbuf_resource(char **respath, struct nsgtk_resource_s *resource) +{ +#ifdef WITH_BUILTIN_PIXBUF + if (strncmp(resource->name, "menu_cursor.png", resource->len) == 0) { + resource->path = (char *)&menu_cursor_pixdata[0]; + resource->type = NSGTK_RESOURCE_INLINE; + LOG("Found builtin for %s", resource->name); + return NSERROR_OK; + } + + if (strncmp(resource->name, "netsurf.xpm", resource->len) == 0) { + resource->path = (char *)&netsurf_pixdata[0]; + resource->type = NSGTK_RESOURCE_INLINE; + LOG("Found builtin for %s", resource->name); + return NSERROR_OK; + } + + if (strncmp(resource->name, "favicon.png", resource->len) == 0) { + resource->path = (char *)&favicon_pixdata[0]; + resource->type = NSGTK_RESOURCE_INLINE; + LOG("Found builtin for %s", resource->name); + return NSERROR_OK; + } +#endif + return init_resource(respath, resource); +} + +/** + * locate a ui resource + * + * UI resources need their resource name changing to account for gtk versions + * + * \param respath A string vector containing the valid resource search paths + * \param ui_res A resource entry to initialise + */ +static nserror init_ui_resource(char **respath, struct nsgtk_resource_s *ui_res) +{ +#if GTK_CHECK_VERSION(3,0,0) + int gtkv = 3; +#else + int gtkv = 2; +#endif + int resnamelen; + char *resname; + struct nsgtk_resource_s resource; + nserror res; + + resnamelen = ui_res->len + 10; /* allow for the expanded ui name */ + + resname = malloc(resnamelen); + if (resname == NULL) { + return NSERROR_NOMEM; + } + snprintf(resname, resnamelen, "%s.gtk%d.ui", ui_res->name, gtkv); + resource.name = resname; + resource.len = ui_res->len; + resource.path = NULL; + + res = init_resource(respath, &resource); + + ui_res->path = resource.path; + ui_res->type = resource.type; + + free(resname); + + return res; +} + +/** + * Find a resource entry by name. + * + * \param resname The resource name to match. + * \param resource The list of resources entries to search. + */ +static struct nsgtk_resource_s * +find_resource_from_name(const char *resname, struct nsgtk_resource_s *resource) +{ + /* find resource from name */ + while ((resource->name != NULL) && + ((resname[0] != resource->name[0]) || + (strncmp(resource->name, resname, resource->len) != 0))) { + resource++; + } + return resource; +} + +#ifdef SHOW_GRESOURCE +/** + * Debug dump of all resources compile din via GResource. + */ +static void list_gresource(void) +{ + const char *nspath = "/org/netsurf"; + char **reslist; + char **cur; + GError* gerror = NULL; + reslist = g_resources_enumerate_children(nspath, + G_RESOURCE_LOOKUP_FLAGS_NONE, + &gerror); + if (gerror) { + LOG("gerror %s", gerror->message); + g_error_free(gerror); + + } else { + cur = reslist; + while (cur != NULL && *cur != NULL) { + LOG("gres %s", *cur); + cur++; + } + g_strfreev(reslist); + } +} +#endif + +/** + * Initialise UI resource table + * + */ +/* exported interface documented in gtk/resources.h */ +nserror nsgtk_init_resources(char **respath) +{ + struct nsgtk_resource_s *resource; + nserror res; + +#ifdef SHOW_GRESOURCE + list_gresource(); +#endif + + /* iterate the ui resource table and initialise all its members */ + resource = &ui_resource[0]; + while (resource->name != NULL) { + res = init_ui_resource(respath, resource); + if (res != NSERROR_OK) { + return res; + } + resource++; + } + + /* iterate the pixbuf resource table and initialise all its members */ + resource = &pixbuf_resource[0]; + while (resource->name != NULL) { + res = init_pixbuf_resource(respath, resource); + if (res != NSERROR_OK) { + return res; + } + resource++; + } + + /* iterate the direct resource table and initialise all its members */ + resource = &direct_resource[0]; + while (resource->name != NULL) { + res = init_direct_resource(respath, resource); + if (res != NSERROR_OK) { + return res; + } + resource++; + } + + return NSERROR_OK; +} + + +/* exported interface documented in gtk/resources.h */ +nserror +nsgdk_pixbuf_new_from_resname(const char *resname, GdkPixbuf **pixbuf_out) +{ + struct nsgtk_resource_s *resource; + GdkPixbuf *new_pixbuf = NULL; + GError* error = NULL; + + resource = find_resource_from_name(resname, &pixbuf_resource[0]); + if (resource->name == NULL) { + return NSERROR_NOT_FOUND; + } + + switch (resource->type) { + case NSGTK_RESOURCE_FILE: + new_pixbuf = gdk_pixbuf_new_from_file(resource->path, &error); + break; + + case NSGTK_RESOURCE_GLIB: +#ifdef WITH_GRESOURCE + new_pixbuf = gdk_pixbuf_new_from_resource(resource->path, &error); +#endif + break; + + case NSGTK_RESOURCE_INLINE: +#ifdef WITH_BUILTIN_PIXBUF + new_pixbuf = gdk_pixbuf_new_from_inline(-1, (const guint8 *)resource->path, FALSE, &error); +#endif + break; + + case NSGTK_RESOURCE_DIRECT: + /* pixbuf resources are not currently direct */ + break; + } + + if (new_pixbuf == NULL) { + if (error != NULL) { + LOG("Unable to create pixbuf from file for %s with path %s \"%s\"", + resource->name, resource->path, error->message); + g_error_free(error); + } else { + LOG("Unable to create pixbuf from file for %s with path %s", + resource->name, resource->path); + } + return NSERROR_INIT_FAILED; + } + *pixbuf_out = new_pixbuf; + + return NSERROR_OK; +} + +/* exported interface documented in gtk/resources.h */ +nserror +nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out) +{ + GtkBuilder *new_builder; + struct nsgtk_resource_s *ui_res; + GError* error = NULL; + + ui_res = find_resource_from_name(resname, &ui_resource[0]); + if (ui_res->name == NULL) { + return NSERROR_NOT_FOUND; + } + + new_builder = gtk_builder_new(); + + if (ui_res->type == NSGTK_RESOURCE_FILE) { + if (!gtk_builder_add_from_file(new_builder, + ui_res->path, + &error)) { + LOG("Unable to add UI builder from file for %s with path %s \"%s\"", + ui_res->name, ui_res->path, error->message); + g_error_free(error); + g_object_unref(G_OBJECT(new_builder)); + return NSERROR_INIT_FAILED; + } + } else { + if (!nsgtk_builder_add_from_resource(new_builder, + ui_res->path, + &error)) { + LOG("Unable to add UI builder from resource for %s with path %s \"%s\"", + ui_res->name, ui_res->path, error->message); + g_error_free(error); + g_object_unref(G_OBJECT(new_builder)); + return NSERROR_INIT_FAILED; + } + } + + *builder_out = new_builder; + + return NSERROR_OK; +} + +/* exported interface documented in gtk/resources.h */ +nserror +nsgtk_data_from_resname(const char *resname, + const uint8_t ** data_out, + size_t *data_size_out) +{ +#ifdef WITH_GRESOURCE + struct nsgtk_resource_s *resource; + GBytes *data; + const gchar *buffer; + gsize buffer_length; + + resource = find_resource_from_name(resname, &direct_resource[0]); + if ((resource->name == NULL) || + (resource->type != NSGTK_RESOURCE_DIRECT)) { + return NSERROR_NOT_FOUND; + } + + data = (GBytes *)resource->path; + + buffer_length = 0; + buffer = g_bytes_get_data(data, &buffer_length); + + if (buffer == NULL) { + return NSERROR_NOMEM; + } + + *data_out = (const uint8_t *)buffer; + *data_size_out = (size_t)buffer_length; + + return NSERROR_OK; +#else + /** \todo consider adding compiled inline resources for things + * other than pixbufs. + */ + return NSERROR_NOT_FOUND; +#endif +} + +/* exported interface documented in gtk/resources.h */ +nserror +nsgtk_path_from_resname(const char *resname, const char **path_out) +{ + struct nsgtk_resource_s *resource; + + resource = find_resource_from_name(resname, &direct_resource[0]); + if ((resource->name == NULL) || + (resource->type != NSGTK_RESOURCE_FILE)) { + return NSERROR_NOT_FOUND; + } + + *path_out = (const char *)resource->path; + + return NSERROR_OK; +} diff --git a/frontends/gtk/resources.h b/frontends/gtk/resources.h new file mode 100644 index 000000000..923031af4 --- /dev/null +++ b/frontends/gtk/resources.h @@ -0,0 +1,109 @@ +/* + * Copyright 2006 Daniel Silverstone + * + * 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 . + */ + +/** + * \file + * Interface to gtk builtin resource handling. + * + * This presents a unified interface to the rest of the codebase to + * obtain resources. Note this is not anything to do with the resource + * scheme handling beyond possibly providing the underlying data. + * + */ + +#ifndef NETSURF_GTK_RESOURCES_H +#define NETSURF_GTK_RESOURCES_H 1 + +/** + * Initialise GTK resources handling. + * + * Must be called before attempting to retrieve any resources but + * after logging is initialised as it logs. + * + * \param respath A string vector of paths to search for resources. + * \return NSERROR_OK if all resources were located else an + * appropriate error code. + */ +nserror nsgtk_init_resources(char **respath); + +/** + * Creates a menu cursor from internal resources. + * + * \return Cursor object or NULL on error. + */ +GdkCursor *nsgtk_create_menu_cursor(void); + +/** + * Create gtk builder object for the named ui resource. + * + * Creating gtk builder objects from a named resource requires the + * source xml resource to be parsed. + * + * This creates a gtk builder instance using an identifier name which + * is mapped to the ui_resource table which must be initialised with + * nsgtk_init_resources() + * + * \param resname The resource name to construct for + * \param builder_out The builder result + * \return NSERROR_OK and builder_out updated or appropriate error code + */ +nserror nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out); + + +/** + * Create gdk pixbuf for the named ui resource. + * + * This creates a pixbuf using an identifier name which is mapped to + * the ui_resource table which must be initialised with + * nsgtk_init_resources() + * + * \param resname The resource name to construct for + * \param pixbuf_out The pixbuf result + * \return NSERROR_OK and pixbuf_out updated or appropriate error code + */ +nserror nsgdk_pixbuf_new_from_resname(const char *resname, GdkPixbuf **pixbuf_out); + +/** + * Get direct pointer to resource data. + * + * For a named resource this obtains a direct acesss pointer to the + * data and its length. + * + * The data is read only through this pointer and remains valid until + * program exit. + * + * \param resname The resource name to obtain data for. + * \param data_out The resulting data. + * \param data_size_out The resulting data size. + * \return NSERROR_OK and data_out updated or appropriate error code. + */ +nserror nsgtk_data_from_resname(const char *resname, const uint8_t **data_out, size_t *data_size_out); + +/** + * Get path to resource data. + * + * For a named resource this obtains the on-disc path to that resource. + * + * The path is read only and remains valid untill program exit. + * \param resname The resource name to obtain path for. + * \param path_out The resulting data. + * \return NSERROR_OK and path_out updated or appropriate error code. + */ +nserror nsgtk_path_from_resname(const char *resname, const char **path_out); + +#endif diff --git a/frontends/gtk/scaffolding.c b/frontends/gtk/scaffolding.c new file mode 100644 index 000000000..777256703 --- /dev/null +++ b/frontends/gtk/scaffolding.c @@ -0,0 +1,2811 @@ +/* + * Copyright 2006 Rob Kendrick + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/utils.h" +#include "utils/dirent.h" +#include "utils/messages.h" +#include "utils/corestrings.h" +#include "utils/log.h" +#include "utils/nsoption.h" +#include "utils/file.h" +#include "desktop/browser_history.h" +#include "desktop/browser.h" +#include "desktop/hotlist.h" +#include "desktop/plotters.h" +#include "desktop/print.h" +#include "desktop/save_complete.h" +#ifdef WITH_PDF_EXPORT +#include "desktop/font_haru.h" +#include "desktop/save_pdf.h" +#endif +#include "desktop/save_text.h" +#include "desktop/searchweb.h" +#include "desktop/textinput.h" +#include "content/hlcache.h" + +#include "gtk/compat.h" +#include "gtk/warn.h" +#include "gtk/cookies.h" +#include "gtk/completion.h" +#include "gtk/preferences.h" +#include "gtk/about.h" +#include "gtk/viewsource.h" +#include "gtk/bitmap.h" +#include "gtk/gui.h" +#include "gtk/history.h" +#include "gtk/hotlist.h" +#include "gtk/download.h" +#include "gtk/menu.h" +#include "gtk/plotters.h" +#include "gtk/print.h" +#include "gtk/search.h" +#include "gtk/throbber.h" +#include "gtk/toolbar.h" +#include "gtk/window.h" +#include "gtk/gdk.h" +#include "gtk/scaffolding.h" +#include "gtk/tabs.h" +#include "gtk/schedule.h" +#include "gtk/viewdata.h" +#include "gtk/resources.h" +#include "gtk/layout_pango.h" + +/** Macro to define a handler for menu, button and activate events. */ +#define MULTIHANDLER(q)\ +static gboolean nsgtk_on_##q##_activate(struct nsgtk_scaffolding *g);\ +static gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *widget, gpointer data)\ +{\ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;\ + return nsgtk_on_##q##_activate(g);\ +}\ +static gboolean nsgtk_on_##q##_activate_button(GtkButton *widget, gpointer data)\ +{\ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;\ + return nsgtk_on_##q##_activate(g);\ +}\ +static gboolean nsgtk_on_##q##_activate(struct nsgtk_scaffolding *g) + +/** Macro to define a handler for menu events. */ +#define MENUHANDLER(q)\ +static gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *widget, gpointer data) + +/** Macro to define a handler for button events. */ +#define BUTTONHANDLER(q)\ +static gboolean nsgtk_on_##q##_activate(GtkButton *widget, gpointer data) + +/** Core scaffolding structure. */ +struct nsgtk_scaffolding { + /** global linked list of scaffoldings for gui interface adjustments */ + struct nsgtk_scaffolding *next, *prev; + + /** currently active gui browsing context */ + struct gui_window *top_level; + + /** local history window */ + struct gtk_history_window *history_window; + + /** Builder object scaffold was created from */ + GtkBuilder *builder; + + /** scaffold container window */ + GtkWindow *window; + bool fullscreen; /**< flag for the scaffold window fullscreen status */ + + /** tab widget holding displayed pages */ + GtkNotebook *notebook; + + /** entry widget holding the url of the current displayed page */ + GtkWidget *url_bar; + GtkEntryCompletion *url_bar_completion; /**< Completions for url_bar */ + + /** Activity throbber */ + GtkImage *throbber; + int throb_frame; /**< Current frame of throbber animation */ + + struct gtk_search *search; + /** Web search widget */ + GtkWidget *webSearchEntry; + + /** controls toolbar */ + GtkToolbar *tool_bar; + struct nsgtk_button_connect *buttons[PLACEHOLDER_BUTTON]; + int offset; + int toolbarmem; + int toolbarbase; + int historybase; + + /** menu bar hierarchy */ + struct nsgtk_bar_submenu *menu_bar; + + /** right click popup menu hierarchy */ + struct nsgtk_popup_menu *menu_popup; + + /** link popup menu */ + struct nsgtk_link_menu *link_menu; + +}; + +/** current scaffold for model dialogue use */ +static struct nsgtk_scaffolding *scaf_current; + +/** global list for interface changes */ +static struct nsgtk_scaffolding *scaf_list = NULL; + +/** holds the context data for what's under the pointer, when the contextual + * menu is opened. + */ +static struct browser_window_features current_menu_features; + + +/** + * Helper to hide popup menu entries by grouping + */ +static void popup_menu_hide(struct nsgtk_popup_menu *menu, bool submenu, + bool nav, bool cnp, bool custom) +{ + if (submenu){ + gtk_widget_hide(GTK_WIDGET(menu->file_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->edit_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->view_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->nav_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->help_menuitem)); + + gtk_widget_hide(menu->first_separator); + } + + if (nav) { + gtk_widget_hide(GTK_WIDGET(menu->back_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->forward_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->stop_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->reload_menuitem)); + } + + if (cnp) { + gtk_widget_hide(GTK_WIDGET(menu->cut_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->copy_menuitem)); + gtk_widget_hide(GTK_WIDGET(menu->paste_menuitem)); + } + + if (custom) { + gtk_widget_hide(GTK_WIDGET(menu->customize_menuitem)); + } + +} + +/** + * Helper to show popup menu entries by grouping + */ +static void popup_menu_show(struct nsgtk_popup_menu *menu, bool submenu, + bool nav, bool cnp, bool custom) +{ + if (submenu){ + gtk_widget_show(GTK_WIDGET(menu->file_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->edit_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->view_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->nav_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->help_menuitem)); + + gtk_widget_show(menu->first_separator); + } + + if (nav) { + gtk_widget_show(GTK_WIDGET(menu->back_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->forward_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->stop_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->reload_menuitem)); + } + + if (cnp) { + gtk_widget_show(GTK_WIDGET(menu->cut_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->copy_menuitem)); + gtk_widget_show(GTK_WIDGET(menu->paste_menuitem)); + } + + if (custom) { + gtk_widget_show(GTK_WIDGET(menu->customize_menuitem)); + } + +} + + +/* event handlers and support functions for them */ + +/** + * resource cleanup function for window destruction. + */ +static void scaffolding_window_destroy(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *gs = data; + + LOG("scaffold:%p", gs); + + if ((gs->history_window) && (gs->history_window->window)) { + gtk_widget_destroy(GTK_WIDGET(gs->history_window->window)); + } + + if (gs->prev != NULL) { + gs->prev->next = gs->next; + } else { + scaf_list = gs->next; + } + if (gs->next != NULL) { + gs->next->prev = gs->prev; + } + + LOG("scaffold list head: %p", scaf_list); + + if (scaf_list == NULL) { + /* no more open windows - stop the browser */ + nsgtk_complete = true; + } +} + +/* signal delivered on window delete event, allowing to halt close if + * download is in progress + */ +static gboolean scaffolding_window_delete_event(GtkWidget *widget, + GdkEvent *event, gpointer data) +{ + struct nsgtk_scaffolding *g = data; + + if (nsgtk_check_for_downloads(GTK_WINDOW(widget)) == false) { + gtk_widget_destroy(GTK_WIDGET(g->window)); + } + return TRUE; +} + +/** + * Update the scaffoling button sensitivity, url bar and local history size + */ +static void scaffolding_update_context(struct nsgtk_scaffolding *g) +{ + int width, height; + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + + g->buttons[BACK_BUTTON]->sensitivity = + browser_window_history_back_available(bw); + g->buttons[FORWARD_BUTTON]->sensitivity = + browser_window_history_forward_available(bw); + + nsgtk_scaffolding_set_sensitivity(g); + + /* update the url bar, particularly necessary when tabbing */ + browser_window_refresh_url_bar(bw); + + /* update the local history window, as well as queuing a redraw + * for it. + */ + browser_window_history_size(bw, &width, &height); + gtk_widget_set_size_request(GTK_WIDGET(g->history_window->drawing_area), + width, height); + gtk_widget_queue_draw(GTK_WIDGET(g->history_window->drawing_area)); +} + +/** + * Make the throbber run. + */ +static void nsgtk_throb(void *p) +{ + struct nsgtk_scaffolding *g = p; + + if (g->throb_frame >= (nsgtk_throbber->nframes - 1)) + g->throb_frame = 1; + else + g->throb_frame++; + + gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[ + g->throb_frame]); + + nsgtk_schedule(100, nsgtk_throb, p); +} + +static guint nsgtk_scaffolding_update_edit_actions_sensitivity( + struct nsgtk_scaffolding *g) +{ + GtkWidget *widget = gtk_window_get_focus(g->window); + gboolean has_selection; + + if (GTK_IS_EDITABLE(widget)) { + has_selection = gtk_editable_get_selection_bounds( + GTK_EDITABLE (widget), NULL, NULL); + + g->buttons[COPY_BUTTON]->sensitivity = has_selection; + g->buttons[CUT_BUTTON]->sensitivity = has_selection; + g->buttons[PASTE_BUTTON]->sensitivity = true; + } else { + struct browser_window *bw = + nsgtk_get_browser_window(g->top_level); + browser_editor_flags edit_f = + browser_window_get_editor_flags(bw); + + g->buttons[COPY_BUTTON]->sensitivity = + edit_f & BW_EDITOR_CAN_COPY; + g->buttons[CUT_BUTTON]->sensitivity = + edit_f & BW_EDITOR_CAN_CUT; + g->buttons[PASTE_BUTTON]->sensitivity = + edit_f & BW_EDITOR_CAN_PASTE; + } + + nsgtk_scaffolding_set_sensitivity(g); + return ((g->buttons[COPY_BUTTON]->sensitivity) | + (g->buttons[CUT_BUTTON]->sensitivity) | + (g->buttons[PASTE_BUTTON]->sensitivity)); +} + + +static void nsgtk_scaffolding_enable_edit_actions_sensitivity( + struct nsgtk_scaffolding *g) +{ + + g->buttons[PASTE_BUTTON]->sensitivity = true; + g->buttons[COPY_BUTTON]->sensitivity = true; + g->buttons[CUT_BUTTON]->sensitivity = true; + nsgtk_scaffolding_set_sensitivity(g); + + popup_menu_show(g->menu_popup, false, false, true, false); +} + +/* signal handling functions for the toolbar, URL bar, and menu bar */ +static gboolean nsgtk_window_edit_menu_clicked(GtkWidget *widget, + struct nsgtk_scaffolding *g) +{ + nsgtk_scaffolding_update_edit_actions_sensitivity(g); + + return TRUE; +} + +static gboolean nsgtk_window_edit_menu_hidden(GtkWidget *widget, + struct nsgtk_scaffolding *g) +{ + nsgtk_scaffolding_enable_edit_actions_sensitivity(g); + + return TRUE; +} + +static gboolean nsgtk_window_popup_menu_hidden(GtkWidget *widget, + struct nsgtk_scaffolding *g) +{ + nsgtk_scaffolding_enable_edit_actions_sensitivity(g); + return TRUE; +} + +gboolean nsgtk_window_url_activate_event(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = data; + nserror ret; + nsurl *url; + + ret = search_web_omni(gtk_entry_get_text(GTK_ENTRY(g->url_bar)), + SEARCH_WEB_OMNI_NONE, + &url); + if (ret == NSERROR_OK) { + ret = browser_window_navigate(nsgtk_get_browser_window(g->top_level), + url, NULL, BW_NAVIGATE_HISTORY, + NULL, NULL, NULL); + nsurl_unref(url); + } + if (ret != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(ret), 0); + } + + return TRUE; +} + +/** + * update handler for URL entry widget + */ +gboolean +nsgtk_window_url_changed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + return nsgtk_completion_update(GTK_ENTRY(widget)); +} + +/** + * Event handler for popup menu on toolbar. + */ +static gboolean nsgtk_window_tool_bar_clicked(GtkToolbar *toolbar, + gint x, gint y, gint button, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + + /* set visibility for right-click popup menu */ + popup_menu_hide(g->menu_popup, true, false, true, false); + popup_menu_show(g->menu_popup, false, false, false, true); + + gtk_menu_popup(g->menu_popup->popup_menu, NULL, NULL, NULL, NULL, 0, + gtk_get_current_event_time()); + + return TRUE; +} + +/** + * Update the menus when the number of tabs changes. + */ +static void nsgtk_window_tabs_add(GtkNotebook *notebook, + GtkWidget *page, guint page_num, struct nsgtk_scaffolding *g) +{ + gboolean visible = gtk_notebook_get_show_tabs(g->notebook); + g_object_set(g->menu_bar->view_submenu->tabs_menuitem, "visible", visible, NULL); + g_object_set(g->menu_popup->view_submenu->tabs_menuitem, "visible", visible, NULL); + g->buttons[NEXTTAB_BUTTON]->sensitivity = visible; + g->buttons[PREVTAB_BUTTON]->sensitivity = visible; + g->buttons[CLOSETAB_BUTTON]->sensitivity = visible; + nsgtk_scaffolding_set_sensitivity(g); +} + +/** + * Update the menus when the number of tabs changes. + */ +static void +nsgtk_window_tabs_remove(GtkNotebook *notebook, + GtkWidget *page, + guint page_num, + struct nsgtk_scaffolding *gs) +{ + /* if the scaffold is being destroyed it is not useful to + * update the state, futher many of the widgets may have + * already been destroyed. + */ + if (gtk_widget_in_destruction(GTK_WIDGET(gs->window)) == TRUE) { + return; + } + + /* if this is the last tab destroy the scaffold in addition */ + if (gtk_notebook_get_n_pages(notebook) == 1) { + gtk_widget_destroy(GTK_WIDGET(gs->window)); + return; + } + + gboolean visible = gtk_notebook_get_show_tabs(gs->notebook); + g_object_set(gs->menu_bar->view_submenu->tabs_menuitem, "visible", visible, NULL); + g_object_set(gs->menu_popup->view_submenu->tabs_menuitem, "visible", visible, NULL); + gs->buttons[NEXTTAB_BUTTON]->sensitivity = visible; + gs->buttons[PREVTAB_BUTTON]->sensitivity = visible; + gs->buttons[CLOSETAB_BUTTON]->sensitivity = visible; + nsgtk_scaffolding_set_sensitivity(gs); +} + +/** + * Handle opening a file path. + */ +static void nsgtk_openfile_open(const char *filename) +{ + struct browser_window *bw; + char *urltxt; + nsurl *url; + nserror error; + + bw = nsgtk_get_browser_window(scaf_current->top_level); + + urltxt = malloc(strlen(filename) + FILE_SCHEME_PREFIX_LEN + 1); + + if (urltxt != NULL) { + sprintf(urltxt, FILE_SCHEME_PREFIX"%s", filename); + + error = nsurl_create(urltxt, &url); + if (error != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(error), 0); + } else { + browser_window_navigate(bw, + url, + NULL, + BW_NAVIGATE_HISTORY, + NULL, + NULL, + NULL); + nsurl_unref(url); + } + free(urltxt); + } +} + +/* signal handlers for menu entries */ + +MULTIHANDLER(newwindow) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + const char *addr; + nsurl *url; + nserror error; + + if (nsoption_charp(homepage_url) != NULL) { + addr = nsoption_charp(homepage_url); + } else { + addr = NETSURF_HOMEPAGE; + } + + error = nsurl_create(addr, &url); + if (error == NSERROR_OK) { + error = browser_window_create(BW_CREATE_HISTORY, + url, + NULL, + bw, + NULL); + nsurl_unref(url); + } + if (error != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(error), 0); + } + + return TRUE; +} + +nserror nsgtk_scaffolding_new_tab(struct gui_window *gw) +{ + struct browser_window *bw = nsgtk_get_browser_window(gw); + nsurl *url = NULL; + nserror error; + + if (!nsoption_bool(new_blank)) { + const char *addr; + if (nsoption_charp(homepage_url) != NULL) { + addr = nsoption_charp(homepage_url); + } else { + addr = NETSURF_HOMEPAGE; + } + error = nsurl_create(addr, &url); + if (error != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(error), 0); + } + } + + error = browser_window_create(BW_CREATE_HISTORY | + BW_CREATE_TAB, + url, + NULL, + bw, + NULL); + if (url != NULL) { + nsurl_unref(url); + } + return error; +} + +MULTIHANDLER(newtab) +{ + nserror error; + + error = nsgtk_scaffolding_new_tab(g->top_level); + if (error != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(error), 0); + } + return TRUE; +} + +MULTIHANDLER(openfile) +{ + GtkWidget *dlgOpen; + gint response; + + scaf_current = g; + dlgOpen = gtk_file_chooser_dialog_new("Open File", + scaf_current->window, + 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) { + gchar *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlgOpen)); + + nsgtk_openfile_open((const char *)filename); + + g_free(filename); + } + + gtk_widget_destroy(dlgOpen); + return TRUE; +} + +static gboolean nsgtk_filter_directory(const GtkFileFilterInfo *info, + gpointer data) +{ + DIR *d = opendir(info->filename); + if (d == NULL) + return FALSE; + closedir(d); + return TRUE; +} + +MULTIHANDLER(savepage) +{ + if (!browser_window_has_content(nsgtk_get_browser_window(g->top_level))) + return FALSE; + + GtkWidget *fc = gtk_file_chooser_dialog_new( + messages_get("gtkcompleteSave"), g->window, + GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + DIR *d; + char *path; + nserror res; + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, "Directories"); + gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, + nsgtk_filter_directory, NULL, NULL); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fc), filter); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fc), filter); + + res = nsurl_nice(browser_window_get_url( + nsgtk_get_browser_window(g->top_level)), &path, false); + if (res != NSERROR_OK) { + path = strdup(messages_get("SaveText")); + if (path == NULL) { + nsgtk_warning("NoMemory", 0); + return FALSE; + } + } + + if (access(path, F_OK) != 0) + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), path); + free(path); + + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc), + TRUE); + + if (gtk_dialog_run(GTK_DIALOG(fc)) != GTK_RESPONSE_ACCEPT) { + gtk_widget_destroy(fc); + return TRUE; + } + + path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc)); + d = opendir(path); + if (d == NULL) { + LOG("Unable to open directory %s for complete save: %s", path, strerror(errno)); + if (errno == ENOTDIR) + nsgtk_warning("NoDirError", path); + else + nsgtk_warning("gtkFileError", path); + gtk_widget_destroy(fc); + g_free(path); + return TRUE; + } + closedir(d); + save_complete(browser_window_get_content(nsgtk_get_browser_window( + g->top_level)), path, NULL); + g_free(path); + + gtk_widget_destroy(fc); + + return TRUE; +} + + +MULTIHANDLER(pdf) +{ +#ifdef WITH_PDF_EXPORT + + GtkWidget *save_dialog; + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + struct print_settings *settings; + char filename[PATH_MAX]; + char dirname[PATH_MAX]; + char *url_name; + nserror res; + + LOG("Print preview (generating PDF) started."); + + res = nsurl_nice(browser_window_get_url(bw), &url_name, true); + if (res != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(res), 0); + return TRUE; + } + + strncpy(filename, url_name, PATH_MAX); + strncat(filename, ".pdf", PATH_MAX - strlen(filename)); + filename[PATH_MAX - 1] = '\0'; + + free(url_name); + + strncpy(dirname, option_downloads_directory, PATH_MAX); + strncat(dirname, "/", PATH_MAX - strlen(dirname)); + dirname[PATH_MAX - 1] = '\0'; + + /* this way the scale used by PDF functions is synchronized with that + * used by the all-purpose print interface + */ + haru_nsfont_set_scale((float)option_export_scale / 100); + + save_dialog = gtk_file_chooser_dialog_new("Export to PDF", g->window, + GTK_FILE_CHOOSER_ACTION_SAVE, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog), + dirname); + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog), + filename); + + if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(save_dialog)); + + settings = print_make_settings(PRINT_OPTIONS, + (const char *) filename, &haru_nsfont); + g_free(filename); + + if (settings == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + gtk_widget_destroy(save_dialog); + return TRUE; + } + + /* This will clean up the print_settings object for us */ + print_basic_run(browser_window_get_content(bw), + &pdf_printer, settings); + } + + gtk_widget_destroy(save_dialog); + +#endif /* WITH_PDF_EXPORT */ + + return TRUE; +} + +MULTIHANDLER(plaintext) +{ + if (!browser_window_has_content(nsgtk_get_browser_window(g->top_level))) + return FALSE; + + GtkWidget *fc = gtk_file_chooser_dialog_new( + messages_get("gtkplainSave"), g->window, + GTK_FILE_CHOOSER_ACTION_SAVE, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + char *filename; + nserror res; + + res = nsurl_nice(browser_window_get_url( + nsgtk_get_browser_window(g->top_level)), + &filename, false); + if (res != NSERROR_OK) { + filename = strdup(messages_get("SaveText")); + if (filename == NULL) { + nsgtk_warning("NoMemory", 0); + return FALSE; + } + } + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), filename); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc), + TRUE); + + free(filename); + + if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc)); + save_as_text(browser_window_get_content( + nsgtk_get_browser_window( + g->top_level)), filename); + g_free(filename); + } + + gtk_widget_destroy(fc); + return TRUE; +} + +MULTIHANDLER(drawfile) +{ + return TRUE; +} + +MULTIHANDLER(postscript) +{ + return TRUE; +} + +MULTIHANDLER(printpreview) +{ + return TRUE; +} + + +MULTIHANDLER(print) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + + GtkPrintOperation *print_op; + GtkPageSetup *page_setup; + GtkPrintSettings *print_settings; + GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR; + struct print_settings *nssettings; + char *settings_fname = NULL; + + 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(g->window, 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, + g->window, + 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; +} + +MULTIHANDLER(closewindow) +{ + gtk_widget_destroy(GTK_WIDGET(g->window)); + return TRUE; +} + +MULTIHANDLER(quit) +{ + struct nsgtk_scaffolding *gs; + + if (nsgtk_check_for_downloads(g->window) == false) { + gs = scaf_list; + while (gs != NULL) { + gtk_widget_destroy(GTK_WIDGET(gs->window)); + gs = gs->next; + } + } + + return TRUE; +} + +MENUHANDLER(savelink) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data; + struct gui_window *gui = g->top_level; + struct browser_window *bw = nsgtk_get_browser_window(gui); + nserror err; + + if (current_menu_features.link == NULL) + return FALSE; + + err = browser_window_navigate(bw, + current_menu_features.link, + NULL, + BW_NAVIGATE_DOWNLOAD, + NULL, + NULL, + NULL); + if (err != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(err), 0); + } + + return TRUE; +} + +/** + * Handler for opening new window from a link. attached to the popup menu. + */ +MENUHANDLER(link_openwin) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data; + struct gui_window *gui = g->top_level; + struct browser_window *bw = nsgtk_get_browser_window(gui); + nserror err; + + if (current_menu_features.link == NULL) + return FALSE; + + err = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY, + current_menu_features.link, NULL, bw, NULL); + if (err != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(err), 0); + } + + return TRUE; +} + +/** + * Handler for opening new tab from a link. attached to the popup menu. + */ +MENUHANDLER(link_opentab) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data; + struct gui_window *gui = g->top_level; + struct browser_window *bw = nsgtk_get_browser_window(gui); + nserror err; + + if (current_menu_features.link == NULL) + return FALSE; + + temp_open_background = 1; + + err = browser_window_create(BW_CREATE_CLONE | + BW_CREATE_HISTORY | + BW_CREATE_TAB, + current_menu_features.link, NULL, bw, NULL); + if (err != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(err), 0); + } + + temp_open_background = -1; + + return TRUE; +} + +/** + * Handler for bookmarking a link. attached to the popup menu. + */ +MENUHANDLER(link_bookmark) +{ + if (current_menu_features.link == NULL) + return FALSE; + + hotlist_add_url(current_menu_features.link); + + return TRUE; +} + +/** + * Handler for copying a link. attached to the popup menu. + */ +MENUHANDLER(link_copy) +{ + GtkClipboard *clipboard; + + if (current_menu_features.link == NULL) + return FALSE; + + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text(clipboard, + nsurl_access(current_menu_features.link), -1); + + return TRUE; +} + + +MULTIHANDLER(cut) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + GtkWidget *focused = gtk_window_get_focus(g->window); + + /* If the url bar has focus, let gtk handle it */ + if (GTK_IS_EDITABLE (focused)) + gtk_editable_cut_clipboard (GTK_EDITABLE(g->url_bar)); + else + browser_window_key_press(bw, NS_KEY_CUT_SELECTION); + + return TRUE; +} + +MULTIHANDLER(copy) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + GtkWidget *focused = gtk_window_get_focus(g->window); + + /* If the url bar has focus, let gtk handle it */ + if (GTK_IS_EDITABLE (focused)) + gtk_editable_copy_clipboard(GTK_EDITABLE(g->url_bar)); + else + browser_window_key_press(bw, NS_KEY_COPY_SELECTION); + + return TRUE; +} + +MULTIHANDLER(paste) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + GtkWidget *focused = gtk_window_get_focus(g->window); + + /* If the url bar has focus, let gtk handle it */ + if (GTK_IS_EDITABLE (focused)) + gtk_editable_paste_clipboard (GTK_EDITABLE (focused)); + else + browser_window_key_press(bw, NS_KEY_PASTE); + + return TRUE; +} + +MULTIHANDLER(delete) +{ + return TRUE; +} + +MENUHANDLER(customize) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + nsgtk_toolbar_customization_init(g); + return TRUE; +} + +MULTIHANDLER(selectall) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + + if (nsgtk_widget_has_focus(GTK_WIDGET(g->url_bar))) { + LOG("Selecting all URL bar text"); + gtk_editable_select_region(GTK_EDITABLE(g->url_bar), 0, -1); + } else { + LOG("Selecting all document text"); + browser_window_key_press(bw, NS_KEY_SELECT_ALL); + } + + return TRUE; +} + +MULTIHANDLER(find) +{ + nsgtk_scaffolding_toggle_search_bar_visibility(g); + return TRUE; +} + +MULTIHANDLER(preferences) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + GtkWidget* wndpreferences; + + wndpreferences = nsgtk_preferences(bw, g->window); + if (wndpreferences != NULL) { + gtk_widget_show(GTK_WIDGET(wndpreferences)); + } + + return TRUE; +} + +MULTIHANDLER(zoomplus) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + float old_scale = nsgtk_get_scale_for_gui(g->top_level); + + browser_window_set_scale(bw, old_scale + 0.05, true); + + return TRUE; +} + +MULTIHANDLER(zoomnormal) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + + browser_window_set_scale(bw, 1.0, true); + + return TRUE; +} + +MULTIHANDLER(zoomminus) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + float old_scale = nsgtk_get_scale_for_gui(g->top_level); + + browser_window_set_scale(bw, old_scale - 0.05, true); + + return TRUE; +} + +MULTIHANDLER(fullscreen) +{ + if (g->fullscreen) { + gtk_window_unfullscreen(g->window); + } else { + gtk_window_fullscreen(g->window); + } + + g->fullscreen = !g->fullscreen; + + return TRUE; +} + +MULTIHANDLER(viewsource) +{ + nserror ret; + + ret = nsgtk_viewsource(g->window, nsgtk_get_browser_window(g->top_level)); + if (ret != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(ret), 0); + } + + return TRUE; +} + +MENUHANDLER(menubar) +{ + GtkWidget *w; + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + + /* if the menubar is not being shown the popup menu shows the + * menubar entries instead. + */ + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { + /* need to synchronise menus as gtk grumbles when one menu + * is attached to both headers */ + w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->menubar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)) + == FALSE) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + TRUE); + + w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->menubar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)) + == FALSE) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + TRUE); + + gtk_widget_show(GTK_WIDGET(g->menu_bar->bar_menu)); + + popup_menu_show(g->menu_popup, false, true, true, true); + popup_menu_hide(g->menu_popup, true, false, false, false); + } else { + w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->menubar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + FALSE); + + w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->menubar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + FALSE); + + gtk_widget_hide(GTK_WIDGET(g->menu_bar->bar_menu)); + + popup_menu_show(g->menu_popup, true, true, true, true); + + } + return TRUE; +} + +MENUHANDLER(toolbar) +{ + GtkWidget *w; + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { + w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->toolbar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)) + == FALSE) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + TRUE); + + w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->toolbar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)) + == FALSE) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + TRUE); + gtk_widget_show(GTK_WIDGET(g->tool_bar)); + } else { + w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->toolbar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + FALSE); + w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->toolbar_menuitem); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), + FALSE); + gtk_widget_hide(GTK_WIDGET(g->tool_bar)); + } + + return TRUE; +} + +MULTIHANDLER(downloads) +{ + nsgtk_download_show(g->window); + + return TRUE; +} + +MULTIHANDLER(savewindowsize) +{ + int x,y,w,h; + char *choices = NULL; + + gtk_window_get_position(g->window, &x, &y); + gtk_window_get_size(g->window, &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; +} + +MULTIHANDLER(toggledebugging) +{ + struct browser_window *bw; + + bw = nsgtk_get_browser_window(g->top_level); + + browser_window_debug(bw, CONTENT_DEBUG_REDRAW); + + nsgtk_reflow_all_windows(); + + return TRUE; +} + +MULTIHANDLER(debugboxtree) +{ + gchar *fname; + gint handle; + FILE *f; + struct browser_window *bw; + + 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 = nsgtk_get_browser_window(g->top_level); + + browser_window_debug_dump(bw, f, CONTENT_DEBUG_RENDER); + + fclose(f); + + nsgtk_viewfile("Box Tree Debug", "boxtree", fname); + + g_free(fname); + + return TRUE; +} + +MULTIHANDLER(debugdomtree) +{ + gchar *fname; + gint handle; + FILE *f; + struct browser_window *bw; + + 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 = nsgtk_get_browser_window(g->top_level); + + browser_window_debug_dump(bw, f, CONTENT_DEBUG_DOM); + + fclose(f); + + nsgtk_viewfile("DOM Tree Debug", "domtree", fname); + + g_free(fname); + + return TRUE; +} + + +MULTIHANDLER(stop) +{ + struct browser_window *bw = + nsgtk_get_browser_window(g->top_level); + + browser_window_stop(bw); + + return TRUE; +} + +MULTIHANDLER(reload) +{ + struct browser_window *bw = + nsgtk_get_browser_window(g->top_level); + if (bw == NULL) + return TRUE; + + /* clear potential search effects */ + browser_window_search_clear(bw); + + browser_window_reload(bw, true); + + return TRUE; +} + +MULTIHANDLER(back) +{ + struct browser_window *bw = + nsgtk_get_browser_window(g->top_level); + + if ((bw == NULL) || (!browser_window_history_back_available(bw))) + return TRUE; + + /* clear potential search effects */ + browser_window_search_clear(bw); + + browser_window_history_back(bw, false); + scaffolding_update_context(g); + + return TRUE; +} + +MULTIHANDLER(forward) +{ + struct browser_window *bw = + nsgtk_get_browser_window(g->top_level); + + if ((bw == NULL) || (!browser_window_history_forward_available(bw))) + return TRUE; + + /* clear potential search effects */ + browser_window_search_clear(bw); + + browser_window_history_forward(bw, false); + scaffolding_update_context(g); + + return TRUE; +} + +MULTIHANDLER(home) +{ + static const char *addr = NETSURF_HOMEPAGE; + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + nsurl *url; + nserror error; + + if (nsoption_charp(homepage_url) != NULL) { + addr = nsoption_charp(homepage_url); + } + + error = nsurl_create(addr, &url); + if (error != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(error), 0); + } else { + browser_window_navigate(bw, + url, + NULL, + BW_NAVIGATE_HISTORY, + NULL, + NULL, + NULL); + nsurl_unref(url); + } + + return TRUE; +} + +MULTIHANDLER(localhistory) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + + int x,y, width, height, mainwidth, mainheight, margin = 20; + /* if entries of the same url but different frag_ids have been added + * the history needs redrawing (what throbber code normally does) + */ + + scaffolding_update_context(g); + gtk_window_get_position(g->window, &x, &y); + gtk_window_get_size(g->window, &mainwidth, &mainheight); + browser_window_history_size(bw, &width, &height); + width = (width + g->historybase + margin > mainwidth) ? + mainwidth - g->historybase : width + margin; + height = (height + g->toolbarbase + margin > mainheight) ? + mainheight - g->toolbarbase : height + margin; + gtk_window_set_default_size(g->history_window->window, width, height); + gtk_widget_set_size_request(GTK_WIDGET(g->history_window->window), + -1, -1); + gtk_window_resize(g->history_window->window, width, height); + gtk_window_set_transient_for(g->history_window->window, g->window); + nsgtk_window_set_opacity(g->history_window->window, 0.9); + gtk_widget_show(GTK_WIDGET(g->history_window->window)); + gtk_window_move(g->history_window->window, x + g->historybase, y + + g->toolbarbase); + gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(g->history_window->window))); + + return TRUE; +} + +MULTIHANDLER(globalhistory) +{ + gtk_widget_show(GTK_WIDGET(wndHistory)); + gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndHistory))); + + return TRUE; +} + +MULTIHANDLER(addbookmarks) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + + if (bw == NULL || !browser_window_has_content(bw)) + return TRUE; + hotlist_add_url(browser_window_get_url(bw)); + return TRUE; +} + +MULTIHANDLER(showbookmarks) +{ + gtk_widget_show(GTK_WIDGET(wndHotlist)); + gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndHotlist))); + gtk_window_set_focus(wndHotlist, NULL); + + return TRUE; +} + +MULTIHANDLER(showcookies) +{ + gtk_widget_show(GTK_WIDGET(wndCookies)); + gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndCookies))); + + return TRUE; +} + +MULTIHANDLER(openlocation) +{ + gtk_widget_grab_focus(GTK_WIDGET(g->url_bar)); + return TRUE; +} + +MULTIHANDLER(nexttab) +{ + nsgtk_tab_next(g->notebook); + + return TRUE; +} + +MULTIHANDLER(prevtab) +{ + + nsgtk_tab_prev(g->notebook); + + return TRUE; +} + +MULTIHANDLER(closetab) +{ + nsgtk_tab_close_current(g->notebook); + + return TRUE; +} + +MULTIHANDLER(contents) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + nsurl *url; + nserror error; + + error = nsurl_create("http://www.netsurf-browser.org/documentation/", &url); + if (error != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(error), 0); + } else { + browser_window_navigate(bw, + url, + NULL, + BW_NAVIGATE_HISTORY, + NULL, + NULL, + NULL); + nsurl_unref(url); + } + + return TRUE; +} + +MULTIHANDLER(guide) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + nsurl *url; + + if (nsurl_create("http://www.netsurf-browser.org/documentation/guide", &url) != NSERROR_OK) { + nsgtk_warning("NoMemory", 0); + } else { + browser_window_navigate(bw, + url, + NULL, + BW_NAVIGATE_HISTORY, + NULL, + NULL, + NULL); + nsurl_unref(url); + } + + return TRUE; +} + +MULTIHANDLER(info) +{ + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + nsurl *url; + + if (nsurl_create("http://www.netsurf-browser.org/documentation/info", &url) != NSERROR_OK) { + nsgtk_warning("NoMemory", 0); + } else { + browser_window_navigate(bw, + url, + NULL, + BW_NAVIGATE_HISTORY, + NULL, + NULL, + NULL); + nsurl_unref(url); + } + + return TRUE; +} + +MULTIHANDLER(about) +{ + nsgtk_about_dialog_init(g->window); + return TRUE; +} + +BUTTONHANDLER(history) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + return nsgtk_on_localhistory_activate(g); +} + +#undef MULTIHANDLER +#undef CHECKHANDLER +#undef BUTTONHANDLER + +#if GTK_CHECK_VERSION(3,0,0) + +static gboolean +nsgtk_history_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data) +{ + struct rect clip; + struct gtk_history_window *hw = (struct gtk_history_window *)data; + struct browser_window *bw = + nsgtk_get_browser_window(hw->g->top_level); + + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &nsgtk_plotters + }; + double x1; + double y1; + double x2; + double y2; + + current_widget = widget; + current_cr = cr; + + cairo_clip_extents(cr, &x1, &y1, &x2, &y2); + + clip.x0 = x1; + clip.y0 = y1; + clip.x1 = x2; + clip.y1 = y2; + + ctx.plot->clip(&clip); + + browser_window_history_redraw(bw, &ctx); + + current_widget = NULL; + + return FALSE; +} +#else + +/* signal handler functions for the local history window */ +static gboolean +nsgtk_history_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer g) +{ + struct rect clip; + struct gtk_history_window *hw = (struct gtk_history_window *)g; + struct browser_window *bw = + nsgtk_get_browser_window(hw->g->top_level); + + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &nsgtk_plotters + }; + + current_widget = widget; + + current_cr = gdk_cairo_create(nsgtk_widget_get_window(widget)); + + clip.x0 = event->area.x; + clip.y0 = event->area.y; + clip.x1 = event->area.x + event->area.width; + clip.y1 = event->area.y + event->area.height; + ctx.plot->clip(&clip); + + browser_window_history_redraw(bw, &ctx); + + cairo_destroy(current_cr); + + current_widget = NULL; + + return FALSE; +} + +#endif /* GTK_CHECK_VERSION(3,0,0) */ + +static gboolean nsgtk_history_button_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer g) +{ + struct gtk_history_window *hw = (struct gtk_history_window *)g; + struct browser_window *bw = + nsgtk_get_browser_window(hw->g->top_level); + + LOG("X=%g, Y=%g", event->x, event->y); + + browser_window_history_click(bw, event->x, event->y, false); + + return TRUE; +} + + + +static void nsgtk_attach_menu_handlers(struct nsgtk_scaffolding *g) +{ + for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + if (g->buttons[i]->main != NULL) { + g_signal_connect(g->buttons[i]->main, "activate", + G_CALLBACK(g->buttons[i]->mhandler), g); + } + if (g->buttons[i]->rclick != NULL) { + g_signal_connect(g->buttons[i]->rclick, "activate", + G_CALLBACK(g->buttons[i]->mhandler), g); + } + if (g->buttons[i]->popup != NULL) { + g_signal_connect(g->buttons[i]->popup, "activate", + G_CALLBACK(g->buttons[i]->mhandler), g); + } + } +#define CONNECT_CHECK(q)\ + g_signal_connect(g->menu_bar->view_submenu->toolbars_submenu->q##_menuitem, "toggled", G_CALLBACK(nsgtk_on_##q##_activate_menu), g);\ + g_signal_connect(g->menu_popup->view_submenu->toolbars_submenu->q##_menuitem, "toggled", G_CALLBACK(nsgtk_on_##q##_activate_menu), g) + CONNECT_CHECK(menubar); + CONNECT_CHECK(toolbar); +#undef CONNECT_CHECK + +} + +/** + * Create and connect handlers to popup menu. + * + * \param g scaffolding to attach popup menu to. + * \param group The accelerator group to use for the popup. + * \return menu structure on success or NULL on error. + */ +static struct nsgtk_popup_menu * +nsgtk_new_scaffolding_popup(struct nsgtk_scaffolding *g, GtkAccelGroup *group) +{ + struct nsgtk_popup_menu *nmenu; + + nmenu = nsgtk_popup_menu_create(group); + + if (nmenu == NULL) { + return NULL; + } + + g_signal_connect(nmenu->popup_menu, "hide", + G_CALLBACK(nsgtk_window_popup_menu_hidden), g); + + g_signal_connect(nmenu->cut_menuitem, "activate", + G_CALLBACK(nsgtk_on_cut_activate_menu), g); + + g_signal_connect(nmenu->copy_menuitem, "activate", + G_CALLBACK(nsgtk_on_copy_activate_menu), g); + + g_signal_connect(nmenu->paste_menuitem, "activate", + G_CALLBACK(nsgtk_on_paste_activate_menu), g); + + g_signal_connect(nmenu->customize_menuitem, "activate", + G_CALLBACK(nsgtk_on_customize_activate_menu), g); + + /* set initial popup menu visibility */ + popup_menu_hide(nmenu, true, false, false, true); + + return nmenu; +} + +/** + * Create and connect handlers to link popup menu. + * + * \param g scaffolding to attach popup menu to. + * \param group The accelerator group to use for the popup. + * \return true on success or false on error. + */ +static struct nsgtk_link_menu * +nsgtk_new_scaffolding_link_popup(struct nsgtk_scaffolding *g, GtkAccelGroup *group) +{ + struct nsgtk_link_menu *nmenu; + + nmenu = nsgtk_link_menu_create(group); + + if (nmenu == NULL) { + return NULL; + } + + g_signal_connect(nmenu->save_menuitem, "activate", + G_CALLBACK(nsgtk_on_savelink_activate_menu), g); + + g_signal_connect(nmenu->opentab_menuitem, "activate", + G_CALLBACK(nsgtk_on_link_opentab_activate_menu), g); + + g_signal_connect(nmenu->openwin_menuitem, "activate", + G_CALLBACK(nsgtk_on_link_openwin_activate_menu), g); + + g_signal_connect(nmenu->bookmark_menuitem, "activate", + G_CALLBACK(nsgtk_on_link_bookmark_activate_menu), g); + + g_signal_connect(nmenu->copy_menuitem, "activate", + G_CALLBACK(nsgtk_on_link_copy_activate_menu), g); + + return nmenu; +} + +/* exported interface documented in gtk/scaffolding.h */ +struct nsgtk_scaffolding *nsgtk_current_scaffolding(void) +{ + if (scaf_current == NULL) { + scaf_current = scaf_list; + } + return scaf_current; +} + +/** + * init the array g->buttons[] + */ +static void nsgtk_scaffolding_toolbar_init(struct nsgtk_scaffolding *g) +{ +#define ITEM_MAIN(p, q, r)\ + g->buttons[p##_BUTTON]->main = g->menu_bar->q->r##_menuitem;\ + g->buttons[p##_BUTTON]->rclick = g->menu_popup->q->r##_menuitem;\ + g->buttons[p##_BUTTON]->mhandler = nsgtk_on_##r##_activate_menu;\ + g->buttons[p##_BUTTON]->bhandler = nsgtk_on_##r##_activate_button;\ + g->buttons[p##_BUTTON]->dataplus = nsgtk_toolbar_##r##_button_data;\ + g->buttons[p##_BUTTON]->dataminus = nsgtk_toolbar_##r##_toolbar_button_data + +#define ITEM_SUB(p, q, r, s)\ + g->buttons[p##_BUTTON]->main =\ + g->menu_bar->q->r##_submenu->s##_menuitem;\ + g->buttons[p##_BUTTON]->rclick =\ + g->menu_popup->q->r##_submenu->s##_menuitem;\ + g->buttons[p##_BUTTON]->mhandler =\ + nsgtk_on_##s##_activate_menu;\ + g->buttons[p##_BUTTON]->bhandler =\ + nsgtk_on_##s##_activate_button;\ + g->buttons[p##_BUTTON]->dataplus =\ + nsgtk_toolbar_##s##_button_data;\ + g->buttons[p##_BUTTON]->dataminus =\ + nsgtk_toolbar_##s##_toolbar_button_data + +#define ITEM_BUTTON(p, q)\ + g->buttons[p##_BUTTON]->bhandler =\ + nsgtk_on_##q##_activate;\ + g->buttons[p##_BUTTON]->dataplus =\ + nsgtk_toolbar_##q##_button_data;\ + g->buttons[p##_BUTTON]->dataminus =\ + nsgtk_toolbar_##q##_toolbar_button_data + +#define ITEM_POP(p, q) \ + g->buttons[p##_BUTTON]->popup = g->menu_popup->q##_menuitem + +#define SENSITIVITY(q) \ + g->buttons[q##_BUTTON]->sensitivity = false + +#define ITEM_ITEM(p, q)\ + g->buttons[p##_ITEM]->dataplus =\ + nsgtk_toolbar_##q##_button_data;\ + g->buttons[p##_ITEM]->dataminus =\ + nsgtk_toolbar_##q##_toolbar_button_data + + ITEM_ITEM(WEBSEARCH, websearch); + ITEM_ITEM(THROBBER, throbber); + ITEM_MAIN(NEWWINDOW, file_submenu, newwindow); + ITEM_MAIN(NEWTAB, file_submenu, newtab); + ITEM_MAIN(OPENFILE, file_submenu, openfile); + ITEM_MAIN(PRINT, file_submenu, print); + ITEM_MAIN(CLOSEWINDOW, file_submenu, closewindow); + ITEM_MAIN(SAVEPAGE, file_submenu, savepage); + ITEM_MAIN(PRINTPREVIEW, file_submenu, printpreview); + ITEM_MAIN(PRINT, file_submenu, print); + ITEM_MAIN(QUIT, file_submenu, quit); + ITEM_MAIN(CUT, edit_submenu, cut); + ITEM_MAIN(COPY, edit_submenu, copy); + ITEM_MAIN(PASTE, edit_submenu, paste); + ITEM_MAIN(DELETE, edit_submenu, delete); + ITEM_MAIN(SELECTALL, edit_submenu, selectall); + ITEM_MAIN(FIND, edit_submenu, find); + ITEM_MAIN(PREFERENCES, edit_submenu, preferences); + ITEM_MAIN(STOP, view_submenu, stop); + ITEM_POP(STOP, stop); + ITEM_MAIN(RELOAD, view_submenu, reload); + ITEM_POP(RELOAD, reload); + ITEM_MAIN(FULLSCREEN, view_submenu, fullscreen); + ITEM_MAIN(DOWNLOADS, tools_submenu, downloads); + ITEM_MAIN(SAVEWINDOWSIZE, view_submenu, savewindowsize); + ITEM_MAIN(BACK, nav_submenu, back); + ITEM_POP(BACK, back); + ITEM_MAIN(FORWARD, nav_submenu, forward); + ITEM_POP(FORWARD, forward); + ITEM_MAIN(HOME, nav_submenu, home); + ITEM_MAIN(LOCALHISTORY, nav_submenu, localhistory); + ITEM_MAIN(GLOBALHISTORY, nav_submenu, globalhistory); + ITEM_MAIN(ADDBOOKMARKS, nav_submenu, addbookmarks); + ITEM_MAIN(SHOWBOOKMARKS, nav_submenu, showbookmarks); + ITEM_MAIN(SHOWCOOKIES, tools_submenu, showcookies); + ITEM_MAIN(OPENLOCATION, nav_submenu, openlocation); + ITEM_MAIN(CONTENTS, help_submenu, contents); + ITEM_MAIN(INFO, help_submenu, info); + ITEM_MAIN(GUIDE, help_submenu, guide); + ITEM_MAIN(ABOUT, help_submenu, about); + ITEM_SUB(PLAINTEXT, file_submenu, export, plaintext); + ITEM_SUB(PDF, file_submenu, export, pdf); + ITEM_SUB(DRAWFILE, file_submenu, export, drawfile); + ITEM_SUB(POSTSCRIPT, file_submenu, export, postscript); + ITEM_SUB(ZOOMPLUS, view_submenu, scaleview, zoomplus); + ITEM_SUB(ZOOMMINUS, view_submenu, scaleview, zoomminus); + ITEM_SUB(ZOOMNORMAL, view_submenu, scaleview, zoomnormal); + ITEM_SUB(NEXTTAB, view_submenu, tabs, nexttab); + ITEM_SUB(PREVTAB, view_submenu, tabs, prevtab); + ITEM_SUB(CLOSETAB, view_submenu, tabs, closetab); + + /* development submenu */ + ITEM_SUB(VIEWSOURCE, tools_submenu, developer, viewsource); + ITEM_SUB(TOGGLEDEBUGGING, tools_submenu, developer, toggledebugging); + ITEM_SUB(SAVEBOXTREE, tools_submenu, developer, debugboxtree); + ITEM_SUB(SAVEDOMTREE, tools_submenu, developer, debugdomtree); + ITEM_BUTTON(HISTORY, history); + + /* disable items that make no sense initially, as well as + * as-yet-unimplemented items */ + SENSITIVITY(BACK); + SENSITIVITY(FORWARD); + SENSITIVITY(STOP); + SENSITIVITY(PRINTPREVIEW); + SENSITIVITY(DELETE); + SENSITIVITY(DRAWFILE); + SENSITIVITY(POSTSCRIPT); + SENSITIVITY(NEXTTAB); + SENSITIVITY(PREVTAB); + SENSITIVITY(CLOSETAB); +#ifndef WITH_PDF_EXPORT + SENSITIVITY(PDF); +#endif + +#undef ITEM_MAIN +#undef ITEM_SUB +#undef ITEM_BUTTON +#undef ITEM_POP +#undef SENSITIVITY + +} + +static void nsgtk_scaffolding_initial_sensitivity(struct nsgtk_scaffolding *g) +{ + for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + if (g->buttons[i]->main != NULL) + gtk_widget_set_sensitive(GTK_WIDGET( + g->buttons[i]->main), + g->buttons[i]->sensitivity); + if (g->buttons[i]->rclick != NULL) + gtk_widget_set_sensitive(GTK_WIDGET( + g->buttons[i]->rclick), + g->buttons[i]->sensitivity); + if ((g->buttons[i]->location != -1) && + (g->buttons[i]->button != NULL)) + gtk_widget_set_sensitive(GTK_WIDGET( + g->buttons[i]->button), + g->buttons[i]->sensitivity); + if (g->buttons[i]->popup != NULL) + gtk_widget_set_sensitive(GTK_WIDGET( + g->buttons[i]->popup), + g->buttons[i]->sensitivity); + } + gtk_widget_set_sensitive(GTK_WIDGET(g->menu_bar->view_submenu->images_menuitem), FALSE); +} + + +void nsgtk_scaffolding_toolbars(struct nsgtk_scaffolding *g, int tbi) +{ + switch (tbi) { + /* case 0 is 'unset' [from fresh install / clearing options] + * see above */ + + case 1: /* Small icons */ + /* main toolbar */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar), + GTK_ICON_SIZE_SMALL_TOOLBAR); + /* search toolbar */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar), + GTK_ICON_SIZE_SMALL_TOOLBAR); + break; + + case 2: /* Large icons */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar), + GTK_ICON_SIZE_LARGE_TOOLBAR); + /* search toolbar */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar), + GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + + case 3: /* Large icons with text */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar), + GTK_TOOLBAR_BOTH); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar), + GTK_ICON_SIZE_LARGE_TOOLBAR); + /* search toolbar */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar), + GTK_TOOLBAR_BOTH); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar), + GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + + case 4: /* Text icons only */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar), + GTK_TOOLBAR_TEXT); + /* search toolbar */ + gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar), + GTK_TOOLBAR_TEXT); + default: + break; + } +} + +/* exported interface documented in gtk/scaffolding.h */ +struct nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel) +{ + struct nsgtk_scaffolding *gs; + int i; + GtkAccelGroup *group; + + gs = malloc(sizeof(*gs)); + if (gs == NULL) { + return NULL; + } + + LOG("Constructing a scaffold of %p for gui_window %p", gs, toplevel); + + gs->top_level = toplevel; + + /* Construct UI widgets */ + if (nsgtk_builder_new_from_resname("netsurf", &gs->builder) != NSERROR_OK) { + free(gs); + return NULL; + } + + gtk_builder_connect_signals(gs->builder, NULL); + +/** Obtain a GTK widget handle from UI builder object */ +#define GET_WIDGET(x) GTK_WIDGET (gtk_builder_get_object(gs->builder, (x))) + + gs->window = GTK_WINDOW(GET_WIDGET("wndBrowser")); + gs->notebook = GTK_NOTEBOOK(GET_WIDGET("notebook")); + gs->tool_bar = GTK_TOOLBAR(GET_WIDGET("toolbar")); + + gs->search = malloc(sizeof(struct gtk_search)); + if (gs->search == NULL) { + free(gs); + return NULL; + } + + gs->search->bar = GTK_TOOLBAR(GET_WIDGET("searchbar")); + gs->search->entry = GTK_ENTRY(GET_WIDGET("searchEntry")); + + gs->search->buttons[0] = GTK_TOOL_BUTTON(GET_WIDGET("searchBackButton")); + gs->search->buttons[1] = GTK_TOOL_BUTTON(GET_WIDGET("searchForwardButton")); + gs->search->buttons[2] = GTK_TOOL_BUTTON(GET_WIDGET("closeSearchButton")); + gs->search->checkAll = GTK_CHECK_BUTTON(GET_WIDGET("checkAllSearch")); + gs->search->caseSens = GTK_CHECK_BUTTON(GET_WIDGET("caseSensButton")); + +#undef GET_WIDGET + + /* allocate buttons */ + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + gs->buttons[i] = calloc(1, sizeof(struct nsgtk_button_connect)); + if (gs->buttons[i] == NULL) { + for (i-- ; i >= BACK_BUTTON; i--) { + free(gs->buttons[i]); + } + free(gs); + return NULL; + } + gs->buttons[i]->location = -1; + gs->buttons[i]->sensitivity = true; + } + + /* here custom toolbutton adding code */ + gs->offset = 0; + gs->toolbarmem = 0; + gs->toolbarbase = 0; + gs->historybase = 0; + nsgtk_toolbar_customization_load(gs); + nsgtk_toolbar_set_physical(gs); + + group = gtk_accel_group_new(); + gtk_window_add_accel_group(gs->window, group); + + gs->menu_bar = nsgtk_menu_bar_create(GTK_MENU_SHELL(gtk_builder_get_object(gs->builder, "menubar")), group); + + + /* set this window's size and position to what's in the options, or + * or some sensible default if they're not set yet. + */ + if (nsoption_int(window_width) > 0) { + gtk_window_move(gs->window, + nsoption_int(window_x), + nsoption_int(window_y)); + gtk_window_resize(gs->window, + nsoption_int(window_width), + nsoption_int(window_height)); + } else { + /* Set to 1000x700, so we're very likely to fit even on + * 1024x768 displays, not being able to take into account + * window furniture or panels. + */ + gtk_window_set_default_size(gs->window, 1000, 700); + } + + /* Default toolbar button type uses system defaults */ + if (nsoption_int(button_type) == 0) { + GtkSettings *settings = gtk_settings_get_default(); + GtkIconSize tooliconsize; + GtkToolbarStyle toolbarstyle; + + g_object_get(settings, + "gtk-toolbar-icon-size", &tooliconsize, + "gtk-toolbar-style", &toolbarstyle, NULL); + + switch (toolbarstyle) { + case GTK_TOOLBAR_ICONS: + if (tooliconsize == GTK_ICON_SIZE_SMALL_TOOLBAR) { + nsoption_set_int(button_type, 1); + } else { + nsoption_set_int(button_type, 2); + } + break; + + case GTK_TOOLBAR_TEXT: + nsoption_set_int(button_type, 4); + break; + + case GTK_TOOLBAR_BOTH: + case GTK_TOOLBAR_BOTH_HORIZ: + /* no labels in default configuration */ + default: + /* No system default, so use large icons */ + nsoption_set_int(button_type, 2); + break; + } + } + + nsgtk_scaffolding_toolbars(gs, nsoption_int(button_type)); + + gtk_toolbar_set_show_arrow(gs->tool_bar, TRUE); + gtk_widget_show_all(GTK_WIDGET(gs->tool_bar)); + nsgtk_tab_init(gs); + + gtk_widget_set_size_request(GTK_WIDGET( + gs->buttons[HISTORY_BUTTON]->button), 20, -1); + + /* create the local history window to be associated with this scaffold */ + gs->history_window = malloc(sizeof(struct gtk_history_window)); + gs->history_window->g = gs; + gs->history_window->window = + GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + gtk_window_set_transient_for(gs->history_window->window, gs->window); + gtk_window_set_title(gs->history_window->window, "NetSurf History"); + gtk_window_set_type_hint(gs->history_window->window, + GDK_WINDOW_TYPE_HINT_UTILITY); + gs->history_window->scrolled = + GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(0, 0)); + gtk_container_add(GTK_CONTAINER(gs->history_window->window), + GTK_WIDGET(gs->history_window->scrolled)); + + gtk_widget_show(GTK_WIDGET(gs->history_window->scrolled)); + gs->history_window->drawing_area = + GTK_DRAWING_AREA(gtk_drawing_area_new()); + + gtk_widget_set_events(GTK_WIDGET(gs->history_window->drawing_area), + GDK_EXPOSURE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK); + nsgtk_widget_override_background_color(GTK_WIDGET(gs->history_window->drawing_area), + GTK_STATE_NORMAL, + 0, 0xffff, 0xffff, 0xffff); + nsgtk_scrolled_window_add_with_viewport(gs->history_window->scrolled, + GTK_WIDGET(gs->history_window->drawing_area)); + gtk_widget_show(GTK_WIDGET(gs->history_window->drawing_area)); + + + /* set up URL bar completion */ + gs->url_bar_completion = nsgtk_url_entry_completion_new(gs); + + /* set up the throbber. */ + gs->throb_frame = 0; + + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + /* connect history window signals to their handlers */ + nsgtk_connect_draw_event(GTK_WIDGET(gs->history_window->drawing_area), + G_CALLBACK(nsgtk_history_draw_event), + gs->history_window); + /*CONNECT(gs->history_window->drawing_area, "motion_notify_event", + nsgtk_history_motion_notify_event, gs->history_window);*/ + CONNECT(gs->history_window->drawing_area, "button_press_event", + nsgtk_history_button_press_event, gs->history_window); + CONNECT(gs->history_window->window, "delete_event", + gtk_widget_hide_on_delete, NULL); + + g_signal_connect_after(gs->notebook, "page-added", + G_CALLBACK(nsgtk_window_tabs_add), gs); + g_signal_connect_after(gs->notebook, "page-removed", + G_CALLBACK(nsgtk_window_tabs_remove), gs); + + /* connect main window signals to their handlers. */ + CONNECT(gs->window, "delete-event", + scaffolding_window_delete_event, gs); + + CONNECT(gs->window, "destroy", scaffolding_window_destroy, gs); + + /* toolbar URL bar menu bar search bar signal handlers */ + CONNECT(gs->menu_bar->edit_submenu->edit, "show", + nsgtk_window_edit_menu_clicked, gs); + CONNECT(gs->menu_bar->edit_submenu->edit, "hide", + nsgtk_window_edit_menu_hidden, gs); + + CONNECT(gs->search->buttons[1], "clicked", + nsgtk_search_forward_button_clicked, gs); + + CONNECT(gs->search->buttons[0], "clicked", + nsgtk_search_back_button_clicked, gs); + + CONNECT(gs->search->entry, "changed", nsgtk_search_entry_changed, gs); + + CONNECT(gs->search->entry, "activate", nsgtk_search_entry_activate, gs); + + CONNECT(gs->search->entry, "key-press-event", + nsgtk_search_entry_key, gs); + + CONNECT(gs->search->buttons[2], "clicked", + nsgtk_search_close_button_clicked, gs); + + CONNECT(gs->search->caseSens, "toggled", + nsgtk_search_entry_changed, gs); + + CONNECT(gs->tool_bar, "popup-context-menu", + nsgtk_window_tool_bar_clicked, gs); + + /* create popup menu */ + gs->menu_popup = nsgtk_new_scaffolding_popup(gs, group); + + gs->link_menu = nsgtk_new_scaffolding_link_popup(gs, group); + + /* set up the menu signal handlers */ + nsgtk_scaffolding_toolbar_init(gs); + nsgtk_toolbar_connect_all(gs); + nsgtk_attach_menu_handlers(gs); + + nsgtk_scaffolding_initial_sensitivity(gs); + + gs->fullscreen = false; + + /* attach to the list */ + if (scaf_list) { + scaf_list->prev = gs; + } + gs->next = scaf_list; + gs->prev = NULL; + scaf_list = gs; + + /* set icon images */ + nsgtk_theme_implement(gs); + + /* set web search provider */ + search_web_select_provider(nsoption_int(search_provider)); + + /* finally, show the window. */ + gtk_widget_show(GTK_WIDGET(gs->window)); + + LOG("creation complete"); + + return gs; +} + +/* exported function documented in gtk/scaffolding.h */ +void nsgtk_window_set_title(struct gui_window *gw, const char *title) +{ + struct nsgtk_scaffolding *gs = nsgtk_get_scaffold(gw); + int title_len; + char *newtitle; + + if ((title == NULL) || (title[0] == '\0')) { + if (gs->top_level != gw) { + gtk_window_set_title(gs->window, "NetSurf"); + } + return; + } + + nsgtk_tab_set_title(gw, title); + + if (gs->top_level != gw) { + /* not top level window so do not set window title */ + return; + } + + title_len = strlen(title) + SLEN(" - NetSurf") + 1; + newtitle = malloc(title_len); + if (newtitle == NULL) { + return; + } + + snprintf(newtitle, title_len, "%s - NetSurf", title); + + gtk_window_set_title(gs->window, newtitle); + + free(newtitle); +} + + +nserror gui_window_set_url(struct gui_window *gw, nsurl *url) +{ + struct nsgtk_scaffolding *g; + size_t idn_url_l; + char *idn_url_s = NULL; + + g = nsgtk_get_scaffold(gw); + if (g->top_level == gw) { + if (nsoption_bool(display_decoded_idn) == true) { + if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) != NSERROR_OK) + idn_url_s = NULL; + } + + gtk_entry_set_text(GTK_ENTRY(g->url_bar), idn_url_s ? idn_url_s : nsurl_access(url)); + + if(idn_url_s) + free(idn_url_s); + + gtk_editable_set_position(GTK_EDITABLE(g->url_bar), -1); + } + return NSERROR_OK; +} + +void gui_window_start_throbber(struct gui_window* _g) +{ + struct nsgtk_scaffolding *g = nsgtk_get_scaffold(_g); + g->buttons[STOP_BUTTON]->sensitivity = true; + g->buttons[RELOAD_BUTTON]->sensitivity = false; + nsgtk_scaffolding_set_sensitivity(g); + + scaffolding_update_context(g); + + nsgtk_schedule(100, nsgtk_throb, g); +} + +void gui_window_stop_throbber(struct gui_window* _g) +{ + struct nsgtk_scaffolding *g = nsgtk_get_scaffold(_g); + if (g == NULL) + return; + scaffolding_update_context(g); + nsgtk_schedule(-1, nsgtk_throb, g); + if (g->buttons[STOP_BUTTON] != NULL) + g->buttons[STOP_BUTTON]->sensitivity = false; + if (g->buttons[RELOAD_BUTTON] != NULL) + g->buttons[RELOAD_BUTTON]->sensitivity = true; + + nsgtk_scaffolding_set_sensitivity(g); + + if ((g->throbber == NULL) || (nsgtk_throbber == NULL) || + (nsgtk_throbber->framedata == NULL) || + (nsgtk_throbber->framedata[0] == NULL)) + return; + gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[0]); +} + + +/** + * set favicon + */ +void +nsgtk_scaffolding_set_icon(struct gui_window *gw) +{ + struct nsgtk_scaffolding *sc = nsgtk_get_scaffold(gw); + GdkPixbuf *icon_pixbuf = nsgtk_get_icon(gw); + + /* check icon needs to be shown */ + if ((icon_pixbuf == NULL) || + (sc->top_level != gw)) { + return; + } + + nsgtk_entry_set_icon_from_pixbuf(sc->url_bar, + GTK_ENTRY_ICON_PRIMARY, + icon_pixbuf); + + gtk_widget_show_all(GTK_WIDGET(sc->buttons[URL_BAR_ITEM]->button)); +} + +static void +nsgtk_scaffolding_set_websearch(struct nsgtk_scaffolding *g, const char *content) +{ + /** \todo this code appears technically correct, though + * currently has no effect at all. + */ + PangoLayout *lo = gtk_entry_get_layout(GTK_ENTRY(g->webSearchEntry)); + if (lo != NULL) { + pango_layout_set_font_description(lo, NULL); + PangoFontDescription *desc = pango_font_description_new(); + if (desc != NULL) { + pango_font_description_set_style(desc, + PANGO_STYLE_ITALIC); + pango_font_description_set_family(desc, "Arial"); + pango_font_description_set_weight(desc, + PANGO_WEIGHT_ULTRALIGHT); + pango_font_description_set_size(desc, + 10 * PANGO_SCALE); + pango_layout_set_font_description(lo, desc); + } + + PangoAttrList *list = pango_attr_list_new(); + if (list != NULL) { + PangoAttribute *italic = pango_attr_style_new( + PANGO_STYLE_ITALIC); + if (italic != NULL) { + italic->start_index = 0; + italic->end_index = strlen(content); + } + PangoAttribute *grey = pango_attr_foreground_new( + 0x7777, 0x7777, 0x7777); + if (grey != NULL) { + grey->start_index = 0; + grey->end_index = strlen(content); + } + pango_attr_list_insert(list, italic); + pango_attr_list_insert(list, grey); + pango_layout_set_attributes(lo, list); + pango_attr_list_unref(list); + } + pango_layout_set_text(lo, content, -1); + } +/* an alternative method */ +/* char *parse = malloc(strlen(content) + 1); + PangoAttrList *list = pango_layout_get_attributes(lo); + char *markup = g_strconcat("", content, + "", NULL); + pango_parse_markup(markup, -1, 0, &list, &parse, NULL, NULL); + gtk_widget_show_all(g->webSearchEntry); +*/ + gtk_entry_set_visibility(GTK_ENTRY(g->webSearchEntry), TRUE); + gtk_entry_set_text(GTK_ENTRY(g->webSearchEntry), content); +} + +/** + * GTK UI callback when search provider details are updated. + * + * \param provider_name The providers name. + * \param provider_bitmap The bitmap representing the provider. + * \return NSERROR_OK on success else error code. + */ +static nserror +gui_search_web_provider_update(const char *provider_name, + struct bitmap *provider_bitmap) +{ + struct nsgtk_scaffolding *current; + GdkPixbuf *srch_pixbuf = NULL; + char *searchcontent; + + LOG("name:%s bitmap %p", provider_name, provider_bitmap); + + if (provider_bitmap != NULL) { + srch_pixbuf = nsgdk_pixbuf_get_from_surface(provider_bitmap->surface, 16, 16); + + if (srch_pixbuf == NULL) { + return NSERROR_NOMEM; + } + } + + /* setup the search content name */ + searchcontent = malloc(strlen(provider_name) + SLEN("Search ") + 1); + if (searchcontent != NULL) { + sprintf(searchcontent, "Search %s", provider_name); + } + + /* set the search provider parameters up in each scaffold */ + for (current = scaf_list; current != NULL; current = current->next) { + /* add ico to each window's toolbar */ + if (srch_pixbuf != NULL) { + nsgtk_entry_set_icon_from_pixbuf(current->webSearchEntry, + GTK_ENTRY_ICON_PRIMARY, + srch_pixbuf); + } else { + nsgtk_entry_set_icon_from_stock(current->webSearchEntry, + GTK_ENTRY_ICON_PRIMARY, + NSGTK_STOCK_FIND); + } + + /* set search entry text */ + if (searchcontent != NULL) { + nsgtk_scaffolding_set_websearch(current, searchcontent); + } else { + nsgtk_scaffolding_set_websearch(current, provider_name); + } + } + + free(searchcontent); + + if (srch_pixbuf != NULL) { + g_object_unref(srch_pixbuf); + } + + return NSERROR_OK; +} + +static struct gui_search_web_table search_web_table = { + .provider_update = gui_search_web_provider_update, +}; + +struct gui_search_web_table *nsgtk_search_web_table = &search_web_table; + +/* exported interface documented in gtk/scaffolding.h */ +GtkWindow* nsgtk_scaffolding_window(struct nsgtk_scaffolding *g) +{ + return g->window; +} + +/* exported interface documented in gtk/scaffolding.h */ +GtkNotebook* nsgtk_scaffolding_notebook(struct nsgtk_scaffolding *g) +{ + return g->notebook; +} + +/* exported interface documented in gtk/scaffolding.h */ +GtkWidget *nsgtk_scaffolding_urlbar(struct nsgtk_scaffolding *g) +{ + return g->url_bar; +} + +/* exported interface documented in gtk/scaffolding.h */ +GtkWidget *nsgtk_scaffolding_websearch(struct nsgtk_scaffolding *g) +{ + return g->webSearchEntry; +} + +/* exported interface documented in gtk/scaffolding.h */ +GtkToolbar *nsgtk_scaffolding_toolbar(struct nsgtk_scaffolding *g) +{ + return g->tool_bar; +} + +/* exported interface documented in gtk/scaffolding.h */ +struct nsgtk_button_connect * +nsgtk_scaffolding_button(struct nsgtk_scaffolding *g, int i) +{ + return g->buttons[i]; +} + +/* exported interface documented in gtk/scaffolding.h */ +struct gtk_search *nsgtk_scaffolding_search(struct nsgtk_scaffolding *g) +{ + return g->search; +} + +/* exported interface documented in gtk/scaffolding.h */ +GtkMenuBar *nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding *g) +{ + return g->menu_bar->bar_menu; +} + +/* exported interface documented in gtk/scaffolding.h */ +struct gtk_history_window * +nsgtk_scaffolding_history_window(struct nsgtk_scaffolding *g) +{ + return g->history_window; +} + +/* exported interface documented in gtk/scaffolding.h */ +struct nsgtk_scaffolding *nsgtk_scaffolding_iterate(struct nsgtk_scaffolding *g) +{ + if (g == NULL) { + return scaf_list; + } + return g->next; +} + +/* exported interface documented in gtk/scaffolding.h */ +void nsgtk_scaffolding_reset_offset(struct nsgtk_scaffolding *g) +{ + g->offset = 0; +} + +/* exported interface documented in gtk/scaffolding.h */ +void nsgtk_scaffolding_update_url_bar_ref(struct nsgtk_scaffolding *g) +{ + g->url_bar = GTK_WIDGET(gtk_bin_get_child(GTK_BIN( + nsgtk_scaffolding_button(g, URL_BAR_ITEM)->button))); + + gtk_entry_set_completion(GTK_ENTRY(g->url_bar), + g->url_bar_completion); +} + +/* exported interface documented in gtk/scaffolding.h */ +void nsgtk_scaffolding_update_throbber_ref(struct nsgtk_scaffolding *g) +{ + g->throbber = GTK_IMAGE(gtk_bin_get_child( + GTK_BIN(g->buttons[THROBBER_ITEM]->button))); +} + +/* exported interface documented in gtk/scaffolding.h */ +void nsgtk_scaffolding_update_websearch_ref(struct nsgtk_scaffolding *g) +{ + g->webSearchEntry = gtk_bin_get_child(GTK_BIN( + g->buttons[WEBSEARCH_ITEM]->button)); +} + +/* exported interface documented in gtk/scaffolding.h */ +void nsgtk_scaffolding_toggle_search_bar_visibility(struct nsgtk_scaffolding *g) +{ + gboolean vis; + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); + + g_object_get(G_OBJECT(g->search->bar), "visible", &vis, NULL); + if (vis) { + if (bw != NULL) { + browser_window_search_clear(bw); + } + + gtk_widget_hide(GTK_WIDGET(g->search->bar)); + } else { + gtk_widget_show(GTK_WIDGET(g->search->bar)); + gtk_widget_grab_focus(GTK_WIDGET(g->search->entry)); + } +} + +/* exported interface documented in gtk/scaffolding.h */ +struct gui_window *nsgtk_scaffolding_top_level(struct nsgtk_scaffolding *g) +{ + return g->top_level; +} + +/* exported interface documented in gtk/scaffolding.h */ +void nsgtk_scaffolding_set_top_level(struct gui_window *gw) +{ + struct browser_window *bw; + struct nsgtk_scaffolding *sc; + + assert(gw != NULL); + + bw = nsgtk_get_browser_window(gw); + + assert(bw != NULL); + + sc = nsgtk_get_scaffold(gw); + assert(sc != NULL); + + sc->top_level = gw; + + /* Synchronise the history (will also update the URL bar) */ + scaffolding_update_context(sc); + + /* clear effects of potential searches */ + browser_window_search_clear(bw); + + nsgtk_scaffolding_set_icon(gw); + + /* Ensure the window's title bar is updated */ + nsgtk_window_set_title(gw, browser_window_get_title(bw)); + +} + +/* exported interface documented in scaffolding.h */ +void nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding *g) +{ + int i; +#define SENSITIVITY(q)\ + i = q##_BUTTON;\ + if (g->buttons[i]->main != NULL)\ + gtk_widget_set_sensitive(GTK_WIDGET(\ + g->buttons[i]->main),\ + g->buttons[i]->sensitivity);\ + if (g->buttons[i]->rclick != NULL)\ + gtk_widget_set_sensitive(GTK_WIDGET(\ + g->buttons[i]->rclick),\ + g->buttons[i]->sensitivity);\ + if ((g->buttons[i]->location != -1) && \ + (g->buttons[i]->button != NULL))\ + gtk_widget_set_sensitive(GTK_WIDGET(\ + g->buttons[i]->button),\ + g->buttons[i]->sensitivity);\ + if (g->buttons[i]->popup != NULL)\ + gtk_widget_set_sensitive(GTK_WIDGET(\ + g->buttons[i]->popup),\ + g->buttons[i]->sensitivity); + + SENSITIVITY(STOP) + SENSITIVITY(RELOAD) + SENSITIVITY(CUT) + SENSITIVITY(COPY) + SENSITIVITY(PASTE) + SENSITIVITY(BACK) + SENSITIVITY(FORWARD) + SENSITIVITY(NEXTTAB) + SENSITIVITY(PREVTAB) + SENSITIVITY(CLOSETAB) +#undef SENSITIVITY +} + + +/* exported interface documented in gtk/scaffolding.h */ +void nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding *g, + gdouble x, + gdouble y) +{ + GtkMenu *gtkmenu; + + /* update the global context menu features */ + browser_window_get_features(nsgtk_get_browser_window(g->top_level), + x, y, ¤t_menu_features); + + if (current_menu_features.link != NULL) { + /* menu is opening over a link */ + gtkmenu = g->link_menu->link_menu; + } else { + gtkmenu = g->menu_popup->popup_menu; + + nsgtk_scaffolding_update_edit_actions_sensitivity(g); + + if (!(g->buttons[COPY_BUTTON]->sensitivity)) { + gtk_widget_hide(GTK_WIDGET(g->menu_popup->copy_menuitem)); + } else { + gtk_widget_show(GTK_WIDGET(g->menu_popup->copy_menuitem)); + } + + if (!(g->buttons[CUT_BUTTON]->sensitivity)) { + gtk_widget_hide(GTK_WIDGET(g->menu_popup->cut_menuitem)); + } else { + gtk_widget_show(GTK_WIDGET(g->menu_popup->cut_menuitem)); + } + + if (!(g->buttons[PASTE_BUTTON]->sensitivity)) { + gtk_widget_hide(GTK_WIDGET(g->menu_popup->paste_menuitem)); + } else { + gtk_widget_show(GTK_WIDGET(g->menu_popup->paste_menuitem)); + } + + /* hide customize */ + popup_menu_hide(g->menu_popup, false, false, false, true); + } + + gtk_menu_popup(gtkmenu, NULL, NULL, NULL, NULL, 0, + gtk_get_current_event_time()); +} + +/** + * reallocate width for history button, reallocate buttons right of history; + * memorise base of history button / toolbar + */ +void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget, + GtkAllocation *alloc, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + int i = nsgtk_toolbar_get_id_from_widget(widget, g); + if (i == -1) + return; + if ((g->toolbarmem == alloc->x) || + (g->buttons[i]->location < + g->buttons[HISTORY_BUTTON]->location)) + /* no reallocation after first adjustment, no reallocation for buttons + * left of history button */ + return; + if (widget == GTK_WIDGET(g->buttons[HISTORY_BUTTON]->button)) { + if (alloc->width == 20) + return; + + g->toolbarbase = alloc->y + alloc->height; + g->historybase = alloc->x + 20; + if (g->offset == 0) + g->offset = alloc->width - 20; + alloc->width = 20; + } else if (g->buttons[i]->location <= + g->buttons[URL_BAR_ITEM]->location) { + alloc->x -= g->offset; + if (i == URL_BAR_ITEM) + alloc->width += g->offset; + } + g->toolbarmem = alloc->x; + gtk_widget_size_allocate(widget, alloc); +} diff --git a/frontends/gtk/scaffolding.h b/frontends/gtk/scaffolding.h new file mode 100644 index 000000000..e1fd9bf2a --- /dev/null +++ b/frontends/gtk/scaffolding.h @@ -0,0 +1,252 @@ +/* + * Copyright 2005 James Bursa + * + * 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 . + */ + +#ifndef NETSURF_GTK_SCAFFOLDING_H +#define NETSURF_GTK_SCAFFOLDING_H 1 + +#include +#include "utils/errors.h" + +struct bitmap; +struct hlcache_handle; +struct gui_window; +struct gui_search_web_table; +struct nsurl; + +extern struct gui_search_web_table *nsgtk_search_web_table; + +typedef enum { + BACK_BUTTON = 0, + HISTORY_BUTTON, + FORWARD_BUTTON, + STOP_BUTTON, + RELOAD_BUTTON, + HOME_BUTTON, + URL_BAR_ITEM, + WEBSEARCH_ITEM, + THROBBER_ITEM, + NEWWINDOW_BUTTON, + NEWTAB_BUTTON, + OPENFILE_BUTTON, + CLOSETAB_BUTTON, + CLOSEWINDOW_BUTTON, + SAVEPAGE_BUTTON, + PDF_BUTTON, + PLAINTEXT_BUTTON, + DRAWFILE_BUTTON, + POSTSCRIPT_BUTTON, + PRINTPREVIEW_BUTTON, + PRINT_BUTTON, + QUIT_BUTTON, + CUT_BUTTON, + COPY_BUTTON, + PASTE_BUTTON, + DELETE_BUTTON, + SELECTALL_BUTTON, + FIND_BUTTON, + PREFERENCES_BUTTON, + ZOOMPLUS_BUTTON, + ZOOMMINUS_BUTTON, + ZOOMNORMAL_BUTTON, + FULLSCREEN_BUTTON, + VIEWSOURCE_BUTTON, + DOWNLOADS_BUTTON, + SAVEWINDOWSIZE_BUTTON, + TOGGLEDEBUGGING_BUTTON, + SAVEBOXTREE_BUTTON, + SAVEDOMTREE_BUTTON, + LOCALHISTORY_BUTTON, + GLOBALHISTORY_BUTTON, + ADDBOOKMARKS_BUTTON, + SHOWBOOKMARKS_BUTTON, + SHOWCOOKIES_BUTTON, + OPENLOCATION_BUTTON, + NEXTTAB_BUTTON, + PREVTAB_BUTTON, + CONTENTS_BUTTON, + GUIDE_BUTTON, + INFO_BUTTON, + ABOUT_BUTTON, + PLACEHOLDER_BUTTON /* size indicator; array maximum indices */ +} nsgtk_toolbar_button; /* PLACEHOLDER_BUTTON - 1 */ + +struct gtk_history_window { + struct nsgtk_scaffolding *g; + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; +}; + +struct gtk_search { + GtkToolbar *bar; + GtkEntry *entry; + GtkToolButton *buttons[3]; /* back, forward, */ + GtkCheckButton *checkAll; /* close */ + GtkCheckButton *caseSens; +}; + +struct nsgtk_button_connect { + GtkToolItem *button; + int location; /* in toolbar */ + bool sensitivity; + GtkWidget *main; /* left click menu entry */ + GtkWidget *rclick; /* right click menu */ + GtkWidget *popup; /* popup menu entry */ + void *mhandler; /* menu item clicked */ + void *bhandler; /* button clicked */ + void *dataplus; /* customization -> toolbar */ + void *dataminus; /* customization -> store */ +}; + +/** + * create a new scaffolding for a window. + * + * \param gw The gui window to create the new scaffold around. + * \return The newly constructed scaffold or NULL on error. + */ +struct nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *gw); + +/** + * Obtain the most recently used scaffolding element. + * + * This allows tabs to be opened in the most recently used window + */ +struct nsgtk_scaffolding *nsgtk_current_scaffolding(void); + +/* acessors for gtk elements within a scaffold */ + +/** + * Get the gtk window for a scaffolding. + */ +GtkWindow *nsgtk_scaffolding_window(struct nsgtk_scaffolding *g); + +/** + * Get the gtk notebook from a scaffold. + */ +GtkNotebook *nsgtk_scaffolding_notebook(struct nsgtk_scaffolding *g); + +/** + * Get the gtk url bar from a scaffold. + */ +GtkWidget *nsgtk_scaffolding_urlbar(struct nsgtk_scaffolding *g); + +/** + * Get the gtk web search entry from a scaffold. + */ +GtkWidget *nsgtk_scaffolding_websearch(struct nsgtk_scaffolding *g); + +/** + * Get the gtk toolbar from a scaffold. + */ +GtkToolbar *nsgtk_scaffolding_toolbar(struct nsgtk_scaffolding *g); + + +struct nsgtk_button_connect *nsgtk_scaffolding_button(struct nsgtk_scaffolding *g, int i); + +struct gtk_search *nsgtk_scaffolding_search(struct nsgtk_scaffolding *g); + +GtkMenuBar *nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding *g); + +struct gtk_history_window *nsgtk_scaffolding_history_window(struct nsgtk_scaffolding *g); + +struct gui_window *nsgtk_scaffolding_top_level(struct nsgtk_scaffolding *g); + +/** + * reset the scaffold offset value to 0. + * + * \todo The value is only ever altered in + * nsgtk_scaffolding_toolbar_size_allocate and is something to do with + * the history button either clarify or remove! + */ +void nsgtk_scaffolding_reset_offset(struct nsgtk_scaffolding *g); + +/** + * Iterate through available scaffolding. + */ +struct nsgtk_scaffolding *nsgtk_scaffolding_iterate(struct nsgtk_scaffolding *g); + +void nsgtk_scaffolding_update_url_bar_ref(struct nsgtk_scaffolding *g); + +void nsgtk_scaffolding_update_throbber_ref(struct nsgtk_scaffolding *g); + +void nsgtk_scaffolding_update_websearch_ref(struct nsgtk_scaffolding *g); + +void nsgtk_scaffolding_toggle_search_bar_visibility(struct nsgtk_scaffolding *g); + +/** + * Set the current active top level gui window. + */ +void nsgtk_scaffolding_set_top_level(struct gui_window *g); + +/** + * update the sensitivity of context sensitive UI elements + * + * widgets altered in arrays: + * main + * right click menu + * location + * popup + * current arrays are: + * stop + * reload + * cut + * copy + * paste + * back + * forward + * nexttab + * prevtab + * closetab + */ +void nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding *g); + +/** + * Open a context sensitive menu. + * + * \param g the scaffolding containing the browser window. + * \param x The x co-ordinate. + * \param y The y co-ordinate. + */ +void nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding *g, gdouble x, gdouble y); + +void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget, GtkAllocation *alloc, gpointer data); + +void nsgtk_scaffolding_set_icon(struct gui_window *gw); + +gboolean nsgtk_window_url_activate_event(GtkWidget *, gpointer); + +gboolean nsgtk_window_url_changed(GtkWidget *, GdkEventKey *, gpointer); + +nserror nsgtk_scaffolding_new_tab(struct gui_window *gw); + +/* core acessors */ +/** + * set the title in the window + * + * \param gw The gui window to set title on + * \param title The title to set which may be NULL + */ +void nsgtk_window_set_title(struct gui_window *gw, const char *title); + +nserror gui_window_set_url(struct gui_window *g, struct nsurl *url); +void gui_window_start_throbber(struct gui_window *g); +void gui_window_stop_throbber(struct gui_window *g); + +void nsgtk_scaffolding_toolbars(struct nsgtk_scaffolding *g, int tbi); + +#endif /* NETSURF_GTK_SCAFFOLDING_H */ diff --git a/frontends/gtk/schedule.c b/frontends/gtk/schedule.c new file mode 100644 index 000000000..cf0333388 --- /dev/null +++ b/frontends/gtk/schedule.c @@ -0,0 +1,136 @@ +/* + * Copyright 2006-2007 Daniel Silverstone + * + * 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 . + */ + +#include +#include +#include + +#include "utils/errors.h" + +#include "gtk/schedule.h" + +#ifdef DEBUG_GTK_SCHEDULE +#include "utils/log.h" +#else +#define LOG(format, args...) ((void) 0) +#endif + +/** Killable callback closure embodiment. */ +typedef struct { + void (*callback)(void *); /**< The callback function. */ + void *context; /**< The context for the callback. */ + bool callback_killed; /**< Whether or not this was killed. */ +} _nsgtk_callback_t; + +/** List of callbacks which have occurred and are pending running. */ +static GList *pending_callbacks = NULL; +/** List of callbacks which are queued to occur in the future. */ +static GList *queued_callbacks = NULL; +/** List of callbacks which are about to be run in this ::schedule_run. */ +static GList *this_run = NULL; + +static gboolean +nsgtk_schedule_generic_callback(gpointer data) +{ + _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(data); + if (cb->callback_killed) { + /* This callback instance has been killed. */ + LOG("CB at %p already dead.", cb); + } + queued_callbacks = g_list_remove(queued_callbacks, cb); + pending_callbacks = g_list_append(pending_callbacks, cb); + return FALSE; +} + +static void +nsgtk_schedule_kill_callback(void *_target, void *_match) +{ + _nsgtk_callback_t *target = (_nsgtk_callback_t *)_target; + _nsgtk_callback_t *match = (_nsgtk_callback_t *)_match; + if ((target->callback == match->callback) && + (target->context == match->context)) { + LOG("Found match for %p(%p), killing.", target->callback, target->context); + target->callback = NULL; + target->context = NULL; + target->callback_killed = true; + } +} + +static void +schedule_remove(void (*callback)(void *p), void *p) +{ + _nsgtk_callback_t cb_match = { + .callback = callback, + .context = p, + }; + + g_list_foreach(queued_callbacks, + nsgtk_schedule_kill_callback, &cb_match); + g_list_foreach(pending_callbacks, + nsgtk_schedule_kill_callback, &cb_match); + g_list_foreach(this_run, + nsgtk_schedule_kill_callback, &cb_match); +} + +/* exported interface documented in gtk/schedule.h */ +nserror nsgtk_schedule(int t, void (*callback)(void *p), void *p) +{ + _nsgtk_callback_t *cb; + + /* Kill any pending schedule of this kind. */ + schedule_remove(callback, p); + + if (t < 0) { + return NSERROR_OK; + } + + cb = malloc(sizeof(_nsgtk_callback_t)); + cb->callback = callback; + cb->context = p; + cb->callback_killed = false; + /* Prepend is faster right now. */ + queued_callbacks = g_list_prepend(queued_callbacks, cb); + g_timeout_add(t, nsgtk_schedule_generic_callback, cb); + + return NSERROR_OK; +} + +bool +schedule_run(void) +{ + /* Capture this run of pending callbacks into the list. */ + this_run = pending_callbacks; + + if (this_run == NULL) + return false; /* Nothing to do */ + + /* Clear the pending list. */ + pending_callbacks = NULL; + + LOG("Captured a run of %d callbacks to fire.", g_list_length(this_run)); + + /* Run all the callbacks which made it this far. */ + while (this_run != NULL) { + _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(this_run->data); + this_run = g_list_remove(this_run, this_run->data); + if (!cb->callback_killed) + cb->callback(cb->context); + free(cb); + } + return true; +} diff --git a/frontends/gtk/schedule.h b/frontends/gtk/schedule.h new file mode 100644 index 000000000..0a2d724d4 --- /dev/null +++ b/frontends/gtk/schedule.h @@ -0,0 +1,26 @@ +/* + * Copyright 2006 Daniel Silverstone + * + * 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 . + */ + +#ifndef NETSURF_GTK_CALLBACK_H +#define NETSURF_GTK_CALLBACK_H 1 + +nserror nsgtk_schedule(int t, void (*callback)(void *p), void *p); + +bool schedule_run(void); + +#endif /* NETSURF_GTK_CALLBACK_H */ diff --git a/frontends/gtk/search.c b/frontends/gtk/search.c new file mode 100644 index 000000000..d2adcf1b5 --- /dev/null +++ b/frontends/gtk/search.c @@ -0,0 +1,245 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + + + /** \file + * Free text search (front component) + */ +#include +#include +#include +#include + +#include "utils/config.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "content/content.h" +#include "content/hlcache.h" +#include "desktop/browser.h" +#include "desktop/search.h" +#include "desktop/searchweb.h" +#include "desktop/gui_search.h" + +#include "gtk/warn.h" +#include "gtk/compat.h" +#include "gtk/search.h" +#include "gtk/scaffolding.h" +#include "gtk/window.h" + +/** + * activate search forwards button in gui. + * + * \param active activate/inactivate + * \param gw The gui window in which to activite the search button in. + */ +static void nsgtk_search_set_forward_state(bool active, struct gui_window *gw) +{ + if (gw != NULL && nsgtk_get_browser_window(gw) != NULL) { + struct nsgtk_scaffolding *g = nsgtk_get_scaffold(gw); + gtk_widget_set_sensitive( + GTK_WIDGET(nsgtk_scaffolding_search(g)->buttons[1]), + active); + } +} + +/** + * activate search back button in gui. + * + * \param active activate/inactivate + * \param gw The gui window in which to activite the search button in. + */ +static void nsgtk_search_set_back_state(bool active, struct gui_window *gw) +{ + if (gw != NULL && nsgtk_get_browser_window(gw) != NULL) { + struct nsgtk_scaffolding *g = nsgtk_get_scaffold(gw); + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_search( + g)->buttons[0]), active); + } +} + +/** connected to the search forward button */ + +gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + struct gui_window *gw = nsgtk_scaffolding_top_level(g); + struct browser_window *bw = nsgtk_get_browser_window(gw); + + assert(bw); + + search_flags_t flags = SEARCH_FLAG_FORWARDS | + (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->caseSens)) ? + SEARCH_FLAG_CASE_SENSITIVE : 0) | + (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->checkAll)) ? + SEARCH_FLAG_SHOWALL : 0); + + browser_window_search(bw, gw, flags, + gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry)); + return TRUE; +} + +/** connected to the search back button */ + +gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + struct gui_window *gw = nsgtk_scaffolding_top_level(g); + struct browser_window *bw = nsgtk_get_browser_window(gw); + + assert(bw); + + search_flags_t flags = 0 |(gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->caseSens)) ? + SEARCH_FLAG_CASE_SENSITIVE : 0) | + (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->checkAll)) ? + SEARCH_FLAG_SHOWALL : 0); + + browser_window_search(bw, gw, flags, + gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry)); + return TRUE; +} + +/** connected to the search close button */ + +gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + nsgtk_scaffolding_toggle_search_bar_visibility(g); + return TRUE; +} + +/** connected to the search entry [typing] */ + +gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + struct gui_window *gw = nsgtk_scaffolding_top_level(g); + struct browser_window *bw = nsgtk_get_browser_window(gw); + search_flags_t flags; + + assert(bw != NULL); + + nsgtk_search_set_forward_state(true, gw); + nsgtk_search_set_back_state(true, gw); + + flags = SEARCH_FLAG_FORWARDS | + (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->caseSens)) ? + SEARCH_FLAG_CASE_SENSITIVE : 0) | + (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->checkAll)) ? + SEARCH_FLAG_SHOWALL : 0); + + browser_window_search(bw, gw, flags, + gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry)); + return TRUE; +} + +/** connected to the search entry [return key] */ + +gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + struct gui_window *gw = nsgtk_scaffolding_top_level(g); + struct browser_window *bw = nsgtk_get_browser_window(gw); + search_flags_t flags; + + assert(bw); + + flags = SEARCH_FLAG_FORWARDS | + (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->caseSens)) ? + SEARCH_FLAG_CASE_SENSITIVE : 0) | + (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + nsgtk_scaffolding_search(g)->checkAll)) ? + SEARCH_FLAG_SHOWALL : 0); + + browser_window_search(bw, gw, flags, + gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry)); + return FALSE; +} + +/** allows escape key to close search bar too */ + +gboolean +nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (event->keyval == GDK_KEY(Escape)) { + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + nsgtk_scaffolding_toggle_search_bar_visibility(g); + } + return FALSE; +} + +/** connected to the websearch entry [return key] */ + +gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = data; + nserror ret; + nsurl *url; + + ret = search_web_omni( + gtk_entry_get_text(GTK_ENTRY(nsgtk_scaffolding_websearch(g))), + SEARCH_WEB_OMNI_SEARCHONLY, + &url); + if (ret == NSERROR_OK) { + temp_open_background = 0; + ret = browser_window_create( + BW_CREATE_HISTORY | BW_CREATE_TAB, + url, + NULL, + nsgtk_get_browser_window(nsgtk_scaffolding_top_level(g)), + NULL); + temp_open_background = -1; + nsurl_unref(url); + } + if (ret != NSERROR_OK) { + nsgtk_warning(messages_get_errorcode(ret), 0); + } + + return TRUE; +} + +/** + * allows a click in the websearch entry field to clear the name of the + * provider + */ + +gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f, + gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + gtk_editable_select_region(GTK_EDITABLE( + nsgtk_scaffolding_websearch(g)), 0, -1); + gtk_widget_grab_focus(GTK_WIDGET(nsgtk_scaffolding_websearch(g))); + return TRUE; +} + + + +static struct gui_search_table search_table = { + .forward_state = (void *)nsgtk_search_set_forward_state, + .back_state = (void *)nsgtk_search_set_back_state, +}; + +struct gui_search_table *nsgtk_search_table = &search_table; diff --git a/frontends/gtk/search.h b/frontends/gtk/search.h new file mode 100644 index 000000000..dd8c60d0f --- /dev/null +++ b/frontends/gtk/search.h @@ -0,0 +1,36 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + +#ifndef _NETSURF_GTK_SEARCH_H_ +#define _NETSURF_GTK_SEARCH_H_ + +struct gui_search_table *nsgtk_search_table; + +struct nsgtk_scaffolding; + +void nsgtk_search_bar_toggle_visibility(struct nsgtk_scaffolding * g); +gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data); +gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data); +gboolean nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event, gpointer data); +gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data); +gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data); +gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data); +gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data); +gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f, gpointer data); + +#endif diff --git a/frontends/gtk/selection.c b/frontends/gtk/selection.c new file mode 100644 index 000000000..d1388ed27 --- /dev/null +++ b/frontends/gtk/selection.c @@ -0,0 +1,96 @@ +/* + * Copyright 2008 Mike Lester + * + * 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 . + */ + +#include +#include +#include + +#include "utils/log.h" +#include "desktop/browser.h" +#include "desktop/gui_clipboard.h" + +#include "gtk/window.h" + +static GString *current_selection = NULL; +static GtkClipboard *clipboard; + + +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +static void gui_get_clipboard(char **buffer, size_t *length) +{ + gchar *gtext; + + *buffer = NULL; + *length = 0; + + /* get clipboard contents from gtk */ + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtext = gtk_clipboard_wait_for_text(clipboard); /* conv to utf-8 */ + + if (gtext == NULL) + return; + + *length = strlen(gtext); + *buffer = malloc(*length); + if (*buffer == NULL) { + *length = 0; + g_free(gtext); + return; + } + + memcpy(*buffer, gtext, *length); + + g_free(gtext); +} + + +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +static void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) +{ + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + if (!current_selection) + current_selection = g_string_new(NULL); + else + g_string_set_size(current_selection, 0); + + current_selection = g_string_append_len(current_selection, + buffer, length); + + gtk_clipboard_set_text(clipboard, current_selection->str, -1); +} + +static struct gui_clipboard_table clipboard_table = { + .get = gui_get_clipboard, + .set = gui_set_clipboard, +}; + +struct gui_clipboard_table *nsgtk_clipboard_table = &clipboard_table; diff --git a/frontends/gtk/selection.h b/frontends/gtk/selection.h new file mode 100644 index 000000000..6463692cf --- /dev/null +++ b/frontends/gtk/selection.h @@ -0,0 +1,24 @@ +/* + * Copyright 2008 Mike Lester + * + * 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 . + */ + +#ifndef GTK_SELECTION_H +#define GTK_SELECTION_H + +struct gui_clipboard_table *nsgtk_clipboard_table; + +#endif diff --git a/frontends/gtk/sexy_icon_entry.c b/frontends/gtk/sexy_icon_entry.c new file mode 100644 index 000000000..fff650cc4 --- /dev/null +++ b/frontends/gtk/sexy_icon_entry.c @@ -0,0 +1,982 @@ +/* + * libsexy/sexy-icon-entry.c Entry widget + * Copyright (C) 2004-2006 Christian Hammond. + * modified for NetSurf + * Copyright 2009 Mark Benjamin + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * This file before modifications was originally part of LibSexy, + * http://www.chipx86.com/; it is redistributed under GPLv2 + * + * 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 + * or write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "gtk/sexy_icon_entry.h" +#include "gtk/compat.h" + +#define ICON_MARGIN 2 +#define MAX_ICONS 2 + +#define IS_VALID_ICON_ENTRY_POSITION(pos) \ + ((pos) == SEXY_ICON_ENTRY_PRIMARY || \ + (pos) == SEXY_ICON_ENTRY_SECONDARY) + +typedef struct +{ + GtkImage *icon; + gboolean highlight; + gboolean hovered; + GdkWindow *window; + +} SexyIconInfo; + +struct _SexyIconEntryPriv +{ + SexyIconInfo icons[MAX_ICONS]; + + gulong icon_released_id; +}; + +enum +{ + ICON_PRESSED, + ICON_RELEASED, + LAST_SIGNAL +}; + +/* static void sexy_icon_entry_class_init(SexyIconEntryClass *klass); */ +static void sexy_icon_entry_editable_init(GtkEditableClass *iface); +/* static void sexy_icon_entry_init(SexyIconEntry *entry); */ +static void sexy_icon_entry_finalize(GObject *obj); +static void sexy_icon_entry_destroy(GtkObject *obj); +static void sexy_icon_entry_map(GtkWidget *widget); +static void sexy_icon_entry_unmap(GtkWidget *widget); +static void sexy_icon_entry_realize(GtkWidget *widget); +static void sexy_icon_entry_unrealize(GtkWidget *widget); +static void sexy_icon_entry_size_request(GtkWidget *widget, + GtkRequisition *requisition); +static void sexy_icon_entry_size_allocate(GtkWidget *widget, + GtkAllocation *allocation); +static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event); +static gint sexy_icon_entry_enter_notify(GtkWidget *widget, + GdkEventCrossing *event); +static gint sexy_icon_entry_leave_notify(GtkWidget *widget, + GdkEventCrossing *event); +static gint sexy_icon_entry_button_press(GtkWidget *widget, + GdkEventButton *event); +static gint sexy_icon_entry_button_release(GtkWidget *widget, + GdkEventButton *event); + +static GtkEntryClass *parent_class = NULL; +static guint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY, + 0, + G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE, + sexy_icon_entry_editable_init)); + +void +sexy_icon_entry_class_init(SexyIconEntryClass *klass) +{ + GObjectClass *gobject_class; + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkEntryClass *entry_class; + + parent_class = g_type_class_peek_parent(klass); + + gobject_class = G_OBJECT_CLASS(klass); + object_class = GTK_OBJECT_CLASS(klass); + widget_class = GTK_WIDGET_CLASS(klass); + entry_class = GTK_ENTRY_CLASS(klass); + + gobject_class->finalize = sexy_icon_entry_finalize; + + object_class->destroy = sexy_icon_entry_destroy; + + widget_class->map = sexy_icon_entry_map; + widget_class->unmap = sexy_icon_entry_unmap; + widget_class->realize = sexy_icon_entry_realize; + widget_class->unrealize = sexy_icon_entry_unrealize; + widget_class->size_request = sexy_icon_entry_size_request; + widget_class->size_allocate = sexy_icon_entry_size_allocate; + widget_class->expose_event = sexy_icon_entry_expose; + widget_class->enter_notify_event = sexy_icon_entry_enter_notify; + widget_class->leave_notify_event = sexy_icon_entry_leave_notify; + widget_class->button_press_event = sexy_icon_entry_button_press; + widget_class->button_release_event = sexy_icon_entry_button_release; + + /* + * SexyIconEntry::icon-pressed: + * @entry: The entry on which the signal is emitted. + * @icon_pos: The position of the clicked icon. + * @button: The mouse button clicked. + * + * The ::icon-pressed signal is emitted when an icon is clicked. + */ + /* signal modified to compile directly in NetSurf - param 8 of + * g_signal_new() changed from marshal type to NULL - so there may + * well be no working signal */ + signals[ICON_PRESSED] = + g_signal_new("icon_pressed", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed), + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); + + /* + * SexyIconEntry::icon-released: + * @entry: The entry on which the signal is emitted. + * @icon_pos: The position of the clicked icon. + * @button: The mouse button clicked. + * + * The ::icon-released signal is emitted on the button release from a + * mouse click. + */ + /* signal modified to compile directly in NetSurf - param 8 of + * g_signal_new() changed from marshal type to NULL - so there may + * well be no working signal */ + signals[ICON_RELEASED] = + g_signal_new("icon_released", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(SexyIconEntryClass, icon_released), + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); +} + +static void +sexy_icon_entry_editable_init(GtkEditableClass *iface) +{ +}; + +void +sexy_icon_entry_init(SexyIconEntry *entry) +{ + entry->priv = g_new0(SexyIconEntryPriv, 1); +} + +static void +sexy_icon_entry_finalize(GObject *obj) +{ + SexyIconEntry *entry; + + g_return_if_fail(obj != NULL); + g_return_if_fail(SEXY_IS_ICON_ENTRY(obj)); + + entry = SEXY_ICON_ENTRY(obj); + + g_free(entry->priv); + + if (G_OBJECT_CLASS(parent_class)->finalize) + G_OBJECT_CLASS(parent_class)->finalize(obj); +} + +static void +sexy_icon_entry_destroy(GtkObject *obj) +{ + SexyIconEntry *entry; + + entry = SEXY_ICON_ENTRY(obj); + + sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_PRIMARY, NULL); + sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_SECONDARY, NULL); + + if (GTK_OBJECT_CLASS(parent_class)->destroy) + GTK_OBJECT_CLASS(parent_class)->destroy(obj); +} + +static void +sexy_icon_entry_map(GtkWidget *widget) +{ + if (nsgtk_widget_get_realized(widget) && !nsgtk_widget_get_mapped(widget)) + { + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + int i; + + GTK_WIDGET_CLASS(parent_class)->map(widget); + + for (i = 0; i < MAX_ICONS; i++) + { + if (entry->priv->icons[i].icon != NULL) + gdk_window_show(entry->priv->icons[i].window); + } + } +} + +static void +sexy_icon_entry_unmap(GtkWidget *widget) +{ + if (nsgtk_widget_get_mapped(widget)) + { + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + int i; + + for (i = 0; i < MAX_ICONS; i++) + { + if (entry->priv->icons[i].icon != NULL) + gdk_window_hide(entry->priv->icons[i].window); + } + + GTK_WIDGET_CLASS(parent_class)->unmap(widget); + } +} + +static gint +get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos) +{ + GtkRequisition requisition; + gint menu_icon_width; + gint width; + SexyIconInfo *icon_info = &entry->priv->icons[icon_pos]; + + if (icon_info->icon == NULL) + return 0; + + gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition); + gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL); + + width = MAX(requisition.width, menu_icon_width); + + return width; +} + +static void +get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder) +{ + GtkWidget *widget = GTK_WIDGET(entry); + gint focus_width; + gboolean interior_focus; + + gtk_widget_style_get(widget, + "interior-focus", &interior_focus, + "focus-line-width", &focus_width, + NULL); + + if (gtk_entry_get_has_frame(GTK_ENTRY(entry))) + { + *xborder = widget->style->xthickness; + *yborder = widget->style->ythickness; + } + else + { + *xborder = 0; + *yborder = 0; + } + + if (!interior_focus) + { + *xborder += focus_width; + *yborder += focus_width; + } +} + +static void +get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc) +{ + GtkWidget *widget = GTK_WIDGET(entry); + GtkRequisition requisition; + gint xborder, yborder; + + gtk_widget_get_child_requisition(widget, &requisition); + get_borders(entry, &xborder, &yborder); + + alloc->x = xborder; + alloc->y = yborder; + alloc->width = widget->allocation.width - xborder * 2; + alloc->height = requisition.height - yborder * 2; +} + +static void +get_icon_allocation(SexyIconEntry *icon_entry, + gboolean left, + GtkAllocation *widget_alloc, + GtkAllocation *text_area_alloc, + GtkAllocation *allocation, + SexyIconEntryPosition *icon_pos) +{ + gboolean rtl; + + rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) == + GTK_TEXT_DIR_RTL); + + if (left) + *icon_pos = (rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY); + else + *icon_pos = (rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY); + + allocation->y = text_area_alloc->y; + allocation->width = get_icon_width(icon_entry, *icon_pos); + allocation->height = text_area_alloc->height; + + if (left) + allocation->x = text_area_alloc->x + ICON_MARGIN; + else + { + allocation->x = text_area_alloc->x + text_area_alloc->width - + allocation->width - ICON_MARGIN; + } +} + +static void +sexy_icon_entry_realize(GtkWidget *widget) +{ + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + GdkWindowAttr attributes; + gint attributes_mask; + int i; + + GTK_WIDGET_CLASS(parent_class)->realize(widget); + + attributes.x = 0; + attributes.y = 0; + attributes.width = 1; + attributes.height = 1; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual(widget); + attributes.colormap = gtk_widget_get_colormap(widget); + attributes.event_mask = gtk_widget_get_events(widget); + attributes.event_mask |= + (GDK_EXPOSURE_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + + for (i = 0; i < MAX_ICONS; i++) + { + SexyIconInfo *icon_info; + + icon_info = &entry->priv->icons[i]; + icon_info->window = gdk_window_new(widget->window, &attributes, + attributes_mask); + gdk_window_set_user_data(icon_info->window, widget); + + gdk_window_set_background(icon_info->window, + &widget->style->base[nsgtk_widget_get_state(widget)]); + } + + gtk_widget_queue_resize(widget); +} + +static void +sexy_icon_entry_unrealize(GtkWidget *widget) +{ + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + int i; + + GTK_WIDGET_CLASS(parent_class)->unrealize(widget); + + for (i = 0; i < MAX_ICONS; i++) + { + SexyIconInfo *icon_info = &entry->priv->icons[i]; + + gdk_window_destroy(icon_info->window); + icon_info->window = NULL; + } +} + +static void +sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition) +{ + GtkEntry *gtkentry; + SexyIconEntry *entry; + gint icon_widths = 0; + int i; + + gtkentry = GTK_ENTRY(widget); + entry = SEXY_ICON_ENTRY(widget); + + for (i = 0; i < MAX_ICONS; i++) + { + int icon_width = get_icon_width(entry, i); + + if (icon_width > 0) + icon_widths += icon_width + ICON_MARGIN; + } + + GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition); + + if (icon_widths > requisition->width) + requisition->width += icon_widths; +} + +static void +place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc) +{ + SexyIconEntryPosition left_icon_pos; + SexyIconEntryPosition right_icon_pos; + GtkAllocation left_icon_alloc; + GtkAllocation right_icon_alloc; + GtkAllocation text_area_alloc; + + get_text_area_size(icon_entry, &text_area_alloc); + get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc, + &left_icon_alloc, &left_icon_pos); + get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc, + &right_icon_alloc, &right_icon_pos); + + if (left_icon_alloc.width > 0) + { + text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width + + ICON_MARGIN; + } + + if (right_icon_alloc.width > 0) + text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN; + + text_area_alloc.width -= text_area_alloc.x; + + gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window, + left_icon_alloc.x, left_icon_alloc.y, + left_icon_alloc.width, left_icon_alloc.height); + + gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window, + right_icon_alloc.x, right_icon_alloc.y, + right_icon_alloc.width, right_icon_alloc.height); + + gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area, + text_area_alloc.x, text_area_alloc.y, + text_area_alloc.width, text_area_alloc.height); +} + +static void +sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + g_return_if_fail(SEXY_IS_ICON_ENTRY(widget)); + g_return_if_fail(allocation != NULL); + + widget->allocation = *allocation; + + GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation); + + if (nsgtk_widget_get_realized(widget)) + place_windows(SEXY_ICON_ENTRY(widget), allocation); +} + +static GdkPixbuf * +get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos) +{ + GdkPixbuf *pixbuf = NULL; + gchar *stock_id; + SexyIconInfo *icon_info = &entry->priv->icons[icon_pos]; + GtkIconSize size; + + switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon))) + { + case GTK_IMAGE_PIXBUF: + pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon)); + g_object_ref(pixbuf); + break; + + case GTK_IMAGE_STOCK: + gtk_image_get_stock(GTK_IMAGE(icon_info->icon), &stock_id, &size); + pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry), + stock_id, size, NULL); + break; + + default: + return NULL; + } + + return pixbuf; +} + +/* Kudos to the gnome-panel guys. */ +static void +colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift) +{ + gint i, j; + gint width, height, has_alpha, src_rowstride, dest_rowstride; + guchar *target_pixels; + guchar *original_pixels; + guchar *pix_src; + guchar *pix_dest; + int val; + guchar r, g, b; + + has_alpha = gdk_pixbuf_get_has_alpha(src); + width = gdk_pixbuf_get_width(src); + height = gdk_pixbuf_get_height(src); + src_rowstride = gdk_pixbuf_get_rowstride(src); + dest_rowstride = gdk_pixbuf_get_rowstride(dest); + original_pixels = gdk_pixbuf_get_pixels(src); + target_pixels = gdk_pixbuf_get_pixels(dest); + + for (i = 0; i < height; i++) + { + pix_dest = target_pixels + i * dest_rowstride; + pix_src = original_pixels + i * src_rowstride; + + for (j = 0; j < width; j++) + { + r = *(pix_src++); + g = *(pix_src++); + b = *(pix_src++); + + val = r + shift; + *(pix_dest++) = CLAMP(val, 0, 255); + + val = g + shift; + *(pix_dest++) = CLAMP(val, 0, 255); + + val = b + shift; + *(pix_dest++) = CLAMP(val, 0, 255); + + if (has_alpha) + *(pix_dest++) = *(pix_src++); + } + } +} + +static void +draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos) +{ + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + SexyIconInfo *icon_info = &entry->priv->icons[icon_pos]; + GdkPixbuf *pixbuf; + gint x, y, width, height; + + if (icon_info->icon == NULL || !nsgtk_widget_get_realized(widget)) + return; + + if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL) + return; + + gdk_drawable_get_size(icon_info->window, &width, &height); + + if (width == 1 || height == 1) + { + /* + * size_allocate hasn't been called yet. These are the default values. + */ + return; + } + + if (gdk_pixbuf_get_height(pixbuf) > height) + { + GdkPixbuf *temp_pixbuf; + int scale; + + scale = height - (2 * ICON_MARGIN); + + temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale, + GDK_INTERP_BILINEAR); + + g_object_unref(pixbuf); + + pixbuf = temp_pixbuf; + } + + x = (width - gdk_pixbuf_get_width(pixbuf)) / 2; + y = (height - gdk_pixbuf_get_height(pixbuf)) / 2; + + if (icon_info->hovered) + { + GdkPixbuf *temp_pixbuf; + + temp_pixbuf = gdk_pixbuf_copy(pixbuf); + + colorshift_pixbuf(temp_pixbuf, pixbuf, 30); + + g_object_unref(pixbuf); + + pixbuf = temp_pixbuf; + } + + gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf, + 0, 0, x, y, -1, -1, + GDK_RGB_DITHER_NORMAL, 0, 0); + + g_object_unref(pixbuf); +} + +static gint +sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event) +{ + SexyIconEntry *entry; + + g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + entry = SEXY_ICON_ENTRY(widget); + + if (nsgtk_widget_is_drawable(widget)) + { + gboolean found = FALSE; + int i; + + for (i = 0; i < MAX_ICONS && !found; i++) + { + SexyIconInfo *icon_info = &entry->priv->icons[i]; + + if (event->window == icon_info->window) + { + gint width; + GtkAllocation text_area_alloc; + + get_text_area_size(entry, &text_area_alloc); + gdk_drawable_get_size(icon_info->window, &width, NULL); + + gtk_paint_flat_box(widget->style, icon_info->window, + nsgtk_widget_get_state(widget), GTK_SHADOW_NONE, + NULL, widget, "entry_bg", + 0, 0, width, text_area_alloc.height); + + draw_icon(widget, i); + + found = TRUE; + } + } + + if (!found) + GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event); + } + + return FALSE; +} + +static void +update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry) +{ + if (param != NULL) + { + const char *name = g_param_spec_get_name(param); + + if (strcmp(name, "pixbuf") && strcmp(name, "stock") && + strcmp(name, "image") && strcmp(name, "pixmap") && + strcmp(name, "icon_set") && strcmp(name, "pixbuf_animation")) + { + return; + } + } + + gtk_widget_queue_resize(GTK_WIDGET(entry)); +} + +static gint +sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event) +{ + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + int i; + + for (i = 0; i < MAX_ICONS; i++) + { + if (event->window == entry->priv->icons[i].window) + { + if (sexy_icon_entry_get_icon_highlight(entry, i)) + { + entry->priv->icons[i].hovered = TRUE; + + update_icon(NULL, NULL, entry); + + break; + } + } + } + + return FALSE; +} + +static gint +sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event) +{ + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + int i; + + for (i = 0; i < MAX_ICONS; i++) + { + if (event->window == entry->priv->icons[i].window) + { + if (sexy_icon_entry_get_icon_highlight(entry, i)) + { + entry->priv->icons[i].hovered = FALSE; + + update_icon(NULL, NULL, entry); + + break; + } + } + } + + return FALSE; +} + +static gint +sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event) +{ + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + int i; + + for (i = 0; i < MAX_ICONS; i++) + { + if (event->window == entry->priv->icons[i].window) + { + if (event->button == 1 && + sexy_icon_entry_get_icon_highlight(entry, i)) + { + entry->priv->icons[i].hovered = FALSE; + + update_icon(NULL, NULL, entry); + } + + g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button); + + return TRUE; + } + } + + if (GTK_WIDGET_CLASS(parent_class)->button_press_event) + return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget, + event); + + return FALSE; +} + +static gint +sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event) +{ + SexyIconEntry *entry = SEXY_ICON_ENTRY(widget); + int i; + + for (i = 0; i < MAX_ICONS; i++) + { + GdkWindow *icon_window = entry->priv->icons[i].window; + + if (event->window == icon_window) + { + int width, height; + gdk_drawable_get_size(icon_window, &width, &height); + + if (event->button == 1 && + sexy_icon_entry_get_icon_highlight(entry, i) && + event->x >= 0 && event->y >= 0 && + event->x <= width && event->y <= height) + { + entry->priv->icons[i].hovered = TRUE; + + update_icon(NULL, NULL, entry); + } + + g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button); + + return TRUE; + } + } + + if (GTK_WIDGET_CLASS(parent_class)->button_release_event) + return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget, + event); + + return FALSE; +} + +/* + * sexy_icon_entry_new + * + * Creates a new SexyIconEntry widget. + * + * Returns a new #SexyIconEntry. + */ +GtkWidget * +sexy_icon_entry_new(void) +{ + return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL)); +} + +/* + * sexy_icon_entry_set_icon + * @param entry A #SexyIconEntry. + * @param position Icon position. + * @param icon A #GtkImage to set as the icon. + * + * Sets the icon shown in the entry + */ +void +sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos, + GtkImage *icon) +{ + SexyIconInfo *icon_info; + + g_return_if_fail(entry != NULL); + g_return_if_fail(SEXY_IS_ICON_ENTRY(entry)); + g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos)); + g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon)); + + icon_info = &entry->priv->icons[icon_pos]; + + if (icon == icon_info->icon) + return; + + if (icon_pos == SEXY_ICON_ENTRY_SECONDARY && + entry->priv->icon_released_id != 0) + { + g_signal_handler_disconnect(entry, entry->priv->icon_released_id); + entry->priv->icon_released_id = 0; + } + + if (icon == NULL) + { + if (icon_info->icon != NULL) + { + gtk_widget_destroy(GTK_WIDGET(icon_info->icon)); + icon_info->icon = NULL; + + /* + * Explicitly check, as the pointer may become invalidated + * during destruction. + */ + if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window)) + gdk_window_hide(icon_info->window); + } + } + else + { + if (icon_info->window != NULL && icon_info->icon == NULL) + gdk_window_show(icon_info->window); + + g_signal_connect(G_OBJECT(icon), "notify", + G_CALLBACK(update_icon), entry); + + icon_info->icon = icon; + g_object_ref(icon); + } + + update_icon(NULL, NULL, entry); +} + +/* + * sexy_icon_entry_set_icon_highlight + * @param entry A #SexyIconEntry; + * @param position Icon position. + * @param highlight TRUE if the icon should highlight on mouse-over + * + * Determines whether the icon will highlight on mouse-over. + */ +void +sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry, + SexyIconEntryPosition icon_pos, + gboolean highlight) +{ + SexyIconInfo *icon_info; + + g_return_if_fail(entry != NULL); + g_return_if_fail(SEXY_IS_ICON_ENTRY(entry)); + g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos)); + + icon_info = &entry->priv->icons[icon_pos]; + + if (icon_info->highlight == highlight) + return; + + icon_info->highlight = highlight; +} + +/* + * sexy_icon_entry_get_icon + * @param entry A #SexyIconEntry. + * @param position Icon position. + * + * Retrieves the image used for the icon + * + * Returns: A #GtkImage. + */ +GtkImage * +sexy_icon_entry_get_icon(const SexyIconEntry *entry, + SexyIconEntryPosition icon_pos) +{ + g_return_val_if_fail(entry != NULL, NULL); + g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL); + g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL); + + return entry->priv->icons[icon_pos].icon; +} + +/* + * sexy_icon_entry_get_icon_highlight + * @param entry A #SexyIconEntry. + * @param position Icon position. + * + * Retrieves whether entry will highlight the icon on mouseover. + * + * Returns: TRUE if icon highlights. + */ +gboolean +sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry, + SexyIconEntryPosition icon_pos) +{ + g_return_val_if_fail(entry != NULL, FALSE); + g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE); + g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE); + + return entry->priv->icons[icon_pos].highlight; +} + +static void +clear_button_clicked_cb(SexyIconEntry *icon_entry, + SexyIconEntryPosition icon_pos, + int button) +{ + if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1) + return; + + gtk_entry_set_text(GTK_ENTRY(icon_entry), ""); +} + +/* + * sexy_icon_entry_add_clear_button + * @param icon_entry A #SexyIconEntry. + * + * A convenience function to add a clear button to the end of the entry. + * This is useful for search boxes. + */ +void +sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry) +{ + GtkWidget *icon; + + g_return_if_fail(icon_entry != NULL); + g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry)); + + icon = nsgtk_image_new_from_stock(NSGTK_STOCK_CLEAR, + GTK_ICON_SIZE_MENU); + gtk_widget_show(icon); + sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(icon_entry), + SEXY_ICON_ENTRY_SECONDARY, + GTK_IMAGE(icon)); + sexy_icon_entry_set_icon_highlight(SEXY_ICON_ENTRY(icon_entry), + SEXY_ICON_ENTRY_SECONDARY, TRUE); + + if (icon_entry->priv->icon_released_id != 0) + { + g_signal_handler_disconnect(icon_entry, + icon_entry->priv->icon_released_id); + } + + icon_entry->priv->icon_released_id = + g_signal_connect(G_OBJECT(icon_entry), "icon_released", + G_CALLBACK(clear_button_clicked_cb), NULL); +} diff --git a/frontends/gtk/sexy_icon_entry.h b/frontends/gtk/sexy_icon_entry.h new file mode 100644 index 000000000..bd7fb3eb9 --- /dev/null +++ b/frontends/gtk/sexy_icon_entry.h @@ -0,0 +1,100 @@ +/* + * file libsexy/sexy-icon-entry.h Entry widget + * + * Copyright (C) 2004-2006 Christian Hammond. + * redistributed under GPLv2 + * + * libsexy 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 + * or write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _SEXY_ICON_ENTRY_H_ +#define _SEXY_ICON_ENTRY_H_ + +typedef struct _SexyIconEntry SexyIconEntry; +typedef struct _SexyIconEntryClass SexyIconEntryClass; +typedef struct _SexyIconEntryPriv SexyIconEntryPriv; + +#include + +#define SEXY_TYPE_ICON_ENTRY (sexy_icon_entry_get_type()) +#define SEXY_ICON_ENTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntry)) +#define SEXY_ICON_ENTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass)) +#define SEXY_IS_ICON_ENTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), SEXY_TYPE_ICON_ENTRY)) +#define SEXY_IS_ICON_ENTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SEXY_TYPE_ICON_ENTRY)) +#define SEXY_ICON_ENTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass)) + +typedef enum +{ + SEXY_ICON_ENTRY_PRIMARY, + SEXY_ICON_ENTRY_SECONDARY + +} SexyIconEntryPosition; + +struct _SexyIconEntry +{ + GtkEntry parent_object; + + SexyIconEntryPriv *priv; + + void (*gtk_reserved1)(void); + void (*gtk_reserved2)(void); + void (*gtk_reserved3)(void); + void (*gtk_reserved4)(void); +}; + +struct _SexyIconEntryClass +{ + GtkEntryClass parent_class; + + /* Signals */ + void (*icon_pressed)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos, + int button); + void (*icon_released)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos, + int button); + + void (*gtk_reserved1)(void); + void (*gtk_reserved2)(void); + void (*gtk_reserved3)(void); + void (*gtk_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType sexy_icon_entry_get_type(void); + +GtkWidget *sexy_icon_entry_new(void); + +void sexy_icon_entry_set_icon(SexyIconEntry *entry, + SexyIconEntryPosition position, + GtkImage *icon); + +void sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry, + SexyIconEntryPosition position, + gboolean highlight); + +GtkImage *sexy_icon_entry_get_icon(const SexyIconEntry *entry, + SexyIconEntryPosition position); + +gboolean sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry, + SexyIconEntryPosition position); +void sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry); + +G_END_DECLS + +#endif /* _SEXY_ICON_ENTRY_H_ */ diff --git a/frontends/gtk/ssl_cert.c b/frontends/gtk/ssl_cert.c new file mode 100644 index 000000000..742029f83 --- /dev/null +++ b/frontends/gtk/ssl_cert.c @@ -0,0 +1,135 @@ +/* + * Copyright 2015 Vincent Sanders + * + * 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 . + */ + +#include +#include + +#include "utils/log.h" +#include "utils/nsurl.h" +#include "desktop/tree.h" +#include "desktop/sslcert_viewer.h" + +#include "gtk/treeview.h" +#include "gtk/scaffolding.h" +#include "gtk/resources.h" +#include "gtk/ssl_cert.h" + + +static void nsgtk_ssl_accept(GtkButton *w, gpointer data) +{ + void **session = data; + GtkBuilder *x = session[0]; + struct nsgtk_treeview *wnd = session[1]; + struct sslcert_session_data *ssl_data = session[2]; + + sslcert_viewer_accept(ssl_data); + + nsgtk_treeview_destroy(wnd); + g_object_unref(G_OBJECT(x)); + free(session); +} + +static void nsgtk_ssl_reject(GtkWidget *w, gpointer data) +{ + void **session = data; + GtkBuilder *x = session[0]; + struct nsgtk_treeview *wnd = session[1]; + struct sslcert_session_data *ssl_data = session[2]; + + sslcert_viewer_reject(ssl_data); + + nsgtk_treeview_destroy(wnd); + g_object_unref(G_OBJECT(x)); + free(session); +} + +static gboolean nsgtk_ssl_delete_event(GtkWidget *w, GdkEvent *event, gpointer data) +{ + nsgtk_ssl_reject(w, data); + return FALSE; +} + +void gtk_cert_verify(nsurl *url, const struct ssl_cert_info *certs, + unsigned long num, nserror (*cb)(bool proceed, void *pw), + void *cbpw) +{ + static struct nsgtk_treeview *ssl_window; + struct sslcert_session_data *data; + GtkButton *accept, *reject; + void **session; + GtkDialog *dlg; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + GtkBuilder *builder; + GtkWindow *gtk_parent; + nserror res; + + /* state while dlg is open */ + session = calloc(sizeof(void *), 3); + if (session == NULL) { + return; + } + + res = nsgtk_builder_new_from_resname("ssl", &builder); + if (res != NSERROR_OK) { + LOG("SSL UI builder init failed"); + free(session); + cb(false, cbpw); + return; + } + + gtk_builder_connect_signals(builder, NULL); + + sslcert_viewer_create_session_data(num, url, cb, cbpw, certs, &data); + ssl_current_session = data; + + dlg = GTK_DIALOG(gtk_builder_get_object(builder, "wndSSLProblem")); + + /* set parent for transient dialog */ + gtk_parent = nsgtk_scaffolding_window(nsgtk_current_scaffolding()); + gtk_window_set_transient_for(GTK_WINDOW(dlg), gtk_parent); + + scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(builder, "SSLScrolled")); + drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(builder, "SSLDrawingArea")); + + ssl_window = nsgtk_treeview_create(TREE_SSLCERT, GTK_WINDOW(dlg), scrolled, + drawing_area); + + if (ssl_window == NULL) { + free(session); + g_object_unref(G_OBJECT(dlg)); + return; + } + + accept = GTK_BUTTON(gtk_builder_get_object(builder, "sslaccept")); + reject = GTK_BUTTON(gtk_builder_get_object(builder, "sslreject")); + + session[0] = builder; + session[1] = ssl_window; + session[2] = data; + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(accept, "clicked", nsgtk_ssl_accept, session); + CONNECT(reject, "clicked", nsgtk_ssl_reject, session); + CONNECT(dlg, "delete_event", G_CALLBACK(nsgtk_ssl_delete_event), + (gpointer)session); + + gtk_widget_show(GTK_WIDGET(dlg)); +} diff --git a/frontends/gtk/ssl_cert.h b/frontends/gtk/ssl_cert.h new file mode 100644 index 000000000..48937d457 --- /dev/null +++ b/frontends/gtk/ssl_cert.h @@ -0,0 +1,36 @@ +/* + * Copyright 2005 James Bursa + * + * 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 . + */ + +#ifndef NETSURF_GTK_SSL_CERT_H +#define NETSURF_GTK_SSL_CERT_H 1 + +struct nsurl; +struct ssl_cert_info; + +/** + * Prompt the user to verify a certificate with issuse. + * + * \param url The URL being verified. + * \param certs The certificate to be verified + * \param num The number of certificates to be verified. + * \param cb Callback upon user decision. + * \param cbpw Context pointer passed to cb + */ +void gtk_cert_verify(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw); + +#endif diff --git a/frontends/gtk/tabs.c b/frontends/gtk/tabs.c new file mode 100644 index 000000000..67a410d20 --- /dev/null +++ b/frontends/gtk/tabs.c @@ -0,0 +1,416 @@ +/* + * Copyright 2008 Michael Lester + * + * 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 . + */ + +#include +#include + +#include "utils/nsoption.h" +#include "utils/messages.h" +#include "desktop/browser.h" +#include "content/content.h" +#include "desktop/search.h" + +#include "gtk/compat.h" +#include "gtk/window.h" +#include "gtk/search.h" +#include "gtk/tabs.h" + +#define TAB_WIDTH_N_CHARS 15 + +/** callback to update sizes when style-set gtk signal */ +static void nsgtk_tab_update_size(GtkWidget *hbox, GtkStyle *previous_style, + GtkWidget *close_button) +{ + PangoFontMetrics *metrics; + PangoContext *context; + int char_width, h, w; + GtkStyleContext *style; + GtkStateFlags state; + + state = nsgtk_widget_get_state_flags(hbox); + style = nsgtk_widget_get_style_context(hbox); + + context = gtk_widget_get_pango_context(hbox); + metrics = pango_context_get_metrics(context, + nsgtk_style_context_get_font(style, state), + pango_context_get_language(context)); + + char_width = pango_font_metrics_get_approximate_digit_width(metrics); + pango_font_metrics_unref(metrics); + + nsgtk_icon_size_lookup_for_settings(gtk_widget_get_settings (hbox), + GTK_ICON_SIZE_MENU, &w, &h); + + gtk_widget_set_size_request(hbox, + TAB_WIDTH_N_CHARS * PANGO_PIXELS(char_width) + 2 * w, + -1); + + gtk_widget_set_size_request(close_button, w + 4, h + 4); +} + +/** Create a notebook tab label */ +static GtkWidget *nsgtk_tab_label_setup(struct gui_window *window) +{ + GtkWidget *hbox, *label, *button, *close; + + hbox = nsgtk_hbox_new(FALSE, 2); + + label = gtk_label_new(messages_get("NewTab")); + gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); + gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE); + nsgtk_widget_set_alignment(label, GTK_ALIGN_START, GTK_ALIGN_CENTER); + nsgtk_widget_set_margins(label, 0, 0); + gtk_widget_show(label); + + button = gtk_button_new(); + + close = nsgtk_image_new_from_stock(NSGTK_STOCK_CLOSE, + GTK_ICON_SIZE_MENU); + gtk_container_add(GTK_CONTAINER(button), close); + gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_widget_set_tooltip_text(button, "Close this tab."); + +#ifdef FIXME + GtkRcStyle *rcstyle; + rcstyle = gtk_rc_style_new(); + rcstyle->xthickness = rcstyle->ythickness = 0; + gtk_widget_modify_style(button, rcstyle); + g_object_unref(rcstyle); +#endif + + g_signal_connect_swapped(button, "clicked", + G_CALLBACK(nsgtk_window_destroy_browser), window); + g_signal_connect(hbox, "style-set", + G_CALLBACK(nsgtk_tab_update_size), button); + + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + g_object_set_data(G_OBJECT(hbox), "label", label); + g_object_set_data(G_OBJECT(hbox), "close-button", button); + + + gtk_widget_show_all(hbox); + return hbox; +} +#include "utils/log.h" + +/** callback when page is switched */ + +static gint srcpagenum; + +/** The switch-page signal handler + * + * This signal is handled both before and after delivery to work round + * issue that setting the selected tab during the switch-page signal + * fails + */ +static void +nsgtk_tab_switch_page(GtkNotebook *notebook, + GtkWidget *page, + guint selpagenum, + gpointer user_data) +{ + srcpagenum = gtk_notebook_get_current_page(notebook); +} + +static void +nsgtk_tab_switch_page_after(GtkNotebook *notebook, + GtkWidget *selpage, + guint selpagenum, + gpointer user_data) +{ + GtkWidget *srcpage; + GtkWidget *addpage; + struct gui_window *gw; + nserror error; + + addpage = g_object_get_data(G_OBJECT(notebook), "addtab"); + + if (selpage == addpage) { + if ((srcpagenum != -1) && + (srcpagenum != (gint)selpagenum)) { + /* ensure the add tab is not actually selected */ + LOG("src %d sel %d", srcpagenum, selpagenum); + srcpage = gtk_notebook_get_nth_page(notebook, srcpagenum); + gw = g_object_get_data(G_OBJECT(srcpage), "gui_window"); + if ((gw != NULL) && (nsgtk_get_scaffold(gw) != NULL)) { + error = nsgtk_scaffolding_new_tab(gw); + if (error != NSERROR_OK) { + LOG("Failed to open new tab."); + } + } + } + } else { + LOG("sel %d", selpagenum); + /* tab with page in it */ + gw = g_object_get_data(G_OBJECT(selpage), "gui_window"); + if (gw != NULL) { + nsgtk_scaffolding_set_top_level(gw); + } + } +} + +static void nsgtk_tab_page_reordered(GtkNotebook *notebook, + GtkWidget *child, + guint page_num, + gpointer user_data) +{ + gint pages; + GtkWidget *addpage; + + pages = gtk_notebook_get_n_pages(notebook); + addpage = g_object_get_data(G_OBJECT(notebook), "addtab"); + + if (((gint)page_num == (pages - 1)) && + (child != addpage)) { + /* moved tab to end */ + gtk_notebook_reorder_child(notebook, addpage, -1); + } +} + +static void +nsgtk_tab_orientation(GtkNotebook *notebook) +{ + switch (nsoption_int(position_tab)) { + case 0: + gtk_notebook_set_tab_pos(notebook, GTK_POS_TOP); + break; + + case 1: + gtk_notebook_set_tab_pos(notebook, GTK_POS_LEFT); + break; + + case 2: + gtk_notebook_set_tab_pos(notebook, GTK_POS_RIGHT); + break; + + case 3: + gtk_notebook_set_tab_pos(notebook, GTK_POS_BOTTOM); + break; + + } +} + +/** adds a "new tab" tab */ +static GtkWidget * +nsgtk_tab_add_newtab(GtkNotebook *notebook) +{ + GtkWidget *tablabel; + GtkWidget *tabcontents; + GtkWidget *add; + + tablabel = nsgtk_hbox_new(FALSE, 1); + tabcontents = nsgtk_hbox_new(FALSE, 1); + + add = nsgtk_image_new_from_stock(NSGTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + + gtk_box_pack_start(GTK_BOX(tablabel), add, FALSE, FALSE, 0); + + gtk_widget_show_all(tablabel); + + gtk_notebook_append_page(notebook, tabcontents, tablabel); + + gtk_notebook_set_tab_reorderable(notebook, tabcontents, false); + + gtk_widget_show_all(tabcontents); + + g_object_set_data(G_OBJECT(notebook), "addtab", tabcontents); + + return tablabel; +} + +/** callback to alter tab visibility when pages are added or removed */ +static void +nsgtk_tab_visibility_update(GtkNotebook *notebook, GtkWidget *child, guint page) +{ + gint pagec = gtk_notebook_get_n_pages(notebook); + GtkWidget *addpage = g_object_get_data(G_OBJECT(notebook), "addtab"); + + if (addpage != NULL) { + pagec--; /* skip the add tab */ + if ((gint)page == pagec) { + /* ensure the add new tab cannot be current */ + gtk_notebook_set_current_page(notebook, page - 1); + } + } + + if ((nsoption_bool(show_single_tab) == true) || (pagec > 1)) { + gtk_notebook_set_show_tabs(notebook, TRUE); + } else { + gtk_notebook_set_show_tabs(notebook, FALSE); + } +} + +/* exported interface documented in gtk/tabs.h */ +void nsgtk_tab_options_changed(GtkNotebook *notebook) +{ + nsgtk_tab_orientation(notebook); + nsgtk_tab_visibility_update(notebook, NULL, 0); +} + + +/* exported interface documented in gtk/tabs.h */ +void nsgtk_tab_init(struct nsgtk_scaffolding *gs) +{ + GtkNotebook *notebook; + + notebook = nsgtk_scaffolding_notebook(gs); + + nsgtk_tab_add_newtab(notebook); + + g_signal_connect(notebook, "switch-page", + G_CALLBACK(nsgtk_tab_switch_page), NULL); + g_signal_connect_after(notebook, "switch-page", + G_CALLBACK(nsgtk_tab_switch_page_after), NULL); + + g_signal_connect(notebook, "page-removed", + G_CALLBACK(nsgtk_tab_visibility_update), NULL); + g_signal_connect(notebook, "page-added", + G_CALLBACK(nsgtk_tab_visibility_update), NULL); + g_signal_connect(notebook, "page-reordered", + G_CALLBACK(nsgtk_tab_page_reordered), NULL); + + + nsgtk_tab_options_changed(notebook); +} + +/* exported interface documented in gtk/tabs.h */ +void nsgtk_tab_add(struct gui_window *gw, + GtkWidget *tab_contents, + bool background) +{ + GtkNotebook *notebook; + GtkWidget *tabBox; + gint remember; + gint pages; + gint newpage; + + g_object_set_data(G_OBJECT(tab_contents), "gui_window", gw); + + notebook = nsgtk_scaffolding_notebook(nsgtk_get_scaffold(gw)); + + tabBox = nsgtk_tab_label_setup(gw); + + nsgtk_window_set_tab(gw, tabBox); + + remember = gtk_notebook_get_current_page(notebook); + + pages = gtk_notebook_get_n_pages(notebook); + + newpage = gtk_notebook_insert_page(notebook, tab_contents, tabBox, pages - 1); + + gtk_notebook_set_tab_reorderable(notebook, tab_contents, true); + + gtk_widget_show_all(tab_contents); + + if (background) { + gtk_notebook_set_current_page(notebook, remember); + } else { + gtk_notebook_set_current_page(notebook, newpage); + } + + gtk_widget_grab_focus(GTK_WIDGET(nsgtk_scaffolding_urlbar( + nsgtk_get_scaffold(gw)))); +} + +/* exported interface documented in gtk/tabs.h */ +void nsgtk_tab_set_title(struct gui_window *g, const char *title) +{ + GtkWidget *label; + GtkWidget *tab; + + tab = nsgtk_window_get_tab(g); + if (tab == NULL) { + return; + } + + label = g_object_get_data(G_OBJECT(tab), "label"); + gtk_label_set_text(GTK_LABEL(label), title); + gtk_widget_set_tooltip_text(tab, title); +} + +/* exported interface documented in gtk/tabs.h */ +nserror nsgtk_tab_close_current(GtkNotebook *notebook) +{ + gint pagen; + GtkWidget *page; + struct gui_window *gw; + GtkWidget *addpage; + + pagen = gtk_notebook_get_current_page(notebook); + if (pagen == -1) { + return NSERROR_OK; + } + + page = gtk_notebook_get_nth_page(notebook, pagen); + if (page == NULL) { + return NSERROR_OK; + } + + addpage = g_object_get_data(G_OBJECT(notebook), "addtab"); + if (page == addpage) { + /* the add new tab page is current, cannot close that */ + return NSERROR_OK; + } + + gw = g_object_get_data(G_OBJECT(page), "gui_window"); + if (gw == NULL) { + return NSERROR_OK; + } + + nsgtk_window_destroy_browser(gw); + + return NSERROR_OK; +} + +nserror nsgtk_tab_prev(GtkNotebook *notebook) +{ + gtk_notebook_prev_page(notebook); + + return NSERROR_OK; + +} + +nserror nsgtk_tab_next(GtkNotebook *notebook) +{ + gint pagen; + GtkWidget *page; + GtkWidget *addpage; + + pagen = gtk_notebook_get_current_page(notebook); + if (pagen == -1) { + return NSERROR_OK; + } + + page = gtk_notebook_get_nth_page(notebook, pagen + 1); + if (page == NULL) { + return NSERROR_OK; + } + + addpage = g_object_get_data(G_OBJECT(notebook), "addtab"); + if (page == addpage) { + /* cannot make add new tab page current */ + return NSERROR_OK; + } + + gtk_notebook_set_current_page(notebook, pagen + 1); + + return NSERROR_OK; +} diff --git a/frontends/gtk/tabs.h b/frontends/gtk/tabs.h new file mode 100644 index 000000000..440d61336 --- /dev/null +++ b/frontends/gtk/tabs.h @@ -0,0 +1,42 @@ +/* + * Copyright 2008 Michael Lester + * + * 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 . + */ + +#ifndef _NETSURF_GTK_TABS_H_ +#define _NETSURF_GTK_TABS_H_ + +struct gui_window; + +void nsgtk_tab_init(struct nsgtk_scaffolding *gs); +void nsgtk_tab_add(struct gui_window *window, GtkWidget *tab_contents, bool background); + +/** set the tab title + * + * The tab title will be set to the parameter + * + * \note currently only called from nsgtk_window_set_title() + * + * \param g the gui window to set tab title for. + * \param title The title text which may not be NULL. + */ +void nsgtk_tab_set_title(struct gui_window *g, const char *title); +void nsgtk_tab_options_changed(GtkNotebook *notebook); +nserror nsgtk_tab_close_current(GtkNotebook *notebook); +nserror nsgtk_tab_prev(GtkNotebook *notebook); +nserror nsgtk_tab_next(GtkNotebook *notebook); + +#endif diff --git a/frontends/gtk/throbber.c b/frontends/gtk/throbber.c new file mode 100644 index 000000000..9392c3909 --- /dev/null +++ b/frontends/gtk/throbber.c @@ -0,0 +1,91 @@ +/* + * Copyright 2008 Rob Kendrick + * Copyright 2008 Sean Fox + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#include "utils/log.h" + +#include "gtk/resources.h" +#include "gtk/throbber.h" + +struct nsgtk_throbber *nsgtk_throbber = NULL; + +#define THROBBER_FRAMES 9 +#define THROBBER_FMT "throbber/throbber%d.png" + +/* exported interface documented in gtk/throbber.h */ +nserror nsgtk_throbber_init(void) +{ + struct nsgtk_throbber *throb; /**< structure we generate */ + int frame; + char resname[] = THROBBER_FMT; + nserror res = NSERROR_OK; + + throb = malloc(sizeof(*throb)); + if (throb == NULL) { + return NSERROR_NOMEM; + } + + throb->framedata = malloc(sizeof(GdkPixbuf *) * THROBBER_FRAMES); + if (throb->framedata == NULL) { + free(throb); + return false; + } + + for (frame = 0; frame < THROBBER_FRAMES; frame++) { + snprintf(resname, sizeof(resname), THROBBER_FMT, frame); + res = nsgdk_pixbuf_new_from_resname(resname, + throb->framedata + frame); + if (res != NSERROR_OK) { + break; + } + LOG("%s",resname); + } + + if (frame < 1) { + /* we need at least two frames - one for idle, one for active */ + LOG("Insufficent number of frames (%d) in throbber animation.", frame); + res = NSERROR_INIT_FAILED; + } + + throb->nframes = frame; + nsgtk_throbber = throb; + return res; + + +} + + +void nsgtk_throbber_finalise(void) +{ + int i; + + for (i = 0; i < nsgtk_throbber->nframes; i++) + g_object_unref(nsgtk_throbber->framedata[i]); + + free(nsgtk_throbber->framedata); + free(nsgtk_throbber); + + nsgtk_throbber = NULL; +} + diff --git a/frontends/gtk/throbber.h b/frontends/gtk/throbber.h new file mode 100644 index 000000000..e0b47e15c --- /dev/null +++ b/frontends/gtk/throbber.h @@ -0,0 +1,35 @@ +/* + * Copyright 2008 Rob Kendrick + * + * 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 . + */ + +#ifndef __GTK_THROBBER_H__ +#define __GTK_THROBBER_H__ + +#include + +struct nsgtk_throbber +{ + int nframes; /**< Number of frames in the throbber */ + GdkPixbuf **framedata; +}; + +extern struct nsgtk_throbber *nsgtk_throbber; + +nserror nsgtk_throbber_init(void); +void nsgtk_throbber_finalise(void); + +#endif /* __GTK_THROBBER_H__ */ diff --git a/frontends/gtk/toolbar.c b/frontends/gtk/toolbar.c new file mode 100644 index 000000000..208b5c0b9 --- /dev/null +++ b/frontends/gtk/toolbar.c @@ -0,0 +1,1416 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + +#include +#include +#include +#include + +#include "desktop/browser.h" +#include "desktop/searchweb.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "utils/utils.h" + +#include "gtk/gui.h" +#include "gtk/warn.h" +#include "gtk/scaffolding.h" +#include "gtk/search.h" +#include "gtk/throbber.h" +#include "gtk/window.h" +#include "gtk/compat.h" +#include "gtk/resources.h" +#include "gtk/toolbar.h" + +static GtkTargetEntry entry = {(char *)"nsgtk_button_data", + GTK_TARGET_SAME_APP, 0}; + +static bool edit_mode = false; + +struct nsgtk_toolbar_custom_store { + GtkWidget *window; + GtkWidget *store_buttons[PLACEHOLDER_BUTTON]; + GtkWidget *widgetvbox; + GtkWidget *currentbar; + char numberh; /* current horizontal location while adding */ + GtkBuilder *builder; /* button widgets to store */ + int buttonlocations[PLACEHOLDER_BUTTON]; + int currentbutton; + bool fromstore; +}; +/* the number of buttons that fit in the width of the store window */ +#define NSGTK_STORE_WIDTH 6 + +/* the 'standard' width of a button that makes sufficient of its label +visible */ +#define NSGTK_BUTTON_WIDTH 111 + +/* 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 + +static struct nsgtk_toolbar_custom_store store; +static struct nsgtk_toolbar_custom_store *window = &store; + + +enum image_sets { + IMAGE_SET_MAIN_MENU = 0, + IMAGE_SET_RCLICK_MENU, + IMAGE_SET_POPUP_MENU, + IMAGE_SET_BUTTONS, + IMAGE_SET_COUNT +}; + +typedef enum search_buttons { + SEARCH_BACK_BUTTON = 0, + SEARCH_FORWARD_BUTTON, + SEARCH_CLOSE_BUTTON, + SEARCH_BUTTONS_COUNT +} nsgtk_search_buttons; + +struct nsgtk_theme { + GtkImage *image[PLACEHOLDER_BUTTON]; + GtkImage *searchimage[SEARCH_BUTTONS_COUNT]; + /* apng throbber element */ +}; + +/** + * 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; +} + + +/** + * get default image for buttons / menu items from gtk stock items. + * + * \param tbbutton button reference + * \param iconsize The size of icons to select. + * \return default images. + */ +static GtkImage * +nsgtk_theme_image_default(nsgtk_toolbar_button tbbutton, GtkIconSize iconsize) +{ + GtkImage *image; /* The GTK image to return */ + + switch(tbbutton) { + +#define BUTTON_IMAGE(p, q) \ + case p##_BUTTON: \ + image = GTK_IMAGE(nsgtk_image_new_from_stock(q, iconsize)); \ + break + + BUTTON_IMAGE(BACK, NSGTK_STOCK_GO_BACK); + BUTTON_IMAGE(FORWARD, NSGTK_STOCK_GO_FORWARD); + BUTTON_IMAGE(STOP, NSGTK_STOCK_STOP); + BUTTON_IMAGE(RELOAD, NSGTK_STOCK_REFRESH); + BUTTON_IMAGE(HOME, NSGTK_STOCK_HOME); + BUTTON_IMAGE(NEWWINDOW, "gtk-new"); + BUTTON_IMAGE(NEWTAB, "gtk-new"); + BUTTON_IMAGE(OPENFILE, NSGTK_STOCK_OPEN); + BUTTON_IMAGE(CLOSETAB, NSGTK_STOCK_CLOSE); + BUTTON_IMAGE(CLOSEWINDOW, NSGTK_STOCK_CLOSE); + BUTTON_IMAGE(SAVEPAGE, NSGTK_STOCK_SAVE_AS); + BUTTON_IMAGE(PRINTPREVIEW, "gtk-print-preview"); + BUTTON_IMAGE(PRINT, "gtk-print"); + BUTTON_IMAGE(QUIT, "gtk-quit"); + BUTTON_IMAGE(CUT, "gtk-cut"); + BUTTON_IMAGE(COPY, "gtk-copy"); + BUTTON_IMAGE(PASTE, "gtk-paste"); + BUTTON_IMAGE(DELETE, "gtk-delete"); + BUTTON_IMAGE(SELECTALL, "gtk-select-all"); + BUTTON_IMAGE(FIND, NSGTK_STOCK_FIND); + BUTTON_IMAGE(PREFERENCES, "gtk-preferences"); + BUTTON_IMAGE(ZOOMPLUS, "gtk-zoom-in"); + BUTTON_IMAGE(ZOOMMINUS, "gtk-zoom-out"); + BUTTON_IMAGE(ZOOMNORMAL, "gtk-zoom-100"); + BUTTON_IMAGE(FULLSCREEN, "gtk-fullscreen"); + BUTTON_IMAGE(VIEWSOURCE, "gtk-index"); + BUTTON_IMAGE(CONTENTS, "gtk-help"); + BUTTON_IMAGE(ABOUT, "gtk-about"); +#undef BUTTON_IMAGE + + case HISTORY_BUTTON: + image = GTK_IMAGE(gtk_image_new_from_pixbuf(arrow_down_pixbuf)); + break; + + default: + image = GTK_IMAGE(nsgtk_image_new_from_stock("gtk-missing-image", + iconsize)); + break; + + } + return image; + +} + +/** + * Get default image for search buttons / menu items from gtk stock items + * + * \param tbbutton search button reference + * \param iconsize The size of icons to select. + * \return default search image. + */ + +static GtkImage * +nsgtk_theme_searchimage_default(nsgtk_search_buttons tbbutton, + GtkIconSize iconsize) +{ + switch (tbbutton) { + + case (SEARCH_BACK_BUTTON): + return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_GO_BACK, + iconsize)); + case (SEARCH_FORWARD_BUTTON): + return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_GO_FORWARD, + iconsize)); + case (SEARCH_CLOSE_BUTTON): + return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_CLOSE, + iconsize)); + default: + return NULL; + } +} + +/** + * initialise a theme structure with gtk images + */ +static struct nsgtk_theme *nsgtk_theme_load(GtkIconSize iconsize) +{ + struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme)); + int btnloop; + + if (theme == NULL) { + nsgtk_warning("NoMemory", 0); + return NULL; + } + + for (btnloop = BACK_BUTTON; btnloop < PLACEHOLDER_BUTTON ; btnloop++) { + theme->image[btnloop] = nsgtk_theme_image_default(btnloop, iconsize); + } + + for (btnloop = SEARCH_BACK_BUTTON; btnloop < SEARCH_BUTTONS_COUNT; btnloop++) { + theme->searchimage[btnloop] = nsgtk_theme_searchimage_default(btnloop, iconsize); + } + return theme; +} + + + +/* exported function documented in gtk/toolbar.h */ +void nsgtk_theme_implement(struct nsgtk_scaffolding *g) +{ + struct nsgtk_theme *theme[IMAGE_SET_COUNT]; + int i; + struct nsgtk_button_connect *button; + struct gtk_search *search; + + theme[IMAGE_SET_MAIN_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU); + theme[IMAGE_SET_RCLICK_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU); + theme[IMAGE_SET_POPUP_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU); + theme[IMAGE_SET_BUTTONS] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR); + + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) || + (i == WEBSEARCH_ITEM)) + continue; + + button = nsgtk_scaffolding_button(g, i); + if (button == NULL) + continue; + + /* gtk_image_menu_item_set_image accepts NULL image */ + if ((button->main != NULL) && + (theme[IMAGE_SET_MAIN_MENU] != NULL)) { + nsgtk_image_menu_item_set_image( + GTK_WIDGET(button->main), + GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->image[i])); + gtk_widget_show_all(GTK_WIDGET(button->main)); + } + if ((button->rclick != NULL) && + (theme[IMAGE_SET_RCLICK_MENU] != NULL)) { + nsgtk_image_menu_item_set_image(GTK_WIDGET(button->rclick), + GTK_WIDGET( + theme[IMAGE_SET_RCLICK_MENU]-> + image[i])); + gtk_widget_show_all(GTK_WIDGET(button->rclick)); + } + if ((button->popup != NULL) && + (theme[IMAGE_SET_POPUP_MENU] != NULL)) { + nsgtk_image_menu_item_set_image(GTK_WIDGET(button->popup), + GTK_WIDGET( + theme[IMAGE_SET_POPUP_MENU]-> + image[i])); + gtk_widget_show_all(GTK_WIDGET(button->popup)); + } + if ((button->location != -1) && (button->button != NULL) && + (theme[IMAGE_SET_BUTTONS] != NULL)) { + gtk_tool_button_set_icon_widget( + GTK_TOOL_BUTTON(button->button), + GTK_WIDGET( + theme[IMAGE_SET_BUTTONS]-> + image[i])); + gtk_widget_show_all(GTK_WIDGET(button->button)); + } + } + + /* set search bar images */ + search = nsgtk_scaffolding_search(g); + if ((search != NULL) && (theme[IMAGE_SET_MAIN_MENU] != NULL)) { + /* gtk_tool_button_set_icon_widget accepts NULL image */ + if (search->buttons[SEARCH_BACK_BUTTON] != NULL) { + gtk_tool_button_set_icon_widget( + search->buttons[SEARCH_BACK_BUTTON], + GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]-> + searchimage[SEARCH_BACK_BUTTON])); + gtk_widget_show_all(GTK_WIDGET( + search->buttons[SEARCH_BACK_BUTTON])); + } + if (search->buttons[SEARCH_FORWARD_BUTTON] != NULL) { + gtk_tool_button_set_icon_widget( + search->buttons[SEARCH_FORWARD_BUTTON], + GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]-> + searchimage[SEARCH_FORWARD_BUTTON])); + gtk_widget_show_all(GTK_WIDGET( + search->buttons[ + SEARCH_FORWARD_BUTTON])); + } + if (search->buttons[SEARCH_CLOSE_BUTTON] != NULL) { + gtk_tool_button_set_icon_widget( + search->buttons[SEARCH_CLOSE_BUTTON], + GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]-> + searchimage[SEARCH_CLOSE_BUTTON])); + gtk_widget_show_all(GTK_WIDGET( + search->buttons[SEARCH_CLOSE_BUTTON])); + } + } + + for (i = 0; i < IMAGE_SET_COUNT; i++) { + if (theme[i] != NULL) { + free(theme[i]); + } + } +} + + +/** + * callback function to iterate toolbar's widgets + */ +static void nsgtk_toolbar_clear_toolbar(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + gtk_container_remove(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)), + widget); +} + +/** + * connect temporary handler for toolbar edit events + * + * \param g The scaffolding + * \param bi The button index + */ +static void nsgtk_toolbar_temp_connect(struct nsgtk_scaffolding *g, + nsgtk_toolbar_button bi) +{ + struct nsgtk_button_connect *bc; + + if (bi != URL_BAR_ITEM) { + bc = nsgtk_scaffolding_button(g, bi); + if ((bc->button != NULL) && (bc->dataminus != NULL)) { + g_signal_connect(bc->button, + "drag-data-get", + G_CALLBACK(bc->dataminus), + g); + } + } +} + +/** + * get scaffolding button index of button at location + * + * \return toolbar item id from location when there is an item at that logical + * location; else -1 + */ +static nsgtk_toolbar_button +nsgtk_toolbar_get_id_at_location(struct nsgtk_scaffolding *g, int i) +{ + int q; + for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++) { + if (nsgtk_scaffolding_button(g, q)->location == i) { + return q; + } + } + return -1; +} + +/** + * widget factory for creation of toolbar item widgets + * \param g the reference scaffolding + * \param i the id of the widget + * \param theme the theme to make the widgets from + */ +static GtkWidget * +nsgtk_toolbar_make_widget(struct nsgtk_scaffolding *g, + nsgtk_toolbar_button i, + struct nsgtk_theme *theme) +{ + GtkWidget *w = NULL; + + switch(i) { + +/* gtk_tool_button_new() accepts NULL args */ +#define MAKE_STOCKBUTTON(p, q) \ + case p##_BUTTON: { \ + GtkStockItem item; \ + char *label = NULL; \ + if (nsgtk_stock_lookup(q, &item) && \ + (item.label != NULL) && \ + ((label = remove_underscores(item.label, false)) != NULL)) { \ + w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \ + theme->image[p##_BUTTON]), label)); \ + free(label); \ + } else { \ + w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \ + theme->image[p##_BUTTON]), q)); \ + } \ + break; \ + } + + MAKE_STOCKBUTTON(HOME, NSGTK_STOCK_HOME) + MAKE_STOCKBUTTON(BACK, NSGTK_STOCK_GO_BACK) + MAKE_STOCKBUTTON(FORWARD, NSGTK_STOCK_GO_FORWARD) + MAKE_STOCKBUTTON(STOP, NSGTK_STOCK_STOP) + MAKE_STOCKBUTTON(RELOAD, NSGTK_STOCK_REFRESH) +#undef MAKE_STOCKBUTTON + + case HISTORY_BUTTON: + w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( + theme->image[HISTORY_BUTTON]), "H")); + break; + + case URL_BAR_ITEM: { + GtkWidget *entry = nsgtk_entry_new(); + w = GTK_WIDGET(gtk_tool_item_new()); + + if ((entry == NULL) || (w == NULL)) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + + nsgtk_entry_set_icon_from_pixbuf(entry, + GTK_ENTRY_ICON_PRIMARY, + favicon_pixbuf); + + gtk_container_add(GTK_CONTAINER(w), entry); + gtk_tool_item_set_expand(GTK_TOOL_ITEM(w), TRUE); + break; + } + + case THROBBER_ITEM: { + if ((nsgtk_throbber == NULL) || + (nsgtk_throbber->framedata == NULL) || + (nsgtk_throbber->framedata[0] == NULL)) { + return NULL; + } + + if (edit_mode) { + w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( + gtk_image_new_from_pixbuf( + nsgtk_throbber->framedata[0])), + "[throbber]")); + } else { + GtkWidget *image; + + w = GTK_WIDGET(gtk_tool_item_new()); + + image = gtk_image_new_from_pixbuf(nsgtk_throbber->framedata[0]); + if (image != NULL) { + nsgtk_widget_set_alignment(image, + GTK_ALIGN_CENTER, + GTK_ALIGN_CENTER); + nsgtk_widget_set_margins(image, 3, 0); + + gtk_container_add(GTK_CONTAINER(w), image); + } + } + break; + } + + case WEBSEARCH_ITEM: { + if (edit_mode) + return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( + nsgtk_image_new_from_stock(NSGTK_STOCK_FIND, + GTK_ICON_SIZE_LARGE_TOOLBAR)), + "[websearch]")); + + GtkWidget *entry = nsgtk_entry_new(); + + w = GTK_WIDGET(gtk_tool_item_new()); + + if ((entry == NULL) || (w == NULL)) { + nsgtk_warning(messages_get("NoMemory"), 0); + return NULL; + } + + gtk_widget_set_size_request(entry, NSGTK_WEBSEARCH_WIDTH, -1); + + nsgtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_PRIMARY, + NSGTK_STOCK_INFO); + + gtk_container_add(GTK_CONTAINER(w), entry); + break; + } + +/* gtk_tool_button_new accepts NULL args */ +#define MAKE_MENUBUTTON(p, q) \ + case p##_BUTTON: { \ + char *label = NULL; \ + label = remove_underscores(messages_get(#q), false); \ + w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \ + theme->image[p##_BUTTON]), label)); \ + if (label != NULL) \ + free(label); \ + break; \ + } + + MAKE_MENUBUTTON(NEWWINDOW, gtkNewWindow) + MAKE_MENUBUTTON(NEWTAB, gtkNewTab) + MAKE_MENUBUTTON(OPENFILE, gtkOpenFile) + MAKE_MENUBUTTON(CLOSETAB, gtkCloseTab) + MAKE_MENUBUTTON(CLOSEWINDOW, gtkCloseWindow) + MAKE_MENUBUTTON(SAVEPAGE, gtkSavePage) + MAKE_MENUBUTTON(PRINTPREVIEW, gtkPrintPreview) + MAKE_MENUBUTTON(PRINT, gtkPrint) + MAKE_MENUBUTTON(QUIT, gtkQuitMenu) + MAKE_MENUBUTTON(CUT, gtkCut) + MAKE_MENUBUTTON(COPY, gtkCopy) + MAKE_MENUBUTTON(PASTE, gtkPaste) + MAKE_MENUBUTTON(DELETE, gtkDelete) + MAKE_MENUBUTTON(SELECTALL, gtkSelectAll) + MAKE_MENUBUTTON(PREFERENCES, gtkPreferences) + MAKE_MENUBUTTON(ZOOMPLUS, gtkZoomPlus) + MAKE_MENUBUTTON(ZOOMMINUS, gtkZoomMinus) + MAKE_MENUBUTTON(ZOOMNORMAL, gtkZoomNormal) + MAKE_MENUBUTTON(FULLSCREEN, gtkFullScreen) + MAKE_MENUBUTTON(VIEWSOURCE, gtkViewSource) + MAKE_MENUBUTTON(CONTENTS, gtkContents) + MAKE_MENUBUTTON(ABOUT, gtkAbout) + MAKE_MENUBUTTON(PDF, gtkPDF) + MAKE_MENUBUTTON(PLAINTEXT, gtkPlainText) + MAKE_MENUBUTTON(DRAWFILE, gtkDrawFile) + MAKE_MENUBUTTON(POSTSCRIPT, gtkPostScript) + MAKE_MENUBUTTON(FIND, gtkFind) + MAKE_MENUBUTTON(DOWNLOADS, gtkDownloads) + MAKE_MENUBUTTON(SAVEWINDOWSIZE, gtkSaveWindowSize) + MAKE_MENUBUTTON(TOGGLEDEBUGGING, gtkToggleDebugging) + MAKE_MENUBUTTON(SAVEBOXTREE, gtkDebugBoxTree) + MAKE_MENUBUTTON(SAVEDOMTREE, gtkDebugDomTree) + MAKE_MENUBUTTON(LOCALHISTORY, gtkLocalHistory) + MAKE_MENUBUTTON(GLOBALHISTORY, gtkGlobalHistory) + MAKE_MENUBUTTON(ADDBOOKMARKS, gtkAddBookMarks) + MAKE_MENUBUTTON(SHOWBOOKMARKS, gtkShowBookMarks) + MAKE_MENUBUTTON(SHOWCOOKIES, gtkShowCookies) + MAKE_MENUBUTTON(OPENLOCATION, gtkOpenLocation) + MAKE_MENUBUTTON(NEXTTAB, gtkNextTab) + MAKE_MENUBUTTON(PREVTAB, gtkPrevTab) + MAKE_MENUBUTTON(GUIDE, gtkGuide) + MAKE_MENUBUTTON(INFO, gtkUserInformation) +#undef MAKE_MENUBUTTON + + default: + break; + + } + return w; +} + +/** + * called when a widget is dropped onto the toolbar + */ +static gboolean +nsgtk_toolbar_data(GtkWidget *widget, + GdkDragContext *gdc, + gint x, + gint y, + guint time, + gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + int ind = gtk_toolbar_get_drop_index(nsgtk_scaffolding_toolbar(g), + x, y); + int q, i; + if (window->currentbutton == -1) + return TRUE; + struct nsgtk_theme *theme = + nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR); + if (theme == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return TRUE; + } + if (nsgtk_scaffolding_button(g, window->currentbutton)->location + != -1) { + /* widget was already in the toolbar; so replace */ + if (nsgtk_scaffolding_button(g, window->currentbutton)-> + location < ind) + ind--; + gtk_container_remove(GTK_CONTAINER( + nsgtk_scaffolding_toolbar(g)), GTK_WIDGET( + nsgtk_scaffolding_button(g, + window->currentbutton)->button)); + /* 'move' all widgets further right than the original location, + * one place to the left in logical schema */ + for (i = nsgtk_scaffolding_button(g, window->currentbutton)-> + location + 1; i < PLACEHOLDER_BUTTON; i++) { + q = nsgtk_toolbar_get_id_at_location(g, i); + if (q == -1) + continue; + nsgtk_scaffolding_button(g, q)->location--; + } + nsgtk_scaffolding_button(g, window->currentbutton)-> + location = -1; + } + nsgtk_scaffolding_button(g, window->currentbutton)->button = + GTK_TOOL_ITEM(nsgtk_toolbar_make_widget(g, + window->currentbutton, theme)); + free(theme); + if (nsgtk_scaffolding_button(g, window->currentbutton)->button + == NULL) { + nsgtk_warning("NoMemory", 0); + return TRUE; + } + /* update logical schema */ + nsgtk_scaffolding_reset_offset(g); + /* 'move' all widgets further right than the new location, one place to + * the right in logical schema */ + for (i = PLACEHOLDER_BUTTON - 1; i >= ind; i--) { + q = nsgtk_toolbar_get_id_at_location(g, i); + if (q == -1) + continue; + nsgtk_scaffolding_button(g, q)->location++; + } + nsgtk_scaffolding_button(g, window->currentbutton)->location = ind; + + /* complete action */ + GtkToolItem *current_button; + + current_button = GTK_TOOL_ITEM(nsgtk_scaffolding_button(g, window->currentbutton)->button); + + gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g), current_button, ind); + + gtk_tool_item_set_use_drag_window(current_button, TRUE); + gtk_drag_source_set(GTK_WIDGET(current_button), + GDK_BUTTON1_MASK, &entry, 1, + GDK_ACTION_COPY); + nsgtk_toolbar_temp_connect(g, window->currentbutton); + gtk_widget_show_all(GTK_WIDGET(current_button)); + + + window->currentbutton = -1; + + return TRUE; +} + +/** + * connected to toolbutton drop; perhaps one day it'll work properly so it may + * replace the global current_button + */ +static gboolean +nsgtk_toolbar_move_complete(GtkWidget *widget, + GdkDragContext *gdc, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time, + gpointer data) +{ + return FALSE; +} + +/** + * called when hovering an item above the toolbar + */ +static gboolean +nsgtk_toolbar_action(GtkWidget *widget, GdkDragContext *gdc, gint x, + gint y, guint time, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + GtkToolItem *item = gtk_tool_button_new(NULL, NULL); + if (item != NULL) + gtk_toolbar_set_drop_highlight_item( + nsgtk_scaffolding_toolbar(g), + GTK_TOOL_ITEM(item), + gtk_toolbar_get_drop_index( + nsgtk_scaffolding_toolbar(g), x, y)); + return FALSE; +} + +/** + * called when hovering stops + */ +static void +nsgtk_toolbar_clear(GtkWidget *widget, GdkDragContext *gdc, guint time, + gpointer data) +{ + gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(widget), NULL, 0); +} + +/** + * add item to toolbar. + * + * the function should be called, when multiple items are being added, + * in ascending order. + * + * \param g the scaffolding whose toolbar an item is added to. + * \param i the location in the toolbar. + * \param theme The theme in use. + */ +static void +nsgtk_toolbar_add_item_to_toolbar(struct nsgtk_scaffolding *g, int i, + struct nsgtk_theme *theme) +{ + int q; + for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++) + if (nsgtk_scaffolding_button(g, q)->location == i) { + nsgtk_scaffolding_button(g, q)->button = GTK_TOOL_ITEM( + nsgtk_toolbar_make_widget(g, q, + theme)); + gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g), + nsgtk_scaffolding_button(g, q)->button, + i); + break; + } +} + +/** + * cleanup code physical update of all toolbars; resensitize + * \param g the 'front' scaffolding that called customize + */ +static void nsgtk_toolbar_close(struct nsgtk_scaffolding *g) +{ + int i; + + struct nsgtk_scaffolding *list; + struct nsgtk_theme *theme; + + list = nsgtk_scaffolding_iterate(NULL); + while (list) { + theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR); + if (theme == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + continue; + } + /* clear toolbar */ + gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar( + list)), nsgtk_toolbar_clear_toolbar, list); + /* then add items */ + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + nsgtk_toolbar_add_item_to_toolbar(list, i, theme); + } + nsgtk_toolbar_connect_all(list); + gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar( + list))); + nsgtk_scaffolding_set_sensitivity(list); + nsgtk_widget_override_background_color(GTK_WIDGET(nsgtk_window_get_layout(nsgtk_scaffolding_top_level(list))), GTK_STATE_NORMAL, 0, 0xFFFF, 0xFFFF, 0xFFFF); + g_signal_handler_unblock(GTK_WIDGET( + nsgtk_window_get_layout( + nsgtk_scaffolding_top_level(list))), + nsgtk_window_get_signalhandler( + nsgtk_scaffolding_top_level(list), + NSGTK_WINDOW_SIGNAL_CLICK)); + g_signal_handler_unblock(GTK_WIDGET( + nsgtk_window_get_layout( + nsgtk_scaffolding_top_level(list))), + nsgtk_window_get_signalhandler( + nsgtk_scaffolding_top_level(list), + NSGTK_WINDOW_SIGNAL_REDRAW)); + browser_window_refresh_url_bar( + nsgtk_get_browser_window( + nsgtk_scaffolding_top_level(list))); + + if (list != g) + gtk_widget_set_sensitive(GTK_WIDGET( + nsgtk_scaffolding_window(list)), TRUE); + free(theme); + list = nsgtk_scaffolding_iterate(list); + } + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)), + TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)), + TRUE); + /* update favicon etc */ + nsgtk_scaffolding_set_top_level(nsgtk_scaffolding_top_level(g)); + + search_web_select_provider(-1); +} + +/** + * when cancel button is clicked + */ +static gboolean nsgtk_toolbar_cancel_clicked(GtkWidget *widget, gpointer data) +{ + edit_mode = false; + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + /* reset g->buttons->location */ + for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + nsgtk_scaffolding_button(g, i)->location = + window->buttonlocations[i]; + } + nsgtk_toolbar_set_physical(g); + nsgtk_toolbar_connect_all(g); + nsgtk_toolbar_close(g); + nsgtk_scaffolding_set_sensitivity(g); + gtk_widget_destroy(window->window); + return TRUE; +} + +/** + * physically add widgets to store window + */ +static bool nsgtk_toolbar_add_store_widget(GtkWidget *widget) +{ + if (window->numberh >= NSGTK_STORE_WIDTH) { + window->currentbar = gtk_toolbar_new(); + if (window->currentbar == NULL) { + nsgtk_warning("NoMemory", 0); + return false; + } + gtk_toolbar_set_style(GTK_TOOLBAR(window->currentbar), + GTK_TOOLBAR_BOTH); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(window->currentbar), + GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_box_pack_start(GTK_BOX(window->widgetvbox), + window->currentbar, FALSE, FALSE, 0); + window->numberh = 0; + } + gtk_widget_set_size_request(widget, NSGTK_BUTTON_WIDTH, + NSGTK_BUTTON_HEIGHT); + gtk_toolbar_insert(GTK_TOOLBAR(window->currentbar), GTK_TOOL_ITEM( + widget), window->numberh++); + gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(widget), TRUE); + gtk_drag_source_set(widget, GDK_BUTTON1_MASK, &entry, 1, + GDK_ACTION_COPY); + gtk_widget_show_all(window->window); + return true; +} + +/** + * save toolbar settings to file + */ +static void nsgtk_toolbar_customization_save(struct nsgtk_scaffolding *g) +{ + int i; + FILE *f = fopen(toolbar_indices_file_location, "w"); + if (f == NULL){ + nsgtk_warning("gtkFileError", toolbar_indices_file_location); + return; + } + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + fprintf(f, "%d;%d|", i, nsgtk_scaffolding_button(g, i)->location); + } + fclose(f); +} + +/** + * cast toolbar settings to all scaffoldings referenced from the global linked + * list of gui_windows + */ +static void nsgtk_toolbar_cast(struct nsgtk_scaffolding *g) +{ + int i; + struct nsgtk_scaffolding *list; + + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + window->buttonlocations[i] = + ((nsgtk_scaffolding_button(g, i)->location + >= -1) && + (nsgtk_scaffolding_button(g, i)->location + < PLACEHOLDER_BUTTON)) ? + nsgtk_scaffolding_button(g, i)->location : -1; + } + + list = nsgtk_scaffolding_iterate(NULL); + while (list) { + if (list != g) + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) + nsgtk_scaffolding_button(list, i)->location = + window->buttonlocations[i]; + list = nsgtk_scaffolding_iterate(list); + } +} + +/** + * when 'save settings' button is clicked + */ +static gboolean nsgtk_toolbar_persist(GtkWidget *widget, gpointer data) +{ + edit_mode = false; + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + /* save state to file, update toolbars for all windows */ + nsgtk_toolbar_customization_save(g); + nsgtk_toolbar_cast(g); + nsgtk_toolbar_set_physical(g); + nsgtk_toolbar_close(g); + gtk_widget_destroy(window->window); + return TRUE; +} + +/** + * when 'reload defaults' button is clicked + */ +static gboolean nsgtk_toolbar_reset(GtkWidget *widget, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + int i; + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) + nsgtk_scaffolding_button(g, i)->location = + (i <= THROBBER_ITEM) ? i : -1; + nsgtk_toolbar_set_physical(g); + for (i = BACK_BUTTON; i <= THROBBER_ITEM; i++) { + if (i == URL_BAR_ITEM) + continue; + gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM( + nsgtk_scaffolding_button(g, i)->button), TRUE); + gtk_drag_source_set(GTK_WIDGET( + nsgtk_scaffolding_button(g, i)->button), + GDK_BUTTON1_MASK, &entry, 1, GDK_ACTION_COPY); + nsgtk_toolbar_temp_connect(g, i); + } + return TRUE; +} + +/** + * when titlebar / alt-F4 window close event happens + */ +static gboolean nsgtk_toolbar_delete(GtkWidget *widget, GdkEvent *event, + gpointer data) +{ + edit_mode = false; + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + /* reset g->buttons->location */ + for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + nsgtk_scaffolding_button(g, i)->location = + window->buttonlocations[i]; + } + nsgtk_toolbar_set_physical(g); + nsgtk_toolbar_connect_all(g); + nsgtk_toolbar_close(g); + nsgtk_scaffolding_set_sensitivity(g); + gtk_widget_destroy(window->window); + return TRUE; +} + +/** + * called when a widget is dropped onto the store window + */ +static gboolean +nsgtk_toolbar_store_return(GtkWidget *widget, GdkDragContext *gdc, + gint x, gint y, guint time, gpointer data) +{ + struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data; + int q, i; + + if ((window->fromstore) || (window->currentbutton == -1)) { + window->currentbutton = -1; + return FALSE; + } + if (nsgtk_scaffolding_button(g, window->currentbutton)->location + != -1) { + /* 'move' all widgets further right, one place to the left + * in logical schema */ + for (i = nsgtk_scaffolding_button(g, window->currentbutton)-> + location + 1; i < PLACEHOLDER_BUTTON; i++) { + q = nsgtk_toolbar_get_id_at_location(g, i); + if (q == -1) + continue; + nsgtk_scaffolding_button(g, q)->location--; + } + gtk_container_remove(GTK_CONTAINER( + nsgtk_scaffolding_toolbar(g)), GTK_WIDGET( + nsgtk_scaffolding_button(g, + window->currentbutton)->button)); + nsgtk_scaffolding_button(g, window->currentbutton)->location + = -1; + } + window->currentbutton = -1; + gtk_drag_finish(gdc, TRUE, TRUE, time); + return FALSE; +} + +/** + * called when hovering above the store + */ +static gboolean +nsgtk_toolbar_store_action(GtkWidget *widget, GdkDragContext *gdc, + gint x, gint y, guint time, gpointer data) +{ + return FALSE; +} + +/** + * create store window + */ +static void nsgtk_toolbar_window_open(struct nsgtk_scaffolding *g) +{ + int x = 0, y = 0; + struct nsgtk_theme *theme; + nserror res; + + theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR); + if (theme == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + nsgtk_toolbar_cancel_clicked(NULL, g); + return; + } + + res = nsgtk_builder_new_from_resname("toolbar", &window->builder); + if (res != NSERROR_OK) { + LOG("Toolbar UI builder init failed"); + nsgtk_warning(messages_get("NoMemory"), 0); + nsgtk_toolbar_cancel_clicked(NULL, g); + free(theme); + return; + } + + gtk_builder_connect_signals(window->builder, NULL); + + window->window = GTK_WIDGET(gtk_builder_get_object(window->builder, + "toolbarwindow")); + if (window->window == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + nsgtk_toolbar_cancel_clicked(NULL, g); + free(theme); + return; + } + + window->widgetvbox = GTK_WIDGET(gtk_builder_get_object(window->builder, + "widgetvbox")); + if (window->widgetvbox == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + nsgtk_toolbar_cancel_clicked(NULL, g); + free(theme); + return; + } + + window->numberh = NSGTK_STORE_WIDTH; /* preset to width [in buttons] of */ + /* store to cause creation of a new toolbar */ + window->currentbutton = -1; + /* load toolbuttons */ + /* add toolbuttons to window */ + /* set event handlers */ + for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + if (i == URL_BAR_ITEM) + continue; + window->store_buttons[i] = + nsgtk_toolbar_make_widget(g, i, theme); + if (window->store_buttons[i] == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + continue; + } + nsgtk_toolbar_add_store_widget(window->store_buttons[i]); + g_signal_connect(window->store_buttons[i], "drag-data-get", + G_CALLBACK( + nsgtk_scaffolding_button(g, i)->dataplus), g); + } + free(theme); + + gtk_window_set_transient_for(GTK_WINDOW(window->window), + nsgtk_scaffolding_window(g)); + gtk_window_set_title(GTK_WINDOW(window->window), messages_get( + "gtkToolBarTitle")); + gtk_window_set_accept_focus(GTK_WINDOW(window->window), FALSE); + gtk_drag_dest_set(GTK_WIDGET(window->window), GTK_DEST_DEFAULT_MOTION | + GTK_DEST_DEFAULT_DROP, &entry, 1, GDK_ACTION_COPY); + gtk_widget_show_all(window->window); + gtk_window_set_position(GTK_WINDOW(window->window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_get_position(nsgtk_scaffolding_window(g), &x, &y); + gtk_window_move(GTK_WINDOW(window->window), x, y + 100); + + g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder, + "cancelbutton")), + "clicked", + G_CALLBACK(nsgtk_toolbar_cancel_clicked), + g); + + g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder, + "okbutton")), + "clicked", + G_CALLBACK(nsgtk_toolbar_persist), + g); + + g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder, + "resetbutton")), + "clicked", + G_CALLBACK(nsgtk_toolbar_reset), + g); + + g_signal_connect(window->window, "delete-event", + G_CALLBACK(nsgtk_toolbar_delete), g); + + g_signal_connect(window->window, "drag-drop", + G_CALLBACK(nsgtk_toolbar_store_return), g); + + g_signal_connect(window->window, "drag-motion", + G_CALLBACK(nsgtk_toolbar_store_action), g); +} + +/** + * change behaviour of scaffoldings while editing toolbar + * + * All buttons as well as window clicks are desensitized; then buttons + * in the front window are changed to movable buttons + */ +void nsgtk_toolbar_customization_init(struct nsgtk_scaffolding *g) +{ + int i; + struct nsgtk_scaffolding *list; + edit_mode = true; + + list = nsgtk_scaffolding_iterate(NULL); + while (list) { + g_signal_handler_block(GTK_WIDGET( + nsgtk_window_get_layout( + nsgtk_scaffolding_top_level(list))), + nsgtk_window_get_signalhandler( + nsgtk_scaffolding_top_level(list), + NSGTK_WINDOW_SIGNAL_CLICK)); + g_signal_handler_block(GTK_WIDGET( + nsgtk_window_get_layout( + nsgtk_scaffolding_top_level(list))), + nsgtk_window_get_signalhandler( + nsgtk_scaffolding_top_level(list), + NSGTK_WINDOW_SIGNAL_REDRAW)); + nsgtk_widget_override_background_color( + GTK_WIDGET(nsgtk_window_get_layout( + nsgtk_scaffolding_top_level(list))), + GTK_STATE_NORMAL, 0, 0xEEEE, 0xEEEE, 0xEEEE); + + if (list == g) { + list = nsgtk_scaffolding_iterate(list); + continue; + } + /* set sensitive for all gui_windows save g */ + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_window( + list)), FALSE); + list = nsgtk_scaffolding_iterate(list); + } + /* set sensitive for all of g save toolbar */ + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)), + FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)), + FALSE); + + /* set editable aspect for toolbar */ + gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)), + nsgtk_toolbar_clear_toolbar, g); + nsgtk_toolbar_set_physical(g); + /* memorize button locations, set editable */ + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + window->buttonlocations[i] = nsgtk_scaffolding_button(g, i) + ->location; + if ((window->buttonlocations[i] == -1) || (i == URL_BAR_ITEM)) + continue; + gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM( + nsgtk_scaffolding_button(g, i)->button), TRUE); + gtk_drag_source_set(GTK_WIDGET(nsgtk_scaffolding_button( + g, i)->button), GDK_BUTTON1_MASK, &entry, 1, + GDK_ACTION_COPY); + nsgtk_toolbar_temp_connect(g, i); + } + + /* add move button listeners */ + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), + "drag-drop", G_CALLBACK(nsgtk_toolbar_data), g); + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), + "drag-data-received", G_CALLBACK( + nsgtk_toolbar_move_complete), g); + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), + "drag-motion", G_CALLBACK(nsgtk_toolbar_action), g); + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), + "drag-leave", G_CALLBACK( + nsgtk_toolbar_clear), g); + + /* set data types */ + gtk_drag_dest_set(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)), + GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, + &entry, 1, GDK_ACTION_COPY); + + /* open toolbar window */ + nsgtk_toolbar_window_open(g); +} + +/** + * set toolbar logical -> physical; physically visible toolbar buttons are made + * to correspond to the logically stored schema in terms of location + * visibility etc + */ +void nsgtk_toolbar_set_physical(struct nsgtk_scaffolding *g) +{ + int i; + struct nsgtk_theme *theme = + nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR); + if (theme == NULL) { + nsgtk_warning(messages_get("NoMemory"), 0); + return; + } + /* simplest is to clear the toolbar then reload it from memory */ + gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)), + nsgtk_toolbar_clear_toolbar, g); + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) + nsgtk_toolbar_add_item_to_toolbar(g, i, theme); + gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(g))); + free(theme); +} + +/** + * \return toolbar item id when a widget is an element of the scaffolding + * else -1 + */ +int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, + struct nsgtk_scaffolding *g) +{ + int i; + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + if ((nsgtk_scaffolding_button(g, i)->location != -1) + && (widget == GTK_WIDGET( + nsgtk_scaffolding_button(g, i)->button))) { + return i; + } + } + return -1; +} + + +/** + * add handlers to factory widgets + * \param g the scaffolding to attach handlers to + * \param i the toolbar item id + */ +static void +nsgtk_toolbar_set_handler(struct nsgtk_scaffolding *g, nsgtk_toolbar_button i) +{ + switch(i){ + case URL_BAR_ITEM: + nsgtk_scaffolding_update_url_bar_ref(g); + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)), + "activate", G_CALLBACK( + nsgtk_window_url_activate_event), g); + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)), + "changed", G_CALLBACK( + nsgtk_window_url_changed), g); + break; + case THROBBER_ITEM: + nsgtk_scaffolding_update_throbber_ref(g); + break; + case WEBSEARCH_ITEM: + nsgtk_scaffolding_update_websearch_ref(g); + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)), + "activate", G_CALLBACK( + nsgtk_websearch_activate), g); + g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)), + "button-press-event", G_CALLBACK( + nsgtk_websearch_clear), g); + break; + default: + if ((nsgtk_scaffolding_button(g, i)->bhandler != NULL) && + (nsgtk_scaffolding_button(g, i)->button + != NULL)) + g_signal_connect(nsgtk_scaffolding_button(g, i)-> + button, "clicked", + G_CALLBACK(nsgtk_scaffolding_button(g, + i)->bhandler), g); + break; + } +} + +/** + * connect 'normal' handlers to toolbar buttons + */ +void nsgtk_toolbar_connect_all(struct nsgtk_scaffolding *g) +{ + int q, i; + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { + q = nsgtk_toolbar_get_id_at_location(g, i); + if (q == -1) + continue; + if (nsgtk_scaffolding_button(g, q)->button != NULL) + g_signal_connect( + nsgtk_scaffolding_button(g, q)->button, + "size-allocate", G_CALLBACK( + nsgtk_scaffolding_toolbar_size_allocate + ), g); + nsgtk_toolbar_set_handler(g, q); + } +} + + +#define DATAHANDLER(p, q, r)\ +gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\ + *cont, GtkSelectionData *selection, guint info, guint time,\ + gpointer data)\ +{\ + r->currentbutton = q##_BUTTON;\ + r->fromstore = true;\ + return TRUE;\ +}\ +gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\ + GdkDragContext *cont, GtkSelectionData *selection, guint info,\ + guint time, gpointer data)\ +{\ + r->currentbutton = q##_BUTTON;\ + r->fromstore = false;\ + return TRUE;\ +} + +DATAHANDLER(home, HOME, window) +DATAHANDLER(forward, FORWARD, window) +DATAHANDLER(back, BACK, window) +DATAHANDLER(stop, STOP, window) +DATAHANDLER(reload, RELOAD, window) +DATAHANDLER(history, HISTORY, window) +DATAHANDLER(newwindow, NEWWINDOW, window) +DATAHANDLER(newtab, NEWTAB, window) +DATAHANDLER(openfile, OPENFILE, window) +DATAHANDLER(closetab, CLOSETAB, window) +DATAHANDLER(closewindow, CLOSEWINDOW, window) +DATAHANDLER(savepage, SAVEPAGE, window) +DATAHANDLER(printpreview, PRINTPREVIEW, window) +DATAHANDLER(print, PRINT, window) +DATAHANDLER(quit, QUIT, window) +DATAHANDLER(cut, CUT, window) +DATAHANDLER(copy, COPY, window) +DATAHANDLER(paste, PASTE, window) +DATAHANDLER(delete, DELETE, window) +DATAHANDLER(selectall, SELECTALL, window) +DATAHANDLER(preferences, PREFERENCES, window) +DATAHANDLER(zoomplus, ZOOMPLUS, window) +DATAHANDLER(zoomminus, ZOOMMINUS, window) +DATAHANDLER(zoomnormal, ZOOMNORMAL, window) +DATAHANDLER(fullscreen, FULLSCREEN, window) +DATAHANDLER(viewsource, VIEWSOURCE, window) +DATAHANDLER(contents, CONTENTS, window) +DATAHANDLER(about, ABOUT, window) +DATAHANDLER(pdf, PDF, window) +DATAHANDLER(plaintext, PLAINTEXT, window) +DATAHANDLER(drawfile, DRAWFILE, window) +DATAHANDLER(postscript, POSTSCRIPT, window) +DATAHANDLER(find, FIND, window) +DATAHANDLER(downloads, DOWNLOADS, window) +DATAHANDLER(savewindowsize, SAVEWINDOWSIZE, window) +DATAHANDLER(toggledebugging, TOGGLEDEBUGGING, window) +DATAHANDLER(debugboxtree, SAVEBOXTREE, window) +DATAHANDLER(debugdomtree, SAVEDOMTREE, window) +DATAHANDLER(localhistory, LOCALHISTORY, window) +DATAHANDLER(globalhistory, GLOBALHISTORY, window) +DATAHANDLER(addbookmarks, ADDBOOKMARKS, window) +DATAHANDLER(showbookmarks, SHOWBOOKMARKS, window) +DATAHANDLER(showcookies, SHOWCOOKIES, window) +DATAHANDLER(openlocation, OPENLOCATION, window) +DATAHANDLER(nexttab, NEXTTAB, window) +DATAHANDLER(prevtab, PREVTAB, window) +DATAHANDLER(guide, GUIDE, window) +DATAHANDLER(info, INFO, window) +#undef DATAHANDLER +#define DATAHANDLER(p, q, r)\ +gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\ + *cont, GtkSelectionData *selection, guint info, guint time,\ + gpointer data)\ +{\ + r->currentbutton = q##_ITEM;\ + r->fromstore = true;\ + return TRUE;\ +}\ +gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\ + GdkDragContext *cont, GtkSelectionData *selection, guint info,\ + guint time, gpointer data)\ +{\ + r->currentbutton = q##_ITEM;\ + r->fromstore = false;\ + return TRUE;\ +} + +DATAHANDLER(throbber, THROBBER, window) +DATAHANDLER(websearch, WEBSEARCH, window) +#undef DATAHANDLER + + +/** + * load toolbar settings from file; file is a set of fields arranged as + * [itemreference];[itemlocation]|[itemreference];[itemlocation]| etc + */ +void nsgtk_toolbar_customization_load(struct nsgtk_scaffolding *g) +{ + int i, ii; + char *val; + char buffer[SLEN("11;|") * 2 * PLACEHOLDER_BUTTON]; /* numbers 0-99 */ + buffer[0] = '\0'; + char *buffer1, *subbuffer, *ptr = NULL, *pter = NULL; + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) + nsgtk_scaffolding_button(g, i)->location = + (i <= THROBBER_ITEM) ? i : -1; + FILE *f = fopen(toolbar_indices_file_location, "r"); + if (f == NULL) { + nsgtk_warning(messages_get("gtkFileError"), + toolbar_indices_file_location); + return; + } + val = fgets(buffer, sizeof buffer, f); + if (val == NULL) { + LOG("empty read toolbar settings"); + } + fclose(f); + i = BACK_BUTTON; + ii = BACK_BUTTON; + buffer1 = strtok_r(buffer, "|", &ptr); + while (buffer1 != NULL) { + subbuffer = strtok_r(buffer1, ";", &pter); + i = atoi(subbuffer); + subbuffer = strtok_r(NULL, ";", &pter); + ii = atoi(subbuffer); + if ((i >= BACK_BUTTON) && (i < PLACEHOLDER_BUTTON) && + (ii >= -1) && (ii < PLACEHOLDER_BUTTON)) { + nsgtk_scaffolding_button(g, i)->location = ii; + } + buffer1 = strtok_r(NULL, "|", &ptr); + } +} diff --git a/frontends/gtk/toolbar.h b/frontends/gtk/toolbar.h new file mode 100644 index 000000000..0453109ca --- /dev/null +++ b/frontends/gtk/toolbar.h @@ -0,0 +1,96 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + +#ifndef _NETSURF_GTK_TOOLBAR_H_ +#define _NETSURF_GTK_TOOLBAR_H_ + +#include + +#include "gtk/scaffolding.h" + +/** + * sets up the images for scaffolding. + */ +void nsgtk_theme_implement(struct nsgtk_scaffolding *g); + +void nsgtk_toolbar_customization_init(struct nsgtk_scaffolding *g); +void nsgtk_toolbar_init(struct nsgtk_scaffolding *g); +void nsgtk_toolbar_customization_load(struct nsgtk_scaffolding *g); +void nsgtk_toolbar_set_physical(struct nsgtk_scaffolding *g); +void nsgtk_toolbar_connect_all(struct nsgtk_scaffolding *g); +int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, struct nsgtk_scaffolding *g); + +#define TOOLPROTO(q) gboolean nsgtk_toolbar_##q##_button_data(\ + GtkWidget *widget, GdkDragContext *cont, GtkSelectionData\ + *selection, guint info, guint time, gpointer data);\ +gboolean nsgtk_toolbar_##q##_toolbar_button_data(GtkWidget *widget,\ + GdkDragContext *cont, GtkSelectionData *selection, guint info,\ + guint time, gpointer data) +TOOLPROTO(home); +TOOLPROTO(back); +TOOLPROTO(forward); +TOOLPROTO(reload); +TOOLPROTO(stop); +TOOLPROTO(throbber); +TOOLPROTO(websearch); +TOOLPROTO(history); +TOOLPROTO(newwindow); +TOOLPROTO(newtab); +TOOLPROTO(openfile); +TOOLPROTO(closetab); +TOOLPROTO(closewindow); +TOOLPROTO(savepage); +TOOLPROTO(pdf); +TOOLPROTO(plaintext); +TOOLPROTO(drawfile); +TOOLPROTO(postscript); +TOOLPROTO(printpreview); +TOOLPROTO(print); +TOOLPROTO(quit); +TOOLPROTO(cut); +TOOLPROTO(copy); +TOOLPROTO(paste); +TOOLPROTO(delete); +TOOLPROTO(selectall); +TOOLPROTO(find); +TOOLPROTO(preferences); +TOOLPROTO(zoomplus); +TOOLPROTO(zoomminus); +TOOLPROTO(zoomnormal); +TOOLPROTO(fullscreen); +TOOLPROTO(viewsource); +TOOLPROTO(downloads); +TOOLPROTO(localhistory); +TOOLPROTO(globalhistory); +TOOLPROTO(addbookmarks); +TOOLPROTO(showbookmarks); +TOOLPROTO(showcookies); +TOOLPROTO(openlocation); +TOOLPROTO(nexttab); +TOOLPROTO(prevtab); +TOOLPROTO(savewindowsize); +TOOLPROTO(toggledebugging); +TOOLPROTO(debugboxtree); +TOOLPROTO(debugdomtree); +TOOLPROTO(contents); +TOOLPROTO(guide); +TOOLPROTO(info); +TOOLPROTO(about); +#undef TOOLPROTO + +#endif diff --git a/frontends/gtk/treeview.c b/frontends/gtk/treeview.c new file mode 100644 index 000000000..9baf57b62 --- /dev/null +++ b/frontends/gtk/treeview.c @@ -0,0 +1,586 @@ +/* + * Copyright 2004 Richard Wilson + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +/** \file + * Generic tree handling (implementation). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utf8.h" +#include "desktop/tree.h" +#include "desktop/plotters.h" + +#include "gtk/warn.h" +#include "gtk/compat.h" +#include "gtk/gui.h" +#include "gtk/plotters.h" +#include "gtk/treeview.h" + +struct nsgtk_treeview { + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + GtkIMContext *input_method; + bool mouse_pressed; + int mouse_pressed_x; + int mouse_pressed_y; + int last_x, last_y; + browser_mouse_state mouse_state; + struct tree *tree; +}; + +void nsgtk_treeview_destroy(struct nsgtk_treeview *tv) +{ + tree_delete(tv->tree); + g_object_unref(tv->input_method); + gtk_widget_destroy(GTK_WIDGET(tv->window)); + free(tv); +} + +struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv) +{ + return tv->tree; +} + +static void nsgtk_tree_redraw_request(int x, int y, int width, int height, void *data) +{ + struct nsgtk_treeview *tw = data; + + gtk_widget_queue_draw_area(GTK_WIDGET(tw->drawing_area), + x, y, width, height); +} + + +/** + * Updates the tree owner following a tree resize + * + * \param tree the tree to update the owner of + * \param width The width to resize to. + * \param height The height to resize to. + * \param data The treeview resize. + */ +static void nsgtk_tree_resized(struct tree *tree, int width, int height, void *data) +{ + struct nsgtk_treeview *tw = data; + + gtk_widget_set_size_request(GTK_WIDGET(tw->drawing_area), + width, height); + return; +} + +/** + * Scrolls the tree to make an element visible + * + * \param y Y coordinate of the element + * \param height height of the element + * \param data user data assigned to the tree on tree creation + */ +static void nsgtk_tree_scroll_visible(int y, int height, void *data) +{ + int y0, y1; + gdouble page; + struct nsgtk_treeview *tw = data; + GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled); + + assert(vadj); + + g_object_get(vadj, "page-size", &page, NULL); + + y0 = (int)(gtk_adjustment_get_value(vadj)); + y1 = y0 + page; + + if ((y >= y0) && (y + height <= y1)) + return; + if (y + height > y1) + y0 = y0 + (y + height - y1); + if (y < y0) + y0 = y; + gtk_adjustment_set_value(vadj, y0); +} + + +/** + * Retrieves the dimensions of the window with the tree + * + * \param data user data assigned to the tree on tree creation + * \param width will be updated to window width if not NULL + * \param height will be updated to window height if not NULL + */ +static void nsgtk_tree_get_window_dimensions(int *width, int *height, void *data) +{ + struct nsgtk_treeview *tw = data; + GtkAdjustment *vadj; + GtkAdjustment *hadj; + gdouble page; + + if (width != NULL) { + hadj = gtk_scrolled_window_get_hadjustment(tw->scrolled); + g_object_get(hadj, "page-size", &page, NULL); + *width = page; + } + + if (height != NULL) { + vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled); + g_object_get(vadj, "page-size", &page, NULL); + *height = page; + } +} + +#if GTK_CHECK_VERSION(3,0,0) + +static gboolean +nsgtk_tree_window_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data) +{ + struct tree *tree = (struct tree *)data; + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &nsgtk_plotters + }; + double x1; + double y1; + double x2; + double y2; + + current_widget = widget; + current_cr = cr; + + cairo_clip_extents(cr, &x1, &y1, &x2, &y2); + + tree_draw(tree, 0, 0, x1, y1, x2 - x1, y2 - y1, &ctx); + + current_widget = NULL; + + return FALSE; +} + +#else + +/* signal handler functions for a tree window */ +static gboolean +nsgtk_tree_window_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer g) +{ + struct tree *tree = (struct tree *) g; + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &nsgtk_plotters + }; + int x, y, width, height; + + x = event->area.x; + y = event->area.y; + width = event->area.width; + height = event->area.height; + + current_widget = widget; + current_cr = gdk_cairo_create(nsgtk_widget_get_window(widget)); + + tree_draw(tree, 0, 0, x, y, width, height, &ctx); + + current_widget = NULL; + cairo_destroy(current_cr); + + return FALSE; +} + +#endif + +void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g) +{ +} + +static gboolean +nsgtk_tree_window_button_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer g) +{ + struct nsgtk_treeview *tw = g; + struct tree *tree = tw->tree; + + gtk_im_context_reset(tw->input_method); + gtk_widget_grab_focus(GTK_WIDGET(tw->drawing_area)); + + tw->mouse_pressed = true; + tw->mouse_pressed_x = event->x; + tw->mouse_pressed_y = event->y; + + if (event->type == GDK_2BUTTON_PRESS) + tw->mouse_state = BROWSER_MOUSE_DOUBLE_CLICK; + + switch (event->button) { + case 1: tw->mouse_state |= BROWSER_MOUSE_PRESS_1; break; + case 2: tw->mouse_state |= BROWSER_MOUSE_PRESS_2; break; + } + /* Handle the modifiers too */ + if (event->state & GDK_SHIFT_MASK) + tw->mouse_state |= BROWSER_MOUSE_MOD_1; + if (event->state & GDK_CONTROL_MASK) + tw->mouse_state |= BROWSER_MOUSE_MOD_2; + if (event->state & GDK_MOD1_MASK) + tw->mouse_state |= BROWSER_MOUSE_MOD_3; + + /* Record where we pressed, for use when determining whether to start + * a drag in motion notify events. */ + tw->last_x = event->x; + tw->last_y = event->y; + + tree_mouse_action(tree, tw->mouse_state, event->x, event->y); + + return TRUE; +} + +static gboolean +nsgtk_tree_window_button_release_event(GtkWidget *widget, + GdkEventButton *event, gpointer g) +{ + bool shift = event->state & GDK_SHIFT_MASK; + bool ctrl = event->state & GDK_CONTROL_MASK; + bool alt = event->state & GDK_MOD1_MASK; + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + struct tree *tree = tw->tree; + + /* We consider only button 1 clicks as double clicks. + * If the mouse state is PRESS then we are waiting for a release to emit + * a click event, otherwise just reset the state to nothing*/ + if (tw->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) { + + if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) + tw->mouse_state ^= BROWSER_MOUSE_PRESS_1 | + BROWSER_MOUSE_CLICK_1; + else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2) + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 | + BROWSER_MOUSE_CLICK_2 | + BROWSER_MOUSE_DOUBLE_CLICK); + + } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) { + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 | + BROWSER_MOUSE_CLICK_1); + } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2) { + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 | + BROWSER_MOUSE_CLICK_2); + } else if (tw->mouse_state & BROWSER_MOUSE_HOLDING_1) { + tw->mouse_state ^= (BROWSER_MOUSE_HOLDING_1 | + BROWSER_MOUSE_DRAG_ON); + } else if (tw->mouse_state & BROWSER_MOUSE_HOLDING_2) { + tw->mouse_state ^= (BROWSER_MOUSE_HOLDING_2 | + BROWSER_MOUSE_DRAG_ON); + } + + /* Handle modifiers being removed */ + if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift) + tw->mouse_state ^= BROWSER_MOUSE_MOD_1; + if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl) + tw->mouse_state ^= BROWSER_MOUSE_MOD_2; + if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt) + tw->mouse_state ^= BROWSER_MOUSE_MOD_3; + + + if (tw->mouse_state & + ~(BROWSER_MOUSE_MOD_1 | + BROWSER_MOUSE_MOD_2 | + BROWSER_MOUSE_MOD_3)) { + tree_mouse_action(tree, tw->mouse_state, + event->x, event->y); + } else { + tree_drag_end(tree, tw->mouse_state, + tw->mouse_pressed_x, + tw->mouse_pressed_y, + event->x, event->y); + } + + tw->mouse_state = 0; + tw->mouse_pressed = false; + + return TRUE; +} + +static gboolean +nsgtk_tree_window_motion_notify_event(GtkWidget *widget, + GdkEventMotion *event, gpointer g) +{ + bool shift = event->state & GDK_SHIFT_MASK; + bool ctrl = event->state & GDK_CONTROL_MASK; + bool alt = event->state & GDK_MOD1_MASK; + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + struct tree *tree = tw->tree; + + if (tw->mouse_pressed == false) + return TRUE; + + if ((fabs(event->x - tw->last_x) < 5.0) && + (fabs(event->y - tw->last_y) < 5.0)) { + /* Mouse hasn't moved far enough from press coordinate + * for this to be considered a drag. + */ + return FALSE; + } else { + /* This is a drag, ensure it's always treated as such, + * even if we drag back over the press location. + */ + tw->last_x = INT_MIN; + tw->last_y = INT_MIN; + } + + if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) { + /* Start button 1 drag */ + tree_mouse_action(tree, BROWSER_MOUSE_DRAG_1, + tw->mouse_pressed_x, tw->mouse_pressed_y); + /* Replace PRESS with HOLDING and declare drag in progress */ + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 | + BROWSER_MOUSE_HOLDING_1); + tw->mouse_state |= BROWSER_MOUSE_DRAG_ON; + return TRUE; + } + else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2){ + /* Start button 2s drag */ + tree_mouse_action(tree, BROWSER_MOUSE_DRAG_2, + tw->mouse_pressed_x, tw->mouse_pressed_y); + /* Replace PRESS with HOLDING and declare drag in progress */ + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 | + BROWSER_MOUSE_HOLDING_2); + tw->mouse_state |= BROWSER_MOUSE_DRAG_ON; + return TRUE; + } + + /* Handle modifiers being removed */ + if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift) + tw->mouse_state ^= BROWSER_MOUSE_MOD_1; + if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl) + tw->mouse_state ^= BROWSER_MOUSE_MOD_2; + if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt) + tw->mouse_state ^= BROWSER_MOUSE_MOD_3; + + if (tw->mouse_state & (BROWSER_MOUSE_HOLDING_1 | + BROWSER_MOUSE_HOLDING_2)) + tree_mouse_action(tree, tw->mouse_state, event->x, + event->y); + + return TRUE; +} + + +static gboolean +nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, + gpointer g) +{ + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + struct tree *tree = tw->tree; + uint32_t nskey; + double value; + GtkAdjustment *vscroll; + GtkAdjustment *hscroll; + GtkAdjustment *scroll = NULL; + gdouble hpage, vpage; + + if (gtk_im_context_filter_keypress(tw->input_method, event)) + return TRUE; + + nskey = gtk_gui_gdkkey_to_nskey(event); + + if (tree_keypress(tree, nskey) == true) + return TRUE; + + vscroll = gtk_scrolled_window_get_vadjustment(tw->scrolled); + hscroll = gtk_scrolled_window_get_hadjustment(tw->scrolled); + g_object_get(vscroll, "page-size", &vpage, NULL); + g_object_get(hscroll, "page-size", &hpage, NULL); + + switch (event->keyval) { + case GDK_KEY(Home): + case GDK_KEY(KP_Home): + scroll = vscroll; + value = nsgtk_adjustment_get_lower(scroll); + break; + + case GDK_KEY(End): + case GDK_KEY(KP_End): + scroll = vscroll; + value = nsgtk_adjustment_get_upper(scroll) - vpage; + if (value < nsgtk_adjustment_get_lower(scroll)) + value = nsgtk_adjustment_get_lower(scroll); + break; + + case GDK_KEY(Left): + case GDK_KEY(KP_Left): + scroll = hscroll; + value = gtk_adjustment_get_value(scroll) - + nsgtk_adjustment_get_step_increment(scroll); + if (value < nsgtk_adjustment_get_lower(scroll)) + value = nsgtk_adjustment_get_lower(scroll); + break; + + case GDK_KEY(Up): + case GDK_KEY(KP_Up): + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) - + nsgtk_adjustment_get_step_increment(scroll); + if (value < nsgtk_adjustment_get_lower(scroll)) + value = nsgtk_adjustment_get_lower(scroll); + break; + + case GDK_KEY(Right): + case GDK_KEY(KP_Right): + scroll = hscroll; + value = gtk_adjustment_get_value(scroll) + + nsgtk_adjustment_get_step_increment(scroll); + if (value > nsgtk_adjustment_get_upper(scroll) - hpage) + value = nsgtk_adjustment_get_upper(scroll) - hpage; + break; + + case GDK_KEY(Down): + case GDK_KEY(KP_Down): + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) + + nsgtk_adjustment_get_step_increment(scroll); + if (value > nsgtk_adjustment_get_upper(scroll) - vpage) + value = nsgtk_adjustment_get_upper(scroll) - vpage; + break; + + case GDK_KEY(Page_Up): + case GDK_KEY(KP_Page_Up): + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) - + nsgtk_adjustment_get_page_increment(scroll); + + if (value < nsgtk_adjustment_get_lower(scroll)) + value = nsgtk_adjustment_get_lower(scroll); + + break; + + case GDK_KEY(Page_Down): + case GDK_KEY(KP_Page_Down): + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) + + nsgtk_adjustment_get_page_increment(scroll); + + if (value > nsgtk_adjustment_get_upper(scroll) - vpage) + value = nsgtk_adjustment_get_upper(scroll) - vpage; + break; + + default: + break; + } + + if (scroll != NULL) + gtk_adjustment_set_value(scroll, value); + + return TRUE; +} + +static gboolean +nsgtk_tree_window_keyrelease_event(GtkWidget *widget, GdkEventKey *event, + gpointer g) +{ + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + + return gtk_im_context_filter_keypress(tw->input_method, event); +} + +static void +nsgtk_tree_window_input_method_commit(GtkIMContext *ctx, + const gchar *str, gpointer data) +{ + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) data; + size_t len = strlen(str), offset = 0; + + while (offset < len) { + uint32_t nskey = utf8_to_ucs4(str + offset, len - offset); + + tree_keypress(tw->tree, nskey); + + offset = utf8_next(str, len, offset); + } +} + + +static const struct treeview_table nsgtk_tree_callbacks = { + .redraw_request = nsgtk_tree_redraw_request, + .resized = nsgtk_tree_resized, + .scroll_visible = nsgtk_tree_scroll_visible, + .get_window_dimensions = nsgtk_tree_get_window_dimensions +}; + +struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags, + GtkWindow *window, GtkScrolledWindow *scrolled, + GtkDrawingArea *drawing_area) +{ + struct nsgtk_treeview *tv; + + assert(drawing_area != NULL); + + tv = malloc(sizeof(struct nsgtk_treeview)); + if (tv == NULL) { + LOG("malloc failed"); + nsgtk_warning("NoMemory", 0); + return NULL; + } + + tv->window = window; + tv->scrolled = scrolled; + tv->drawing_area = drawing_area; + tv->input_method = gtk_im_multicontext_new(); + tv->tree = tree_create(flags, &nsgtk_tree_callbacks, tv); + tv->mouse_state = 0; + tv->mouse_pressed = false; + + nsgtk_widget_override_background_color(GTK_WIDGET(drawing_area), + GTK_STATE_NORMAL, + 0, 0xffff, 0xffff, 0xffff); + + nsgtk_connect_draw_event(GTK_WIDGET(drawing_area), G_CALLBACK(nsgtk_tree_window_draw_event), tv->tree); + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + CONNECT(drawing_area, "button-press-event", + nsgtk_tree_window_button_press_event, + tv); + CONNECT(drawing_area, "button-release-event", + nsgtk_tree_window_button_release_event, + tv); + CONNECT(drawing_area, "motion-notify-event", + nsgtk_tree_window_motion_notify_event, + tv); + CONNECT(drawing_area, "key-press-event", + nsgtk_tree_window_keypress_event, + tv); + CONNECT(drawing_area, "key-release-event", + nsgtk_tree_window_keyrelease_event, + tv); + + + /* input method */ + gtk_im_context_set_client_window(tv->input_method, + nsgtk_widget_get_window(GTK_WIDGET(tv->window))); + gtk_im_context_set_use_preedit(tv->input_method, FALSE); + /* input method signals */ + CONNECT(tv->input_method, "commit", + nsgtk_tree_window_input_method_commit, + tv); + + return tv; +} diff --git a/frontends/gtk/treeview.h b/frontends/gtk/treeview.h new file mode 100644 index 000000000..ad8180f33 --- /dev/null +++ b/frontends/gtk/treeview.h @@ -0,0 +1,38 @@ +/* + * Copyright 2004 Richard Wilson + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +/** \file + * Generic tree handling. + */ + +#ifndef __NSGTK_TREEVIEW_H__ +#define __NSGTK_TREEVIEW_H__ + +struct nsgtk_treeview; + +struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags, + GtkWindow *window, GtkScrolledWindow *scrolled, + GtkDrawingArea *drawing_area); +void nsgtk_treeview_destroy(struct nsgtk_treeview *tv); + +struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv); + +void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g); + +#endif /*__NSGTK_TREEVIEW_H__*/ diff --git a/frontends/gtk/viewdata.c b/frontends/gtk/viewdata.c new file mode 100644 index 000000000..55b25467e --- /dev/null +++ b/frontends/gtk/viewdata.c @@ -0,0 +1,990 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +/** + * \file + * generic data viewer implementation. + * + * This viewer can be used for utf-8 encoded chunk of data. Thie data + * might be page source or the debugging of dom or box trees. It will + * show the data in a tab, window or editor as per user configuration. + */ + +#include +#include +#include +#define _WITH_GETLINE /* necessary for FreeBSD */ +#include +#include +#include + +#include "utils/log.h" +#include "utils/nsoption.h" +#include "utils/utf8.h" +#include "utils/messages.h" +#include "utils/utils.h" +#include "utils/file.h" +#include "utils/filepath.h" + +#include "desktop/browser.h" +#include "content/hlcache.h" +#include "content/content.h" + +#include "gtk/warn.h" +#include "gtk/about.h" +#include "gtk/fetch.h" +#include "gtk/compat.h" +#include "gtk/resources.h" +#include "gtk/viewdata.h" + +struct nsgtk_viewdata_ctx { + char *data; + size_t data_len; + char *filename; + + GtkBuilder *builder; /**< The gtk builder that built the widgets. */ + GtkWindow *window; /**< handle to gtk window (builder holds reference) */ + GtkTextView *gv; /**< handle to gtk text view (builder holds reference) */ + + struct nsgtk_viewdata_ctx *next; + struct nsgtk_viewdata_ctx *prev; +}; + +struct menu_events { + const char *widget; + GCallback handler; +}; + +static struct nsgtk_viewdata_ctx *nsgtk_viewdata_list = NULL; +static char viewdata_zoomlevel = 10; + +#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) } +#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \ + GtkMenuItem *widget, gpointer g) + +MENUPROTO(viewdata_save_as); +MENUPROTO(viewdata_print); +MENUPROTO(viewdata_close); +MENUPROTO(viewdata_select_all); +MENUPROTO(viewdata_cut); +MENUPROTO(viewdata_copy); +MENUPROTO(viewdata_paste); +MENUPROTO(viewdata_delete); +MENUPROTO(viewdata_zoom_in); +MENUPROTO(viewdata_zoom_out); +MENUPROTO(viewdata_zoom_normal); +MENUPROTO(viewdata_about); + +static struct menu_events viewdata_menu_events[] = { + MENUEVENT(viewdata_save_as), + MENUEVENT(viewdata_print), + MENUEVENT(viewdata_close), + MENUEVENT(viewdata_select_all), + MENUEVENT(viewdata_cut), + MENUEVENT(viewdata_copy), + MENUEVENT(viewdata_paste), + MENUEVENT(viewdata_delete), + MENUEVENT(viewdata_zoom_in), + MENUEVENT(viewdata_zoom_out), + MENUEVENT(viewdata_zoom_normal), + MENUEVENT(viewdata_about), + {NULL, NULL} +}; + +static void nsgtk_attach_viewdata_menu_handlers(GtkBuilder *xml, gpointer g) +{ + struct menu_events *event = viewdata_menu_events; + + while (event->widget != NULL) + { + GtkWidget *w = GTK_WIDGET(gtk_builder_get_object(xml, event->widget)); + g_signal_connect(G_OBJECT(w), "activate", event->handler, g); + event++; + } +} + +static gboolean nsgtk_viewdata_destroy_event(GtkBuilder *window, gpointer g) +{ + struct nsgtk_viewdata_ctx *vdctx = (struct nsgtk_viewdata_ctx *)g; + + if (vdctx->next != NULL) { + vdctx->next->prev = vdctx->prev; + } + + if (vdctx->prev != NULL) { + vdctx->prev->next = vdctx->next; + } else { + nsgtk_viewdata_list = vdctx->next; + } + + /* release the data */ + free(vdctx->data); + + /* free the builder */ + g_object_unref(G_OBJECT(vdctx->builder)); + + /* free the context structure */ + free(vdctx); + + return FALSE; +} + +static gboolean nsgtk_viewdata_delete_event(GtkWindow * window, gpointer g) +{ + return FALSE; +} + + + +static void nsgtk_viewdata_file_save(GtkWindow *parent, const char *filename, + const char *data, size_t data_size) +{ + FILE *f; + GtkWidget *notif; + GtkWidget *label; + + f = fopen(filename, "w+"); + if (f != NULL) { + fwrite(data, data_size, 1, f); + fclose(f); + return; + } + + /* inform user of faliure */ + notif = gtk_dialog_new_with_buttons(messages_get("gtkSaveFailedTitle"), + parent, + GTK_DIALOG_MODAL, + NSGTK_STOCK_OK, + GTK_RESPONSE_NONE, + NULL); + + g_signal_connect_swapped(notif, "response", + G_CALLBACK(gtk_widget_destroy), notif); + + label = gtk_label_new(messages_get("gtkSaveFailed")); + gtk_container_add(GTK_CONTAINER(nsgtk_dialog_get_content_area(GTK_DIALOG(notif))), label); + gtk_widget_show_all(notif); + +} + + +gboolean nsgtk_on_viewdata_save_as_activate(GtkMenuItem *widget, gpointer g) +{ + struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g; + GtkWidget *fc; + + fc = gtk_file_chooser_dialog_new(messages_get("gtkSaveFile"), + nsg->window, + GTK_FILE_CHOOSER_ACTION_SAVE, + NSGTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + NSGTK_STOCK_SAVE, + GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), nsg->filename); + + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc), + TRUE); + + if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc)); + nsgtk_viewdata_file_save(nsg->window, filename, nsg->data, nsg->data_len); + g_free(filename); + } + + gtk_widget_destroy(fc); + + return TRUE; +} + + +gboolean nsgtk_on_viewdata_print_activate( GtkMenuItem *widget, gpointer g) +{ + /* correct printing */ + + return TRUE; +} + +gboolean nsgtk_on_viewdata_close_activate( GtkMenuItem *widget, gpointer g) +{ + struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g; + + gtk_widget_destroy(GTK_WIDGET(nsg->window)); + + return TRUE; +} + + + +gboolean nsgtk_on_viewdata_select_all_activate (GtkMenuItem *widget, gpointer g) +{ + struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g; + GtkTextBuffer *buf = gtk_text_view_get_buffer(nsg->gv); + GtkTextIter start, end; + + gtk_text_buffer_get_bounds(buf, &start, &end); + + gtk_text_buffer_select_range(buf, &start, &end); + + return TRUE; +} + +gboolean nsgtk_on_viewdata_cut_activate(GtkMenuItem *widget, gpointer g) +{ + return TRUE; +} + +gboolean nsgtk_on_viewdata_copy_activate(GtkMenuItem *widget, gpointer g) +{ + struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g; + GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv)); + + gtk_text_buffer_copy_clipboard(buf, + gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); + + return TRUE; +} + +gboolean nsgtk_on_viewdata_paste_activate(GtkMenuItem *widget, gpointer g) +{ + return TRUE; +} + +gboolean nsgtk_on_viewdata_delete_activate(GtkMenuItem *widget, gpointer g) +{ + return TRUE; +} + +static void nsgtk_viewdata_update_zoomlevel(gpointer g) +{ + struct nsgtk_viewdata_ctx *nsg; + GtkTextBuffer *buf; + GtkTextTagTable *tab; + GtkTextTag *tag; + + nsg = nsgtk_viewdata_list; + while (nsg) { + if (nsg->gv) { + buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv)); + + tab = gtk_text_buffer_get_tag_table( + GTK_TEXT_BUFFER(buf)); + + tag = gtk_text_tag_table_lookup(tab, "zoomlevel"); + if (!tag) { + tag = gtk_text_tag_new("zoomlevel"); + gtk_text_tag_table_add(tab, GTK_TEXT_TAG(tag)); + } + + gdouble fscale = ((gdouble) viewdata_zoomlevel) / 10; + + g_object_set(GTK_TEXT_TAG(tag), "scale", fscale, NULL); + + GtkTextIter start, end; + + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buf), + &start, &end); + gtk_text_buffer_remove_all_tags(GTK_TEXT_BUFFER(buf), + &start, &end); + gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(buf), + GTK_TEXT_TAG(tag), &start, &end); + } + nsg = nsg->next; + } +} + +gboolean nsgtk_on_viewdata_zoom_in_activate(GtkMenuItem *widget, gpointer g) +{ + viewdata_zoomlevel++; + nsgtk_viewdata_update_zoomlevel(g); + + return TRUE; +} + +gboolean nsgtk_on_viewdata_zoom_out_activate(GtkMenuItem *widget, gpointer g) +{ + if (viewdata_zoomlevel > 1) { + viewdata_zoomlevel--; + nsgtk_viewdata_update_zoomlevel(g); + } + + return TRUE; +} + + +gboolean nsgtk_on_viewdata_zoom_normal_activate(GtkMenuItem *widget, gpointer g) +{ + viewdata_zoomlevel = 10; + nsgtk_viewdata_update_zoomlevel(g); + + return TRUE; +} + +gboolean nsgtk_on_viewdata_about_activate(GtkMenuItem *widget, gpointer g) +{ + struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g; + + nsgtk_about_dialog_init(nsg->window); + + return TRUE; +} + +/** + * View the data in a gtk text window. + */ +static nserror +window_init(const char *title, + const char *filename, + char *ndata, + size_t ndata_len) +{ + GtkWindow *window; + GtkWidget *cutbutton; + GtkWidget *pastebutton; + GtkWidget *deletebutton; + GtkWidget *printbutton; + GtkTextView *dataview; + PangoFontDescription *fontdesc; + GtkTextBuffer *tb; + struct nsgtk_viewdata_ctx *newctx; + nserror res; + + newctx = malloc(sizeof(struct nsgtk_viewdata_ctx)); + if (newctx == NULL) { + return NSERROR_NOMEM; + } + + res = nsgtk_builder_new_from_resname("viewdata", &newctx->builder); + if (res != NSERROR_OK) { + LOG("Viewdata UI builder init failed"); + free(newctx); + return res; + } + + gtk_builder_connect_signals(newctx->builder, NULL); + + window = GTK_WINDOW(gtk_builder_get_object(newctx->builder, + "ViewDataWindow")); + if (window == NULL) { + LOG("Unable to find window in builder "); + + /* free the builder */ + g_object_unref(G_OBJECT(newctx->builder)); + + /* free the context structure */ + free(newctx); + + return NSERROR_INIT_FAILED; + } + + cutbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_cut")); + pastebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_paste")); + deletebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_delete")); + printbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_print")); + gtk_widget_set_sensitive(cutbutton, FALSE); + gtk_widget_set_sensitive(pastebutton, FALSE); + gtk_widget_set_sensitive(deletebutton, FALSE); + /* for now */ + gtk_widget_set_sensitive(printbutton, FALSE); + + + newctx->filename = strdup(filename); + + newctx->data = ndata; + newctx->data_len = ndata_len; + + newctx->window = window; + + newctx->next = nsgtk_viewdata_list; + newctx->prev = NULL; + if (nsgtk_viewdata_list != NULL) { + nsgtk_viewdata_list->prev = newctx; + } + nsgtk_viewdata_list = newctx; + + nsgtk_attach_viewdata_menu_handlers(newctx->builder, newctx); + + gtk_window_set_title(window, title); + + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(nsgtk_viewdata_destroy_event), + newctx); + g_signal_connect(G_OBJECT(window), "delete-event", + G_CALLBACK(nsgtk_viewdata_delete_event), + newctx); + + dataview = GTK_TEXT_VIEW(gtk_builder_get_object(newctx->builder, + "viewdata_view")); + + fontdesc = pango_font_description_from_string("Monospace 8"); + + newctx->gv = dataview; + nsgtk_widget_modify_font(GTK_WIDGET(dataview), fontdesc); + + tb = gtk_text_view_get_buffer(dataview); + gtk_text_buffer_set_text(tb, newctx->data, -1); + + gtk_widget_show(GTK_WIDGET(window)); + + return NSERROR_OK; +} + +/** + * open a window to dispaly an existing file. + */ +static nserror +window_init_fname(const char *title, + const char *leafname, + const char *filename) +{ + nserror ret; + FILE *f; + char *ndata; + long tell_len; + size_t ndata_len; + + f = fopen(filename, "r"); + if (f == NULL) { + return NSERROR_NOT_FOUND; + } + if (fseek(f, 0, SEEK_END) != 0) { + fclose(f); + return NSERROR_BAD_SIZE; + } + + tell_len = ftell(f); + if (tell_len == -1) { + fclose(f); + return NSERROR_BAD_SIZE; + } + + if (fseek(f, 0, SEEK_SET) != 0) { + fclose(f); + return NSERROR_BAD_SIZE; + } + + ndata = malloc(tell_len); + + ndata_len = fread(ndata, 1, tell_len, f); + + fclose(f); + + /* window init takes ownership of the ndata if there is no error */ + ret = window_init(title, leafname, ndata, ndata_len); + if (ret != NSERROR_OK) { + free(ndata); + } + + return ret; +} + +/** + * open a new tab from an existing file. + */ +static nserror +tab_init_fname(const char *title, + const char *leafname, + const char *fname) +{ + nsurl *url; + nserror ret; + + /* Open tab on temporary file */ + ret = netsurf_path_to_nsurl(fname, &url); + if (ret != NSERROR_OK) { + return ret; + } + + /* open tab on temportary file */ + ret = browser_window_create(BW_CREATE_TAB | BW_CREATE_HISTORY, url, NULL, NULL, NULL); + nsurl_unref(url); + if (ret != NSERROR_OK) { + return ret; + } + + return NSERROR_OK; +} + +/** + * create a new tab from data. + */ +static nserror +tab_init(const char *title, + const char *leafname, + char *ndata, + size_t ndata_len) +{ + nserror ret; + gchar *fname; + gint handle; + FILE *f; + + handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL); + if ((handle == -1) || (fname == NULL)) { + return NSERROR_SAVE_FAILED; + } + close(handle); /* in case it was binary mode */ + + /* save data to temporary file */ + f = fopen(fname, "w"); + if (f == NULL) { + nsgtk_warning(messages_get("gtkSourceTabError"), 0); + g_free(fname); + return NSERROR_SAVE_FAILED; + } + fprintf(f, "%s", ndata); + fclose(f); + + ret = tab_init_fname(title, leafname, fname); + if (ret == NSERROR_OK) { + free(ndata); + } + + g_free(fname); + + return ret; +} + + +/** + * Build string vector of search path. + * + * ${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share} + * + * $XDG_DATA_HOME if empty use $HOME/.local/share + * + * XDG_DATA_DIRS if empty use /usr/local/share/:/usr/share/ + * + * \return string vector of search pathnames or NULL on error. + */ +static char** xdg_data_strvec(void) +{ + const char *xdg_data_dirs; + const char *xdg_data_home; + const char *home_dir; + char *xdg_data_path; + int xdg_data_size; + char **svec; + + xdg_data_dirs = getenv("XDG_DATA_DIRS"); + if ((xdg_data_dirs == NULL) || + (*xdg_data_dirs == 0) || + (strlen(xdg_data_dirs) > 4096)) { + xdg_data_dirs = "/usr/local/share/:/usr/share/"; + } + + xdg_data_home = getenv("XDG_DATA_HOME"); + if ((xdg_data_home == NULL) || + (*xdg_data_home == 0) || + (strlen(xdg_data_home) > 4096)) { + /* $XDG_DATA_HOME is empty use $HOME/.local/share */ + + home_dir = getenv("HOME"); + if ((home_dir == NULL) || + (*home_dir == 0) || + (strlen(home_dir) > 4096)) { + xdg_data_path = strdup(xdg_data_dirs); + } else { + xdg_data_size = strlen(home_dir) + + SLEN("/.local/share:") + + strlen(xdg_data_dirs) + 1; + xdg_data_path = malloc(xdg_data_size); + snprintf(xdg_data_path, xdg_data_size , + "%s/.local/share/:%s", + home_dir, xdg_data_dirs); + } + } else { + xdg_data_size = strlen(xdg_data_home) + + strlen(xdg_data_dirs) + 2; + xdg_data_path = malloc(xdg_data_size); + snprintf(xdg_data_path, xdg_data_size , "%s:%s", + xdg_data_home, xdg_data_dirs); + } + + LOG("%s", xdg_data_path); + + svec = filepath_path_to_strvec(xdg_data_path); + free(xdg_data_path); + + return svec; +} + +/** + * Search application defaults file for matching mime type. + * + * create filename form path and applications/defaults.list + * + * look for [Default Applications] + * search lines looking like mime/type=Desktop + * + * \param path The base path. + * \param mimetype The mimetype to search for. + * \return The desktop file associated with the mime type or NULL if not found. + */ +static char *xdg_get_default_app(const char *path, const char *mimetype) +{ + FILE *fp; + char *line = NULL; + size_t len = 0; + ssize_t rd; + int fname_len; + char *fname; + int mimetype_len; + char *ret = NULL; + + fname_len = strlen(path) + SLEN("/applications/defaults.list") + 1; + fname = malloc(fname_len); + snprintf(fname, fname_len, "%s/applications/defaults.list", path); + + LOG("Checking %s", fname); + + fp = fopen(fname, "r"); + free(fname); + if (fp == NULL) { + return NULL; + } + + mimetype_len = strlen(mimetype); + while ((rd = getline(&line, &len, fp)) != -1) { + /* line includes line endings if present, remove them */ + while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) { + rd--; + } + line[rd] = 0; + + /* look for mimetype */ + if ((rd > mimetype_len) && + (line[mimetype_len] == '=') && + (strncmp(line, mimetype, mimetype_len) == 0)) { + + ret = strdup(line + mimetype_len + 1); + + LOG("Found line match for %s length %zu\n", mimetype, rd); + LOG("Result %s", ret); + + break; + } + } + + free(line); + fclose(fp); + + return ret; +} + +/** + * Search desktop file for an Exec line. + * + * search path is combined with applications/application.desktop to + * create a filename. + * + * Desktop file format http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html + * + * \todo The parsing of the desktop file is badly incomplete and needs + * improving. For example the handling of the = delimiter is wrong and + * selection from the "Desktop Entry" group is completely absent. + * + */ +static char *xdg_get_exec_cmd(const char *path, const char *desktop) +{ + FILE *fp; + char *line = NULL; + size_t len = 0; + ssize_t rd; + int fname_len; + char *fname; + char *ret = NULL; + + fname_len = strlen(path) + SLEN("/applications/") + strlen(desktop) + 1; + fname = malloc(fname_len); + snprintf(fname, fname_len, "%s/applications/%s", path, desktop); + + LOG("Checking %s", fname); + + fp = fopen(fname, "r"); + free(fname); + if (fp == NULL) { + return NULL; + } + + while ((rd = getline(&line, &len, fp)) != -1) { + /* line includes line endings if present, remove them */ + while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) { + rd--; + } + line[rd] = 0; + + /* look for mimetype */ + if ((rd > (ssize_t)SLEN("Exec=")) && + (strncmp(line, "Exec=", SLEN("Exec=")) == 0)) { + + ret = strdup(line + SLEN("Exec=")); + + LOG("Found Exec length %zu", rd); + LOG("Result %s", ret); + + break; + } + } + + free(line); + fclose(fp); + + return ret; +} + +static char *exec_arg(const char *arg, int len, const char *fname) +{ + char *res = NULL; + + if (*arg == '%') { + arg++; + if ((*arg == 'f') || (*arg == 'F') || + (*arg == 'u') || (*arg == 'U')) { + res = strdup(fname); + } + } else { + res = calloc(1, len + 1); + if (res != NULL) { + memcpy(res, arg, len); + } + } + + return res; +} + +/** + * Build vector for executing app. + */ +static char **build_exec_argv(const char *fname, const char *exec_cmd) +{ + char **argv; + const char *start; /* current arguments start */ + const char *cur; /* current ptr within exec cmd */ + int aidx = 0; /* argv index */ + + argv = calloc(10, sizeof(char *)); + if (argv == NULL) { + return NULL; + } + + cur = exec_cmd; + while (*cur != 0) { + /* skip whitespace */ + while ((*cur != 0) && (*cur == ' ')) { + cur++; + } + if (*cur == 0) { + break; + } + start = cur; + + /* find end of element */ + while ((*cur != 0) && (*cur != ' ')) { + cur++; + } + + argv[aidx] = exec_arg(start, cur - start, fname); + if (argv[aidx] != NULL) { + LOG("adding \"%s\"", argv[aidx]); + aidx++; + } + } + + /* if no arguments were found there was nothing to execute */ + if (aidx == 0) { + free(argv); + return NULL; + } + + return argv; +} + + +/** + * open an editor from an existing file. + */ +static nserror +editor_init_fname(const char *title, + const char *leafname, + const char *fname) +{ + char **xdg_data_vec; + int veci; + /* desktop file of default app for mimetype */ + char *def_app_desktop = NULL; + char *exec_cmd = NULL; + char **argv; + + /* build string vector of search path */ + xdg_data_vec = xdg_data_strvec(); + + /* find user configured app for opening text/plain */ + veci = 0; + while (xdg_data_vec[veci] != NULL) { + def_app_desktop = xdg_get_default_app(xdg_data_vec[veci], + "text/plain"); + if (def_app_desktop != NULL) { + break; + } + veci++; + } + + if (def_app_desktop == NULL) { + /* no default app */ + filepath_free_strvec(xdg_data_vec); + return NSERROR_NOT_FOUND; + } + + /* find app to execute */ + veci = 0; + while (xdg_data_vec[veci] != NULL) { + exec_cmd = xdg_get_exec_cmd(xdg_data_vec[veci], def_app_desktop); + if (exec_cmd != NULL) { + break; + } + veci++; + } + free(def_app_desktop); + filepath_free_strvec(xdg_data_vec); + + if (exec_cmd == NULL) { + /* no exec entry */ + return NSERROR_NOT_FOUND; + } + + /* build exec vector */ + argv = build_exec_argv(fname, exec_cmd); + free(exec_cmd); + + /* execute target app on saved data */ + if (g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, + NULL, NULL) != TRUE) { + return NSERROR_NOT_FOUND; + } + filepath_free_strvec(argv); + + return NSERROR_OK; +} + +/** + * open an editor with data. + */ +static nserror +editor_init(const char *title, + const char *leafname, + char *ndata, + size_t ndata_len) +{ + + nserror ret; + gchar *fname; + gint handle; + FILE *f; + + handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL); + if ((handle == -1) || (fname == NULL)) { + return NSERROR_SAVE_FAILED; + } + close(handle); /* in case it was binary mode */ + + /* save data to temporary file */ + f = fopen(fname, "w"); + if (f == NULL) { + nsgtk_warning(messages_get("gtkSourceTabError"), 0); + g_free(fname); + return NSERROR_SAVE_FAILED; + } + fprintf(f, "%s", ndata); + fclose(f); + + ret = editor_init_fname(title, leafname, fname); + if (ret == NSERROR_OK) { + free(ndata); + } + + g_free(fname); + + return ret; +} + +/* exported interface documented in gtk/viewdata.h */ +nserror +nsgtk_viewdata(const char *title, + const char *filename, + char *ndata, + size_t ndata_len) +{ + nserror ret; + + switch (nsoption_int(developer_view)) { + case 0: + ret = window_init(title, filename, ndata, ndata_len); + break; + + case 1: + ret = tab_init(title, filename, ndata, ndata_len); + break; + + case 2: + ret = editor_init(title, filename, ndata, ndata_len); + break; + + default: + ret = NSERROR_BAD_PARAMETER; + break; + } + if (ret != NSERROR_OK) { + /* release the data */ + free(ndata); + } + + + return ret; +} + +/* exported interface documented in gtk/viewdata.h */ +nserror +nsgtk_viewfile(const char *title, + const char *leafname, + const char *filename) +{ + nserror ret; + + switch (nsoption_int(developer_view)) { + case 0: + ret = window_init_fname(title, leafname, filename); + break; + + case 1: + ret = tab_init_fname(title, leafname, filename); + break; + + case 2: + ret = editor_init_fname(title, leafname, filename); + break; + + default: + ret = NSERROR_BAD_PARAMETER; + break; + } + + return ret; +} diff --git a/frontends/gtk/viewdata.h b/frontends/gtk/viewdata.h new file mode 100644 index 000000000..1767b4821 --- /dev/null +++ b/frontends/gtk/viewdata.h @@ -0,0 +1,48 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +#ifndef _NETSURF_GTK_VIEWDATA_H_ +#define _NETSURF_GTK_VIEWDATA_H_ + +/** + * Display text to a user. + * + * The data is utf-8 encoded text and will be presented in a window, a + * tab or an editor as per the user configuration. + * + * \param title The title of the data being displayed. + * \param filename The suggested filename to be used. + * \param data The data to be shown. This data will be freed once the + * display is complete, the caller no longer owns the allocation. + * \param data_size The size of the data in data. + */ +nserror nsgtk_viewdata(const char *title, const char *filename, char *data, size_t data_size); + +/** + * Display file to a user. + * + * The file is interpreted as utf-8 encoded text and will be presented + * in a window, a tab or an editor as per the user configuration. + * + * \param title The title of the data being displayed. + * \param leafname The suggested leafname to be used. + * \param filename The filename of the data to be viewed. + */ +nserror nsgtk_viewfile(const char *title, const char *leafname, const char *filename); + +#endif diff --git a/frontends/gtk/viewsource.c b/frontends/gtk/viewsource.c new file mode 100644 index 000000000..554cfbf39 --- /dev/null +++ b/frontends/gtk/viewsource.c @@ -0,0 +1,82 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + +#include +#include + +#include "utils/utils.h" +#include "utils/utf8.h" +#include "utils/nsurl.h" +#include "utils/messages.h" +#include "desktop/browser.h" +#include "content/content.h" + +#include "gtk/viewdata.h" +#include "gtk/viewsource.h" + +nserror nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw) +{ + nserror ret; + struct hlcache_handle *hlcontent; + const char *source_data; + unsigned long source_size; + char *ndata = NULL; + size_t ndata_len; + char *filename; + char *title; + + hlcontent = browser_window_get_content(bw); + if (hlcontent == NULL) { + return NSERROR_BAD_PARAMETER; + } + + if (content_get_type(hlcontent) != CONTENT_HTML) { + return NSERROR_BAD_CONTENT; + } + + source_data = content_get_source_data(hlcontent, &source_size); + + ret = nsurl_nice(browser_window_get_url(bw), &filename, false); + if (ret != NSERROR_OK) { + filename = strdup(messages_get("SaveSource")); + if (filename == NULL) { + return NSERROR_NOMEM; + } + } + + title = malloc(strlen(nsurl_access(browser_window_get_url(bw))) + SLEN("Source of - NetSurf") + 1); + if (title == NULL) { + free(filename); + return NSERROR_NOMEM; + } + sprintf(title, "Source of %s - NetSurf", nsurl_access(browser_window_get_url(bw))); + + ret = utf8_from_enc(source_data, + content_get_encoding(hlcontent, CONTENT_ENCODING_NORMAL), + source_size, + &ndata, + &ndata_len); + if (ret == NSERROR_OK) { + ret = nsgtk_viewdata(title, filename, ndata, ndata_len); + } + + free(filename); + free(title); + + return ret; +} diff --git a/frontends/gtk/viewsource.h b/frontends/gtk/viewsource.h new file mode 100644 index 000000000..bba878874 --- /dev/null +++ b/frontends/gtk/viewsource.h @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +#ifndef _NETSURF_GTK_VIEWSOURCE_H_ +#define _NETSURF_GTK_VIEWSOURCE_H_ + +nserror nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw); + +#endif + diff --git a/frontends/gtk/warn.h b/frontends/gtk/warn.h new file mode 100644 index 000000000..d24f55438 --- /dev/null +++ b/frontends/gtk/warn.h @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Vincent Sanders + * + * 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 . + */ + +#ifndef GTK_WARN_H +#define GTK_WARN_H + +/** + * Warn the user of an event. + * + * \param[in] warning A warning looked up in the message translation table + * \param[in] detail Additional text to be displayed or NULL. + * \return NSERROR_OK on success or error code if there was a + * faliure displaying the message to the user. + */ +nserror nsgtk_warning(const char *warning, const char *detail); + +#endif diff --git a/frontends/gtk/window.c b/frontends/gtk/window.c new file mode 100644 index 000000000..de333d1b0 --- /dev/null +++ b/frontends/gtk/window.c @@ -0,0 +1,1337 @@ +/* + * Copyright 2006 Daniel Silverstone + * Copyright 2006 Rob Kendrick + * + * 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 . + */ + +/** + * \file + * Implementation of gtk windowing. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utf8.h" +#include "utils/utils.h" +#include "utils/nsoption.h" +#include "content/hlcache.h" +#include "gtk/window.h" +#include "gtk/selection.h" +#include "desktop/browser.h" +#include "desktop/mouse.h" +#include "desktop/searchweb.h" +#include "desktop/textinput.h" +#include "desktop/gui_window.h" +#include "desktop/plotters.h" +#include "render/form.h" + +#include "gtk/warn.h" +#include "gtk/compat.h" +#include "gtk/gui.h" +#include "gtk/scaffolding.h" +#include "gtk/plotters.h" +#include "gtk/schedule.h" +#include "gtk/tabs.h" +#include "gtk/bitmap.h" +#include "gtk/gdk.h" +#include "gtk/resources.h" + +static GtkWidget *select_menu; +static struct form_control *select_menu_control; + +static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem, + gpointer user_data); + +struct gui_window { + /** + * The gtk scaffold object containing menu, buttons, url bar, [tabs], + * drawing area, etc that may contain one or more gui_windows. + */ + struct nsgtk_scaffolding *scaffold; + + /** The 'content' window that is rendered in the gui_window */ + struct browser_window *bw; + + /** mouse state and events. */ + struct { + struct gui_window *gui; + + gdouble pressed_x; + gdouble pressed_y; + gboolean waiting; + browser_mouse_state state; + } mouse; + + /** caret dimension and location for rendering */ + int caretx, carety, careth; + + /** caret shape for rendering */ + gui_pointer_shape current_pointer; + + /** previous event location */ + int last_x, last_y; + + /** The top level container (tabContents) */ + GtkWidget *container; + + /** display widget for this page or frame */ + GtkLayout *layout; + + /** handle to the the visible tab */ + GtkWidget *tab; + + /** statusbar */ + GtkLabel *status_bar; + + /** status pane */ + GtkPaned *paned; + + /** has the status pane had its first size operation yet? */ + bool paned_sized; + + /** to allow disactivation / resume of normal window behaviour */ + gulong signalhandler[NSGTK_WINDOW_SIGNAL_COUNT]; + + /** The icon this window should have */ + GdkPixbuf *icon; + + /** The input method to use with this window */ + GtkIMContext *input_method; + + /** list for cleanup */ + struct gui_window *next, *prev; +}; + +/**< first entry in window list */ +struct gui_window *window_list = NULL; + +/** flag controlling opening of tabs in teh background */ +int temp_open_background = -1; + +struct nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g) +{ + return g->scaffold; +} + +GdkPixbuf *nsgtk_get_icon(struct gui_window *gw) +{ + return gw->icon; +} + +struct browser_window *nsgtk_get_browser_window(struct gui_window *g) +{ + return g->bw; +} + +unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i) +{ + return g->signalhandler[i]; +} + +GtkLayout *nsgtk_window_get_layout(struct gui_window *g) +{ + return g->layout; +} + +GtkWidget *nsgtk_window_get_tab(struct gui_window *g) +{ + return g->tab; +} + +void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w) +{ + g->tab = w; +} + + +struct gui_window *nsgtk_window_iterate(struct gui_window *g) +{ + return g->next; +} + +float nsgtk_get_scale_for_gui(struct gui_window *g) +{ + return browser_window_get_scale(g->bw); +} + +static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem, + gpointer user_data) +{ + form_select_process_selection(select_menu_control, + (intptr_t)user_data); +} + +#if GTK_CHECK_VERSION(3,0,0) + +static gboolean +nsgtk_window_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data) +{ + struct gui_window *gw = data; + struct gui_window *z; + struct rect clip; + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &nsgtk_plotters + }; + + double x1; + double y1; + double x2; + double y2; + + assert(gw); + assert(gw->bw); + + for (z = window_list; z && z != gw; z = z->next) + continue; + assert(z); + assert(GTK_WIDGET(gw->layout) == widget); + + current_widget = (GtkWidget *)gw->layout; + current_cr = cr; + + GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(gw->layout); + GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(gw->layout); + + cairo_clip_extents(cr, &x1, &y1, &x2, &y2); + + clip.x0 = x1; + clip.y0 = y1; + clip.x1 = x2; + clip.y1 = y2; + + browser_window_redraw(gw->bw, + -gtk_adjustment_get_value(hscroll), + -gtk_adjustment_get_value(vscroll), + &clip, + &ctx); + + if (gw->careth != 0) { + nsgtk_plot_caret(gw->caretx, gw->carety, gw->careth); + } + + current_widget = NULL; + + return FALSE; +} + +#else + +static gboolean +nsgtk_window_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + struct gui_window *gw = data; + struct gui_window *z; + struct rect clip; + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &nsgtk_plotters + }; + + assert(gw); + assert(gw->bw); + + for (z = window_list; z && z != gw; z = z->next) + continue; + assert(z); + assert(GTK_WIDGET(gw->layout) == widget); + + current_widget = (GtkWidget *)gw->layout; + current_cr = gdk_cairo_create(nsgtk_layout_get_bin_window(gw->layout)); + + clip.x0 = event->area.x; + clip.y0 = event->area.y; + clip.x1 = event->area.x + event->area.width; + clip.y1 = event->area.y + event->area.height; + + browser_window_redraw(gw->bw, 0, 0, &clip, &ctx); + + if (gw->careth != 0) { + nsgtk_plot_caret(gw->caretx, gw->carety, gw->careth); + } + + cairo_destroy(current_cr); + + current_widget = NULL; + + return FALSE; +} + +#endif + +static gboolean nsgtk_window_motion_notify_event(GtkWidget *widget, + GdkEventMotion *event, gpointer data) +{ + struct gui_window *g = data; + bool shift = event->state & GDK_SHIFT_MASK; + bool ctrl = event->state & GDK_CONTROL_MASK; + + if ((fabs(event->x - g->last_x) < 5.0) && + (fabs(event->y - g->last_y) < 5.0)) { + /* Mouse hasn't moved far enough from press coordinate + * for this to be considered a drag. + */ + return FALSE; + } else { + /* This is a drag, ensure it's always treated as such, + * even if we drag back over the press location. + */ + g->last_x = INT_MIN; + g->last_y = INT_MIN; + } + + if (g->mouse.state & BROWSER_MOUSE_PRESS_1) { + /* Start button 1 drag */ + browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_1, + g->mouse.pressed_x, g->mouse.pressed_y); + + /* Replace PRESS with HOLDING and declare drag in progress */ + g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 | + BROWSER_MOUSE_HOLDING_1); + g->mouse.state |= BROWSER_MOUSE_DRAG_ON; + } else if (g->mouse.state & BROWSER_MOUSE_PRESS_2) { + /* Start button 2 drag */ + browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_2, + g->mouse.pressed_x, g->mouse.pressed_y); + + /* Replace PRESS with HOLDING and declare drag in progress */ + g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 | + BROWSER_MOUSE_HOLDING_2); + g->mouse.state |= BROWSER_MOUSE_DRAG_ON; + } + + /* Handle modifiers being removed */ + if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift) + g->mouse.state ^= BROWSER_MOUSE_MOD_1; + if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl) + g->mouse.state ^= BROWSER_MOUSE_MOD_2; + + browser_window_mouse_track(g->bw, g->mouse.state, + event->x / browser_window_get_scale(g->bw), + event->y / browser_window_get_scale(g->bw)); + + return TRUE; +} + +static gboolean nsgtk_window_button_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct gui_window *g = data; + + gtk_im_context_reset(g->input_method); + gtk_widget_grab_focus(GTK_WIDGET(g->layout)); + gtk_widget_hide(GTK_WIDGET(nsgtk_scaffolding_history_window( + g->scaffold)->window)); + + g->mouse.pressed_x = event->x / browser_window_get_scale(g->bw); + g->mouse.pressed_y = event->y / browser_window_get_scale(g->bw); + + switch (event->button) { + case 1: /* Left button, usually. Pass to core as BUTTON 1. */ + g->mouse.state = BROWSER_MOUSE_PRESS_1; + break; + + case 2: /* Middle button, usually. Pass to core as BUTTON 2 */ + g->mouse.state = BROWSER_MOUSE_PRESS_2; + break; + + case 3: /* Right button, usually. Action button, context menu. */ + browser_window_remove_caret(g->bw, true); + nsgtk_scaffolding_context_menu(g->scaffold, + g->mouse.pressed_x, + g->mouse.pressed_y); + return TRUE; + + default: + return FALSE; + } + + /* Modify for double & triple clicks */ + if (event->type == GDK_3BUTTON_PRESS) + g->mouse.state |= BROWSER_MOUSE_TRIPLE_CLICK; + else if (event->type == GDK_2BUTTON_PRESS) + g->mouse.state |= BROWSER_MOUSE_DOUBLE_CLICK; + + /* Handle the modifiers too */ + if (event->state & GDK_SHIFT_MASK) + g->mouse.state |= BROWSER_MOUSE_MOD_1; + if (event->state & GDK_CONTROL_MASK) + g->mouse.state |= BROWSER_MOUSE_MOD_2; + + /* Record where we pressed, for use when determining whether to start + * a drag in motion notify events. */ + g->last_x = event->x; + g->last_y = event->y; + + browser_window_mouse_click(g->bw, g->mouse.state, g->mouse.pressed_x, + g->mouse.pressed_y); + + return TRUE; +} + +static gboolean nsgtk_window_button_release_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct gui_window *g = data; + bool shift = event->state & GDK_SHIFT_MASK; + bool ctrl = event->state & GDK_CONTROL_MASK; + + /* If the mouse state is PRESS then we are waiting for a release to emit + * a click event, otherwise just reset the state to nothing */ + if (g->mouse.state & BROWSER_MOUSE_PRESS_1) + g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1); + else if (g->mouse.state & BROWSER_MOUSE_PRESS_2) + g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2); + + /* Handle modifiers being removed */ + if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift) + g->mouse.state ^= BROWSER_MOUSE_MOD_1; + if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl) + g->mouse.state ^= BROWSER_MOUSE_MOD_2; + + if (g->mouse.state & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) { + browser_window_mouse_click(g->bw, g->mouse.state, + event->x / browser_window_get_scale(g->bw), + event->y / browser_window_get_scale(g->bw)); + } else { + browser_window_mouse_track(g->bw, 0, + event->x / browser_window_get_scale(g->bw), + event->y / browser_window_get_scale(g->bw)); + } + + g->mouse.state = 0; + return TRUE; +} + +static gboolean +nsgtk_window_scroll_event(GtkWidget *widget, + GdkEventScroll *event, + gpointer data) +{ + struct gui_window *g = data; + double value; + double deltax = 0; + double deltay = 0; + GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(g->layout); + GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(g->layout); + GtkAllocation alloc; + + switch (event->direction) { + case GDK_SCROLL_LEFT: + deltax = -1.0; + break; + + case GDK_SCROLL_UP: + deltay = -1.0; + break; + + case GDK_SCROLL_RIGHT: + deltax = 1.0; + break; + + case GDK_SCROLL_DOWN: + deltay = 1.0; + break; + +#if GTK_CHECK_VERSION(3,4,0) + case GDK_SCROLL_SMOOTH: + gdk_event_get_scroll_deltas((GdkEvent *)event, &deltax, &deltay); + break; +#endif + default: + LOG("Unhandled mouse scroll direction"); + return TRUE; + } + + deltax *= nsgtk_adjustment_get_step_increment(hscroll); + deltay *= nsgtk_adjustment_get_step_increment(vscroll); + + if (browser_window_scroll_at_point(g->bw, + event->x / browser_window_get_scale(g->bw), + event->y / browser_window_get_scale(g->bw), + deltax, deltay) != true) { + + /* core did not handle event so change adjustments */ + + /* Horizontal */ + if (deltax != 0) { + value = gtk_adjustment_get_value(hscroll) + deltax; + + /* @todo consider gtk_widget_get_allocated_width() */ + nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc); + + if (value > nsgtk_adjustment_get_upper(hscroll) - alloc.width) { + value = nsgtk_adjustment_get_upper(hscroll) - alloc.width; + } + if (value < nsgtk_adjustment_get_lower(hscroll)) { + value = nsgtk_adjustment_get_lower(hscroll); + } + + gtk_adjustment_set_value(hscroll, value); + } + + /* Vertical */ + if (deltay != 0) { + value = gtk_adjustment_get_value(vscroll) + deltay; + + /* @todo consider gtk_widget_get_allocated_height */ + nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc); + + if (value > (nsgtk_adjustment_get_upper(vscroll) - alloc.height)) { + value = nsgtk_adjustment_get_upper(vscroll) - alloc.height; + } + if (value < nsgtk_adjustment_get_lower(vscroll)) { + value = nsgtk_adjustment_get_lower(vscroll); + } + + gtk_adjustment_set_value(vscroll, value); + } + } + + return TRUE; +} + +static gboolean nsgtk_window_keypress_event(GtkWidget *widget, + GdkEventKey *event, gpointer data) +{ + struct gui_window *g = data; + uint32_t nskey; + + if (gtk_im_context_filter_keypress(g->input_method, event)) + return TRUE; + + nskey = gtk_gui_gdkkey_to_nskey(event); + + if (browser_window_key_press(g->bw, nskey)) + return TRUE; + + if ((event->state & 0x7) != 0) + return TRUE; + + double value; + GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(g->layout); + GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(g->layout); + GtkAllocation alloc; + + /* @todo consider gtk_widget_get_allocated_width() */ + nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc); + + switch (event->keyval) { + + case GDK_KEY(Home): + case GDK_KEY(KP_Home): + value = nsgtk_adjustment_get_lower(vscroll); + gtk_adjustment_set_value(vscroll, value); + break; + + case GDK_KEY(End): + case GDK_KEY(KP_End): + value = nsgtk_adjustment_get_upper(vscroll) - alloc.height; + + if (value < nsgtk_adjustment_get_lower(vscroll)) + value = nsgtk_adjustment_get_lower(vscroll); + + gtk_adjustment_set_value(vscroll, value); + break; + + case GDK_KEY(Left): + case GDK_KEY(KP_Left): + value = gtk_adjustment_get_value(hscroll) - + nsgtk_adjustment_get_step_increment(hscroll); + + if (value < nsgtk_adjustment_get_lower(hscroll)) + value = nsgtk_adjustment_get_lower(hscroll); + + gtk_adjustment_set_value(hscroll, value); + break; + + case GDK_KEY(Up): + case GDK_KEY(KP_Up): + value = gtk_adjustment_get_value(vscroll) - + nsgtk_adjustment_get_step_increment(vscroll); + + if (value < nsgtk_adjustment_get_lower(vscroll)) + value = nsgtk_adjustment_get_lower(vscroll); + + gtk_adjustment_set_value(vscroll, value); + break; + + case GDK_KEY(Right): + case GDK_KEY(KP_Right): + value = gtk_adjustment_get_value(hscroll) + + nsgtk_adjustment_get_step_increment(hscroll); + + if (value > nsgtk_adjustment_get_upper(hscroll) - alloc.width) + value = nsgtk_adjustment_get_upper(hscroll) - alloc.width; + + gtk_adjustment_set_value(hscroll, value); + break; + + case GDK_KEY(Down): + case GDK_KEY(KP_Down): + value = gtk_adjustment_get_value(vscroll) + + nsgtk_adjustment_get_step_increment(vscroll); + + if (value > nsgtk_adjustment_get_upper(vscroll) - alloc.height) + value = nsgtk_adjustment_get_upper(vscroll) - alloc.height; + + gtk_adjustment_set_value(vscroll, value); + break; + + case GDK_KEY(Page_Up): + case GDK_KEY(KP_Page_Up): + value = gtk_adjustment_get_value(vscroll) - + nsgtk_adjustment_get_page_increment(vscroll); + + if (value < nsgtk_adjustment_get_lower(vscroll)) + value = nsgtk_adjustment_get_lower(vscroll); + + gtk_adjustment_set_value(vscroll, value); + break; + + case GDK_KEY(Page_Down): + case GDK_KEY(KP_Page_Down): + value = gtk_adjustment_get_value(vscroll) + + nsgtk_adjustment_get_page_increment(vscroll); + + if (value > nsgtk_adjustment_get_upper(vscroll) - alloc.height) + value = nsgtk_adjustment_get_upper(vscroll) - alloc.height; + + gtk_adjustment_set_value(vscroll, value); + break; + + default: + break; + + } + + return TRUE; +} + +static gboolean nsgtk_window_keyrelease_event(GtkWidget *widget, + GdkEventKey *event, gpointer data) +{ + struct gui_window *g = data; + + return gtk_im_context_filter_keypress(g->input_method, event); +} + + +static void nsgtk_window_input_method_commit(GtkIMContext *ctx, + const gchar *str, gpointer data) +{ + struct gui_window *g = data; + size_t len = strlen(str), offset = 0; + + while (offset < len) { + uint32_t nskey = utf8_to_ucs4(str + offset, len - offset); + + browser_window_key_press(g->bw, nskey); + + offset = utf8_next(str, len, offset); + } +} + + +static gboolean nsgtk_window_size_allocate_event(GtkWidget *widget, + GtkAllocation *allocation, gpointer data) +{ + struct gui_window *g = data; + + browser_window_schedule_reformat(g->bw); + + return TRUE; +} + + +/** when the pane position is changed update the user option + * + * The slightly awkward implementation with the first allocation flag + * is necessary because the initial window creation does not cause an + * allocate-event signal so the position value in the pane is incorrect + * and we cannot know what it should be until after the allocation + * (which did not generate a signal) is done as the user position is a + * percentage of pane total width not an absolute value. + */ +static void +nsgtk_paned_notify__position(GObject *gobject, GParamSpec *pspec, gpointer data) +{ + struct gui_window *g = data; + GtkAllocation pane_alloc; + + gtk_widget_get_allocation(GTK_WIDGET(g->paned), &pane_alloc); + + if (g->paned_sized == false) + { + g->paned_sized = true; + gtk_paned_set_position(g->paned, + (nsoption_int(toolbar_status_size) * pane_alloc.width) / 10000); + return; + } + + nsoption_set_int(toolbar_status_size, + ((gtk_paned_get_position(g->paned) * 10000) / (pane_alloc.width - 1))); +} + +/** Set status bar / scroll bar proportion according to user option + * when pane is resized. + */ +static gboolean nsgtk_paned_size_allocate_event(GtkWidget *widget, + GtkAllocation *allocation, gpointer data) +{ + gtk_paned_set_position(GTK_PANED(widget), + (nsoption_int(toolbar_status_size) * allocation->width) / 10000); + + return TRUE; +} + +/* destroy the browsing context as there is nothing to display it now */ +static void window_destroy(GtkWidget *widget, gpointer data) +{ + struct gui_window *gw = data; + + browser_window_destroy(gw->bw); + + g_object_unref(gw->input_method); +} + + +/** + * Create and open a gtk container (window or tab) for a browsing context. + * + * \param bw The browsing context to create gui_window for. + * \param existing An existing gui_window, may be NULL + * \param flags flags to control the container creation + * \return gui window, or NULL on error + * + * If GW_CREATE_CLONE flag is set existing is non-NULL. + * + * Front end's gui_window must include a reference to the + * browser window passed in the bw param. + */ +static struct gui_window * +gui_window_create(struct browser_window *bw, + struct gui_window *existing, + gui_window_create_flags flags) +{ + struct gui_window *g; /* what is being created to return */ + bool tempback; + GtkBuilder* tab_builder; + + nserror res; + + res = nsgtk_builder_new_from_resname("tabcontents", &tab_builder); + if (res != NSERROR_OK) { + LOG("Tab contents UI builder init failed"); + return NULL; + } + + gtk_builder_connect_signals(tab_builder, NULL); + + g = calloc(1, sizeof(*g)); + if (!g) { + nsgtk_warning("NoMemory", 0); + g_object_unref(tab_builder); + return NULL; + } + + LOG("Creating gui window %p for browser window %p", g, bw); + + g->bw = bw; + g->mouse.state = 0; + g->current_pointer = GUI_POINTER_DEFAULT; + + /* attach scaffold */ + if (flags & GW_CREATE_TAB) { + /* open in new tab, attach to existing scaffold */ + if (existing != NULL) { + g->scaffold = existing->scaffold; + } else { + g->scaffold = nsgtk_current_scaffolding(); + } + } else { + /* open in new window, create and attach to scaffold */ + g->scaffold = nsgtk_new_scaffolding(g); + } + if (g->scaffold == NULL) { + nsgtk_warning("NoMemory", 0); + free(g); + g_object_unref(tab_builder); + return NULL; + } + + /* Construct our primary elements */ + g->container = GTK_WIDGET(gtk_builder_get_object(tab_builder, "tabContents")); + g->layout = GTK_LAYOUT(gtk_builder_get_object(tab_builder, "layout")); + g->status_bar = GTK_LABEL(gtk_builder_get_object(tab_builder, "status_bar")); + g->paned = GTK_PANED(gtk_builder_get_object(tab_builder, "hpaned1")); + g->input_method = gtk_im_multicontext_new(); + + /* set a default favicon */ + g_object_ref(favicon_pixbuf); + g->icon = favicon_pixbuf; + + /* add new gui window to global list (push_top) */ + if (window_list) { + window_list->prev = g; + } + g->next = window_list; + g->prev = NULL; + window_list = g; + + /* set the events we're interested in receiving from the browser's + * drawing area. + */ + gtk_widget_add_events(GTK_WIDGET(g->layout), + GDK_EXPOSURE_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_SCROLL_MASK); + nsgtk_widget_set_can_focus(GTK_WIDGET(g->layout), TRUE); + + /* set the default background colour of the drawing area to white. */ + nsgtk_widget_override_background_color(GTK_WIDGET(g->layout), + GTK_STATE_NORMAL, + 0, 0xffff, 0xffff, 0xffff); + + g->signalhandler[NSGTK_WINDOW_SIGNAL_REDRAW] = + nsgtk_connect_draw_event(GTK_WIDGET(g->layout), + G_CALLBACK(nsgtk_window_draw_event), g); + + /* helper macro to conect signals to callbacks */ +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + /* layout signals */ + CONNECT(g->layout, "motion-notify-event", + nsgtk_window_motion_notify_event, g); + g->signalhandler[NSGTK_WINDOW_SIGNAL_CLICK] = + CONNECT(g->layout, "button-press-event", + nsgtk_window_button_press_event, g); + CONNECT(g->layout, "button-release-event", + nsgtk_window_button_release_event, g); + CONNECT(g->layout, "key-press-event", + nsgtk_window_keypress_event, g); + CONNECT(g->layout, "key-release-event", + nsgtk_window_keyrelease_event, g); + CONNECT(g->layout, "size-allocate", + nsgtk_window_size_allocate_event, g); + CONNECT(g->layout, "scroll-event", + nsgtk_window_scroll_event, g); + + /* status pane signals */ + CONNECT(g->paned, "size-allocate", + nsgtk_paned_size_allocate_event, g); + + CONNECT(g->paned, "notify::position", + nsgtk_paned_notify__position, g); + + /* gtk container destructor */ + CONNECT(g->container, "destroy", window_destroy, g); + + /* input method */ + gtk_im_context_set_client_window(g->input_method, + nsgtk_layout_get_bin_window(g->layout)); + gtk_im_context_set_use_preedit(g->input_method, FALSE); + + /* input method signals */ + CONNECT(g->input_method, "commit", + nsgtk_window_input_method_commit, g); + + /* add the tab container to the scaffold notebook */ + switch (temp_open_background) { + case -1: + tempback = !(nsoption_bool(focus_new)); + break; + case 0: + tempback = false; + break; + default: + tempback = true; + break; + } + nsgtk_tab_add(g, g->container, tempback); + + /* safe to drop the reference to the tab_builder as the container is + * referenced by the notebook now. + */ + g_object_unref(tab_builder); + + return g; +} + + + +void nsgtk_reflow_all_windows(void) +{ + for (struct gui_window *g = window_list; g; g = g->next) { + nsgtk_tab_options_changed(nsgtk_scaffolding_notebook(g->scaffold)); + browser_window_schedule_reformat(g->bw); + } +} + + +/** + * callback from core to reformat a window. + */ +static void nsgtk_window_reformat(struct gui_window *gw) +{ + GtkAllocation alloc; + + if (gw != NULL) { + /** @todo consider gtk_widget_get_allocated_width() */ + nsgtk_widget_get_allocation(GTK_WIDGET(gw->layout), &alloc); + + browser_window_reformat(gw->bw, false, alloc.width, alloc.height); + } +} + +void nsgtk_window_destroy_browser(struct gui_window *gw) +{ + /* remove tab */ + gtk_widget_destroy(gw->container); +} + +static void gui_window_destroy(struct gui_window *g) +{ + LOG("gui_window: %p", g); + assert(g != NULL); + assert(g->bw != NULL); + LOG("scaffolding: %p", g->scaffold); + + if (g->prev) { + g->prev->next = g->next; + } else { + window_list = g->next; + } + + if (g->next) { + g->next->prev = g->prev; + } + + LOG("window list head: %p", window_list); +} + +/** + * favicon setting for gtk gui window. + * + * \param gw gtk gui window to set favicon on. + * \param icon A handle to the new favicon content. + */ +static void gui_window_set_icon(struct gui_window *gw, hlcache_handle *icon) +{ + struct bitmap *icon_bitmap = NULL; + + /* free any existing icon */ + if (gw->icon != NULL) { + g_object_unref(gw->icon); + gw->icon = NULL; + } + + if (icon != NULL) { + icon_bitmap = content_get_bitmap(icon); + if (icon_bitmap != NULL) { + LOG("Using %p bitmap", icon_bitmap); + gw->icon = nsgdk_pixbuf_get_from_surface(icon_bitmap->surface, 16, 16); + } + } + + if (gw->icon == NULL) { + LOG("Using default favicon"); + g_object_ref(favicon_pixbuf); + gw->icon = favicon_pixbuf; + } + + nsgtk_scaffolding_set_icon(gw); +} + +static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy) +{ + GtkAdjustment *vadj = nsgtk_layout_get_vadjustment(g->layout); + GtkAdjustment *hadj = nsgtk_layout_get_hadjustment(g->layout); + + assert(vadj); + assert(hadj); + + *sy = (int)(gtk_adjustment_get_value(vadj)); + *sx = (int)(gtk_adjustment_get_value(hadj)); + + return true; +} + +static void nsgtk_redraw_caret(struct gui_window *g) +{ + int sx, sy; + + if (g->careth == 0) + return; + + gui_window_get_scroll(g, &sx, &sy); + + gtk_widget_queue_draw_area(GTK_WIDGET(g->layout), + g->caretx - sx, g->carety - sy, 1, g->careth + 1); + +} + +static void gui_window_remove_caret(struct gui_window *g) +{ + int sx, sy; + int oh = g->careth; + + if (oh == 0) + return; + + g->careth = 0; + + gui_window_get_scroll(g, &sx, &sy); + + gtk_widget_queue_draw_area(GTK_WIDGET(g->layout), + g->caretx - sx, g->carety - sy, 1, oh + 1); + +} + +static void gui_window_redraw_window(struct gui_window *g) +{ + gtk_widget_queue_draw(GTK_WIDGET(g->layout)); +} + +static void gui_window_update_box(struct gui_window *g, const struct rect *rect) +{ + int sx, sy; + float scale; + + if (!browser_window_has_content(g->bw)) + return; + + gui_window_get_scroll(g, &sx, &sy); + scale = browser_window_get_scale(g->bw); + + gtk_widget_queue_draw_area(GTK_WIDGET(g->layout), + rect->x0 * scale - sx, + rect->y0 * scale - sy, + (rect->x1 - rect->x0) * scale, + (rect->y1 - rect->y0) * scale); +} + +static void gui_window_set_status(struct gui_window *g, const char *text) +{ + assert(g); + assert(g->status_bar); + gtk_label_set_text(g->status_bar, text); +} + + +static void gui_window_set_scroll(struct gui_window *g, int sx, int sy) +{ + GtkAdjustment *vadj = nsgtk_layout_get_vadjustment(g->layout); + GtkAdjustment *hadj = nsgtk_layout_get_hadjustment(g->layout); + gdouble vlower, vpage, vupper, hlower, hpage, hupper, x = (double)sx, y = (double)sy; + + assert(vadj); + assert(hadj); + + g_object_get(vadj, "page-size", &vpage, "lower", &vlower, "upper", &vupper, NULL); + g_object_get(hadj, "page-size", &hpage, "lower", &hlower, "upper", &hupper, NULL); + + if (x < hlower) + x = hlower; + if (x > (hupper - hpage)) + x = hupper - hpage; + if (y < vlower) + y = vlower; + if (y > (vupper - vpage)) + y = vupper - vpage; + + gtk_adjustment_set_value(vadj, y); + gtk_adjustment_set_value(hadj, x); +} + +static void gui_window_update_extent(struct gui_window *g) +{ + int w, h; + + if (browser_window_get_extents(g->bw, true, &w, &h) == NSERROR_OK) { + gtk_layout_set_size(g->layout, w, h); + } +} + +static void gui_window_set_pointer(struct gui_window *g, + gui_pointer_shape shape) +{ + GdkCursor *cursor = NULL; + GdkCursorType cursortype; + bool nullcursor = false; + + if (g->current_pointer == shape) + return; + + g->current_pointer = shape; + + switch (shape) { + case GUI_POINTER_POINT: + cursortype = GDK_HAND2; + break; + case GUI_POINTER_CARET: + cursortype = GDK_XTERM; + break; + case GUI_POINTER_UP: + cursortype = GDK_TOP_SIDE; + break; + case GUI_POINTER_DOWN: + cursortype = GDK_BOTTOM_SIDE; + break; + case GUI_POINTER_LEFT: + cursortype = GDK_LEFT_SIDE; + break; + case GUI_POINTER_RIGHT: + cursortype = GDK_RIGHT_SIDE; + break; + case GUI_POINTER_LD: + cursortype = GDK_BOTTOM_LEFT_CORNER; + break; + case GUI_POINTER_RD: + cursortype = GDK_BOTTOM_RIGHT_CORNER; + break; + case GUI_POINTER_LU: + cursortype = GDK_TOP_LEFT_CORNER; + break; + case GUI_POINTER_RU: + cursortype = GDK_TOP_RIGHT_CORNER; + break; + case GUI_POINTER_CROSS: + cursortype = GDK_CROSS; + break; + case GUI_POINTER_MOVE: + cursortype = GDK_FLEUR; + break; + case GUI_POINTER_WAIT: + cursortype = GDK_WATCH; + break; + case GUI_POINTER_HELP: + cursortype = GDK_QUESTION_ARROW; + break; + case GUI_POINTER_MENU: + cursor = nsgtk_create_menu_cursor(); + nullcursor = true; + break; + case GUI_POINTER_PROGRESS: + /* In reality, this needs to be the funky left_ptr_watch + * which we can't do easily yet. + */ + cursortype = GDK_WATCH; + break; + /* The following we're not sure about */ + case GUI_POINTER_NO_DROP: + case GUI_POINTER_NOT_ALLOWED: + case GUI_POINTER_DEFAULT: + default: + nullcursor = true; + } + + if (!nullcursor) + cursor = gdk_cursor_new_for_display( + gtk_widget_get_display( + GTK_WIDGET(g->layout)), + cursortype); + gdk_window_set_cursor(nsgtk_widget_get_window(GTK_WIDGET(g->layout)), + cursor); + + if (!nullcursor) + nsgdk_cursor_unref(cursor); +} + + +static void gui_window_place_caret(struct gui_window *g, int x, int y, int height, + const struct rect *clip) +{ + nsgtk_redraw_caret(g); + + y += 1; + height -= 1; + + if (y < clip->y0) { + height -= clip->y0 - y; + y = clip->y0; + } + + if (y + height > clip->y1) { + height = clip->y1 - y + 1; + } + + g->caretx = x; + g->carety = y; + g->careth = height; + + nsgtk_redraw_caret(g); + + gtk_widget_grab_focus(GTK_WIDGET(g->layout)); +} + + +static void gui_window_get_dimensions(struct gui_window *g, int *width, int *height, + bool scaled) +{ + GtkAllocation alloc; + + /* @todo consider gtk_widget_get_allocated_width() */ + nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc); + + *width = alloc.width; + *height = alloc.height; + + if (scaled) { + float scale = browser_window_get_scale(g->bw); + *width /= scale; + *height /= scale; + } + LOG("width: %i", *width); + LOG("height: %i", *height); +} + +static void gui_window_start_selection(struct gui_window *g) +{ + gtk_widget_grab_focus(GTK_WIDGET(g->layout)); +} + +static void gui_window_create_form_select_menu(struct gui_window *g, + struct form_control *control) +{ + intptr_t item; + struct form_option *option; + + GtkWidget *menu_item; + + /* control->data.select.multiple is true if multiple selections + * are allowable. We ignore this, as the core handles it for us. + * Yay. \o/ + */ + + if (select_menu != NULL) { + gtk_widget_destroy(select_menu); + } + + select_menu = gtk_menu_new(); + select_menu_control = control; + + item = 0; + option = form_select_get_option(control, item); + while (option != NULL) { + LOG("Item %"PRIdPTR" option %p text %s", + item, option, option->text); + menu_item = gtk_check_menu_item_new_with_label(option->text); + if (option->selected) { + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(menu_item), TRUE); + } + + /* + * This casts the item index integer into an integer + * the size of a pointer. This allows the callback + * parameter to be passed avoiding allocating memory + * for a context with a single integer in it. + */ + g_signal_connect(menu_item, "toggled", + G_CALLBACK(nsgtk_select_menu_clicked), (gpointer)item); + + gtk_menu_shell_append(GTK_MENU_SHELL(select_menu), menu_item); + + item++; + option = form_select_get_option(control, item); + } + + gtk_widget_show_all(select_menu); + + gtk_menu_popup(GTK_MENU(select_menu), NULL, NULL, NULL, + NULL /* data */, 0, gtk_get_current_event_time()); + +} + +static void +gui_window_file_gadget_open(struct gui_window *g, + hlcache_handle *hl, + struct form_control *gadget) +{ + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Select File", + nsgtk_scaffolding_window(g->scaffold), + GTK_FILE_CHOOSER_ACTION_OPEN, + NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NSGTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + LOG("*** open dialog: %p", dialog); + + int ret = gtk_dialog_run(GTK_DIALOG(dialog)); + LOG("*** return value: %d", ret); + if (ret == GTK_RESPONSE_ACCEPT) { + char *filename; + + filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(dialog)); + + browser_window_set_gadget_filename(g->bw, gadget, filename); + + g_free(filename); + } + + gtk_widget_destroy(dialog); +} + +static struct gui_window_table window_table = { + .create = gui_window_create, + .destroy = gui_window_destroy, + .redraw = gui_window_redraw_window, + .update = gui_window_update_box, + .get_scroll = gui_window_get_scroll, + .set_scroll = gui_window_set_scroll, + .get_dimensions = gui_window_get_dimensions, + .update_extent = gui_window_update_extent, + .reformat = nsgtk_window_reformat, + + .set_icon = gui_window_set_icon, + .set_status = gui_window_set_status, + .set_pointer = gui_window_set_pointer, + .place_caret = gui_window_place_caret, + .remove_caret = gui_window_remove_caret, + .create_form_select_menu = gui_window_create_form_select_menu, + .file_gadget_open = gui_window_file_gadget_open, + .start_selection = gui_window_start_selection, + + /* from scaffold */ + .set_title = nsgtk_window_set_title, + .set_url = gui_window_set_url, + .start_throbber = gui_window_start_throbber, + .stop_throbber = gui_window_stop_throbber, +}; + +struct gui_window_table *nsgtk_window_table = &window_table; diff --git a/frontends/gtk/window.h b/frontends/gtk/window.h new file mode 100644 index 000000000..c604bf3f7 --- /dev/null +++ b/frontends/gtk/window.h @@ -0,0 +1,48 @@ +/* + * Copyright 2006 Daniel Silverstone + * + * 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 . + */ + +#ifndef NETSURF_GTK_WINDOW_H +#define NETSURF_GTK_WINDOW_H 1 + +#include "gtk/scaffolding.h" + +extern struct gui_window_table *nsgtk_window_table; + +typedef enum nsgtk_window_signals { + NSGTK_WINDOW_SIGNAL_CLICK, + NSGTK_WINDOW_SIGNAL_REDRAW, + NSGTK_WINDOW_SIGNAL_COUNT +} nsgtk_window_signal; + +extern struct gui_window *window_list; +extern int temp_open_background; + +struct browser_window *nsgtk_get_browser_window(struct gui_window *g); +struct nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g); +GdkPixbuf *nsgtk_get_icon(struct gui_window *gw); +void nsgtk_reflow_all_windows(void); +float nsgtk_get_scale_for_gui(struct gui_window *g); +int nsgtk_gui_window_update_targets(struct gui_window *g); +void nsgtk_window_destroy_browser(struct gui_window *g); +unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i); +GtkLayout *nsgtk_window_get_layout(struct gui_window *g); +struct gui_window *nsgtk_window_iterate(struct gui_window *g); +GtkWidget *nsgtk_window_get_tab(struct gui_window *g); +void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w); + +#endif /* NETSURF_GTK_WINDOW_H */ -- cgit v1.2.3