[geany/geany-plugins] ce45ff: Devhelp: Several fixes

Frank Lanitz git-noreply at xxxxx
Wed Mar 28 19:32:09 UTC 2012


Branch:      refs/heads/master
Author:      Frank Lanitz <frank at frank.uvena.de>
Committer:   Frank Lanitz <frank at frank.uvena.de>
Date:        Wed, 28 Mar 2012 19:32:09
Commit:      ce45ffcfc19f68caa1f38d142962a594e417a8eb
             https://github.com/geany/geany-plugins/commit/ce45ffcfc19f68caa1f38d142962a594e417a8eb

Log Message:
-----------
Devhelp: Several fixes


Modified Paths:
--------------
    build/devhelp.m4
    devhelp/Makefile.am
    devhelp/devhelp/Makefile.am
    devhelp/devhelp/dh-assistant-view.c
    devhelp/devhelp/dh-assistant-view.h
    devhelp/devhelp/dh-assistant.c
    devhelp/devhelp/dh-assistant.h
    devhelp/devhelp/dh-base.c
    devhelp/devhelp/dh-base.h
    devhelp/devhelp/dh-book-manager.c
    devhelp/devhelp/dh-book-manager.h
    devhelp/devhelp/dh-book-tree.c
    devhelp/devhelp/dh-book-tree.h
    devhelp/devhelp/dh-book.c
    devhelp/devhelp/dh-book.h
    devhelp/devhelp/dh-enum-types.c
    devhelp/devhelp/dh-enum-types.c.template
    devhelp/devhelp/dh-enum-types.h
    devhelp/devhelp/dh-enum-types.h.template
    devhelp/devhelp/dh-error.c
    devhelp/devhelp/dh-error.h
    devhelp/devhelp/dh-keyword-model.c
    devhelp/devhelp/dh-keyword-model.h
    devhelp/devhelp/dh-link.c
    devhelp/devhelp/dh-link.h
    devhelp/devhelp/dh-marshal.c
    devhelp/devhelp/dh-marshal.h
    devhelp/devhelp/dh-marshal.list
    devhelp/devhelp/dh-parser.c
    devhelp/devhelp/dh-parser.h
    devhelp/devhelp/dh-preferences.c
    devhelp/devhelp/dh-preferences.h
    devhelp/devhelp/dh-search.c
    devhelp/devhelp/dh-search.h
    devhelp/devhelp/dh-util.c
    devhelp/devhelp/dh-util.h
    devhelp/devhelp/dh-window.c
    devhelp/devhelp/dh-window.h
    devhelp/devhelp/eggfindbar.c
    devhelp/devhelp/eggfindbar.h
    devhelp/devhelp/ige-conf-gconf.c
    devhelp/devhelp/ige-conf-mac.c
    devhelp/devhelp/ige-conf-private.h
    devhelp/devhelp/ige-conf.c
    devhelp/devhelp/ige-conf.h
    devhelp/src/Makefile.am
    devhelp/src/dhp-codesearch.c
    devhelp/src/dhp-manpages.c
    devhelp/src/dhp-object.c
    devhelp/src/dhp-plugin.c
    devhelp/src/dhp.h
    devhelp/wscript_build
    devhelp/wscript_configure

Modified: build/devhelp.m4
25 files changed, 10 insertions(+), 15 deletions(-)
===================================================================
@@ -3,31 +3,26 @@ AC_DEFUN([GP_CHECK_DEVHELP],
     GP_ARG_DISABLE([devhelp], [auto])
 
     GTK_VERSION=2.16
-    GLIB_VERSION=2.16
-    WEBKIT_VERSION=1.1.18
-    DEVHELP1_VERSION=2.30.1
-    DEVHELP2_VERSION=2.32.0
+    WEBKIT_VERSION=1.1.13
+    GCONF_VERSION=2.6.0
+    LIBWNCK_VERSION=2.10.0
 
-    # Use newer libdevhelp-2.0 if present, and fallback on older libdevhelp-1.0
-    libdevhelp_pkg=libdevhelp-2.0
-    libdevhelp_version=${DEVHELP2_VERSION}
-    AS_IF([test "x$enable_devhelp" != "xno"],
-          [PKG_CHECK_EXISTS([${libdevhelp_pkg} >= ${libdevhelp_version}],
-                            [AC_DEFINE([HAVE_BOOK_MANAGER], [1], [Use libdevhelp-2.0])],
-                            [libdevhelp_pkg=libdevhelp-1.0
-                             libdevhelp_version=${DEVHELP1_VERSION}])])
+    AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
+    AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums)
 
     GP_CHECK_PLUGIN_DEPS([devhelp], [DEVHELP],
                          [gtk+-2.0 >= ${GTK_VERSION}
-                          glib-2.0 >= ${GLIB_VERSION}
                           webkit-1.0 >= ${WEBKIT_VERSION}
-                          ${libdevhelp_pkg} >= ${libdevhelp_version}
-                          gthread-2.0])
+                          libwnck-1.0 >= ${LIBWNCK_VERSION}
+                          gconf-2.0 >= ${GCONF_VERSION}
+                          gthread-2.0
+                          zlib])
 
     GP_STATUS_PLUGIN_ADD([DevHelp], [$enable_devhelp])
 
     AC_CONFIG_FILES([
         devhelp/Makefile
+        devhelp/devhelp/Makefile
         devhelp/src/Makefile
         devhelp/data/Makefile
     ])


Modified: devhelp/Makefile.am
2 files changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -4,5 +4,5 @@ else
 include $(top_srcdir)/build/vars.docs.mk
 endif
 
-SUBDIRS = src data
+SUBDIRS = devhelp src data
 plugin = devhelp


Modified: devhelp/devhelp/Makefile.am
84 files changed, 84 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,84 @@
+dh_headers = \
+	dh-assistant.h \
+	dh-assistant-view.h \
+	dh-base.h \
+	dh-book-manager.h \
+	dh-book.h \
+	dh-book-tree.h \
+	dh-error.h \
+	dh-keyword-model.h \
+	dh-link.h \
+	dh-search.h \
+	dh-window.h
+
+dh-enum-types.h: dh-enum-types.h.template $(dh_headers) $(GLIB_MKENUMS)
+	$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template dh-enum-types.h.template $(dh_headers)) > $@
+
+dh-enum-types.c: dh-enum-types.c.template $(dh_headers) $(GLIB_MKENUMS)
+	$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template dh-enum-types.c.template $(dh_headers)) > $@
+
+BUILT_SOURCES = \
+	dh-marshal.h \
+	dh-marshal.c \
+	dh-enum-types.h \
+	dh-enum-types.c
+
+EXTRA_DIST = \
+	dh-marshal.list \
+	dh-enum-types.c.template \
+	dh-enum-types.h.template
+
+noinst_LTLIBRARIES = libdevhelp-2.la
+
+libdevhelp_2_la_SOURCES = \
+	dh-assistant.c \
+	dh-assistant-view.c \
+	dh-base.c \
+	dh-book.c \
+	dh-book-manager.c \
+	dh-book-tree.c \
+	dh-enum-types.c \
+	dh-enum-types.h \
+	dh-error.c \
+	dh-keyword-model.c \
+	dh-link.c \
+	dh-marshal.c \
+	dh-marshal.h \
+	dh-parser.c \
+	dh-parser.h \
+	dh-preferences.c \
+	dh-preferences.h \
+	dh-search.c \
+	dh-util.c \
+	dh-util.h \
+	dh-window.c \
+	eggfindbar.c \
+	eggfindbar.h \
+	ige-conf.c \
+	ige-conf-gconf.c \
+	ige-conf.h \
+	ige-conf-private.h \
+	$(dh_headers)
+
+libdevhelp_2_la_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-DLOCALEDIR=\""$(datadir)/locale"\" \
+	-DDATADIR=\""$(datadir)"\" \
+	-DG_LOG_DOMAIN=\"Devhelp\" \
+	$(DEVHELP_CPPFLAGS)
+
+libdevhelp_2_la_CFLAGS = \
+	$(DEVHELP_CFLAGS)
+
+libdevhelp_2_la_LIBADD = \
+	$(DEVHELP_LIBS)
+
+libdevhelp_2_la_LDFLAGS = \
+	-no-undefined
+
+dh-marshal.h: dh-marshal.list
+	$(AM_V_GEN) $(GLIB_GENMARSHAL) $< --header --prefix=_dh_marshal dh-marshal.list > $@
+
+dh-marshal.c: dh-marshal.list
+	$(AM_V_GEN) echo "#include \"dh-marshal.h\"" > $@ && \
+	$(GLIB_GENMARSHAL) $< --body --prefix=_dh_marshal dh-marshal.list >> $@


Modified: devhelp/devhelp/dh-assistant-view.c
465 files changed, 465 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,465 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ * Copyright (C) 2008 Sven Herzberg
+ *
+ * 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 <glib/gi18n-lib.h>
+#include <webkit/webkit.h>
+#include "dh-assistant-view.h"
+#include "dh-link.h"
+#include "dh-util.h"
+#include "dh-book-manager.h"
+#include "dh-book.h"
+#include "dh-window.h"
+
+typedef struct {
+        DhBase   *base;
+        DhLink   *link;
+        gchar    *current_search;
+        gboolean  snippet_loaded;
+} DhAssistantViewPriv;
+
+G_DEFINE_TYPE (DhAssistantView, dh_assistant_view, WEBKIT_TYPE_WEB_VIEW);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, DH_TYPE_ASSISTANT_VIEW, DhAssistantViewPriv)
+
+static void
+view_finalize (GObject *object)
+{
+        DhAssistantViewPriv *priv = GET_PRIVATE (object);
+
+        if (priv->link) {
+                g_object_unref (priv->link);
+        }
+
+        if (priv->base) {
+                g_object_unref (priv->base);
+        }
+
+        g_free (priv->current_search);
+
+        G_OBJECT_CLASS (dh_assistant_view_parent_class)->finalize (object);
+}
+
+static WebKitNavigationResponse
+assistant_navigation_requested (WebKitWebView        *web_view,
+                                WebKitWebFrame       *frame,
+                                WebKitNetworkRequest *request)
+{
+        DhAssistantViewPriv *priv;
+        const gchar         *uri;
+
+        priv = GET_PRIVATE (web_view);
+
+        uri = webkit_network_request_get_uri (request);
+        if (strcmp (uri, "about:blank") == 0) {
+                return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
+        }
+        else if (! priv->snippet_loaded) {
+                priv->snippet_loaded = TRUE;
+                return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
+        }
+        else if (g_str_has_prefix (uri, "file://")) {
+                GtkWidget *window;
+
+                window = dh_base_get_window (priv->base);
+                _dh_window_display_uri (DH_WINDOW (window), uri);
+        }
+
+        return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
+}
+
+static gboolean
+assistant_button_press_event (GtkWidget      *widget,
+                              GdkEventButton *event)
+{
+        /* Block webkit's builtin context menu. */
+        if (event->button != 1) {
+                return TRUE;
+        }
+
+        return GTK_WIDGET_CLASS (dh_assistant_view_parent_class)->button_press_event (widget, event);
+}
+
+static void
+dh_assistant_view_class_init (DhAssistantViewClass* klass)
+{
+        GObjectClass       *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass     *widget_class = GTK_WIDGET_CLASS (klass);
+        WebKitWebViewClass *web_view_class = WEBKIT_WEB_VIEW_CLASS (klass);
+
+        object_class->finalize = view_finalize;
+
+        widget_class->button_press_event = assistant_button_press_event;
+
+        web_view_class->navigation_requested = assistant_navigation_requested;
+
+        g_type_class_add_private (klass, sizeof (DhAssistantViewPriv));
+}
+
+static void
+dh_assistant_view_init (DhAssistantView *view)
+{
+}
+
+DhBase*
+dh_assistant_view_get_base (DhAssistantView *view)
+{
+        DhAssistantViewPriv *priv;
+
+        g_return_val_if_fail (DH_IS_ASSISTANT_VIEW (view), NULL);
+
+        priv = GET_PRIVATE (view);
+
+        return priv->base;
+}
+
+GtkWidget*
+dh_assistant_view_new (void)
+{
+        return g_object_new (DH_TYPE_ASSISTANT_VIEW, NULL);
+}
+
+static const gchar *
+find_in_buffer (const gchar *buffer,
+                const gchar *key,
+                gsize        length,
+                gsize        key_length)
+{
+        gsize m = 0;
+        gsize i = 0;
+
+        while (i < length) {
+                if (key[m] == buffer[i]) {
+                        m++;
+                        if (m == key_length) {
+                                return buffer + i - m + 1;
+                        }
+                } else {
+                        m = 0;
+                }
+                i++;
+        }
+
+        return NULL;
+}
+
+/**
+ * dh_assistant_view_set_link:
+ * @view: an devhelp assistant view
+ * @link: the #DhLink
+ *
+ * Open @link in the assistant view, if %NULL the view will be blanked.
+ *
+ * Return value: %TRUE if the requested link is open, %FALSE otherwise.
+ **/
+gboolean
+dh_assistant_view_set_link (DhAssistantView *view,
+                            DhLink          *link)
+{
+        DhAssistantViewPriv *priv;
+        gchar               *uri;
+        const gchar         *anchor;
+        gchar               *filename;
+        GMappedFile         *file;
+        const gchar         *contents;
+        gsize                length;
+        gchar               *key;
+        gsize                key_length;
+        gsize                offset = 0;
+        const gchar         *start;
+        const gchar         *end;
+
+        g_return_val_if_fail (DH_IS_ASSISTANT_VIEW (view), FALSE);
+
+        priv = GET_PRIVATE (view);
+
+        if (priv->link == link) {
+                return TRUE;
+        }
+
+        if (priv->link) {
+                dh_link_unref (priv->link);
+                priv->link = NULL;
+        }
+
+        if (link) {
+                link = dh_link_ref (link);
+        } else {
+                webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), "about:blank");
+                return TRUE;
+        }
+
+        uri = dh_link_get_uri (link);
+        anchor = strrchr (uri, '#');
+        if (anchor) {
+                filename = g_strndup (uri, anchor - uri);
+                anchor++;
+                g_free (uri);
+        } else {
+                g_free (uri);
+                return FALSE;
+        }
+
+        if (g_str_has_prefix (filename, "file://"))
+            offset = 7;
+
+        file = g_mapped_file_new (filename + offset, FALSE, NULL);
+        if (!file) {
+                g_free (filename);
+                return FALSE;
+        }
+
+        contents = g_mapped_file_get_contents (file);
+        length = g_mapped_file_get_length (file);
+
+        key = g_strdup_printf ("<a name=\"%s\"", anchor);
+        key_length = strlen (key);
+
+        start = find_in_buffer (contents, key, length, key_length);
+        g_free (key);
+
+        end = NULL;
+
+        if (start) {
+                const gchar *start_key;
+                const gchar *end_key;
+
+                length -= start - contents;
+
+                start_key = "<pre class=\"programlisting\">";
+
+                start = find_in_buffer (start,
+                                        start_key,
+                                        length,
+                                        strlen (start_key));
+
+                end_key = "<div class=\"refsect";
+
+                if (start) {
+                        end = find_in_buffer (start, end_key,
+                                              length - strlen (start_key),
+                                              strlen (end_key));
+                        if (!end) {
+                                end_key = "<div class=\"footer";
+                                end = find_in_buffer (start, end_key,
+                                                      length - strlen (start_key),
+                                                      strlen (end_key));
+                        }
+                }
+        }
+
+        if (start && end) {
+                gchar       *buf;
+                gboolean     break_line;
+                const gchar *function;
+                gchar       *stylesheet;
+                gchar       *javascript;
+                gchar       *html;
+
+                buf = g_strndup (start, end-start);
+
+                /* Try to reformat function signatures so they take less
+                 * space and look nicer. Don't reformat things that don't
+                 * look like functions.
+                 */
+                switch (dh_link_get_link_type (link)) {
+                case DH_LINK_TYPE_FUNCTION:
+                        break_line = TRUE;
+                        function = "onload=\"reformatSignature()\"";
+                        break;
+                case DH_LINK_TYPE_MACRO:
+                        break_line = TRUE;
+                        function = "onload=\"cleanupSignature()\"";
+                        break;
+                default:
+                        break_line = FALSE;
+                        function = "";
+                        break;
+                }
+
+                if (break_line) {
+                        gchar *name;
+
+                        name = strstr (buf, dh_link_get_name (link));
+                        if (name && name > buf) {
+                                name[-1] = '\n';
+                        }
+                }
+
+                stylesheet = dh_util_build_data_filename ("devhelp",
+                                                          "assistant",
+                                                          "assistant.css",
+                                                          NULL);
+                javascript = dh_util_build_data_filename ("devhelp",
+                                                          "assistant",
+                                                          "assistant.js",
+                                                          NULL);
+
+                html = g_strdup_printf (
+                        "<html>"
+                        "<head>"
+                        "<link rel=\"stylesheet\" type=\"text/css\" href=\"file://%s\"/>"
+                        "<script src=\"file://%s\"></script>"
+                        "</head>"
+                        "<body %s>"
+                        "<div class=\"title\">%s: <a href=\"%s\">%s</a></div>"
+                        "<div class=\"subtitle\">%s %s</div>"
+                        "<div class=\"content\">%s</div>"
+                        "</body>"
+                        "</html>",
+                        stylesheet,
+                        javascript,
+                        function,
+                        dh_link_get_type_as_string (link),
+                        dh_link_get_uri (link),
+                        dh_link_get_name (link),
+                        _("Book:"),
+                        dh_link_get_book_name (link),
+                        buf);
+                g_free (buf);
+
+                g_free (stylesheet);
+                g_free (javascript);
+
+                priv->snippet_loaded = FALSE;
+                webkit_web_view_load_string (
+                        WEBKIT_WEB_VIEW (view),
+                        html,
+                        "text/html",
+                        NULL,
+                        filename);
+
+                g_free (html);
+        } else {
+                webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), "about:blank");
+        }
+
+#if GLIB_CHECK_VERSION(2,21,3)
+        g_mapped_file_unref (file);
+#else
+        g_mapped_file_free (file);
+#endif
+
+        g_free (filename);
+
+        return TRUE;
+}
+
+gboolean
+dh_assistant_view_search (DhAssistantView *view,
+                          const gchar     *str)
+{
+        DhAssistantViewPriv *priv;
+        const gchar         *name;
+        DhLink              *link;
+        DhLink              *exact_link;
+        DhLink              *prefix_link;
+        DhBookManager       *book_manager;
+        GList               *books;
+
+        g_return_val_if_fail (DH_IS_ASSISTANT_VIEW (view), FALSE);
+        g_return_val_if_fail (str, FALSE);
+
+        priv = GET_PRIVATE (view);
+
+        /* Filter out very short strings. */
+        if (strlen (str) < 4) {
+                return FALSE;
+        }
+
+        if (priv->current_search && strcmp (priv->current_search, str) == 0) {
+                return FALSE;
+        }
+        g_free (priv->current_search);
+        priv->current_search = g_strdup (str);
+
+        book_manager = dh_base_get_book_manager (dh_assistant_view_get_base (view));
+
+        prefix_link = NULL;
+        exact_link = NULL;
+
+        for (books = dh_book_manager_get_books (book_manager);
+             !exact_link && books;
+             books = g_list_next (books)) {
+                GList *l;
+
+                for (l = dh_book_get_keywords (DH_BOOK (books->data));
+                     l && exact_link == NULL;
+                     l = l->next) {
+                        DhLinkType type;
+
+                        link = l->data;
+
+                        type = dh_link_get_link_type (link);
+
+                        if (type == DH_LINK_TYPE_BOOK ||
+                            type == DH_LINK_TYPE_PAGE ||
+                            type == DH_LINK_TYPE_KEYWORD) {
+                                continue;
+                        }
+
+                        name = dh_link_get_name (link);
+                        if (strcmp (name, str) == 0) {
+                                exact_link = link;
+                        }
+                        else if (g_str_has_prefix (name, str)) {
+                                /* Prefer shorter prefix matches. */
+                                if (!prefix_link) {
+                                        prefix_link = link;
+                                }
+                                else if (strlen (dh_link_get_name (prefix_link)) > strlen (name)) {
+                                        prefix_link = link;
+                                }
+                        }
+                }
+        }
+
+        if (exact_link) {
+                /*g_print ("exact hit: '%s' '%s'\n", exact_link->name, str);*/
+                dh_assistant_view_set_link (view, exact_link);
+        }
+        else if (prefix_link) {
+                /*g_print ("prefix hit: '%s' '%s'\n", prefix_link->name, str);*/
+                dh_assistant_view_set_link (view, prefix_link);
+        } else {
+                /*g_print ("no hit\n");*/
+                /*assistant_view_set_link (view, NULL);*/
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+void
+dh_assistant_view_set_base (DhAssistantView *view,
+                            DhBase          *base)
+{
+        DhAssistantViewPriv *priv;
+
+        g_return_if_fail (DH_IS_ASSISTANT_VIEW (view));
+        g_return_if_fail (DH_IS_BASE (base));
+
+        priv = GET_PRIVATE (view);
+
+        priv->base = g_object_ref (base);
+}


Modified: devhelp/devhelp/dh-assistant-view.h
59 files changed, 59 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Sven Herzberg
+ *
+ * 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
+ */
+
+#ifndef __DH_ASSISTANT_VIEW_H__
+#define __DH_ASSISTANT_VIEW_H__
+
+#include <webkit/webkit.h>
+#include "dh-base.h"
+#include "dh-link.h"
+
+G_BEGIN_DECLS
+
+#define DH_TYPE_ASSISTANT_VIEW         (dh_assistant_view_get_type ())
+#define DH_ASSISTANT_VIEW(i)           (G_TYPE_CHECK_INSTANCE_CAST ((i), DH_TYPE_ASSISTANT_VIEW, DhAssistantView))
+#define DH_ASSISTANT_VIEW_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), DH_TYPE_ASSISTANT_VIEW, DhAssistantViewClass))
+#define DH_IS_ASSISTANT_VIEW(i)        (G_TYPE_CHECK_INSTANCE_TYPE ((i), DH_TYPE_ASSISTANT_VIEW))
+#define DH_IS_ASSISTANT_VIEW_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), DH_ASSISTANT_VIEW))
+#define DH_ASSISTANT_VIEW_GET_CLASS(i) (G_TYPE_INSTANCE_GET_CLASS ((i), DH_TYPE_ASSISTANT_VIEW, DhAssistantView))
+
+typedef struct _DhAssistantView      DhAssistantView;
+typedef struct _DhAssistantViewClass DhAssistantViewClass;
+
+struct _DhAssistantView {
+        WebKitWebView parent_instance;
+};
+
+struct _DhAssistantViewClass {
+        WebKitWebViewClass parent_class;
+};
+
+GType      dh_assistant_view_get_type (void) G_GNUC_CONST;
+GtkWidget* dh_assistant_view_new      (void);
+gboolean   dh_assistant_view_search   (DhAssistantView *view,
+                                       const gchar     *str);
+DhBase*    dh_assistant_view_get_base (DhAssistantView *view);
+void       dh_assistant_view_set_base (DhAssistantView *view,
+                                       DhBase          *base);
+gboolean   dh_assistant_view_set_link (DhAssistantView *view,
+                                       DhLink          *link);
+G_END_DECLS
+
+#endif /* __DH_ASSISTANT_VIEW_H__ */


Modified: devhelp/devhelp/dh-assistant.c
130 files changed, 130 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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 <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include "dh-window.h"
+#include "dh-util.h"
+#include "dh-assistant-view.h"
+#include "dh-assistant.h"
+
+typedef struct {
+        GtkWidget *main_box;
+        GtkWidget *view;
+} DhAssistantPriv;
+
+static void dh_assistant_class_init (DhAssistantClass *klass);
+static void dh_assistant_init       (DhAssistant      *assistant);
+
+G_DEFINE_TYPE (DhAssistant, dh_assistant, GTK_TYPE_WINDOW);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, DH_TYPE_ASSISTANT, DhAssistantPriv)
+
+static gboolean
+assistant_key_press_event_cb (GtkWidget   *widget,
+                              GdkEventKey *event,
+                              DhAssistant *assistant)
+{
+        if (event->keyval == GDK_Escape) {
+                gtk_widget_destroy (GTK_WIDGET (assistant));
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static void
+dh_assistant_class_init (DhAssistantClass *klass)
+{
+        g_type_class_add_private (klass, sizeof (DhAssistantPriv));
+}
+
+static void
+dh_assistant_init (DhAssistant *assistant)
+{
+        DhAssistantPriv *priv = GET_PRIVATE (assistant);
+        GtkWidget       *scrolled_window;
+
+        priv->main_box = gtk_vbox_new (FALSE, 0);
+        gtk_widget_show (priv->main_box);
+        gtk_container_add (GTK_CONTAINER (assistant), priv->main_box);
+
+        /* i18n: Please don't translate "Devhelp". */
+        gtk_window_set_title (GTK_WINDOW (assistant), _("Devhelp — Assistant"));
+        gtk_window_set_icon_name (GTK_WINDOW (assistant), "devhelp");
+
+        priv->view = dh_assistant_view_new ();
+
+        g_signal_connect (assistant, "key-press-event",
+                          G_CALLBACK (assistant_key_press_event_cb),
+                          assistant);
+
+        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+        gtk_container_add (GTK_CONTAINER (scrolled_window), priv->view);
+
+        gtk_widget_show_all (scrolled_window);
+
+        gtk_box_pack_start (GTK_BOX (priv->main_box),
+                            scrolled_window, TRUE, TRUE, 0);
+
+        dh_util_state_manage_window (GTK_WINDOW (assistant),
+                                     "assistant/window");
+}
+
+GtkWidget *
+dh_assistant_new (DhBase *base)
+{
+        GtkWidget       *assistant;
+        DhAssistantPriv *priv;
+
+        assistant = g_object_new (DH_TYPE_ASSISTANT, NULL);
+
+        priv = GET_PRIVATE (assistant);
+
+        dh_assistant_view_set_base (DH_ASSISTANT_VIEW (priv->view), base);
+
+        return assistant;
+}
+
+gboolean
+dh_assistant_search (DhAssistant *assistant,
+                     const gchar *str)
+{
+        DhAssistantPriv *priv;
+
+        g_return_val_if_fail (DH_IS_ASSISTANT (assistant), FALSE);
+        g_return_val_if_fail (str != NULL, FALSE);
+
+        priv = GET_PRIVATE (assistant);
+
+        if (dh_assistant_view_search (DH_ASSISTANT_VIEW (priv->view), str)) {
+                gtk_widget_show (GTK_WIDGET (assistant));
+                return TRUE;
+        }
+
+        return FALSE;
+}


Modified: devhelp/devhelp/dh-assistant.h
54 files changed, 54 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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.
+ */
+
+#ifndef __DH_ASSISTANT_H__
+#define __DH_ASSISTANT_H__
+
+#include <gtk/gtk.h>
+#include "dh-base.h"
+
+G_BEGIN_DECLS
+
+#define DH_TYPE_ASSISTANT         (dh_assistant_get_type ())
+#define DH_ASSISTANT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DH_TYPE_ASSISTANT, DhAssistant))
+#define DH_ASSISTANT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), DH_TYPE_ASSISTANT, DhAssistantClass))
+#define DH_IS_ASSISTANT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DH_TYPE_ASSISTANT))
+#define DH_IS_ASSISTANT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DH_TYPE_ASSISTANT))
+#define DH_ASSISTANT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DH_TYPE_ASSISTANT, DhAssistantClass))
+
+typedef struct _DhAssistant      DhAssistant;
+typedef struct _DhAssistantClass DhAssistantClass;
+
+struct _DhAssistant {
+        GtkWindow parent_instance;
+};
+
+struct _DhAssistantClass {
+        GtkWindowClass parent_class;
+};
+
+GType      dh_assistant_get_type  (void) G_GNUC_CONST;
+GtkWidget *dh_assistant_new       (DhBase      *base);
+gboolean   dh_assistant_search    (DhAssistant *assistant,
+                                   const gchar *str);
+
+G_END_DECLS
+
+#endif /* __DH_ASSISTANT_H__ */


Modified: devhelp/devhelp/dh-base.c
307 files changed, 307 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,307 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2004-2008 Imendio AB
+ *
+ * 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 <gtk/gtk.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <unistd.h>
+#include <gdk/gdkx.h>
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include <libwnck/libwnck.h>
+#endif
+
+#include "dh-window.h"
+#include "dh-link.h"
+#include "dh-parser.h"
+#include "dh-preferences.h"
+#include "dh-assistant.h"
+#include "dh-util.h"
+#include "ige-conf.h"
+#include "dh-base.h"
+#include "dh-book-manager.h"
+
+typedef struct {
+        GSList        *windows;
+        GSList        *assistants;
+        DhBookManager *book_manager;
+} DhBasePriv;
+
+G_DEFINE_TYPE (DhBase, dh_base, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, DH_TYPE_BASE, DhBasePriv)
+
+static void dh_base_init           (DhBase      *base);
+static void dh_base_class_init     (DhBaseClass *klass);
+
+static DhBase *base_instance;
+
+static void
+base_finalize (GObject *object)
+{
+        G_OBJECT_CLASS (dh_base_parent_class)->finalize (object);
+}
+
+static void
+base_dispose (GObject *object)
+{
+        DhBasePriv *priv;
+
+        priv = GET_PRIVATE (object);
+
+        if (priv->book_manager) {
+                g_object_unref (priv->book_manager);
+                priv->book_manager = NULL;
+        }
+}
+
+
+static void
+dh_base_class_init (DhBaseClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = base_finalize;
+        object_class->dispose = base_dispose;
+
+	g_type_class_add_private (klass, sizeof (DhBasePriv));
+}
+
+static void
+dh_base_init (DhBase *base)
+{
+        DhBasePriv *priv = GET_PRIVATE (base);
+        IgeConf    *conf;
+        gchar      *path;
+
+        conf = ige_conf_get ();
+        path = dh_util_build_data_filename ("devhelp", "devhelp.defaults", NULL);
+        ige_conf_add_defaults (conf, path);
+        g_free (path);
+
+        priv->book_manager = dh_book_manager_new ();
+        dh_book_manager_populate (priv->book_manager);
+
+#ifdef GDK_WINDOWING_X11
+        {
+                gint n_screens, i;
+
+                /* For some reason, libwnck doesn't seem to update its list of
+                 * workspaces etc if we don't do this.
+                 */
+                n_screens = gdk_display_get_n_screens (gdk_display_get_default ());
+                for (i = 0; i < n_screens; i++)
+                        wnck_screen_get (i);
+        }
+#endif
+}
+
+static void
+base_window_or_assistant_finalized_cb (DhBase   *base,
+                                       gpointer  window_or_assistant)
+{
+        DhBasePriv *priv = GET_PRIVATE (base);
+
+        priv->windows = g_slist_remove (priv->windows, window_or_assistant);
+        priv->assistants = g_slist_remove (priv->assistants, window_or_assistant);
+
+        if (priv->windows == NULL && priv->assistants == NULL) {
+                gtk_main_quit ();
+        }
+}
+
+DhBase *
+dh_base_get (void)
+{
+        if (!base_instance) {
+                base_instance = g_object_new (DH_TYPE_BASE, NULL);
+        }
+
+        return base_instance;
+}
+
+DhBase *
+dh_base_new (void)
+{
+        if (base_instance) {
+                g_error ("You can only have one DhBase instance.");
+        }
+
+        return dh_base_get ();
+}
+
+GtkWidget *
+dh_base_new_window (DhBase *base)
+{
+        DhBasePriv *priv;
+        GtkWidget  *window;
+
+        g_return_val_if_fail (DH_IS_BASE (base), NULL);
+
+        priv = GET_PRIVATE (base);
+
+        window = dh_window_new (base);
+
+        priv->windows = g_slist_prepend (priv->windows, window);
+
+        g_object_weak_ref (G_OBJECT (window),
+                           (GWeakNotify) base_window_or_assistant_finalized_cb,
+                           base);
+
+        return window;
+}
+
+GtkWidget *
+dh_base_new_assistant (DhBase *base)
+{
+        DhBasePriv *priv;
+        GtkWidget  *assistant;
+
+        g_return_val_if_fail (DH_IS_BASE (base), NULL);
+
+        priv = GET_PRIVATE (base);
+
+        assistant = dh_assistant_new (base);
+
+        priv->assistants = g_slist_prepend (priv->assistants, assistant);
+
+        g_object_weak_ref (G_OBJECT (assistant),
+                           (GWeakNotify) base_window_or_assistant_finalized_cb,
+                           base);
+
+        return assistant;
+}
+
+DhBookManager *
+dh_base_get_book_manager (DhBase *base)
+{
+        DhBasePriv *priv;
+
+        g_return_val_if_fail (DH_IS_BASE (base), NULL);
+
+        priv = GET_PRIVATE (base);
+
+        return priv->book_manager;
+}
+
+GtkWidget *
+dh_base_get_window_on_current_workspace (DhBase *base)
+{
+        DhBasePriv *priv;
+
+        g_return_val_if_fail (DH_IS_BASE (base), NULL);
+
+        priv = GET_PRIVATE (base);
+
+        if (!priv->windows) {
+                return NULL;
+        }
+
+#ifdef GDK_WINDOWING_X11
+        {
+                WnckWorkspace *workspace;
+                WnckScreen    *screen;
+                GtkWidget     *window;
+                GList         *windows, *w;
+                GSList        *l;
+                gulong         xid;
+                pid_t          pid;
+
+                screen = wnck_screen_get (0);
+                if (!screen) {
+                        return NULL;
+                }
+
+                workspace = wnck_screen_get_active_workspace (screen);
+                if (!workspace) {
+                        return NULL;
+                }
+
+                xid = 0;
+                pid = getpid ();
+
+                /* Use _stacked so we can use the one on top. */
+                windows = wnck_screen_get_windows_stacked (screen);
+                windows = g_list_last (windows);
+
+                for (w = windows; w; w = w->prev) {
+                        if (wnck_window_is_on_workspace (w->data, workspace) &&
+                            wnck_window_get_pid (w->data) == pid) {
+                                xid = wnck_window_get_xid (w->data);
+                                break;
+                        }
+                }
+
+                if (!xid) {
+                        return NULL;
+                }
+
+                /* Return the first matching window we have. */
+                for (l = priv->windows; l; l = l->next) {
+                        window = l->data;
+
+#if GTK_CHECK_VERSION (2,14,0)
+                        if (GDK_WINDOW_XID (gtk_widget_get_window (window)) == xid) {
+#else
+                        if (GDK_WINDOW_XID (window->window) == xid) {
+#endif
+                                return window;
+                        }
+                }
+        }
+
+        return NULL;
+#else
+        return priv->windows->data;
+#endif
+}
+
+GtkWidget *
+dh_base_get_window (DhBase *base)
+{
+        GtkWidget *window;
+
+        g_return_val_if_fail (DH_IS_BASE (base), NULL);
+
+        window = dh_base_get_window_on_current_workspace (base);
+        if (!window) {
+                window = dh_base_new_window (base);
+                gtk_window_present (GTK_WINDOW (window));
+        }
+
+        return window;
+}
+
+void
+dh_base_quit (DhBase *base)
+{
+        DhBasePriv *priv = GET_PRIVATE (base);
+
+        /* Make sure all of the windows get a chance to release their resources
+         * properly.  As they get destroyed,
+         * base_window_or_assistant_finalized_cb() will be called, and when the
+         * last one is removed, we will quit */
+        g_slist_foreach (priv->windows, (GFunc)gtk_widget_destroy, NULL);
+        g_slist_foreach (priv->assistants, (GFunc)gtk_widget_destroy, NULL);
+}


Modified: devhelp/devhelp/dh-base.h
62 files changed, 62 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2005-2008 Imendio AB
+ *
+ * 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.
+ */
+
+#ifndef __DH_BASE_H__
+#define __DH_BASE_H__
+
+#include <gtk/gtk.h>
+
+#include "dh-book-manager.h"
+
+G_BEGIN_DECLS
+
+typedef struct _DhBase      DhBase;
+typedef struct _DhBaseClass DhBaseClass;
+
+#define DH_TYPE_BASE         (dh_base_get_type ())
+#define DH_BASE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DH_TYPE_BASE, DhBase))
+#define DH_BASE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), DH_TYPE_BASE, DhBaseClass))
+#define DH_IS_BASE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DH_TYPE_BASE))
+#define DH_IS_BASE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DH_TYPE_BASE))
+#define DH_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DH_TYPE_BASE, DhBaseClass))
+
+struct _DhBase {
+        GObject parent_instance;
+};
+
+struct _DhBaseClass {
+        GObjectClass parent_class;
+};
+
+GType          dh_base_get_type                        (void) G_GNUC_CONST;
+DhBase *       dh_base_get                             (void);
+DhBase *       dh_base_new                             (void);
+GtkWidget *    dh_base_new_window                      (DhBase *base);
+GtkWidget *    dh_base_new_assistant                   (DhBase *base);
+GtkWidget *    dh_base_get_window                      (DhBase *base);
+GtkWidget *    dh_base_get_window_on_current_workspace (DhBase *base);
+DhBookManager *dh_base_get_book_manager                (DhBase *base);
+void           dh_base_quit                            (DhBase *base);
+
+G_END_DECLS
+
+#endif /* __DH_BASE_H__ */


Modified: devhelp/devhelp/dh-book-manager.c
408 files changed, 408 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,408 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2004-2008 Imendio AB
+ * Copyright (C) 2010 Lanedo GmbH
+ *
+ * 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 "dh-link.h"
+#include "dh-util.h"
+#include "dh-book.h"
+#include "dh-book-manager.h"
+#include "dh-marshal.h"
+
+typedef struct {
+        /* The list of all DhBooks found in the system */
+        GList *books;
+} DhBookManagerPriv;
+
+enum {
+        DISABLED_BOOK_LIST_UPDATED,
+        LAST_SIGNAL
+};
+
+static gint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (DhBookManager, dh_book_manager, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE       \
+        (instance, DH_TYPE_BOOK_MANAGER, DhBookManagerPriv)
+
+static void    dh_book_manager_init       (DhBookManager      *book_manager);
+static void    dh_book_manager_class_init (DhBookManagerClass *klass);
+
+static void    book_manager_add_from_filepath     (DhBookManager *book_manager,
+                                                   const gchar   *book_path);
+static void    book_manager_add_from_dir          (DhBookManager *book_manager,
+                                                   const gchar   *dir_path);
+
+#ifdef GDK_WINDOWING_QUARTZ
+static void    book_manager_add_from_xcode_docset (DhBookManager *book_manager,
+                                                   const gchar   *dir_path);
+#endif
+
+static void
+book_manager_finalize (GObject *object)
+{
+        DhBookManagerPriv *priv;
+        GList             *l;
+
+        priv = GET_PRIVATE (object);
+
+        /* Destroy all books */
+        for (l = priv->books; l; l = g_list_next (l)) {
+                g_object_unref (l->data);
+        }
+        g_list_free (priv->books);
+
+        G_OBJECT_CLASS (dh_book_manager_parent_class)->finalize (object);
+}
+
+static void
+dh_book_manager_class_init (DhBookManagerClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = book_manager_finalize;
+
+        signals[DISABLED_BOOK_LIST_UPDATED] =
+                g_signal_new ("disabled-book-list-updated",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (DhBookManagerClass, disabled_book_list_updated),
+                              NULL, NULL,
+                              _dh_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
+
+	g_type_class_add_private (klass, sizeof (DhBookManagerPriv));
+}
+
+static void
+dh_book_manager_init (DhBookManager *book_manager)
+{
+        DhBookManagerPriv *priv = GET_PRIVATE (book_manager);
+
+        priv->books = NULL;
+}
+
+static void
+book_manager_clean_list_of_books_disabled (GSList *books_disabled)
+{
+        GSList *sl;
+
+        for (sl = books_disabled; sl; sl = g_slist_next (sl)) {
+                g_free (sl->data);
+        }
+        g_slist_free (sl);
+}
+
+static void
+book_manager_check_status_from_conf (DhBookManager *book_manager)
+{
+        GSList *books_disabled, *sl;
+
+        books_disabled = dh_util_state_load_books_disabled ();
+
+        for (sl = books_disabled; sl; sl = g_slist_next (sl)) {
+                DhBook *book;
+
+                book = dh_book_manager_get_book_by_name (book_manager,
+                                                         (const gchar *)sl->data);
+                if (book) {
+                        dh_book_set_enabled (book, FALSE);
+                }
+        }
+
+        book_manager_clean_list_of_books_disabled (books_disabled);
+}
+
+static void
+book_manager_add_books_in_data_dir (DhBookManager *book_manager,
+                                    const gchar   *data_dir)
+{
+        gchar *dir;
+
+        dir = g_build_filename (data_dir, "gtk-doc", "html", NULL);
+        book_manager_add_from_dir (book_manager, dir);
+        g_free (dir);
+
+        dir = g_build_filename (data_dir, "devhelp", "books", NULL);
+        book_manager_add_from_dir (book_manager, dir);
+        g_free (dir);
+}
+
+void
+dh_book_manager_populate (DhBookManager *book_manager)
+{
+        const gchar * const * system_dirs;
+
+        book_manager_add_books_in_data_dir (book_manager,
+                                            g_get_user_data_dir ());
+
+        system_dirs = g_get_system_data_dirs ();
+        while (*system_dirs) {
+                book_manager_add_books_in_data_dir (book_manager,
+                                                    *system_dirs);
+                system_dirs++;
+        }
+
+#ifdef GDK_WINDOWING_QUARTZ
+        book_manager_add_from_xcode_docset (
+                book_manager,
+                "/Library/Developer/Shared/Documentation/DocSets");
+#endif
+
+        /* Once all books are loaded, check enabled status from conf */
+        book_manager_check_status_from_conf (book_manager);
+}
+
+static gchar *
+book_manager_get_book_path (const gchar *base_path,
+                            const gchar *name)
+{
+        static const gchar *suffixes[] = {
+                "devhelp2",
+                "devhelp2.gz",
+                "devhelp",
+                "devhelp.gz",
+                NULL
+        };
+        gchar *tmp;
+        gchar *book_path;
+        guint  i;
+
+        for (i = 0; suffixes[i]; i++) {
+                tmp = g_build_filename (base_path, name, name, NULL);
+                book_path = g_strconcat (tmp, ".", suffixes[i], NULL);
+                g_free (tmp);
+
+                if (g_file_test (book_path, G_FILE_TEST_EXISTS)) {
+                        return book_path;;
+                }
+                g_free (book_path);
+        }
+        return NULL;
+}
+
+static void
+book_manager_add_from_dir (DhBookManager *book_manager,
+                           const gchar   *dir_path)
+{
+        GDir        *dir;
+        const gchar *name;
+
+        g_return_if_fail (book_manager);
+        g_return_if_fail (dir_path);
+
+        /* Open directory */
+        dir = g_dir_open (dir_path, 0, NULL);
+        if (!dir) {
+                return;
+        }
+
+        /* And iterate it */
+        while ((name = g_dir_read_name (dir)) != NULL) {
+                gchar *book_path;
+
+                book_path = book_manager_get_book_path (dir_path, name);
+                if (book_path) {
+                        /* Add book from filepath */
+                        book_manager_add_from_filepath (book_manager,
+                                                        book_path);
+                        g_free (book_path);
+                }
+        }
+
+        g_dir_close (dir);
+}
+
+#ifdef GDK_WINDOWING_QUARTZ
+static gboolean
+seems_docset_dir (const gchar *path)
+{
+        gchar    *tmp;
+        gboolean  seems_like_devhelp = FALSE;
+
+        g_return_val_if_fail (path, FALSE);
+
+        /* Do some sanity checking on the directory first so we don't have
+         * to go through several hundreds of files in every docset.
+         */
+        tmp = g_build_filename (path, "style.css", NULL);
+        if (g_file_test (tmp, G_FILE_TEST_EXISTS)) {
+                gchar *tmp;
+
+                tmp = g_build_filename (path, "index.sgml", NULL);
+                if (g_file_test (tmp, G_FILE_TEST_EXISTS)) {
+                        seems_like_devhelp = TRUE;
+                }
+                g_free (tmp);
+        }
+        g_free (tmp);
+
+        return seems_like_devhelp;
+}
+
+static void
+book_manager_add_from_xcode_docset (DhBookManager *book_manager,
+                                    const gchar   *dir_path)
+{
+        GDir        *dir;
+        const gchar *name;
+
+        g_return_if_fail (book_manager);
+        g_return_if_fail (dir_path);
+
+        if (!seems_docset_dir (dir_path)) {
+                return;
+        }
+
+        /* Open directory */
+        dir = g_dir_open (dir_path, 0, NULL);
+        if (!dir) {
+                return;
+        }
+
+        /* And iterate it, looking for files ending with .devhelp2 */
+        while ((name = g_dir_read_name (dir)) != NULL) {
+                if (g_strcmp0 (strrchr (name, '.'),
+                               ".devhelp2") == 0) {
+                        gchar *book_path;
+
+                        book_path = g_build_filename (path, name, NULL);
+                        /* Add book from filepath */
+                        book_manager_add_from_filepath (book_manager,
+                                                        book_path);
+                        g_free (book_path);
+                }
+        }
+
+        g_dir_close (dir);
+}
+#endif
+
+static void
+book_manager_add_from_filepath (DhBookManager *book_manager,
+                                const gchar   *book_path)
+{
+        DhBookManagerPriv *priv;
+        DhBook            *book;
+
+        g_return_if_fail (book_manager);
+        g_return_if_fail (book_path);
+
+        priv = GET_PRIVATE (book_manager);
+
+        /* Allocate new book struct */
+        book = dh_book_new (book_path);
+
+        /* Check if book with same path was already loaded in the manager */
+        if (g_list_find_custom (priv->books,
+                                book,
+                                (GCompareFunc)dh_book_cmp_by_path)) {
+                g_object_unref (book);
+                return;
+        }
+
+        /* Check if book with same bookname was already loaded in the manager
+         * (we need to force unique book names) */
+        if (g_list_find_custom (priv->books,
+                                book,
+                                (GCompareFunc)dh_book_cmp_by_name)) {
+                g_object_unref (book);
+                return;
+        }
+
+        /* Add the book to the book list */
+        priv->books = g_list_insert_sorted (priv->books,
+                                            book,
+                                            (GCompareFunc)dh_book_cmp_by_title);
+}
+
+GList *
+dh_book_manager_get_books (DhBookManager *book_manager)
+{
+        g_return_val_if_fail (book_manager, NULL);
+
+        return GET_PRIVATE (book_manager)->books;
+}
+
+DhBook *
+dh_book_manager_get_book_by_name (DhBookManager *book_manager,
+                                  const gchar *name)
+{
+        DhBook *book = NULL;
+        GList  *l;
+
+        g_return_val_if_fail (book_manager, NULL);
+
+        for (l = GET_PRIVATE (book_manager)->books;
+             l && !book;
+             l = g_list_next (l)) {
+                if (g_strcmp0 (name,
+                               dh_book_get_name (DH_BOOK (l->data))) == 0) {
+                        book = l->data;
+                }
+        }
+
+        return book;
+}
+
+void
+dh_book_manager_update (DhBookManager *book_manager)
+{
+        DhBookManagerPriv *priv;
+        GSList *books_disabled = NULL;
+        GList  *l;
+
+        g_return_if_fail (book_manager);
+
+        priv = GET_PRIVATE (book_manager);
+
+        /* Create list of disabled books */
+        for (l = priv->books; l; l = g_list_next (l)) {
+                DhBook *book = DH_BOOK (l->data);
+
+                if (!dh_book_get_enabled (book)) {
+                        books_disabled = g_slist_append (books_disabled,
+                                                         g_strdup (dh_book_get_name (book)));
+                }
+        }
+
+        /* Store in conf */
+        dh_util_state_store_books_disabled (books_disabled);
+
+        /* Emit signal to notify others */
+        g_signal_emit (book_manager,
+                       signals[DISABLED_BOOK_LIST_UPDATED],
+                       0);
+
+        book_manager_clean_list_of_books_disabled (books_disabled);
+}
+
+DhBookManager *
+dh_book_manager_new (void)
+{
+        return g_object_new (DH_TYPE_BOOK_MANAGER, NULL);
+}
+


Modified: devhelp/devhelp/dh-book-manager.h
61 files changed, 61 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Lanedo GmbH
+ *
+ * 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.
+ */
+
+#ifndef __DH_BOOK_MANAGER_H__
+#define __DH_BOOK_MANAGER_H__
+
+#include <gtk/gtk.h>
+
+#include "dh-book.h"
+
+G_BEGIN_DECLS
+
+typedef struct _DhBookManager      DhBookManager;
+typedef struct _DhBookManagerClass DhBookManagerClass;
+
+#define DH_TYPE_BOOK_MANAGER         (dh_book_manager_get_type ())
+#define DH_BOOK_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DH_TYPE_BOOK_MANAGER, DhBookManager))
+#define DH_BOOK_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), DH_TYPE_BOOK_MANAGER, DhBookManagerClass))
+#define DH_IS_BOOK_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DH_TYPE_BOOK_MANAGER))
+#define DH_IS_BOOK_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DH_TYPE_BOOK_MANAGER))
+#define DH_BOOK_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DH_TYPE_BOOK_MANAGER, DhBookManagerClass))
+
+struct _DhBookManager {
+        GObject parent_instance;
+};
+
+struct _DhBookManagerClass {
+        GObjectClass parent_class;
+
+        /* Signals */
+        void (* disabled_book_list_updated) (DhBookManager *book_manager);
+};
+
+GType          dh_book_manager_get_type         (void) G_GNUC_CONST;
+DhBookManager *dh_book_manager_new              (void);
+void           dh_book_manager_populate         (DhBookManager *book_manager);
+GList         *dh_book_manager_get_books        (DhBookManager *book_manager);
+DhBook        *dh_book_manager_get_book_by_name (DhBookManager *book_manager,
+                                                 const gchar *name);
+void           dh_book_manager_update           (DhBookManager *book_manager);
+
+G_END_DECLS
+
+#endif /* __DH_BOOK_MANAGER_H__ */


Modified: devhelp/devhelp/dh-book-tree.c
385 files changed, 385 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,385 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2003      CodeFactory AB
+ * Copyright (C) 2008      Imendio AB
+ *
+ * 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 <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "dh-marshal.h"
+#include "dh-book-tree.h"
+#include "dh-book.h"
+
+typedef struct {
+        const gchar *uri;
+        gboolean     found;
+        GtkTreeIter  iter;
+        GtkTreePath *path;
+} FindURIData;
+
+typedef struct {
+        GtkTreeStore  *store;
+        DhBookManager *book_manager;
+        DhLink        *selected_link;
+} DhBookTreePriv;
+
+static void dh_book_tree_class_init        (DhBookTreeClass  *klass);
+static void dh_book_tree_init              (DhBookTree       *tree);
+static void book_tree_add_columns          (DhBookTree       *tree);
+static void book_tree_setup_selection      (DhBookTree       *tree);
+static void book_tree_populate_tree        (DhBookTree       *tree);
+static void book_tree_insert_node          (DhBookTree       *tree,
+                                            GNode            *node,
+                                            GtkTreeIter      *parent_iter);
+static void book_tree_selection_changed_cb (GtkTreeSelection *selection,
+                                            DhBookTree       *tree);
+
+enum {
+        LINK_SELECTED,
+        LAST_SIGNAL
+};
+
+enum {
+	COL_TITLE,
+	COL_LINK,
+	COL_WEIGHT,
+	N_COLUMNS
+};
+
+G_DEFINE_TYPE (DhBookTree, dh_book_tree, GTK_TYPE_TREE_VIEW);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, DH_TYPE_BOOK_TREE, DhBookTreePriv);
+
+static gint signals[LAST_SIGNAL] = { 0 };
+
+static void
+book_tree_finalize (GObject *object)
+{
+	DhBookTreePriv *priv = GET_PRIVATE (object);
+
+	g_object_unref (priv->store);
+        g_object_unref (priv->book_manager);
+
+        G_OBJECT_CLASS (dh_book_tree_parent_class)->finalize (object);
+}
+
+static void
+dh_book_tree_class_init (DhBookTreeClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = book_tree_finalize;
+
+        signals[LINK_SELECTED] =
+                g_signal_new ("link-selected",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      _dh_marshal_VOID__POINTER,
+			      G_TYPE_NONE,
+			      1, G_TYPE_POINTER);
+
+	g_type_class_add_private (klass, sizeof (DhBookTreePriv));
+}
+
+static void
+dh_book_tree_init (DhBookTree *tree)
+{
+        DhBookTreePriv *priv;
+
+        priv = GET_PRIVATE (tree);
+
+	priv->store = gtk_tree_store_new (N_COLUMNS,
+					  G_TYPE_STRING,
+					  G_TYPE_POINTER,
+                                          PANGO_TYPE_WEIGHT);
+	priv->selected_link = NULL;
+	gtk_tree_view_set_model (GTK_TREE_VIEW (tree),
+				 GTK_TREE_MODEL (priv->store));
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);
+
+	book_tree_add_columns (tree);
+
+	book_tree_setup_selection (tree);
+}
+
+static void
+book_tree_add_columns (DhBookTree *tree)
+{
+	GtkCellRenderer   *cell;
+	GtkTreeViewColumn *column;
+
+	column = gtk_tree_view_column_new ();
+
+	cell = gtk_cell_renderer_text_new ();
+	g_object_set (cell,
+		      "ellipsize", PANGO_ELLIPSIZE_END,
+		      NULL);
+	gtk_tree_view_column_pack_start (column, cell, TRUE);
+	gtk_tree_view_column_set_attributes (column, cell,
+					     "text", COL_TITLE,
+                                             "weight", COL_WEIGHT,
+					     NULL);
+
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
+}
+
+static void
+book_tree_setup_selection (DhBookTree *tree)
+{
+	GtkTreeSelection *selection;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
+
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+	g_signal_connect (selection, "changed",
+			  G_CALLBACK (book_tree_selection_changed_cb),
+			  tree);
+}
+
+static void
+book_tree_populate_tree (DhBookTree *tree)
+{
+        DhBookTreePriv *priv = GET_PRIVATE (tree);
+        GList          *l;
+
+        gtk_tree_store_clear (priv->store);
+
+        for (l = dh_book_manager_get_books (priv->book_manager);
+             l;
+             l = g_list_next (l)) {
+                DhBook *book = DH_BOOK (l->data);
+                GNode  *node;
+
+                node = dh_book_get_tree (book);
+                while(node) {
+                        book_tree_insert_node (tree, node, NULL);
+                        node = g_node_next_sibling (node);
+                }
+        }
+}
+
+static void
+book_manager_disabled_book_list_changed_cb (DhBookManager *book_manager,
+                                            gpointer user_data)
+{
+        DhBookTree *tree = user_data;
+        book_tree_populate_tree (tree);
+}
+
+static void
+book_tree_insert_node (DhBookTree  *tree,
+		       GNode       *node,
+		       GtkTreeIter *parent_iter)
+
+{
+        DhBookTreePriv *priv = GET_PRIVATE (tree);
+	DhLink         *link;
+	GtkTreeIter     iter;
+        PangoWeight     weight;
+	GNode          *child;
+
+	link = node->data;
+
+	gtk_tree_store_append (priv->store, &iter, parent_iter);
+
+	if (dh_link_get_link_type (link) == DH_LINK_TYPE_BOOK) {
+                weight = PANGO_WEIGHT_BOLD;
+	} else {
+                weight = PANGO_WEIGHT_NORMAL;
+        }
+
+        gtk_tree_store_set (priv->store, &iter,
+                            COL_TITLE, dh_link_get_name (link),
+                            COL_LINK, link,
+                            COL_WEIGHT, weight,
+                            -1);
+
+	for (child = g_node_first_child (node);
+	     child;
+	     child = g_node_next_sibling (child)) {
+		book_tree_insert_node (tree, child, &iter);
+	}
+}
+
+static void
+book_tree_selection_changed_cb (GtkTreeSelection *selection,
+				DhBookTree       *tree)
+{
+        DhBookTreePriv *priv = GET_PRIVATE (tree);
+        GtkTreeIter     iter;
+
+	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+                DhLink *link;
+
+		gtk_tree_model_get (GTK_TREE_MODEL (priv->store),
+				    &iter,
+                                    COL_LINK, &link,
+                                    -1);
+		if (link != priv->selected_link) {
+			g_signal_emit (tree, signals[LINK_SELECTED], 0, link);
+		}
+		priv->selected_link = link;
+	}
+}
+
+GtkWidget *
+dh_book_tree_new (DhBookManager *book_manager)
+{
+        DhBookTree     *tree;
+        DhBookTreePriv *priv;
+	GtkTreeSelection *selection;
+	GtkTreeIter     iter;
+	DhLink *link;
+
+	tree = g_object_new (DH_TYPE_BOOK_TREE, NULL);
+        priv = GET_PRIVATE (tree);
+
+        priv->book_manager = g_object_ref (book_manager);
+        g_signal_connect (priv->book_manager,
+                          "disabled-book-list-updated",
+                          G_CALLBACK (book_manager_disabled_book_list_changed_cb),
+                          tree);
+
+        book_tree_populate_tree (tree);
+
+	/* Mark the first item as selected, or it would get automatically
+	 * selected when the treeview will get focus; but that's not even
+	 * enough as a selection changed would still be emitted when there
+	 * is no change, hence the manual tracking of selection in
+	 * selected_link.
+	 *   https://bugzilla.gnome.org/show_bug.cgi?id=492206
+	 */
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
+	g_signal_handlers_block_by_func	(selection,
+					 book_tree_selection_changed_cb,
+					 tree);
+	gtk_tree_model_get_iter_first ( GTK_TREE_MODEL (priv->store), &iter);
+	gtk_tree_model_get (GTK_TREE_MODEL (priv->store),
+			&iter, COL_LINK, &link, -1);
+	priv->selected_link = link;
+	gtk_tree_selection_select_iter (selection, &iter);
+	g_signal_handlers_unblock_by_func (selection,
+					   book_tree_selection_changed_cb,
+					   tree);
+
+        return GTK_WIDGET (tree);
+}
+
+static gboolean
+book_tree_find_uri_foreach (GtkTreeModel *model,
+			    GtkTreePath  *path,
+			    GtkTreeIter  *iter,
+			    FindURIData  *data)
+{
+	DhLink      *link;
+        gchar       *link_uri;
+
+	gtk_tree_model_get (model, iter,
+			    COL_LINK, &link,
+			    -1);
+
+        link_uri = dh_link_get_uri (link);
+	if (g_str_has_prefix (data->uri, link_uri)) {
+		data->found = TRUE;
+		data->iter = *iter;
+		data->path = gtk_tree_path_copy (path);
+	}
+        g_free (link_uri);
+
+	return data->found;
+}
+
+void
+dh_book_tree_select_uri (DhBookTree  *tree,
+			 const gchar *uri)
+{
+        DhBookTreePriv   *priv = GET_PRIVATE (tree);
+	GtkTreeSelection *selection;
+	FindURIData       data;
+
+	data.found = FALSE;
+	data.uri = uri;
+
+	gtk_tree_model_foreach (GTK_TREE_MODEL (priv->store),
+				(GtkTreeModelForeachFunc) book_tree_find_uri_foreach,
+				&data);
+
+	if (!data.found) {
+		return;
+	}
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
+
+	g_signal_handlers_block_by_func	(selection,
+					 book_tree_selection_changed_cb,
+					 tree);
+
+	gtk_tree_view_expand_to_path (GTK_TREE_VIEW (tree), data.path);
+	gtk_tree_selection_select_iter (selection, &data.iter);
+	gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree), data.path, NULL, 0);
+
+	g_signal_handlers_unblock_by_func (selection,
+					   book_tree_selection_changed_cb,
+					   tree);
+
+	gtk_tree_path_free (data.path);
+}
+
+const gchar *
+dh_book_tree_get_selected_book_title (DhBookTree *tree)
+{
+	GtkTreeSelection *selection;
+	GtkTreeModel     *model;
+	GtkTreeIter       iter;
+	GtkTreePath      *path;
+	DhLink           *link;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
+	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		return NULL;
+	}
+
+	path = gtk_tree_model_get_path (model, &iter);
+
+	/* Get the book node for this link. */
+	while (1) {
+		if (gtk_tree_path_get_depth (path) <= 1) {
+			break;
+		}
+
+		gtk_tree_path_up (path);
+	}
+
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_path_free (path);
+
+	gtk_tree_model_get (model, &iter,
+			    COL_LINK, &link,
+			    -1);
+
+	return dh_link_get_name (link);
+}


