mirror of https://github.com/GNOME/gimp.git
958 lines
24 KiB
C
958 lines
24 KiB
C
/* The GIMP -- an image manipulation program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* The GIMP Help Browser
|
|
* Copyright (C) 1999-2002 Sven Neumann <sven@gimp.org>
|
|
* Michael Natterer <mitch@gimp.org>
|
|
*
|
|
* Some code & ideas stolen from the GNOME help browser.
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
#include <libgtkhtml/gtkhtml.h>
|
|
|
|
#include <libgimp/gimp.h>
|
|
#include <libgimp/gimpui.h>
|
|
|
|
#include "queue.h"
|
|
#include "uri.h"
|
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
|
|
/* defines */
|
|
|
|
#ifdef __EMX__
|
|
#define chdir _chdir2
|
|
#endif
|
|
|
|
#define GIMP_HELP_EXT_NAME "extension_gimp_help_browser"
|
|
#define GIMP_HELP_TEMP_EXT_NAME "extension_gimp_help_browser_temp"
|
|
|
|
#define GIMP_HELP_PREFIX "help"
|
|
|
|
enum
|
|
{
|
|
BUTTON_HOME,
|
|
BUTTON_INDEX,
|
|
BUTTON_BACK,
|
|
BUTTON_FORWARD
|
|
};
|
|
|
|
|
|
typedef struct
|
|
{
|
|
const gchar *title;
|
|
const gchar *ref;
|
|
gint count;
|
|
} HistoryItem;
|
|
|
|
|
|
/* please make sure the translation is a valid and complete HTML snippet */
|
|
static const gchar *doc_not_found_format_string =
|
|
N_("<html><head><title>Document Not Found</title></head>"
|
|
"<body bgcolor=\"white\">"
|
|
"<div align=\"center\">"
|
|
"<div>%s</div>"
|
|
"<h3>Could not locate help documentation</h3>"
|
|
"<tt>%s</tt>"
|
|
"</div>"
|
|
"<br /><br />"
|
|
"<div align=\"justify\">"
|
|
"<small>"
|
|
"The requested document could not be found in your GIMP-Help path as "
|
|
"shown above. This means that the topic has not yet been written or your "
|
|
"installation is not complete. Ensure that your installation is complete "
|
|
"before reporting this error as a bug."
|
|
"</small>"
|
|
"</div>"
|
|
"</body>"
|
|
"</html>");
|
|
|
|
static const gchar *eek_png_tag = "<h1>Eeek!</h1>";
|
|
|
|
static gchar *gimp_help_root = NULL;
|
|
|
|
static GList *history = NULL;
|
|
static Queue *queue;
|
|
static gchar *current_ref;
|
|
|
|
static GtkWidget *html;
|
|
static GtkWidget *back_button;
|
|
static GtkWidget *forward_button;
|
|
static GtkWidget *combo;
|
|
|
|
static GtkTargetEntry help_dnd_target_table[] =
|
|
{
|
|
{ "_NETSCAPE_URL", 0, 0 },
|
|
};
|
|
|
|
|
|
/* GIMP plugin stuff */
|
|
|
|
static void query (void);
|
|
static void run (gchar *name,
|
|
gint nparams,
|
|
GimpParam *param,
|
|
gint *nreturn_vals,
|
|
GimpParam **return_vals);
|
|
|
|
GimpPlugInInfo PLUG_IN_INFO =
|
|
{
|
|
NULL, /* init_proc */
|
|
NULL, /* quit_proc */
|
|
query, /* query_proc */
|
|
run, /* run_proc */
|
|
};
|
|
|
|
static gboolean temp_proc_installed = FALSE;
|
|
|
|
/* forward declaration */
|
|
|
|
static void load_page (const gchar *ref,
|
|
gboolean add_to_queue);
|
|
static void request_url (HtmlDocument *doc,
|
|
const gchar *url,
|
|
HtmlStream *stream,
|
|
gpointer data);
|
|
static gboolean io_handler (GIOChannel *io,
|
|
GIOCondition condition,
|
|
gpointer data);
|
|
|
|
|
|
/* Taken from glib/gconvert.c:
|
|
* Test of haystack has the needle prefix, comparing case
|
|
* insensitive. haystack may be UTF-8, but needle must
|
|
* contain only ascii.
|
|
*/
|
|
static gboolean
|
|
has_case_prefix (const gchar *haystack, const gchar *needle)
|
|
{
|
|
const gchar *h = haystack;
|
|
const gchar *n = needle;
|
|
|
|
while (*n && *h && g_ascii_tolower (*n) == g_ascii_tolower (*h))
|
|
{
|
|
n++;
|
|
h++;
|
|
}
|
|
|
|
return (*n == '\0');
|
|
}
|
|
|
|
static void
|
|
close_callback (GtkWidget *widget,
|
|
gpointer user_data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
static void
|
|
update_toolbar (void)
|
|
{
|
|
if (back_button)
|
|
gtk_widget_set_sensitive (back_button, queue_has_prev (queue));
|
|
if (forward_button)
|
|
gtk_widget_set_sensitive (forward_button, queue_has_next (queue));
|
|
}
|
|
|
|
static void
|
|
button_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
const gchar *ref;
|
|
|
|
switch (GPOINTER_TO_INT (data))
|
|
{
|
|
case BUTTON_HOME:
|
|
load_page ("contents.html", TRUE);
|
|
break;
|
|
|
|
case BUTTON_INDEX:
|
|
load_page ("index.html", TRUE);
|
|
break;
|
|
|
|
case BUTTON_BACK:
|
|
if (!(ref = queue_prev (queue)))
|
|
return;
|
|
load_page (ref, FALSE);
|
|
queue_move_prev (queue);
|
|
break;
|
|
|
|
case BUTTON_FORWARD:
|
|
if (!(ref = queue_next (queue)))
|
|
return;
|
|
load_page (ref, FALSE);
|
|
queue_move_next (queue);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
update_toolbar ();
|
|
}
|
|
|
|
static void
|
|
entry_changed_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
GList *list;
|
|
HistoryItem *item;
|
|
const gchar *entry_text;
|
|
gchar *compare_text;
|
|
gboolean found = FALSE;
|
|
|
|
entry_text = gtk_entry_get_text (GTK_ENTRY (widget));
|
|
|
|
for (list = history; list && !found; list = list->next)
|
|
{
|
|
item = (HistoryItem *) list->data;
|
|
|
|
if (item->count)
|
|
{
|
|
compare_text = g_strdup_printf ("%s <%i>",
|
|
item->title, item->count + 1);
|
|
}
|
|
else
|
|
{
|
|
compare_text = (gchar *) item->title;
|
|
}
|
|
|
|
if (strcmp (compare_text, entry_text) == 0)
|
|
{
|
|
load_page (item->ref, TRUE);
|
|
found = TRUE;
|
|
}
|
|
|
|
if (item->count)
|
|
{
|
|
g_free (compare_text);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
history_add (const gchar *ref,
|
|
const gchar *title)
|
|
{
|
|
GList *list;
|
|
GList *found = NULL;
|
|
HistoryItem *item;
|
|
GList *combo_list = NULL;
|
|
gint title_found_count = 0;
|
|
|
|
for (list = history; list && !found; list = list->next)
|
|
{
|
|
item = (HistoryItem *) list->data;
|
|
|
|
if (strcmp (item->title, title) == 0)
|
|
{
|
|
if (strcmp (item->ref, ref) != 0)
|
|
{
|
|
title_found_count++;
|
|
continue;
|
|
}
|
|
|
|
found = list;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
item = (HistoryItem *) found->data;
|
|
history = g_list_remove_link (history, found);
|
|
}
|
|
else
|
|
{
|
|
item = g_new (HistoryItem, 1);
|
|
item->ref = g_strdup (ref);
|
|
item->title = g_strdup (title);
|
|
item->count = title_found_count;
|
|
}
|
|
|
|
history = g_list_prepend (history, item);
|
|
|
|
for (list = history; list; list = list->next)
|
|
{
|
|
gchar* combo_title;
|
|
|
|
item = (HistoryItem *) list->data;
|
|
|
|
if (item->count)
|
|
combo_title = g_strdup_printf ("%s <%i>",
|
|
item->title,
|
|
item->count + 1);
|
|
else
|
|
combo_title = g_strdup (item->title);
|
|
|
|
combo_list = g_list_prepend (combo_list, combo_title);
|
|
}
|
|
|
|
combo_list = g_list_reverse (combo_list);
|
|
|
|
g_signal_handlers_block_by_func (G_OBJECT (GTK_COMBO (combo)->entry),
|
|
entry_changed_callback, combo);
|
|
gtk_combo_set_popdown_strings (GTK_COMBO (combo), combo_list);
|
|
g_signal_handlers_unblock_by_func (G_OBJECT (GTK_COMBO (combo)->entry),
|
|
entry_changed_callback, combo);
|
|
|
|
for (list = combo_list; list; list = list->next)
|
|
g_free (list->data);
|
|
|
|
g_list_free (combo_list);
|
|
}
|
|
|
|
static void
|
|
title_changed (HtmlDocument *doc,
|
|
const gchar *new_title,
|
|
gpointer data)
|
|
{
|
|
gchar *title;
|
|
|
|
if (!new_title)
|
|
new_title = (_("<Untitled>"));
|
|
|
|
title = g_strstrip (g_strdup (new_title));
|
|
|
|
history_add (current_ref, title);
|
|
|
|
g_signal_handlers_block_by_func (G_OBJECT (GTK_COMBO (combo)->entry),
|
|
entry_changed_callback, combo);
|
|
gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (combo)->entry), title);
|
|
g_signal_handlers_unblock_by_func (G_OBJECT (GTK_COMBO (combo)->entry),
|
|
entry_changed_callback, combo);
|
|
|
|
g_free (title);
|
|
}
|
|
|
|
static void
|
|
load_remote_page (const gchar *ref)
|
|
{
|
|
GimpParam *return_vals;
|
|
gint nreturn_vals;
|
|
|
|
/* try to call netscape through the web_browser interface */
|
|
return_vals = gimp_run_procedure ("extension_web_browser",
|
|
&nreturn_vals,
|
|
GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
|
|
GIMP_PDB_STRING, ref,
|
|
GIMP_PDB_INT32, FALSE,
|
|
GIMP_PDB_END);
|
|
gimp_destroy_params (return_vals, nreturn_vals);
|
|
|
|
}
|
|
|
|
static void
|
|
load_page (const gchar *ref,
|
|
gboolean add_to_queue)
|
|
{
|
|
HtmlDocument *doc = HTML_VIEW (html)->document;
|
|
gchar *abs;
|
|
gchar *new_ref;
|
|
gchar *anchor;
|
|
|
|
g_return_if_fail (ref != NULL);
|
|
|
|
abs = uri_to_abs (ref, current_ref);
|
|
|
|
g_return_if_fail (abs != NULL);
|
|
|
|
anchor = strchr (ref, '#');
|
|
if (anchor && anchor[0] && anchor[1])
|
|
{
|
|
new_ref = g_strconcat (abs, anchor, NULL);
|
|
anchor += 1;
|
|
}
|
|
else
|
|
{
|
|
new_ref = g_strdup (abs);
|
|
anchor = NULL;
|
|
}
|
|
|
|
if (strcmp (current_ref, abs))
|
|
{
|
|
if (!has_case_prefix (abs, "file:/"))
|
|
{
|
|
load_remote_page (ref);
|
|
return;
|
|
}
|
|
|
|
html_document_clear (doc);
|
|
html_document_open_stream (doc, "text/html");
|
|
gtk_adjustment_set_value (gtk_layout_get_vadjustment (GTK_LAYOUT (html)),
|
|
0);
|
|
|
|
request_url (doc, abs, doc->current_stream, NULL);
|
|
}
|
|
|
|
if (anchor)
|
|
html_view_jump_to_anchor (HTML_VIEW (html), anchor);
|
|
|
|
g_free (current_ref);
|
|
current_ref = new_ref;
|
|
|
|
if (add_to_queue)
|
|
queue_add (queue, new_ref);
|
|
|
|
update_toolbar ();
|
|
}
|
|
|
|
static void
|
|
link_clicked (HtmlDocument *doc,
|
|
const gchar *url,
|
|
gpointer data)
|
|
{
|
|
load_page (url, TRUE);
|
|
}
|
|
|
|
static void
|
|
request_url (HtmlDocument *doc,
|
|
const gchar *url,
|
|
HtmlStream *stream,
|
|
gpointer data)
|
|
{
|
|
gchar *abs;
|
|
gchar *filename;
|
|
|
|
g_return_if_fail (url != NULL);
|
|
g_return_if_fail (stream != NULL);
|
|
|
|
abs = uri_to_abs (url, current_ref);
|
|
if (!abs)
|
|
return;
|
|
|
|
filename = g_filename_from_uri (abs, NULL, NULL);
|
|
|
|
if (filename)
|
|
{
|
|
gint fd;
|
|
|
|
fd = open (filename, O_RDONLY);
|
|
|
|
if (fd == -1)
|
|
{
|
|
gchar *msg = g_strdup_printf (gettext (doc_not_found_format_string),
|
|
eek_png_tag, filename);
|
|
|
|
html_document_write_stream (doc, msg, strlen (msg));
|
|
}
|
|
else
|
|
{
|
|
GIOChannel *io = g_io_channel_unix_new (fd);
|
|
|
|
g_io_channel_set_close_on_unref (io, TRUE);
|
|
g_io_channel_set_encoding (io, NULL, NULL);
|
|
|
|
g_io_add_watch (io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
|
|
io_handler, stream);
|
|
}
|
|
|
|
g_free (filename);
|
|
}
|
|
|
|
g_free (abs);
|
|
}
|
|
|
|
static gboolean
|
|
io_handler (GIOChannel *io,
|
|
GIOCondition condition,
|
|
gpointer data)
|
|
{
|
|
HtmlStream *stream;
|
|
gchar buffer[8192];
|
|
guint bytes;
|
|
|
|
stream = (HtmlStream *) data;
|
|
|
|
if (condition & G_IO_IN)
|
|
{
|
|
if (g_io_channel_read_chars (io, buffer, sizeof (buffer),
|
|
&bytes, NULL) != G_IO_STATUS_ERROR
|
|
&& bytes > 0)
|
|
{
|
|
html_stream_write (stream, buffer, bytes);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (condition & G_IO_HUP)
|
|
{
|
|
while (g_io_channel_read_chars (io, buffer, sizeof (buffer),
|
|
&bytes, NULL) != G_IO_STATUS_ERROR
|
|
&& bytes > 0)
|
|
{
|
|
html_stream_write (stream, buffer, bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
|
|
{
|
|
html_stream_close (stream);
|
|
g_io_channel_unref (io);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
drag_begin (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gpointer data)
|
|
{
|
|
gtk_drag_set_icon_stock (context, GTK_STOCK_JUMP_TO, -8, -8);
|
|
}
|
|
|
|
static void
|
|
drag_data_get (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
guint time,
|
|
gpointer data)
|
|
{
|
|
if (! current_ref)
|
|
return;
|
|
|
|
gtk_selection_data_set (selection_data,
|
|
selection_data->target,
|
|
8,
|
|
current_ref,
|
|
strlen (current_ref));
|
|
}
|
|
|
|
static void
|
|
open_browser_dialog (const gchar *help_path,
|
|
const gchar *locale,
|
|
const gchar *help_file)
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *bbox;
|
|
GtkWidget *scroll;
|
|
GtkWidget *button;
|
|
GtkWidget *drag_source;
|
|
GtkWidget *image;
|
|
gchar *eek_png_path;
|
|
|
|
gimp_ui_init ("helpbrowser", TRUE);
|
|
|
|
eek_png_path = g_build_filename (gimp_help_root, "images", "eek.png", NULL);
|
|
|
|
if (g_file_test (eek_png_path, G_FILE_TEST_EXISTS))
|
|
eek_png_tag = g_strdup_printf ("<img src=\"%s\">", eek_png_path);
|
|
|
|
g_free (eek_png_path);
|
|
|
|
/* the dialog window */
|
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
g_signal_connect (G_OBJECT (window), "destroy",
|
|
G_CALLBACK (close_callback),
|
|
NULL);
|
|
gtk_window_set_wmclass (GTK_WINDOW (window), "helpbrowser", "Gimp");
|
|
gtk_window_set_title (GTK_WINDOW (window), _("GIMP Help Browser"));
|
|
|
|
gimp_help_connect (window, gimp_standard_help_func, "dialogs/help.html");
|
|
|
|
vbox = gtk_vbox_new (FALSE, 2);
|
|
gtk_container_add (GTK_CONTAINER (window), vbox);
|
|
gtk_widget_show (vbox);
|
|
|
|
/* buttons */
|
|
bbox = gtk_hbutton_box_new ();
|
|
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START);
|
|
gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (bbox);
|
|
|
|
button = gtk_button_new_from_stock (GTK_STOCK_HOME);
|
|
gtk_container_add (GTK_CONTAINER (bbox), button);
|
|
gtk_widget_show (button);
|
|
g_signal_connect (G_OBJECT (button), "clicked",
|
|
G_CALLBACK (button_callback),
|
|
GINT_TO_POINTER (BUTTON_HOME));
|
|
|
|
button = gtk_button_new_from_stock (GTK_STOCK_INDEX);
|
|
gtk_container_add (GTK_CONTAINER (bbox), button);
|
|
gtk_widget_show (button);
|
|
g_signal_connect (G_OBJECT (button), "clicked",
|
|
G_CALLBACK (button_callback),
|
|
GINT_TO_POINTER (BUTTON_INDEX));
|
|
|
|
back_button = button = gtk_button_new_from_stock (GTK_STOCK_GO_BACK);
|
|
gtk_container_add (GTK_CONTAINER (bbox), button);
|
|
gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
|
|
g_signal_connect (G_OBJECT (button), "clicked",
|
|
G_CALLBACK (button_callback),
|
|
GINT_TO_POINTER (BUTTON_BACK));
|
|
gtk_widget_show (button);
|
|
|
|
forward_button = button = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD);
|
|
gtk_container_add (GTK_CONTAINER (bbox), button);
|
|
gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
|
|
g_signal_connect (G_OBJECT (button), "clicked",
|
|
G_CALLBACK (button_callback),
|
|
GINT_TO_POINTER (BUTTON_FORWARD));
|
|
gtk_widget_show (button);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 2);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
/* the drag source */
|
|
drag_source = gtk_event_box_new ();
|
|
gtk_box_pack_start (GTK_BOX (hbox), drag_source, FALSE, FALSE, 4);
|
|
gtk_widget_show (drag_source);
|
|
|
|
gtk_drag_source_set (GTK_WIDGET (drag_source),
|
|
GDK_BUTTON1_MASK,
|
|
help_dnd_target_table,
|
|
G_N_ELEMENTS (help_dnd_target_table),
|
|
GDK_ACTION_MOVE | GDK_ACTION_COPY);
|
|
g_signal_connect (G_OBJECT (drag_source), "drag_begin",
|
|
G_CALLBACK (drag_begin),
|
|
NULL);
|
|
g_signal_connect (G_OBJECT (drag_source), "drag_data_get",
|
|
G_CALLBACK (drag_data_get),
|
|
NULL);
|
|
|
|
image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_BUTTON);
|
|
gtk_container_add (GTK_CONTAINER (drag_source), image);
|
|
gtk_widget_show (image);
|
|
|
|
/* the title combo */
|
|
combo = gtk_combo_new ();
|
|
gtk_widget_set_size_request (GTK_WIDGET (combo), 360, -1);
|
|
gtk_combo_set_use_arrows (GTK_COMBO (combo), TRUE);
|
|
g_object_set (G_OBJECT (GTK_COMBO (combo)->entry), "editable", FALSE, NULL);
|
|
gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
|
|
gtk_widget_show (combo);
|
|
|
|
g_signal_connect (G_OBJECT (GTK_COMBO (combo)->entry), "changed",
|
|
G_CALLBACK (entry_changed_callback),
|
|
combo);
|
|
|
|
|
|
/* HTML view */
|
|
html = html_view_new ();
|
|
queue = queue_new ();
|
|
|
|
gtk_widget_set_size_request (GTK_WIDGET (html), -1, 240);
|
|
|
|
scroll =
|
|
gtk_scrolled_window_new (gtk_layout_get_hadjustment (GTK_LAYOUT (html)),
|
|
gtk_layout_get_vadjustment (GTK_LAYOUT (html)));
|
|
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
|
|
gtk_widget_show (scroll);
|
|
|
|
gtk_container_add (GTK_CONTAINER (scroll), html);
|
|
gtk_widget_show (html);
|
|
|
|
html_view_set_document (HTML_VIEW (html), html_document_new ());
|
|
|
|
g_signal_connect (G_OBJECT (HTML_VIEW (html)->document), "title_changed",
|
|
G_CALLBACK (title_changed),
|
|
NULL);
|
|
g_signal_connect (G_OBJECT (HTML_VIEW (html)->document), "link_clicked",
|
|
G_CALLBACK (link_clicked),
|
|
NULL);
|
|
g_signal_connect (G_OBJECT (HTML_VIEW (html)->document), "request_url",
|
|
G_CALLBACK (request_url),
|
|
NULL);
|
|
|
|
gtk_widget_show (window);
|
|
|
|
current_ref = g_strconcat ("file://", help_path, "/", locale, "/", NULL);
|
|
|
|
load_page (help_file, TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
idle_load_page (gpointer data)
|
|
{
|
|
gchar *path = data;
|
|
|
|
load_page (path, TRUE);
|
|
g_free (path);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
run_temp_proc (gchar *name,
|
|
gint nparams,
|
|
GimpParam *param,
|
|
gint *nreturn_vals,
|
|
GimpParam **return_vals)
|
|
{
|
|
static GimpParam values[1];
|
|
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
|
gchar *help_path;
|
|
gchar *locale;
|
|
gchar *help_file;
|
|
gchar *path;
|
|
|
|
/* set default values */
|
|
help_path = g_strdup (gimp_help_root);
|
|
locale = g_strdup ("C");
|
|
help_file = g_strdup ("introduction.html");
|
|
|
|
/* Make sure all the arguments are there! */
|
|
if (nparams == 3)
|
|
{
|
|
if (param[0].data.d_string && strlen (param[0].data.d_string))
|
|
{
|
|
g_free (help_path);
|
|
help_path = g_strdup (param[0].data.d_string);
|
|
g_strdelimit (help_path, "/", G_DIR_SEPARATOR);
|
|
}
|
|
if (param[1].data.d_string && strlen (param[1].data.d_string))
|
|
{
|
|
g_free (locale);
|
|
locale = g_strdup (param[1].data.d_string);
|
|
}
|
|
if (param[2].data.d_string && strlen (param[2].data.d_string))
|
|
{
|
|
g_free (help_file);
|
|
help_file = g_strdup (param[2].data.d_string);
|
|
g_strdelimit (help_file, "/", G_DIR_SEPARATOR);
|
|
}
|
|
}
|
|
|
|
path = g_build_filename (help_path, locale, help_file, NULL);
|
|
|
|
g_free (help_path);
|
|
g_free (locale);
|
|
g_free (help_file);
|
|
|
|
g_idle_add (idle_load_page, path); /* frees path */
|
|
|
|
*nreturn_vals = 1;
|
|
*return_vals = values;
|
|
|
|
values[0].type = GIMP_PDB_STATUS;
|
|
values[0].data.d_status = status;
|
|
}
|
|
|
|
/* from libgimp/gimp.c */
|
|
void
|
|
gimp_run_temp (void);
|
|
|
|
static gboolean
|
|
input_callback (GIOChannel *channel,
|
|
GIOCondition condition,
|
|
gpointer data)
|
|
{
|
|
/* We have some data in the wire - read it */
|
|
/* The below will only ever run a single proc */
|
|
gimp_run_temp ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
install_temp_proc (void)
|
|
{
|
|
static GimpParamDef args[] =
|
|
{
|
|
{ GIMP_PDB_STRING, "help_path", "" },
|
|
{ GIMP_PDB_STRING, "locale", "Langusge to use" },
|
|
{ GIMP_PDB_STRING, "help_file", "Path of a local document to open. "
|
|
"Can be relative to GIMP_HELP_PATH." }
|
|
};
|
|
|
|
gimp_install_temp_proc (GIMP_HELP_TEMP_EXT_NAME,
|
|
"DON'T USE THIS ONE",
|
|
"(Temporary procedure)",
|
|
"Sven Neumann <sven@gimp.org>, "
|
|
"Michael Natterer <mitch@gimp.org>",
|
|
"Sven Neumann & Michael Natterer",
|
|
"1999-2002",
|
|
NULL,
|
|
"",
|
|
GIMP_TEMPORARY,
|
|
G_N_ELEMENTS (args), 0,
|
|
args, NULL,
|
|
run_temp_proc);
|
|
|
|
/* Tie into the gdk input function */
|
|
g_io_add_watch (_readchannel, G_IO_IN | G_IO_PRI, input_callback, NULL);
|
|
|
|
temp_proc_installed = TRUE;
|
|
}
|
|
|
|
static void
|
|
open_url (const gchar *help_path,
|
|
const gchar *locale,
|
|
const gchar *help_file)
|
|
{
|
|
open_browser_dialog (help_path, locale, help_file);
|
|
|
|
install_temp_proc ();
|
|
gtk_main ();
|
|
}
|
|
|
|
|
|
MAIN ()
|
|
|
|
static void
|
|
query (void)
|
|
{
|
|
static GimpParamDef args[] =
|
|
{
|
|
{ GIMP_PDB_INT32, "run_mode", "Interactive" },
|
|
{ GIMP_PDB_STRING, "help_path", "" },
|
|
{ GIMP_PDB_STRING, "locale", "Language to use" },
|
|
{ GIMP_PDB_STRING, "help_file", "Path of a local document to open. "
|
|
"Can be relative to GIMP_HELP_PATH." }
|
|
};
|
|
|
|
gimp_install_procedure (GIMP_HELP_EXT_NAME,
|
|
"Browse the GIMP help pages",
|
|
"A small and simple HTML browser optimized for "
|
|
"browsing the GIMP help pages.",
|
|
"Sven Neumann <sven@gimp.org>, "
|
|
"Michael Natterer <mitch@gimp.org>",
|
|
"Sven Neumann & Michael Natterer",
|
|
"1999-2002",
|
|
NULL,
|
|
"",
|
|
GIMP_EXTENSION,
|
|
G_N_ELEMENTS (args), 0,
|
|
args, NULL);
|
|
}
|
|
|
|
static void
|
|
run (gchar *name,
|
|
gint nparams,
|
|
GimpParam *param,
|
|
gint *nreturn_vals,
|
|
GimpParam **return_vals)
|
|
{
|
|
static GimpParam values[1];
|
|
GimpRunMode run_mode;
|
|
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
|
const gchar *env_root_dir = NULL;
|
|
gchar *help_path = NULL;
|
|
gchar *locale = NULL;
|
|
gchar *help_file = NULL;
|
|
|
|
run_mode = param[0].data.d_int32;
|
|
|
|
values[0].type = GIMP_PDB_STATUS;
|
|
values[0].data.d_status = status;
|
|
|
|
*nreturn_vals = 1;
|
|
*return_vals = values;
|
|
|
|
INIT_I18N_UI ();
|
|
|
|
if (strcmp (name, GIMP_HELP_EXT_NAME) == 0)
|
|
{
|
|
switch (run_mode)
|
|
{
|
|
case GIMP_RUN_INTERACTIVE:
|
|
case GIMP_RUN_NONINTERACTIVE:
|
|
case GIMP_RUN_WITH_LAST_VALS:
|
|
/* set default values */
|
|
env_root_dir = g_getenv ("GIMP_HELP_ROOT");
|
|
|
|
if (env_root_dir)
|
|
{
|
|
if (!g_file_test (env_root_dir, G_FILE_TEST_IS_DIR))
|
|
{
|
|
g_message (_("GIMP Help Browser Error.\n\n"
|
|
"Couldn't find GIMP_HELP_ROOT html directory.\n"
|
|
"(%s)"), env_root_dir);
|
|
|
|
status = GIMP_PDB_EXECUTION_ERROR;
|
|
break;
|
|
}
|
|
|
|
gimp_help_root = g_strdup (env_root_dir);
|
|
}
|
|
else
|
|
{
|
|
gimp_help_root = g_build_filename (gimp_data_directory(),
|
|
GIMP_HELP_PREFIX, NULL);
|
|
}
|
|
|
|
help_path = g_strdup (gimp_help_root);
|
|
locale = g_strdup ("C");
|
|
help_file = g_strdup ("introduction.html");
|
|
|
|
/* Make sure all the arguments are there! */
|
|
if (nparams == 4)
|
|
{
|
|
if (param[1].data.d_string && strlen (param[1].data.d_string))
|
|
{
|
|
g_free (help_path);
|
|
help_path = g_strdup (param[1].data.d_string);
|
|
}
|
|
if (param[2].data.d_string && strlen (param[2].data.d_string))
|
|
{
|
|
g_free (locale);
|
|
locale = g_strdup (param[2].data.d_string);
|
|
}
|
|
if (param[3].data.d_string && strlen (param[3].data.d_string))
|
|
{
|
|
g_free (help_file);
|
|
help_file = g_strdup (param[3].data.d_string);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
status = GIMP_PDB_CALLING_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (status == GIMP_PDB_SUCCESS)
|
|
{
|
|
open_url (help_path, locale, help_file);
|
|
|
|
g_free (help_path);
|
|
g_free (locale);
|
|
g_free (help_file);
|
|
}
|
|
|
|
values[0].data.d_status = status;
|
|
}
|
|
else
|
|
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
|
|
}
|