gimp/plug-ins/help-browser/dialog.c

1281 lines
39 KiB
C

/* 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 <webkit2/webkit2.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 (WebKitWebView *view,
GParamSpec *pspec,
GtkWidget *window);
static void load_changed (WebKitWebView *view,
WebKitLoadEvent event);
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);
/* 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);
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);
view = webkit_web_view_new ();
gtk_widget_set_size_request (view, 300, 200);
gtk_widget_show (view);
gtk_box_pack_start (GTK_BOX (main_vbox), view, TRUE, TRUE, 0);
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, "notify::title",
G_CALLBACK (title_changed),
window);
g_signal_connect (view, "load-changed",
G_CALLBACK (load_changed),
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)));
}
static void
copy_location_callback (GtkAction *action,
gpointer data)
{
const gchar *uri;
uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view));
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)
{
WebKitEditorState *editor_state =
webkit_web_view_get_editor_state (WEBKIT_WEB_VIEW (view));
if (webkit_editor_state_is_copy_available (editor_state))
{
webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (view),
WEBKIT_EDITING_COMMAND_COPY);
}
}
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)
{
gdouble zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (view));
if (zoom_level < 10.0)
webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (view), zoom_level + 0.1);
}
static void
zoom_out_callback (GtkAction *action,
gpointer data)
{
gdouble zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (view));
if (zoom_level > 0.1)
webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (view), zoom_level - 0.1);
}
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);
WebKitBackForwardList *back_fw_list =
webkit_web_view_get_back_forward_list (WEBKIT_WEB_VIEW (view));
WebKitBackForwardListItem *back_fw_list_item =
webkit_back_forward_list_get_nth_item (back_fw_list, steps);
if (back_fw_list_item)
webkit_web_view_go_to_back_forward_list_item (WEBKIT_WEB_VIEW (view),
back_fw_list_item);
}
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++)
{
WebKitBackForwardListItem *item = iter->data;
const gchar *title;
title = webkit_back_forward_list_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;
WebKitBackForwardList *back_forward_list;
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_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_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");
gtk_action_set_sensitive (action, webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view)) != 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)
{
g_object_unref (busy_cursor);
busy_cursor = NULL;
}
}
static gboolean
view_popup_menu (GtkWidget *widget,
GdkEventButton *event)
{
GtkWidget *menu;
const gchar *path;
WebKitEditorState *editor_state =
webkit_web_view_get_editor_state (WEBKIT_WEB_VIEW (view));
if (webkit_editor_state_is_copy_available (editor_state))
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_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
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 (WebKitWebView *view,
GParamSpec *pspec,
GtkWidget *window)
{
gchar *full_title;
const char *title = webkit_web_view_get_title (view);
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_changed (WebKitWebView *view,
WebKitLoadEvent event)
{
GtkAction *action = gtk_ui_manager_get_action (ui_manager,
"/ui/help-browser-popup/stop");
switch (event)
{
case WEBKIT_LOAD_STARTED:
gtk_action_set_sensitive (action, TRUE);
break;
case WEBKIT_LOAD_FINISHED:
gtk_action_set_sensitive (action, FALSE);
update_actions ();
select_index (webkit_web_view_get_uri (view));
break;
case WEBKIT_LOAD_REDIRECTED:
case WEBKIT_LOAD_COMMITTED:
break;
}
}
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)));
}
static gboolean
search_entry_key_press (GtkWidget *entry,
GdkEventKey *event)
{
WebKitFindController *find_controller =
webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (view));
switch (event->keyval)
{
case GDK_KEY_Escape:
gtk_widget_hide (searchbar);
webkit_find_controller_search_finish (find_controller);
return TRUE;
case GDK_KEY_Return:
case GDK_KEY_KP_Enter:
case GDK_KEY_ISO_Enter:
search (gtk_entry_get_text (GTK_ENTRY (entry)));
return TRUE;
}
return FALSE;
}
static void
search_prev_clicked (GtkWidget *button,
GtkWidget *entry)
{
WebKitFindController *find_controller =
webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (view));
webkit_find_controller_search_previous (find_controller);
}
static void
search_next_clicked (GtkWidget *button,
GtkWidget *entry)
{
WebKitFindController *find_controller =
webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (view));
webkit_find_controller_search_next (find_controller);
}
static void
search (const gchar *text)
{
WebKitFindController *find_controller =
webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (view));
if (text)
{
const char *prev_text =
webkit_find_controller_get_search_text (find_controller);
/* The previous search, if any, may need to be canceled. */
if (prev_text && strcmp (text, prev_text) != 0)
webkit_find_controller_search_finish (find_controller);
webkit_find_controller_search (find_controller,
text,
WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE |
WEBKIT_FIND_OPTIONS_WRAP_AROUND,
G_MAXUINT);
}
else
webkit_find_controller_search_finish (find_controller);
}
static void
search_close_clicked (GtkWidget *button)
{
WebKitFindController *find_controller =
webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (view));
gtk_widget_hide (searchbar);
webkit_find_controller_search_finish (find_controller);
}