Modified: devhelp/devhelp/dh-book-tree.h
55 files changed, 55 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001 Mikael Hallendal <micke at imendio.com>
+ *
+ * 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.
+ */
+
+#ifndef __DH_BOOK_TREE_H__
+#define __DH_BOOK_TREE_H__
+
+#include <gtk/gtk.h>
+#include "dh-link.h"
+#include "dh-book-manager.h"
+
+G_BEGIN_DECLS
+
+#define DH_TYPE_BOOK_TREE            (dh_book_tree_get_type ())
+#define DH_BOOK_TREE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), DH_TYPE_BOOK_TREE, DhBookTree))
+#define DH_BOOK_TREE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), DH_TYPE_BOOK_TREE, DhBookTreeClass))
+#define DH_IS_BOOK_TREE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DH_TYPE_BOOK_TREE))
+#define DH_IS_BOOK_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), DH_TYPE_BOOK_TREE))
+
+typedef struct _DhBookTree      DhBookTree;
+typedef struct _DhBookTreeClass DhBookTreeClass;
+
+struct _DhBookTree {
+        GtkTreeView parent_instance;
+};
+
+struct _DhBookTreeClass {
+        GtkTreeViewClass parent_class;
+};
+
+GType        dh_book_tree_get_type                (void) G_GNUC_CONST;
+GtkWidget *  dh_book_tree_new                     (DhBookManager *book_manager);
+void         dh_book_tree_select_uri              (DhBookTree    *book_tree,
+                                                   const gchar   *uri);
+const gchar *dh_book_tree_get_selected_book_title (DhBookTree    *tree);
+
+G_END_DECLS
+
+#endif /* __DH_BOOK_TREE_H__ */


