
2002-09-26 Michael Natterer <mitch@gimp.org> Made F1 help work again: * libgimpwidgets/gimphelpui.c: add the "F1" binding entries to the correct class, use GtkWidget's "show_help" signal correctly, cleanup, replaced deprecated stuff (obviously this file was make-it-compile ported in the first place :) * plug-ins/helpbrowser/helpbrowser.c: close the HtmlStream when there is no more data to read (avoids implicit calling of the stream's cancel function which fails because we didn't set it). Minor cleanup.
960 lines
24 KiB
C
960 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
|
|
{
|
|
html_stream_close (stream);
|
|
g_io_channel_unref (io);
|
|
|
|
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", "Language 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;
|
|
}
|