yelp patches



I've been working on adding some features to yelp and wish to work on
getting them upstreamed.

Included are 4 yelp patches.

The first, yelp-helpuri.patch, enables yelp to recognize help: uris as
used by KDE.  It will also find things in some gnome directories (with
the same behavior as the patch I added to khelpcenter for SuSE a while
back.)  I've written up a standard, but it needs some discussion.  This
could go in as is to match with KDE.

The second, yelp-libmenu.patch, causes yelp to use the menu system to
generate its table of contents instead of using scrollkeeper.  This will
allow us to get rid of the installtime hit from scrollkeeper as well as
make the help menu match the Applications menu.  This is just reading
from a .menu file, so we can have a help.menu file that includes
applications.menu so we can include stuff not in the menus.

The third, yelp-printing.patch, adds printing support to yelp.  It will
print either the page or the whole document.

The fourth, yelp-toctree.patch, displays the table of contents as a tree
on a single page instead of across multiple pages.

All 4 patches can be applied to the same tree.

Known bugs:

- Printing the whole document leaks some memory because I'm not sure how
to know when the printing is done so I can free the temporary mozilla
window.

- Having the table of contents tree remember which nodes are open across
sessions requires yelp_html_initialize from yelp-printing.patch, but I
didn't want to have the patches conflict too much.


Enjoy,
   Chris
Index: src/yelp-utils.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-utils.c,v
retrieving revision 1.26
diff -u -p -r1.26 yelp-utils.c
--- src/yelp-utils.c	28 Oct 2005 20:37:37 -0000	1.26
+++ src/yelp-utils.c	31 Oct 2005 19:10:29 -0000
@@ -68,6 +68,7 @@ struct _YelpDocInfo {
 
 static YelpDocType  get_doc_type       (gchar   *uri);
 static gchar *      convert_ghelp_uri  (gchar   *uri);
+static gchar *      convert_help_uri   (gchar   *uri);
 
 static gchar *      convert_man_uri    (gchar   *uri);
 static gchar *      convert_info_uri   (gchar   *uri);
@@ -78,7 +79,7 @@ yelp_doc_info_new (const gchar *uri)
     YelpDocInfo *doc;
     gchar       *doc_uri  = NULL;
     gchar       *full_uri = NULL;
-    YelpDocType  doc_type;
+    YelpDocType  doc_type = YELP_DOC_TYPE_EXTERNAL;
     YelpURIType  uri_type;
     gchar *cur;
 
@@ -106,6 +107,27 @@ yelp_doc_info_new (const gchar *uri)
 	    doc_type = get_doc_type (doc_uri);
 	uri_type = YELP_URI_TYPE_GHELP;
     }
+    else if (g_str_has_prefix (full_uri, "help:")) {
+	doc_uri  = convert_help_uri (full_uri);
+	if (doc_uri)
+	    doc_type = get_doc_type (doc_uri);
+#if 0
+	if ((cur = strchr (doc_uri, '#'))) {
+	    char *temp;
+
+	    if (!strchr (full_uri, '#')) {
+		temp = full_uri;
+		full_uri = g_strconcat (full_uri, "#", cur + 1, NULL);
+		g_free (temp);
+	    }
+
+	    temp = doc_uri;
+	    doc_uri = g_strndup (doc_uri, cur - doc_uri);
+	    g_free (temp);
+	}
+#endif
+	uri_type = YELP_URI_TYPE_HELP;
+    }
     else if (g_str_has_prefix (full_uri, "man:")) {
 	doc_uri  = convert_man_uri (full_uri);
 	doc_type = YELP_DOC_TYPE_MAN;
@@ -117,7 +139,7 @@ yelp_doc_info_new (const gchar *uri)
 	uri_type = YELP_URI_TYPE_INFO;
     }
     else if (g_str_has_prefix (full_uri, "x-yelp-toc:")) {
-	doc_uri = g_strdup ("file://" DATADIR "/yelp/toc.xml");
+	doc_uri = g_strdup ("file://" DATADIR "/yelp/xslt/toc2html.xsl");
 	doc_type = YELP_DOC_TYPE_TOC;
 	uri_type = YELP_URI_TYPE_TOC;
     }