Modified: devhelp/devhelp/dh-book.c
246 files changed, 246 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,246 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2004-2008 Imendio AB
+ * Copyright (C) 2010 Lanedo GmbH
+ *
+ * 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 "dh-link.h"
+#include "dh-parser.h"
+#include "dh-book.h"
+
+/* Structure defining basic contents to store about every book */
+typedef struct {
+        /* File path of the book */
+        gchar    *path;
+        /* Enable or disabled? */
+        gboolean  enabled;
+        /* Book name */
+        gchar    *name;
+        /* Book title */
+        gchar    *title;
+        /* Generated book tree */
+        GNode    *tree;
+        /* Generated list of keywords in the book */
+        GList    *keywords;
+} DhBookPriv;
+
+G_DEFINE_TYPE (DhBook, dh_book, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE       \
+        (instance, DH_TYPE_BOOK, DhBookPriv)
+
+static void    dh_book_init       (DhBook      *book);
+static void    dh_book_class_init (DhBookClass *klass);
+
+static void    unref_node_link    (GNode *node,
+                                   gpointer data);
+
+static void
+book_finalize (GObject *object)
+{
+        DhBookPriv *priv;
+
+        priv = GET_PRIVATE (object);
+
+        if (priv->tree) {
+                g_node_traverse (priv->tree,
+                                 G_IN_ORDER,
+                                 G_TRAVERSE_ALL,
+                                 -1,
+                                 (GNodeTraverseFunc)unref_node_link,
+                                 NULL);
+                g_node_destroy (priv->tree);
+        }
+
+        if (priv->keywords) {
+                g_list_foreach (priv->keywords, (GFunc)dh_link_unref, NULL);
+                g_list_free (priv->keywords);
+        }
+
+        g_free (priv->title);
+
+        g_free (priv->path);
+
+        G_OBJECT_CLASS (dh_book_parent_class)->finalize (object);
+}
+
+static void
+dh_book_class_init (DhBookClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = book_finalize;
+
+	g_type_class_add_private (klass, sizeof (DhBookPriv));
+}
+
+static void
+dh_book_init (DhBook *book)
+{
+        DhBookPriv *priv = GET_PRIVATE (book);
+
+        priv->name = NULL;
+        priv->path = NULL;
+        priv->title = NULL;
+        priv->enabled = TRUE;
+        priv->tree = NULL;
+        priv->keywords = NULL;
+}
+
+static void
+unref_node_link (GNode *node,
+                 gpointer data)
+{
+        dh_link_unref (node->data);
+}
+
+DhBook *
+dh_book_new (const gchar  *book_path)
+{
+        DhBookPriv *priv;
+        DhBook     *book;
+        GError     *error = NULL;
+
+        g_return_val_if_fail (book_path, NULL);
+
+        book = g_object_new (DH_TYPE_BOOK, NULL);
+        priv = GET_PRIVATE (book);
+
+        /* Parse file storing contents in the book struct */
+        if (!dh_parser_read_file  (book_path,
+                                   &priv->tree,
+                                   &priv->keywords,
+                                   &error)) {
+                g_warning ("Failed to read '%s': %s",
+                           priv->path, error->message);
+                g_error_free (error);
+
+                /* Deallocate the book, as we are not going to add it
+                 *  in the manager */
+                g_object_unref (book);
+                return NULL;
+        }
+
+        /* Store path */
+        priv->path = g_strdup (book_path);
+
+        /* Setup title */
+        priv->title = g_strdup (dh_link_get_name ((DhLink *)priv->tree->data));
+
+        /* Setup name */
+        priv->name = g_strdup (dh_link_get_book_id ((DhLink *)priv->tree->data));
+
+        return book;
+}
+
+GList *
+dh_book_get_keywords (DhBook *book)
+{
+        DhBookPriv *priv;
+
+        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
+
+        priv = GET_PRIVATE (book);
+
+        return priv->enabled ? priv->keywords : NULL;
+}
+
+GNode *
+dh_book_get_tree (DhBook *book)
+{
+        DhBookPriv *priv;
+
+        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
+
+        priv = GET_PRIVATE (book);
+
+        return priv->enabled ? priv->tree : NULL;
+}
+
+const gchar *
+dh_book_get_name (DhBook *book)
+{
+        DhBookPriv *priv;
+
+        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
+
+        priv = GET_PRIVATE (book);
+
+        return priv->name;
+}
+
+const gchar *
+dh_book_get_title (DhBook *book)
+{
+        DhBookPriv *priv;
+
+        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
+
+        priv = GET_PRIVATE (book);
+
+        return priv->title;
+}
+
+gboolean
+dh_book_get_enabled (DhBook *book)
+{
+        g_return_val_if_fail (DH_IS_BOOK (book), FALSE);
+
+        return GET_PRIVATE (book)->enabled;
+}
+
+void
+dh_book_set_enabled (DhBook *book,
+                     gboolean enabled)
+{
+        g_return_if_fail (DH_IS_BOOK (book));
+
+        GET_PRIVATE (book)->enabled = enabled;
+}
+
+gint
+dh_book_cmp_by_path (const DhBook *a,
+                     const DhBook *b)
+{
+        return ((a && b) ?
+                g_strcmp0 (GET_PRIVATE (a)->path, GET_PRIVATE (b)->path) :
+                -1);
+}
+
+gint
+dh_book_cmp_by_name (const DhBook *a,
+                     const DhBook *b)
+{
+        return ((a && b) ?
+                g_ascii_strcasecmp (GET_PRIVATE (a)->name, GET_PRIVATE (b)->name) :
+                -1);
+}
+
+gint
+dh_book_cmp_by_title (const DhBook *a,
+                      const DhBook *b)
+{
+        return ((a && b) ?
+                g_utf8_collate (GET_PRIVATE (a)->title, GET_PRIVATE (b)->title) :
+                -1);
+}


Modified: devhelp/devhelp/dh-book.h
67 files changed, 67 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2005-2008 Imendio AB
+ * Copyright (C) 2010 Lanedo GmbH
+ *
+ * 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.
+ */
+
+#ifndef _DH_BOOK_H_
+#define _DH_BOOK_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _DhBook      DhBook;
+typedef struct _DhBookClass DhBookClass;
+
+#define DH_TYPE_BOOK         (dh_book_get_type ())
+#define DH_BOOK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DH_TYPE_BOOK, DhBook))
+#define DH_BOOK_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), DH_TYPE_BOOK, DhBookClass))
+#define DH_IS_BOOK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DH_TYPE_BOOK))
+#define DH_IS_BOOK_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DH_TYPE_BOOK))
+#define DH_BOOK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DH_TYPE_BOOK, DhBookClass))
+
+struct _DhBook {
+        GObject parent_instance;
+};
+
+struct _DhBookClass {
+        GObjectClass parent_class;
+};
+
+GType        dh_book_get_type     (void) G_GNUC_CONST;
+DhBook      *dh_book_new          (const gchar  *book_path);
+GList       *dh_book_get_keywords (DhBook *book);
+GNode       *dh_book_get_tree     (DhBook *book);
+const gchar *dh_book_get_name     (DhBook *book);
+const gchar *dh_book_get_title    (DhBook *book);
+gboolean     dh_book_get_enabled  (DhBook *book);
+void         dh_book_set_enabled  (DhBook *book,
+                                   gboolean enabled);
+gint         dh_book_cmp_by_path  (const DhBook *a,
+                                   const DhBook *b);
+gint         dh_book_cmp_by_name  (const DhBook *a,
+                                   const DhBook *b);
+gint         dh_book_cmp_by_title (const DhBook *a,
+                                   const DhBook *b);
+
+G_END_DECLS
+
+#endif /* _DH_BOOK_H_ */


Modified: devhelp/devhelp/dh-enum-types.c
137 files changed, 137 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,137 @@
+
+/* Generated data (by glib-mkenums) */
+
+#include "dh-enum-types.h"
+#include "dh-assistant.h"
+#include "dh-assistant-view.h"
+#include "dh-base.h"
+#include "dh-book-manager.h"
+#include "dh-book.h"
+#include "dh-book-tree.h"
+#include "dh-error.h"
+#include "dh-keyword-model.h"
+#include "dh-link.h"
+#include "dh-search.h"
+#include "dh-window.h"
+
+GType
+dh_error_get_type (void)
+{
+	static GType the_type = 0;
+
+	if (the_type == 0)
+	{
+		static const GEnumValue values[] = {
+			{ DH_ERROR_FILE_NOT_FOUND,
+			  "DH_ERROR_FILE_NOT_FOUND",
+			  "file-not-found" },
+			{ DH_ERROR_MALFORMED_BOOK,
+			  "DH_ERROR_MALFORMED_BOOK",
+			  "malformed-book" },
+			{ DH_ERROR_INVALID_BOOK_TYPE,
+			  "DH_ERROR_INVALID_BOOK_TYPE",
+			  "invalid-book-type" },
+			{ DH_ERROR_INTERNAL_ERROR,
+			  "DH_ERROR_INTERNAL_ERROR",
+			  "internal-error" },
+			{ 0, NULL, NULL }
+		};
+		the_type = g_enum_register_static (
+				g_intern_static_string ("DhError"),
+				values);
+	}
+	return the_type;
+}
+
+
+GType
+dh_link_type_get_type (void)
+{
+	static GType the_type = 0;
+
+	if (the_type == 0)
+	{
+		static const GEnumValue values[] = {
+			{ DH_LINK_TYPE_BOOK,
+			  "DH_LINK_TYPE_BOOK",
+			  "book" },
+			{ DH_LINK_TYPE_PAGE,
+			  "DH_LINK_TYPE_PAGE",
+			  "page" },
+			{ DH_LINK_TYPE_KEYWORD,
+			  "DH_LINK_TYPE_KEYWORD",
+			  "keyword" },
+			{ DH_LINK_TYPE_FUNCTION,
+			  "DH_LINK_TYPE_FUNCTION",
+			  "function" },
+			{ DH_LINK_TYPE_STRUCT,
+			  "DH_LINK_TYPE_STRUCT",
+			  "struct" },
+			{ DH_LINK_TYPE_MACRO,
+			  "DH_LINK_TYPE_MACRO",
+			  "macro" },
+			{ DH_LINK_TYPE_ENUM,
+			  "DH_LINK_TYPE_ENUM",
+			  "enum" },
+			{ DH_LINK_TYPE_TYPEDEF,
+			  "DH_LINK_TYPE_TYPEDEF",
+			  "typedef" },
+			{ 0, NULL, NULL }
+		};
+		the_type = g_enum_register_static (
+				g_intern_static_string ("DhLinkType"),
+				values);
+	}
+	return the_type;
+}
+
+GType
+dh_link_flags_get_type (void)
+{
+	static GType the_type = 0;
+
+	if (the_type == 0)
+	{
+		static const GFlagsValue values[] = {
+			{ DH_LINK_FLAGS_NONE,
+			  "DH_LINK_FLAGS_NONE",
+			  "none" },
+			{ DH_LINK_FLAGS_DEPRECATED,
+			  "DH_LINK_FLAGS_DEPRECATED",
+			  "deprecated" },
+			{ 0, NULL, NULL }
+		};
+		the_type = g_flags_register_static (
+				g_intern_static_string ("DhLinkFlags"),
+				values);
+	}
+	return the_type;
+}
+
+
+GType
+dh_open_link_flags_get_type (void)
+{
+	static GType the_type = 0;
+
+	if (the_type == 0)
+	{
+		static const GFlagsValue values[] = {
+			{ DH_OPEN_LINK_NEW_WINDOW,
+			  "DH_OPEN_LINK_NEW_WINDOW",
+			  "window" },
+			{ DH_OPEN_LINK_NEW_TAB,
+			  "DH_OPEN_LINK_NEW_TAB",
+			  "tab" },
+			{ 0, NULL, NULL }
+		};
+		the_type = g_flags_register_static (
+				g_intern_static_string ("DhOpenLinkFlags"),
+				values);
+	}
+	return the_type;
+}
+
+
+/* Generated data ends here */
+


Modified: devhelp/devhelp/dh-enum-types.c.template
47 files changed, 47 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,47 @@
+/*** BEGIN file-header ***/
+#include "dh-enum-types.h"
+#include "dh-assistant.h"
+#include "dh-assistant-view.h"
+#include "dh-base.h"
+#include "dh-book-manager.h"
+#include "dh-book.h"
+#include "dh-book-tree.h"
+#include "dh-error.h"
+#include "dh-keyword-model.h"
+#include "dh-link.h"
+#include "dh-search.h"
+#include "dh-window.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ at enum_name@_get_type (void)
+{
+	static GType the_type = 0;
+
+	if (the_type == 0)
+	{
+		static const G at Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+			{ @VALUENAME@,
+			  "@VALUENAME@",
+			  "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+			{ 0, NULL, NULL }
+		};
+		the_type = g_ at type@_register_static (
+				g_intern_static_string ("@EnumName@"),
+				values);
+	}
+	return the_type;
+}
+
+/*** END value-tail ***/


Modified: devhelp/devhelp/dh-enum-types.h
34 files changed, 34 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,34 @@
+
+/* Generated data (by glib-mkenums) */
+
+#ifndef __DH_ENUM_TYPES_H__
+#define __DH_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* Enumerations from "dh-error.h" */
+
+#define DH_TYPE_ERROR	(dh_error_get_type())
+GType dh_error_get_type	(void) G_GNUC_CONST;
+
+/* Enumerations from "dh-link.h" */
+
+#define DH_TYPE_LINK_TYPE	(dh_link_type_get_type())
+GType dh_link_type_get_type	(void) G_GNUC_CONST;
+
+#define DH_TYPE_LINK_FLAGS	(dh_link_flags_get_type())
+GType dh_link_flags_get_type	(void) G_GNUC_CONST;
+
+/* Enumerations from "dh-window.h" */
+
+#define DH_TYPE_OPEN_LINK_FLAGS	(dh_open_link_flags_get_type())
+GType dh_open_link_flags_get_type	(void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __DH_ENUM_TYPES_H__ */
+
+/* Generated data ends here */
+


Modified: devhelp/devhelp/dh-enum-types.h.template
27 files changed, 27 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,27 @@
+/*** BEGIN file-header ***/
+#ifndef __DH_ENUM_TYPES_H__
+#define __DH_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* Enumerations from "@filename@" */
+
+/*** END file-production ***/
+
+/*** BEGIN enumeration-production ***/
+#define DH_TYPE_ at ENUMSHORT@	(@enum_name at _get_type())
+GType @enum_name at _get_type	(void) G_GNUC_CONST;
+
+/*** END enumeration-production ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __DH_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+


Modified: devhelp/devhelp/dh-error.c
35 files changed, 35 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ *
+ * 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 "dh-error.h"
+
+GQuark
+dh_error_quark (void)
+{
+        static GQuark q = 0;
+
+        if (q == 0) {
+                q = g_quark_from_static_string ("dh-error-quark");
+        }
+
+        return q;
+}


Modified: devhelp/devhelp/dh-error.h
42 files changed, 42 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ *
+ * 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.
+ */
+
+#ifndef __DH_ERROR_H__
+#define __DH_ERROR_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define DH_ERROR dh_error_quark ()
+
+typedef enum {
+        DH_ERROR_FILE_NOT_FOUND,
+        DH_ERROR_MALFORMED_BOOK,
+        DH_ERROR_INVALID_BOOK_TYPE,
+        DH_ERROR_INTERNAL_ERROR
+} DhError;
+
+GQuark dh_error_quark (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __DH_ERROR_H__ */


Modified: devhelp/devhelp/dh-keyword-model.c
545 files changed, 545 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,545 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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 <gtk/gtk.h>
+#include <string.h>
+
+#include "dh-link.h"
+#include "dh-book.h"
+#include "dh-keyword-model.h"
+
+struct _DhKeywordModelPriv {
+        DhBookManager *book_manager;
+
+        GList *keyword_words;
+        gint   keyword_words_length;
+
+        gint   stamp;
+};
+
+#define G_LIST(x) ((GList *) x)
+#define MAX_HITS 100
+
+static void dh_keyword_model_init            (DhKeywordModel      *list_store);
+static void dh_keyword_model_class_init      (DhKeywordModelClass *class);
+static void dh_keyword_model_tree_model_init (GtkTreeModelIface   *iface);
+
+G_DEFINE_TYPE_WITH_CODE (DhKeywordModel, dh_keyword_model, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+                                                dh_keyword_model_tree_model_init));
+
+static void
+keyword_model_dispose (GObject *object)
+{
+        DhKeywordModel     *model = DH_KEYWORD_MODEL (object);
+        DhKeywordModelPriv *priv = model->priv;
+
+        if (priv->book_manager) {
+                g_object_unref (priv->book_manager);
+                priv->book_manager = NULL;
+        }
+
+        G_OBJECT_CLASS (dh_keyword_model_parent_class)->dispose (object);
+}
+
+static void
+keyword_model_finalize (GObject *object)
+{
+        DhKeywordModel     *model = DH_KEYWORD_MODEL (object);
+        DhKeywordModelPriv *priv = model->priv;
+
+        g_list_free (priv->keyword_words);
+
+        g_free (model->priv);
+
+        G_OBJECT_CLASS (dh_keyword_model_parent_class)->finalize (object);
+}
+
+static void
+dh_keyword_model_class_init (DhKeywordModelClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);;
+
+        object_class->finalize = keyword_model_finalize;
+        object_class->dispose = keyword_model_dispose;
+}
+
+static void
+dh_keyword_model_init (DhKeywordModel *model)
+{
+        DhKeywordModelPriv *priv;
+
+        priv = g_new0 (DhKeywordModelPriv, 1);
+        model->priv = priv;
+
+        do {
+                priv->stamp = g_random_int ();
+        } while (priv->stamp == 0);
+}
+
+static GtkTreeModelFlags
+keyword_model_get_flags (GtkTreeModel *tree_model)
+{
+        return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
+}
+
+static gint
+keyword_model_get_n_columns (GtkTreeModel *tree_model)
+{
+        return DH_KEYWORD_MODEL_NUM_COLS;
+}
+
+static GType
+keyword_model_get_column_type (GtkTreeModel *tree_model,
+                               gint          column)
+{
+        switch (column) {
+        case DH_KEYWORD_MODEL_COL_NAME:
+                return G_TYPE_STRING;
+                break;
+        case DH_KEYWORD_MODEL_COL_LINK:
+                return G_TYPE_POINTER;
+        default:
+                return G_TYPE_INVALID;
+        }
+}
+
+static gboolean
+keyword_model_get_iter (GtkTreeModel *tree_model,
+                        GtkTreeIter  *iter,
+                        GtkTreePath  *path)
+{
+        DhKeywordModel     *model;
+        DhKeywordModelPriv *priv;
+        GList              *node;
+        const gint         *indices;
+
+        model = DH_KEYWORD_MODEL (tree_model);
+        priv = model->priv;
+
+        indices = gtk_tree_path_get_indices (path);
+
+        if (indices == NULL) {
+                return FALSE;
+        }
+
+        if (indices[0] >= priv->keyword_words_length) {
+                return FALSE;
+        }
+
+        node = g_list_nth (priv->keyword_words, indices[0]);
+
+        iter->stamp     = priv->stamp;
+        iter->user_data = node;
+
+        return TRUE;
+}
+
+static GtkTreePath *
+keyword_model_get_path (GtkTreeModel *tree_model,
+                        GtkTreeIter  *iter)
+{
+        DhKeywordModel     *model = DH_KEYWORD_MODEL (tree_model);
+        DhKeywordModelPriv *priv;
+        GtkTreePath        *path;
+        gint                i = 0;
+
+        g_return_val_if_fail (iter->stamp == model->priv->stamp, NULL);
+
+        priv = model->priv;
+
+        i = g_list_position (priv->keyword_words, iter->user_data);
+        if (i < 0) {
+                return NULL;
+        }
+
+        path = gtk_tree_path_new ();
+        gtk_tree_path_append_index (path, i);
+
+        return path;
+}
+
+static void
+keyword_model_get_value (GtkTreeModel *tree_model,
+                         GtkTreeIter  *iter,
+                         gint          column,
+                         GValue       *value)
+{
+        DhLink *link;
+
+        link = G_LIST (iter->user_data)->data;
+
+        switch (column) {
+        case DH_KEYWORD_MODEL_COL_NAME:
+                g_value_init (value, G_TYPE_STRING);
+                g_value_set_string (value, dh_link_get_name (link));
+                break;
+        case DH_KEYWORD_MODEL_COL_LINK:
+                g_value_init (value, G_TYPE_POINTER);
+                g_value_set_pointer (value, link);
+                break;
+        default:
+                g_warning ("Bad column %d requested", column);
+        }
+}
+
+static gboolean
+keyword_model_iter_next (GtkTreeModel *tree_model,
+                         GtkTreeIter  *iter)
+{
+        DhKeywordModel *model = DH_KEYWORD_MODEL (tree_model);
+
+        g_return_val_if_fail (model->priv->stamp == iter->stamp, FALSE);
+
+        iter->user_data = G_LIST (iter->user_data)->next;
+
+        return (iter->user_data != NULL);
+}
+
+static gboolean
+keyword_model_iter_children (GtkTreeModel *tree_model,
+                             GtkTreeIter  *iter,
+                             GtkTreeIter  *parent)
+{
+        DhKeywordModelPriv *priv;
+
+        priv = DH_KEYWORD_MODEL (tree_model)->priv;
+
+        /* This is a list, nodes have no children. */
+        if (parent) {
+                return FALSE;
+        }
+
+        /* But if parent == NULL we return the list itself as children of
+         * the "root".
+         */
+        if (priv->keyword_words) {
+                iter->stamp = priv->stamp;
+                iter->user_data = priv->keyword_words;
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static gboolean
+keyword_model_iter_has_child (GtkTreeModel *tree_model,
+                              GtkTreeIter  *iter)
+{
+        return FALSE;
+}
+
+static gint
+keyword_model_iter_n_children (GtkTreeModel *tree_model,
+                               GtkTreeIter  *iter)
+{
+        DhKeywordModelPriv *priv;
+
+        priv = DH_KEYWORD_MODEL (tree_model)->priv;
+
+        if (iter == NULL) {
+                return priv->keyword_words_length;
+        }
+
+        g_return_val_if_fail (priv->stamp == iter->stamp, -1);
+
+        return 0;
+}
+
+static gboolean
+keyword_model_iter_nth_child (GtkTreeModel *tree_model,
+                              GtkTreeIter  *iter,
+                              GtkTreeIter  *parent,
+                              gint          n)
+{
+        DhKeywordModelPriv *priv;
+        GList              *child;
+
+        priv = DH_KEYWORD_MODEL (tree_model)->priv;
+
+        if (parent) {
+                return FALSE;
+        }
+
+        child = g_list_nth (priv->keyword_words, n);
+
+        if (child) {
+                iter->stamp = priv->stamp;
+                iter->user_data = child;
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static gboolean
+keyword_model_iter_parent (GtkTreeModel *tree_model,
+                           GtkTreeIter  *iter,
+                           GtkTreeIter  *child)
+{
+        return FALSE;
+}
+
+static void
+dh_keyword_model_tree_model_init (GtkTreeModelIface *iface)
+{
+        iface->get_flags       = keyword_model_get_flags;
+        iface->get_n_columns   = keyword_model_get_n_columns;
+        iface->get_column_type = keyword_model_get_column_type;
+        iface->get_iter        = keyword_model_get_iter;
+        iface->get_path        = keyword_model_get_path;
+        iface->get_value       = keyword_model_get_value;
+        iface->iter_next       = keyword_model_iter_next;
+        iface->iter_children   = keyword_model_iter_children;
+        iface->iter_has_child  = keyword_model_iter_has_child;
+        iface->iter_n_children = keyword_model_iter_n_children;
+        iface->iter_nth_child  = keyword_model_iter_nth_child;
+        iface->iter_parent     = keyword_model_iter_parent;
+}
+
+DhKeywordModel *
+dh_keyword_model_new (void)
+{
+        DhKeywordModel *model;
+
+        model = g_object_new (DH_TYPE_KEYWORD_MODEL, NULL);
+
+        return model;
+}
+
+void
+dh_keyword_model_set_words (DhKeywordModel *model,
+                            DhBookManager  *book_manager)
+{
+        g_return_if_fail (DH_IS_KEYWORD_MODEL (model));
+
+        model->priv->book_manager = g_object_ref (book_manager);
+}
+
+static GList *
+keyword_model_search (DhKeywordModel  *model,
+                      const gchar     *string,
+                      gchar          **stringv,
+                      const gchar     *book_id,
+                      gboolean         case_sensitive,
+                      DhLink         **exact_link)
+{
+        DhKeywordModelPriv *priv;
+        GList              *new_list = NULL, *b;
+        gint                hits = 0;
+        gchar              *page_id = NULL;
+        gchar              *page_filename_prefix = NULL;
+
+        priv = model->priv;
+
+        /* The search string may be prefixed by a page:foobar qualifier, it
+         * will be matched against the filenames of the hits to limit the
+         * search to pages whose filename is prefixed by "foobar.
+         */
+        if (stringv && g_str_has_prefix(stringv[0], "page:")) {
+                page_id = stringv[0] + 5;
+                page_filename_prefix = g_strdup_printf("%s.", page_id);
+                stringv++;
+        }
+
+        for (b = dh_book_manager_get_books (priv->book_manager);
+             b && hits < MAX_HITS;
+             b = g_list_next (b)) {
+                DhBook *book;
+                GList *l;
+
+                book = DH_BOOK (b->data);
+
+                for (l = dh_book_get_keywords (book);
+                     l && hits < MAX_HITS;
+                     l = g_list_next (l)) {
+                        DhLink   *link;
+                        gboolean  found;
+                        gchar    *name;
+
+                        link = l->data;
+                        found = FALSE;
+
+                        if (book_id &&
+                            dh_link_get_book_id (link) &&
+                            strcmp (dh_link_get_book_id (link), book_id) != 0) {
+                                continue;
+                        }
+
+                        if (page_id &&
+                            (dh_link_get_link_type (link) != DH_LINK_TYPE_PAGE &&
+                             !g_str_has_prefix (dh_link_get_file_name (link), page_filename_prefix))) {
+                                continue;
+                        }
+
+                        if (!case_sensitive) {
+                                name = g_ascii_strdown (dh_link_get_name (link), -1);
+                        } else {
+                                name = g_strdup (dh_link_get_name (link));
+                        }
+
+                        if (!found) {
+                                gint i;
+
+                                if (stringv[0] == NULL) {
+                                        /* means only a page was specified, no keyword */
+                                        if (g_strrstr (dh_link_get_name(link), page_id))
+                                                found = TRUE;
+                                } else {
+                                        found = TRUE;
+                                        for (i = 0; stringv[i] != NULL; i++) {
+                                                if (!g_strrstr (name, stringv[i])) {
+                                                        found = FALSE;
+                                                        break;
+                                                }
+                                        }
+                                }
+                        }
+
+                        g_free (name);
+
+                        if (found) {
+                                /* Include in the new list. */
+                                new_list = g_list_prepend (new_list, link);
+                                hits++;
+
+                                if (!*exact_link &&
+                                    dh_link_get_name (link) && (
+                                            (dh_link_get_link_type (link) == DH_LINK_TYPE_PAGE &&
+                                             page_id && strcmp (dh_link_get_name (link), page_id) == 0) ||
+                                            (strcmp (dh_link_get_name (link), string) == 0))) {
+                                        *exact_link = link;
+                                }
+                        }
+                }
+        }
+
+        g_free (page_filename_prefix);
+
+        return g_list_sort (new_list, dh_link_compare);
+}
+
+DhLink *
+dh_keyword_model_filter (DhKeywordModel *model,
+                         const gchar    *string,
+                         const gchar    *book_id)
+{
+        DhKeywordModelPriv  *priv;
+        GList               *new_list = NULL;
+        gint                 old_length;
+        DhLink              *exact_link = NULL;
+        gint                 hits;
+        gint                 i;
+        GtkTreePath         *path;
+        GtkTreeIter          iter;
+
+        g_return_val_if_fail (DH_IS_KEYWORD_MODEL (model), NULL);
+        g_return_val_if_fail (string != NULL, NULL);
+
+        priv = model->priv;
+
+        /* Do the minimum amount of work: call update on all rows that are
+         * kept and remove the rest.
+         */
+        old_length = priv->keyword_words_length;
+        new_list = NULL;
+        hits = 0;
+
+        if (string[0] != '\0') {
+                gchar    **stringv;
+                gboolean   case_sensitive;
+
+                stringv = g_strsplit (string, " ", -1);
+
+                case_sensitive = FALSE;
+
+                /* Search for any parameters and position search cursor to
+                 * the next element in the search string.
+                 */
+                for (i = 0; stringv[i] != NULL; i++) {
+                        gchar *lower;
+
+                        /* Searches are case sensitive when any uppercase
+                         * letter is used in the search terms, matching vim
+                         * smartcase behaviour.
+                         */
+                        lower = g_ascii_strdown (stringv[i], -1);
+                        if (strcmp (lower, stringv[i]) != 0) {
+                                case_sensitive = TRUE;
+                                g_free (lower);
+                                break;
+                        }
+                        g_free (lower);
+                }
+
+                new_list = keyword_model_search (model,
+                                                 string,
+                                                 stringv,
+                                                 book_id,
+                                                 case_sensitive,
+                                                 &exact_link);
+                hits = g_list_length (new_list);
+
+                g_strfreev (stringv);
+        }
+
+        /* Update the list of hits. */
+        g_list_free (priv->keyword_words);
+        priv->keyword_words = new_list;
+        priv->keyword_words_length = hits;
+
+        /* Update model: rows 0 -> hits. */
+        for (i = 0; i < hits; ++i) {
+                path = gtk_tree_path_new ();
+                gtk_tree_path_append_index (path, i);
+                keyword_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
+                gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+                gtk_tree_path_free (path);
+        }
+
+        if (old_length > hits) {
+                /* Update model: remove rows hits -> old_length. */
+                for (i = old_length - 1; i >= hits; i--) {
+                        path = gtk_tree_path_new ();
+                        gtk_tree_path_append_index (path, i);
+                        gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+                        gtk_tree_path_free (path);
+                }
+        }
+        else if (old_length < hits) {
+                /* Update model: add rows old_length -> hits. */
+                for (i = old_length; i < hits; i++) {
+                        path = gtk_tree_path_new ();
+                        gtk_tree_path_append_index (path, i);
+                        keyword_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
+                        gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+                        gtk_tree_path_free (path);
+                }
+        }
+
+        if (hits == 1) {
+                return priv->keyword_words->data;
+        }
+
+        return exact_link;
+}


Modified: devhelp/devhelp/dh-keyword-model.h
69 files changed, 69 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ *
+ * 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.
+ */
+
+#ifndef __DH_KEYWORD_MODEL_H__
+#define __DH_KEYWORD_MODEL_H__
+
+#include <gtk/gtk.h>
+#include "dh-link.h"
+#include "dh-book-manager.h"
+
+G_BEGIN_DECLS
+
+#define DH_TYPE_KEYWORD_MODEL            (dh_keyword_model_get_type ())
+#define DH_KEYWORD_MODEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), DH_TYPE_KEYWORD_MODEL, DhKeywordModel))
+#define DH_KEYWORD_MODEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), DH_TYPE_KEYWORD_MODEL, DhKeywordModelClass))
+#define DH_IS_KEYWORD_MODEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DH_TYPE_KEYWORD_MODEL))
+#define DH_IS_KEYWORD_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DH_TYPE_KEYWORD_MODEL))
+#define DH_KEYWORD_MODEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), DH_TYPE_KEYWORD_MODEL, DhKeywordModelClass))
+
+typedef struct _DhKeywordModel      DhKeywordModel;
+typedef struct _DhKeywordModelClass DhKeywordModelClass;
+typedef struct _DhKeywordModelPriv  DhKeywordModelPriv;
+
+struct _DhKeywordModel
+{
+        GObject             parent_instance;
+        DhKeywordModelPriv *priv;
+};
+
+struct _DhKeywordModelClass
+{
+        GObjectClass parent_class;
+};
+
+enum {
+        DH_KEYWORD_MODEL_COL_NAME,
+        DH_KEYWORD_MODEL_COL_LINK,
+        DH_KEYWORD_MODEL_NUM_COLS
+};
+
+GType           dh_keyword_model_get_type  (void);
+DhKeywordModel *dh_keyword_model_new       (void);
+void            dh_keyword_model_set_words (DhKeywordModel *model,
+                                            DhBookManager  *book_manager);
+DhLink *        dh_keyword_model_filter    (DhKeywordModel *model,
+                                            const gchar    *string,
+                                            const gchar    *book_id);
+
+G_END_DECLS
+
+#endif /* __DH_KEYWORD_MODEL_H__ */


Modified: devhelp/devhelp/dh-link.c
291 files changed, 291 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,291 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2008      Imendio AB
+ *
+ * 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 <glib-object.h>
+#include <glib/gi18n-lib.h>
+#include "dh-link.h"
+
+struct _DhLink {
+        /* FIXME: Those two could exist only for book to save some
+         * memory.
+         */
+        gchar       *id;
+        gchar       *base;
+
+        gchar       *name;
+        gchar       *filename;
+
+        DhLink      *book;
+        DhLink      *page;
+
+        guint        ref_count;
+
+        DhLinkType   type : 8;
+        DhLinkFlags  flags : 8;
+};
+
+GType
+dh_link_get_type (void)
+{
+        static GType type = 0;
+
+        if (G_UNLIKELY (type == 0)) {
+                type = g_boxed_type_register_static (
+                        "DhLink",
+                        (GBoxedCopyFunc) dh_link_ref,
+                        (GBoxedFreeFunc) dh_link_unref);
+        }
+        return type;
+}
+
+static void
+link_free (DhLink *link)
+{
+	g_free (link->base);
+	g_free (link->id);
+	g_free (link->name);
+	g_free (link->filename);
+
+        if (link->book) {
+                dh_link_unref (link->book);
+        }
+	if (link->page) {
+                dh_link_unref (link->page);
+        }
+
+	g_slice_free (DhLink, link);
+}
+
+DhLink *
+dh_link_new (DhLinkType   type,
+             const gchar *base,
+	     const gchar *id,
+	     const gchar *name,
+	     DhLink      *book,
+	     DhLink      *page,
+	     const gchar *filename)
+{
+	DhLink *link;
+
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (filename != NULL, NULL);
+
+        if (type == DH_LINK_TYPE_BOOK) {
+                g_return_val_if_fail (base != NULL, NULL);
+                g_return_val_if_fail (id != NULL, NULL);
+        }
+        if (type != DH_LINK_TYPE_BOOK && type != DH_LINK_TYPE_PAGE) {
+                g_return_val_if_fail (book != NULL, NULL);
+                g_return_val_if_fail (page != NULL, NULL);
+        }
+
+	link = g_slice_new0 (DhLink);
+
+	link->ref_count = 1;
+	link->type = type;
+
+        if (type == DH_LINK_TYPE_BOOK) {
+                link->base = g_strdup (base);
+                link->id = g_strdup (id);
+        }
+
+	link->name = g_strdup (name);
+	link->filename = g_strdup (filename);
+
+	if (book) {
+                link->book = dh_link_ref (book);
+        }
+	if (page) {
+                link->page = dh_link_ref (page);
+        }
+
+	return link;
+}
+
+gint
+dh_link_compare (gconstpointer a,
+                 gconstpointer b)
+{
+        DhLink *la = (DhLink *) a;
+        DhLink *lb = (DhLink *) b;
+	gint    flags_diff;
+
+        /* Sort deprecated hits last. */
+        flags_diff = (la->flags & DH_LINK_FLAGS_DEPRECATED) - 
+                (lb->flags & DH_LINK_FLAGS_DEPRECATED);
+        if (flags_diff != 0) {
+                return flags_diff;
+        }
+
+        return strcmp (la->name, lb->name);
+}
+
+DhLink *
+dh_link_ref (DhLink *link)
+{
+	g_return_val_if_fail (link != NULL, NULL);
+
+	link->ref_count++;
+
+	return link;
+}
+
+void
+dh_link_unref (DhLink *link)
+{
+	g_return_if_fail (link != NULL);
+
+	link->ref_count--;
+
+	if (link->ref_count == 0) {
+		link_free (link);
+	}
+}
+
+const gchar *
+dh_link_get_name (DhLink *link)
+{
+        return link->name;
+}
+
+const gchar *
+dh_link_get_book_name (DhLink *link)
+{
+        if (link->book) {
+                return link->book->name;
+        }
+
+        return "";
+}
+
+const gchar *
+dh_link_get_page_name (DhLink *link)
+{
+        if (link->page) {
+                return link->page->name;
+        }
+
+        return "";
+}
+
+const gchar *
+dh_link_get_file_name (DhLink *link)
+{
+        if (link->page) {
+                return link->filename;
+        }
+
+        return "";
+}
+
+const gchar *
+dh_link_get_book_id (DhLink *link)
+{
+        if (link->type == DH_LINK_TYPE_BOOK) {
+                return link->id;
+        }
+
+        if (link->book) {
+                return link->book->id;
+        }
+
+        return "";
+}
+
+gchar *
+dh_link_get_uri (DhLink *link)
+{
+	gchar *base, *uri;
+
+        if (link->type == DH_LINK_TYPE_BOOK)
+                base = link->base;
+        else
+                base = link->book->base;
+
+	uri = g_strconcat ("file://", base, "/", link->filename, NULL, NULL);
+
+	return uri;
+}
+
+DhLinkType
+dh_link_get_link_type (DhLink *link)
+{
+        return link->type;
+}
+
+DhLinkFlags
+dh_link_get_flags (DhLink *link)
+{
+	return link->flags;
+}
+
+void
+dh_link_set_flags (DhLink      *link,
+                   DhLinkFlags  flags)
+{
+        link->flags = flags;
+}
+
+const gchar *
+dh_link_get_type_as_string (DhLink *link)
+{
+        switch (link->type) {
+        case DH_LINK_TYPE_BOOK:
+                /* i18n: a documentation book */
+                return _("Book");
+	case DH_LINK_TYPE_PAGE:
+                /* i18n: a "page" in a documentation book */
+                return _("Page");
+	case DH_LINK_TYPE_KEYWORD:
+                /* i18n: a search hit in the documentation, could be a
+                 * function, macro, struct, etc */
+                return _("Keyword");
+        case DH_LINK_TYPE_FUNCTION:
+                /* i18n: in the programming language context, if you don't
+                 * have an ESTABLISHED term for it, leave it
+                 * untranslated. */
+                return _("Function");
+	case DH_LINK_TYPE_STRUCT:
+                /* i18n: in the programming language context, if you don't
+                 * have an ESTABLISHED term for it, leave it
+                 * untranslated. */
+                return _("Struct");
+	case DH_LINK_TYPE_MACRO:
+                /* i18n: in the programming language context, if you don't
+                 * have an ESTABLISHED term for it, leave it
+                 * untranslated. */
+                return _("Macro");
+	case DH_LINK_TYPE_ENUM:
+                /* i18n: in the programming language context, if you don't
+                 * have an ESTABLISHED term for it, leave it
+                 * untranslated. */
+               return _("Enum");
+	case DH_LINK_TYPE_TYPEDEF:
+                /* i18n: in the programming language context, if you don't
+                 * have an ESTABLISHED term for it, leave it
+                 * untranslated. */
+                return _("Type");
+        }
+
+        return "";
+}


Modified: devhelp/devhelp/dh-link.h
72 files changed, 72 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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.
+ */
+
+#ifndef __DH_LINK_H__
+#define __DH_LINK_H__
+
+#include <glib-object.h>
+
+typedef enum {
+        DH_LINK_TYPE_BOOK,
+        DH_LINK_TYPE_PAGE,
+        DH_LINK_TYPE_KEYWORD,
+        DH_LINK_TYPE_FUNCTION,
+        DH_LINK_TYPE_STRUCT,
+        DH_LINK_TYPE_MACRO,
+        DH_LINK_TYPE_ENUM,
+        DH_LINK_TYPE_TYPEDEF
+} DhLinkType;
+
+typedef enum {
+        DH_LINK_FLAGS_NONE       = 0,
+        DH_LINK_FLAGS_DEPRECATED = 1 << 0
+} DhLinkFlags;
+
+typedef struct _DhLink DhLink;
+
+#define DH_TYPE_LINK (dh_link_get_type ())
+
+GType        dh_link_get_type           (void);
+DhLink *     dh_link_new                (DhLinkType     type,
+                                         const gchar   *base,
+                                         const gchar   *id,
+					 const gchar   *name,
+                                         DhLink        *book,
+                                         DhLink        *page,
+					 const gchar   *filename);
+void         dh_link_free               (DhLink        *link);
+gint         dh_link_compare            (gconstpointer  a,
+					 gconstpointer  b);
+DhLink *     dh_link_ref                (DhLink        *link);
+void         dh_link_unref              (DhLink        *link);
+const gchar *dh_link_get_name           (DhLink        *link);
+const gchar *dh_link_get_book_name      (DhLink        *link);
+const gchar *dh_link_get_page_name      (DhLink        *link);
+const gchar *dh_link_get_file_name      (DhLink        *link);
+const gchar *dh_link_get_book_id        (DhLink        *link);
+gchar       *dh_link_get_uri            (DhLink        *link);
+DhLinkFlags  dh_link_get_flags          (DhLink        *link);
+void         dh_link_set_flags          (DhLink        *link,
+					 DhLinkFlags    flags);
+DhLinkType   dh_link_get_link_type      (DhLink        *link);
+const gchar *dh_link_get_type_as_string (DhLink        *link);
+
+#endif /* __DH_LINK_H__ */


Modified: devhelp/devhelp/dh-marshal.c
148 files changed, 148 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,148 @@
+#include "dh-marshal.h"
+
+#include	<glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:BOOLEAN (dh-marshal.list:1) */
+
+/* VOID:POINTER (dh-marshal.list:2) */
+
+/* VOID:STRING (dh-marshal.list:3) */
+
+/* VOID:VOID (dh-marshal.list:4) */
+
+/* BOOLEAN:STRING (dh-marshal.list:5) */
+void
+_dh_marshal_BOOLEAN__STRING (GClosure     *closure,
+                             GValue       *return_value G_GNUC_UNUSED,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint G_GNUC_UNUSED,
+                             gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__STRING) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     data2);
+  register GMarshalFunc_BOOLEAN__STRING callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_BOOLEAN__STRING) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_string (param_values + 1),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* VOID:STRING,FLAGS (dh-marshal.list:6) */
+void
+_dh_marshal_VOID__STRING_FLAGS (GClosure     *closure,
+                                GValue       *return_value G_GNUC_UNUSED,
+                                guint         n_param_values,
+                                const GValue *param_values,
+                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_FLAGS) (gpointer     data1,
+                                                   gpointer     arg_1,
+                                                   guint        arg_2,
+                                                   gpointer     data2);
+  register GMarshalFunc_VOID__STRING_FLAGS callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__STRING_FLAGS) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_flags (param_values + 2),
+            data2);
+}
+
+/* VOID:BOOLEAN (dh-marshal.list:1) */
+
+/* VOID:POINTER (dh-marshal.list:2) */
+
+/* VOID:STRING (dh-marshal.list:3) */
+
+/* VOID:VOID (dh-marshal.list:4) */
+
+/* BOOLEAN:STRING (dh-marshal.list:5) */
+
+/* VOID:STRING,FLAGS (dh-marshal.list:6) */
+


Modified: devhelp/devhelp/dh-marshal.h
52 files changed, 52 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,52 @@
+
+#ifndef ___dh_marshal_MARSHAL_H__
+#define ___dh_marshal_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:BOOLEAN (dh-marshal.list:1) */
+#define _dh_marshal_VOID__BOOLEAN	g_cclosure_marshal_VOID__BOOLEAN
+
+/* VOID:POINTER (dh-marshal.list:2) */
+#define _dh_marshal_VOID__POINTER	g_cclosure_marshal_VOID__POINTER
+
+/* VOID:STRING (dh-marshal.list:3) */
+#define _dh_marshal_VOID__STRING	g_cclosure_marshal_VOID__STRING
+
+/* VOID:VOID (dh-marshal.list:4) */
+#define _dh_marshal_VOID__VOID	g_cclosure_marshal_VOID__VOID
+
+/* BOOLEAN:STRING (dh-marshal.list:5) */
+extern void _dh_marshal_BOOLEAN__STRING (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
+
+/* VOID:STRING,FLAGS (dh-marshal.list:6) */
+extern void _dh_marshal_VOID__STRING_FLAGS (GClosure     *closure,
+                                            GValue       *return_value,
+                                            guint         n_param_values,
+                                            const GValue *param_values,
+                                            gpointer      invocation_hint,
+                                            gpointer      marshal_data);
+
+/* VOID:BOOLEAN (dh-marshal.list:1) */
+
+/* VOID:POINTER (dh-marshal.list:2) */
+
+/* VOID:STRING (dh-marshal.list:3) */
+
+/* VOID:VOID (dh-marshal.list:4) */
+
+/* BOOLEAN:STRING (dh-marshal.list:5) */
+
+/* VOID:STRING,FLAGS (dh-marshal.list:6) */
+
+G_END_DECLS
+
+#endif /* ___dh_marshal_MARSHAL_H__ */
+


Modified: devhelp/devhelp/dh-marshal.list
6 files changed, 6 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,6 @@
+VOID:BOOLEAN
+VOID:POINTER
+VOID:STRING
+VOID:VOID
+BOOLEAN:STRING
+VOID:STRING,FLAGS


Modified: devhelp/devhelp/dh-parser.c
611 files changed, 611 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,611 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (c) 2002-2003 Mikael Hallendal <micke at imendio.com>
+ * Copyright (c) 2002-2003 CodeFactory AB
+ * Copyright (C) 2005,2008 Imendio AB
+ *
+ * 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 <errno.h>
+#include <zlib.h>
+#include <glib/gi18n-lib.h>
+
+#include "dh-error.h"
+#include "dh-link.h"
+#include "dh-parser.h"
+
+#define NAMESPACE      "http://www.devhelp.net/book"
+#define BYTES_PER_READ 4096
+
+typedef struct {
+	GMarkupParser       *m_parser;
+	GMarkupParseContext *context;
+
+	const gchar         *path;
+
+	/* Top node of book */
+	GNode               *book_node;
+
+	/* Current sub section node */
+	GNode               *parent;
+
+	gboolean             parsing_chapters;
+	gboolean             parsing_keywords;
+
+ 	GNode              **book_tree;
+	GList              **keywords;
+
+	/* Version 2 uses <keyword> instead of <function>. */
+	gint                 version;
+} DhParser;
+
+static void
+dh_parser_free (DhParser *parser)
+{
+        // NOTE: priv->book_tree and priv->keywords do not need to be freed
+        // because they're only used to store the locations for the return
+        // params of dh_parser_read_file()
+
+        g_markup_parse_context_free (parser->context);
+        g_free (parser->m_parser);
+        g_free (parser);
+}
+
+static void
+parser_start_node_book (DhParser             *parser,
+                        GMarkupParseContext  *context,
+                        const gchar          *node_name,
+                        const gchar         **attribute_names,
+                        const gchar         **attribute_values,
+                        GError              **error)
+{
+        gint         i, j;
+        gint         line, col;
+        gchar       *title = NULL;
+        gchar *base = NULL;
+        const gchar *name = NULL;
+        const gchar *uri = NULL;
+	DhLink      *link;
+
+        if (g_ascii_strcasecmp (node_name, "book") != 0) {
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("Expected '%s', got '%s' at line %d, column %d"),
+                             "book", node_name, line, col);
+                return;
+        }
+
+        for (i = 0; attribute_names[i]; ++i) {
+                const gchar *xmlns;
+
+                if (g_ascii_strcasecmp (attribute_names[i], "xmlns") == 0) {
+                        xmlns = attribute_values[i];
+                        if (g_ascii_strcasecmp (xmlns, NAMESPACE) != 0) {
+                                g_markup_parse_context_get_position (context,
+                                                                     &line,
+                                                                     &col);
+                                g_set_error (error,
+                                             DH_ERROR,
+                                             DH_ERROR_MALFORMED_BOOK,
+                                             _("Invalid namespace '%s' at"
+                                               " line %d, column %d"),
+                                             xmlns, line, col);
+                                return;
+                        }
+                }
+                else if (g_ascii_strcasecmp (attribute_names[i], "name") == 0) {
+                        name = attribute_values[i];
+                }
+                else if (g_ascii_strcasecmp (attribute_names[i], "title") == 0) {
+                        title = g_strdup(attribute_values[i]);
+                        for (j = 0; title[j]; j++) {
+                                if (title[j] == '\n') title[j] = ' ';
+                        }
+                }
+                else if (g_ascii_strcasecmp (attribute_names[i], "base") == 0) {
+                        base = g_strdup (attribute_values[i]);
+			}
+                else if (g_ascii_strcasecmp (attribute_names[i], "link") == 0) {
+                        uri = attribute_values[i];
+                }
+        }
+
+        if (!title || !name || !uri) {
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("\"title\", \"name\" and \"link\" elements are "
+                               "required at line %d, column %d"),
+                             line, col);
+                g_free (title);
+                return;
+        }
+
+        if (!base) {
+                base = g_path_get_dirname (parser->path);
+        }
+
+        link = dh_link_new (DH_LINK_TYPE_BOOK,
+                            base,
+                            name,
+                            title,
+                            NULL,
+                            NULL,
+                            uri);
+        g_free (base);
+
+        *parser->keywords = g_list_prepend (*parser->keywords, dh_link_ref (link));
+
+        parser->book_node = g_node_new (dh_link_ref (link));
+        *parser->book_tree = parser->book_node;
+        parser->parent = parser->book_node;
+        g_free (title);
+        dh_link_unref (link);
+}
+
+static void
+parser_start_node_chapter (DhParser             *parser,
+                           GMarkupParseContext  *context,
+                           const gchar          *node_name,
+                           const gchar         **attribute_names,
+                           const gchar         **attribute_values,
+                           GError              **error)
+{
+        gint         i;
+        gint         line, col;
+        const gchar *name = NULL;
+        const gchar *uri = NULL;
+	DhLink      *link;
+        GNode       *node;
+
+        if (g_ascii_strcasecmp (node_name, "sub") != 0) {
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("Expected '%s', got '%s' at line %d, column %d"),
+                             "sub", node_name, line, col);
+                return;
+        }
+
+        for (i = 0; attribute_names[i]; ++i) {
+                if (g_ascii_strcasecmp (attribute_names[i], "name") == 0) {
+                        name = attribute_values[i];
+                }
+                else if (g_ascii_strcasecmp (attribute_names[i], "link") == 0) {
+                        uri = attribute_values[i];
+                }
+        }
+
+        if (!name || !uri) {
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("\"name\" and \"link\" elements are required "
+                               "inside <sub> on line %d, column %d"),
+                             line, col);
+                return;
+        }
+
+        link = dh_link_new (DH_LINK_TYPE_PAGE,
+                            NULL,
+                            NULL,
+                            name,
+                            parser->book_node->data,
+                            NULL,
+                            uri);
+
+        *parser->keywords = g_list_prepend (*parser->keywords, link);
+
+        node = g_node_new (link);
+        g_node_prepend (parser->parent, node);
+        parser->parent = node;
+}
+
+static void
+parser_start_node_keyword (DhParser             *parser,
+                           GMarkupParseContext  *context,
+                           const gchar          *node_name,
+                           const gchar         **attribute_names,
+                           const gchar         **attribute_values,
+                           GError              **error)
+{
+        gint         i;
+        gint         line, col;
+        const gchar *name = NULL;
+        const gchar *uri = NULL;
+        const gchar *type = NULL;
+        const gchar *deprecated = NULL;
+        DhLinkType   link_type;
+	DhLink      *link;
+        gchar       *tmp;
+
+        if (parser->version == 2 &&
+            g_ascii_strcasecmp (node_name, "keyword") != 0) {
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("Expected '%s', got '%s' at line %d, column %d"),
+                             "keyword", node_name, line, col);
+                return;
+        }
+        else if (parser->version == 1 &&
+            g_ascii_strcasecmp (node_name, "function") != 0) {
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("Expected '%s', got '%s' at line %d, column %d"),
+                             "function", node_name, line, col);
+                return;
+        }
+
+        for (i = 0; attribute_names[i]; ++i) {
+                if (g_ascii_strcasecmp (attribute_names[i], "type") == 0) {
+                        type = attribute_values[i];
+                }
+                else if (g_ascii_strcasecmp (attribute_names[i], "name") == 0) {
+                        name = attribute_values[i];
+                }
+                else if (g_ascii_strcasecmp (attribute_names[i], "link") == 0) {
+                        uri = attribute_values[i];
+                }
+                else if (g_ascii_strcasecmp (attribute_names[i], "deprecated") == 0) {
+                        deprecated = attribute_values[i];
+                }
+        }
+
+        if (!name || !uri) {
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("\"name\" and \"link\" elements are required "
+                               "inside '%s' on line %d, column %d"),
+                             parser->version == 2 ? "keyword" : "function",
+                             line, col);
+                return;
+        }
+
+        if (parser->version == 2 && !type) {
+                /* Required */
+                g_markup_parse_context_get_position (context, &line, &col);
+                g_set_error (error,
+                             DH_ERROR,
+                             DH_ERROR_MALFORMED_BOOK,
+                             _("\"type\" element is required "
+                               "inside <keyword> on line %d, column %d"),
+                             line, col);
+                return;
+        }
+
+        if (parser->version == 2) {
+                if (strcmp (type, "function") == 0) {
+                        link_type = DH_LINK_TYPE_FUNCTION;
+                }
+                else if (strcmp (type, "struct") == 0) {
+                        link_type = DH_LINK_TYPE_STRUCT;
+                }
+                else if (strcmp (type, "macro") == 0) {
+                        link_type = DH_LINK_TYPE_MACRO;
+                }
+                else if (strcmp (type, "enum") == 0) {
+                        link_type = DH_LINK_TYPE_ENUM;
+                }
+                else if (strcmp (type, "typedef") == 0) {
+                        link_type = DH_LINK_TYPE_TYPEDEF;
+                } else {
+                        link_type = DH_LINK_TYPE_KEYWORD;
+                }
+        } else {
+                link_type = DH_LINK_TYPE_KEYWORD;
+        }
+
+        /* Strip out trailing " () or "()". */
+        if (g_str_has_suffix (name, " ()")) {
+                tmp = g_strndup (name, strlen (name) - 3);
+
+                if (link_type == DH_LINK_TYPE_KEYWORD) {
+                        link_type = DH_LINK_TYPE_FUNCTION;
+                }
+                name = tmp;
+        }
+        else if (g_str_has_suffix (name, "()")) {
+                tmp = g_strndup (name, strlen (name) - 2);
+
+                /* With old devhelp format, take a guess that this is a
+                 * macro.
+                 */
+                if (link_type == DH_LINK_TYPE_KEYWORD) {
+                        link_type = DH_LINK_TYPE_MACRO;
+                }
+                name = tmp;
+        } else {
+                tmp = NULL;
+        }
+
+        /* Strip out prefixing "struct", "union", "enum", to make searching
+         * easier. Also fix up the link type (only applies for old devhelp
+         * format).
+         */
+        if (g_str_has_prefix (name, "struct ")) {
+                name = name + 7;
+                if (link_type == DH_LINK_TYPE_KEYWORD) {
+                        link_type = DH_LINK_TYPE_STRUCT;
+                }
+        }
+        else if (g_str_has_prefix (name, "union ")) {
+                name = name + 6;
+                if (link_type == DH_LINK_TYPE_KEYWORD) {
+                        link_type = DH_LINK_TYPE_STRUCT;
+                }
+        }
+        else if (g_str_has_prefix (name, "enum ")) {
+                name = name + 5;
+                if (link_type == DH_LINK_TYPE_KEYWORD) {
+                        link_type = DH_LINK_TYPE_ENUM;
+                }
+        }
+
+        link = dh_link_new (link_type,
+                            NULL,
+                            NULL,
+                            name,
+                            parser->book_node->data,
+                            parser->parent->data,
+                            uri);
+
+        g_free (tmp);
+
+        if (deprecated) {
+                dh_link_set_flags (
+                        link,
+                        dh_link_get_flags (link) | DH_LINK_FLAGS_DEPRECATED);
+        }
+
+        *parser->keywords = g_list_prepend (*parser->keywords, link);
+}
+
+static void
+parser_start_node_cb (GMarkupParseContext  *context,
+		      const gchar          *node_name,
+		      const gchar         **attribute_names,
+		      const gchar         **attribute_values,
+		      gpointer              user_data,
+		      GError              **error)
+{
+	DhParser *parser = user_data;
+
+        if (parser->parsing_keywords) {
+                parser_start_node_keyword (parser,
+                                           context,
+                                           node_name,
+                                           attribute_names,
+                                           attribute_values,
+                                           error);
+                return;
+        }
+        else if (parser->parsing_chapters) {
+                parser_start_node_chapter (parser,
+                                           context,
+                                           node_name,
+                                           attribute_names,
+                                           attribute_values,
+                                           error);
+                return;
+        }
+	else if (g_ascii_strcasecmp (node_name, "functions") == 0) {
+		parser->parsing_keywords = TRUE;
+	}
+	else if (g_ascii_strcasecmp (node_name, "chapters") == 0) {
+		parser->parsing_chapters = TRUE;
+	}
+	if (!parser->book_node) {
+                parser_start_node_book (parser,
+                                        context,
+                                        node_name,
+                                        attribute_names,
+                                        attribute_values,
+                                        error);
+		return;
+	}
+}
+
+static void
+parser_end_node_cb (GMarkupParseContext  *context,
+		    const gchar          *node_name,
+		    gpointer              user_data,
+		    GError              **error)
+{
+	DhParser *parser = user_data;
+
+        if (parser->parsing_keywords) {
+                if (g_ascii_strcasecmp (node_name, "functions") == 0) {
+			parser->parsing_keywords = FALSE;
+		}
+	}
+	else if (parser->parsing_chapters) {
+		g_node_reverse_children (parser->parent);
+		if (g_ascii_strcasecmp (node_name, "sub") == 0) {
+			parser->parent = parser->parent->parent;
+			/* Move up in the tree */
+		}
+		else if (g_ascii_strcasecmp (node_name, "chapters") == 0) {
+			parser->parsing_chapters = FALSE;
+		}
+	}
+}
+
+static void
+parser_error_cb (GMarkupParseContext *context,
+		 GError              *error,
+		 gpointer             user_data)
+{
+	DhParser *parser = user_data;
+
+	g_markup_parse_context_free (parser->context);
+ 	parser->context = NULL;
+}
+
+static gboolean
+parser_read_gz_file (DhParser     *parser,
+                     const gchar  *path,
+		     GError      **error)
+{
+	gchar  buf[BYTES_PER_READ];
+	gzFile file;
+
+	file = gzopen (path, "r");
+	if (!file) {
+		g_set_error (error,
+			     DH_ERROR,
+			     DH_ERROR_FILE_NOT_FOUND,
+			     "%s", g_strerror (errno));
+		return FALSE;
+	}
+
+	while (TRUE) {
+		gssize bytes_read;
+
+		bytes_read = gzread (file, buf, BYTES_PER_READ);
+		if (bytes_read == -1) {
+			gint         err;
+			const gchar *message;
+
+			message = gzerror (file, &err);
+			g_set_error (error,
+				     DH_ERROR,
+				     DH_ERROR_INTERNAL_ERROR,
+				     _("Cannot uncompress book '%s': %s"),
+				     path, message);
+			return FALSE;
+		}
+
+		g_markup_parse_context_parse (parser->context, buf,
+					      bytes_read, error);
+		if (error != NULL && *error != NULL) {
+			return FALSE;
+		}
+		if (bytes_read < BYTES_PER_READ) {
+			break;
+		}
+	}
+
+	gzclose (file);
+
+	return TRUE;
+}
+
+gboolean
+dh_parser_read_file (const gchar  *path,
+		     GNode       **book_tree,
+		     GList       **keywords,
+		     GError      **error)
+{
+	DhParser   *parser;
+        gboolean    gz;
+	GIOChannel *io = NULL;
+	gchar       buf[BYTES_PER_READ];
+	gboolean    result = TRUE;
+
+	parser = g_new0 (DhParser, 1);
+
+	if (g_str_has_suffix (path, ".devhelp2")) {
+		parser->version = 2;
+                gz = FALSE;
+        }
+        else if (g_str_has_suffix (path, ".devhelp")) {
+		parser->version = 1;
+                gz = FALSE;
+        }
+        else if (g_str_has_suffix (path, ".devhelp2.gz")) {
+		parser->version = 2;
+                gz = TRUE;
+        } else {
+		parser->version = 1;
+                gz = TRUE;
+        }
+
+	parser->m_parser = g_new0 (GMarkupParser, 1);
+
+	parser->m_parser->start_element = parser_start_node_cb;
+	parser->m_parser->end_element = parser_end_node_cb;
+	parser->m_parser->error = parser_error_cb;
+
+	parser->context = g_markup_parse_context_new (parser->m_parser, 0,
+						      parser, NULL);
+
+	parser->path = path;
+	parser->book_tree = book_tree;
+	parser->keywords = keywords;
+
+        if (gz) {
+                if (!parser_read_gz_file (parser,
+                                          path,
+                                          error)) {
+                        result = FALSE;
+                }
+                goto exit;
+	} else {
+                io = g_io_channel_new_file (path, "r", error);
+                if (!io) {
+                        result = FALSE;
+                        goto exit;
+                }
+
+                while (TRUE) {
+                        GIOStatus io_status;
+                        gsize     bytes_read;
+
+                        io_status = g_io_channel_read_chars (io, buf, BYTES_PER_READ,
+                                                             &bytes_read, error);
+                        if (io_status == G_IO_STATUS_ERROR) {
+                                result = FALSE;
+                                goto exit;
+                        }
+                        if (io_status != G_IO_STATUS_NORMAL) {
+                                break;
+                        }
+
+                        g_markup_parse_context_parse (parser->context, buf,
+                                                      bytes_read, error);
+                        if (error != NULL && *error != NULL) {
+                                result = FALSE;
+                                goto exit;
+                        }
+
+                        if (bytes_read < BYTES_PER_READ) {
+                                break;
+                        }
+                }
+        }
+
+ exit:
+	if (io) {
+                g_io_channel_unref (io);
+        }
+	dh_parser_free (parser);
+
+	return result;
+}


