diff options
Diffstat (limited to 'plug-ins/help-browser/dialog.c')
-rw-r--r-- | plug-ins/help-browser/dialog.c | 1255 |
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)); +} |