@@ -189,7 +211,7 @@ yelp_doc_info_get (const gchar *uri)
     if (!doc) {
 	doc = yelp_doc_info_new (doc_uri);
 	if (doc && doc->type != YELP_DOC_TYPE_EXTERNAL) {
-	    YelpDocInfo *old_doc;
+	    YelpDocInfo *old_doc = NULL;
 
 	    for (i = 0; i < doc->num_uris; i++) {
 		old_doc = g_hash_table_lookup (doc_info_table,
@@ -478,6 +500,18 @@ yelp_uri_get_fragment (const gchar *uri)
 	    if (*(++cur) != '\0')
 		frag_id = g_strdup (cur);
 
+    if (g_str_has_prefix (uri, "help:"))
+	if (g_str_has_suffix (uri, ".html")) {
+	    YelpDocInfo *doc = yelp_doc_info_get (uri);
+	    char *file_uri;
+	    file_uri = yelp_doc_info_get_uri (doc, NULL, YELP_URI_TYPE_FILE);
+	    if (!g_str_has_suffix (file_uri, ".html")) {
+		cur = strrchr (uri, '/');
+		frag_id = g_strndup (cur + 1, strlen (cur + 1) - strlen (".html"));
+	    }
+	    g_free (file_uri);
+	}
+
     if ((cur = strchr (uri, '#')))
 	if (*(++cur) != '\0') {
 	    if (frag_id)
@@ -520,7 +554,7 @@ get_doc_type (gchar *uri)
     if (mime_type == NULL)
 	return YELP_DOC_TYPE_ERROR;
 
-    if (g_str_equal (mime_type, "text/xml"))
+    if (g_str_equal (mime_type, "text/xml") || g_str_equal (mime_type, "application/docbook+xml"))
 	type = YELP_DOC_TYPE_DOCBOOK_XML;
     else if (g_str_equal (mime_type, "text/sgml"))
 	type = YELP_DOC_TYPE_DOCBOOK_SGML;
@@ -567,6 +601,161 @@ yelp_doc_info_add_uri (YelpDocInfo *doc_
 
 /******************************************************************************/
 /** Convert fancy URIs to file URIs *******************************************/
+
+static gchar *
+help_uri_check_file (gchar *filename, gchar *reference) {
+    d(g_print ("Checking %s\n", filename));
+    if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
+	gchar *full_uri;
+	if (reference) {
+	    full_uri = g_strconcat ("file://", filename, NULL, "#", reference, NULL);
+	} else {
+	    full_uri = g_strconcat ("file://", filename, NULL);
+	}
+
+	return full_uri;
+    }
+
+    return NULL;
+}
+
+static gchar *
+help_uri_check_dir (gchar *path, gchar *identifier, gchar *reference)
+{
+    gchar *result;
+
+    gchar *full_path;
+    gchar *full_identifier;
+
+    result = help_uri_check_file (path, reference);
+    if (result)
+	return result;
+
+    full_path = g_build_filename (path, "index.docbook", NULL);
+    result = help_uri_check_file (full_path, reference);
+    g_free (full_path);
+    if (result)
+	return result;
+
+    full_identifier = g_strconcat (identifier, ".xml", NULL);
+    full_path = g_build_filename (path, full_identifier, NULL);
+    result = help_uri_check_file (full_path, reference);
+    g_free (full_path);
+    g_free (full_identifier);
+    if (result)
+	return result;
+
+    return NULL;
+}
+
+static gchar *
+help_uri_search (gchar *path, gchar *identifier, char *reference)
+{
+    gchar *result;
+
+    result = help_uri_check_dir (path, identifier, reference);
+    if (result)
+	return result;
+
+    if (strlen (path) > 5 && ! strcmp (path + strlen (path) - 5, ".html")) {
+	gchar *slash = strrchr (path, '/');
+	if (slash != NULL) {
+	    gchar *shortened = g_strndup (path, slash - path);
+	    gchar *shortened_reference = g_strndup (slash + 1, strlen (slash + 1) - 5);
+	    result = help_uri_check_dir (shortened, identifier, reference ? reference : shortened_reference);
+	    g_free (reference);
+	    g_free (shortened);
+	    if (result)
+		return result;
+	}
+    }
+    return NULL;
+}
+
+static gchar *
+help_uri_expand_datadirs (gchar *path, gchar *identifier, char *reference)
+{
+    char const* const* data_dirs = g_get_system_data_dirs ();
+    int i;
+    char *result;
+    for (i = 0; data_dirs[i]; i++) {
+	char *full_path = g_build_filename (data_dirs[i], path, NULL);
+	result = help_uri_search (full_path, identifier, reference);
+	g_free (full_path);
+	if (result)
+	    return result;
+    }
+    return NULL;
+}
+
+static gchar *
+convert_help_uri (gchar *uri)
+{
+    gchar  *path;
+    gchar  *file_name = NULL;
+    const gchar * const * langs;
+    gchar *result = NULL;
+    gchar *full_path;
+    int i;
+    gchar *reference;
+
+    if ((path = strchr(uri, ':')))
+	path++;
+    else
+	return NULL;
+
+    while (*path == '/')
+	path++;
+
+    path = g_strdup (path);
+
+    reference = strchr (path, '#');
+    if (reference) {
+	*reference = 0;
+	reference ++;
+    }
+
+    /* This isn't the filename so much as the rest of the path after the first bit of the path. */
+    file_name = strchr (path, '/');
+    if (file_name) {
+	*file_name = 0;
+	file_name ++;
+    } else {
+	file_name = "";
+    }
+
+    langs = g_get_language_names ();
+
+    for (i = 0; langs[i] != NULL; i++) {
+	full_path = g_strdup_printf ("/gnome/help/%s/%s/%s", path, langs[i], file_name);
+	help_uri_expand_datadirs (full_path, path, reference);
+	g_free (full_path);
+	if (result)
+	    goto end;
+
+	full_path = g_strdup_printf ("/doc/HTML/%s/%s/%s", langs[i], path, file_name);
+	help_uri_expand_datadirs (full_path, path, reference);
+	g_free (full_path);
+	if (result)
+	    goto end;
+    }
+
+    full_path = g_strdup_printf ("/gnome/help/%s/C/%s", path, file_name);
+    result = help_uri_expand_datadirs (full_path, path, reference);
+    g_free (full_path);
+    if (result)
+	goto end;
+
+    full_path = g_strdup_printf ("/doc/HTML/en/%s/%s", path, file_name);
+    result = help_uri_expand_datadirs (full_path, path, reference);
+    g_free (full_path);
+    if (result)
+	goto end;
+
+ end:
+    g_free (path);
+    return result;
+}
 
 static gchar *
 locate_file_lang (gchar *path, gchar *file, const gchar *lang)
Index: src/yelp-utils.h
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-utils.h,v
retrieving revision 1.12
diff -u -p -r1.12 yelp-utils.h
--- src/yelp-utils.h	28 Oct 2005 20:37:37 -0000	1.12
+++ src/yelp-utils.h	31 Oct 2005 19:10:29 -0000
@@ -67,6 +67,7 @@ typedef enum {
     YELP_URI_TYPE_TOC      = 1 << 4,
     YELP_URI_TYPE_EXTERNAL = 1 << 5,
     YELP_URI_TYPE_SEARCH   = 1 << 6,
+    YELP_URI_TYPE_HELP     = 1 << 7,
 
     YELP_URI_TYPE_NO_FILE =
       YELP_URI_TYPE_GHELP    |
@@ -74,9 +75,10 @@ typedef enum {
       YELP_URI_TYPE_INFO     |
       YELP_URI_TYPE_TOC      |
       YELP_URI_TYPE_EXTERNAL |
+      YELP_URI_TYPE_HELP     |
       YELP_URI_TYPE_SEARCH,
     YELP_URI_TYPE_ANY =
-      YELP_URI_TYPE_FILE    |
+      YELP_URI_TYPE_FILE     |
       YELP_URI_TYPE_NO_FILE
 } YelpURIType;
 
Index: configure.in
===================================================================
RCS file: /cvs/gnome/yelp/configure.in,v
retrieving revision 1.192
diff -u -p -r1.192 configure.in
--- configure.in	28 Oct 2005 20:37:36 -0000	1.192
+++ configure.in	31 Oct 2005 18:44:10 -0000
@@ -63,6 +63,7 @@ PKG_CHECK_MODULES(YELP,
 	libxml-2.0 >= 2.6.5
 	libxslt >= 1.1.4
 	libexslt >= 0.8.1
+	libgnome-menu >= 2.11.1
 ])
 AC_SUBST([YELP_CFLAGS])
 AC_SUBST([YELP_LIBS])
Index: src/yelp-toc-pager.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-toc-pager.c,v
retrieving revision 1.51
diff -u -p -r1.51 yelp-toc-pager.c
--- src/yelp-toc-pager.c	21 Oct 2005 01:47:56 -0000	1.51
+++ src/yelp-toc-pager.c	31 Oct 2005 18:44:10 -0000
@@ -34,6 +34,7 @@
 #include <libxml/xpath.h>
 #include <libxml/xpathInternals.h>
 #include <libxml/HTMLtree.h>
+#include <libxml/tree.h>
 #include <libxslt/xslt.h>
 #include <libxslt/templates.h>
 #include <libxslt/transform.h>
@@ -46,6 +47,12 @@
 #include "yelp-toc-pager.h"
 #include "yelp-utils.h"
 
+#define GMENU_I_KNOW_THIS_IS_UNSTABLE
+#include <gmenu-tree.h>
+
+#define DESKTOP_ENTRY_GROUP     "Desktop Entry"
+#define KDE_DESKTOP_ENTRY_GROUP "KDE Desktop Entry"
+
 #ifdef YELP_DEBUG
 #define d(x) x
 #else
@@ -96,6 +103,9 @@ struct _YelpTocPagerPriv {
 
     xsltStylesheetPtr       stylesheet;
     xsltTransformContextPtr transformContext;
+
+    GMenuTree *libmenu_tree;
+    GSList *libmenu_stack;
 };
 
 struct _YelpListing {
@@ -125,10 +135,14 @@ GtkTreeModel *       toc_pager_get_secti
 static gboolean      toc_process_pending       (YelpTocPager      *pager);
 
 
-static gboolean      process_read_menu         (YelpTocPager      *pager);
+static gboolean      process_libmenu           (YelpTocPager      *pager);
+static gboolean      process_libmenu_node      (YelpTocPager      *pager);
 static gboolean      process_xslt              (YelpTocPager      *pager);
+#ifdef ENABLE_SCROLLKEEPER
+static gboolean      process_read_menu         (YelpTocPager      *pager);
 static gboolean      process_read_scrollkeeper (YelpTocPager      *pager);
 static gboolean      process_omf_pending       (YelpTocPager      *pager);
+#endif
 #ifdef ENABLE_MAN
 static gboolean      process_mandir_pending    (YelpTocPager      *pager);
 #endif
@@ -137,11 +151,13 @@ static gboolean      process_info_dir_pe
 static gboolean      process_info_pending      (YelpTocPager      *pager);
 #endif
 
+#ifdef ENABLE_SCROLLKEEPER
 static void          toc_add_doc_info          (YelpTocPager      *pager,
 						YelpDocInfo       *doc_info);
 static void          toc_remove_doc_info       (YelpTocPager      *pager,
 						YelpDocInfo       *doc_info);
 static void          xml_trim_titles           (xmlNodePtr         node);
+#endif
 static void          xslt_yelp_document        (xsltTransformContextPtr ctxt,
 						xmlNodePtr              node,
 						xmlNodePtr              inst,
@@ -213,6 +229,9 @@ toc_pager_init (YelpTocPager *pager)
     priv->cancel       = 0;
     priv->pause_count  = 0;
     priv->pending_func = NULL;
+
+    priv->libmenu_tree  = NULL;
+    priv->libmenu_stack = NULL;
 }
 
 static void
@@ -320,7 +339,7 @@ toc_pager_process (YelpPager *pager)
     yelp_pager_set_state (pager, YELP_PAGER_STATE_RUNNING);
     g_signal_emit_by_name (pager, "start");
 
-    priv->pending_func = (ProcessFunction) process_read_menu;
+    priv->pending_func = (ProcessFunction) process_libmenu;
 
     gtk_idle_add_priority (G_PRIORITY_LOW,
 			   (GtkFunction) toc_process_pending,
@@ -351,10 +370,15 @@ toc_process_pending (YelpTocPager *pager
     gboolean readd;
     YelpTocPagerPriv *priv = pager->priv;
     static gpointer process_funcs[] = {
+	process_libmenu,
+	process_libmenu_node,
+	process_xslt,
+#ifdef ENABLE_SCROLLKEEPER
 	process_read_menu,
 	process_read_scrollkeeper,
 	process_omf_pending,
 	process_xslt,
+#endif
 #ifdef ENABLE_MAN
 	process_mandir_pending,
 	process_xslt,
@@ -386,6 +410,204 @@ toc_process_pending (YelpTocPager *pager
 	return FALSE;
 }
 
+static void
+set_icon (xmlNode *node, const char *icon)
+{
+    if (icon) {
+	GtkIconInfo *info;
+	GtkIconTheme *theme = 
+	    (GtkIconTheme *) yelp_settings_get_icon_theme ();
+	info = gtk_icon_theme_lookup_icon (theme, icon, 48, 0);
+	if (info) {
+	    xmlNodePtr new = xmlNewChild (node, NULL, "icon", NULL);
+	    xmlNewNsProp (new, NULL, "file", gtk_icon_info_get_filename (info));
+	    gtk_icon_info_free (info);
+	}
+    }
+}
+
+typedef struct {
+    xmlNode *tree;
+    GSList *children;
+    GSList *iterator;
+    gboolean non_empty;
+} LibmenuStackFrame;
+
+static void
+push_stack_frame (YelpTocPager *pager, GMenuTreeDirectory *dir)
+{
+    LibmenuStackFrame *frame;
+    YelpTocPagerPriv *priv  = YELP_TOC_PAGER (pager)->priv;
+
+    frame = g_new (LibmenuStackFrame, 1);
+    frame->iterator = frame->children = gmenu_tree_directory_get_contents (dir);
+    frame->non_empty = FALSE;
+
+    frame->tree = xmlNewNode (NULL, "toc");
+    xmlSetProp (frame->tree, "id", gmenu_tree_directory_get_menu_id (dir));
+    xmlNewTextChild (frame->tree, NULL, "title", gmenu_tree_directory_get_name (dir));
+    set_icon (frame->tree, gmenu_tree_directory_get_icon (dir));
+
+    priv->libmenu_stack = g_slist_prepend (priv->libmenu_stack, frame);
+}
+
+static void
+pop_stack_frame (YelpTocPager *pager)
+{
+    LibmenuStackFrame *frame;
+    YelpTocPagerPriv *priv  = YELP_TOC_PAGER (pager)->priv;
+
+    frame = priv->libmenu_stack->data;
+
+    g_slist_free (frame->children);
+    g_free (frame);
+
+    priv->libmenu_stack = g_slist_delete_link (priv->libmenu_stack, priv->libmenu_stack);
+}
+
+static GTimer *timer;
+/* iterator points to next node to process and is incremented at the
+   end of the function.  NULL means finished with this level.  Parent
+   iterators are incremented before processing the child tree. */
+static gboolean
+process_libmenu_node (YelpTocPager *pager)
+{
+    LibmenuStackFrame *frame;
+    GMenuTreeItem *item;
+    YelpTocPagerPriv *priv  = YELP_TOC_PAGER (pager)->priv;
+    gulong ms_start;
+
+
+    frame = priv->libmenu_stack->data;
+
+    if (frame->iterator == NULL) {
+	if (priv->libmenu_stack->next) {
+	    if (frame->non_empty) {
+		xmlNode *tree = frame->tree;
+		pop_stack_frame (pager);
+
+		frame = priv->libmenu_stack->data;
+		xmlAddChild (frame->tree, tree);
+		frame->non_empty = TRUE;
+	    } else {
+		xmlFreeNode (frame->tree);
+		pop_stack_frame(pager);
+	    }
+
+	    return TRUE;
+	} else {
+	    YelpTocPagerPriv *priv  = YELP_TOC_PAGER (pager)->priv;
+	    xmlNode *title;
+
+	    priv->toc_doc = xmlNewDoc ("1.0");
+	    xmlSetProp (frame->tree, "id", "index");
+	    xmlDocSetRootElement (priv->toc_doc, frame->tree);
+
+	    for (title = frame->tree->children; title; title = title->next) {
+		if (g_str_equal (title->name, "title")) {
+		    xmlNodeSetContent (title, _("Help Topics"));
+		    break;
+		}
+	    }
+	    if (title == NULL) {
+		xmlNewTextChild (frame->tree, NULL, "title", _("Help Topics"));
+	    }
+
+	    pop_stack_frame (pager);
+
+	    gmenu_tree_unref (priv->libmenu_tree);
+	    priv->libmenu_tree = NULL;
+
+
+
+	    return FALSE;
+	}
+    }
+
+    item = frame->iterator->data;
+
+    switch (gmenu_tree_item_get_type (item)) {
+    case GMENU_TREE_ITEM_DIRECTORY: 
+	{
+	    push_stack_frame (pager, GMENU_TREE_DIRECTORY (item));
+	}
+	break;
+    case GMENU_TREE_ITEM_ENTRY:
+	{
+	    GMenuTreeEntry *entry = GMENU_TREE_ENTRY (item);
+	    const char     *path = gmenu_tree_entry_get_desktop_file_path (entry);
+	    const char     *desktop_entry_group;
+	    char           *docpath = NULL;
+	    GKeyFile       *key_file;
+
+	    key_file = g_key_file_new ();
+
+
+	    if (!g_key_file_load_from_file (key_file, path, 0, NULL)) {
+		g_key_file_free (key_file);
+		break;
+	    }
+
+	    if (g_key_file_has_group (key_file, DESKTOP_ENTRY_GROUP)) {
+		desktop_entry_group = DESKTOP_ENTRY_GROUP;
+	    } else {
+		if (g_key_file_has_group (key_file, KDE_DESKTOP_ENTRY_GROUP)) {
+		    desktop_entry_group = KDE_DESKTOP_ENTRY_GROUP;
+		} else {
+		    g_key_file_free (key_file);
+		    break;
+		}
+	    }
+
+	    docpath = g_key_file_get_string (key_file, desktop_entry_group, "DocPath", NULL);
+
+	    if (docpath) {
+		xmlNode *new = xmlNewChild (frame->tree, NULL, "doc", NULL);
+		if (strchr(docpath, ':')) {
+		    xmlNewNsProp (new, NULL, "href", docpath);
+		} else {
+		    char *href = g_strdup_printf ("help:%s", docpath);
+		    xmlNewNsProp (new, NULL, "href", href);
+		    g_free (href);
+		}
+
+		xmlNewTextChild (new, NULL, "title", gmenu_tree_entry_get_name (entry));
+		xmlNewTextChild (new, NULL, "description", gmenu_tree_entry_get_comment (entry));
+		set_icon (new, gmenu_tree_entry_get_icon (entry));
+
+		frame->non_empty = TRUE;
+
+		g_free (docpath);
+	    }
+	}
+	break;
+    default:
+	/* Ignore */
+	break;
+    }
+
+
+    frame->iterator = frame->iterator->next;
+    return TRUE;
+}
+
+
+static gboolean
+process_libmenu (YelpTocPager *pager)
+{
+    YelpTocPagerPriv *priv  = YELP_TOC_PAGER (pager)->priv;
+
+
+    priv->libmenu_tree = gmenu_tree_lookup ("applications.menu",
+					     GMENU_TREE_FLAGS_NONE);
+
+    push_stack_frame (pager, gmenu_tree_get_root_directory (priv->libmenu_tree));
+
+    return FALSE;
+}
+
+#ifdef ENABLE_SCROLLKEEPER
+
 /** process_read_scrollkeeper *************************************************/
 
 static void
@@ -559,6 +781,7 @@ process_omf_pending (YelpTocPager *pager
     else
 	return FALSE;
 }
+#endif
 
 #ifdef ENABLE_MAN
 static gboolean
@@ -752,6 +975,7 @@ process_info_pending (YelpTocPager *page
 }
 #endif /* ENABLE_INFO */
 
+#ifdef ENABLE_SCROLLKEEPER
 static gboolean
 process_read_menu (YelpTocPager *pager)
 {
@@ -796,17 +1020,7 @@ process_read_menu (YelpTocPager *pager)
 	xml_trim_titles (node);
 
 	icon = xmlGetProp (node, "icon");
-	if (icon) {
-	    GtkIconInfo *info;
-	    GtkIconTheme *theme = 
-		(GtkIconTheme *) yelp_settings_get_icon_theme ();
-	    info = gtk_icon_theme_lookup_icon (theme, icon, 48, 0);
-	    if (info) {
-		xmlNodePtr new = xmlNewChild (node, NULL, "icon", NULL);
-		xmlNewNsProp (new, NULL, "file", gtk_icon_info_get_filename (info));
-		gtk_icon_info_free (info);
-	    }
-	}
+	set_icon (node, icon);
 	xmlFree (icon);
 
     }
@@ -864,12 +1078,13 @@ process_read_menu (YelpTocPager *pager)
 
     return FALSE;
 }
+#endif
 
 static gboolean
 process_xslt (YelpTocPager *pager)
 {
     GError *error = NULL;
-    xmlDocPtr outdoc;
+    xmlDocPtr outdoc = NULL;
     YelpTocPagerPriv *priv = pager->priv;
     gchar **params = NULL;
     gint  params_i = 0;
@@ -948,6 +1163,7 @@ process_xslt (YelpTocPager *pager)
     return FALSE;
 }
 
+#ifdef ENABLE_SCROLLKEEPER
 static void
 toc_add_doc_info (YelpTocPager *pager, YelpDocInfo *doc_info)
 {
@@ -1002,6 +1218,8 @@ toc_remove_doc_info (YelpTocPager *pager
 #endif
 }
 
+#endif
+
 static void
 xslt_yelp_document (xsltTransformContextPtr ctxt,
 		    xmlNodePtr              node,
@@ -1122,6 +1340,7 @@ xslt_yelp_document (xsltTransformContext
 	xsltFreeStylesheet (style);
 }
 
+#ifdef ENABLE_SCROLLKEEPER
 static void
 xml_trim_titles (xmlNodePtr node)
 {
@@ -1171,3 +1390,4 @@ xml_trim_titles (xmlNodePtr node)
     }
     xmlFree (keep_lang);
 }
+#endif
? document.ps
? page.ps
Index: data/ui/yelp-ui.xml
===================================================================
RCS file: /cvs/gnome/yelp/data/ui/yelp-ui.xml,v
retrieving revision 1.11
diff -u -p -r1.11 yelp-ui.xml
--- data/ui/yelp-ui.xml	28 Oct 2005 20:37:37 -0000	1.11
+++ data/ui/yelp-ui.xml	31 Oct 2005 18:33:36 -0000
@@ -6,6 +6,9 @@
       <separator/>
       <menuitem action="AboutDocument"/>
       <separator/>
+      <menuitem action="PrintPage"/>
+      <menuitem action="PrintDocument"/>
+      <separator/>
       <menuitem action="CloseWindow"/>
     </menu>
     <menu action="EditMenu">
Index: data/ui/yelp.glade
===================================================================
RCS file: /cvs/gnome/yelp/data/ui/yelp.glade,v
retrieving revision 1.11
diff -u -p -r1.11 yelp.glade
--- data/ui/yelp.glade	15 Oct 2005 16:34:55 -0000	1.11
+++ data/ui/yelp.glade	31 Oct 2005 18:33:36 -0000
@@ -19,6 +19,7 @@
   <property name="skip_pager_hint">False</property>
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -102,6 +103,10 @@
 			  <property name="yalign">0.5</property>
 			  <property name="xpad">0</property>
 			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
 			</widget>
 			<packing>
 			  <property name="padding">0</property>
@@ -173,6 +178,10 @@
 			  <property name="yalign">0.5</property>
 			  <property name="xpad">0</property>
 			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
 			</widget>
 			<packing>
 			  <property name="padding">0</property>
@@ -200,7 +209,7 @@
 	  <property name="visible">True</property>
 	  <property name="xalign">0.5</property>
 	  <property name="yalign">1</property>
-	  <property name="xscale">0.9</property>
+	  <property name="xscale">0.899999976158</property>
 	  <property name="yscale">0.5</property>
 	  <property name="top_padding">0</property>
 	  <property name="bottom_padding">0</property>
@@ -233,6 +242,10 @@
 		      <property name="xpad">0</property>
 		      <property name="ypad">0</property>
 		      <property name="mnemonic_widget">find_entry</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
 		    </widget>
 		    <packing>
 		      <property name="padding">3</property>
@@ -340,6 +353,7 @@
   <property name="skip_pager_hint">False</property>
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -409,6 +423,10 @@
 	      <property name="xpad">0</property>
 	      <property name="ypad">0</property>
 	      <property name="mnemonic_widget">location_entry</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
 	    </widget>
 	    <packing>
 	      <property name="padding">0</property>
@@ -461,6 +479,7 @@
   <property name="skip_pager_hint">False</property>
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -521,6 +540,10 @@
 		  <property name="yalign">0.5</property>
 		  <property name="xpad">0</property>
 		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
 		</widget>
 		<packing>
 		  <property name="padding">0</property>
@@ -597,6 +620,10 @@
 			  <property name="xpad">0</property>
 			  <property name="ypad">0</property>
 			  <property name="mnemonic_widget">variable_font</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
 			</widget>
 			<packing>
 			  <property name="left_attach">0</property>
@@ -622,6 +649,10 @@
 			  <property name="xpad">0</property>
 			  <property name="ypad">0</property>
 			  <property name="mnemonic_widget">fixed_font</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
 			</widget>
 			<packing>
 			  <property name="left_attach">0</property>
@@ -706,6 +737,10 @@
 		  <property name="yalign">0.5</property>
 		  <property name="xpad">0</property>
 		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
 		</widget>
 		<packing>
 		  <property name="padding">0</property>
@@ -779,6 +814,7 @@
   <property name="skip_pager_hint">False</property>
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -837,6 +873,10 @@
 	      <property name="xpad">0</property>
 	      <property name="ypad">0</property>
 	      <property name="mnemonic_widget">bookmarks_view</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
 	    </widget>
 	    <packing>
 	      <property name="left_attach">0</property>
@@ -920,6 +960,9 @@
 		  <property name="rules_hint">False</property>
 		  <property name="reorderable">False</property>
 		  <property name="enable_search">True</property>
+		  <property name="fixed_height_mode">False</property>
+		  <property name="hover_selection">False</property>
+		  <property name="hover_expand">False</property>
 		</widget>
 	      </child>
 	    </widget>
@@ -955,6 +998,7 @@
   <property name="skip_pager_hint">False</property>
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -1036,6 +1080,10 @@
 		  <property name="xpad">0</property>
 		  <property name="ypad">0</property>
 		  <property name="mnemonic_widget">bookmark_title_entry</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
 		</widget>
 		<packing>
 		  <property name="padding">0</property>
@@ -1064,6 +1112,249 @@
 		</packing>
 	      </child>
 	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkDialog" id="print_dialog">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Print</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox10">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area10">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="button2">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-cancel</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-6</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="button3">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="has_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="has_focus">True</property>
+	      <property name="label">gtk-print</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">0</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="vbox135">
+	  <property name="border_width">6</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkRadioButton" id="radiobutton_page">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="active">True</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment15">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">0</property>
+		  <property name="yscale">0</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">0</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkHBox" id="hbox91">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">2</property>
+
+		      <child>
+			<widget class="GtkImage" id="image8">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-file</property>
+			  <property name="icon_size">4</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="label1232">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">Print current pa_ge</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkRadioButton" id="radiobutton_document">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="active">False</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+	      <property name="group">radiobutton_page</property>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment16">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">0</property>
+		  <property name="yscale">0</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">0</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkHBox" id="hbox92">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">2</property>
+
+		      <child>
+			<widget class="GtkImage" id="image9">
+			  <property name="visible">True</property>
+			  <property name="stock">gnome-stock-book-blue</property>
+			  <property name="icon_size">4</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="label1233">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">Print entire _document</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
 	  </child>
 	</widget>
 	<packing>
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/yelp/src/Makefile.am,v
retrieving revision 1.85
diff -u -p -r1.85 Makefile.am
--- src/Makefile.am	28 Oct 2005 20:37:37 -0000	1.85
+++ src/Makefile.am	31 Oct 2005 18:33:36 -0000
@@ -9,6 +9,7 @@ yelp_SOURCES =							\
 	yelp-bookmarks.c 	yelp-bookmarks.h		\
 	yelp-cache.c		yelp-cache.h			\
 	yelp-db-pager.c		yelp-db-pager.h			\
+	yelp-db-print-pager.c	yelp-db-print-pager.h		\
 	yelp-error.c		yelp-error.h			\
 	yelp-gecko-utils.cpp	yelp-gecko-utils.h		\
 	yelp-html.cpp		yelp-html.h			\
@@ -112,6 +113,7 @@ test_man_parser_LDADD = $(YELP_LIBS) $(Z
 
 test_pager_SOURCES =						\
 	yelp-db-pager.c		yelp-db-pager.h			\
+	yelp-db-print-pager.c	yelp-db-print-pager.h		\
 	yelp-error.c		yelp-error.h			\
 	yelp-io-channel.c	yelp-io-channel.h		\
 	yelp-man-pager.c	yelp-man-pager.h		\
Index: src/yelp-db-print-pager.c
===================================================================
RCS file: src/yelp-db-print-pager.c
diff -N src/yelp-db-print-pager.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/yelp-db-print-pager.c	31 Oct 2005 18:33:36 -0000
@@ -0,0 +1,295 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003 Shaun McCance  <shaunm gnome org>
+ *
+ * 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.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xinclude.h>
+#include <libxslt/xslt.h>
+#include <libxslt/templates.h>
+#include <libxslt/transform.h>
+#include <libxslt/extensions.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/xsltutils.h>
+
+#include "yelp-error.h"
+#include "yelp-db-print-pager.h"
+#include "yelp-toc-pager.h"
+#include "yelp-settings.h"
+
+#ifdef YELP_DEBUG
+#define d(x) x
+#else
+#define d(x)
+#endif
+
+#define STYLESHEET_PATH DATADIR"/yelp/xslt/"
+#define DB_STYLESHEET   STYLESHEET_PATH"db2html.xsl"
+#define DB_TITLE        STYLESHEET_PATH"db-title.xsl"
+
+#define BOOK_CHUNK_DEPTH 2
+#define ARTICLE_CHUNK_DEPTH 1
+
+#define EVENTS_PENDING while (yelp_pager_get_state (pager) <= YELP_PAGER_STATE_RUNNING && gtk_events_pending ()) gtk_main_iteration ();
+#define CANCEL_CHECK if (!main_running || yelp_pager_get_state (pager) >= YELP_PAGER_STATE_ERROR) goto done;
+
+extern gboolean main_running;
+
+#define YELP_DB_PRINT_PAGER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DB_PRINT_PAGER, YelpDBPrintPagerPriv))
+
+struct _YelpDBPrintPagerPriv {
+    gchar          *root_id;
+};
+
+static void           db_print_pager_class_init   (YelpDBPrintPagerClass *klass);
+static void           db_print_pager_init         (YelpDBPrintPager      *pager);
+static void           db_print_pager_dispose      (GObject          *gobject);
+
+static void           db_print_pager_cancel       (YelpPager        *pager);
+static xmlDocPtr      db_print_pager_parse        (YelpPager        *pager);
+static gchar **       db_print_pager_params       (YelpPager        *pager);
+
+static const gchar *  db_print_pager_resolve_frag (YelpPager        *pager,
+					     const gchar      *frag_id);
+static GtkTreeModel * db_print_pager_get_sections (YelpPager        *pager);
+
+static YelpPagerClass *parent_class;
+
+GType
+yelp_db_print_pager_get_type (void)
+{
+    static GType type = 0;
+
+    if (!type) {
+	static const GTypeInfo info = {
+	    sizeof (YelpDBPrintPagerClass),
+	    NULL,
+	    NULL,
+	    (GClassInitFunc) db_print_pager_class_init,
+	    NULL,
+	    NULL,
+	    sizeof (YelpDBPrintPager),
+	    0,
+	    (GInstanceInitFunc) db_print_pager_init,
+	};
+	type = g_type_register_static (YELP_TYPE_XSLT_PAGER,
+				       "YelpDBPrintPager", 
+				       &info, 0);
+    }
+    return type;
+}
+
+static void
+db_print_pager_class_init (YelpDBPrintPagerClass *klass)
+{
+    GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+    YelpPagerClass *pager_class  = YELP_PAGER_CLASS (klass);
+    YelpXsltPagerClass *xslt_class = YELP_XSLT_PAGER_CLASS (klass);
+
+    parent_class = g_type_class_peek_parent (klass);
+
+    object_class->dispose = db_print_pager_dispose;
+
+    pager_class->cancel   = db_print_pager_cancel;
+
+    pager_class->resolve_frag = db_print_pager_resolve_frag;
+
+    xslt_class->parse  = db_print_pager_parse;
+    xslt_class->params = db_print_pager_params;
+
+    xslt_class->stylesheet = DB_STYLESHEET;
+
+    g_type_class_add_private (klass, sizeof (YelpDBPrintPagerPriv));
+}
+
+static void
+db_print_pager_init (YelpDBPrintPager *pager)
+{
+    YelpDBPrintPagerPriv *priv;
+
+    pager->priv = priv = YELP_DB_PRINT_PAGER_GET_PRIVATE (pager);
+
+    pager->priv->root_id = NULL;
+}
+
+static void
+db_print_pager_dispose (GObject *object)
+{
+    YelpDBPrintPager *pager = YELP_DB_PRINT_PAGER (object);
+
+    G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/******************************************************************************/
+
+YelpPager *
+yelp_db_print_pager_new (YelpDocInfo *doc_info)
+{
+    YelpDBPrintPager *pager;
+
+    g_return_val_if_fail (doc_info != NULL, NULL);
+
+    pager = (YelpDBPrintPager *) g_object_new (YELP_TYPE_DB_PRINT_PAGER,
+					  "document-info", doc_info,
+					  NULL);
+
+    return (YelpPager *) pager;
+}
+
+static xmlDocPtr
+db_print_pager_parse (YelpPager *pager)
+{
+    YelpDBPrintPagerPriv *priv;
+    YelpDocInfo     *doc_info;
+    gchar           *filename = NULL;
+
+    xmlParserCtxtPtr parserCtxt = NULL;
+    xmlDocPtr doc = NULL;
+
+    xmlChar     *id;
+    GError      *error = NULL;
+
+    d (g_print ("db_print_pager_parse\n"));
+
+    doc_info = yelp_pager_get_doc_info (pager);
+
+    g_return_val_if_fail (pager != NULL, NULL);
+    g_return_val_if_fail (YELP_IS_DB_PRINT_PAGER (pager), NULL);
+    priv = YELP_DB_PRINT_PAGER (pager)->priv;
+
+    g_object_ref (pager);
+
+    if (yelp_pager_get_state (pager) >= YELP_PAGER_STATE_ERROR)
+	goto done;
+
+    filename = yelp_doc_info_get_filename (doc_info);
+
+    parserCtxt = xmlNewParserCtxt ();
+    doc = xmlCtxtReadFile (parserCtxt,
+			   (const char *) filename, NULL,
+			   XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+			   XML_PARSE_NOENT   | XML_PARSE_NONET   );
+    if (doc == NULL) {
+	g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_DOC,
+		     _("The file â??%sâ?? could not be parsed. Either the file "
+		       "does not exist, or it is not well-formed XML."),
+		     filename);
+	yelp_pager_error (pager, error);
+	goto done;
+    }
+
+    xmlXIncludeProcessFlags (doc,
+			     XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+			     XML_PARSE_NOENT   | XML_PARSE_NONET   );
+
+
+    priv->root_id = g_strdup ("index");
+
+    EVENTS_PENDING;
+    CANCEL_CHECK;
+
+ done:
+    g_free (filename);
+
+    if (parserCtxt)
+	xmlFreeParserCtxt (parserCtxt);
+
+    g_object_unref (pager);
+
+    return doc;
+}
+
+static gchar **
+db_print_pager_params (YelpPager *pager)
+{
+    YelpDBPrintPagerPriv *priv;
+    YelpDocInfo     *doc_info;
+    gchar **params;
+    gint params_i = 0;
+    gint params_max = 20;
+
+    d (g_print ("db_print_pager_process\n"));
+
+    doc_info = yelp_pager_get_doc_info (pager);
+
+    g_return_val_if_fail (pager != NULL, FALSE);
+    g_return_val_if_fail (YELP_IS_DB_PRINT_PAGER (pager), FALSE);
+    priv = YELP_DB_PRINT_PAGER (pager)->priv;
+
+    if (yelp_pager_get_state (pager) >= YELP_PAGER_STATE_ERROR)
+	return NULL;
+
+    params = g_new0 (gchar *, params_max);
+
+    yelp_settings_params (&params, &params_i, &params_max);
+
+    if ((params_i + 10) >= params_max - 1) {
+	params_max += 20;
+	params = g_renew (gchar *, params, params_max);
+    }
+    params[params_i++] = "db.chunk.extension";
+    params[params_i++] = g_strdup ("\"\"");
+    params[params_i++] = "db.chunk.info_basename";
+    params[params_i++] = g_strdup ("\"index\"");
+    params[params_i++] = "db.chunk.max_depth";
+    params[params_i++] = g_strdup_printf ("0");
+    params[params_i++] = "yelp.javascript";
+    params[params_i++] = g_strdup_printf ("\"%s\"", DATADIR "/yelp/yelp.js");
+
+    params[params_i] = NULL;
+
+    return params;
+}
+
+static void
+db_print_pager_cancel (YelpPager *pager)
+{
+    YelpDBPrintPagerPriv *priv = YELP_DB_PRINT_PAGER (pager)->priv;
+
+    d (g_print ("db_print_pager_cancel\n"));
+
+    yelp_pager_set_state (pager, YELP_PAGER_STATE_INVALID);
+
+    g_free (priv->root_id);
+    priv->root_id = NULL;
+
+    YELP_PAGER_CLASS (parent_class)->cancel (pager);
+}
+
+static const gchar *
+db_print_pager_resolve_frag (YelpPager *pager, const gchar *frag_id)
+{
+    YelpDBPrintPager  *db_print_pager;
+
+    g_return_val_if_fail (pager != NULL, NULL);
+    g_return_val_if_fail (YELP_IS_DB_PRINT_PAGER (pager), NULL);
+
+    db_print_pager = YELP_DB_PRINT_PAGER (pager);
+
+    return frag_id;
+}
+
Index: src/yelp-db-print-pager.h
===================================================================
RCS file: src/yelp-db-print-pager.h
diff -N src/yelp-db-print-pager.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/yelp-db-print-pager.h	31 Oct 2005 18:33:36 -0000
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003 Shaun McCance  <shaunm gnome org>
+ *
+ * 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.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifndef __YELP_DB_PRINT_PAGER_H__
+#define __YELP_DB_PRINT_PAGER_H__
+
+#include <glib-object.h>
+
+#include "yelp-pager.h"
+#include "yelp-xslt-pager.h"
+
+#define YELP_TYPE_DB_PRINT_PAGER         (yelp_db_print_pager_get_type ())
+#define YELP_DB_PRINT_PAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DB_PRINT_PAGER, YelpDBPrintPager))
+#define YELP_DB_PRINT_PAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DB_PRINT_PAGER, YelpDBPrintPagerClass))
+#define YELP_IS_DB_PRINT_PAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DB_PRINT_PAGER))
+#define YELP_IS_DB_PRINT_PAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DB_PRINT_PAGER))
+#define YELP_DB_PRINT_PAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DB_PRINT_PAGER, YelpDBPrintPagerClass))
+
+typedef struct _YelpDBPrintPager      YelpDBPrintPager;
+typedef struct _YelpDBPrintPagerClass YelpDBPrintPagerClass;
+typedef struct _YelpDBPrintPagerPriv  YelpDBPrintPagerPriv;
+
+struct _YelpDBPrintPager {
+    YelpXsltPager    parent;
+
+    YelpDBPrintPagerPriv *priv;
+};
+
+struct _YelpDBPrintPagerClass {
+    YelpXsltPagerClass   parent_class;
+};
+
+GType           yelp_db_print_pager_get_type     (void);
+YelpPager *     yelp_db_print_pager_new          (YelpDocInfo *doc_info);
+
+#endif /* __YELP_DB_PRINT_PAGER_H__ */
Index: src/yelp-html.cpp
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-html.cpp,v
retrieving revision 1.1
diff -u -p -r1.1 yelp-html.cpp
--- src/yelp-html.cpp	16 May 2005 21:02:33 -0000	1.1
+++ src/yelp-html.cpp	31 Oct 2005 18:33:36 -0000
@@ -34,6 +34,12 @@
 
 #include "Yelper.h"
 
+#include "gtkmozembed_internal.h"
+#include "nsIWebBrowserPrint.h"
+#include "nsIInterfaceRequestorUtils.h"
+
+#include <libgnome/gnome-init.h>
+
 #ifdef GNOME_ENABLE_DEBUG
 #define d(x) x
 #else
@@ -42,6 +48,9 @@
 
 #define YELP_HTML_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_HTML, YelpHtmlPriv))
 
+#define MOZILLA_PROFILE_DIR  "/mozilla"
+#define MOZILLA_PROFILE_NAME "yelp"
+
 struct _YelpHtmlPriv {
     Yelper      *yelper;
     gchar       *base_uri;
@@ -51,6 +60,7 @@ struct _YelpHtmlPriv {
 static void      html_set_fonts          (void);
 static void      html_set_colors         (void);
 static void      html_set_a11y           (void);
+static void      open_new_window (GtkMozEmbed **newEmbed, guint chrome_mask);
 
 enum {
     URI_SELECTED,
@@ -120,6 +130,14 @@ html_realize (GtkWidget *widget)
 }
 
 static void
+html_new_window (GtkMozEmbed *embed,
+		 GtkMozEmbed **newEmbed,
+		 guint chrome_mask)
+{
+    open_new_window (newEmbed, chrome_mask);
+}
+
+static void
 html_init (YelpHtml *html)
 {
     YelpHtmlPriv  *priv;
@@ -197,6 +215,7 @@ html_class_init (YelpHtmlClass *klass)
     moz_embed_class->title = html_title;
     moz_embed_class->dom_mouse_down = html_dom_mouse_down;
     moz_embed_class->open_uri = html_open_uri;
+    moz_embed_class->new_window = html_new_window;
 
     klass->font_handler = 0;
 
@@ -385,6 +404,24 @@ yelp_html_select_all (YelpHtml *html)
     html->priv->yelper->DoCommand ("cmd_selectAll");
 }
 
+void
+yelp_html_print (YelpHtml *html)
+{
+    nsCOMPtr<nsIWebBrowser> webBrowser;
+
+    gtk_moz_embed_get_nsIWebBrowser  (GTK_MOZ_EMBED (html), getter_AddRefs(webBrowser));
+
+    if (webBrowser) {
+
+	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(webBrowser));
+
+	if (print) {
+	    /* FIXME: Deal with retval. */
+	    print->Print (nsnull, nsnull);
+	}
+    }
+}
+
 static void
 html_set_fonts (void)
 {
@@ -421,4 +458,123 @@ html_set_a11y (void)
 
     caret = yelp_settings_get_caret ();
     yelp_gecko_set_caret (caret);
+}
+
+static void
+new_window_orphan_cb (GtkMozEmbedSingle *moz_single,
+		      GtkMozEmbed **newEmbed,
+		      guint chrome_mask,
+		      gpointer user_data)
+{
+    open_new_window (newEmbed, chrome_mask);
+}
+
+void
+yelp_html_initialize (void)
+{
+    static gboolean initialized = FALSE;
+    GtkMozEmbedSingle *single;
+    char *profile_path;
+
+    if (initialized)
+	return;
+    initialized = TRUE;
+
+    /* get single */
+    single = gtk_moz_embed_single_get ();
+    if (single == NULL) {
+	g_warning ("Failed to get singleton embed object!\n");
+    }
+
+    /* allow creation of orphan windows */
+    g_signal_connect (G_OBJECT (single), "new_window_orphan",
+		      G_CALLBACK (new_window_orphan_cb),
+		      NULL);
+
+    profile_path = g_build_filename (g_get_home_dir (),
+				     GNOME_DOT_GNOME,
+				     "yelp.d",
+				     MOZILLA_PROFILE_DIR, 
+				     NULL);
+    gtk_moz_embed_set_profile_path (profile_path, MOZILLA_PROFILE_NAME);
+    g_free (profile_path);
+}
+
+static GtkMozEmbed *new_xul_dialog (void);
+
+static void
+xul_visibility_cb (GtkWidget *embed, gboolean visibility, GtkWidget *window)
+{
+    if (visibility) {
+	gtk_widget_show (window);
+    } else {
+	gtk_widget_hide (window);
+    }
+}
+
+static void
+xul_size_to_cb (GtkWidget *embed, gint width, gint height, gpointer dummy)
+{
+        gtk_widget_set_size_request (embed, width, height);
+}
+
+static void
+xul_new_window_cb (GtkMozEmbed *embed,
+                   GtkMozEmbed **retval,
+                   guint chrome_mask,
+                   gpointer dummy)
+{
+    g_assert (chrome_mask & GTK_MOZ_EMBED_FLAG_OPENASCHROME);
+
+    *retval = new_xul_dialog ();
+}
+
+static void
+xul_title_cb (GtkMozEmbed *embed,
+              GtkWindow *window)
+{
+    char *title;
+
+    title = gtk_moz_embed_get_title (embed);
+    gtk_window_set_title (window, title);
+    g_free (title);
+}
+
+static GtkMozEmbed *
+new_xul_dialog (void)
+{
+    GtkWidget *window, *embed;
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    embed = gtk_moz_embed_new ();
+    gtk_widget_show (embed);
+    gtk_container_add (GTK_CONTAINER (window), embed);
+
+    g_signal_connect_object (embed, "destroy_browser",
+			     G_CALLBACK (gtk_widget_destroy),
+			     window, G_CONNECT_SWAPPED);
+    g_signal_connect_object (embed, "visibility",
+			     G_CALLBACK (xul_visibility_cb),
+			     window, (GConnectFlags) 0);
+    g_signal_connect_object (embed, "size_to",
+			     G_CALLBACK (xul_size_to_cb),
+			     NULL, (GConnectFlags) 0);
+    g_signal_connect_object (embed, "new_window",
+			     G_CALLBACK (xul_new_window_cb),
+			     NULL, (GConnectFlags) 0);
+    g_signal_connect_object (embed, "title",
+			     G_CALLBACK (xul_title_cb),
+			     window, (GConnectFlags) 0);
+
+    return GTK_MOZ_EMBED (embed);
+}
+
+static void
+open_new_window (GtkMozEmbed **newEmbed,
+		 guint chrome_mask)
+{
+    if (chrome_mask & GTK_MOZ_EMBED_FLAG_OPENASCHROME) {
+	*newEmbed = new_xul_dialog ();
+	return;
+    }
 }
Index: src/yelp-html.h
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-html.h,v
retrieving revision 1.30
diff -u -p -r1.30 yelp-html.h
--- src/yelp-html.h	16 May 2005 21:02:33 -0000	1.30
+++ src/yelp-html.h	31 Oct 2005 18:33:36 -0000
@@ -98,6 +98,10 @@ void            yelp_html_copy_selection
 
 void            yelp_html_select_all     (YelpHtml    *html);
 
+void            yelp_html_print          (YelpHtml    *html);
+
+void            yelp_html_initialize     (void);
+
 G_END_DECLS
 
 #endif /* __YELP_HTML_H__ */
Index: src/yelp-main.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-main.c,v
retrieving revision 1.38
diff -u -p -r1.38 yelp-main.c
--- src/yelp-main.c	6 Mar 2005 20:07:11 -0000	1.38
+++ src/yelp-main.c	31 Oct 2005 18:33:36 -0000
@@ -42,6 +42,7 @@
 #include "GNOME_Yelp.h"
 #include "yelp-window.h"
 #include "yelp-base.h"
+#include "yelp-html.h"
 
 #define YELP_FACTORY_OAFIID "OAFIID:GNOME_Yelp_Factory"
 
@@ -365,6 +366,8 @@ main (int argc, char **argv) 
 	if (!factory) { /* Not started, start now */ 
 		BonoboGenericFactory *factory;
 		char                 *registration_id;
+
+		yelp_html_initialize ();
 
 		registration_id = bonobo_activation_make_registration_id (
 					YELP_FACTORY_OAFIID,
Index: src/yelp-window.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-window.c,v
retrieving revision 1.178
diff -u -p -r1.178 yelp-window.c
--- src/yelp-window.c	28 Oct 2005 20:37:37 -0000	1.178
+++ src/yelp-window.c	31 Oct 2005 18:33:37 -0000
@@ -42,6 +42,7 @@
 
 #include "yelp-bookmarks.h"
 #include "yelp-db-pager.h"
+#include "yelp-db-print-pager.h"
 #include "yelp-error.h"
 #include "yelp-html.h"
 #include "yelp-pager.h"
@@ -160,6 +161,8 @@ static void    window_add_widget        
 					 GtkWidget    *vbox);
 static void    window_new_window_cb     (GtkAction *action, YelpWindow *window);
 static void    window_about_document_cb (GtkAction *action, YelpWindow *window);
+static void    window_print_document_cb (GtkAction *action, YelpWindow *window);
+static void    window_print_page_cb    (GtkAction *action, YelpWindow *window);
 static void    window_open_location_cb  (GtkAction *action, YelpWindow *window);
 static void    window_close_window_cb   (GtkAction *action, YelpWindow *window);
 static void    window_copy_cb           (GtkAction *action, YelpWindow *window);
@@ -308,6 +311,16 @@ static const GtkActionEntry entries[] = 
       "<Control>N",
       NULL,
       G_CALLBACK (window_new_window_cb) },
+    { "PrintDocument", NULL,
+      N_("Print This Document"),
+      NULL,
+      NULL,
+      G_CALLBACK (window_print_document_cb) },
+    { "PrintPage", NULL,
+      N_("Print This Page"),
+      NULL,
+      NULL,
+      G_CALLBACK (window_print_page_cb) },
     { "AboutDocument", NULL,
       N_("About This Document"),
       NULL,
@@ -1925,6 +1938,181 @@ window_new_window_cb (GtkAction *action,
     g_return_if_fail (YELP_IS_WINDOW (window));
 
     g_signal_emit (window, signals[NEW_WINDOW_REQUESTED], 0, NULL);
+}
+
+typedef struct {
+    gulong page_handler;
+    gulong error_handler;
+    gulong cancel_handler;
+    gulong finish_handler;
+    YelpPager *pager;
+} PrintStruct;
+
+static void
+print_disconnect (PrintStruct *data)
+{
+    d(g_print ("print disconnect\n"));
+    if (data->page_handler) {
+	g_signal_handler_disconnect (data->pager,
+				     data->page_handler);
+	data->page_handler = 0;
+    }
+    if (data->error_handler) {
+	g_signal_handler_disconnect (data->pager,
+				     data->error_handler);
+	data->error_handler = 0;
+    }
+    if (data->cancel_handler) {
+	g_signal_handler_disconnect (data->pager,
+				     data->cancel_handler);
+	data->cancel_handler = 0;
+    }
+    if (data->finish_handler) {
+	g_signal_handler_disconnect (data->pager,
+				     data->finish_handler);
+	data->finish_handler = 0;
+    }
+#if 0
+    if (data) {
+	if (data->pager)
+	    g_object_unref (data->pager);
+    }
+#endif
+    g_free (data);
+}
+
+static void
+print_pager_page_cb (YelpPager *pager,
+		     gchar     *page_id,
+		     gpointer   user_data)
+{
+    PrintStruct *data = user_data;
+    YelpPage    *page;
+
+    d (g_print ("print_pager_page_cb\n"));
+    d (g_print ("  page_id=\"%s\"\n", page_id));
+
+    page = (YelpPage *) yelp_pager_get_page (pager, page_id);
+
+    if (page) {
+	YelpHtml *html;
+	GtkWidget *gtk_window;
+	int length, offset;
+	char *uri;
+
+	d(g_print (page->contents));
+
+	gtk_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	html = yelp_html_new ();
+	gtk_container_add (GTK_CONTAINER (gtk_window), GTK_WIDGET (html));
+	gtk_widget_realize (gtk_window);
+	gtk_widget_realize (GTK_WIDGET (html));
+
+
+	uri = yelp_doc_info_get_uri (yelp_pager_get_doc_info (pager),
+				     page_id,
+				     YELP_URI_TYPE_FILE);
+
+	d (g_print ("  uri            = %s\n", uri));
+
+	yelp_html_set_base_uri (html, uri);
+	g_free (uri);
+
+	yelp_html_open_stream (html, "application/xhtml+xml");
+	for (length = strlen (page->contents), offset = 0; length > 0; length -= BUFFER_SIZE, offset += BUFFER_SIZE) {
+	    d(g_print ("data: %.*s\n", MIN (length, BUFFER_SIZE), page->contents + offset));
+	    yelp_html_write (html, page->contents + offset, MIN (length, BUFFER_SIZE));
+	}
+	yelp_html_close (html);
+
+	yelp_html_print (html);
+
+	print_disconnect (data);
+
+	/* This needs to be done at some point, but we need to wait until printing is done. */
+	/* gtk_widget_destroy (gtk_window); */
+    }
+}
+
+static void
+print_pager_error_cb (YelpPager   *pager,
+		      gpointer     user_data)
+{
+    PrintStruct *data = user_data;
+    /*     GError *error = yelp_pager_get_error (pager);*/
+
+    d (g_print ("print_pager_error_cb\n"));
+
+    print_disconnect (data);
+}
+
+static void
+print_pager_cancel_cb (YelpPager   *pager,
+		       gpointer     user_data)
+{
+    PrintStruct *data = user_data;
+    d (g_print ("print_pager_cancel_cb\n"));
+
+    print_disconnect (data);
+}
+
+static void
+print_pager_finish_cb (YelpPager   *pager,
+		       gpointer     user_data)
+{
+    PrintStruct *data = user_data;
+
+    d (g_print ("print_pager_finish_cb\n"));
+
+    print_disconnect (data);
+}
+
+static void
+window_print_document_cb (GtkAction *action, YelpWindow *window)
+{
+    PrintStruct *data;
+    YelpPager *pager;
+
+    if (!window->priv->current_doc)
+	return;
+
+    pager = yelp_db_print_pager_new (window->priv->current_doc);
+
+    if (!pager) {
+	return;
+    }
+
+    data = g_new0 (PrintStruct, 1);
+    data->pager = pager;
+
+    data->page_handler =
+	g_signal_connect (data->pager,
+			  "page",
+			  G_CALLBACK (print_pager_page_cb),
+			  data);
+    data->error_handler =
+	g_signal_connect (data->pager,
+			  "error",
+			  G_CALLBACK (print_pager_error_cb),
+			  data);
+    data->cancel_handler =
+	g_signal_connect (data->pager,
+			  "error",
+			  G_CALLBACK (print_pager_cancel_cb),
+			  data);
+    data->finish_handler =
+	g_signal_connect (data->pager,
+			  "finish",
+			  G_CALLBACK (print_pager_finish_cb),
+			  data);
+
+    /* handled = */ yelp_pager_start (data->pager);
+}
+
+static void
+window_print_page_cb (GtkAction *action, YelpWindow *window)
+{
+    yelp_html_print (window->priv->html_view);
 }
 
 static void
Index: data/yelp.js
===================================================================
RCS file: /cvs/gnome/yelp/data/yelp.js,v
retrieving revision 1.3
diff -u -p -r1.3 yelp.js
--- data/yelp.js	28 Oct 2005 20:37:36 -0000	1.3
+++ data/yelp.js	31 Oct 2005 19:05:14 -0000
@@ -87,6 +87,50 @@ window.addEventListener("load",slt.init,
 window.addEventListener("DOMContentLoaded",slt.init,false);
 window.addEventListener("resize",slt.init,false);
 
+function createCookie(name,value,days)
+{
+	if (days)
+	{
+		var date = new Date();
+		date.setTime(date.getTime()+(days*24*60*60*1000));
+		var expires = "; expires="+date.toGMTString();
+	}
+	else var expires = "";
+	document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function load_hidden ()
+{
+    var ca = document.cookie.split(';');
+    for (var i=0; i < ca.length; i++) {
+	var c = ca[i];
+	while (c.charAt(0)==' ') c = c.substring(1,c.length);
+	if (c.indexOf('display:') == 0) {
+	    var colon = c.indexOf(':');
+	    var equal = c.indexOf('=');
+
+	    var id = c.substring (colon + 1, equal);
+	    var value = c.substring (equal + 1);
+
+	    document.getElementById (id).style.display = value;
+	}
+    }
+}
+
+function show_hide (id)
+{
+    var element = document.getElementById (id + '-children');
+    if (element.style.display == "none") {
+	element.style.display = "block";
+	createCookie ('display:' + id + '-children', "block", 1000);
+    } else {
+	element.style.display = "none";
+	createCookie ('display:' + id + '-children', "none", 1000);
+    }
+}
+
+window.addEventListener("DOMContentLoaded",load_hidden,false);
+
 function submit_search ()
 {
     window.location = "x-yelp-search:" + document.getElementById ('search-entry').value;
Index: src/yelp-toc-pager.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-toc-pager.c,v
retrieving revision 1.51
diff -u -p -r1.51 yelp-toc-pager.c
--- src/yelp-toc-pager.c	21 Oct 2005 01:47:56 -0000	1.51
+++ src/yelp-toc-pager.c	31 Oct 2005 19:05:15 -0000
@@ -873,7 +873,7 @@ process_xslt (YelpTocPager *pager)
     YelpTocPagerPriv *priv = pager->priv;
     gchar **params = NULL;
     gint  params_i = 0;
-    gint  params_max = 10;
+    gint  params_max = 14;
     GtkIconInfo *info;
     GtkIconTheme *theme = (GtkIconTheme *) yelp_settings_get_icon_theme ();
 
@@ -899,8 +899,8 @@ process_xslt (YelpTocPager *pager)
     params = g_new0 (gchar *, params_max);
     yelp_settings_params (&params, &params_i, &params_max);
 
-    if ((params_i + 10) >= params_max - 1) {
-	params_max += 10;
+    if ((params_i + 14) >= params_max - 1) {
+	params_max += 14;
 	params = g_renew (gchar *, params, params_max);
     }
 
@@ -914,6 +914,11 @@ process_xslt (YelpTocPager *pager)
 					      gtk_icon_info_get_base_size (info));
 	gtk_icon_info_free (info);
     }
+
+    params[params_i++] = "yelp.javascript";
+    params[params_i++] = g_strdup_printf ("\"%s\"", DATADIR "/yelp/yelp.js");
+    params[params_i++] = "yelp.topimage";
+    params[params_i++] = g_strdup_printf ("\"%s\"", DATADIR "/yelp/icons/help-title.png");
 
     params[params_i++] = NULL;
 
Index: stylesheets/toc2html.xsl
===================================================================
RCS file: /cvs/gnome/yelp/stylesheets/toc2html.xsl,v
retrieving revision 1.11
diff -u -p -r1.11 toc2html.xsl
--- stylesheets/toc2html.xsl	11 May 2005 18:19:20 -0000	1.11
+++ stylesheets/toc2html.xsl	31 Oct 2005 19:05:15 -0000
@@ -5,9 +5,13 @@
                 extension-element-prefixes="yelp"
                 version="1.0">
 
+<xsl:import href="search-header.xsl"/>
 <xsl:param name="help_icon"/>
 <xsl:param name="help_icon_size"/>
 
+<xsl:param name="yelp.javascript"/>
+<xsl:param name="yelp.topimage"/>
+
 <xsl:param name="yelp.color.fg"/>
 <xsl:param name="yelp.color.bg"/>
 <xsl:param name="yelp.color.anchor"/>
@@ -35,133 +39,181 @@
         <title>
           <xsl:value-of select="title[1]"/>
         </title>
-        <style><xsl:text>
+        <script type="text/javascript">
+          <xsl:attribute name="src">
+            <xsl:value-of select="concat('file://', $yelp.javascript)"/>
+          </xsl:attribute>
+        </script>
+        <style type="text/css"><xsl:text>
+
         body {
           margin: 0px;
           padding: 0px;
-        }
-        h1 {
-          font-size: 1.6em;
-          margin-bottom: 0.4em;
-          margin-top: 12px;
-          margin-left: 12px;
-          margin-right: 12px;
-          padding-left: 204px;
-          padding-top: 0.2em;
-          padding-bottom: 0.2em;
-          -moz-border-radius: 6px;
-          border: solid 1px </xsl:text>
-          <xsl:value-of select="$yelp.color.selected.bg.dark1"/><xsl:text>;
-          background-color: </xsl:text>
-          <xsl:value-of select="$yelp.color.selected.bg"/><xsl:text>;
-          color: </xsl:text>
-          <xsl:value-of select="$yelp.color.selected.fg"/><xsl:text>;
-        }
-        h1 img {
-          position: absolute;
-          top: 6px;
-          right: 18px;
-        }
-        div[class~="body"] { }
-        div[class~="leftbar"] {
-          position: absolute;
-          top: 4em;
-          left: 12px;
-          width: 192px;
-          min-height: 192px;
-          text-align: center;
-          <!-- FIXME: this isn't working -->
-          padding-top: </xsl:text>
-          <xsl:value-of select="$help_icon_size"/><xsl:text> px;
+	  background: white;
           background-image: url("</xsl:text>
-          <xsl:value-of select="$help_icon"/><xsl:text>");
-          background-position: </xsl:text>
-          <xsl:value-of select="(192 - $help_icon_size) div 2"/><xsl:text>px 0px;
-          background-repeat: no-repeat;
-          opacity: .3;
-        }
-        div[class~="rightbar"] {
-          margin-left: 216px;
-          padding-bottom: 1em;
-          margin-right: 12px;
-        }
-        div[class~="tocs"] + div[class~="docs"] {
-          border-top: solid 1px </xsl:text>
-          <xsl:value-of select="$yelp.color.selected.bg"/><xsl:text>;
+          <xsl:value-of select="$yelp.topimage"/><xsl:text>");
+	  background-position: 0px 0px;
+	  background-repeat: no-repeat;
+	  font-family: Segoe, Sans;
+	  font-size: 11px;
+          position: absolute;
+          width: 100%;
         }
-        ul { margin-left: 0em; padding-left: 0em; }
+	div.content {
+	  margin-top: 30px;
+	  padding: 12px;
+	  padding-top: 2px;
+	  padding-left: 38px;
+	}
+	#index-header { 
+	   margin-top: 12px;
+	   color: #333366; 
+	   font-size: 32px;
+	}
+        h1 img {
+           position: relative;
+           top: -6px;
+           right: 18px;
+	   display: none;
+        }
+	h1 {
+	   margin: 0px;
+	   padding: 3px;
+	   font-size: 16pt;
+	}
+	li.toc li.toc h1 {
+	   font-size: 12pt;
+	}
+	h1.child {
+	   color: #003399;
+	}
+	h1.child:hover {
+	   text-decoration: underline; 
+	   background: #e9ece0;
+	   -moz-border-radius: 5px;
+	   color: #003399;
+	   cursor: pointer;
+	}
+	div.docs dl dt {
+	   font-size: 10pt;
+	   margin-left: 24px;
+	   margin-top: 6px;
+	}
+	div.docs dl dd {
+	   font-size: 9pt;
+	   color: #999999;
+	   margin-left: 36px;
+	}	
+	div.docs a {
+	   color: #003399;
+	   text-decoration: none;
+	}
+	div.docs a:hover {
+	   cursor: pointer;
+	   text-decoration: underline;
+	}
+        ul { font-size: 1em; margin-left: 0em; padding-left: 0em; }
         li {
-          margin-top: 0.5em;
-          margin-left: 0em;
+          margin-top: 2px;
+          margin-left: 32px;
           padding-left: 0em;
-          font-size: 1.2em;
           list-style-type: none;
         }
-        dl { margin-left: 0em; padding-left: 0em; }
-        dt { font-size: 1.2em; margin-top: 1em; }
-        dd { margin-left: 1em; margin-top: 0.5em; }
-        a { text-decoration: none; }
-        a:hover { text-decoration: underline; }
+	div.collapse-action { 
+	  font-size: 11px;
+	  color: red;
+	  position: absolute;
+	  top: 34px;
+	  right: 12px;
+	}
         </xsl:text></style>
       </head>
       <body>
+        <div class="collapse-action"><a href="javascript:expand_all()">Expand all categories</a></div>
         <xsl:apply-templates mode="body.mode" select="."/>
       </body>
     </html>
   </yelp:document>
-  <xsl:apply-templates select="toc[.//doc]"/>
+<!--  <xsl:apply-templates select="toc[.//doc]"/> -->
+</xsl:template>
+
+<xsl:template name="header">
+  <xsl:param name="class"/>
+  <h1 id="{ id}-header" onclick="show_hide ('{ id}');" class="{$class}">
+    <xsl:if test="icon">
+      <img src="{icon/@file}"/>
+    </xsl:if>
+    <xsl:apply-templates select="title[1]/node()"/>
+  </h1>
+</xsl:template>
+
+<xsl:template name="children">
+  <xsl:param name="class"/>
+  <div id="{ id}-children">
+    <xsl:if test="@id != 'index'">
+      <xsl:attribute name="style">
+        <xsl:value-of select="'display: none'"/>
+      </xsl:attribute>
+    </xsl:if>
+    <xsl:if test="@id = 'index' or toc[.//doc]">
+      <div class="tocs">
+        <ul>
+          <xsl:for-each select="toc[../@id = 'index' or .//doc]">
+<!--             <xsl:sort select="number(../@id = 'index') * position()"/>
+            <xsl:sort select="normalize-space(title)"/>-->
+            <li class="toc">
+              <xsl:apply-templates mode="children.mode" select="."/>
+            </li>
+          </xsl:for-each>
+        </ul>
+      </div>
+    </xsl:if>
+    <xsl:if test="doc">
+      <div class="docs">
+        <dl>
+          <xsl:for-each select="doc">
+            <xsl:sort select="normalize-space(title)"/>
+            <dt class="doc">
+              <a href="{ href}" title="{ href}">
+                <xsl:if test="tooltip">
+                  <xsl:attribute name="title">
+                    <xsl:value-of select="tooltip"/>
+                  </xsl:attribute>
+                </xsl:if>
+                <xsl:value-of select="title"/>
+              </a>
+            </dt>
+            <dd>
+              <xsl:value-of select="description"/>
+            </dd>
+          </xsl:for-each>
+        </dl>
+      </div>
+    </xsl:if>
+  </div>
 </xsl:template>
 
 <xsl:template mode="body.mode" match="toc">
-  <div class="body">
-    <h1>
-      <xsl:if test="icon">
-        <img src="{icon/@file}"/>
-      </xsl:if>
-      <xsl:apply-templates select="title[1]/node()"/>
-    </h1>
-    <div class="leftbar">
-    </div>
+  <div class="content">
+    <xsl:call-template name="search-header"/>
+    <xsl:call-template name="header">
+      <xsl:with-param name="class" select="'index'"/>
+    </xsl:call-template>
     <div class="rightbar">
-      <xsl:if test="toc[.//doc]">
-        <div class="tocs">
-          <ul>
-            <xsl:for-each select="toc[../@id = 'index' or .//doc]">
-              <xsl:sort select="number(../@id = 'index') * position()"/>
-              <xsl:sort select="normalize-space(title)"/>
-              <li class="toc">
-                <a href="x-yelp-toc:{ id}">
-                  <xsl:apply-templates select="title[1]/node()"/>
-                </a>
-              </li>
-            </xsl:for-each>
-          </ul>
-        </div>
-      </xsl:if>
-      <xsl:if test="doc">
-        <div class="docs">
-          <dl>
-            <xsl:for-each select="doc">
-              <xsl:sort select="normalize-space(title)"/>
-              <dt class="doc">
-                <a href="{ href}" title="{ href}">
-                  <xsl:if test="tooltip">
-                    <xsl:attribute name="title">
-                      <xsl:value-of select="tooltip"/>
-                    </xsl:attribute>
-                  </xsl:if>
-                  <xsl:value-of select="title"/>
-                </a>
-              </dt>
-              <dd>
-                <xsl:value-of select="description"/>
-              </dd>
-            </xsl:for-each>
-          </dl>
-        </div>
-      </xsl:if>
+       <xsl:call-template name="children">
+        <xsl:with-param name="class" select="'index'"/>
+      </xsl:call-template>
     </div>
   </div>
+</xsl:template>
+
+<xsl:template mode="children.mode" match="toc">
+  <xsl:call-template name="header">
+    <xsl:with-param name="class" select="'child'"/>
+  </xsl:call-template>
+  <xsl:call-template name="children">
+    <xsl:with-param name="class" select="'child'"/>
+  </xsl:call-template>
 </xsl:template>
 
 </xsl:stylesheet>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]