Modified: devhelp/devhelp/dh-parser.h
36 files changed, 36 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003 Mikael Hallendal <micke at imendio.com>
+ *
+ * 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.
+ */
+
+#ifndef __DH_PARSER_H__
+#define __DH_PARSER_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gboolean dh_parser_read_file (const gchar  *path,
+                              GNode       **book_tree,
+                              GList       **keywords,
+                              GError      **error);
+
+G_END_DECLS
+
+#endif /* __DH_PARSER_H__ */


Modified: devhelp/devhelp/dh-preferences.c
417 files changed, 417 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,417 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004-2008 Imendio AB
+ * Copyright (C) 2010 Lanedo GmbH
+ *
+ * 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 <gtk/gtk.h>
+#include <string.h>
+#include "dh-util.h"
+#include "dh-preferences.h"
+#include "ige-conf.h"
+#include "dh-base.h"
+
+typedef struct {
+	GtkWidget *dialog;
+
+        /* Fonts tab */
+	GtkWidget *system_fonts_button;
+	GtkWidget *fonts_table;
+	GtkWidget *variable_font_button;
+	GtkWidget *fixed_font_button;
+	guint      use_system_fonts_id;
+	guint      system_var_id;
+	guint      system_fixed_id;
+	guint      var_id;
+	guint      fixed_id;
+
+        /* Book Shelf tab */
+        DhBookManager *book_manager;
+        GtkTreeView   *booklist_treeview;
+        GtkListStore  *booklist_store;
+} DhPreferences;
+
+/* Fonts-tab related */
+static void     preferences_fonts_font_set_cb               (GtkFontButton    *button,
+                                                             gpointer          user_data);
+static void     preferences_fonts_system_fonts_toggled_cb   (GtkToggleButton  *button,
+                                                             gpointer          user_data);
+#if 0
+static void     preferences_fonts_var_font_notify_cb        (IgeConf          *client,
+                                                             const gchar      *path,
+                                                             gpointer          user_data);
+static void     preferences_fonts_fixed_font_notify_cb      (IgeConf          *client,
+                                                             const gchar      *path,
+                                                             gpointer          user_data);
+static void     preferences_fonts_use_system_font_notify_cb (IgeConf          *client,
+                                                             const gchar      *path,
+                                                             gpointer          user_data);
+static void     preferences_connect_conf_listeners          (void);
+#endif
+static void     preferences_fonts_get_font_names            (gboolean          use_system_fonts,
+                                                             gchar           **variable,
+                                                             gchar           **fixed);
+
+/* Bookshelf-tab related */
+static void     preferences_bookshelf_tree_selection_toggled_cb (GtkCellRendererToggle *cell_renderer,
+                                                                 gchar                 *path,
+                                                                 gpointer               user_data);
+static void     preferences_bookshelf_populate_store            (void);
+
+/* Common */
+static void     preferences_close_cb                        (GtkButton        *button,
+                                                             gpointer          user_data);
+
+#define DH_CONF_PATH                  "/apps/devhelp"
+#define DH_CONF_USE_SYSTEM_FONTS      DH_CONF_PATH "/ui/use_system_fonts"
+#define DH_CONF_VARIABLE_FONT         DH_CONF_PATH "/ui/variable_font"
+#define DH_CONF_FIXED_FONT            DH_CONF_PATH "/ui/fixed_font"
+#define DH_CONF_SYSTEM_VARIABLE_FONT  "/desktop/gnome/interface/font_name"
+#define DH_CONF_SYSTEM_FIXED_FONT     "/desktop/gnome/interface/monospace_font_name"
+
+/* Book list store columns... */
+#define LTCOLUMN_ENABLED  0
+#define LTCOLUMN_TITLE    1
+#define LTCOLUMN_BOOK     2
+
+static DhPreferences *prefs;
+
+static void
+preferences_init (void)
+{
+	if (!prefs) {
+                prefs = g_new0 (DhPreferences, 1);
+                prefs->book_manager  = dh_base_get_book_manager (dh_base_get ());
+        }
+}
+
+static void
+preferences_close_cb (GtkButton *button, gpointer user_data)
+{
+	DhPreferences *prefs = user_data;
+
+	gtk_widget_destroy (GTK_WIDGET (prefs->dialog));
+	prefs->dialog = NULL;
+
+	prefs->system_fonts_button = NULL;
+	prefs->fonts_table = NULL;
+	prefs->variable_font_button = NULL;
+	prefs->fixed_font_button = NULL;
+
+        prefs->booklist_treeview = NULL;
+        prefs->booklist_store = NULL;
+}
+
+static void
+preferences_fonts_font_set_cb (GtkFontButton *button,
+                               gpointer       user_data)
+{
+	DhPreferences *prefs = user_data;
+	const gchar   *font_name;
+	const gchar   *key;
+
+	font_name = gtk_font_button_get_font_name (button);
+
+	if (GTK_WIDGET (button) == prefs->variable_font_button) {
+		key = DH_CONF_VARIABLE_FONT;
+	} else {
+		key = DH_CONF_FIXED_FONT;
+	}
+
+	ige_conf_set_string (ige_conf_get (), key, font_name);
+}
+
+static void
+preferences_fonts_system_fonts_toggled_cb (GtkToggleButton *button,
+                                           gpointer         user_data)
+{
+	DhPreferences *prefs = user_data;
+	gboolean       active;
+
+	active = gtk_toggle_button_get_active (button);
+
+	ige_conf_set_bool (ige_conf_get (),
+                           DH_CONF_USE_SYSTEM_FONTS,
+                           active);
+
+	gtk_widget_set_sensitive (prefs->fonts_table, !active);
+}
+
+#if 0
+static void
+preferences_fonts_var_font_notify_cb (IgeConf     *client,
+                                      const gchar *path,
+                                      gpointer     user_data)
+{
+	DhPreferences *prefs = user_data;
+	gboolean       use_system_fonts;
+	gchar         *font_name;
+
+        ige_conf_get_bool (ige_conf_get (),
+                           DH_CONF_USE_SYSTEM_FONTS,
+                           &use_system_fonts);
+
+	if (prefs->variable_font_button) {
+		ige_conf_get_string (ige_conf_get (), path, &font_name);
+		gtk_font_button_set_font_name (GTK_FONT_BUTTON (prefs->variable_font_button),
+					       font_name);
+                g_free (font_name);
+	}
+}
+
+static void
+preferences_fonts_fixed_font_notify_cb (IgeConf     *client,
+                                        const gchar *path,
+                                        gpointer     user_data)
+{
+	DhPreferences *prefs = user_data;
+	gboolean       use_system_fonts;
+	gchar         *font_name;
+
+	ige_conf_get_bool (ige_conf_get (),
+                           DH_CONF_USE_SYSTEM_FONTS,
+                           &use_system_fonts);
+
+	if (prefs->fixed_font_button) {
+                ige_conf_get_string (ige_conf_get (), path, &font_name);
+		gtk_font_button_set_font_name (GTK_FONT_BUTTON (prefs->fixed_font_button),
+					       font_name);
+                g_free (font_name);
+	}
+}
+
+static void
+preferences_fonts_use_system_font_notify_cb (IgeConf     *client,
+                                             const gchar *path,
+                                             gpointer     user_data)
+{
+	DhPreferences *prefs = user_data;
+	gboolean       use_system_fonts;
+
+	ige_conf_get_bool (ige_conf_get (), path, &use_system_fonts);
+
+	if (prefs->system_fonts_button) {
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (prefs->system_fonts_button),
+					      use_system_fonts);
+	}
+
+	if (prefs->fonts_table) {
+		gtk_widget_set_sensitive (prefs->fonts_table, !use_system_fonts);
+	}
+}
+
+/* FIXME: This is not hooked up yet (to update the dialog if the values are
+ * changed outside of devhelp).
+ */
+static void
+preferences_connect_conf_listeners (void)
+{
+	IgeConf *conf;
+
+	conf = ige_conf_get ();
+
+	prefs->use_system_fonts_id =
+		ige_conf_notify_add (conf,
+                                     DH_CONF_USE_SYSTEM_FONTS,
+                                     preferences_use_system_font_notify_cb,
+                                     prefs);
+	prefs->system_var_id =
+		ige_conf_notify_add (conf,
+                                     DH_CONF_SYSTEM_VARIABLE_FONT,
+                                     preferences_var_font_notify_cb,
+                                     prefs);
+	prefs->system_fixed_id =
+		ige_conf_notify_add (conf,
+                                     DH_CONF_SYSTEM_FIXED_FONT,
+                                     preferences_fixed_font_notify_cb,
+                                     prefs);
+	prefs->var_id =
+		ige_conf_notify_add (conf,
+                                     DH_CONF_VARIABLE_FONT,
+                                     preferences_var_font_notify_cb,
+                                     prefs);
+	prefs->fixed_id =
+		ige_conf_notify_add (conf,
+                                     DH_CONF_FIXED_FONT,
+                                     preferences_fixed_font_notify_cb,
+                                     prefs);
+}
+#endif
+
+/* FIXME: Use the functions in dh-util.c for this. */
+static void
+preferences_fonts_get_font_names (gboolean   use_system_fonts,
+                                  gchar    **variable,
+                                  gchar    **fixed)
+{
+	gchar   *var_font_name, *fixed_font_name;
+	IgeConf *conf;
+
+	conf = ige_conf_get ();
+
+	if (use_system_fonts) {
+#ifdef GDK_WINDOWING_QUARTZ
+                var_font_name = g_strdup ("Lucida Grande 14");
+                fixed_font_name = g_strdup ("Monaco 14");
+#else
+		ige_conf_get_string (conf,
+                                     DH_CONF_SYSTEM_VARIABLE_FONT,
+                                     &var_font_name);
+		ige_conf_get_string (conf,
+                                     DH_CONF_SYSTEM_FIXED_FONT,
+                                     &fixed_font_name);
+#endif
+	} else {
+		ige_conf_get_string (conf,
+                                     DH_CONF_VARIABLE_FONT,
+                                     &var_font_name);
+                ige_conf_get_string (conf,
+                                     DH_CONF_FIXED_FONT,
+                                     &fixed_font_name);
+	}
+
+	*variable = var_font_name;
+	*fixed = fixed_font_name;
+}
+
+static void
+preferences_bookshelf_tree_selection_toggled_cb (GtkCellRendererToggle *cell_renderer,
+                                                 gchar                 *path,
+                                                 gpointer               user_data)
+{
+        GtkTreeIter iter;
+
+        if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (prefs->booklist_store),
+                                                 &iter,
+                                                 path))
+        {
+                gpointer book = NULL;
+                gboolean enabled;
+
+                gtk_tree_model_get (GTK_TREE_MODEL (prefs->booklist_store),
+                                    &iter,
+                                    LTCOLUMN_BOOK,       &book,
+                                    LTCOLUMN_ENABLED,    &enabled,
+                                    -1);
+
+                if (book) {
+                        /* Update book conf */
+                        dh_book_set_enabled (book, !enabled);
+
+                        gtk_list_store_set (prefs->booklist_store, &iter,
+                                            LTCOLUMN_ENABLED, !enabled,
+                                            -1);
+
+                        dh_book_manager_update (prefs->book_manager);
+                }
+        }
+}
+
+static void
+preferences_bookshelf_populate_store (void)
+{
+        GList         *l;
+
+        for (l = dh_book_manager_get_books (prefs->book_manager);
+             l;
+             l = g_list_next (l)) {
+                GtkTreeIter  iter;
+                DhBook      *book;
+
+                book = DH_BOOK (l->data);
+
+                gtk_list_store_append (prefs->booklist_store, &iter);
+                gtk_list_store_set (prefs->booklist_store, &iter,
+                                    LTCOLUMN_ENABLED,  dh_book_get_enabled (book),
+                                    LTCOLUMN_TITLE,    dh_book_get_title (book),
+                                    LTCOLUMN_BOOK,     book,
+                                    -1);
+        }
+}
+
+void
+dh_preferences_show_dialog (GtkWindow *parent)
+{
+        gchar      *path;
+	GtkBuilder *builder;
+	gboolean    use_system_fonts;
+	gchar      *var_font_name, *fixed_font_name;
+
+        preferences_init ();
+
+	if (prefs->dialog != NULL) {
+		gtk_window_present (GTK_WINDOW (prefs->dialog));
+		return;
+	}
+
+        path = dh_util_build_data_filename ("devhelp", "ui",
+                                            "devhelp.builder",
+                                            NULL);
+	builder = dh_util_builder_get_file (
+                path,
+                "preferences_dialog",
+                NULL,
+                "preferences_dialog", &prefs->dialog,
+                "fonts_table", &prefs->fonts_table,
+                "system_fonts_button", &prefs->system_fonts_button,
+                "variable_font_button", &prefs->variable_font_button,
+                "fixed_font_button", &prefs->fixed_font_button,
+                "book_manager_store", &prefs->booklist_store,
+                "book_manager_treeview", &prefs->booklist_treeview,
+                NULL);
+        g_free (path);
+
+	dh_util_builder_connect (
+                builder,
+                prefs,
+                "variable_font_button", "font_set", preferences_fonts_font_set_cb,
+                "fixed_font_button", "font_set", preferences_fonts_font_set_cb,
+                "system_fonts_button", "toggled", preferences_fonts_system_fonts_toggled_cb,
+                "book_manager_toggle", "toggled", preferences_bookshelf_tree_selection_toggled_cb,
+                "preferences_close_button", "clicked", preferences_close_cb,
+                NULL);
+
+	ige_conf_get_bool (ige_conf_get (),
+                           DH_CONF_USE_SYSTEM_FONTS,
+                           &use_system_fonts);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (prefs->system_fonts_button),
+				      use_system_fonts);
+	gtk_widget_set_sensitive (prefs->fonts_table, !use_system_fonts);
+
+	preferences_fonts_get_font_names (FALSE, &var_font_name, &fixed_font_name);
+
+	if (var_font_name) {
+		gtk_font_button_set_font_name (GTK_FONT_BUTTON (prefs->variable_font_button),
+					       var_font_name);
+		g_free (var_font_name);
+	}
+
+	if (fixed_font_name) {
+		gtk_font_button_set_font_name (GTK_FONT_BUTTON (prefs->fixed_font_button),
+					       fixed_font_name);
+		g_free (fixed_font_name);
+	}
+
+        preferences_bookshelf_populate_store ();
+
+	g_object_unref (builder);
+
+	gtk_window_set_transient_for (GTK_WINDOW (prefs->dialog), parent);
+	gtk_widget_show_all (prefs->dialog);
+}


Modified: devhelp/devhelp/dh-preferences.h
34 files changed, 34 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004-2008 Imendio AB
+ * Copyright (C) 2010 Lanedo GmbH
+ *
+ * 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.
+ */
+
+#ifndef __DH_PREFERENCES_H__
+#define __DH_PREFERENCES_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void dh_preferences_show_dialog (GtkWindow *parent);
+
+G_END_DECLS
+
+#endif /* __DH_PREFERENCES_H__ */
+


Modified: devhelp/devhelp/dh-search.c
725 files changed, 725 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,725 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003 CodeFactory AB
+ * Copyright (C) 2001-2003 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2005-2008 Imendio AB
+ *
+ * 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 <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include "dh-marshal.h"
+#include "dh-keyword-model.h"
+#include "dh-search.h"
+#include "dh-preferences.h"
+#include "dh-base.h"
+#include "dh-util.h"
+#include "dh-book-manager.h"
+#include "dh-book.h"
+
+typedef struct {
+        DhKeywordModel *model;
+
+        DhBookManager  *book_manager;
+
+        DhLink         *selected_link;
+
+        GtkWidget      *book_combo;
+        GtkWidget      *entry;
+        GtkWidget      *hitlist;
+
+        GCompletion    *completion;
+
+        guint           idle_complete;
+        guint           idle_filter;
+} DhSearchPriv;
+
+static void         dh_search_init                  (DhSearch         *search);
+static void         dh_search_class_init            (DhSearchClass    *klass);
+static void         search_grab_focus               (GtkWidget        *widget);
+static void         search_selection_changed_cb     (GtkTreeSelection *selection,
+                                                     DhSearch         *content);
+static gboolean     search_tree_button_press_cb     (GtkTreeView      *view,
+                                                     GdkEventButton   *event,
+                                                     DhSearch         *search);
+static gboolean     search_entry_key_press_event_cb (GtkEntry         *entry,
+                                                     GdkEventKey      *event,
+                                                     DhSearch         *search);
+static void         search_combo_changed_cb         (GtkComboBox      *combo,
+                                                     DhSearch         *search);
+static void         search_entry_changed_cb         (GtkEntry         *entry,
+                                                     DhSearch         *search);
+static void         search_entry_activated_cb       (GtkEntry         *entry,
+                                                     DhSearch         *search);
+static void         search_entry_text_inserted_cb   (GtkEntry         *entry,
+                                                     const gchar      *text,
+                                                     gint              length,
+                                                     gint             *position,
+                                                     DhSearch         *search);
+static gboolean     search_complete_idle            (DhSearch         *search);
+static gboolean     search_filter_idle              (DhSearch         *search);
+static const gchar *search_complete_func            (DhLink           *link);
+
+enum {
+        LINK_SELECTED,
+        LAST_SIGNAL
+};
+
+G_DEFINE_TYPE (DhSearch, dh_search, GTK_TYPE_VBOX);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, DH_TYPE_SEARCH, DhSearchPriv);
+
+static gint signals[LAST_SIGNAL] = { 0 };
+
+static void
+search_finalize (GObject *object)
+{
+        DhSearchPriv *priv;
+
+        priv = GET_PRIVATE (object);
+
+        g_completion_free (priv->completion);
+        g_object_unref (priv->book_manager);
+
+        G_OBJECT_CLASS (dh_search_parent_class)->finalize (object);
+}
+
+static void
+dh_search_class_init (DhSearchClass *klass)
+{
+        GObjectClass   *object_class = (GObjectClass *) klass;;
+        GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;;
+
+        object_class->finalize = search_finalize;
+
+        widget_class->grab_focus = search_grab_focus;
+
+        signals[LINK_SELECTED] =
+                g_signal_new ("link_selected",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (DhSearchClass, link_selected),
+                              NULL, NULL,
+                              _dh_marshal_VOID__POINTER,
+                              G_TYPE_NONE,
+                              1, G_TYPE_POINTER);
+
+        g_type_class_add_private (klass, sizeof (DhSearchPriv));
+}
+
+static void
+dh_search_init (DhSearch *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+
+        priv->completion = g_completion_new (
+                (GCompletionFunc) search_complete_func);
+
+        priv->hitlist = gtk_tree_view_new ();
+        priv->model = dh_keyword_model_new ();
+
+        gtk_tree_view_set_model (GTK_TREE_VIEW (priv->hitlist),
+                                 GTK_TREE_MODEL (priv->model));
+
+        gtk_tree_view_set_enable_search (GTK_TREE_VIEW (priv->hitlist), FALSE);
+
+        gtk_box_set_spacing (GTK_BOX (search), 4);
+}
+
+static void
+search_grab_focus (GtkWidget *widget)
+{
+        DhSearchPriv *priv = GET_PRIVATE (widget);
+
+        gtk_widget_grab_focus (priv->entry);
+}
+
+static void
+search_selection_changed_cb (GtkTreeSelection *selection,
+                             DhSearch         *search)
+{
+        DhSearchPriv *priv;
+        GtkTreeIter   iter;
+
+        priv = GET_PRIVATE (search);
+
+        if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+                DhLink *link;
+
+                gtk_tree_model_get (GTK_TREE_MODEL (priv->model), &iter,
+                                    DH_KEYWORD_MODEL_COL_LINK, &link,
+                                    -1);
+
+                if (link != priv->selected_link) {
+                        priv->selected_link = link;
+                        g_signal_emit (search, signals[LINK_SELECTED], 0, link);
+                }
+        }
+}
+
+/* Make it possible to jump back to the currently selected item, useful when the
+ * html view has been scrolled away.
+ */
+static gboolean
+search_tree_button_press_cb (GtkTreeView    *view,
+                             GdkEventButton *event,
+                             DhSearch       *search)
+{
+        GtkTreePath  *path;
+        GtkTreeIter   iter;
+        DhSearchPriv *priv;
+        DhLink       *link;
+
+        priv = GET_PRIVATE (search);
+
+        gtk_tree_view_get_path_at_pos (view, event->x, event->y, &path,
+                                       NULL, NULL, NULL);
+        if (!path) {
+                return FALSE;
+        }
+
+        gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path);
+        gtk_tree_path_free (path);
+
+        gtk_tree_model_get (GTK_TREE_MODEL (priv->model),
+                            &iter,
+                            DH_KEYWORD_MODEL_COL_LINK, &link,
+                            -1);
+
+        priv->selected_link = link;
+
+        g_signal_emit (search, signals[LINK_SELECTED], 0, link);
+
+        /* Always return FALSE so the tree view gets the event and can update
+         * the selection etc.
+         */
+        return FALSE;
+}
+
+static gboolean
+search_entry_key_press_event_cb (GtkEntry    *entry,
+                                 GdkEventKey *event,
+                                 DhSearch    *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+
+        if (event->keyval == GDK_Tab) {
+                if (event->state & GDK_CONTROL_MASK) {
+                        gtk_widget_grab_focus (priv->hitlist);
+                } else {
+                        gtk_editable_set_position (GTK_EDITABLE (entry), -1);
+                        gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
+                }
+                return TRUE;
+        }
+
+        if (event->keyval == GDK_Return ||
+            event->keyval == GDK_KP_Enter) {
+                GtkTreeIter  iter;
+                DhLink      *link;
+                gchar       *name;
+
+                /* Get the first entry found. */
+                if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->model), &iter)) {
+                        gtk_tree_model_get (GTK_TREE_MODEL (priv->model),
+                                            &iter,
+                                            DH_KEYWORD_MODEL_COL_LINK, &link,
+                                            DH_KEYWORD_MODEL_COL_NAME, &name,
+                                            -1);
+
+                        gtk_entry_set_text (GTK_ENTRY (entry), name);
+                        g_free (name);
+
+                        gtk_editable_set_position (GTK_EDITABLE (entry), -1);
+                        gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
+
+                        g_signal_emit (search, signals[LINK_SELECTED], 0, link);
+
+                        return TRUE;
+                }
+        }
+
+        return FALSE;
+}
+
+static void
+search_combo_set_active_id (DhSearch    *search,
+                            const gchar *book_id)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+        GtkTreeIter   iter;
+        GtkTreeModel *model;
+        gboolean      has_next;
+
+        g_signal_handlers_block_by_func (priv->book_combo,
+                                         search_combo_changed_cb,
+                                         search);
+
+        if (book_id != NULL) {
+                model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->book_combo));
+
+                has_next = gtk_tree_model_get_iter_first (model, &iter);
+                while (has_next) {
+                        gchar *id;
+
+                        gtk_tree_model_get (model, &iter,
+                                            1, &id,
+                                            -1);
+
+                        if (id && strcmp (book_id, id) == 0) {
+                                g_free (id);
+
+                                gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->book_combo),
+                                                               &iter);
+                                break;
+                        }
+
+                        g_free (id);
+
+                        has_next = gtk_tree_model_iter_next (model, &iter);
+                }
+        } else {
+                gtk_combo_box_set_active (GTK_COMBO_BOX (priv->book_combo), 0);
+        }
+
+        g_signal_handlers_unblock_by_func (priv->book_combo,
+                                           search_combo_changed_cb,
+                                           search);
+}
+
+static gchar *
+search_combo_get_active_id (DhSearch *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+        GtkTreeIter   iter;
+        GtkTreeModel *model;
+        gchar        *id;
+
+        if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->book_combo),
+                                            &iter)) {
+                return NULL;
+        }
+
+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->book_combo));
+
+        gtk_tree_model_get (model, &iter,
+                            1, &id,
+                            -1);
+
+        return id;
+}
+
+static void
+search_combo_changed_cb (GtkComboBox *combo,
+                         DhSearch    *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+
+        if (!priv->idle_filter) {
+                priv->idle_filter =
+                        g_idle_add ((GSourceFunc) search_filter_idle, search);
+        }
+}
+
+static void
+search_entry_changed_cb (GtkEntry *entry,
+                         DhSearch *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+
+        if (!priv->idle_filter) {
+                priv->idle_filter =
+                        g_idle_add ((GSourceFunc) search_filter_idle, search);
+        }
+}
+
+static void
+search_entry_activated_cb (GtkEntry *entry,
+                           DhSearch *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+        gchar        *id;
+        const gchar  *str;
+
+        id = search_combo_get_active_id (search);
+        str = gtk_entry_get_text (GTK_ENTRY (priv->entry));
+        dh_keyword_model_filter (priv->model, str, id);
+        g_free (id);
+}
+
+static void
+search_entry_text_inserted_cb (GtkEntry    *entry,
+                               const gchar *text,
+                               gint         length,
+                               gint        *position,
+                               DhSearch    *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+
+        if (!priv->idle_complete) {
+                priv->idle_complete =
+                        g_idle_add ((GSourceFunc) search_complete_idle,
+                                    search);
+        }
+}
+
+static gboolean
+search_complete_idle (DhSearch *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+        const gchar  *str;
+        gchar        *completed = NULL;
+        gsize         length;
+
+        str = gtk_entry_get_text (GTK_ENTRY (priv->entry));
+
+        g_completion_complete (priv->completion, str, &completed);
+        if (completed) {
+                length = strlen (str);
+
+                gtk_entry_set_text (GTK_ENTRY (priv->entry), completed);
+                gtk_editable_set_position (GTK_EDITABLE (priv->entry), length);
+                gtk_editable_select_region (GTK_EDITABLE (priv->entry),
+                                            length, -1);
+                g_free (completed);
+        }
+
+        priv->idle_complete = 0;
+
+        return FALSE;
+}
+
+static gboolean
+search_filter_idle (DhSearch *search)
+{
+        DhSearchPriv *priv = GET_PRIVATE (search);
+        const gchar  *str;
+        gchar        *id;
+        DhLink       *link;
+
+        str = gtk_entry_get_text (GTK_ENTRY (priv->entry));
+        id = search_combo_get_active_id (search);
+        link = dh_keyword_model_filter (priv->model, str, id);
+        g_free (id);
+
+        priv->idle_filter = 0;
+
+        if (link) {
+                g_signal_emit (search, signals[LINK_SELECTED], 0, link);
+        }
+
+        return FALSE;
+}
+
+static const gchar *
+search_complete_func (DhLink *link)
+{
+        return dh_link_get_name (link);
+}
+
+static void
+search_cell_data_func (GtkTreeViewColumn *tree_column,
+                       GtkCellRenderer   *cell,
+                       GtkTreeModel      *tree_model,
+                       GtkTreeIter       *iter,
+                       gpointer           data)
+{
+        DhLink       *link;
+        PangoStyle    style;
+
+        gtk_tree_model_get (tree_model, iter,
+                            DH_KEYWORD_MODEL_COL_LINK, &link,
+                            -1);
+
+        style = PANGO_STYLE_NORMAL;
+
+        if (dh_link_get_flags (link) & DH_LINK_FLAGS_DEPRECATED) {
+                style |= PANGO_STYLE_ITALIC;
+        }
+
+        g_object_set (cell,
+                      "text", dh_link_get_name (link),
+                      "style", style,
+                      NULL);
+}
+
+static gboolean
+search_combo_row_separator_func (GtkTreeModel *model,
+                                 GtkTreeIter  *iter,
+                                 gpointer      data)
+{
+        char *label;
+        char *link;
+        gboolean result;
+
+        gtk_tree_model_get (model, iter, 0, &label, 1, &link, -1);
+
+        result = (link == NULL && label == NULL);
+        g_free (label);
+        g_free (link);
+
+        return result;
+}
+
+static void
+search_combo_populate (DhSearch *search)
+{
+        DhSearchPriv *priv;
+        GtkListStore *store;
+        GtkTreeIter   iter;
+        GList        *l;
+
+        priv = GET_PRIVATE (search);
+
+        store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->book_combo)));
+
+        gtk_list_store_clear (store);
+
+        gtk_list_store_append (store, &iter);
+        gtk_list_store_set (store, &iter,
+                            0, _("All books"),
+                            1, NULL,
+                            -1);
+
+        /* Add a separator */
+        gtk_list_store_append (store, &iter);
+        gtk_list_store_set (store, &iter,
+                            0, NULL,
+                            1, NULL,
+                            -1);
+
+        for (l = dh_book_manager_get_books (priv->book_manager);
+             l;
+             l = g_list_next (l)) {
+                DhBook *book = DH_BOOK (l->data);
+                GNode  *node;
+
+                node = dh_book_get_tree (book);
+                if (node) {
+                        DhLink *link;
+
+                        link = node->data;
+
+                        gtk_list_store_append (store, &iter);
+                        gtk_list_store_set (store, &iter,
+                                            0, dh_link_get_name (link),
+                                            1, dh_link_get_book_id (link),
+                                            -1);
+                }
+        }
+
+        gtk_combo_box_set_active (GTK_COMBO_BOX (priv->book_combo), 0);
+}
+
+
+static void
+search_combo_create (DhSearch *search)
+{
+        GtkListStore    *store;
+        GtkCellRenderer *cell;
+        DhSearchPriv    *priv;
+
+        priv = GET_PRIVATE (search);
+
+        store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+        priv->book_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
+        g_object_unref (store);
+
+        search_combo_populate (search);
+
+        gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (priv->book_combo),
+                                              search_combo_row_separator_func,
+                                              NULL, NULL);
+
+        cell = gtk_cell_renderer_text_new ();
+        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->book_combo),
+                                    cell,
+                                    TRUE);
+        gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->book_combo),
+                                       cell,
+                                       "text", 0);
+}
+
+static void
+completion_add_items (DhSearch *search)
+{
+        DhSearchPriv *priv;
+        GList        *l;
+
+        priv = GET_PRIVATE (search);
+
+        for (l = dh_book_manager_get_books (priv->book_manager);
+             l;
+             l = g_list_next (l)) {
+                DhBook *book = DH_BOOK (l->data);
+                GList  *keywords;
+
+                keywords = dh_book_get_keywords(book);
+
+                if (keywords) {
+                        g_completion_add_items (priv->completion,
+                                                keywords);
+                }
+        }
+}
+
+static void
+book_manager_disabled_book_list_changed_cb (DhBookManager *book_manager,
+                                            gpointer user_data)
+{
+        DhSearch *search = user_data;
+        search_combo_populate (search);
+}
+
+GtkWidget *
+dh_search_new (DhBookManager *book_manager)
+{
+        DhSearch         *search;
+        DhSearchPriv     *priv;
+        GtkTreeSelection *selection;
+        GtkWidget        *list_sw;
+        GtkWidget        *hbox;
+        GtkWidget        *book_label;
+        GtkCellRenderer  *cell;
+
+        search = g_object_new (DH_TYPE_SEARCH, NULL);
+
+        priv = GET_PRIVATE (search);
+
+        priv->book_manager = g_object_ref (book_manager);
+        g_signal_connect (priv->book_manager,
+                          "disabled-book-list-updated",
+                          G_CALLBACK (book_manager_disabled_book_list_changed_cb),
+                          search);
+
+        gtk_container_set_border_width (GTK_CONTAINER (search), 2);
+
+        search_combo_create (search);
+        g_signal_connect (priv->book_combo, "changed",
+                          G_CALLBACK (search_combo_changed_cb),
+                          search);
+
+        book_label = gtk_label_new_with_mnemonic (_("Search in:"));
+        gtk_label_set_mnemonic_widget (GTK_LABEL (book_label), priv->book_combo);
+
+        hbox = gtk_hbox_new (FALSE, 6);
+        gtk_box_pack_start (GTK_BOX (hbox), book_label, FALSE, FALSE, 0);
+        gtk_box_pack_start (GTK_BOX (hbox), priv->book_combo, TRUE, TRUE, 0);
+        gtk_box_pack_start (GTK_BOX (search), hbox, FALSE, FALSE, 0);
+
+        /* Setup the keyword box. */
+        priv->entry = gtk_entry_new ();
+        g_signal_connect (priv->entry, "key-press-event",
+                          G_CALLBACK (search_entry_key_press_event_cb),
+                          search);
+
+        g_signal_connect (priv->hitlist, "button-press-event",
+                          G_CALLBACK (search_tree_button_press_cb),
+                          search);
+
+        g_signal_connect (priv->entry, "changed",
+                          G_CALLBACK (search_entry_changed_cb),
+                          search);
+
+        g_signal_connect (priv->entry, "activate",
+                          G_CALLBACK (search_entry_activated_cb),
+                          search);
+
+        g_signal_connect (priv->entry, "insert-text",
+                          G_CALLBACK (search_entry_text_inserted_cb),
+                          search);
+
+        gtk_box_pack_start (GTK_BOX (search), priv->entry, FALSE, FALSE, 0);
+
+        /* Setup the hitlist */
+        list_sw = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (list_sw), GTK_SHADOW_IN);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (list_sw),
+                                        GTK_POLICY_NEVER,
+                                        GTK_POLICY_AUTOMATIC);
+
+        cell = gtk_cell_renderer_text_new ();
+        g_object_set (cell,
+                      "ellipsize", PANGO_ELLIPSIZE_END,
+                      NULL);
+
+        gtk_tree_view_insert_column_with_data_func (
+                GTK_TREE_VIEW (priv->hitlist),
+                -1,
+                NULL,
+                cell,
+                search_cell_data_func,
+                search, NULL);
+
+        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->hitlist),
+                                           FALSE);
+        gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->hitlist),
+                                         DH_KEYWORD_MODEL_COL_NAME);
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->hitlist));
+
+        g_signal_connect (selection, "changed",
+                          G_CALLBACK (search_selection_changed_cb),
+                          search);
+
+        gtk_container_add (GTK_CONTAINER (list_sw), priv->hitlist);
+
+        gtk_box_pack_end (GTK_BOX (search), list_sw, TRUE, TRUE, 0);
+
+        completion_add_items (search);
+        dh_keyword_model_set_words (priv->model, book_manager);
+
+        gtk_widget_show_all (GTK_WIDGET (search));
+
+        return GTK_WIDGET (search);
+}
+
+void
+dh_search_set_search_string (DhSearch    *search,
+                             const gchar *str,
+                             const gchar *book_id)
+{
+        DhSearchPriv *priv;
+
+        g_return_if_fail (DH_IS_SEARCH (search));
+
+        priv = GET_PRIVATE (search);
+
+        g_signal_handlers_block_by_func (priv->entry,
+                                         search_entry_changed_cb,
+                                         search);
+
+        gtk_entry_set_text (GTK_ENTRY (priv->entry), str);
+
+        gtk_editable_set_position (GTK_EDITABLE (priv->entry), -1);
+        gtk_editable_select_region (GTK_EDITABLE (priv->entry), -1, -1);
+
+        g_signal_handlers_unblock_by_func (priv->entry,
+                                           search_entry_changed_cb,
+                                           search);
+
+        search_combo_set_active_id (search, book_id);
+
+        if (!priv->idle_filter) {
+                priv->idle_filter =
+                        g_idle_add ((GSourceFunc) search_filter_idle, search);
+        }
+}


