summaryrefslogtreecommitdiffstats
path: root/plug-ins/help-browser/dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/help-browser/dialog.c')
-rw-r--r--plug-ins/help-browser/dialog.c1255
1 files changed, 1255 insertions, 0 deletions
diff --git a/plug-ins/help-browser/dialog.c b/plug-ins/help-browser/dialog.c
new file mode 100644
index 0000000..af95ae9
--- /dev/null
+++ b/plug-ins/help-browser/dialog.c
@@ -0,0 +1,1255 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Help Browser
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * RĂ³man Joost <romanofski@gimp.org>
+ *
+ * dialog.c
+ *
+ * This program 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <gdk/gdkkeysyms.h>
+
+#include <webkit/webkit.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "plug-ins/help/gimphelp.h"
+
+#include "gimpthrobber.h"
+#include "gimpthrobberaction.h"
+
+#include "dialog.h"
+#include "uri.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define GIMP_HELP_BROWSER_DIALOG_DATA "gimp-help-browser-dialog"
+
+#define GIMP_HELP_BROWSER_INDEX_MAX_DEPTH 4
+
+
+typedef struct
+{
+ gint width;
+ gint height;
+ gint paned_position;
+ gboolean show_index;
+ gdouble zoom;
+} DialogData;
+
+enum
+{
+ HISTORY_TITLE,
+ HISTORY_URI
+};
+
+/* local function prototypes */
+
+static GtkUIManager * ui_manager_new (GtkWidget *window);
+
+static GtkWidget * build_searchbar (void);
+
+static void back_callback (GtkAction *action,
+ gpointer data);
+static void forward_callback (GtkAction *action,
+ gpointer data);
+static void reload_callback (GtkAction *action,
+ gpointer data);
+static void stop_callback (GtkAction *action,
+ gpointer data);
+static void home_callback (GtkAction *action,
+ gpointer data);
+static void find_callback (GtkAction *action,
+ gpointer data);
+static void find_again_callback (GtkAction *action,
+ gpointer data);
+static void copy_location_callback (GtkAction *action,
+ gpointer data);
+static void copy_selection_callback (GtkAction *action,
+ gpointer data);
+static void show_index_callback (GtkAction *action,
+ gpointer data);
+static void zoom_in_callback (GtkAction *action,
+ gpointer data);
+static void zoom_out_callback (GtkAction *action,
+ gpointer data);
+static void close_callback (GtkAction *action,
+ gpointer data);
+static void website_callback (GtkAction *action,
+ gpointer data);
+
+static void update_actions (void);
+
+static void row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column);
+static void dialog_unmap (GtkWidget *window,
+ GtkWidget *paned);
+
+static void view_realize (GtkWidget *widget);
+static void view_unrealize (GtkWidget *widget);
+static gboolean view_popup_menu (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean view_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean view_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+
+static void title_changed (GtkWidget *view,
+ WebKitWebFrame *frame,
+ const gchar *title,
+ GtkWidget *window);
+static void load_started (GtkWidget *view,
+ WebKitWebFrame *frame);
+static void load_finished (GtkWidget *view,
+ WebKitWebFrame *frame);
+
+static void select_index (const gchar *uri);
+
+static void search_entry_changed (GtkWidget *entry);
+static gboolean search_entry_key_press (GtkWidget *entry,
+ GdkEventKey *event);
+static void search_prev_clicked (GtkWidget *button,
+ GtkWidget *entry);
+static void search_next_clicked (GtkWidget *button,
+ GtkWidget *entry);
+static void search_close_clicked (GtkWidget *button);
+static void search (const gchar *text,
+ gboolean forward);
+
+
+/* private variables */
+
+static GHashTable *uri_hash_table = NULL;
+
+static GtkWidget *view = NULL;
+static GtkWidget *sidebar = NULL;
+static GtkWidget *searchbar = NULL;
+static GtkWidget *tree_view = NULL;
+static GtkUIManager *ui_manager = NULL;
+static GtkWidget *button_prev = NULL;
+static GtkWidget *button_next = NULL;
+static GdkCursor *busy_cursor = NULL;
+
+
+/* public functions */
+
+void
+browser_dialog_open (const gchar *plug_in_binary)
+{
+ GtkWidget *window;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *toolbar;
+ GtkWidget *paned;
+ GtkWidget *scrolled;
+ GtkToolItem *item;
+ GtkAction *action;
+ DialogData data = { 720, 560, 240, TRUE, 1.0 };
+
+ gimp_ui_init (plug_in_binary, TRUE);
+
+ gimp_get_data (GIMP_HELP_BROWSER_DIALOG_DATA, &data);
+
+ /* the dialog window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), _("GIMP Help Browser"));
+ gtk_window_set_role (GTK_WINDOW (window), plug_in_binary);
+ gtk_window_set_icon_name (GTK_WINDOW (window), GIMP_ICON_HELP_USER_MANUAL);
+
+ gtk_window_set_default_size (GTK_WINDOW (window), data.width, data.height);
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_widget_show (vbox);
+
+ ui_manager = ui_manager_new (window);
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/help-browser-toolbar");
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ item = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 0);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/forward");
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (item), action);
+ g_object_notify (G_OBJECT (action), "tooltip");
+ button_next = GTK_WIDGET (item);
+
+ item = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 0);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/back");
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (item), action);
+ g_object_notify (G_OBJECT (action), "tooltip");
+ button_prev = GTK_WIDGET (item);
+
+ item =
+ GTK_TOOL_ITEM (gtk_ui_manager_get_widget (ui_manager,
+ "/help-browser-toolbar/space"));
+ gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
+ gtk_tool_item_set_expand (item, TRUE);
+
+ /* the horizontal paned */
+ paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_start (GTK_BOX (vbox), paned, TRUE, TRUE, 0);
+ gtk_widget_show (paned);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_paned_add1 (GTK_PANED (paned), scrolled);
+ gtk_paned_set_position (GTK_PANED (paned), data.paned_position);
+
+ sidebar = scrolled;
+
+ if (data.show_index)
+ gtk_widget_show (sidebar);
+
+ tree_view = gtk_tree_view_new ();
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
+ gtk_container_add (GTK_CONTAINER (scrolled), tree_view);
+ gtk_widget_show (tree_view);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view), -1,
+ NULL,
+ gtk_cell_renderer_text_new (),
+ "text", 1,
+ NULL);
+
+ g_signal_connect (tree_view, "row-activated",
+ G_CALLBACK (row_activated),
+ NULL);
+
+ /* HTML view */
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_widget_show (main_vbox);
+ gtk_paned_pack2 (GTK_PANED (paned), main_vbox, TRUE, TRUE);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_widget_set_size_request (scrolled, 300, 200);
+ gtk_box_pack_start (GTK_BOX (main_vbox), scrolled, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled);
+
+ view = webkit_web_view_new ();
+ webkit_web_view_set_maintains_back_forward_list (WEBKIT_WEB_VIEW (view),
+ TRUE);
+ gtk_container_add (GTK_CONTAINER (scrolled), view);
+ gtk_widget_show (view);
+
+ g_signal_connect (view, "realize",
+ G_CALLBACK (view_realize),
+ NULL);
+ g_signal_connect (view, "unrealize",
+ G_CALLBACK (view_unrealize),
+ NULL);
+
+ g_signal_connect (view, "popup-menu",
+ G_CALLBACK (view_popup_menu),
+ NULL);
+ g_signal_connect (view, "button-press-event",
+ G_CALLBACK (view_button_press),
+ NULL);
+ g_signal_connect (view, "key-press-event",
+ G_CALLBACK (view_key_press),
+ NULL);
+
+ webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (view), data.zoom);
+
+ g_signal_connect (view, "title-changed",
+ G_CALLBACK (title_changed),
+ window);
+
+ g_signal_connect (view, "load-started",
+ G_CALLBACK (load_started),
+ NULL);
+ g_signal_connect (view, "load-finished",
+ G_CALLBACK (load_finished),
+ NULL);
+
+ gtk_widget_grab_focus (view);
+
+ g_signal_connect (window, "unmap",
+ G_CALLBACK (dialog_unmap),
+ paned);
+
+ update_actions ();
+
+ /* Searchbar */
+ searchbar = build_searchbar ();
+ gtk_box_pack_start (GTK_BOX (main_vbox), searchbar, FALSE, FALSE, 0);
+}
+
+void
+browser_dialog_load (const gchar *uri)
+{
+ g_return_if_fail (uri != NULL);
+
+ webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), uri);
+
+ select_index (uri);
+
+ gtk_window_present (GTK_WINDOW (gtk_widget_get_toplevel (view)));
+}
+
+static void
+browser_dialog_make_index_foreach (const gchar *help_id,
+ GimpHelpItem *item,
+ GimpHelpLocale *locale)
+{
+ gchar *sort_key = item->title;
+
+#if DEBUG_SORT_HELP_ITEMS
+ g_printerr ("%s: processing %s (parent %s)\n",
+ G_STRFUNC,
+ item->title ? item->title : "NULL",
+ item->parent ? item->parent : "NULL");
+#endif
+
+ if (item->sort &&
+ g_regex_match_simple ("^[0-9]+([.][0-9]+)*$", item->sort, 0, 0))
+ {
+ sort_key = item->sort;
+
+#if DEBUG_SORT_HELP_ITEMS
+ g_printerr ("%s: sort key = %s\n", G_STRFUNC, sort_key);
+#endif
+ }
+
+ item->index = 0;
+
+ if (sort_key)
+ {
+ const gint max_tokens = GIMP_HELP_BROWSER_INDEX_MAX_DEPTH;
+ gchar* *indices = g_strsplit (sort_key, ".", max_tokens + 1);
+ gint i;
+
+ for (i = 0; i < max_tokens; i++)
+ {
+ gunichar c;
+
+ if (! indices[i])
+ {
+ /* make sure that all item->index's are comparable */
+ item->index <<= (8 * (max_tokens - i));
+ break;
+ }
+
+ item->index <<= 8; /* NOP if i = 0 */
+ c = g_utf8_get_char (indices[i]);
+ if (g_unichar_isdigit (c))
+ {
+ item->index += atoi (indices[i]);
+ }
+ else if (g_utf8_strlen (indices[i], -1) == 1)
+ {
+ item->index += (c & 0xFF);
+ }
+ }
+
+ g_strfreev (indices);
+
+#if DEBUG_SORT_HELP_ITEMS
+ g_printerr ("%s: index = %lu\n", G_STRFUNC, item->index);
+#endif
+ }
+
+ if (item->parent && strlen (item->parent))
+ {
+ GimpHelpItem *parent;
+
+ parent = g_hash_table_lookup (locale->help_id_mapping, item->parent);
+
+ if (parent)
+ {
+ parent->children = g_list_prepend (parent->children, item);
+ }
+ }
+ else
+ {
+ locale->toplevel_items = g_list_prepend (locale->toplevel_items, item);
+ }
+}
+
+static gint
+help_item_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const GimpHelpItem *item_a = a;
+ const GimpHelpItem *item_b = b;
+
+ if (item_a->index > item_b->index)
+ return 1;
+ else if (item_a->index < item_b->index)
+ return -1;
+
+ return 0;
+}
+
+static void
+add_child (GtkTreeStore *store,
+ GimpHelpDomain *domain,
+ GimpHelpLocale *locale,
+ GtkTreeIter *parent,
+ GimpHelpItem *item,
+ gint depth)
+{
+ GtkTreeIter iter;
+ GList *list;
+ gchar *uri;
+
+ gtk_tree_store_append (store, &iter, parent);
+
+ gtk_tree_store_set (store, &iter,
+ 0, item,
+ 1, item->title,
+ -1);
+
+ uri = g_strconcat (domain->help_uri, "/",
+ locale->locale_id, "/",
+ item->ref,
+ NULL);
+
+ g_hash_table_insert (uri_hash_table,
+ uri,
+ gtk_tree_iter_copy (&iter));
+
+ if (depth + 1 == GIMP_HELP_BROWSER_INDEX_MAX_DEPTH)
+ return;
+
+ item->children = g_list_sort (item->children, help_item_compare);
+
+ for (list = item->children; list; list = g_list_next (list))
+ {
+ GimpHelpItem *item = list->data;
+
+ add_child (store, domain, locale, &iter, item, depth + 1);
+ }
+}
+
+void
+browser_dialog_make_index (GimpHelpDomain *domain,
+ GimpHelpLocale *locale)
+{
+ GtkTreeStore *store;
+ GList *list;
+
+ if (! locale->toplevel_items)
+ {
+ g_hash_table_foreach (locale->help_id_mapping,
+ (GHFunc) browser_dialog_make_index_foreach,
+ locale);
+
+ locale->toplevel_items = g_list_sort (locale->toplevel_items,
+ help_item_compare);
+ }
+
+ store = gtk_tree_store_new (2,
+ G_TYPE_POINTER,
+ G_TYPE_STRING);
+
+ g_object_set_data (G_OBJECT (store), "domain", domain);
+ g_object_set_data (G_OBJECT (store), "locale", locale);
+
+ if (uri_hash_table)
+ g_hash_table_unref (uri_hash_table);
+
+ uri_hash_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gtk_tree_iter_free);
+
+ for (list = locale->toplevel_items; list; list = g_list_next (list))
+ {
+ GimpHelpItem *item = list->data;
+
+ add_child (store, domain, locale, NULL, item, 0);
+ }
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (store));
+ g_object_unref (store);
+}
+
+static void
+select_index (const gchar *uri)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter *iter = NULL;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+
+ if (uri)
+ iter = g_hash_table_lookup (uri_hash_table, uri);
+
+ if (iter)
+ {
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+ GtkTreePath *path;
+ GtkTreePath *scroll_path;
+
+ path = gtk_tree_model_get_path (model, iter);
+ scroll_path = gtk_tree_path_copy (path);
+
+ gtk_tree_path_up (path);
+ gtk_tree_view_expand_to_path (GTK_TREE_VIEW (tree_view), path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view), scroll_path,
+ NULL, FALSE, 0.0, 0.0);
+
+ gtk_tree_path_free (path);
+ gtk_tree_path_free (scroll_path);
+
+ gtk_tree_selection_select_iter (selection, iter);
+ }
+ else
+ {
+ gtk_tree_selection_unselect_all (selection);
+ }
+}
+
+
+/* private functions */
+
+static GtkUIManager *
+ui_manager_new (GtkWidget *window)
+{
+ static const GtkActionEntry actions[] =
+ {
+ {
+ "back", GIMP_ICON_GO_PREVIOUS,
+ NULL, "<alt>Left", N_("Go back one page"),
+ G_CALLBACK (back_callback)
+ },
+ {
+ "forward", GIMP_ICON_GO_NEXT,
+ NULL, "<alt>Right", N_("Go forward one page"),
+ G_CALLBACK (forward_callback)
+ },
+ {
+ "reload", GIMP_ICON_VIEW_REFRESH,
+ N_("_Reload"), "<control>R", N_("Reload current page"),
+ G_CALLBACK (reload_callback)
+ },
+ {
+ "stop", GIMP_ICON_PROCESS_STOP,
+ N_("_Stop"), "Escape", N_("Stop loading this page"),
+ G_CALLBACK (stop_callback)
+ },
+ {
+ "home", GIMP_ICON_GO_HOME,
+ NULL, "<alt>Home", N_("Go to the index page"),
+ G_CALLBACK (home_callback)
+ },
+ {
+ "copy-location", GIMP_ICON_EDIT_COPY,
+ N_("C_opy location"), "",
+ N_("Copy the location of this page to the clipboard"),
+ G_CALLBACK (copy_location_callback)
+ },
+ {
+ "copy-selection", GIMP_ICON_EDIT_COPY,
+ NULL, "<control>C", NULL,
+ G_CALLBACK (copy_selection_callback)
+ },
+ {
+ "zoom-in", GIMP_ICON_ZOOM_IN,
+ NULL, "<control>plus", NULL,
+ G_CALLBACK (zoom_in_callback)
+ },
+ {
+ "zoom-out", GIMP_ICON_ZOOM_OUT,
+ NULL, "<control>minus", NULL,
+ G_CALLBACK (zoom_out_callback)
+ },
+ {
+ "find", GIMP_ICON_EDIT_FIND,
+ NULL, "<control>F", N_("Find text in current page"),
+ G_CALLBACK (find_callback)
+ },
+ {
+ "find-again", NULL,
+ N_("Find _Again"), "<control>G", NULL,
+ G_CALLBACK (find_again_callback)
+ },
+ {
+ "close", GIMP_ICON_WINDOW_CLOSE,
+ NULL, "<control>W", NULL,
+ G_CALLBACK (close_callback)
+ },
+ {
+ "quit", GIMP_ICON_APPLICATION_EXIT,
+ NULL, "<control>Q", NULL,
+ G_CALLBACK (close_callback)
+ }
+ };
+
+ static const GtkToggleActionEntry toggle_actions[] =
+ {
+ {
+ "show-index", NULL,
+ N_("S_how Index"), "<control>I",
+ N_("Toggle the visibility of the sidebar"),
+ G_CALLBACK (show_index_callback), FALSE
+ }
+ };
+
+ GtkUIManager *ui_manager = gtk_ui_manager_new ();
+ GtkActionGroup *group = gtk_action_group_new ("Actions");
+ GtkAction *action;
+ GError *error = NULL;
+
+ gtk_action_group_set_translation_domain (group, NULL);
+ gtk_action_group_add_actions (group,
+ actions, G_N_ELEMENTS (actions),
+ NULL);
+ gtk_action_group_add_toggle_actions (group,
+ toggle_actions,
+ G_N_ELEMENTS (toggle_actions),
+ NULL);
+
+ action = gimp_throbber_action_new ("website",
+ "docs.gimp.org",
+ _("Visit the GIMP documentation website"),
+ GIMP_ICON_HELP_USER_MANUAL);
+ g_signal_connect_closure (action, "activate",
+ g_cclosure_new (G_CALLBACK (website_callback),
+ NULL, NULL),
+ FALSE);
+ gtk_action_group_add_action (group, action);
+ g_object_unref (action);
+
+ gtk_window_add_accel_group (GTK_WINDOW (window),
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
+
+ gtk_ui_manager_insert_action_group (ui_manager, group, -1);
+ g_object_unref (group);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <toolbar name=\"help-browser-toolbar\">"
+ " <toolitem action=\"reload\" />"
+ " <toolitem action=\"stop\" />"
+ " <toolitem action=\"home\" />"
+ " <separator name=\"space\" />"
+ " <toolitem action=\"website\" />"
+ " </toolbar>"
+ " <accelerator action=\"close\" />"
+ " <accelerator action=\"quit\" />"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <popup name=\"help-browser-popup\">"
+ " <menuitem action=\"back\" />"
+ " <menuitem action=\"forward\" />"
+ " <menuitem action=\"reload\" />"
+ " <menuitem action=\"stop\" />"
+ " <separator />"
+ " <menuitem action=\"home\" />"
+ " <menuitem action=\"copy-location\" />"
+ " <menuitem action=\"show-index\" />"
+ " <separator />"
+ " <menuitem action=\"find\" />"
+ " <menuitem action=\"find-again\" />"
+ " <separator />"
+ " <menuitem action=\"zoom-in\" />"
+ " <menuitem action=\"zoom-out\" />"
+ " <separator />"
+ " <menuitem action=\"close\" />"
+ " </popup>"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <popup name=\"help-browser-copy-popup\">"
+ " <menuitem action=\"copy-selection\" />"
+ " </popup>"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ return ui_manager;
+}
+
+static void
+back_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_go_back (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+forward_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_go_forward (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+reload_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_reload (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+stop_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+home_callback (GtkAction *action,
+ gpointer data)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+ GimpHelpDomain *domain = g_object_get_data (G_OBJECT (model), "domain");
+ GimpHelpLocale *locale = g_object_get_data (G_OBJECT (model), "locale");
+
+ if (domain && locale)
+ {
+ gchar *uri = g_strconcat (domain->help_uri, "/",
+ locale->locale_id, "/",
+ gimp_help_locale_map (locale,
+ GIMP_HELP_DEFAULT_ID),
+ NULL);
+ browser_dialog_load (uri);
+ g_free (uri);
+ }
+}
+
+static void
+find_callback (GtkAction *action,
+ gpointer data)
+{
+ GtkWidget *entry = g_object_get_data (G_OBJECT (searchbar), "entry");
+
+ gtk_widget_show (searchbar);
+ gtk_widget_grab_focus (entry);
+}
+
+static void
+find_again_callback (GtkAction *action,
+ gpointer data)
+{
+ GtkWidget *entry = g_object_get_data (G_OBJECT (searchbar), "entry");
+
+ gtk_widget_show (searchbar);
+ gtk_widget_grab_focus (entry);
+
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+}
+
+static void
+copy_location_callback (GtkAction *action,
+ gpointer data)
+{
+ WebKitWebFrame *frame;
+ const gchar *uri;
+
+ frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view));
+ uri = webkit_web_frame_get_uri (frame);
+
+ if (uri)
+ {
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (view),
+ GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, uri, -1);
+ }
+}
+
+static void
+copy_selection_callback (GtkAction *action,
+ gpointer data)
+{
+ if (webkit_web_view_can_copy_clipboard (WEBKIT_WEB_VIEW (view)))
+ {
+ webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view));
+ }
+}
+
+static void
+show_index_callback (GtkAction *action,
+ gpointer data)
+{
+ gtk_widget_set_visible (sidebar,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+static void
+zoom_in_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+zoom_out_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+website_callback (GtkAction *action,
+ gpointer data)
+{
+ browser_dialog_load ("https://docs.gimp.org/");
+}
+
+static void
+close_callback (GtkAction *action,
+ gpointer data)
+{
+ gtk_widget_destroy (gtk_widget_get_toplevel (view));
+}
+
+static void
+menu_callback (GtkWidget *menu,
+ gpointer data)
+{
+ gint steps = GPOINTER_TO_INT (data);
+
+ webkit_web_view_go_back_or_forward (WEBKIT_WEB_VIEW (view), steps);
+}
+
+static GtkWidget *
+build_menu (const GList *items,
+ gboolean back)
+{
+ GtkWidget *menu;
+ const GList *iter;
+ gint steps;
+
+ if (! items)
+ return NULL;
+
+ menu = gtk_menu_new ();
+
+ for (iter = items, steps = 1; iter; iter = g_list_next (iter), steps++)
+ {
+ WebKitWebHistoryItem *item = iter->data;
+ const gchar *title;
+
+ title = webkit_web_history_item_get_title (item);
+
+ if (title)
+ {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label (title);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ g_signal_connect (menu_item, "activate",
+ G_CALLBACK (menu_callback),
+ GINT_TO_POINTER (back ? - steps : steps));
+ }
+ }
+
+ return menu;
+}
+
+static void
+update_actions (void)
+{
+ GtkAction *action;
+ WebKitWebBackForwardList *back_forward_list;
+ WebKitWebFrame *frame;
+
+ back_forward_list =
+ webkit_web_view_get_back_forward_list (WEBKIT_WEB_VIEW (view));
+
+ /* update the back button and its menu */
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/back");
+ gtk_action_set_sensitive (action,
+ webkit_web_view_can_go_back (WEBKIT_WEB_VIEW (view)));
+
+ if (back_forward_list)
+ {
+ const GList *list;
+
+ list = webkit_web_back_forward_list_get_back_list_with_limit (back_forward_list,
+ 12);
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_prev),
+ build_menu (list, TRUE));
+ }
+ else
+ {
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_prev), NULL);
+ }
+
+ /* update the forward button and its menu */
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/forward");
+ gtk_action_set_sensitive (action,
+ webkit_web_view_can_go_forward (WEBKIT_WEB_VIEW (view)));
+
+ if (back_forward_list)
+ {
+ const GList *list;
+
+ list = webkit_web_back_forward_list_get_forward_list_with_limit (back_forward_list,
+ 12);
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_next),
+ build_menu (list, FALSE));
+ }
+ else
+ {
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_next), NULL);
+ }
+
+ /* update the copy-location action */
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/copy-location");
+
+ frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view));
+ gtk_action_set_sensitive (action, webkit_web_frame_get_uri (frame) != NULL);
+
+ /* update the show-index action */
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/show-index");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ gtk_widget_get_visible (sidebar));
+}
+
+static void
+row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+ GtkTreeIter iter;
+ GimpHelpDomain *domain;
+ GimpHelpLocale *locale;
+ GimpHelpItem *item;
+ gchar *uri;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_tree_model_get (model, &iter,
+ 0, &item,
+ -1);
+
+ domain = g_object_get_data (G_OBJECT (model), "domain");
+ locale = g_object_get_data (G_OBJECT (model), "locale");
+
+ uri = g_strconcat (domain->help_uri, "/",
+ locale->locale_id, "/",
+ item->ref,
+ NULL);
+
+ browser_dialog_load (uri);
+
+ g_free (uri);
+}
+
+static void
+dialog_unmap (GtkWidget *window,
+ GtkWidget *paned)
+{
+ DialogData data;
+
+ gtk_window_get_size (GTK_WINDOW (window), &data.width, &data.height);
+
+ data.paned_position = gtk_paned_get_position (GTK_PANED (paned));
+ data.show_index = gtk_widget_get_visible (sidebar);
+
+ data.zoom = (view ?
+ webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (view)) : 1.0);
+
+ gimp_set_data (GIMP_HELP_BROWSER_DIALOG_DATA, &data, sizeof (data));
+
+ gtk_main_quit ();
+}
+
+static void
+view_realize (GtkWidget *widget)
+{
+ g_return_if_fail (busy_cursor == NULL);
+
+ busy_cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_WATCH);
+}
+
+static void
+view_unrealize (GtkWidget *widget)
+{
+ if (busy_cursor)
+ {
+ gdk_cursor_unref (busy_cursor);
+ busy_cursor = NULL;
+ }
+}
+
+static gboolean
+view_popup_menu (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkWidget *menu;
+ const gchar *path;
+
+ if (webkit_web_view_can_copy_clipboard (WEBKIT_WEB_VIEW (view)))
+ path = "/help-browser-copy-popup";
+ else
+ path = "/help-browser-popup";
+
+ menu = gtk_ui_manager_get_widget (ui_manager, path);
+
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ event ? event->button : 0,
+ event ? event->time : gtk_get_current_event_time ());
+
+ return TRUE;
+}
+
+static gboolean
+view_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ return view_popup_menu (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+view_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ if (event->keyval == GDK_KEY_slash)
+ {
+ GtkAction *action;
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/find");
+ gtk_action_activate (action);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+title_changed (GtkWidget *view,
+ WebKitWebFrame *frame,
+ const gchar *title,
+ GtkWidget *window)
+{
+ gchar *full_title;
+
+ full_title = g_strdup_printf ("%s - %s",
+ title ? title : _("Untitled"),
+ _("GIMP Help Browser"));
+
+ gtk_window_set_title (GTK_WINDOW (window), full_title);
+ g_free (full_title);
+
+ update_actions ();
+}
+
+static void
+load_started (GtkWidget *view,
+ WebKitWebFrame *frame)
+{
+ GtkAction *action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/stop");
+ gtk_action_set_sensitive (action, TRUE);
+}
+
+static void
+load_finished (GtkWidget *view,
+ WebKitWebFrame *frame)
+{
+ GtkAction *action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/stop");
+ gtk_action_set_sensitive (action, FALSE);
+
+ update_actions ();
+
+ select_index (webkit_web_frame_get_uri (frame));
+}
+
+static GtkWidget *
+build_searchbar (void)
+{
+ GtkWidget *button;
+ GtkWidget *entry;
+ GtkWidget *hbox;
+ GtkWidget *label;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ label = gtk_label_new (_("Find:"));
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ entry = gtk_entry_new ();
+ gtk_widget_show (entry);
+ gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+ g_object_set_data (G_OBJECT (hbox), "entry", entry);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (search_entry_changed),
+ NULL);
+
+ g_signal_connect (entry, "key-press-event",
+ G_CALLBACK (search_entry_key_press),
+ NULL);
+
+ button = gtk_button_new_with_mnemonic (C_("search", "_Previous"));
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_button_set_image (GTK_BUTTON (button),
+ gtk_image_new_from_icon_name (GIMP_ICON_GO_PREVIOUS,
+ GTK_ICON_SIZE_BUTTON));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (search_prev_clicked),
+ entry);
+
+ button = gtk_button_new_with_mnemonic (C_("search", "_Next"));
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_button_set_image (GTK_BUTTON (button),
+ gtk_image_new_from_icon_name (GIMP_ICON_GO_NEXT,
+ GTK_ICON_SIZE_BUTTON));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (search_next_clicked),
+ entry);
+
+ button = gtk_button_new_with_mnemonic (C_("search", "_Close"));
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (search_close_clicked),
+ NULL);
+
+ return hbox;
+}
+
+static void
+search_entry_changed (GtkWidget *entry)
+{
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+}
+
+static gboolean
+search_entry_key_press (GtkWidget *entry,
+ GdkEventKey *event)
+{
+ switch (event->keyval)
+ {
+ case GDK_KEY_Escape:
+ gtk_widget_hide (searchbar);
+ webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view));
+ return TRUE;
+
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_ISO_Enter:
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+search_prev_clicked (GtkWidget *button,
+ GtkWidget *entry)
+{
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), FALSE);
+}
+
+static void
+search_next_clicked (GtkWidget *button,
+ GtkWidget *entry)
+{
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+}
+
+static void
+search (const gchar *text,
+ gboolean forward)
+{
+ if (text)
+ webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
+ text, FALSE, forward, TRUE);
+}
+
+static void
+search_close_clicked (GtkWidget *button)
+{
+ gtk_widget_hide (searchbar);
+ webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view));
+}