diff options
author | Shaun McCance <shaunm@src.gnome.org> | 2004-02-03 21:06:13 +0000 |
---|---|---|
committer | Shaun McCance <shaunm@src.gnome.org> | 2004-02-03 21:06:13 +0000 |
commit | 2bcb7e7f6c1788b96668aed9a938521c50345b06 (patch) | |
tree | 6f11ccfcac6fc0c29c0ec8ac260762016f5ac1ca | |
parent | 2fb523b26949b4aa7983a9ce2a9a05920d1e0d88 (diff) | |
download | yelp-2bcb7e7f6c1788b96668aed9a938521c50345b06.tar.gz |
- Introduced a timeout function for scrolling to anchor/top, since
* src/yelp-html-gtkhtml2.c:
- Introduced a timeout function for scrolling to anchor/top, since gtkhtml2
also has a timeout that likes to scroll to the first link. This timeout
checks if gtkhtml2's timeout is still pending, and readds itself if so.
* src/yelp-window.c:
- Do the writing of the page in an idle, which produces better renderings
and helps me to beat idle and timeout functions that mess with scrolling.
- Fix problem where Previous/Next/TOC menu items stayed active when they
shouldn't have.
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | src/yelp-html-gtkhtml2.c | 70 | ||||
-rw-r--r-- | src/yelp-window.c | 137 |
3 files changed, 180 insertions, 40 deletions
@@ -1,3 +1,16 @@ +2004-02-03 Shaun McCance <shaunm@gnome.org> + + * src/yelp-html-gtkhtml2.c: + - Introduced a timeout function for scrolling to anchor/top, since gtkhtml2 + also has a timeout that likes to scroll to the first link. This timeout + checks if gtkhtml2's timeout is still pending, and readds itself if so. + + * src/yelp-window.c: + - Do the writing of the page in an idle, which produces better renderings + and helps me to beat idle and timeout functions that mess with scrolling. + - Fix problem where Previous/Next/TOC menu items stayed active when they + shouldn't have. + 2004-01-23 Mikael Hallendal <micke@imendio.com> * src/yelp-window.h: include gtkwindow.h instead of gnome-app.h diff --git a/src/yelp-html-gtkhtml2.c b/src/yelp-html-gtkhtml2.c index e7b23707..f40e7522 100644 --- a/src/yelp-html-gtkhtml2.c +++ b/src/yelp-html-gtkhtml2.c @@ -24,6 +24,8 @@ #include <config.h> #endif +#include <gdk/gdkkeysyms.h> +#include <gtk/gtkenums.h> #include <libgnomevfs/gnome-vfs.h> #include <libgnomevfs/gnome-vfs-mime-utils.h> #include <libgnome/gnome-i18n.h> @@ -46,6 +48,7 @@ typedef struct _YelpHtmlBoxNode YelpHtmlBoxNode; #define YELP_HTML_BOX_NODE(x) ((YelpHtmlBoxNode *)(x)) +#define ADJUSTMENT_TIMEOUT_INTERVAL 200 struct _YelpHtmlPriv { HtmlView *view; @@ -61,6 +64,8 @@ struct _YelpHtmlPriv { GList *find_list; GList *find_elem; gboolean find_is_forward; + + gchar *anchor; }; struct _YelpHtmlBoxNode { @@ -86,6 +91,7 @@ static void html_link_clicked_cb (HtmlDocument *doc, static void html_title_changed_cb (HtmlDocument *doc, const gchar *new_title, YelpHtml *html); +static gint adjustment_timeout_cb (gpointer data); static DomNode * html_get_dom_node (HtmlDocument *doc, const gchar *node_name); @@ -159,7 +165,7 @@ html_init (YelpHtml *html) G_CALLBACK (html_url_requested_cb), html); g_signal_connect (G_OBJECT (priv->doc), "title_changed", G_CALLBACK (html_title_changed_cb), html); - + gtk_widget_set_size_request (GTK_WIDGET (priv->view), 300, 200); html->priv = priv; @@ -295,6 +301,7 @@ html_link_clicked_cb (HtmlDocument *doc, const gchar *url, YelpHtml *html) html_clear_find_data (html); g_signal_emit (html, signals[URI_SELECTED], 0, uri, handled); + yelp_uri_unref (uri); } static void @@ -308,6 +315,48 @@ html_title_changed_cb (HtmlDocument *doc, g_signal_emit (html, signals[TITLE_CHANGED], 0, new_title); } +static gint +adjustment_timeout_cb (gpointer data) +{ + YelpHtml *html = YELP_HTML (data); + YelpHtmlPriv *priv = html->priv; + GtkAdjustment *adjustment; + + /* gtkhtml registers a relayout callback on a one second timeout which, + * among other things, focuses the first link on the page, which causes + * it to scroll to said link. This causes anchor scrolling not to work, + * and causes pages without a link at the top to start off scrolled down. + * As I'm sure you can imagine, this is really annoying. Here we have my + * ugly hack wherein I put in my own timeout callback to scroll to where + * I actually want the page. In order to make it visually quick but still + * avoid happening before gtkhtml's timeout, I check relayout_timeout_id + * on the HtmlView, which is non-zero if there's a timeout still waiting + * to happen. If the gtkhtml timeout is still there, this function is + * readded, and we try again later. + * + * There also seems to be an idle function in GtkLayout that triggers the + * gtkhtml2 relayout function. So technically, we have a race condition. + * However, grabbing focus before everything else, regardless of whether + * or not we readd the timeout function, seems to be fairly reliable. + */ + + gtk_widget_grab_focus (GTK_WIDGET (priv->view)); + + if (priv->view->relayout_timeout_id != 0) + return TRUE; + + adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (priv->view)); + gtk_adjustment_set_value (adjustment, adjustment->lower); + + adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (priv->view)); + gtk_adjustment_set_value (adjustment, adjustment->lower); + + if (priv->anchor) + html_view_jump_to_anchor (HTML_VIEW (priv->view), priv->anchor); + + return FALSE; +} + YelpHtml * yelp_html_new (void) { @@ -356,6 +405,11 @@ yelp_html_clear (YelpHtml *html) priv = html->priv; + if (priv->anchor) { + g_free (priv->anchor); + priv->anchor = NULL; + } + html_document_clear (priv->doc); html_document_open_stream (priv->doc, "text/html"); html_stream_set_cancel_func (priv->doc->current_stream, @@ -409,8 +463,7 @@ yelp_html_close (YelpHtml *html) html_document_close_stream (priv->doc); - gtk_adjustment_set_value (gtk_layout_get_vadjustment (GTK_LAYOUT (priv->view)), - 0); + g_timeout_add (ADJUSTMENT_TIMEOUT_INTERVAL, adjustment_timeout_cb, html); } GtkWidget * @@ -802,5 +855,14 @@ void yelp_html_jump_to_anchor (YelpHtml *html, gchar *anchor) { - html_view_jump_to_anchor (HTML_VIEW (html->priv->view), anchor); + YelpHtmlPriv *priv; + + g_return_if_fail (html != NULL); + + priv = html->priv; + + if (priv->anchor) + g_free (priv->anchor); + + priv->anchor = g_strdup (anchor); } diff --git a/src/yelp-window.c b/src/yelp-window.c index d2ae002b..a44c1403 100644 --- a/src/yelp-window.c +++ b/src/yelp-window.c @@ -26,6 +26,7 @@ #include <config.h> #endif +#include <libgtkhtml/gtkhtml.h> #include <gdk-pixbuf/gdk-pixbuf.h> #include <gtk/gtk.h> #include <bonobo/bonobo-main.h> @@ -227,6 +228,22 @@ struct _YelpWindowPriv { gchar *toc; }; +typedef struct _IdleWriterContext IdleWriterContext; +struct _IdleWriterContext { + YelpWindow *window; + + enum { + IDLE_WRITER_MEMORY, + IDLE_WRITER_VFS + } type; + + const gchar *buffer; + gint cur; + gint length; +}; + +static gboolean idle_write (IdleWriterContext *context); + static GtkItemFactoryEntry menu_items[] = { {N_("/_File"), NULL, 0, 0, "<Branch>" }, {N_("/File/_New window"), NULL, @@ -584,8 +601,6 @@ window_populate (YelpWindow *window) gtk_box_pack_start (GTK_BOX (priv->main_box), priv->html_sw, TRUE, TRUE, 0); - - gtk_widget_grab_focus (yelp_html_get_widget (priv->html_view)); } static GtkWidget * @@ -844,8 +859,6 @@ window_handle_pager_uri (YelpWindow *window, gchar *loading = _("Loading..."); GdkCursor *cursor = gdk_cursor_new (GDK_WATCH); - yelp_html_clear (priv->html_view); - gdk_window_set_cursor (GTK_WIDGET (window)->window, cursor); gdk_cursor_unref (cursor); @@ -868,6 +881,7 @@ window_handle_pager_uri (YelpWindow *window, gtk_window_set_title (GTK_WINDOW (window), (const gchar *) loading); + yelp_html_clear (priv->html_view); yelp_html_printf (priv->html_view, "<html><head><meta http-equiv='Content-Type'" @@ -944,23 +958,28 @@ window_handle_html_uri (YelpWindow *window, gtk_widget_set_sensitive (menu_item, FALSE); - yelp_html_clear (priv->html_view); - yelp_html_set_base_uri (priv->html_view, uri); - result = gnome_vfs_open (&handle, gnome_vfs_uri_get_path (uri->uri), GNOME_VFS_OPEN_READ); if (result != GNOME_VFS_OK) { - // FIXME: Give an error + GError *error = NULL; + yelp_set_error (&error, YELP_ERROR_NO_DOC); + window_error (window, error); + g_error_free (error); return FALSE; } + yelp_html_clear (priv->html_view); + yelp_html_set_base_uri (priv->html_view, uri); + while ((result = gnome_vfs_read (handle, buffer, BUFFER_SIZE, &n)) == GNOME_VFS_OK) { yelp_html_write (priv->html_view, buffer, n); } + yelp_html_close (priv->html_view); + gnome_vfs_close (handle); return TRUE; @@ -977,6 +996,7 @@ window_handle_page (YelpWindow *window, YelpWindowPriv *priv; gchar *id; gboolean valid; + IdleWriterContext *context; g_return_if_fail (YELP_IS_WINDOW (window)); @@ -1015,39 +1035,43 @@ window_handle_page (YelpWindow *window, } } - if (page->prev) { - priv->prev = page->prev; - menu_item = - gtk_item_factory_get_item_by_action (priv->item_factory, - YELP_WINDOW_GO_PREVIOUS); - if (menu_item) - gtk_widget_set_sensitive (menu_item, TRUE); - } - if (page->next) { - priv->next = page->next; - menu_item = - gtk_item_factory_get_item_by_action (priv->item_factory, - YELP_WINDOW_GO_NEXT); - if (menu_item) - gtk_widget_set_sensitive (menu_item, TRUE); - } - if (page->toc) { - priv->toc = page->toc; - menu_item = - gtk_item_factory_get_item_by_action (priv->item_factory, - YELP_WINDOW_GO_TOC); - if (menu_item) - gtk_widget_set_sensitive (menu_item, TRUE); - } + priv->prev = page->prev; + menu_item = + gtk_item_factory_get_item_by_action (priv->item_factory, + YELP_WINDOW_GO_PREVIOUS); + if (menu_item) + gtk_widget_set_sensitive (menu_item, + priv->prev ? TRUE : FALSE); + + priv->next = page->next; + menu_item = + gtk_item_factory_get_item_by_action (priv->item_factory, + YELP_WINDOW_GO_NEXT); + if (menu_item) + gtk_widget_set_sensitive (menu_item, + priv->next ? TRUE : FALSE); + + priv->toc = page->toc; + menu_item = + gtk_item_factory_get_item_by_action (priv->item_factory, + YELP_WINDOW_GO_TOC); + if (menu_item) + gtk_widget_set_sensitive (menu_item, + priv->toc ? TRUE : FALSE); gtk_window_set_title (GTK_WINDOW (window), (const gchar *) page->title); + context = g_new0 (IdleWriterContext, 1); + context->window = window; + context->type = IDLE_WRITER_MEMORY; + context->buffer = page->chunk; + context->length = strlen (page->chunk); + yelp_html_clear (priv->html_view); yelp_html_set_base_uri (priv->html_view, uri); - yelp_html_write (priv->html_view, - page->chunk, - strlen (page->chunk)); + + gtk_idle_add ((GtkFunction) idle_write, context); if (gnome_vfs_uri_get_fragment_identifier (uri->uri)) { yelp_html_jump_to_anchor @@ -1192,7 +1216,6 @@ html_uri_selected_cb (YelpHtml *html, if (!handled) { yelp_window_open_uri (window, uri); - yelp_uri_unref (uri); } } @@ -1711,3 +1734,45 @@ tree_model_iter_following (GtkTreeModel *model, return FALSE; } +/* Writing incrementally in an idle function has a number of advantages. First, + * it keeps the interface responsive for really big pages. Second, it prevents + * some weird rendering artifacts in gtkhtml2. Third, and most important, it + * helps me beat the relayout race condition that I discuss at length in a big + * comment in yelp-html-gtkhtml2.c. + */ + +static gboolean +idle_write (IdleWriterContext *context) +{ + YelpWindowPriv *priv; + + g_return_val_if_fail (context != NULL, FALSE); + g_return_val_if_fail (context->window != NULL, FALSE); + + priv = context->window->priv; + + switch (context->type) { + case IDLE_WRITER_MEMORY: + if (context->cur + BUFFER_SIZE < context->length) { + yelp_html_write (priv->html_view, + context->buffer + context->cur, + BUFFER_SIZE); + context->cur += BUFFER_SIZE; + return TRUE; + } else { + if (context->length > context->cur) + yelp_html_write (priv->html_view, + context->buffer + context->cur, + context->length - context->cur); + yelp_html_close (priv->html_view); + g_free (context); + return FALSE; + } + break; + case IDLE_WRITER_VFS: + default: + g_assert_not_reached (); + } + + return FALSE; +} |