Modified: devhelp/devhelp/dh-search.h
60 files changed, 60 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2002 CodeFactory AB
+ * Copyright (C) 2001-2002 Mikael Hallendal <micke at imendio.com>
+ *
+ * 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.
+ */
+
+#ifndef __DH_SEARCH_H__
+#define __DH_SEARCH_H__
+
+#include <gtk/gtk.h>
+#include "dh-link.h"
+#include "dh-book-manager.h"
+
+G_BEGIN_DECLS
+
+#define DH_TYPE_SEARCH           (dh_search_get_type ())
+#define DH_SEARCH(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), DH_TYPE_SEARCH, DhSearch))
+#define DH_SEARCH_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), DH_TYPE_SEARCH, DhSearchClass))
+#define DH_IS_SEARCH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DH_TYPE_SEARCH))
+#define DH_IS_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DH_TYPE_SEARCH))
+
+typedef struct _DhSearch      DhSearch;
+typedef struct _DhSearchClass DhSearchClass;
+
+struct _DhSearch {
+        GtkVBox parent_instance;
+};
+
+struct _DhSearchClass {
+        GtkVBoxClass parent_class;
+
+        /* Signals */
+        void (*link_selected) (DhSearch          *search,
+                               DhLink            *link);
+};
+
+GType      dh_search_get_type          (void);
+GtkWidget *dh_search_new               (DhBookManager *book_manager);
+void       dh_search_set_search_string (DhSearch      *search,
+                                        const gchar   *str,
+                                        const gchar   *book_id);
+
+G_END_DECLS
+
+#endif /* __DH_SEARCH_H__ */


Modified: devhelp/devhelp/dh-util.c
812 files changed, 812 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,812 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001      Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2004,2008 Imendio AB
+ *
+ * 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 <stdlib.h>
+#include <gtk/gtk.h>
+#ifdef GDK_WINDOWING_QUARTZ
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+#include "ige-conf.h"
+#include "dh-util.h"
+
+static GList *views;
+
+static GtkBuilder *
+get_builder_file (const gchar *filename,
+                  const gchar *root,
+                  const gchar *domain,
+                  const gchar *first_required_widget,
+                  va_list args)
+{
+        GtkBuilder  *builder;
+        const char  *name;
+        GObject    **object_ptr;
+
+        builder = gtk_builder_new ();
+        if (!gtk_builder_add_from_file (builder, filename, NULL)) {
+                g_warning ("Couldn't find necessary UI file '%s'", filename);
+                g_object_unref (builder);
+                return NULL;
+        }
+
+        for (name = first_required_widget; name; name = va_arg (args, char *)) {
+                object_ptr = va_arg (args, void *);
+                *object_ptr = gtk_builder_get_object (builder, name);
+
+                if (!*object_ptr) {
+                        g_warning ("UI file '%s' is missing widget '%s'.",
+                                   filename, name);
+                        continue;
+                }
+        }
+
+        return builder;
+}
+
+GtkBuilder *
+dh_util_builder_get_file (const gchar *filename,
+                          const gchar *root,
+                          const gchar *domain,
+                          const gchar *first_required_widget,
+                          ...)
+{
+        va_list     args;
+        GtkBuilder *builder;
+
+        va_start (args, first_required_widget);
+        builder = get_builder_file (filename,
+                                    root,
+                                    domain,
+                                    first_required_widget,
+                                    args);
+        va_end (args);
+
+        return builder;
+}
+
+void
+dh_util_builder_connect (GtkBuilder *builder,
+                         gpointer    user_data,
+                         gchar     *first_widget,
+                         ...)
+{
+        va_list      args;
+        const gchar *name;
+        const gchar *signal;
+        GObject     *object;
+        gpointer    *callback;
+
+        va_start (args, first_widget);
+
+        for (name = first_widget; name; name = va_arg (args, char *)) {
+                signal = va_arg (args, void *);
+                callback = va_arg (args, void *);
+
+                object = gtk_builder_get_object (builder, name);
+                if (!object) {
+                        g_warning ("UI file is missing widget '%s', aborting",
+                                   name);
+                        continue;
+                }
+
+                g_signal_connect (object,
+                                  signal,
+                                  G_CALLBACK (callback),
+                                  user_data);
+        }
+
+        va_end (args);
+}
+
+#ifdef GDK_WINDOWING_QUARTZ
+static gchar *
+cf_string_to_utf8 (CFStringRef str)
+{
+  CFIndex  len;
+  gchar   *ret;
+
+  len = CFStringGetMaximumSizeForEncoding (CFStringGetLength (str),
+                                           kCFStringEncodingUTF8) + 1;
+
+  ret = g_malloc (len);
+  ret[len] = '\0';
+
+  if (CFStringGetCString (str, ret, len, kCFStringEncodingUTF8))
+    return ret;
+
+  g_free (ret);
+  return NULL;
+}
+
+static gchar *
+util_get_mac_data_dir (void)
+{
+        const gchar *env;
+        CFBundleRef  cf_bundle;
+        UInt32       type;
+        UInt32       creator;
+        CFURLRef     cf_url;
+        CFStringRef  cf_string;
+        gchar       *ret, *tmp;
+
+        /* The environment variable overrides all. */
+        env = g_getenv ("DEVHELP_DATADIR");
+        if (env) {
+                return g_strdup (env);
+        }
+
+        cf_bundle = CFBundleGetMainBundle ();
+        if (!cf_bundle) {
+                return NULL;
+        }
+
+        /* Only point into the bundle if it's an application. */
+        CFBundleGetPackageInfo (cf_bundle, &type, &creator);
+        if (type != 'APPL') {
+                return NULL;
+        }
+
+        cf_url = CFBundleCopyBundleURL (cf_bundle);
+        cf_string = CFURLCopyFileSystemPath (cf_url, kCFURLPOSIXPathStyle);
+        ret = cf_string_to_utf8 (cf_string);
+        CFRelease (cf_string);
+        CFRelease (cf_url);
+
+        tmp = g_build_filename (ret, "Contents", "Resources", NULL);
+        g_free (ret);
+
+        return tmp;
+}
+#endif
+
+gchar *
+dh_util_build_data_filename (const gchar *first_part,
+                             ...)
+{
+        gchar        *datadir = NULL;
+        va_list       args;
+        const gchar  *part;
+        gchar       **strv;
+        gint          i;
+        gchar        *ret;
+
+        va_start (args, first_part);
+
+#ifdef GDK_WINDOWING_QUARTZ
+        datadir = util_get_mac_data_dir ();
+#endif
+
+        if (datadir == NULL) {
+                datadir = g_strdup (DATADIR);
+        }
+
+        /* 2 = 1 initial component + terminating NULL element. */
+        strv = g_malloc (sizeof (gchar *) * 2);
+        strv[0] = (gchar *) datadir;
+
+        i = 1;
+        for (part = first_part; part; part = va_arg (args, char *), i++) {
+                /* +2 = 1 new element + terminating NULL element. */
+                strv = g_realloc (strv, sizeof (gchar*) * (i + 2));
+                strv[i] = (gchar *) part;
+        }
+
+        strv[i] = NULL;
+        ret = g_build_filenamev (strv);
+        g_free (strv);
+
+        g_free (datadir);
+
+        va_end (args);
+
+        return ret;
+}
+
+typedef struct {
+        gchar *name;
+        guint  timeout_id;
+} DhUtilStateItem;
+
+static void
+util_state_item_free (DhUtilStateItem *item)
+{
+        g_free (item->name);
+        if (item->timeout_id) {
+                g_source_remove (item->timeout_id);
+        }
+        g_slice_free (DhUtilStateItem, item);
+}
+
+static void
+util_state_setup_widget (GtkWidget   *widget,
+                         const gchar *name)
+{
+        DhUtilStateItem *item;
+
+        item = g_slice_new0 (DhUtilStateItem);
+        item->name = g_strdup (name);
+
+        g_object_set_data_full (G_OBJECT (widget),
+                                "dh-util-state",
+                                item,
+                                (GDestroyNotify) util_state_item_free);
+}
+
+static gchar *
+util_state_get_key (const gchar *name,
+                    const gchar *key)
+{
+        return g_strdup_printf ("/apps/devhelp/state/%s/%s", name, key);
+}
+
+static void
+util_state_schedule_save (GtkWidget   *widget,
+                          GSourceFunc  func)
+
+{
+        DhUtilStateItem *item;
+
+        item = g_object_get_data (G_OBJECT (widget), "dh-util-state");
+        if (item->timeout_id) {
+		g_source_remove (item->timeout_id);
+	}
+
+	item->timeout_id = g_timeout_add (500,
+                                          func,
+                                          widget);
+}
+
+static void
+util_state_save_window (GtkWindow   *window,
+                        const gchar *name)
+{
+        gchar          *key;
+        GdkWindowState  state;
+        gboolean        maximized;
+        gint            width, height;
+        gint            x, y;
+
+#if GTK_CHECK_VERSION (2,14,0)
+        state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window)));
+#else
+        state = gdk_window_get_state (GTK_WIDGET (window)->window);
+#endif
+        if (state & GDK_WINDOW_STATE_MAXIMIZED) {
+                maximized = TRUE;
+        } else {
+                maximized = FALSE;
+        }
+
+        key = util_state_get_key (name, "maximized");
+        ige_conf_set_bool (ige_conf_get (), key, maximized);
+        g_free (key);
+
+        /* If maximized don't save the size and position. */
+        if (maximized) {
+                return;
+        }
+
+        gtk_window_get_size (GTK_WINDOW (window), &width, &height);
+
+        key = util_state_get_key (name, "width");
+        ige_conf_set_int (ige_conf_get (), key, width);
+        g_free (key);
+
+        key = util_state_get_key (name, "height");
+        ige_conf_set_int (ige_conf_get (), key, height);
+        g_free (key);
+
+        gtk_window_get_position (GTK_WINDOW (window), &x, &y);
+
+        key = util_state_get_key (name, "x_position");
+        ige_conf_set_int (ige_conf_get (), key, x);
+        g_free (key);
+
+        key = util_state_get_key (name, "y_position");
+        ige_conf_set_int (ige_conf_get (), key, y);
+        g_free (key);
+}
+
+static void
+util_state_restore_window (GtkWindow   *window,
+                           const gchar *name)
+{
+        gchar     *key;
+        gboolean   maximized;
+        gint       width, height;
+        gint       x, y;
+        GdkScreen *screen;
+        gint       max_width, max_height;
+
+        key = util_state_get_key (name, "width");
+        ige_conf_get_int (ige_conf_get (), key, &width);
+        g_free (key);
+
+        key = util_state_get_key (name, "height");
+        ige_conf_get_int (ige_conf_get (), key, &height);
+        g_free (key);
+
+        key = util_state_get_key (name, "x_position");
+        ige_conf_get_int (ige_conf_get (), key, &x);
+        g_free (key);
+
+        key = util_state_get_key (name, "y_position");
+        ige_conf_get_int (ige_conf_get (), key, &y);
+        g_free (key);
+
+        if (width > 1 && height > 1) {
+                screen = gtk_widget_get_screen (GTK_WIDGET (window));
+                max_width = gdk_screen_get_width (screen);
+                max_height = gdk_screen_get_height (screen);
+
+                width = CLAMP (width, 0, max_width);
+                height = CLAMP (height, 0, max_height);
+
+                x = CLAMP (x, 0, max_width - width);
+                y = CLAMP (y, 0, max_height - height);
+
+                gtk_window_set_default_size (window, width, height);
+        }
+
+        gtk_window_move (window, x, y);
+
+        key = util_state_get_key (name, "maximized");
+        ige_conf_get_bool (ige_conf_get (), key, &maximized);
+        g_free (key);
+
+        if (maximized) {
+                gtk_window_maximize (window);
+        }
+}
+
+static gboolean
+util_state_window_timeout_cb (gpointer window)
+{
+        DhUtilStateItem *item;
+
+        item = g_object_get_data (window, "dh-util-state");
+        if (item) {
+                item->timeout_id = 0;
+                util_state_save_window (window, item->name);
+        }
+
+	return FALSE;
+}
+
+static gboolean
+util_state_window_configure_event_cb (GtkWidget         *window,
+                                      GdkEventConfigure *event,
+                                      gpointer           user_data)
+{
+	util_state_schedule_save (window, util_state_window_timeout_cb);
+	return FALSE;
+}
+
+static gboolean
+util_state_paned_timeout_cb (gpointer paned)
+{
+        DhUtilStateItem *item;
+
+        item = g_object_get_data (paned, "dh-util-state");
+        if (item) {
+                gchar *key;
+
+                item->timeout_id = 0;
+
+                key = util_state_get_key (item->name, "position");
+                ige_conf_set_int (ige_conf_get (),
+                                  key,
+                                  gtk_paned_get_position (paned));
+                g_free (key);
+        }
+
+	return FALSE;
+}
+
+static gboolean
+util_state_paned_changed_cb (GtkWidget *paned,
+                             gpointer   user_data)
+{
+	util_state_schedule_save (paned, util_state_paned_timeout_cb);
+	return FALSE;
+}
+
+void
+dh_util_state_manage_window (GtkWindow   *window,
+                             const gchar *name)
+{
+        util_state_setup_widget (GTK_WIDGET (window), name);
+
+        g_signal_connect (window, "configure-event",
+                          G_CALLBACK (util_state_window_configure_event_cb),
+                          NULL);
+
+        util_state_restore_window (window, name);
+}
+
+void
+dh_util_state_manage_paned (GtkPaned    *paned,
+                            const gchar *name)
+{
+        gchar *key;
+        gint   position;
+
+        util_state_setup_widget (GTK_WIDGET (paned), name);
+
+        key = util_state_get_key (name, "position");
+        if (ige_conf_get_int (ige_conf_get (), key, &position)) {
+                gtk_paned_set_position (paned, position);
+        }
+        g_free (key);
+
+        g_signal_connect (paned, "notify::position",
+                          G_CALLBACK (util_state_paned_changed_cb),
+                          NULL);
+}
+
+GSList *
+dh_util_state_load_books_disabled (void)
+{
+        gchar *key;
+        GSList *books_disabled = NULL;
+
+        key = util_state_get_key ("main/contents", "books_disabled");
+        ige_conf_get_string_list (ige_conf_get (), key, &books_disabled);
+        g_free(key);
+
+        return books_disabled;
+}
+
+void
+dh_util_state_store_books_disabled (GSList *books_disabled)
+{
+        gchar *key;
+
+        key = util_state_get_key ("main/contents", "books_disabled");
+        ige_conf_set_string_list (ige_conf_get (), key, books_disabled);
+        g_free(key);
+}
+
+static gboolean
+util_state_notebook_timeout_cb (gpointer notebook)
+{
+        DhUtilStateItem *item;
+
+        item = g_object_get_data (notebook, "dh-util-state");
+        if (item) {
+                GtkWidget   *page;
+                const gchar *page_name;
+
+                item->timeout_id = 0;
+
+                page = gtk_notebook_get_nth_page (
+                        notebook,
+                        gtk_notebook_get_current_page (notebook));
+                page_name = dh_util_state_get_notebook_page_name (page);
+                if (page_name) {
+                        gchar *key;
+
+                        key = util_state_get_key (item->name, "selected_tab");
+                        ige_conf_set_string (ige_conf_get (), key, page_name);
+                        g_free (key);
+                }
+        }
+
+	return FALSE;
+}
+
+static void
+util_state_notebook_switch_page_cb (GtkWidget       *notebook,
+                                    gpointer         page,
+                                    guint            page_num,
+                                    gpointer         user_data)
+{
+	util_state_schedule_save (notebook, util_state_notebook_timeout_cb);
+}
+
+void
+dh_util_state_set_notebook_page_name (GtkWidget   *page,
+                                      const gchar *page_name)
+{
+        g_object_set_data_full (G_OBJECT (page),
+                                "dh-util-state-tab-name",
+                                g_strdup (page_name),
+                                g_free);
+}
+
+const gchar *
+dh_util_state_get_notebook_page_name (GtkWidget *page)
+{
+        return g_object_get_data (G_OBJECT (page),
+                                  "dh-util-state-tab-name");
+}
+
+void
+dh_util_state_manage_notebook (GtkNotebook *notebook,
+                               const gchar *name,
+                               const gchar *default_tab)
+{
+        gchar     *key;
+        gchar     *tab;
+        gint       i;
+
+        util_state_setup_widget (GTK_WIDGET (notebook), name);
+
+        key = util_state_get_key (name, "selected_tab");
+        if (!ige_conf_get_string (ige_conf_get (), key, &tab)) {
+                tab = g_strdup (default_tab);
+        }
+        g_free (key);
+
+        for (i = 0; i < gtk_notebook_get_n_pages (notebook); i++) {
+                GtkWidget   *page;
+                const gchar *page_name;
+
+                page = gtk_notebook_get_nth_page (notebook, i);
+                page_name = dh_util_state_get_notebook_page_name (page);
+                if (page_name && strcmp (page_name, tab) == 0) {
+                        gtk_notebook_set_current_page (notebook, i);
+                        gtk_widget_grab_focus (page);
+                        break;
+                }
+        }
+
+        g_free (tab);
+
+        g_signal_connect (notebook, "switch-page",
+                          G_CALLBACK (util_state_notebook_switch_page_cb),
+                          NULL);
+}
+
+static gboolean
+split_font_string (const gchar  *name_and_size,
+                   gchar       **name,
+                   gdouble      *size)
+{
+	PangoFontDescription *desc;
+	PangoFontMask         mask;
+	gboolean              retval = FALSE;
+
+	desc = pango_font_description_from_string (name_and_size);
+	if (!desc) {
+		return FALSE;
+	}
+
+	mask = (PANGO_FONT_MASK_FAMILY | PANGO_FONT_MASK_SIZE);
+        if ((pango_font_description_get_set_fields (desc) & mask) == mask) {
+		*size = PANGO_PIXELS (pango_font_description_get_size (desc));
+		*name = g_strdup (pango_font_description_get_family (desc));
+		retval = TRUE;
+	}
+
+	pango_font_description_free (desc);
+
+	return retval;
+}
+
+#define DH_CONF_PATH                  "/apps/devhelp"
+#define DH_CONF_USE_SYSTEM_FONTS      DH_CONF_PATH "/ui/use_system_fonts"
+#define DH_CONF_VARIABLE_FONT         DH_CONF_PATH "/ui/variable_font"
+#define DH_CONF_FIXED_FONT            DH_CONF_PATH "/ui/fixed_font"
+#define DH_CONF_SYSTEM_VARIABLE_FONT  "/desktop/gnome/interface/font_name"
+#define DH_CONF_SYSTEM_FIXED_FONT     "/desktop/gnome/interface/monospace_font_name"
+
+void
+dh_util_font_get_variable (gchar    **name,
+                           gdouble   *size,
+                           gboolean   use_system_fonts)
+{
+	IgeConf *conf;
+	gchar   *name_and_size;
+
+	conf = ige_conf_get ();
+
+	if (use_system_fonts) {
+#ifdef GDK_WINDOWING_QUARTZ
+                name_and_size = g_strdup ("Lucida Grande 14");
+#else
+		ige_conf_get_string (conf,
+                                     DH_CONF_SYSTEM_VARIABLE_FONT,
+                                     &name_and_size);
+#endif
+	} else {
+		ige_conf_get_string (conf,
+                                     DH_CONF_VARIABLE_FONT,
+                                     &name_and_size);
+	}
+
+        if (!split_font_string (name_and_size, name, size)) {
+                *name = g_strdup ("sans");
+                *size = 12;
+        }
+
+        g_free (name_and_size);
+}
+
+void
+dh_util_font_get_fixed (gchar    **name,
+                        gdouble   *size,
+                        gboolean   use_system_fonts)
+{
+	IgeConf *conf;
+	gchar   *name_and_size;
+
+	conf = ige_conf_get ();
+
+	if (use_system_fonts) {
+#ifdef GDK_WINDOWING_QUARTZ
+                name_and_size = g_strdup ("Monaco 14");
+#else
+		ige_conf_get_string (conf,
+                                     DH_CONF_SYSTEM_FIXED_FONT,
+                                     &name_and_size);
+#endif
+	} else {
+		ige_conf_get_string (conf,
+                                     DH_CONF_FIXED_FONT,
+                                     &name_and_size);
+	}
+
+        if (!split_font_string (name_and_size, name, size)) {
+                *name = g_strdup ("monospace");
+                *size = 12;
+        }
+
+        g_free (name_and_size);
+}
+
+static void
+view_destroy_cb (GtkWidget *view,
+                 gpointer   user_data)
+{
+        views = g_list_remove (views, view);
+}
+
+static void
+view_setup_fonts (WebKitWebView *view)
+{
+        IgeConf           *conf;
+        WebKitWebSettings *settings;
+        gboolean           use_system_fonts;
+	gchar             *variable_name;
+	gdouble            variable_size;
+	gchar             *fixed_name;
+	gdouble            fixed_size;
+
+        conf = ige_conf_get ();
+
+        settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+
+	ige_conf_get_bool (conf,
+                           DH_CONF_USE_SYSTEM_FONTS,
+                           &use_system_fonts);
+
+        dh_util_font_get_variable (&variable_name, &variable_size,
+                                   use_system_fonts);
+        dh_util_font_get_fixed (&fixed_name, &fixed_size,
+                                   use_system_fonts);
+
+        g_object_set (settings,
+                      "monospace-font-family", fixed_name,
+                      "default-monospace-font-size", (guint) fixed_size,
+                      "sans-serif-font-family", variable_name,
+                      "serif-font-family", variable_name,
+                      "default-font-size", (guint) variable_size,
+                      NULL);
+
+        g_free (variable_name);
+        g_free (fixed_name);
+}
+
+static void
+font_notify_cb (IgeConf     *conf,
+                const gchar *path,
+                gpointer     user_data)
+{
+        GList *l;
+
+        for (l = views; l; l = l->next) {
+                view_setup_fonts (l->data);
+        }
+}
+
+void
+dh_util_font_add_web_view (WebKitWebView *view)
+{
+        static gboolean setup;
+
+        if (!setup) {
+                IgeConf *conf;
+
+                conf = ige_conf_get ();
+
+		ige_conf_notify_add (conf,
+                                     DH_CONF_USE_SYSTEM_FONTS,
+                                     font_notify_cb,
+                                     NULL);
+		ige_conf_notify_add (conf,
+                                     DH_CONF_SYSTEM_VARIABLE_FONT,
+                                     font_notify_cb,
+                                     NULL);
+		ige_conf_notify_add (conf,
+                                     DH_CONF_SYSTEM_FIXED_FONT,
+                                     font_notify_cb,
+                                     NULL);
+		ige_conf_notify_add (conf,
+                                     DH_CONF_VARIABLE_FONT,
+                                     font_notify_cb,
+                                     NULL);
+		ige_conf_notify_add (conf,
+                                     DH_CONF_FIXED_FONT,
+                                     font_notify_cb,
+                                     NULL);
+
+                setup = TRUE;
+        }
+
+        views = g_list_prepend (views, view);
+
+        g_signal_connect (view, "destroy",
+                          G_CALLBACK (view_destroy_cb),
+                          NULL);
+
+        view_setup_fonts (view);
+}
+
+gint
+dh_util_cmp_book (DhLink *a, DhLink *b)
+{
+        const gchar *name_a;
+        const gchar *name_b;
+        gchar       *name_a_casefold;
+        gchar       *name_b_casefold;
+        int          rc;
+
+        name_a = dh_link_get_name (a);
+        if (!name_a) {
+                name_a = "";
+        }
+
+        name_b = dh_link_get_name (b);
+        if (!name_b) {
+                name_b = "";
+        }
+
+        if (g_ascii_strncasecmp (name_a, "the ", 4) == 0) {
+                name_a += 4;
+        }
+        if (g_ascii_strncasecmp (name_b, "the ", 4) == 0) {
+                name_b += 4;
+        }
+
+        name_a_casefold = g_utf8_casefold (name_a, -1);
+        name_b_casefold = g_utf8_casefold (name_b, -1);
+
+        rc = strcmp (name_a_casefold, name_b_casefold);
+
+        g_free (name_a_casefold);
+        g_free (name_b_casefold);
+
+        return rc;
+}
+


Modified: devhelp/devhelp/dh-util.h
67 files changed, 67 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2004,2008 Imendio AB
+ *
+ * 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.
+ */
+
+#ifndef __DH_UTIL_H__
+#define __DH_UTIL_H__
+
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+#include "dh-link.h"
+
+G_BEGIN_DECLS
+
+GtkBuilder * dh_util_builder_get_file             (const gchar *filename,
+                                                   const gchar *root,
+                                                   const gchar *domain,
+                                                   const gchar *first_required_widget,
+                                                   ...);
+void         dh_util_builder_connect              (GtkBuilder  *gui,
+                                                   gpointer     user_data,
+                                                   gchar       *first_widget,
+                                                   ...);
+gchar *      dh_util_build_data_filename          (const gchar *first_part,
+                                                   ...);
+void         dh_util_state_manage_window          (GtkWindow   *window,
+                                                   const gchar *name);
+void         dh_util_state_manage_paned           (GtkPaned    *paned,
+                                                   const gchar *name);
+void         dh_util_state_manage_notebook        (GtkNotebook *notebook,
+                                                   const gchar *name,
+                                                   const gchar *default_tab);
+void         dh_util_state_set_notebook_page_name (GtkWidget   *page,
+                                                   const gchar *page_name);
+const gchar *dh_util_state_get_notebook_page_name (GtkWidget   *page);
+GSList *     dh_util_state_load_books_disabled    (void);
+void         dh_util_state_store_books_disabled   (GSList *books_disabled);
+
+void         dh_util_font_get_variable            (gchar        **name,
+                                                   gdouble       *size,
+                                                   gboolean       use_system_font);
+void         dh_util_font_get_fixed               (gchar        **name,
+                                                   gdouble       *size,
+                                                   gboolean       use_system_font);
+void         dh_util_font_add_web_view            (WebKitWebView *view);
+gint         dh_util_cmp_book                     (DhLink *a,
+                                                   DhLink *b);
+
+G_END_DECLS
+
+#endif /* __DH_UTIL_H__ */


