From a166462bcaf8211416c3e19a421c0ae5279e1b9f Mon Sep 17 00:00:00 2001 From: Diego Escalante Urrelo Date: Wed, 7 Mar 2012 18:04:43 -0500 Subject: ephy-navigation-history-action: restore menus In ebbb1c48197f53b98575b0cb4f6d9fa1e4535abc back/forward drop-downs were removed. This commit brings them back, using the removed code with minor updates. https://bugzilla.gnome.org/show_bug.cgi?id=671609 --- src/ephy-navigation-history-action.c | 396 +++++++++++++++++++++++++++++++++++ 1 file changed, 396 insertions(+) (limited to 'src') diff --git a/src/ephy-navigation-history-action.c b/src/ephy-navigation-history-action.c index f0f0a5e48..bbf6c72b3 100644 --- a/src/ephy-navigation-history-action.c +++ b/src/ephy-navigation-history-action.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Copyright © 2003, 2004 Marco Pesenti Gritti * Copyright © 2003, 2004 Christian Persch @@ -26,6 +27,7 @@ #include "ephy-action-helper.h" #include "ephy-debug.h" #include "ephy-embed-container.h" +#include "ephy-embed-prefs.h" #include "ephy-embed-shell.h" #include "ephy-embed-utils.h" #include "ephy-gui.h" @@ -46,6 +48,7 @@ struct _EphyNavigationHistoryActionPrivate { EphyNavigationHistoryDirection direction; EphyHistoryService *history; + guint menu_timeout; }; enum { @@ -53,6 +56,15 @@ enum { PROP_DIRECTION }; +#define MAX_LABEL_LENGTH 48 +#define HISTORY_ITEM_DATA_KEY "history-item-data-key" + +typedef enum { + WEBKIT_HISTORY_BACKWARD, + WEBKIT_HISTORY_FORWARD +} WebKitHistoryType; + + static void ephy_navigation_history_action_init (EphyNavigationHistoryAction *action); static void ephy_navigation_history_action_class_init (EphyNavigationHistoryActionClass *klass); @@ -132,6 +144,8 @@ ephy_navigation_history_action_init (EphyNavigationHistoryAction *action) action->priv->history = EPHY_HISTORY_SERVICE (ephy_embed_shell_get_global_history_service (embed_shell)); + action->priv->menu_timeout = 0; + g_signal_connect (action->priv->history, "cleared", G_CALLBACK (ephy_history_cleared_cb), action); @@ -142,6 +156,9 @@ ephy_navigation_history_action_finalize (GObject *object) { EphyNavigationHistoryAction *action = EPHY_NAVIGATION_HISTORY_ACTION (object); + if (action->priv->menu_timeout > 0) + g_source_remove (action->priv->menu_timeout); + g_signal_handlers_disconnect_by_func (action->priv->history, ephy_history_cleared_cb, action); @@ -185,6 +202,383 @@ ephy_navigation_history_action_get_property (GObject *object, } } +static gboolean +item_enter_notify_event_cb (GtkWidget *widget, + GdkEvent *event, + EphyWebView *view) +{ + char *text; + + text = g_object_get_data (G_OBJECT (widget), "link-message"); + ephy_web_view_set_link_message (view, g_strdup (text)); + g_object_set_data (G_OBJECT (widget), "link-message", text); + + return FALSE; +} + +static gboolean +item_leave_notify_event_cb (GtkWidget *widget, + GdkEvent *event, + EphyWebView *view) +{ + ephy_web_view_set_link_message (view, NULL); + return FALSE; +} + +static void +icon_loaded_cb (GObject *source, + GAsyncResult *result, + GtkImageMenuItem *item) +{ + WebKitFaviconDatabase* database; + GdkPixbuf *favicon; + + database = webkit_get_favicon_database (); + favicon = webkit_favicon_database_get_favicon_pixbuf_finish (database, result, NULL); + + if (favicon) { + GtkWidget *image; + + image = gtk_image_new_from_pixbuf (favicon); + gtk_image_menu_item_set_image (item, image); + gtk_image_menu_item_set_always_show_image (item, TRUE); + + g_object_unref (favicon); + } +} + +static GtkWidget * +new_history_menu_item (EphyWebView *view, + const char *origtext, + const char *address) +{ + GtkWidget *item; + GtkLabel *label; + WebKitFaviconDatabase* database; + GdkPixbuf *favicon; + + g_return_val_if_fail (address != NULL && origtext != NULL, NULL); + + item = gtk_image_menu_item_new_with_label (origtext); + + label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))); + gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END); + gtk_label_set_max_width_chars (label, MAX_LABEL_LENGTH); + + database = webkit_get_favicon_database (); + favicon = webkit_favicon_database_try_get_favicon_pixbuf (database, address, + FAVICON_SIZE, FAVICON_SIZE); + + if (favicon) { + GtkWidget *image; + + image = gtk_image_new_from_pixbuf (favicon); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (item), TRUE); + + g_object_unref (favicon); + } else { + webkit_favicon_database_get_favicon_pixbuf (database, address, + FAVICON_SIZE, FAVICON_SIZE, NULL, + (GAsyncReadyCallback) icon_loaded_cb, + GTK_IMAGE_MENU_ITEM (item)); + } + + g_object_set_data (G_OBJECT (item), "link-message", g_strdup (address)); + + g_signal_connect (item, "enter-notify-event", + G_CALLBACK (item_enter_notify_event_cb), view); + g_signal_connect (item, "leave-notify-event", + G_CALLBACK (item_leave_notify_event_cb), view); + + gtk_widget_show (item); + + return item; +} + +static void +set_new_back_history (EphyEmbed *source, + EphyEmbed *dest, + gint offset) +{ + WebKitWebView *source_view, *dest_view; + WebKitWebBackForwardList* source_list, *dest_list; + WebKitWebHistoryItem *item; + GList *items; + guint limit; + guint i; + + source_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (source); + dest_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (dest); + + source_list = webkit_web_view_get_back_forward_list (source_view); + dest_list = webkit_web_view_get_back_forward_list (dest_view); + + if (offset >= 0) { + /* Copy the whole back history in this case (positive offset) */ + ephy_web_view_copy_back_history (ephy_embed_get_web_view (source), + ephy_embed_get_web_view (dest)); + + items = webkit_web_back_forward_list_get_forward_list_with_limit (source_list, + EPHY_WEBKIT_BACK_FORWARD_LIMIT); + limit = offset - 1; + } else { + items = webkit_web_back_forward_list_get_back_list_with_limit (source_list, + EPHY_WEBKIT_BACK_FORWARD_LIMIT); + limit = g_list_length (items) + offset; + } + + /* Add the remaining items to the BackForward list */ + items = g_list_reverse (items); + for (i = 0; i < limit; i++) { + item = webkit_web_history_item_copy ((WebKitWebHistoryItem *) items->data); + webkit_web_back_forward_list_add_item (dest_list, item); + g_object_unref (item); + + items = items->next; + } + g_list_free (items); +} + +static void +middle_click_handle_on_history_menu_item (EphyNavigationHistoryAction *action, + EphyEmbed *embed, + WebKitWebHistoryItem *item) +{ + EphyEmbed *new_embed = NULL; + WebKitWebView *web_view; + WebKitWebBackForwardList *history; + GList *list; + const gchar *url; + guint current; + gint offset; + + web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed); + + /* Save old history and item's offset from current */ + history = webkit_web_view_get_back_forward_list (web_view); + if (action->priv->direction == EPHY_NAVIGATION_HISTORY_DIRECTION_BACK) { + list = webkit_web_back_forward_list_get_back_list_with_limit (history, + EPHY_WEBKIT_BACK_FORWARD_LIMIT); + current = -1; + } else { + list = webkit_web_back_forward_list_get_forward_list_with_limit (history, + EPHY_WEBKIT_BACK_FORWARD_LIMIT); + current = g_list_length (list); + } + offset = current - g_list_index (list, item); + + new_embed = ephy_shell_new_tab (ephy_shell_get_default (), + EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))), + embed, + NULL, + EPHY_NEW_TAB_IN_EXISTING_WINDOW | + EPHY_NEW_TAB_DONT_COPY_HISTORY); + g_return_if_fail (new_embed != NULL); + + /* We manually set the back history instead of trusting + ephy_shell_new_tab because the logic is more complex than + usual, due to handling also the forward history */ + set_new_back_history (embed, new_embed, offset); + + /* Load the new URL */ + url = webkit_web_history_item_get_original_uri (item); + ephy_web_view_load_url (ephy_embed_get_web_view (new_embed), url); +} + +static void +activate_menu_item_cb (GtkWidget *menuitem, + EphyNavigationHistoryAction *action) +{ + WebKitWebHistoryItem *item; + EphyWindow *window; + EphyEmbed *embed; + + window = ephy_window_action_get_window (EPHY_WINDOW_ACTION (action)); + embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window)); + g_return_if_fail (embed != NULL); + + item = (WebKitWebHistoryItem *) g_object_get_data (G_OBJECT (menuitem), HISTORY_ITEM_DATA_KEY); + g_return_if_fail (item != NULL); + + if (ephy_gui_is_middle_click ()) + middle_click_handle_on_history_menu_item (action, embed, item); + else { + WebKitWebView *web_view; + + web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed); + webkit_web_view_go_to_back_forward_item (web_view, item); + } +} +static GList* +webkit_construct_history_list (WebKitWebView *web_view, + WebKitHistoryType hist_type, + int limit) +{ + WebKitWebBackForwardList *web_back_forward_list; + GList *webkit_items; + + web_back_forward_list = webkit_web_view_get_back_forward_list (web_view); + + if (hist_type == WEBKIT_HISTORY_FORWARD) + webkit_items = g_list_reverse (webkit_web_back_forward_list_get_forward_list_with_limit (web_back_forward_list, + limit)); + else + webkit_items = webkit_web_back_forward_list_get_back_list_with_limit (web_back_forward_list, + limit); + + return webkit_items; +} + +static GtkWidget * +build_dropdown_menu (EphyNavigationHistoryAction *action) +{ + EphyWindow *window; + GtkMenuShell *menu; + EphyEmbed *embed; + GList *list, *l; + WebKitWebView *web_view; + + window = ephy_window_action_get_window (EPHY_WINDOW_ACTION (action)); + embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window)); + g_return_val_if_fail (embed != NULL, NULL); + + menu = GTK_MENU_SHELL (gtk_menu_new ()); + + web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed); + + if (action->priv->direction == EPHY_NAVIGATION_HISTORY_DIRECTION_BACK) + list = webkit_construct_history_list (web_view, + WEBKIT_HISTORY_BACKWARD, 10); + else + list = webkit_construct_history_list (web_view, + WEBKIT_HISTORY_FORWARD, 10); + + for (l = list; l != NULL; l = l->next) { + GtkWidget *item; + WebKitWebHistoryItem *hitem; + const char *uri; + char *title; + + hitem = (WebKitWebHistoryItem *) l->data; + uri = webkit_web_history_item_get_uri (hitem); + + title = g_strdup (webkit_web_history_item_get_title (hitem)); + + if (title == NULL || g_strstrip (title)[0] == '\0') + item = new_history_menu_item (EPHY_WEB_VIEW (web_view), uri, uri); + else + item = new_history_menu_item (EPHY_WEB_VIEW (web_view), title, uri); + + g_free (title); + + g_object_set_data_full (G_OBJECT (item), HISTORY_ITEM_DATA_KEY, + g_object_ref (hitem), g_object_unref); + + g_signal_connect (item, "activate", + G_CALLBACK (activate_menu_item_cb), action); + + gtk_menu_shell_append (menu, item); + gtk_widget_show_all (item); + } + + g_list_free (list); + + return GTK_WIDGET (menu); +} + +typedef struct { + EphyNavigationHistoryAction *action; + GdkEventButton *event; + GtkWidget *widget; +} PopupData; + +static GtkWidget * +popup_history_menu (EphyNavigationHistoryAction *action, + GtkWidget *widget, + GdkEventButton *event) +{ + GtkWidget *menu; + + menu = build_dropdown_menu (action); + gtk_menu_popup (GTK_MENU (menu), + NULL, NULL, + ephy_gui_menu_position_under_widget, widget, + event->button, event->time); + + return menu; +} + +static gboolean +menu_timeout_cb (PopupData *data) +{ + if (data != NULL && data->widget) + popup_history_menu (data->action, data->widget, data->event); + + return FALSE; +} + +static gboolean +tool_button_press_event_cb (GtkButton *button, + GdkEventButton *event, + EphyNavigationHistoryAction *action) +{ + if (event->button == 1) { + PopupData *data; + + data = g_new (PopupData, 1); + data->action = action; + data->event = event; + data->widget = GTK_WIDGET (button); + + action->priv->menu_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 500, + (GSourceFunc) menu_timeout_cb, + data, + (GDestroyNotify) g_free); + } else if (event->button == 3) + popup_history_menu (action, GTK_WIDGET (button), event); + + return FALSE; +} + +static gboolean +tool_leave_notify_event_cb (GtkButton *button, + GdkEvent *event, + EphyNavigationHistoryAction *action) +{ + if (action->priv->menu_timeout > 0) + g_source_remove (action->priv->menu_timeout); + + action->priv->menu_timeout = 0; + return FALSE; +} + +static void +connect_proxy (GtkAction *gaction, + GtkWidget *proxy) +{ + g_signal_connect (proxy, "button-press-event", + G_CALLBACK (tool_button_press_event_cb), gaction); + g_signal_connect (proxy, "button-release-event", + G_CALLBACK (tool_leave_notify_event_cb), gaction); + g_signal_connect (proxy, "leave-notify-event", + G_CALLBACK (tool_leave_notify_event_cb), gaction); + + GTK_ACTION_CLASS (ephy_navigation_history_action_parent_class)->connect_proxy (gaction, proxy); +} + +static void +disconnect_proxy (GtkAction *gaction, + GtkWidget *proxy) +{ + g_signal_handlers_disconnect_by_func (proxy, + G_CALLBACK (tool_button_press_event_cb), gaction); + g_signal_handlers_disconnect_by_func (proxy, + G_CALLBACK (tool_leave_notify_event_cb), gaction); + + GTK_ACTION_CLASS (ephy_navigation_history_action_parent_class)->disconnect_proxy (gaction, proxy); +} + static void ephy_navigation_history_action_class_init (EphyNavigationHistoryActionClass *klass) { @@ -196,6 +590,8 @@ ephy_navigation_history_action_class_init (EphyNavigationHistoryActionClass *kla object_class->get_property = ephy_navigation_history_action_get_property; action_class->activate = action_activate; + action_class->connect_proxy = connect_proxy; + action_class->disconnect_proxy = disconnect_proxy; g_object_class_install_property (object_class, PROP_DIRECTION, -- cgit v1.2.3