Modified: devhelp/devhelp/dh-window.c
1982 files changed, 1982 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,1982 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2008 Imendio AB
+ *
+ * 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.
+ */
+
+/*
+ * Fullscreen mode code adapted from gedit
+ *  Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
+ *  Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
+ *  Copyright (C) 2002-2005 Paolo Maggi
+ */
+
+#include "config.h"
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+
+#ifdef GDK_WINDOWING_QUARTZ
+#include <ige-mac-integration.h>
+#endif
+
+#include "dh-book-tree.h"
+#include "dh-book-manager.h"
+#include "dh-book.h"
+#include "dh-preferences.h"
+#include "dh-search.h"
+#include "dh-window.h"
+#include "dh-util.h"
+#include "dh-marshal.h"
+#include "dh-enum-types.h"
+#include "eggfindbar.h"
+#include "ige-conf.h"
+
+#define FULLSCREEN_ANIMATION_SPEED 4
+
+struct _DhWindowPriv {
+        DhBase         *base;
+
+        GtkWidget      *main_box;
+        GtkWidget      *menu_box;
+        GtkWidget      *hpaned;
+        GtkWidget      *control_notebook;
+        GtkWidget      *book_tree;
+        GtkWidget      *search;
+        GtkWidget      *notebook;
+
+        GtkWidget      *vbox;
+        GtkWidget      *findbar;
+
+        GtkWidget      *fullscreen_controls;
+        guint           fullscreen_animation_timeout_id;
+        gboolean        fullscreen_animation_enter;
+
+        GtkUIManager   *manager;
+        GtkActionGroup *action_group;
+
+        DhLink         *selected_search_link;
+        guint           find_source_id;
+};
+
+enum {
+        OPEN_LINK,
+        LAST_SIGNAL
+};
+
+static gint signals[LAST_SIGNAL] = { 0 };
+
+static guint tab_accel_keys[] = {
+        GDK_1, GDK_2, GDK_3, GDK_4, GDK_5,
+        GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
+};
+
+static const
+struct
+{
+        gchar *name;
+        int    level;
+}
+zoom_levels[] =
+{
+        { N_("50%"), 70 },
+        { N_("75%"), 84 },
+        { N_("100%"), 100 },
+        { N_("125%"), 119 },
+        { N_("150%"), 141 },
+        { N_("175%"), 168 },
+        { N_("200%"), 200 },
+        { N_("300%"), 283 },
+        { N_("400%"), 400 }
+};
+
+#define ZOOM_MINIMAL    (zoom_levels[0].level)
+#define ZOOM_MAXIMAL    (zoom_levels[8].level)
+#define ZOOM_DEFAULT    (zoom_levels[2].level)
+
+#if GTK_CHECK_VERSION (2,17,5)
+#define ERRORS_IN_INFOBAR
+#endif
+
+static void           dh_window_class_init           (DhWindowClass   *klass);
+static void           dh_window_init                 (DhWindow        *window);
+static void           window_populate                (DhWindow        *window);
+static void           window_tree_link_selected_cb   (GObject         *ignored,
+                                                      DhLink          *link,
+                                                      DhWindow        *window);
+static void           window_search_link_selected_cb (GObject         *ignored,
+                                                      DhLink          *link,
+                                                      DhWindow        *window);
+static void           window_check_history           (DhWindow        *window,
+                                                      WebKitWebView   *web_view);
+static void           window_web_view_tab_accel_cb   (GtkAccelGroup   *accel_group,
+                                                      GObject         *object,
+                                                      guint            key,
+                                                      GdkModifierType  mod,
+                                                      DhWindow        *window);
+static void           window_find_search_changed_cb  (GObject         *object,
+                                                      GParamSpec      *arg1,
+                                                      DhWindow        *window);
+static void           window_find_case_changed_cb    (GObject         *object,
+                                                      GParamSpec      *arg1,
+                                                      DhWindow        *window);
+static void           window_find_previous_cb        (GtkEntry        *entry,
+                                                      DhWindow        *window);
+static void           window_find_next_cb            (GtkEntry        *entry,
+                                                      DhWindow        *window);
+static void           window_findbar_close_cb        (GtkWidget       *widget,
+                                                      DhWindow        *window);
+static GtkWidget *    window_new_tab_label           (DhWindow        *window,
+                                                      const gchar     *label,
+                                                      const GtkWidget *parent);
+static int            window_open_new_tab            (DhWindow        *window,
+                                                      const gchar     *location,
+                                                      gboolean         switch_focus);
+static WebKitWebView *window_get_active_web_view     (DhWindow        *window);
+#ifdef ERRORS_IN_INFOBAR
+static GtkWidget *    window_get_active_info_bar     (DhWindow *window);
+#endif
+static void           window_update_title            (DhWindow        *window,
+                                                      WebKitWebView   *web_view,
+                                                      const gchar     *title);
+static void           window_tab_set_title           (DhWindow        *window,
+                                                      WebKitWebView   *web_view,
+                                                      const gchar     *title);
+static void           window_close_tab               (DhWindow *window,
+                                                      gint      page_num);
+
+G_DEFINE_TYPE (DhWindow, dh_window, GTK_TYPE_WINDOW);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, DH_TYPE_WINDOW, DhWindowPriv);
+
+static void
+window_activate_new_window (GtkAction *action,
+                            DhWindow  *window)
+{
+        DhWindowPriv *priv;
+        GtkWidget    *new_window;
+
+        priv = window->priv;
+
+        new_window = dh_base_new_window (priv->base);
+        gtk_widget_show (new_window);
+}
+
+static void
+window_activate_new_tab (GtkAction *action,
+                         DhWindow  *window)
+{
+        window_open_new_tab (window, NULL, TRUE);
+}
+
+static void
+window_activate_print (GtkAction *action,
+                       DhWindow  *window)
+{
+    WebKitWebView *web_view;
+
+    web_view = window_get_active_web_view (window);
+    webkit_web_view_execute_script (web_view, "print();");
+}
+
+static void
+window_close_tab (DhWindow *window,
+                  gint      page_num)
+{
+        DhWindowPriv *priv;
+        gint          pages;
+
+        priv = window->priv;
+
+        gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), page_num);
+
+        pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
+
+        if (pages == 0) {
+                gtk_widget_destroy (GTK_WIDGET (window));
+        }
+        else if (pages == 1) {
+                gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
+        }
+}
+
+static void
+window_activate_close (GtkAction *action,
+                       DhWindow  *window)
+{
+        gint          page_num;
+
+        page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->priv->notebook));
+        window_close_tab (window, page_num);
+}
+
+static void
+window_activate_quit (GtkAction *action,
+                      DhWindow  *window)
+{
+        dh_base_quit (window->priv->base);
+}
+
+static void
+window_activate_copy (GtkAction *action,
+                      DhWindow  *window)
+{
+        GtkWidget *widget;
+        DhWindowPriv  *priv;
+
+        priv = window->priv;
+
+        widget = gtk_window_get_focus (GTK_WINDOW (window));
+
+        if (GTK_IS_EDITABLE (widget)) {
+                gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
+        } else if (GTK_IS_TREE_VIEW (widget) &&
+                   gtk_widget_is_ancestor (widget, priv->search) &&
+                   priv->selected_search_link) {
+                GtkClipboard *clipboard;
+                clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
+                gtk_clipboard_set_text (clipboard,
+                                dh_link_get_name(priv->selected_search_link), -1);
+        } else {
+                WebKitWebView *web_view;
+
+                web_view = window_get_active_web_view (window);
+                webkit_web_view_copy_clipboard (web_view);
+        }
+}
+
+static void
+window_activate_find (GtkAction *action,
+                      DhWindow  *window)
+{
+        DhWindowPriv  *priv;
+        WebKitWebView *web_view;
+
+        priv = window->priv;
+        web_view = window_get_active_web_view (window);
+
+        gtk_widget_show (priv->findbar);
+        gtk_widget_grab_focus (priv->findbar);
+
+        webkit_web_view_set_highlight_text_matches (web_view, TRUE);
+}
+
+static int
+window_get_current_zoom_level_index (DhWindow *window)
+{
+        WebKitWebView *web_view;
+        float zoom_level;
+        int zoom_level_as_int = ZOOM_DEFAULT;
+        int i;
+
+        web_view = window_get_active_web_view (window);
+        if (web_view) {
+                g_object_get (web_view, "zoom-level", &zoom_level, NULL);
+                zoom_level_as_int = (int)(zoom_level*100);
+        }
+
+        for (i=0; zoom_levels[i].level != ZOOM_MAXIMAL; i++) {
+                if (zoom_levels[i].level == zoom_level_as_int)
+                        return i;
+        }
+        return i;
+}
+
+static void
+window_update_zoom_actions_sensitiveness (DhWindow *window)
+{
+        DhWindowPriv *priv;
+        GtkAction *zoom_in, *zoom_out, *zoom_default;
+        int zoom_level_idx;
+
+        priv = window->priv;
+        zoom_in = gtk_action_group_get_action (priv->action_group, "ZoomIn");
+        zoom_out = gtk_action_group_get_action (priv->action_group, "ZoomOut");
+        zoom_default = gtk_action_group_get_action (priv->action_group, "ZoomDefault");
+
+        zoom_level_idx = window_get_current_zoom_level_index (window);
+
+        gtk_action_set_sensitive (zoom_in,
+                                  zoom_levels[zoom_level_idx].level < ZOOM_MAXIMAL);
+        gtk_action_set_sensitive (zoom_out,
+                                  zoom_levels[zoom_level_idx].level > ZOOM_MINIMAL);
+        gtk_action_set_sensitive (zoom_default,
+                                  zoom_levels[zoom_level_idx].level != ZOOM_DEFAULT);
+}
+
+static void
+window_activate_zoom_in (GtkAction *action,
+                         DhWindow  *window)
+{
+        int zoom_level_idx;
+
+        zoom_level_idx = window_get_current_zoom_level_index (window);
+        if (zoom_levels[zoom_level_idx].level < ZOOM_MAXIMAL) {
+                WebKitWebView *web_view;
+
+                web_view = window_get_active_web_view (window);
+                g_object_set (web_view,
+                              "zoom-level", (float)(zoom_levels[zoom_level_idx+1].level)/100,
+                              NULL);
+                window_update_zoom_actions_sensitiveness (window);
+        }
+
+}
+
+static void
+window_activate_zoom_out (GtkAction *action,
+                          DhWindow  *window)
+{
+        int zoom_level_idx;
+
+        zoom_level_idx = window_get_current_zoom_level_index (window);
+        if (zoom_levels[zoom_level_idx].level > ZOOM_MINIMAL) {
+                WebKitWebView *web_view;
+
+                web_view = window_get_active_web_view (window);
+                g_object_set (web_view,
+                              "zoom-level", (float)(zoom_levels[zoom_level_idx-1].level)/100,
+                              NULL);
+                window_update_zoom_actions_sensitiveness (window);
+        }
+}
+
+static void
+window_activate_zoom_default (GtkAction *action,
+                              DhWindow  *window)
+{
+        WebKitWebView *web_view;
+
+        web_view = window_get_active_web_view (window);
+        g_object_set (web_view, "zoom-level", (float)(ZOOM_DEFAULT)/100, NULL);
+        window_update_zoom_actions_sensitiveness (window);
+}
+
+static gboolean
+run_fullscreen_animation (gpointer data)
+{
+	DhWindow *window = DH_WINDOW (data);
+	GdkScreen *screen;
+	GdkRectangle fs_rect;
+	gint x, y;
+
+	screen = gtk_window_get_screen (GTK_WINDOW (window));
+	gdk_screen_get_monitor_geometry (screen,
+					 gdk_screen_get_monitor_at_window (screen,
+									   gtk_widget_get_window (GTK_WIDGET (window))),
+					 &fs_rect);
+
+	gtk_window_get_position (GTK_WINDOW (window->priv->fullscreen_controls),
+				 &x, &y);
+
+	if (window->priv->fullscreen_animation_enter)
+	{
+		if (y == fs_rect.y)
+		{
+			window->priv->fullscreen_animation_timeout_id = 0;
+			return FALSE;
+		}
+		else
+		{
+			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
+					 x, y + 1);
+			return TRUE;
+		}
+	}
+	else
+	{
+		gint w, h;
+
+		gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls),
+				     &w, &h);
+
+		if (y == fs_rect.y - h + 1)
+		{
+			window->priv->fullscreen_animation_timeout_id = 0;
+			return FALSE;
+		}
+		else
+		{
+			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
+					 x, y - 1);
+			return TRUE;
+		}
+	}
+}
+
+static void
+show_hide_fullscreen_toolbar (DhWindow *window,
+                              gboolean     show,
+                              gint         height)
+{
+        GtkSettings *settings;
+        gboolean enable_animations;
+
+        settings = gtk_widget_get_settings (GTK_WIDGET (window));
+        g_object_get (G_OBJECT (settings),
+                      "gtk-enable-animations",
+                      &enable_animations,
+                      NULL);
+
+        if (enable_animations)
+        {
+                window->priv->fullscreen_animation_enter = show;
+
+                if (window->priv->fullscreen_animation_timeout_id == 0)
+                {
+                        window->priv->fullscreen_animation_timeout_id =
+                                g_timeout_add (FULLSCREEN_ANIMATION_SPEED,
+                                               (GSourceFunc) run_fullscreen_animation,
+                                               window);
+                }
+        }
+        else
+        {
+                GdkRectangle fs_rect;
+                GdkScreen *screen;
+
+                screen = gtk_window_get_screen (GTK_WINDOW (window));
+                gdk_screen_get_monitor_geometry (screen,
+                                                 gdk_screen_get_monitor_at_window (screen,
+                                                                                   gtk_widget_get_window (GTK_WIDGET (window))),
+                                                 &fs_rect);
+
+                if (show)
+                        gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
+                                 fs_rect.x, fs_rect.y);
+                else
+                        gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
+                                         fs_rect.x, fs_rect.y - height + 1);
+        }
+
+}
+
+
+static gboolean
+on_fullscreen_controls_enter_notify_event (GtkWidget        *widget,
+                                           GdkEventCrossing *event,
+                                           DhWindow      *window)
+{
+        show_hide_fullscreen_toolbar (window, TRUE, 0);
+
+        return FALSE;
+}
+
+static gboolean
+on_fullscreen_controls_leave_notify_event (GtkWidget        *widget,
+                                           GdkEventCrossing *event,
+                                           DhWindow      *window)
+{
+        GdkDisplay *display;
+        GdkScreen *screen;
+        gint w, h;
+        gint x, y;
+
+        display = gdk_display_get_default ();
+        screen = gtk_window_get_screen (GTK_WINDOW (window));
+
+        gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h);
+        gdk_display_get_pointer (display, &screen, &x, &y, NULL);
+
+        /* gtk seems to emit leave notify when clicking on tool items,
+         * work around it by checking the coordinates
+         */
+        if (y >= h)
+        {
+                show_hide_fullscreen_toolbar (window, FALSE, h);
+        }
+
+        return FALSE;
+}
+
+static gboolean
+window_is_fullscreen (DhWindow *window)
+{
+        GdkWindowState  state;
+
+        g_return_val_if_fail (DH_IS_WINDOW (window), FALSE);
+
+#if GTK_CHECK_VERSION (2,14,0)
+        state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window)));
+#else
+        state = gdk_window_get_state (GTK_WIDGET (window)->window);
+#endif
+
+        return state & GDK_WINDOW_STATE_FULLSCREEN;
+}
+
+static void
+window_fullscreen_controls_build (DhWindow *window)
+{
+        GtkWidget *toolbar;
+        GtkAction *action;
+        DhWindowPriv  *priv;
+
+        priv = window->priv;
+        if (priv->fullscreen_controls != NULL)
+                return;
+
+        priv->fullscreen_controls = gtk_window_new (GTK_WINDOW_POPUP);
+        gtk_window_set_transient_for (GTK_WINDOW (priv->fullscreen_controls),
+                                      GTK_WINDOW (window));
+
+        toolbar = gtk_ui_manager_get_widget (priv->manager, "/FullscreenToolBar");
+        gtk_container_add (GTK_CONTAINER (priv->fullscreen_controls),
+                           toolbar);
+        action = gtk_action_group_get_action (priv->action_group,
+                                              "LeaveFullscreen");
+        g_object_set (action, "is-important", TRUE, NULL);
+
+        /* Set the toolbar style */
+        gtk_toolbar_set_style (GTK_TOOLBAR (toolbar),
+                               GTK_TOOLBAR_BOTH_HORIZ);
+
+        g_signal_connect (priv->fullscreen_controls, "enter-notify-event",
+                          G_CALLBACK (on_fullscreen_controls_enter_notify_event),
+                          window);
+        g_signal_connect (priv->fullscreen_controls, "leave-notify-event",
+                          G_CALLBACK (on_fullscreen_controls_leave_notify_event),
+                          window);
+}
+
+static void
+window_fullscreen_controls_show (DhWindow *window)
+{
+        GdkScreen *screen;
+        GdkRectangle fs_rect;
+        gint w, h;
+
+        screen = gtk_window_get_screen (GTK_WINDOW (window));
+        gdk_screen_get_monitor_geometry (screen,
+                        gdk_screen_get_monitor_at_window (
+                                screen,
+                                gtk_widget_get_window (GTK_WIDGET (window))),
+                        &fs_rect);
+
+        gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h);
+
+        gtk_window_resize (GTK_WINDOW (window->priv->fullscreen_controls),
+                           fs_rect.width, h);
+
+        gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
+                         fs_rect.x, fs_rect.y - h + 1);
+
+        gtk_widget_show_all (window->priv->fullscreen_controls);
+}
+
+static void
+window_fullscreen (DhWindow *window)
+{
+        if (window_is_fullscreen (window))
+                return;
+
+        gtk_window_fullscreen (GTK_WINDOW (window));
+        gtk_widget_hide (gtk_ui_manager_get_widget (window->priv->manager, "/MenuBar"));
+        gtk_widget_hide (gtk_ui_manager_get_widget (window->priv->manager, "/Toolbar"));
+
+        window_fullscreen_controls_build (window);
+        window_fullscreen_controls_show (window);
+}
+
+static void
+window_unfullscreen (DhWindow *window)
+{
+        if (! window_is_fullscreen (window))
+                return;
+
+        gtk_window_unfullscreen (GTK_WINDOW (window));
+        gtk_widget_show (gtk_ui_manager_get_widget (window->priv->manager, "/MenuBar"));
+        gtk_widget_show (gtk_ui_manager_get_widget (window->priv->manager, "/Toolbar"));
+
+        gtk_widget_hide (window->priv->fullscreen_controls);
+}
+
+
+static void
+window_toggle_fullscreen_mode (GtkAction *action,
+                               DhWindow  *window)
+{
+        if (window_is_fullscreen (window)) {
+                window_unfullscreen (window);
+        } else {
+                window_fullscreen (window);
+        }
+}
+
+static void
+window_leave_fullscreen_mode (GtkAction *action,
+                              DhWindow *window)
+{
+        GtkAction *view_action;
+
+        view_action = gtk_action_group_get_action (window->priv->action_group,
+                                                   "ViewFullscreen");
+        g_signal_handlers_block_by_func (view_action,
+                        G_CALLBACK (window_toggle_fullscreen_mode),
+                        window);
+        gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (view_action),
+                                      FALSE);
+        window_unfullscreen (window);
+        g_signal_handlers_unblock_by_func (view_action,
+                        G_CALLBACK (window_toggle_fullscreen_mode),
+                        window);
+}
+
+static void
+window_activate_preferences (GtkAction *action,
+                             DhWindow  *window)
+{
+        dh_preferences_show_dialog (GTK_WINDOW (window));
+}
+
+static void
+window_activate_back (GtkAction *action,
+                      DhWindow  *window)
+{
+        DhWindowPriv  *priv;
+        WebKitWebView *web_view;
+        GtkWidget     *frame;
+
+        priv = window->priv;
+
+        frame = gtk_notebook_get_nth_page (
+                GTK_NOTEBOOK (priv->notebook),
+                gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)));
+        web_view = g_object_get_data (G_OBJECT (frame), "web_view");
+
+        webkit_web_view_go_back (web_view);
+}
+
+static void
+window_activate_forward (GtkAction *action,
+                         DhWindow  *window)
+{
+        DhWindowPriv  *priv;
+        WebKitWebView *web_view;
+        GtkWidget     *frame;
+
+        priv = window->priv;
+
+        frame = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook),
+                                           gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook))
+                                          );
+        web_view = g_object_get_data (G_OBJECT (frame), "web_view");
+
+        webkit_web_view_go_forward (web_view);
+}
+
+static void
+window_activate_show_contents (GtkAction *action,
+                               DhWindow  *window)
+{
+        DhWindowPriv *priv;
+
+        priv = window->priv;
+
+        gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 0);
+        gtk_widget_grab_focus (priv->book_tree);
+}
+
+static void
+window_activate_show_search (GtkAction *action,
+                             DhWindow  *window)
+{
+        DhWindowPriv *priv;
+
+        priv = window->priv;
+
+        gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
+        gtk_widget_grab_focus (priv->search);
+}
+
+static void
+window_activate_about (GtkAction *action,
+                       DhWindow  *window)
+{
+        const gchar  *authors[] = {
+                "Mikael Hallendal <micke at imendio.com>",
+                "Richard Hult <richard at imendio.com>",
+                "Johan Dahlin <johan at gnome.org>",
+                "Ross Burton <ross at burtonini.com>",
+                "Aleksander Morgado <aleksander at lanedo.com>",
+                NULL
+        };
+        const gchar **documenters = NULL;
+        const gchar  *translator_credits = _("translator_credits");
+
+        /* i18n: Please don't translate "Devhelp" (it's marked as translatable
+         * for transliteration only) */
+        gtk_show_about_dialog (GTK_WINDOW (window),
+                               "name", _("Devhelp"),
+                               "version", PACKAGE_VERSION,
+                               "comments", _("A developers' help browser for GNOME"),
+                               "authors", authors,
+                               "documenters", documenters,
+                               "translator-credits",
+                               strcmp (translator_credits, "translator_credits") != 0 ?
+                               translator_credits : NULL,
+                               "website", "http://live.gnome.org/devhelp",
+                               "logo-icon-name", "devhelp",
+                               NULL);
+}
+
+static void
+window_open_link_cb (DhWindow *window,
+                     const char *location,
+                     DhOpenLinkFlags flags)
+{
+        DhWindowPriv *priv;
+        priv = window->priv;
+
+        if (flags & DH_OPEN_LINK_NEW_TAB) {
+                window_open_new_tab (window, location, FALSE);
+        }
+        else if (flags & DH_OPEN_LINK_NEW_WINDOW) {
+                GtkWidget *new_window;
+                new_window = dh_base_new_window (priv->base);
+                gtk_widget_show (new_window);
+        }
+}
+
+static const GtkActionEntry actions[] = {
+        { "FileMenu", NULL, N_("_File"), NULL, NULL, NULL },
+        { "EditMenu", NULL, N_("_Edit"), NULL, NULL, NULL },
+        { "ViewMenu", NULL, N_("_View"), NULL, NULL, NULL },
+        { "GoMenu",   NULL, N_("_Go"), NULL, NULL, NULL },
+        { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
+
+        /* File menu */
+        { "NewWindow", GTK_STOCK_NEW, N_("_New Window"), "<control>N", NULL,
+          G_CALLBACK (window_activate_new_window) },
+        { "NewTab", GTK_STOCK_NEW, N_("New _Tab"), "<control>T", NULL,
+          G_CALLBACK (window_activate_new_tab) },
+        { "Print", GTK_STOCK_PRINT, N_("_Print…"), "<control>P", NULL,
+          G_CALLBACK (window_activate_print) },
+        { "Close", GTK_STOCK_CLOSE, NULL, NULL, NULL,
+          G_CALLBACK (window_activate_close) },
+        { "Quit", GTK_STOCK_QUIT, NULL, NULL, NULL,
+          G_CALLBACK (window_activate_quit) },
+
+        /* Edit menu */
+        { "Copy", GTK_STOCK_COPY, NULL, "<control>C", NULL,
+          G_CALLBACK (window_activate_copy) },
+        { "Find", GTK_STOCK_FIND, NULL, "<control>F", NULL,
+          G_CALLBACK (window_activate_find) },
+        { "Find Next", GTK_STOCK_GO_FORWARD, N_("Find Next"), "<control>G", NULL,
+          G_CALLBACK (window_find_next_cb) },
+        { "Find Previous", GTK_STOCK_GO_BACK, N_("Find Previous"), "<shift><control>G", NULL,
+          G_CALLBACK (window_find_previous_cb) },
+        { "Preferences", GTK_STOCK_PREFERENCES, NULL, NULL, NULL,
+          G_CALLBACK (window_activate_preferences) },
+
+        /* Go menu */
+        { "Back", GTK_STOCK_GO_BACK, NULL, "<alt>Left",
+          N_("Go to the previous page"),
+          G_CALLBACK (window_activate_back) },
+        { "Forward", GTK_STOCK_GO_FORWARD, NULL, "<alt>Right",
+          N_("Go to the next page"),
+          G_CALLBACK (window_activate_forward) },
+
+        { "ShowContentsTab", NULL, N_("_Contents Tab"), "<ctrl>B", NULL,
+          G_CALLBACK (window_activate_show_contents) },
+
+        { "ShowSearchTab", NULL, N_("_Search Tab"), "<ctrl>S", NULL,
+          G_CALLBACK (window_activate_show_search) },
+
+        /* View menu */
+        { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("_Larger Text"), "<ctrl>plus",
+          N_("Increase the text size"),
+          G_CALLBACK (window_activate_zoom_in) },
+        { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("S_maller Text"), "<ctrl>minus",
+          N_("Decrease the text size"),
+          G_CALLBACK (window_activate_zoom_out) },
+        { "ZoomDefault", GTK_STOCK_ZOOM_100, N_("_Normal Size"), "<ctrl>0",
+          N_("Use the normal text size"),
+          G_CALLBACK (window_activate_zoom_default) },
+
+        /* About menu */
+        { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL,
+          G_CALLBACK (window_activate_about) },
+
+        /* Fullscreen toolbar */
+        { "LeaveFullscreen", GTK_STOCK_LEAVE_FULLSCREEN, NULL,
+          NULL, N_("Leave fullscreen mode"),
+          G_CALLBACK (window_leave_fullscreen_mode) }
+};
+
+static const GtkToggleActionEntry always_sensitive_toggle_menu_entries[] =
+{
+        { "ViewFullscreen", GTK_STOCK_FULLSCREEN, NULL, "F11",
+          N_("Display in full screen"),
+          G_CALLBACK (window_toggle_fullscreen_mode), FALSE },
+};
+
+static const gchar* important_actions[] = {
+        "Back",
+        "Forward"
+};
+
+static void
+window_finalize (GObject *object)
+{
+        DhWindowPriv *priv = GET_PRIVATE (object);
+
+        g_object_unref (priv->base);
+
+        G_OBJECT_CLASS (dh_window_parent_class)->finalize (object);
+}
+
+static void
+dh_window_class_init (DhWindowClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = window_finalize;
+
+        signals[OPEN_LINK] =
+                g_signal_new ("open-link",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (DhWindowClass, open_link),
+                              NULL, NULL,
+                              _dh_marshal_VOID__STRING_FLAGS,
+                              G_TYPE_NONE,
+                              2,
+                              G_TYPE_STRING,
+                              DH_TYPE_OPEN_LINK_FLAGS);
+
+        gtk_rc_parse_string ("style \"devhelp-tab-close-button-style\"\n"
+                             "{\n"
+                             "GtkWidget::focus-padding = 0\n"
+                             "GtkWidget::focus-line-width = 0\n"
+                             "xthickness = 0\n"
+                             "ythickness = 0\n"
+                             "}\n"
+                             "widget \"*.devhelp-tab-close-button\" "
+                             "style \"devhelp-tab-close-button-style\"");
+
+        g_type_class_add_private (klass, sizeof (DhWindowPriv));
+}
+
+static void
+dh_window_init (DhWindow *window)
+{
+        DhWindowPriv  *priv;
+        GtkAction     *action;
+        GtkAccelGroup *accel_group;
+        GClosure      *closure;
+        guint           i;
+
+        priv = GET_PRIVATE (window);
+        window->priv = priv;
+
+        priv->selected_search_link = NULL;
+
+        priv->manager = gtk_ui_manager_new ();
+
+        accel_group = gtk_ui_manager_get_accel_group (priv->manager);
+        gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
+
+        priv->main_box = gtk_vbox_new (FALSE, 0);
+        gtk_widget_show (priv->main_box);
+
+        priv->menu_box = gtk_vbox_new (FALSE, 0);
+        gtk_widget_show (priv->menu_box);
+        gtk_container_set_border_width (GTK_CONTAINER (priv->menu_box), 0);
+        gtk_box_pack_start (GTK_BOX (priv->main_box), priv->menu_box,
+                            FALSE, TRUE, 0);
+
+        gtk_container_add (GTK_CONTAINER (window), priv->main_box);
+
+        g_signal_connect (window,
+                          "open-link",
+                          G_CALLBACK (window_open_link_cb),
+                          window);
+
+        priv->action_group = gtk_action_group_new ("MainWindow");
+
+        gtk_action_group_set_translation_domain (priv->action_group,
+                                                 GETTEXT_PACKAGE);
+
+        gtk_action_group_add_actions (priv->action_group,
+                                      actions,
+                                      G_N_ELEMENTS (actions),
+                                      window);
+        gtk_action_group_add_toggle_actions (priv->action_group,
+                                             always_sensitive_toggle_menu_entries,
+                                             G_N_ELEMENTS (always_sensitive_toggle_menu_entries),
+                                             window);
+
+        for (i = 0; i < G_N_ELEMENTS (important_actions); i++) {
+                action = gtk_action_group_get_action (priv->action_group,
+                                                      important_actions[i]);
+                g_object_set (action, "is-important", TRUE, NULL);
+        }
+
+        gtk_ui_manager_insert_action_group (priv->manager,
+                                            priv->action_group,
+                                            0);
+
+        action = gtk_action_group_get_action (priv->action_group,
+                                              "Back");
+        g_object_set (action, "sensitive", FALSE, NULL);
+
+        action = gtk_action_group_get_action (priv->action_group,
+                                              "Forward");
+        g_object_set (action, "sensitive", FALSE, NULL);
+
+        action = gtk_action_group_get_action (priv->action_group, "ZoomIn");
+        /* Translators: This refers to text size */
+        g_object_set (action, "short_label", _("Larger"), NULL);
+        action = gtk_action_group_get_action (priv->action_group, "ZoomOut");
+        /* Translators: This refers to text size */
+        g_object_set (action, "short_label", _("Smaller"), NULL);
+
+        accel_group = gtk_accel_group_new ();
+        gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
+
+        for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
+                closure =  g_cclosure_new (G_CALLBACK (window_web_view_tab_accel_cb),
+                                           window,
+                                           NULL);
+                gtk_accel_group_connect (accel_group,
+                                         tab_accel_keys[i],
+                                         GDK_MOD1_MASK,
+                                         0,
+                                         closure);
+        }
+}
+
+/* The ugliest hack. When switching tabs, the selection and cursor is changed
+ * for the tree view so the web_view content is changed. Block the signal during
+ * switch.
+ */
+static void
+window_control_switch_page_cb (GtkWidget       *notebook,
+                               gpointer         page,
+                               guint            page_num,
+                               DhWindow        *window)
+{
+        DhWindowPriv *priv;
+
+        priv = window->priv;
+
+        g_signal_handlers_block_by_func (priv->book_tree,
+                                         window_tree_link_selected_cb,
+                                         window);
+}
+
+static void
+window_control_after_switch_page_cb (GtkWidget       *notebook,
+                                     gpointer         page,
+                                     guint            page_num,
+                                     DhWindow        *window)
+{
+        DhWindowPriv *priv;
+
+        priv = window->priv;
+
+        g_signal_handlers_unblock_by_func (priv->book_tree,
+                                           window_tree_link_selected_cb,
+                                           window);
+}
+
+static void
+window_web_view_switch_page_cb (GtkNotebook     *notebook,
+                                gpointer         page,
+                                guint            new_page_num,
+                                DhWindow        *window)
+{
+        DhWindowPriv *priv;
+        GtkWidget    *new_page;
+
+        priv = window->priv;
+
+        new_page = gtk_notebook_get_nth_page (notebook, new_page_num);
+        if (new_page) {
+                WebKitWebView  *new_web_view;
+                WebKitWebFrame *web_frame;
+                const gchar    *location;
+
+                new_web_view = g_object_get_data (G_OBJECT (new_page), "web_view");
+
+                /* Sync the book tree. */
+                web_frame = webkit_web_view_get_main_frame (new_web_view);
+                location = webkit_web_frame_get_uri (web_frame);
+
+                if (location) {
+                        dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree),
+                                                 location);
+                }
+                window_check_history (window, new_web_view);
+
+                window_update_title (window, new_web_view, NULL);
+        } else {
+                /* i18n: Please don't translate "Devhelp" (it's marked as translatable
+                 * for transliteration only) */
+                gtk_window_set_title (GTK_WINDOW (window), _("Devhelp"));
+                window_check_history (window, NULL);
+        }
+}
+
+static void
+window_web_view_switch_page_after_cb (GtkNotebook     *notebook,
+                                      gpointer         page,
+                                      guint            new_page_num,
+                                      DhWindow        *window)
+{
+        window_update_zoom_actions_sensitiveness (window);
+}
+
+static void
+window_populate (DhWindow *window)
+{
+        DhWindowPriv  *priv;
+        gchar         *path;
+        GtkWidget     *book_tree_sw;
+        DhBookManager *book_manager;
+        GtkWidget     *menubar;
+        GtkWidget     *toolbar;
+
+        priv = window->priv;
+
+        path = dh_util_build_data_filename ("devhelp", "ui", "window.ui", NULL);
+        gtk_ui_manager_add_ui_from_file (priv->manager,
+                                         path,
+                                         NULL);
+        g_free (path);
+        gtk_ui_manager_ensure_update (priv->manager);
+
+        menubar = gtk_ui_manager_get_widget (priv->manager, "/MenuBar");
+        gtk_box_pack_start (GTK_BOX (priv->menu_box), menubar,
+                            FALSE, FALSE, 0);
+        toolbar = gtk_ui_manager_get_widget (priv->manager, "/Toolbar");
+        gtk_box_pack_start (GTK_BOX (priv->menu_box), toolbar,
+                            FALSE, FALSE, 0);
+
+#ifdef GDK_WINDOWING_QUARTZ
+        {
+                GtkWidget       *widget;
+                IgeMacMenuGroup *group;
+
+                /* Hide toolbar labels. */
+                gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+
+                /* Setup menubar. */
+                ige_mac_menu_set_menu_bar (GTK_MENU_SHELL (menubar));
+                gtk_widget_hide (menubar);
+
+                widget = gtk_ui_manager_get_widget (priv->manager, "/MenuBar/FileMenu/Quit");
+                ige_mac_menu_set_quit_menu_item (GTK_MENU_ITEM (widget));
+
+                group =  ige_mac_menu_add_app_menu_group ();
+                widget = gtk_ui_manager_get_widget (priv->manager, "/MenuBar/HelpMenu/About");
+                ige_mac_menu_add_app_menu_item (group, GTK_MENU_ITEM (widget),
+                                                /* i18n: please don't translate
+                                                 * "Devhelp", it's a name, not a
+                                                 * generic word. */
+                                                _("About Devhelp"));
+
+                group =  ige_mac_menu_add_app_menu_group ();
+                widget = gtk_ui_manager_get_widget (priv->manager, "/MenuBar/EditMenu/Preferences");
+                ige_mac_menu_add_app_menu_item (group, GTK_MENU_ITEM (widget),
+                                                _("Preferences…"));
+
+                ige_mac_menu_set_global_key_handler_enabled (TRUE);
+        }
+#endif
+
+        priv->hpaned = gtk_hpaned_new ();
+
+        gtk_box_pack_start (GTK_BOX (priv->main_box), priv->hpaned, TRUE, TRUE, 0);
+
+        /* Search and contents notebook. */
+        priv->control_notebook = gtk_notebook_new ();
+
+        gtk_paned_add1 (GTK_PANED (priv->hpaned), priv->control_notebook);
+
+        g_signal_connect (priv->control_notebook,
+                          "switch-page",
+                          G_CALLBACK (window_control_switch_page_cb),
+                          window);
+
+        g_signal_connect_after (priv->control_notebook,
+                                "switch-page",
+                                G_CALLBACK (window_control_after_switch_page_cb),
+                                window);
+
+        book_tree_sw = gtk_scrolled_window_new (NULL, NULL);
+
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (book_tree_sw),
+                                        GTK_POLICY_NEVER,
+                                        GTK_POLICY_AUTOMATIC);
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (book_tree_sw),
+                                             GTK_SHADOW_IN);
+        gtk_container_set_border_width (GTK_CONTAINER (book_tree_sw), 2);
+
+        book_manager = dh_base_get_book_manager (priv->base);
+
+        priv->book_tree = dh_book_tree_new (book_manager);
+        gtk_container_add (GTK_CONTAINER (book_tree_sw),
+                           priv->book_tree);
+        dh_util_state_set_notebook_page_name (book_tree_sw, "content");
+        gtk_notebook_append_page (GTK_NOTEBOOK (priv->control_notebook),
+                                  book_tree_sw,
+                                  gtk_label_new (_("Contents")));
+        g_signal_connect (priv->book_tree,
+                          "link-selected",
+                          G_CALLBACK (window_tree_link_selected_cb),
+                          window);
+
+        priv->search = dh_search_new (book_manager);
+        dh_util_state_set_notebook_page_name (priv->search, "search");
+        gtk_notebook_append_page (GTK_NOTEBOOK (priv->control_notebook),
+                                  priv->search,
+                                  gtk_label_new (_("Search")));
+        g_signal_connect (priv->search,
+                          "link-selected",
+                          G_CALLBACK (window_search_link_selected_cb),
+                          window);
+
+        priv->vbox = gtk_vbox_new (FALSE, 0);
+        gtk_paned_add2 (GTK_PANED (priv->hpaned), priv->vbox);
+
+        /* HTML tabs notebook. */
+        priv->notebook = gtk_notebook_new ();
+        gtk_container_set_border_width (GTK_CONTAINER (priv->notebook), 0);
+        gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);
+        gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
+        gtk_box_pack_start (GTK_BOX (priv->vbox), priv->notebook, TRUE, TRUE, 0);
+
+        g_signal_connect (priv->notebook,
+                          "switch-page",
+                          G_CALLBACK (window_web_view_switch_page_cb),
+                          window);
+        g_signal_connect_after (priv->notebook,
+                                "switch-page",
+                                G_CALLBACK (window_web_view_switch_page_after_cb),
+                                window);
+
+
+        /* Create findbar. */
+        priv->findbar = egg_find_bar_new ();
+        gtk_widget_set_no_show_all (priv->findbar, TRUE);
+        gtk_box_pack_start (GTK_BOX (priv->vbox), priv->findbar, FALSE, FALSE, 0);
+
+        g_signal_connect (priv->findbar,
+                          "notify::search-string",
+                          G_CALLBACK(window_find_search_changed_cb),
+                          window);
+        g_signal_connect (priv->findbar,
+                          "notify::case-sensitive",
+                          G_CALLBACK (window_find_case_changed_cb),
+                          window);
+        g_signal_connect (priv->findbar,
+                          "previous",
+                          G_CALLBACK (window_find_previous_cb),
+                          window);
+        g_signal_connect (priv->findbar,
+                          "next",
+                          G_CALLBACK (window_find_next_cb),
+                          window);
+        g_signal_connect (priv->findbar,
+                          "close",
+                          G_CALLBACK (window_findbar_close_cb),
+                          window);
+
+        gtk_widget_show_all (priv->hpaned);
+
+        window_update_zoom_actions_sensitiveness (window);
+        window_open_new_tab (window, NULL, TRUE);
+}
+
+
+static gchar *
+find_library_equivalent (DhWindow    *window,
+                         const gchar *uri)
+{
+        DhWindowPriv *priv;
+        gchar **components;
+        GList *iter;
+        DhLink *link;
+        DhBookManager *book_manager;
+        gchar *book_id;
+        gchar *filename;
+        gchar *local_uri = NULL;
+        GList *books;
+
+        components = g_strsplit (uri, "/", 0);
+        book_id = components[4];
+        filename = components[6];
+
+        priv = window->priv;
+        book_manager = dh_base_get_book_manager (priv->base);
+
+        /* use list pointer to iterate */
+        for (books = dh_book_manager_get_books (book_manager);
+             !local_uri && books;
+             books = g_list_next (books)) {
+                DhBook *book = DH_BOOK (books->data);
+
+                for (iter = dh_book_get_keywords (book);
+                     iter;
+                     iter = g_list_next (iter)) {
+                        link = iter->data;
+                        if (g_strcmp0 (dh_link_get_book_id (link), book_id) != 0) {
+                                continue;
+                        }
+                        if (g_strcmp0 (dh_link_get_file_name (link), filename) != 0) {
+                                continue;
+                        }
+                        local_uri = dh_link_get_uri (link);
+                        break;
+                }
+        }
+
+        g_strfreev (components);
+
+        return local_uri;
+}
+
+
+static gboolean
+window_web_view_navigation_policy_decision_requested (WebKitWebView             *web_view,
+                                                      WebKitWebFrame            *frame,
+                                                      WebKitNetworkRequest      *request,
+                                                      WebKitWebNavigationAction *navigation_action,
+                                                      WebKitWebPolicyDecision   *policy_decision,
+                                                      DhWindow                  *window)
+{
+        DhWindowPriv *priv;
+        const char   *uri;
+
+        priv = window->priv;
+
+        uri = webkit_network_request_get_uri (request);
+
+#ifdef ERRORS_IN_INFOBAR
+        /* make sure to hide the info bar on page change */
+        gtk_widget_hide (window_get_active_info_bar (window));
+#endif
+
+        if (webkit_web_navigation_action_get_button (navigation_action) == 2) { /* middle click */
+                webkit_web_policy_decision_ignore (policy_decision);
+                g_signal_emit (window, signals[OPEN_LINK], 0, uri, DH_OPEN_LINK_NEW_TAB);
+                return TRUE;
+        }
+
+        if (strcmp (uri, "about:blank") == 0) {
+                return FALSE;
+        }
+
+        if (strncmp (uri, "http://library.gnome.org/devel/", 31) == 0) {
+                gchar *local_uri = find_library_equivalent (window, uri);
+                if (local_uri) {
+                        webkit_web_policy_decision_ignore (policy_decision);
+                        _dh_window_display_uri (window, local_uri);
+                        g_free (local_uri);
+                        return TRUE;
+                }
+        }
+
+        if (strncmp (uri, "file://", 7) != 0) {
+                webkit_web_policy_decision_ignore (policy_decision);
+                gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, NULL);
+                return TRUE;
+        }
+
+        if (web_view == window_get_active_web_view (window)) {
+                dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree), uri);
+                window_check_history (window, web_view);
+        }
+
+        return FALSE;
+}
+
+
+static gboolean
+window_web_view_load_error_cb (WebKitWebView  *web_view,
+                               WebKitWebFrame *frame,
+                               gchar          *uri,
+                               gpointer       *web_error,
+                               DhWindow       *window)
+{
+#ifdef ERRORS_IN_INFOBAR
+        GtkWidget *info_bar;
+        GtkWidget *content_area;
+        GtkWidget *message_label;
+        GList     *children;
+        gchar     *markup;
+
+        info_bar = window_get_active_info_bar (window);
+        markup = g_strdup_printf ("<b>%s</b>",
+                       _("Error opening the requested link."));
+        message_label = gtk_label_new (markup);
+        gtk_misc_set_alignment (GTK_MISC (message_label), 0, 0.5);
+        gtk_label_set_use_markup (GTK_LABEL (message_label), TRUE);
+        content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+        children = gtk_container_get_children (GTK_CONTAINER (content_area));
+        if (children) {
+                gtk_container_remove (GTK_CONTAINER (content_area), children->data);
+                g_list_free (children);
+        }
+        gtk_container_add (GTK_CONTAINER (content_area), message_label);
+        gtk_widget_show (message_label);
+
+        gtk_widget_show (info_bar);
+        g_free (markup);
+
+        return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+static void
+window_tree_link_selected_cb (GObject  *ignored,
+                              DhLink   *link,
+                              DhWindow *window)
+{
+        WebKitWebView *view;
+        gchar         *uri;
+
+        view = window_get_active_web_view (window);
+
+        uri = dh_link_get_uri (link);
+        webkit_web_view_load_uri (view, uri);
+        g_free (uri);
+
+        window_check_history (window, view);
+}
+
+static void
+window_search_link_selected_cb (GObject  *ignored,
+                                DhLink   *link,
+                                DhWindow *window)
+{
+        DhWindowPriv  *priv;
+        WebKitWebView *view;
+        gchar         *uri;
+
+        priv = window->priv;
+
+        priv->selected_search_link = link;
+
+        view = window_get_active_web_view (window);
+
+        uri = dh_link_get_uri (link);
+        webkit_web_view_load_uri (view, uri);
+        g_free (uri);
+
+        window_check_history (window, view);
+}
+
+static void
+window_check_history (DhWindow      *window,
+                      WebKitWebView *web_view)
+{
+        DhWindowPriv *priv;
+        GtkAction    *action;
+
+        priv = window->priv;
+
+        action = gtk_action_group_get_action (priv->action_group, "Forward");
+        g_object_set (action,
+                      "sensitive", web_view ? webkit_web_view_can_go_forward (web_view) : FALSE,
+                      NULL);
+
+        action = gtk_action_group_get_action (priv->action_group, "Back");
+        g_object_set (action,
+                      "sensitive", web_view ? webkit_web_view_can_go_back (web_view) : FALSE,
+                      NULL);
+}
+
+static void
+window_web_view_title_changed_cb (WebKitWebView  *web_view,
+                                  WebKitWebFrame *web_frame,
+                                  const gchar    *title,
+                                  DhWindow       *window)
+{
+        if (web_view == window_get_active_web_view (window)) {
+                window_update_title (window, web_view, title);
+        }
+
+        window_tab_set_title (window, web_view, title);
+}
+
+static gboolean
+window_web_view_button_press_event_cb (WebKitWebView  *web_view,
+                                       GdkEventButton *event,
+                                       DhWindow       *window)
+{
+        if (event->button == 3) {
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static gboolean
+do_search (DhWindow *window)
+{
+        DhWindowPriv  *priv = window->priv;
+        WebKitWebView *web_view;
+
+        priv->find_source_id = 0;
+
+        web_view = window_get_active_web_view (window);
+
+        webkit_web_view_unmark_text_matches (web_view);
+        webkit_web_view_mark_text_matches (
+                web_view,
+                egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar)),
+                egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar)), 0);
+        webkit_web_view_set_highlight_text_matches (web_view, TRUE);
+
+        webkit_web_view_search_text (
+                web_view, egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar)),
+                egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar)),
+                TRUE, TRUE);
+
+	return FALSE;
+}
+
+static void
+window_find_search_changed_cb (GObject    *object,
+                               GParamSpec *pspec,
+                               DhWindow   *window)
+{
+        DhWindowPriv *priv = window->priv;
+
+        if (priv->find_source_id != 0) {
+                g_source_remove (priv->find_source_id);
+                priv->find_source_id = 0;
+        }
+
+        priv->find_source_id = g_timeout_add (300, (GSourceFunc)do_search, window);
+}
+
+static void
+window_find_case_changed_cb (GObject    *object,
+                             GParamSpec *pspec,
+                             DhWindow   *window)
+{
+        DhWindowPriv  *priv = window->priv;;
+        WebKitWebView *view;
+        const gchar   *string;
+        gboolean       case_sensitive;
+
+        view = window_get_active_web_view (window);
+
+        string = egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar));
+        case_sensitive = egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar));
+
+        webkit_web_view_unmark_text_matches (view);
+        webkit_web_view_mark_text_matches (view, string, case_sensitive, 0);
+        webkit_web_view_set_highlight_text_matches (view, TRUE);
+}
+
+static void
+window_find_next_cb (GtkEntry *entry,
+                     DhWindow *window)
+{
+        DhWindowPriv  *priv = window->priv;
+        WebKitWebView *view;
+        const gchar   *string;
+        gboolean       case_sensitive;
+
+        view = window_get_active_web_view (window);
+
+        gtk_widget_show (priv->findbar);
+
+        string = egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar));
+        case_sensitive = egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar));
+
+        webkit_web_view_search_text (view, string, case_sensitive, TRUE, TRUE);
+}
+
+static void
+window_find_previous_cb (GtkEntry *entry,
+                         DhWindow *window)
+{
+        DhWindowPriv  *priv = window->priv;
+        WebKitWebView *view;
+        const gchar   *string;
+        gboolean       case_sensitive;
+
+        view = window_get_active_web_view (window);
+
+        gtk_widget_show (priv->findbar);
+
+        string = egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar));
+        case_sensitive = egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar));
+
+        webkit_web_view_search_text (view, string, case_sensitive, FALSE, TRUE);
+}
+
+static void
+window_findbar_close_cb (GtkWidget *widget,
+                         DhWindow  *window)
+{
+        DhWindowPriv  *priv = window->priv;
+        WebKitWebView *view;
+
+        view = window_get_active_web_view (window);
+
+        gtk_widget_hide (priv->findbar);
+
+        webkit_web_view_set_highlight_text_matches (view, FALSE);
+}
+
+#if 0
+static void
+window_web_view_open_new_tab_cb (WebKitWebView *web_view,
+                                 const gchar   *location,
+                                 DhWindow      *window)
+{
+        window_open_new_tab (window, location);
+}
+#endif
+
+static void
+window_web_view_tab_accel_cb (GtkAccelGroup   *accel_group,
+                              GObject         *object,
+                              guint            key,
+                              GdkModifierType  mod,
+                              DhWindow        *window)
+{
+        DhWindowPriv *priv;
+        gint          i, num;
+
+        priv = window->priv;
+
+        num = -1;
+        for (i = 0; i < (gint) G_N_ELEMENTS (tab_accel_keys); i++) {
+                if (tab_accel_keys[i] == key) {
+                        num = i;
+                        break;
+                }
+        }
+
+        if (num != -1) {
+                gtk_notebook_set_current_page (
+                        GTK_NOTEBOOK (priv->notebook), num);
+        }
+}
+
+static int
+window_open_new_tab (DhWindow    *window,
+                     const gchar *location,
+                     gboolean     switch_focus)
+{
+        DhWindowPriv *priv;
+        GtkWidget    *view;
+        GtkWidget    *vbox;
+        GtkWidget    *scrolled_window;
+        GtkWidget    *label;
+        gint          num;
+#ifdef ERRORS_IN_INFOBAR
+        GtkWidget    *info_bar;
+#endif
+
+        priv = window->priv;
+
+        /* Prepare the web view */
+        view = webkit_web_view_new ();
+        gtk_widget_show (view);
+        dh_util_font_add_web_view (WEBKIT_WEB_VIEW (view));
+
+#ifdef ERRORS_IN_INFOBAR
+        /* Prepare the info bar */
+        info_bar = gtk_info_bar_new ();
+        gtk_widget_set_no_show_all (info_bar, TRUE);
+        gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+                                 GTK_STOCK_CLOSE, GTK_RESPONSE_OK);
+        gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
+                                       GTK_MESSAGE_ERROR);
+        g_signal_connect (info_bar, "response",
+                          G_CALLBACK (gtk_widget_hide), NULL);
+#endif
+
+#if 0
+        /* Leave this in for now to make it easier to experiment. */
+        {
+                WebKitWebSettings *settings;
+                settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+
+                g_object_set (settings,
+                              "user-stylesheet-uri", "file://" DATADIR "/devhelp/devhelp.css",
+                              NULL);
+        }
+#endif
+
+        vbox = gtk_vbox_new (0, FALSE);
+        gtk_widget_show (vbox);
+
+        /* XXX: Really it would be much better to use real structures */
+        g_object_set_data (G_OBJECT (vbox), "web_view", view);
+#ifdef ERRORS_IN_INFOBAR
+        g_object_set_data (G_OBJECT (vbox), "info_bar", info_bar);
+
+        gtk_box_pack_start (GTK_BOX(vbox), info_bar, FALSE, TRUE, 0);
+#endif
+
+        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                        GTK_POLICY_AUTOMATIC,
+                                        GTK_POLICY_AUTOMATIC);
+        gtk_container_add (GTK_CONTAINER (scrolled_window), view);
+        gtk_widget_show (scrolled_window);
+        gtk_box_pack_start (GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
+
+        label = window_new_tab_label (window, _("Empty Page"), vbox);
+        gtk_widget_show_all (label);
+
+        g_signal_connect (view, "title-changed",
+                          G_CALLBACK (window_web_view_title_changed_cb),
+                          window);
+        g_signal_connect (view, "button-press-event",
+                          G_CALLBACK (window_web_view_button_press_event_cb),
+                          window);
+        g_signal_connect (view, "navigation-policy-decision-requested",
+                          G_CALLBACK (window_web_view_navigation_policy_decision_requested),
+                          window);
+        g_signal_connect (view, "load-error",
+                          G_CALLBACK (window_web_view_load_error_cb),
+                          window);
+
+        num = gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook),
+                                        vbox, NULL);
+
+        gtk_notebook_set_tab_label (GTK_NOTEBOOK (priv->notebook),
+                                    vbox, label);
+
+        if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)) > 1) {
+                gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), TRUE);
+        } else {
+                gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
+        }
+
+        if (location) {
+                webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), location);
+        } else {
+                webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), "about:blank");
+        }
+
+        if (switch_focus) {
+                gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
+        }
+
+        return num;
+}
+
+#ifndef GDK_WINDOWING_QUARTZ
+static void
+close_button_clicked_cb (GtkButton *button,
+                         DhWindow  *window)
+{
+        GtkWidget *parent_tab;
+        gint       pages;
+        gint       i;
+
+        parent_tab = g_object_get_data (G_OBJECT (button), "parent_tab");
+        pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->priv->notebook));
+        for (i=0; i<pages; i++) {
+                if (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->priv->notebook), i) == parent_tab) {
+                        window_close_tab (window, i);
+                        break;
+                }
+        }
+}
+
+static void
+tab_label_style_set_cb (GtkWidget *hbox,
+                        GtkStyle  *previous_style,
+                        gpointer   user_data)
+{
+        PangoFontMetrics *metrics;
+        PangoContext     *context;
+        GtkWidget        *button;
+        GtkStyle         *style;
+        gint              char_width;
+        gint              h, w;
+
+        context = gtk_widget_get_pango_context (hbox);
+        style = gtk_widget_get_style (hbox);
+        metrics = pango_context_get_metrics (context,
+                                             style->font_desc,
+                                             pango_context_get_language (context));
+
+        char_width = pango_font_metrics_get_approximate_digit_width (metrics);
+        pango_font_metrics_unref (metrics);
+
+        gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (hbox),
+                                           GTK_ICON_SIZE_MENU, &w, &h);
+
+        gtk_widget_set_size_request (hbox, 15 * PANGO_PIXELS (char_width) + 2 * w, -1);
+
+        button = g_object_get_data (G_OBJECT (hbox), "close-button");
+        gtk_widget_set_size_request (button, w + 2, h + 2);
+}
+#endif
+
+/* Don't create a close button on quartz, it looks very much out of
+ * place.
+ */
+static GtkWidget*
+window_new_tab_label (DhWindow        *window,
+                      const gchar     *str,
+                      const GtkWidget *parent)
+{
+        GtkWidget *label;
+#ifndef GDK_WINDOWING_QUARTZ
+        GtkWidget *hbox;
+        GtkWidget *close_button;
+        GtkWidget *image;
+
+        hbox = gtk_hbox_new (FALSE, 4);
+
+        label = gtk_label_new (str);
+        gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+
+        close_button = gtk_button_new ();
+        gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
+        gtk_button_set_focus_on_click (GTK_BUTTON (close_button), FALSE);
+        gtk_widget_set_name (close_button, "devhelp-tab-close-button");
+        g_object_set_data (G_OBJECT (close_button), "parent_tab", (gpointer) parent);
+
+        image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+        g_signal_connect (close_button, "clicked",
+                          G_CALLBACK (close_button_clicked_cb),
+                          window);
+        gtk_container_add (GTK_CONTAINER (close_button), image);
+
+        gtk_box_pack_start (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
+
+        /* Set minimal size */
+        g_signal_connect (hbox, "style-set",
+                          G_CALLBACK (tab_label_style_set_cb),
+                          NULL);
+
+        g_object_set_data (G_OBJECT (hbox), "label", label);
+        g_object_set_data (G_OBJECT (hbox), "close-button", close_button);
+
+        return hbox;
+#else
+        label = gtk_label_new (str);
+        g_object_set_data (G_OBJECT (label), "label", label);
+
+        return label;
+#endif
+}
+
+static WebKitWebView *
+window_get_active_web_view (DhWindow *window)
+{
+        DhWindowPriv *priv;
+        gint          page_num;
+        GtkWidget    *page;
+
+        priv = window->priv;
+
+        page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+        if (page_num == -1) {
+                return NULL;
+        }
+
+        page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), page_num);
+
+        return g_object_get_data (G_OBJECT (page), "web_view");
+}
+
+#ifdef ERRORS_IN_INFOBAR
+static GtkWidget *
+window_get_active_info_bar (DhWindow *window)
+{
+        DhWindowPriv *priv;
+        gint          page_num;
+        GtkWidget    *page;
+
+        priv = window->priv;
+
+        page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+        if (page_num == -1) {
+                return NULL;
+        }
+
+        page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), page_num);
+
+        return g_object_get_data (G_OBJECT (page), "info_bar");
+}
+#endif
+
+static void
+window_update_title (DhWindow      *window,
+                     WebKitWebView *web_view,
+                     const gchar   *web_view_title)
+{
+        DhWindowPriv *priv;
+        const gchar  *book_title;
+
+        priv = window->priv;
+
+        if (!web_view_title) {
+                WebKitWebFrame *web_frame;
+
+                web_frame = webkit_web_view_get_main_frame (web_view);
+                web_view_title = webkit_web_frame_get_title (web_frame);
+        }
+
+        if (web_view_title && *web_view_title == '\0') {
+                web_view_title = NULL;
+        }
+
+        book_title = dh_book_tree_get_selected_book_title (DH_BOOK_TREE (priv->book_tree));
+
+        /* Don't use both titles if they are the same. */
+        if (book_title && web_view_title && strcmp (book_title, web_view_title) == 0) {
+                web_view_title = NULL;
+        }
+
+        if (!book_title) {
+                /* i18n: Please don't translate "Devhelp" (it's marked as translatable
+                 * for transliteration only) */
+                book_title = _("Devhelp");
+        }
+
+        if (web_view_title) {
+                gchar *full_title;
+                full_title = g_strdup_printf ("%s - %s", book_title, web_view_title);
+                gtk_window_set_title (GTK_WINDOW (window), full_title);
+                g_free (full_title);
+        } else {
+                gtk_window_set_title (GTK_WINDOW (window), book_title);
+        }
+}
+
+static void
+window_tab_set_title (DhWindow      *window,
+                      WebKitWebView *web_view,
+                      const gchar   *title)
+{
+        DhWindowPriv *priv;
+        gint          num_pages, i;
+        GtkWidget    *page;
+        GtkWidget    *hbox;
+        GtkWidget    *label;
+        GtkWidget    *page_web_view;
+
+        priv = window->priv;
+
+        if (!title || title[0] == '\0') {
+                title = _("Empty Page");
+        }
+
+        num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
+        for (i = 0; i < num_pages; i++) {
+                page = gtk_notebook_get_nth_page (
+                        GTK_NOTEBOOK (priv->notebook), i);
+                page_web_view = g_object_get_data (G_OBJECT (page), "web_view");
+
+                /* The web_view widget is inside a frame. */
+                if (page_web_view == GTK_WIDGET (web_view)) {
+                        hbox = gtk_notebook_get_tab_label (
+                                GTK_NOTEBOOK (priv->notebook), page);
+
+                        if (hbox) {
+                                label = g_object_get_data (G_OBJECT (hbox), "label");
+                                gtk_label_set_text (GTK_LABEL (label), title);
+                        }
+                        break;
+                }
+        }
+}
+
+GtkWidget *
+dh_window_new (DhBase *base)
+{
+        DhWindow     *window;
+        DhWindowPriv *priv;
+
+        window = g_object_new (DH_TYPE_WINDOW, NULL);
+        priv = window->priv;
+
+        priv->base = g_object_ref (base);
+
+        window_populate (window);
+
+        gtk_window_set_icon_name (GTK_WINDOW (window), "devhelp");
+
+        dh_util_state_manage_window (GTK_WINDOW (window), "main/window");
+        dh_util_state_manage_paned (GTK_PANED (priv->hpaned), "main/paned");
+        dh_util_state_manage_notebook (GTK_NOTEBOOK (priv->control_notebook),
+                                       "main/search_notebook",
+                                       "content");
+
+        return GTK_WIDGET (window);
+}
+
+void
+dh_window_search (DhWindow    *window,
+                  const gchar *str,
+                  const gchar *book_id)
+{
+        DhWindowPriv *priv;
+
+        g_return_if_fail (DH_IS_WINDOW (window));
+
+        priv = window->priv;
+
+        gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
+        dh_search_set_search_string (DH_SEARCH (priv->search), str, book_id);
+}
+
+void
+dh_window_focus_search (DhWindow *window)
+{
+        DhWindowPriv *priv;
+
+        g_return_if_fail (DH_IS_WINDOW (window));
+
+        priv = window->priv;
+
+        gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
+        gtk_widget_grab_focus (priv->search);
+}
+
+/* Only call this with a URI that is known to be in the docs. */
+void
+_dh_window_display_uri (DhWindow    *window,
+                        const gchar *uri)
+{
+        DhWindowPriv  *priv;
+        WebKitWebView *web_view;
+
+        g_return_if_fail (DH_IS_WINDOW (window));
+        g_return_if_fail (uri != NULL);
+
+        priv = window->priv;
+
+        web_view = window_get_active_web_view (window);
+        webkit_web_view_load_uri (web_view, uri);
+        dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree), uri);
+}


Modified: devhelp/devhelp/dh-window.h
73 files changed, 73 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2001-2002 Mikael Hallendal <micke at imendio.com>
+ * Copyright (C) 2005 Imendio AB
+ *
+ * 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.
+ */
+
+#ifndef __DH_WINDOW_H__
+#define __DH_WINDOW_H__
+
+#include <gtk/gtk.h>
+#include "dh-base.h"
+
+G_BEGIN_DECLS
+
+#define DH_TYPE_WINDOW         (dh_window_get_type ())
+#define DH_WINDOW(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DH_TYPE_WINDOW, DhWindow))
+#define DH_WINDOW_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), DH_TYPE_WINDOW, DhWindowClass))
+#define DH_IS_WINDOW(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DH_TYPE_WINDOW))
+#define DH_IS_WINDOW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DH_TYPE_WINDOW))
+#define DH_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DH_TYPE_WINDOW, DhWindowClass))
+
+typedef struct _DhWindow       DhWindow;
+typedef struct _DhWindowClass  DhWindowClass;
+typedef struct _DhWindowPriv   DhWindowPriv;
+
+typedef enum
+{
+        DH_OPEN_LINK_NEW_WINDOW = 1 << 0,
+        DH_OPEN_LINK_NEW_TAB    = 1 << 1
+} DhOpenLinkFlags;
+
+struct _DhWindow {
+        GtkWindow     parent_instance;
+        DhWindowPriv *priv;
+};
+
+struct _DhWindowClass {
+        GtkWindowClass parent_class;
+
+        /* Signals */
+        void (*open_link) (DhWindow        *window,
+                           const char      *location,
+                           DhOpenLinkFlags  flags);
+};
+
+GType      dh_window_get_type     (void) G_GNUC_CONST;
+GtkWidget *dh_window_new          (DhBase      *base);
+void       dh_window_search       (DhWindow    *window,
+                                   const gchar *str,
+                                   const gchar *book_id);
+void       dh_window_focus_search (DhWindow    *window);
+void       _dh_window_display_uri (DhWindow    *window,
+                                   const gchar *uri);
+
+G_END_DECLS
+
+#endif /* __DH_WINDOW_H__ */


Modified: devhelp/devhelp/eggfindbar.c
755 files changed, 755 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,755 @@
+/* Copyright (C) 2004 Red Hat, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the Gnome Library; see the file COPYING.LIB.  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 <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "eggfindbar.h"
+
+struct _EggFindBarPrivate
+{
+  gchar *search_string;
+
+  GtkToolItem *next_button;
+  GtkToolItem *previous_button;
+  GtkToolItem *status_separator;
+  GtkToolItem *status_item;
+  GtkToolItem *case_button;
+
+  GtkWidget *find_entry;
+  GtkWidget *status_label;
+
+  gulong set_focus_handler;
+  guint case_sensitive : 1;
+};
+
+#define EGG_FIND_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EGG_TYPE_FIND_BAR, EggFindBarPrivate))
+
+enum {
+    PROP_0,
+    PROP_SEARCH_STRING,
+    PROP_CASE_SENSITIVE
+};
+
+static void egg_find_bar_finalize      (GObject        *object);
+static void egg_find_bar_get_property  (GObject        *object,
+                                        guint           prop_id,
+                                        GValue         *value,
+                                        GParamSpec     *pspec);
+static void egg_find_bar_set_property  (GObject        *object,
+                                        guint           prop_id,
+                                        const GValue   *value,
+                                        GParamSpec     *pspec);
+static void egg_find_bar_show          (GtkWidget *widget);
+static void egg_find_bar_hide          (GtkWidget *widget);
+static void egg_find_bar_grab_focus    (GtkWidget *widget);
+
+G_DEFINE_TYPE (EggFindBar, egg_find_bar, GTK_TYPE_TOOLBAR);
+
+enum
+  {
+    NEXT,
+    PREVIOUS,
+    CLOSE,
+    SCROLL,
+    LAST_SIGNAL
+  };
+
+static guint find_bar_signals[LAST_SIGNAL] = { 0 };
+
+static void
+egg_find_bar_class_init (EggFindBarClass *klass)
+{
+  GObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+  GtkBindingSet *binding_set;
+        
+  egg_find_bar_parent_class = g_type_class_peek_parent (klass);
+
+  object_class = (GObjectClass *)klass;
+  widget_class = (GtkWidgetClass *)klass;
+
+  object_class->set_property = egg_find_bar_set_property;
+  object_class->get_property = egg_find_bar_get_property;
+
+  object_class->finalize = egg_find_bar_finalize;
+
+  widget_class->show = egg_find_bar_show;
+  widget_class->hide = egg_find_bar_hide;
+  
+  widget_class->grab_focus = egg_find_bar_grab_focus;
+
+  find_bar_signals[NEXT] =
+    g_signal_new ("next",
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (EggFindBarClass, next),
+		  NULL, NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+  find_bar_signals[PREVIOUS] =
+    g_signal_new ("previous",
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (EggFindBarClass, previous),
+		  NULL, NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+  find_bar_signals[CLOSE] =
+    g_signal_new ("close",
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (EggFindBarClass, close),
+		  NULL, NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+  find_bar_signals[SCROLL] =
+    g_signal_new ("scroll",
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (EggFindBarClass, scroll),
+		  NULL, NULL,
+		  g_cclosure_marshal_VOID__ENUM,
+		  G_TYPE_NONE, 1,
+		  GTK_TYPE_SCROLL_TYPE);
+
+  /**
+   * EggFindBar:search_string:
+   *
+   * The current string to search for. NULL or empty string
+   * both mean no current string.
+   *
+   */
+  g_object_class_install_property (object_class,
+				   PROP_SEARCH_STRING,
+				   g_param_spec_string ("search_string",
+							("Search string"),
+							("The name of the string to be found"),
+							NULL,
+							G_PARAM_READWRITE));
+
+  /**
+   * EggFindBar:case_sensitive:
+   *
+   * TRUE for a case sensitive search.
+   *
+   */
+  g_object_class_install_property (object_class,
+				   PROP_CASE_SENSITIVE,
+				   g_param_spec_boolean ("case_sensitive",
+                                                         ("Case sensitive"),
+                                                         ("TRUE for a case sensitive search"),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+
+  /* Style properties */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_boxed ("all_matches_color",
+                                                               ("Highlight color"),
+                                                               ("Color of highlight for all matches"),
+                                                               GDK_TYPE_COLOR,
+                                                               G_PARAM_READABLE));
+
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_boxed ("current_match_color",
+                                                               ("Current color"),
+                                                               ("Color of highlight for the current match"),
+                                                               GDK_TYPE_COLOR,
+                                                               G_PARAM_READABLE));
+
+  g_type_class_add_private (object_class, sizeof (EggFindBarPrivate));
+
+  binding_set = gtk_binding_set_by_class (klass);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+				"close", 0);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
+                                "scroll", 1,
+                                GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
+                                "scroll", 1,
+                                GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
+				"scroll", 1,
+				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
+				"scroll", 1,
+				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
+				"scroll", 1,
+				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
+				"scroll", 1,
+				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+}
+
+static void
+egg_find_bar_emit_next (EggFindBar *find_bar)
+{
+  g_signal_emit (find_bar, find_bar_signals[NEXT], 0);
+}
+
+static void
+egg_find_bar_emit_previous (EggFindBar *find_bar)
+{
+  g_signal_emit (find_bar, find_bar_signals[PREVIOUS], 0);
+}
+
+static void
+next_clicked_callback (GtkButton *button,
+                       void      *data)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+  egg_find_bar_emit_next (find_bar);
+}
+
+static void
+previous_clicked_callback (GtkButton *button,
+                           void      *data)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+  egg_find_bar_emit_previous (find_bar);
+}
+
+static void
+case_sensitive_toggled_callback (GtkCheckButton *button,
+                                 void           *data)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+  egg_find_bar_set_case_sensitive (find_bar,
+                                   gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
+}
+
+static void
+entry_activate_callback (GtkEntry *entry,
+                          void     *data)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+  if (find_bar->priv->search_string != NULL)
+    egg_find_bar_emit_next (find_bar);
+}
+
+static void
+entry_changed_callback (GtkEntry *entry,
+                        void     *data)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (data);
+  char *text;
+
+  /* paranoid strdup because set_search_string() sets
+   * the entry text
+   */
+  text = g_strdup (gtk_entry_get_text (entry));
+
+  egg_find_bar_set_search_string (find_bar, text);
+  
+  g_free (text);
+}
+
+static void
+set_focus_cb (GtkWidget *window,
+	      GtkWidget *widget,
+	      EggFindBar *bar)
+{
+  GtkWidget *wbar = GTK_WIDGET (bar);
+
+  while (widget != NULL && widget != wbar)
+    {
+      widget = gtk_widget_get_parent (widget);
+    }
+
+  /* if widget == bar, the new focus widget is in the bar, so we
+   * don't deactivate.
+   */
+  if (widget != wbar)
+    {
+      g_signal_emit (bar, find_bar_signals[CLOSE], 0);
+    }
+}
+
+static void
+egg_find_bar_init (EggFindBar *find_bar)
+{
+  EggFindBarPrivate *priv;
+  GtkWidget *label;
+  GtkWidget *alignment;
+  GtkWidget *box;
+  GtkToolItem *item;
+  GtkWidget *arrow;
+
+  /* Data */
+  priv = EGG_FIND_BAR_GET_PRIVATE (find_bar);
+  
+  find_bar->priv = priv;  
+  priv->search_string = NULL;
+
+  gtk_toolbar_set_style (GTK_TOOLBAR (find_bar), GTK_TOOLBAR_BOTH_HORIZ);
+
+  /* Find: |_____| */
+  item = gtk_tool_item_new ();
+  box = gtk_hbox_new (FALSE, 12);
+  
+  alignment = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
+  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 2, 2);
+
+  label = gtk_label_new_with_mnemonic (_("Find:"));
+
+  priv->find_entry = gtk_entry_new ();
+  gtk_entry_set_width_chars (GTK_ENTRY (priv->find_entry), 32);
+  gtk_entry_set_max_length (GTK_ENTRY (priv->find_entry), 512);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->find_entry);
+
+  /* Prev */
+  arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
+  priv->previous_button = gtk_tool_button_new (arrow, Q_("Find Previous"));
+  gtk_tool_item_set_is_important (priv->previous_button, TRUE);
+#if GTK_CHECK_VERSION (2, 11, 5)
+  gtk_widget_set_tooltip_text (GTK_WIDGET (priv->previous_button),
+			       _("Find previous occurrence of the search string"));
+#else
+  gtk_tool_item_set_tooltip (priv->previous_button, GTK_TOOLBAR (find_bar)->tooltips,
+		             _("Find previous occurrence of the search string"),
+		             NULL);
+#endif
+
+  /* Next */
+  arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+  priv->next_button = gtk_tool_button_new (arrow, Q_("Find Next"));
+  gtk_tool_item_set_is_important (priv->next_button, TRUE);
+#if GTK_CHECK_VERSION (2, 11, 5)
+  gtk_widget_set_tooltip_text (GTK_WIDGET (priv->next_button),
+			       _("Find next occurrence of the search string"));
+#else
+  gtk_tool_item_set_tooltip (priv->next_button, GTK_TOOLBAR (find_bar)->tooltips,
+		             _("Find next occurrence of the search string"),
+		             NULL);
+#endif
+
+  /* Separator*/
+  priv->status_separator = gtk_separator_tool_item_new();
+
+  /* Case button */
+  priv->case_button = gtk_toggle_tool_button_new ();
+  g_object_set (G_OBJECT (priv->case_button), "label", _("C_ase Sensitive"), NULL);
+  gtk_tool_item_set_is_important (priv->case_button, TRUE);
+#if GTK_CHECK_VERSION (2, 11, 5)
+  gtk_widget_set_tooltip_text (GTK_WIDGET (priv->case_button),
+			       _("Toggle case sensitive search"));
+#else  
+  gtk_tool_item_set_tooltip (priv->case_button, GTK_TOOLBAR (find_bar)->tooltips,
+		             _("Toggle case sensitive search"),
+		             NULL);
+#endif
+  /* Status */
+  priv->status_item = gtk_tool_item_new();
+  gtk_tool_item_set_expand (priv->status_item, TRUE);
+  priv->status_label = gtk_label_new (NULL);
+  gtk_label_set_ellipsize (GTK_LABEL (priv->status_label),
+                           PANGO_ELLIPSIZE_END);
+  gtk_misc_set_alignment (GTK_MISC (priv->status_label), 0.0, 0.5);
+
+
+  g_signal_connect (priv->find_entry, "changed",
+                    G_CALLBACK (entry_changed_callback),
+                    find_bar);
+  g_signal_connect (priv->find_entry, "activate",
+                    G_CALLBACK (entry_activate_callback),
+                    find_bar);
+  g_signal_connect (priv->next_button, "clicked",
+                    G_CALLBACK (next_clicked_callback),
+                    find_bar);
+  g_signal_connect (priv->previous_button, "clicked",
+                    G_CALLBACK (previous_clicked_callback),
+                    find_bar);
+  g_signal_connect (priv->case_button, "toggled",
+                    G_CALLBACK (case_sensitive_toggled_callback),
+                    find_bar);
+
+  gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (box), priv->find_entry, TRUE, TRUE, 0);
+  gtk_container_add (GTK_CONTAINER (alignment), box);
+  gtk_container_add (GTK_CONTAINER (item), alignment);
+  gtk_toolbar_insert (GTK_TOOLBAR (find_bar), item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->previous_button, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->next_button, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->case_button, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->status_separator, -1);
+  gtk_container_add  (GTK_CONTAINER (priv->status_item), priv->status_label);
+  gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->status_item, -1);
+
+  /* don't show status separator/label until they are set */
+
+  gtk_widget_show_all (GTK_WIDGET (item));
+  gtk_widget_show_all (GTK_WIDGET (priv->next_button));
+  gtk_widget_show_all (GTK_WIDGET (priv->previous_button));
+  gtk_widget_show (priv->status_label);
+}
+
+static void
+egg_find_bar_finalize (GObject *object)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (object);
+  EggFindBarPrivate *priv = (EggFindBarPrivate *)find_bar->priv;
+
+  g_free (priv->search_string);
+
+  G_OBJECT_CLASS (egg_find_bar_parent_class)->finalize (object);
+}
+
+static void
+egg_find_bar_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (object);
+
+  switch (prop_id)
+    {
+    case PROP_SEARCH_STRING:
+      egg_find_bar_set_search_string (find_bar, g_value_get_string (value));
+      break;
+    case PROP_CASE_SENSITIVE:
+      egg_find_bar_set_case_sensitive (find_bar, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+egg_find_bar_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (object);
+  EggFindBarPrivate *priv = (EggFindBarPrivate *)find_bar->priv;
+
+  switch (prop_id)
+    {
+    case PROP_SEARCH_STRING:
+      g_value_set_string (value, priv->search_string);
+      break;
+    case PROP_CASE_SENSITIVE:
+      g_value_set_boolean (value, priv->case_sensitive);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+egg_find_bar_show (GtkWidget *widget)
+{
+  EggFindBar *bar = EGG_FIND_BAR (widget);
+  EggFindBarPrivate *priv = bar->priv;
+
+  GTK_WIDGET_CLASS (egg_find_bar_parent_class)->show (widget);
+
+  if (priv->set_focus_handler == 0)
+    {
+      GtkWidget *toplevel;
+
+      toplevel = gtk_widget_get_toplevel (widget);
+
+      priv->set_focus_handler =
+	g_signal_connect (toplevel, "set-focus",
+			  G_CALLBACK (set_focus_cb), bar);
+    }
+}
+
+static void
+egg_find_bar_hide (GtkWidget *widget)
+{
+  EggFindBar *bar = EGG_FIND_BAR (widget);
+  EggFindBarPrivate *priv = bar->priv;
+
+  if (priv->set_focus_handler != 0)
+    {
+      GtkWidget *toplevel;
+
+      toplevel = gtk_widget_get_toplevel (widget);
+
+      g_signal_handlers_disconnect_by_func
+	(toplevel, (void (*)) G_CALLBACK (set_focus_cb), bar);
+      priv->set_focus_handler = 0;
+    }
+
+  GTK_WIDGET_CLASS (egg_find_bar_parent_class)->hide (widget);
+}
+
+static void
+egg_find_bar_grab_focus (GtkWidget *widget)
+{
+  EggFindBar *find_bar = EGG_FIND_BAR (widget);
+  EggFindBarPrivate *priv = find_bar->priv;
+
+  gtk_widget_grab_focus (priv->find_entry);
+}
+
+/**
+ * egg_find_bar_new:
+ *
+ * Creates a new #EggFindBar.
+ *
+ * Returns: a newly created #EggFindBar
+ *
+ * Since: 2.6
+ */
+GtkWidget *
+egg_find_bar_new (void)
+{
+  EggFindBar *find_bar;
+
+  find_bar = g_object_new (EGG_TYPE_FIND_BAR, NULL);
+
+  return GTK_WIDGET (find_bar);
+}
+
+/**
+ * egg_find_bar_set_search_string:
+ *
+ * Sets the string that should be found/highlighted in the document.
+ * Empty string is converted to NULL.
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_set_search_string  (EggFindBar *find_bar,
+                                 const char *search_string)
+{
+  EggFindBarPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
+
+  priv = (EggFindBarPrivate *)find_bar->priv;
+
+  g_object_freeze_notify (G_OBJECT (find_bar));
+  
+  if (priv->search_string != search_string)
+    {
+      char *old;
+      
+      old = priv->search_string;
+
+      if (search_string && *search_string == '\0')
+        search_string = NULL;
+
+      /* Only update if the string has changed; setting the entry
+       * will emit changed on the entry which will re-enter
+       * this function, but we'll handle that fine with this
+       * short-circuit.
+       */
+      if ((old && search_string == NULL) ||
+          (old == NULL && search_string) ||
+          (old && search_string &&
+           strcmp (old, search_string) != 0))
+        {
+	  gboolean not_empty;
+	  
+          priv->search_string = g_strdup (search_string);
+          g_free (old);
+          
+          gtk_entry_set_text (GTK_ENTRY (priv->find_entry),
+                              priv->search_string ?
+                              priv->search_string :
+                              "");
+	   
+          not_empty = (search_string == NULL) ? FALSE : TRUE;		 
+
+          gtk_widget_set_sensitive (GTK_WIDGET (find_bar->priv->next_button), not_empty);
+          gtk_widget_set_sensitive (GTK_WIDGET (find_bar->priv->previous_button), not_empty);
+
+          g_object_notify (G_OBJECT (find_bar),
+                           "search_string");
+        }
+    }
+
+  g_object_thaw_notify (G_OBJECT (find_bar));
+}
+
+
+/**
+ * egg_find_bar_get_search_string:
+ *
+ * Gets the string that should be found/highlighted in the document.
+ *
+ * Returns: the string
+ *
+ * Since: 2.6
+ */
+const char*
+egg_find_bar_get_search_string  (EggFindBar *find_bar)
+{
+  EggFindBarPrivate *priv;
+
+  g_return_val_if_fail (EGG_IS_FIND_BAR (find_bar), NULL);
+
+  priv = find_bar->priv;
+
+  return priv->search_string ? priv->search_string : "";
+}
+
+/**
+ * egg_find_bar_set_case_sensitive:
+ *
+ * Sets whether the search is case sensitive
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_set_case_sensitive (EggFindBar *find_bar,
+                                 gboolean    case_sensitive)
+{
+  EggFindBarPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
+
+  priv = (EggFindBarPrivate *)find_bar->priv;
+
+  g_object_freeze_notify (G_OBJECT (find_bar));
+
+  case_sensitive = case_sensitive != FALSE;
+
+  if (priv->case_sensitive != case_sensitive)
+    {
+      priv->case_sensitive = case_sensitive;
+
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->case_button),
+                                    priv->case_sensitive);
+
+      g_object_notify (G_OBJECT (find_bar),
+                       "case_sensitive");
+    }
+
+  g_object_thaw_notify (G_OBJECT (find_bar));
+}
+
+/**
+ * egg_find_bar_get_case_sensitive:
+ *
+ * Gets whether the search is case sensitive
+ *
+ * Returns: TRUE if it's case sensitive
+ *
+ * Since: 2.6
+ */
+gboolean
+egg_find_bar_get_case_sensitive (EggFindBar *find_bar)
+{
+  EggFindBarPrivate *priv;
+
+  g_return_val_if_fail (EGG_IS_FIND_BAR (find_bar), FALSE);
+
+  priv = (EggFindBarPrivate *)find_bar->priv;
+
+  return priv->case_sensitive;
+}
+
+static void
+get_style_color (EggFindBar *find_bar,
+                 const char *style_prop_name,
+                 GdkColor   *color)
+{
+  GdkColor *style_color;
+
+  gtk_widget_ensure_style (GTK_WIDGET (find_bar));
+  gtk_widget_style_get (GTK_WIDGET (find_bar),
+                        "color", &style_color, NULL);
+  if (style_color)
+    {
+      *color = *style_color;
+      gdk_color_free (style_color);
+    }
+}
+
+/**
+ * egg_find_bar_get_all_matches_color:
+ *
+ * Gets the color to use to highlight all the
+ * known matches.
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_get_all_matches_color (EggFindBar *find_bar,
+                                    GdkColor   *color)
+{
+  GdkColor found_color = { 0, 0, 0, 0x0f0f };
+
+  get_style_color (find_bar, "all_matches_color", &found_color);
+
+  *color = found_color;
+}
+
+/**
+ * egg_find_bar_get_current_match_color:
+ *
+ * Gets the color to use to highlight the match
+ * we're currently on.
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_get_current_match_color (EggFindBar *find_bar,
+                                      GdkColor   *color)
+{
+  GdkColor found_color = { 0, 0, 0, 0xffff };
+
+  get_style_color (find_bar, "current_match_color", &found_color);
+
+  *color = found_color;
+}
+
+/**
+ * egg_find_bar_set_status_text:
+ *
+ * Sets some text to display if there's space; typical text would
+ * be something like "5 results on this page" or "No results"
+ *
+ * @text: the text to display
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_set_status_text (EggFindBar *find_bar,
+                              const char *text)
+{
+  EggFindBarPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
+
+  priv = (EggFindBarPrivate *)find_bar->priv;
+  
+  gtk_label_set_text (GTK_LABEL (priv->status_label), text);
+  g_object_set (priv->status_separator, "visible", text != NULL && *text != '\0', NULL);
+  g_object_set (priv->status_item, "visible", text != NULL && *text !='\0', NULL);
+}


Modified: devhelp/devhelp/eggfindbar.h
81 files changed, 81 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,81 @@
+/* Copyright (C) 2004 Red Hat, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the Gnome Library; see the file COPYING.LIB.  If not,
+write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __EGG_FIND_BAR_H__
+#define __EGG_FIND_BAR_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_FIND_BAR            (egg_find_bar_get_type ())
+#define EGG_FIND_BAR(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), EGG_TYPE_FIND_BAR, EggFindBar))
+#define EGG_FIND_BAR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_FIND_BAR, EggFindBarClass))
+#define EGG_IS_FIND_BAR(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), EGG_TYPE_FIND_BAR))
+#define EGG_IS_FIND_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_FIND_BAR))
+#define EGG_FIND_BAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_FIND_BAR, EggFindBarClass))
+
+typedef struct _EggFindBar        EggFindBar;
+typedef struct _EggFindBarClass   EggFindBarClass;
+typedef struct _EggFindBarPrivate EggFindBarPrivate;
+
+struct _EggFindBar
+{
+  GtkToolbar parent;
+
+  /*< private >*/
+  EggFindBarPrivate *priv;
+};
+
+struct _EggFindBarClass
+{
+  GtkToolbarClass parent_class;
+
+  void (* next)	    (EggFindBar *find_bar);
+  void (* previous) (EggFindBar *find_bar);
+  void (* close)    (EggFindBar *find_bar);
+  void (* scroll)   (EggFindBar *find_bar, GtkScrollType* scroll);
+
+  /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
+  void (*_gtk_reserved2) (void);
+  void (*_gtk_reserved3) (void);
+  void (*_gtk_reserved4) (void);
+};
+
+GType                  egg_find_bar_get_type               (void) G_GNUC_CONST;
+GtkWidget             *egg_find_bar_new                    (void);
+
+void        egg_find_bar_set_search_string       (EggFindBar *find_bar,
+                                                  const char *search_string);
+const char* egg_find_bar_get_search_string       (EggFindBar *find_bar);
+void        egg_find_bar_set_case_sensitive      (EggFindBar *find_bar,
+                                                  gboolean    case_sensitive);
+gboolean    egg_find_bar_get_case_sensitive      (EggFindBar *find_bar);
+void        egg_find_bar_get_all_matches_color   (EggFindBar *find_bar,
+                                                  GdkColor   *color);
+void        egg_find_bar_get_current_match_color (EggFindBar *find_bar,
+                                                  GdkColor   *color);
+void        egg_find_bar_set_status_text         (EggFindBar *find_bar,
+                                                  const char *text);
+
+G_END_DECLS
+
+#endif /* __EGG_FIND_BAR_H__ */
+
+


Modified: devhelp/devhelp/ige-conf-gconf.c
387 files changed, 387 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,387 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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 <gconf/gconf-client.h>
+#include "ige-conf-private.h"
+
+typedef struct {
+        GConfClient *gconf_client;
+        GList       *defaults;
+} IgeConfPriv;
+
+typedef struct {
+        IgeConf           *conf;
+        IgeConfNotifyFunc  func;
+        gpointer           user_data;
+} IgeConfNotifyData;
+
+G_DEFINE_TYPE (IgeConf, ige_conf, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, IGE_TYPE_CONF, IgeConfPriv);
+
+static IgeConf *global_conf = NULL;
+
+static void
+conf_finalize (GObject *object)
+{
+        IgeConfPriv *priv;
+
+        priv = GET_PRIVATE (object);
+
+        /* FIXME: Remove added dirs.
+        gconf_client_remove_dir (priv->gconf_client,
+                                 CONF_PATH,
+                                 NULL);
+        */
+
+        g_object_unref (priv->gconf_client);
+
+        _ige_conf_defaults_free_list (priv->defaults);
+
+        G_OBJECT_CLASS (ige_conf_parent_class)->finalize (object);
+}
+
+static void
+ige_conf_class_init (IgeConfClass *class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (class);
+
+        object_class->finalize = conf_finalize;
+
+        g_type_class_add_private (object_class, sizeof (IgeConfPriv));
+}
+
+static void
+ige_conf_init (IgeConf *conf)
+{
+        IgeConfPriv *priv;
+
+        priv = GET_PRIVATE (conf);
+
+        priv->gconf_client = gconf_client_get_default ();
+}
+
+IgeConf *
+ige_conf_get (void)
+{
+        if (!global_conf) {
+                global_conf = g_object_new (IGE_TYPE_CONF, NULL);
+        }
+
+        return global_conf;
+}
+
+void
+ige_conf_add_defaults (IgeConf     *conf,
+                       const gchar *path)
+{
+        IgeConfPriv *priv = GET_PRIVATE (conf);
+        gchar       *root;
+
+        priv->defaults = _ige_conf_defaults_read_file (path, NULL);
+        root = _ige_conf_defaults_get_root (priv->defaults);
+
+        gconf_client_add_dir (priv->gconf_client,
+                              root,
+                              GCONF_CLIENT_PRELOAD_ONELEVEL,
+                              NULL);
+
+        g_free (root);
+}
+
+static GConfEntry *
+conf_get_entry (IgeConf     *conf,
+                const gchar *key)
+{
+        IgeConfPriv *priv;
+
+        priv = GET_PRIVATE (conf);
+
+        return gconf_client_get_entry (priv->gconf_client, key,
+                                       NULL, TRUE, NULL);
+}
+
+gboolean
+ige_conf_set_int (IgeConf     *conf,
+                  const gchar *key,
+                  gint         value)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        return gconf_client_set_int (priv->gconf_client,
+                                     key,
+                                     value,
+                                     NULL);
+}
+
+gboolean
+ige_conf_get_int (IgeConf     *conf,
+                  const gchar *key,
+                  gint        *value)
+{
+        IgeConfPriv *priv;
+        GConfEntry  *entry;
+        gboolean     got_value = FALSE;
+
+        *value = 0;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+        g_return_val_if_fail (value != NULL, FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        entry = conf_get_entry (conf, key);
+        if (entry) {
+                GConfValue *v;
+
+                v = gconf_entry_get_value (entry);
+                if (v) {
+                        *value = gconf_value_get_int (v);
+                        got_value = TRUE;
+                }
+        }
+
+        gconf_entry_free (entry);
+
+        if (!got_value) {
+                *value = _ige_conf_defaults_get_int (priv->defaults, key);
+        }
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_set_bool (IgeConf     *conf,
+                   const gchar *key,
+                   gboolean     value)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        return gconf_client_set_bool (priv->gconf_client,
+                                      key,
+                                      value,
+                                      NULL);
+}
+
+gboolean
+ige_conf_get_bool (IgeConf     *conf,
+                   const gchar *key,
+                   gboolean    *value)
+{
+        IgeConfPriv *priv;
+        GConfEntry  *entry;
+        gboolean     got_value = FALSE;
+
+        *value = 0;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+        g_return_val_if_fail (value != NULL, FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        entry = conf_get_entry (conf, key);
+        if (entry) {
+                GConfValue *v;
+
+                v = gconf_entry_get_value (entry);
+                if (v) {
+                        *value = gconf_value_get_bool (v);
+                        got_value = TRUE;
+                }
+        }
+
+        gconf_entry_free (entry);
+
+        if (!got_value) {
+                *value = _ige_conf_defaults_get_bool (priv->defaults, key);
+        }
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_set_string (IgeConf     *conf,
+                     const gchar *key,
+                     const gchar *value)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        return gconf_client_set_string (priv->gconf_client,
+                                        key,
+                                        value,
+                                        NULL);
+}
+
+gboolean
+ige_conf_get_string (IgeConf      *conf,
+                     const gchar  *key,
+                     gchar       **value)
+{
+        IgeConfPriv *priv;
+        GError      *error = NULL;
+
+        *value = NULL;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        *value = gconf_client_get_string (priv->gconf_client,
+                                          key,
+                                          &error);
+
+        if (error) {
+                g_error_free (error);
+                return FALSE;
+        }
+
+        if (*value == NULL) {
+                *value = g_strdup (_ige_conf_defaults_get_string (priv->defaults, key));
+       }
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_set_string_list (IgeConf     *conf,
+                          const gchar *key,
+                          GSList      *value)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        return gconf_client_set_list (priv->gconf_client,
+                                      key,
+                                      GCONF_VALUE_STRING,
+                                      value,
+                                      NULL);
+}
+
+gboolean
+ige_conf_get_string_list (IgeConf      *conf,
+                          const gchar  *key,
+                          GSList      **value)
+{
+        IgeConfPriv *priv;
+        GError      *error = NULL;
+
+        *value = NULL;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        *value = gconf_client_get_list (priv->gconf_client,
+                                        key,
+                                        GCONF_VALUE_STRING,
+                                        &error);
+        if (error) {
+                g_error_free (error);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static void
+conf_notify_data_free (IgeConfNotifyData *data)
+{
+        g_object_unref (data->conf);
+        g_slice_free (IgeConfNotifyData, data);
+}
+
+static void
+conf_notify_func (GConfClient *client,
+                  guint        id,
+                  GConfEntry  *entry,
+                  gpointer     user_data)
+{
+        IgeConfNotifyData *data;
+
+        data = user_data;
+
+        data->func (data->conf,
+                    gconf_entry_get_key (entry),
+                    data->user_data);
+}
+
+guint
+ige_conf_notify_add (IgeConf           *conf,
+                     const gchar       *key,
+                     IgeConfNotifyFunc  func,
+                     gpointer           user_data)
+{
+        IgeConfPriv       *priv;
+        guint              id;
+        IgeConfNotifyData *data;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), 0);
+
+        priv = GET_PRIVATE (conf);
+
+        data = g_slice_new (IgeConfNotifyData);
+        data->func = func;
+        data->user_data = user_data;
+        data->conf = g_object_ref (conf);
+
+        id = gconf_client_notify_add (priv->gconf_client,
+                                      key,
+                                      conf_notify_func,
+                                      data,
+                                      (GFreeFunc) conf_notify_data_free,
+                                      NULL);
+
+        return id;
+}
+
+gboolean
+ige_conf_notify_remove (IgeConf *conf,
+                        guint    id)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        gconf_client_notify_remove (priv->gconf_client, id);
+
+        return TRUE;
+}


Modified: devhelp/devhelp/ige-conf-mac.c
342 files changed, 342 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,342 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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"
+#import <Cocoa/Cocoa.h>
+#include <string.h>
+#include "ige-conf-private.h"
+
+typedef struct {
+        NSUserDefaults *defaults;
+} IgeConfPriv;
+
+typedef struct {
+        IgeConf           *conf;
+        IgeConfNotifyFunc  func;
+        gpointer           user_data;
+} IgeConfNotifyData;
+
+G_DEFINE_TYPE (IgeConf, ige_conf, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
+  (instance, IGE_TYPE_CONF, IgeConfPriv);
+
+static IgeConf *global_conf = NULL;
+
+static void
+conf_finalize (GObject *object)
+{
+        IgeConfPriv *priv = GET_PRIVATE (object);
+
+        [priv->defaults synchronize];
+
+        if (IGE_CONF (object) == global_conf) {
+                global_conf = NULL;
+        }
+
+        G_OBJECT_CLASS (ige_conf_parent_class)->finalize (object);
+}
+
+static void
+ige_conf_class_init (IgeConfClass *class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (class);
+
+        object_class->finalize = conf_finalize;
+
+        g_type_class_add_private (object_class, sizeof (IgeConfPriv));
+}
+
+static void
+ige_conf_init (IgeConf *conf)
+{
+}
+
+static void
+conf_atexit (void)
+{
+        if (global_conf) {
+                IgeConfPriv *priv = GET_PRIVATE (global_conf);
+
+                [priv->defaults synchronize];
+        }
+}
+
+IgeConf *
+ige_conf_get (void)
+{
+        if (!global_conf) {
+                global_conf = g_object_new (IGE_TYPE_CONF, NULL);
+                g_atexit (conf_atexit);
+        }
+
+        return global_conf;
+}
+
+void
+ige_conf_add_defaults (IgeConf     *conf,
+                       const gchar *path)
+{
+        IgeConfPriv  *priv = GET_PRIVATE (conf);
+        NSDictionary *dict;
+        GList        *defaults, *l;
+
+        priv->defaults = [NSUserDefaults standardUserDefaults];
+
+        dict = [NSMutableDictionary dictionaryWithCapacity: 10];
+
+        defaults = _ige_conf_defaults_read_file (path, NULL);
+        for (l = defaults; l; l = l->next) {
+                IgeConfDefaultItem *item = l->data;
+                NSString           *key;
+                NSString           *value;
+
+                key = [NSString stringWithUTF8String: item->key];
+                value = [NSString stringWithUTF8String: item->value];
+                [dict setValue:value forKey:key];
+        }
+
+        _ige_conf_defaults_free_list (defaults);
+
+        [priv->defaults registerDefaults: dict];
+}
+
+gboolean
+ige_conf_set_int (IgeConf     *conf,
+                  const gchar *key,
+                  gint         value)
+{
+        IgeConfPriv *priv;
+        NSString    *string;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        string = [NSString stringWithUTF8String: key];
+        [priv->defaults setInteger: value forKey: string];
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_get_int (IgeConf     *conf,
+                  const gchar *key,
+                  gint        *value)
+{
+        IgeConfPriv *priv;
+        NSString    *string;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        string = [NSString stringWithUTF8String: key];
+        *value = [priv->defaults integerForKey: string];
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_set_bool (IgeConf     *conf,
+                   const gchar *key,
+                   gboolean     value)
+{
+        IgeConfPriv *priv;
+        NSString    *string;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        string = [NSString stringWithUTF8String: key];
+        [priv->defaults setBool: value forKey: string];
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_get_bool (IgeConf     *conf,
+                   const gchar *key,
+                   gboolean    *value)
+{
+        IgeConfPriv *priv;
+        NSString    *string;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        string = [NSString stringWithUTF8String: key];
+        *value = [priv->defaults boolForKey: string];
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_set_string (IgeConf     *conf,
+                     const gchar *key,
+                     const gchar *value)
+{
+        IgeConfPriv *priv;
+        NSString    *string, *nsvalue;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        string = [NSString stringWithUTF8String: key];
+        nsvalue = [NSString stringWithUTF8String: value];
+
+        [priv->defaults setObject: nsvalue forKey: string];
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_get_string (IgeConf      *conf,
+                     const gchar  *key,
+                     gchar       **value)
+{
+        IgeConfPriv *priv;
+        NSString    *string, *nsvalue;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        *value = NULL;
+
+        string = [NSString stringWithUTF8String: key];
+        nsvalue = [priv->defaults stringForKey: string];
+        if (nsvalue == NULL) {
+                return FALSE;
+        }
+
+        *value = g_strdup ([nsvalue UTF8String]);
+
+        return TRUE;
+}
+
+gboolean
+ige_conf_set_string_list (IgeConf     *conf,
+                          const gchar *key,
+                          GSList      *value)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        return TRUE; /*gconf_client_set_string_list (priv->gconf_client,
+                                             key,
+                                             value,
+                                             NULL);
+                     */
+}
+
+gboolean
+ige_conf_get_string_list (IgeConf      *conf,
+                          const gchar  *key,
+                          GSList      **value)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        *value = NULL; /*gconf_client_get_string_list (priv->gconf_client,
+                                               key,
+                                               &error);
+                       */
+        return TRUE;
+}
+
+/*
+static void
+conf_notify_data_free (IgeConfNotifyData *data)
+{
+        g_object_unref (data->conf);
+        g_slice_free (IgeConfNotifyData, data);
+}
+
+static void
+conf_notify_func (GConfClient *client,
+                  guint        id,
+                  GConfEntry  *entry,
+                  gpointer     user_data)
+{
+        IgeConfNotifyData *data;
+
+        data = user_data;
+
+        data->func (data->conf,
+                    gconf_entry_get_key (entry),
+                    data->user_data);
+}
+*/
+
+guint
+ige_conf_notify_add (IgeConf           *conf,
+                     const gchar       *key,
+                     IgeConfNotifyFunc  func,
+                     gpointer           user_data)
+{
+        IgeConfPriv       *priv;
+        guint              id;
+        IgeConfNotifyData *data;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), 0);
+
+        priv = GET_PRIVATE (conf);
+
+        data = g_slice_new (IgeConfNotifyData);
+        data->func = func;
+        data->user_data = user_data;
+        data->conf = g_object_ref (conf);
+
+        id = 0; /*gconf_client_notify_add (priv->gconf_client,
+                                      key,
+                                      conf_notify_func,
+                                      data,
+                                      (GFreeFunc) conf_notify_data_free,
+                                      NULL);
+        */
+        return id;
+}
+
+gboolean
+ige_conf_notify_remove (IgeConf *conf,
+                        guint    id)
+{
+        IgeConfPriv *priv;
+
+        g_return_val_if_fail (IGE_IS_CONF (conf), FALSE);
+
+        priv = GET_PRIVATE (conf);
+
+        /*gconf_client_notify_remove (priv->gconf_client, id);*/
+
+        return TRUE;
+}


Modified: devhelp/devhelp/ige-conf-private.h
54 files changed, 54 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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.
+ */
+
+#ifndef __IGE_CONF_PRIVATE_H__
+#define __IGE_CONF_PRIVATE_H__
+
+#include <glib.h>
+#include "ige-conf.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+        IGE_CONF_TYPE_INT,
+        IGE_CONF_TYPE_BOOLEAN,
+        IGE_CONF_TYPE_STRING
+} IgeConfType;
+
+typedef struct {
+        IgeConfType  type;
+        gchar       *key;
+        gchar       *value;
+} IgeConfDefaultItem;
+
+GList *      _ige_conf_defaults_read_file  (const gchar  *path,
+					    GError      **error);
+void         _ige_conf_defaults_free_list  (GList        *defaults);
+gchar *      _ige_conf_defaults_get_root   (GList        *defaults);
+const gchar *_ige_conf_defaults_get_string (GList        *defaults,
+					    const gchar  *key);
+gint         _ige_conf_defaults_get_int    (GList        *defaults,
+					    const gchar  *key);
+gboolean     _ige_conf_defaults_get_bool   (GList        *defaults,
+					    const gchar  *key);
+
+G_END_DECLS
+
+#endif /* __IGE_CONF_PRIVATE_H__ */


Modified: devhelp/devhelp/ige-conf.c
337 files changed, 337 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,337 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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 <stdlib.h>
+#include "ige-conf-private.h"
+
+typedef struct {
+        GString      *text;
+
+        gchar        *current_key;
+        gchar        *current_value;
+        IgeConfType   current_type;
+
+        GList        *defaults;
+} DefaultData;
+
+#define BYTES_PER_READ 4096
+
+static void
+parser_start_cb (GMarkupParseContext  *context,
+                 const gchar          *node_name,
+                 const gchar         **attribute_names,
+                 const gchar         **attribute_values,
+                 gpointer              user_data,
+                 GError              **error)
+{
+	DefaultData *data = user_data;
+
+        if (g_ascii_strcasecmp (node_name, "applyto") == 0) {
+                data->text = g_string_new (NULL);
+        }
+        else if (g_ascii_strcasecmp (node_name, "type") == 0) {
+                data->text = g_string_new (NULL);
+        }
+        else if (g_ascii_strcasecmp (node_name, "default") == 0) {
+                data->text = g_string_new (NULL);
+        }
+}
+
+static void
+parser_end_cb (GMarkupParseContext  *context,
+               const gchar          *node_name,
+               gpointer              user_data,
+               GError              **error)
+{
+	DefaultData *data = user_data;
+
+        if (g_ascii_strcasecmp (node_name, "schema") == 0) {
+                IgeConfDefaultItem *item;
+
+                item = g_slice_new0 (IgeConfDefaultItem);
+                item->key = data->current_key;
+                item->type = data->current_type;
+
+                switch (item->type) {
+                case IGE_CONF_TYPE_INT:
+                case IGE_CONF_TYPE_STRING:
+                        item->value = g_strdup (data->current_value);
+                        break;
+                case IGE_CONF_TYPE_BOOLEAN:
+                        if (strcmp (data->current_value, "true") == 0) {
+                                item->value = g_strdup ("YES");
+                        } else {
+                                item->value = g_strdup ("NO");
+                        }
+                        break;
+                }
+
+                data->defaults = g_list_prepend (data->defaults, item);
+
+                data->current_key = NULL;
+
+                g_free (data->current_value);
+                data->current_value = NULL;
+        }
+        else if (g_ascii_strcasecmp (node_name, "applyto") == 0) {
+                data->current_key = g_string_free (data->text, FALSE);
+                data->text = NULL;
+        }
+        else if (g_ascii_strcasecmp (node_name, "type") == 0) {
+                gchar *str;
+
+                str = g_string_free (data->text, FALSE);
+                if (strcmp (str, "int") == 0) {
+                        data->current_type = IGE_CONF_TYPE_INT;
+                }
+                else if (strcmp (str, "bool") == 0) {
+                        data->current_type = IGE_CONF_TYPE_BOOLEAN;
+                }
+                else if (strcmp (str, "string") == 0) {
+                        data->current_type = IGE_CONF_TYPE_STRING;
+                }
+
+                g_free (str);
+                data->text = NULL;
+        }
+        else if (g_ascii_strcasecmp (node_name, "default") == 0) {
+                data->current_value = g_string_free (data->text, FALSE);
+                data->text = NULL;
+        }
+}
+
+static void
+parser_text_cb (GMarkupParseContext  *context,
+                const gchar          *text,
+                gsize                 text_len,
+                gpointer              user_data,
+                GError              **error)
+{
+        DefaultData *data = user_data;
+
+        if (data->text) {
+                g_string_append_len (data->text, text, text_len);
+        }
+}
+
+static void
+parser_error_cb (GMarkupParseContext *context,
+		 GError              *error,
+		 gpointer             user_data)
+{
+	g_warning ("Error: %s\n", error->message);
+}
+
+GList *
+_ige_conf_defaults_read_file (const gchar  *path,
+                              GError      **error)
+{
+        DefaultData          data;
+	GMarkupParser       *parser;
+	GMarkupParseContext *context;
+	GIOChannel          *io = NULL;
+	gchar                buf[BYTES_PER_READ];
+
+        io = g_io_channel_new_file (path, "r", error);
+        if (!io) {
+                return NULL;
+        }
+
+	parser = g_new0 (GMarkupParser, 1);
+
+	parser->start_element = parser_start_cb;
+	parser->end_element = parser_end_cb;
+	parser->text = parser_text_cb;
+	parser->error = parser_error_cb;
+
+        memset (&data, 0, sizeof (DefaultData));
+
+	context = g_markup_parse_context_new (parser,
+                                              0,
+                                              &data,
+                                              NULL);
+
+        while (TRUE) {
+                GIOStatus io_status;
+                gsize     bytes_read;
+
+                io_status = g_io_channel_read_chars (io, buf, BYTES_PER_READ,
+                                                     &bytes_read, error);
+                if (io_status == G_IO_STATUS_ERROR) {
+                        goto exit;
+                }
+                if (io_status != G_IO_STATUS_NORMAL) {
+                        break;
+                }
+
+                g_markup_parse_context_parse (context, buf, bytes_read, error);
+                if (error != NULL && *error != NULL) {
+                        goto exit;
+                }
+
+                if (bytes_read < BYTES_PER_READ) {
+                        break;
+                }
+        }
+
+ exit:
+        g_io_channel_unref (io);
+	g_markup_parse_context_free (context);
+	g_free (parser);
+
+	return data.defaults;
+}
+
+void
+_ige_conf_defaults_free_list (GList *defaults)
+{
+        GList *l;
+
+        for (l = defaults; l; l = l->next) {
+                IgeConfDefaultItem *item = l->data;
+
+                g_free (item->value);
+                g_slice_free (IgeConfDefaultItem, item);
+        }
+
+        g_list_free (defaults);
+}
+
+gchar *
+_ige_conf_defaults_get_root (GList *defaults)
+{
+        GList  *l;
+        gchar  *root;
+        gchar **strv_prev = NULL;
+        gint    i;
+        gint    last_common = G_MAXINT;
+
+        for (l = defaults; l; l = l->next) {
+                IgeConfDefaultItem  *item = l->data;
+                gchar              **strv;
+
+                strv = g_strsplit (item->key, "/", 0);
+                if (strv_prev == NULL) {
+                        strv_prev = strv;
+                        continue;
+                }
+
+                i = 0;
+                while (strv[i] && strv_prev[i] && i < last_common) {
+                        if (strcmp (strv[i], strv_prev[i]) != 0) {
+                                last_common = i;
+                                break;
+                        }
+                        i++;
+                }
+
+                g_strfreev (strv_prev);
+                strv_prev = strv;
+        }
+
+        if (strv_prev) {
+                GString *str;
+
+                str = g_string_new (NULL);
+                i = 0;
+                while (strv_prev[i] && i < last_common) {
+                        if (strv_prev[i][0] != '\0') {
+                                g_string_append_c (str, '/');
+                                g_string_append (str, strv_prev[i]);
+                        }
+                        i++;
+                }
+                root = g_string_free (str, FALSE);
+                g_strfreev (strv_prev);
+        } else {
+                root = g_strdup ("/");
+        }
+
+        return root;
+}
+
+static IgeConfDefaultItem *
+defaults_get_item (GList       *defaults,
+                   const gchar *key)
+{
+        GList  *l;
+
+        for (l = defaults; l; l = l->next) {
+                IgeConfDefaultItem *item = l->data;
+
+                if (strcmp (item->key, key) == 0) {
+                        return item;
+                }
+        }
+
+        return NULL;
+}
+
+const gchar *
+_ige_conf_defaults_get_string (GList       *defaults,
+                               const gchar *key)
+{
+        IgeConfDefaultItem *item;
+
+        item = defaults_get_item (defaults, key);
+
+        if (item) {
+                return item->value;
+        }
+
+        return NULL;
+}
+
+gint
+_ige_conf_defaults_get_int (GList       *defaults,
+                            const gchar *key)
+{
+        IgeConfDefaultItem *item;
+
+        item = defaults_get_item (defaults, key);
+
+        if (item) {
+                return strtol (item->value, NULL, 10);
+        }
+
+        return 0;
+}
+
+gboolean
+_ige_conf_defaults_get_bool (GList       *defaults,
+                             const gchar *key)
+{
+        IgeConfDefaultItem *item;
+
+        item = defaults_get_item (defaults, key);
+
+        if (item) {
+                if (strcmp (item->value, "false") == 0) {
+                        return FALSE;
+                }
+                else if (strcmp (item->value, "true") == 0) {
+                        return TRUE;
+                }
+        }
+
+        return FALSE;
+}


Modified: devhelp/devhelp/ige-conf.h
87 files changed, 87 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Imendio AB
+ *
+ * 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.
+ */
+
+#ifndef __IGE_CONF_H__
+#define __IGE_CONF_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IGE_TYPE_CONF         (ige_conf_get_type ())
+#define IGE_CONF(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), IGE_TYPE_CONF, IgeConf))
+#define IGE_CONF_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), IGE_TYPE_CONF, IgeConfClass))
+#define IGE_IS_CONF(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), IGE_TYPE_CONF))
+#define IGE_IS_CONF_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), IGE_TYPE_CONF))
+#define IGE_CONF_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), IGE_TYPE_CONF, IgeConfClass))
+
+typedef struct _IgeConf      IgeConf;
+typedef struct _IgeConfClass IgeConfClass;
+
+struct _IgeConf  {
+        GObject parent_instance;
+};
+
+struct _IgeConfClass {
+        GObjectClass parent_class;
+};
+
+typedef void (*IgeConfNotifyFunc) (IgeConf     *conf,
+                                   const gchar *key,
+                                   gpointer     user_data);
+
+GType       ige_conf_get_type        (void);
+IgeConf    *ige_conf_get             (void);
+void        ige_conf_add_defaults    (IgeConf            *conf,
+				      const gchar        *path);
+guint       ige_conf_notify_add      (IgeConf            *conf,
+                                      const gchar        *key,
+                                      IgeConfNotifyFunc   func,
+                                      gpointer            data);
+gboolean    ige_conf_notify_remove   (IgeConf            *conf,
+                                      guint               id);
+gboolean    ige_conf_set_int         (IgeConf            *conf,
+                                      const gchar        *key,
+                                      gint                value);
+gboolean    ige_conf_get_int         (IgeConf            *conf,
+                                      const gchar        *key,
+                                      gint               *value);
+gboolean    ige_conf_set_bool        (IgeConf            *conf,
+                                      const gchar        *key,
+                                      gboolean            value);
+gboolean    ige_conf_get_bool        (IgeConf            *conf,
+                                      const gchar        *key,
+                                      gboolean           *value);
+gboolean    ige_conf_set_string      (IgeConf            *conf,
+                                      const gchar        *key,
+                                      const gchar        *value);
+gboolean    ige_conf_get_string      (IgeConf            *conf,
+                                      const gchar        *key,
+                                      gchar             **value);
+gboolean    ige_conf_set_string_list (IgeConf            *conf,
+                                      const gchar        *key,
+                                      GSList             *value);
+gboolean    ige_conf_get_string_list (IgeConf            *conf,
+                                      const gchar        *key,
+                                      GSList            **value);
+
+G_END_DECLS
+
+#endif /* __IGE_CONF_H__ */


Modified: devhelp/src/Makefile.am
9 files changed, 6 insertions(+), 3 deletions(-)
===================================================================
@@ -10,7 +10,6 @@ EXTRA_LTLIBRARIES = devhelp.la
 endif
 
 devhelp_la_SOURCES = \
-	dhp-codesearch.c \
 	dhp-manpages.c \
 	dhp-object.c \
 	dhp-plugin.c
@@ -24,9 +23,13 @@ noinst_HEADERS = \
 
 devhelp_la_CFLAGS = \
 	$(AM_CFLAGS) \
+	-I$(top_srcdir)/devhelp \
 	$(DEVHELP_CFLAGS) \
-	-DDHPLUG_DATA_DIR=\"$(plugindatadir)\"
+	-DDHPLUG_DATA_DIR=\"$(plugindatadir)\" \
+	-DHAVE_BOOK_MANAGER=1
 
-devhelp_la_LIBADD = $(DEVHELP_LIBS)
+devhelp_la_LIBADD = \
+	$(DEVHELP_LIBS) \
+	$(top_builddir)/devhelp/devhelp/libdevhelp-2.la
 
 include $(top_srcdir)/build/cppcheck.mk


Modified: devhelp/src/dhp-codesearch.c
101 files changed, 0 insertions(+), 101 deletions(-)
===================================================================
@@ -1,101 +0,0 @@
-#include <glib.h>
-#include <webkit/webkitwebview.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include <geanyplugin.h>
-
-#include "dhp.h"
-
-
-struct LangMapEnt
-{
-	const gchar *geany_name;
-	const gchar *google_name;
-};
-
-
-#define GOOGLE_CODE_SEARCH_URI "http://www.google.com/codesearch"
-
-#define LANG_MAP_MAX 33 /* update this with lang_map[] size below */
-
-/* maps Geany language names to Google Code language names */
-static const struct LangMapEnt lang_map[LANG_MAP_MAX] = {
-	{ "ActionScript", "actionscript" },
-	{ "Ada", "ada" },
-	{ "ASM", "assembly" },
-	{ "FreeBasic", "basic" },
-	{ "C", "c" },
-	{ "C++", "c++" },
-	{ "C#", "c#" },
-	{ "COBOL", "cobol" },
-	{ "CSS", "css" },
-	{ "D", "d" },
-	{ "Erlang", "erlang" },
-	{ "Fortran", "fortran" },
-	{ "Haskell", "haskell" },
-	{ "Java", "java" },
-	{ "Javascript", "javascript" },
-	{ "Lisp", "lisp" },
-	{ "Lua", "lua" },
-	{ "Make", "makefile" },
-	{ "Matlab/Octave", "matlab" },
-	{ "CAML", "ocaml" },
-	{ "Pascal", "pascal" },
-	{ "Perl", "perl" },
-	{ "PHP", "php" },
-	{ "Python", "python" },
-	{ "R", "r" },
-	{ "Ruby", "ruby" },
-	{ "Sh", "shell" },
-	{ "SQL", "sql" },
-	{ "Tcl", "tcl" },
-	{ "LaTeX", "tex" },
-	{ "Verilog", "verilog" },
-	{ "VHDL", "vhdl" },
-	{ "None", NULL }
-};
-
-
-void devhelp_plugin_search_code(DevhelpPlugin *self, const gchar *term, const gchar *lang)
-{
-	gint i;
-	gchar *uri, *term_enc, *lang_enc;
-	const gchar *google_lang = NULL;
-
-	g_return_if_fail(self != NULL);
-	g_return_if_fail(term != NULL);
-
-	if (lang != NULL)
-	{
-		for (i = 0; i < LANG_MAP_MAX; i++)
-		{
-			if (g_strcmp0(lang, lang_map[i].geany_name) == 0)
-			{
-				google_lang = lang_map[i].google_name;
-				break;
-			}
-		}
-	}
-
-	if (google_lang != NULL)
-	{
-		lang_enc = g_uri_escape_string(google_lang, NULL, TRUE);
-		term_enc = g_uri_escape_string(term, NULL, TRUE);
-		uri = g_strdup_printf("%s?as_q=%s&as_lang=%s", GOOGLE_CODE_SEARCH_URI, term_enc, lang_enc);
-		g_free(lang_enc);
-		g_free(term_enc);
-	}
-	else
-	{
-		term_enc = g_uri_escape_string(term, NULL, TRUE);
-		uri = g_strdup_printf("%s?as_q=%s", GOOGLE_CODE_SEARCH_URI, term_enc);
-		g_free(term_enc);
-	}
-
-	webkit_web_view_open(devhelp_plugin_get_webview(self), uri);
-	g_free(uri);
-
-	devhelp_plugin_activate_webview_tab(self);
-}


Modified: devhelp/src/dhp-manpages.c
3 files changed, 2 insertions(+), 1 deletions(-)
===================================================================
@@ -120,7 +120,8 @@
 gchar *devhelp_plugin_manpages_search(DevhelpPlugin *self, const gchar *term, const gchar *section)
 {
 	FILE *fp = NULL;
-	gint fd = -1, len;
+	gint fd = -1;
+	gsize len;
 	gchar *man_fn = NULL, *tmp_fn = NULL, *uri = NULL;
 	gchar *text = NULL, *html_text = NULL;
 	const gchar *tmpl = "devhelp_manpage_XXXXXX.html";


Modified: devhelp/src/dhp-object.c
29 files changed, 1 insertions(+), 28 deletions(-)
===================================================================
@@ -119,7 +119,6 @@ enum
 /* Internal callbacks */
 static void on_search_help_activate(GtkMenuItem * menuitem, DevhelpPlugin *self);
 static void on_search_help_man_activate(GtkMenuItem * menuitem, DevhelpPlugin *self);
-static void on_search_help_code_activate(GtkMenuItem *menuitem, DevhelpPlugin *self);
 static void on_editor_menu_popup(GtkWidget * widget, DevhelpPlugin *self);
 static void on_link_clicked(GObject * ignored, DhLink * dhlink, DevhelpPlugin *self);
 static void on_back_button_clicked(GtkToolButton * btn, DevhelpPlugin *self);
@@ -377,7 +376,7 @@ static void devhelp_plugin_init_dh(DevhelpPlugin *self)
 /* Initialize the stuff in the editor's context menu */
 static void devhelp_plugin_init_edit_menu(DevhelpPlugin *self)
 {
-	GtkWidget *doc_menu, *devhelp_item, *code_item, *man_item;
+	GtkWidget *doc_menu, *devhelp_item, *man_item;
 	DevhelpPluginPrivate *p;
 
 	g_return_if_fail(self != NULL);
@@ -401,11 +400,6 @@ static void devhelp_plugin_init_edit_menu(DevhelpPlugin *self)
 		gtk_widget_show(man_item);
 	}
 
-	code_item = gtk_menu_item_new_with_label(_("Google Code"));
-	gtk_menu_shell_append(GTK_MENU_SHELL(doc_menu), code_item);
-	g_signal_connect(code_item, "activate", G_CALLBACK(on_search_help_code_activate), self);
-	gtk_widget_show(code_item);
-
 	g_signal_connect(geany->main_widgets->editor_menu, "show", G_CALLBACK(on_editor_menu_popup), self);
 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(p->editor_menu_item), doc_menu);
 	gtk_menu_shell_append(GTK_MENU_SHELL(geany->main_widgets->editor_menu), p->editor_menu_sep);
@@ -1277,27 +1271,6 @@ static void on_search_help_man_activate(GtkMenuItem * menuitem, DevhelpPlugin *s
 }
 
 
-static void on_search_help_code_activate(GtkMenuItem *menuitem, DevhelpPlugin *self)
-{
-	gchar *current_tag;
-	const gchar *lang = NULL;
-	GeanyDocument *doc;
-
-	g_return_if_fail(self != NULL);
-
-	if ((current_tag = devhelp_plugin_get_current_word(self)) == NULL)
-		return;
-
-	doc = document_get_current();
-	if (doc != NULL && doc->file_type != NULL && doc->file_type->name != NULL)
-		lang = doc->file_type->name;
-
-	devhelp_plugin_search_code(self, current_tag, lang);
-
-	g_free(current_tag);
-}
-
-
 /*
  * Called when the editor context menu is shown so that the devhelp
  * search item can be disabled if there isn't a selected tag.


Modified: devhelp/src/dhp-plugin.c
20 files changed, 0 insertions(+), 20 deletions(-)
===================================================================
@@ -60,7 +60,6 @@ enum
 	KB_DEVHELP_ACTIVATE_DEVHELP,
 	KB_DEVHELP_SEARCH_SYMBOL,
 	KB_DEVHELP_SEARCH_MANPAGES,
-	KB_DEVHELP_SEARCH_CODESEARCH,
 	KB_COUNT
 };
 
@@ -101,23 +100,6 @@ static void kb_activate(guint key_id)
 			g_free(current_tag);
 			break;
 		}
-		case KB_DEVHELP_SEARCH_CODESEARCH:
-		{
-			const gchar *lang = NULL;
-			GeanyDocument *doc;
-
-			if ((current_tag = devhelp_plugin_get_current_word(plugin.devhelp)) == NULL)
-				return;
-
-			doc = document_get_current();
-			if (doc == NULL || doc->file_type == NULL || doc->file_type->name == NULL)
-				lang = doc->file_type->name;
-
-			devhelp_plugin_search_code(plugin.devhelp, current_tag, lang);
-
-			g_free(current_tag);
-			break;
-		}
 	}
 }
 
@@ -201,8 +183,6 @@ void plugin_init(GeanyData *data)
 		keybindings_set_item(key_group, KB_DEVHELP_SEARCH_MANPAGES, kb_activate,
 			0, 0, "devhelp_search_manpages", _("Search for current tag in Manual Pages"), NULL);
 	}
-	keybindings_set_item(key_group, KB_DEVHELP_SEARCH_CODESEARCH, kb_activate,
-		0, 0, "devhelp_search_codesearch", _("Search for current tag in Google Code Search"), NULL);
 }
 
 


Modified: devhelp/src/dhp.h
4 files changed, 0 insertions(+), 4 deletions(-)
===================================================================
@@ -125,10 +125,6 @@ struct _DevhelpPluginClass
 void			devhelp_plugin_remove_manpages_temp_files	(DevhelpPlugin *self);
 
 
-/* Google Code Search (see codesearch.c) */
-void devhelp_plugin_search_code(DevhelpPlugin *self, const gchar *term, const gchar *lang);
-
-
 /* TODO: make properties for these */
 gboolean devhelp_plugin_get_devhelp_sidebar_visible(DevhelpPlugin *self);
 void devhelp_plugin_set_devhelp_sidebar_visible(DevhelpPlugin *self, gboolean visible);


Modified: devhelp/wscript_build
31 files changed, 24 insertions(+), 7 deletions(-)
===================================================================
@@ -23,13 +23,30 @@
 
 from build.wafutils import build_plugin
 
-
 name = 'Devhelp'
-includes = ['devhelp/src']
-libraries = [ 'GTK', 'GLIB', 'GTHREAD', 'LIBDEVHELP', 'WEBKIT' ]
-sources = [ 'src/dhp-codesearch.c',
-			'src/dhp-manpages.c',
-			'src/dhp-object.c',
-			'src/dhp-plugin.c' ]
+includes = [ '../devhelp', 'devhelp/src', 'devhelp/devhelp' ]
+libraries = [ 'GTK', 'GTHREAD', 'WEBKIT', 'LIBWNCK', 'GCONF2', 'ZLIB' ]
+sources = [ "devhelp/dh-assistant.c",
+			"devhelp/dh-assistant-view.c",
+			"devhelp/dh-base.c",
+			"devhelp/dh-book.c",
+			"devhelp/dh-book-manager.c",
+			"devhelp/dh-book-tree.c",
+			"devhelp/dh-enum-types.c",
+			"devhelp/dh-error.c",
+			"devhelp/dh-keyword-model.c",
+			"devhelp/dh-link.c",
+			"devhelp/dh-marshal.c",
+			"devhelp/dh-parser.c",
+			"devhelp/dh-preferences.c",
+			"devhelp/dh-search.c",
+			"devhelp/dh-util.c",
+			"devhelp/dh-window.c",
+			"devhelp/eggfindbar.c",
+			"devhelp/ige-conf.c",
+			"devhelp/ige-conf-gconf.c",
+			"src/dhp-manpages.c",
+			"src/dhp-object.c",
+			"src/dhp-plugin.c" ]
 
 build_plugin(bld, name, sources=sources, includes=includes, libraries=libraries)


Modified: devhelp/wscript_configure
31 files changed, 6 insertions(+), 25 deletions(-)
===================================================================
@@ -26,9 +26,11 @@ from build.wafutils import add_to_env_and_define, check_cfg_cached
 
 packages = [
 	('gtk+-2.0', '2.16', 'GTK'),
-    ('glib-2.0', '2.16', 'GLIB'),
     ('gthread-2.0','','GTHREAD'),
-    ('webkit-1.0', '1.1.18', 'WEBKIT')
+    ('webkit-1.0', '1.1.13', 'WEBKIT'),
+    ('libwnck-1.0', '2.10.0', 'LIBWNCK'),
+    ('gconf-2.0', '2.6.0', 'GCONF2'),
+    ('zlib', '', 'ZLIB'),
     ]
 
 for package_name, package_version, uselib_store in packages:
@@ -39,26 +41,5 @@ for package_name, package_version, uselib_store in packages:
                      mandatory=True,
                      args='--cflags --libs')
 
-
-# Use newer libdevhelp-2.0 if present, and fallback on older libdevhelp-1.0
-check_cfg_cached(conf,
-                 package='libdevhelp-2.0',
-                 atleast_version='2.32.0',
-                 uselib_store='LIBDEVHELP',
-                 mandatory=False,
-                 args='--cflags --libs')
-
-
-if conf.env['HAVE_LIBDEVHELP'] == 1:
-    add_to_env_and_define(conf, 'HAVE_BOOK_MANAGER', 1)
-else:
-    # fallback
-    check_cfg_cached(conf,
-                     package='libdevhelp-1.0',
-                     atleast_version='2.30.1',
-                     uselib_store='LIBDEVHELP',
-                     mandatory=False,
-                     args='--cflags --libs')
-    # finally raise an error if we didn't find any suitable devhelp library to disable this plugin
-    if not conf.env['HAVE_LIBDEVHELP'] == 1:
-        raise ConfigurationError('libdevhelp is necessary for the devhelp plugin')
+add_to_env_and_define(conf, 'HAVE_BOOK_MANAGER', 1)
+add_to_env_and_define(conf, 'PACKAGE_VERSION', 1)



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: TBD).



More information about the Plugins-Commits mailing list