Logo Search packages:      
Sourcecode: galeon version File versions

galeon-action-navigation.c

/*
 *  Copyright (C) 2003 Ricardo Fernández Pascual
 *
 *  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, 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "galeon-action-navigation.h"
#include "galeon-shell.h"
#include "galeon-favicon-cache.h"
#include "gul-toolbutton.h"
#include "gul-string.h"
#include "galeon-debug.h"

#include <gtk/gtkstock.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkimagemenuitem.h>

#include <glib/gi18n.h>
#include <string.h>

enum {
      PROP_0,
      PROP_SHOW_ARROW,
      PROP_DIRECTION,
};

#define MENU_ITEM_MAX_LENGTH 40

static void       galeon_action_navigation_set_property           (GObject *object,
                                                       guint prop_id,
                                                       const GValue *value,
                                                       GParamSpec *pspec);
static void       galeon_action_navigation_get_property           (GObject *object,
                                                       guint prop_id,
                                                       GValue *value,
                                                       GParamSpec *pspec);
static void       galeon_action_navigation_connect_proxy          (GtkAction *action, GtkWidget *proxy);
static void       galeon_action_navigation_update                 (GaleonAction *action);
static void       galeon_action_navigation_current_embed_changed  (GaleonAction *action, 
                                                       GaleonEmbed *old_embed, GaleonEmbed *new_embed);
static void             galeon_action_navigation_activate               (GtkAction *action);
static void             galeon_action_navigation_finalize               (GObject *object);

G_DEFINE_TYPE (GaleonActionNavigation, galeon_action_navigation, GALEON_TYPE_ACTION);

GType
galeon_action_navigation_direcion_get_type (void)
{
      static GType etype = 0;
      if (etype == 0)
      {
            static const GEnumValue values[] = {
                  { GALEON_NAVIGATION_DIRECTION_UP, "GALEON_NAVIGATION_DIRECTION_UP", "up" },
                  { GALEON_NAVIGATION_DIRECTION_BACK, "GALEON_NAVIGATION_DIRECTION_BACK", "back" },
                  { GALEON_NAVIGATION_DIRECTION_FORWARD, "GALEON_NAVIGATION_DIRECTION_FORWARD", "forward" },
                  { 0, NULL, NULL }
            };
            etype = g_enum_register_static ("GaleonActionNavigationDirection", values);
      }
      return etype;
}

static void
galeon_action_navigation_class_init (GaleonActionNavigationClass *class)
{
      GObjectClass *object_class = G_OBJECT_CLASS (class);
      GtkActionClass *action_class = GTK_ACTION_CLASS (class);
      GaleonActionClass *galeon_action_class = GALEON_ACTION_CLASS (class);

      object_class->set_property = galeon_action_navigation_set_property;
      object_class->get_property = galeon_action_navigation_get_property;
      object_class->finalize = galeon_action_navigation_finalize;

      action_class->menu_item_type    = GTK_TYPE_IMAGE_MENU_ITEM;
      action_class->toolbar_item_type = GUL_TYPE_TOOLBUTTON;
      action_class->connect_proxy = galeon_action_navigation_connect_proxy;
      action_class->activate      = galeon_action_navigation_activate;

      galeon_action_class->update = galeon_action_navigation_update;
      galeon_action_class->current_embed_changed = galeon_action_navigation_current_embed_changed;
      
      g_object_class_install_property (object_class,
                               PROP_SHOW_ARROW,
                               g_param_spec_boolean ("show_arrow",
                                                 _("Show Arrow"),
                                                 _("Show the dropdown arrow."),
                                                 FALSE,
                                                 G_PARAM_READWRITE));

      g_object_class_install_property (object_class,
                               PROP_DIRECTION,
                               g_param_spec_enum ("direction",
                                              _("Direction"),
                                              _("Direction of the navigation."),
                                              GALEON_TYPE_ACTION_NAVIGATION_DIRECTION,
                                              GALEON_NAVIGATION_DIRECTION_BACK,
                                              G_PARAM_READWRITE));
      
}

static void
galeon_action_navigation_init (GaleonActionNavigation *action)
{
      LOG ("GaleonActionNavigation ctor (%p)", action);
}
static void
galeon_action_navigation_finalize (GObject *object)
{
      LOG ("GaleonActionNavigation dtor (%p)", object);
      G_OBJECT_CLASS (galeon_action_navigation_parent_class)->finalize (object);
}


static void
galeon_action_navigation_set_property (GObject *object,
                      guint prop_id,
                      const GValue *value,
                      GParamSpec *pspec)
{
      GaleonActionNavigation *a = GALEON_ACTION_NAVIGATION (object);

      switch (prop_id)
      {
      case PROP_SHOW_ARROW:
            {
                  gboolean v = g_value_get_boolean (value);
                  galeon_action_navigation_set_show_arrow (a, v);
            }
            break;
      case PROP_DIRECTION:
            {
                  GaleonActionNavigationDirection v = g_value_get_enum (value);
                  galeon_action_navigation_set_direction (a, v);
            }
      default: 
            break;
      }
}

static void
galeon_action_navigation_get_property (GObject *object,
                               guint prop_id,
                               GValue *value,
                               GParamSpec *pspec)
{
      GaleonActionNavigation *a = GALEON_ACTION_NAVIGATION (object);
      
      switch (prop_id)
      {
      case PROP_SHOW_ARROW:
            g_value_set_boolean (value, a->show_arrow);
            break;
      case PROP_DIRECTION:
            g_value_set_enum (value, a->direction);
            break;
      default: 
            break;
      }
}

static void
galeon_action_navigation_update (GaleonAction *action)
{
      gboolean sensitive;
      GaleonEmbed *embed = galeon_action_get_embed (action);

      if (embed)
      {
            char *url = NULL, *title = NULL;
            char *tooltip = NULL;

            switch (GALEON_ACTION_NAVIGATION (action)->direction)
            {
            case GALEON_NAVIGATION_DIRECTION_BACK:
                  sensitive = galeon_embed_can_go_back (embed);
                  if (sensitive)
                  {
                        galeon_embed_shistory_get_nth (embed, -1, TRUE, &url, &title);
                        tooltip = g_strdup_printf (_("Go back to \"%s\""), title);
                  }
                  break;
            case GALEON_NAVIGATION_DIRECTION_FORWARD:
                  sensitive = galeon_embed_can_go_forward (embed);
                  if (sensitive)
                  {
                        galeon_embed_shistory_get_nth (embed, +1, TRUE, &url, &title);
                        tooltip = g_strdup_printf (_("Go forward to \"%s\""), title);
                  }
                  break;
            case GALEON_NAVIGATION_DIRECTION_UP:
                  sensitive = galeon_embed_can_go_up (embed);
                  break;
            default:
                  sensitive = FALSE;
                  g_assert_not_reached ();
            }

            if (tooltip != NULL)
            {
                  g_object_set (action, "tooltip", tooltip, NULL);
            }

            g_free (url);
            g_free (title);
            g_free (tooltip);
      }
      else
      {
            sensitive = FALSE;
      }

      g_object_set (G_OBJECT (action), "sensitive", sensitive, NULL);
}

static void
galeon_action_navigation_location_cb (GaleonEmbed *embed, GaleonAction *action)
{
      galeon_action_navigation_update (action);
}

static void
galeon_action_navigation_current_embed_changed (GaleonAction *action, 
                                    GaleonEmbed *old_embed, GaleonEmbed *new_embed)
{
      if (old_embed)
      {
            g_signal_handlers_disconnect_matched (old_embed, G_SIGNAL_MATCH_DATA, 
                                          0, 0, NULL, NULL, action);
      }

      if (new_embed)
      {
            g_signal_connect_object (new_embed, "ge-location", 
                               G_CALLBACK (galeon_action_navigation_location_cb),
                               action, 0);
      }

      galeon_action_navigation_update (action);
}

static void
galeon_action_navigation_activate (GtkAction *action)
{
      GaleonActionNavigation *a = GALEON_ACTION_NAVIGATION (action);
      GaleonWindow *window = galeon_action_get_window (GALEON_ACTION (a));
      GaleonEmbed *embed = galeon_action_get_embed (GALEON_ACTION (a));

      g_return_if_fail (window != NULL);
      g_return_if_fail (embed != NULL);

      if (a->ignore_next_menu_activate)
      {
            a->ignore_next_menu_activate = FALSE;
            return;
      }

      switch (a->direction)
      {
      case GALEON_NAVIGATION_DIRECTION_UP:
            galeon_embed_go_up (embed);
            break;
      case GALEON_NAVIGATION_DIRECTION_BACK:
            galeon_embed_go_back (embed);
            break;
      case GALEON_NAVIGATION_DIRECTION_FORWARD:
            galeon_embed_go_forward (embed);
            break;
      default:
            g_assert_not_reached ();
            break;
      }
}

static GtkWidget *
new_num_accel_menu_item (gint num, const gchar *origtext, const gchar *url)
{
        gchar *shortened, *escaped, *text;
        GtkWidget *item;

      /* shorten, escape, add accel */
      shortened = gul_string_shorten (origtext, MENU_ITEM_MAX_LENGTH);
      escaped   = gul_string_double_underscores (shortened);
      text      = gul_string_new_num_accel (num, escaped, TRUE);

      g_free (shortened);
      g_free (escaped);

      if (text == NULL) return NULL;
      item = gtk_image_menu_item_new_with_mnemonic (text);
      gtk_widget_show (item);
      g_free (text);

      if (url)
      {
            GaleonFaviconCache *cache = galeon_shell_get_favicon_cache (galeon_shell);
            GaleonFaviconCacheEntry *entry = galeon_favicon_cache_lookup (cache, url);
            if (entry)
            {
                  GdkPixbuf *icon = galeon_favicon_cache_entry_get_pixbuf (entry);

                  if (icon)
                  {
                        GtkWidget *image = gtk_image_new_from_pixbuf (icon);
                        
                        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
                                                 GTK_WIDGET (image));
                        gtk_widget_show (GTK_WIDGET (image));
                  }
                  g_object_unref (entry);
            }
      }

        return item;
}

static void
activate_back_or_forward_menu_item_cb (GtkWidget *menu, GaleonActionNavigation *a)
{
      GaleonEmbed *embed = galeon_action_get_embed (GALEON_ACTION (a));
      int go_nth = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu), "go_nth"));
      
      g_return_if_fail (embed != NULL);

      if (a->ignore_next_menu_activate)
      {
            a->ignore_next_menu_activate = FALSE;
            return;
      }

      galeon_embed_shistory_go_nth (embed, go_nth);
}

static void
open_back_or_forward_in_new_tab (GaleonActionNavigation *a, int go_nth)
{
      GaleonEmbed *embed = galeon_action_get_embed (GALEON_ACTION (a));
      GaleonWindow *window;
      GaleonTab *tab;
      GaleonEmbed *new_embed;
      GaleonTab *new_tab;

      g_return_if_fail (embed != NULL);
            
      window = galeon_action_get_window (GALEON_ACTION (a));
      tab = galeon_window_get_active_tab (window);

      /* Do some nifty things here, we create a new tab with about:blank
       * inside it, and then do a history_go_nth to get the user into the
       * correct part of the history navigation */
      new_tab = galeon_shell_new_tab (galeon_shell, window, tab, NULL,
                              GALEON_NEW_TAB_COPY_HISTORY | 
                              GALEON_NEW_TAB_IS_A_COPY);

      new_embed = galeon_tab_get_embed (new_tab);
      if (galeon_embed_shistory_go_nth (new_embed, go_nth) == FALSE)
      {
            gchar *url, *title;
            /* If that fails, load the url directly. I can't actually get
             * this case to be called, but it is here just for robustness */
            if (!galeon_embed_shistory_get_nth (embed, go_nth, FALSE, &url, &title))
            {
                  return;
            }
            g_free (title);

            if (!url) return;

            galeon_embed_load_url (new_embed, url);
            g_free (url);
      }
}

static gboolean
button_press_event_back_or_forward_menu_item_cb  (GtkWidget *widget, GdkEventButton *event, 
                                      GaleonActionNavigation *a)
{
      GaleonWindow *window = galeon_action_get_window (GALEON_ACTION (a));
      if (event->button == 2 && GALEON_IS_WINDOW (window))
      {
            int go_nth = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "go_nth"));
            open_back_or_forward_in_new_tab (a, go_nth);

            /* Ignore the next activation, otherwise we will open this url in the
             * current tab as well */
            a->ignore_next_menu_activate = TRUE;

            return TRUE;
      }
      return FALSE;
}

static void
activate_up_menu_item_cb (GtkWidget *menu, GaleonActionNavigation *a)
{
      GaleonEmbed *embed = galeon_action_get_embed (GALEON_ACTION (a));
      int go_nth = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu), "go_nth"));
      GSList *l;
      gchar *url;
      
      g_return_if_fail (embed != NULL);

      if (a->ignore_next_menu_activate)
      {
            a->ignore_next_menu_activate = FALSE;
            return;
      }

      l = galeon_embed_get_go_up_list (embed);

      url = g_slist_nth_data (l, go_nth);
      if (url)
      {
            galeon_embed_load_url (embed, url);
      }

      g_slist_foreach (l, (GFunc) g_free, NULL);
      g_slist_free (l);
}

static void
open_up_in_tab (GaleonActionNavigation *a, int go_nth)
{
      GaleonEmbed *embed = galeon_action_get_embed (GALEON_ACTION (a));
      gchar *url;
      GSList *l;

      g_return_if_fail (embed != NULL);
            
      l = galeon_embed_get_go_up_list (embed);
      url = g_slist_nth_data (l, go_nth);

      if (url)
      {
            GaleonWindow *window = galeon_action_get_window (GALEON_ACTION (a));
            GaleonTab *tab = galeon_window_get_active_tab (window);
            galeon_shell_new_tab (galeon_shell, window, tab, url, 0);
      }

      g_slist_foreach (l, (GFunc) g_free, NULL);
      g_slist_free (l);
}


static gboolean
button_press_event_up_menu_item_cb  (GtkWidget *widget, GdkEventButton *event, 
                             GaleonActionNavigation *a)
{
      GaleonWindow *window = galeon_action_get_window (GALEON_ACTION (a));
      if (event->button == 2 && GALEON_IS_WINDOW (window))
      {
            int go_nth = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "go_nth"));
            open_up_in_tab (a, go_nth);

            /* Ignore the next activation, otherwise we will open this url in the
             * current tab as well */
            a->ignore_next_menu_activate = TRUE;

            return TRUE;
      }
      return FALSE;
}

static gboolean
galeon_action_navigation_button_press_event_cb (GtkWidget *widget, 
                                    GdkEventButton *event, 
                                    GaleonActionNavigation *a)
{
      GaleonWindow *window = galeon_action_get_window (GALEON_ACTION (a));
      GaleonEmbed *embed;

      if (event->button != 2 || ! GALEON_IS_WINDOW (window))
      {
            return FALSE;
      }

      embed = galeon_action_get_embed (GALEON_ACTION (a));
      g_return_val_if_fail (embed != NULL, FALSE);
      

      if (GTK_IS_MENU_ITEM (widget))
      {
            /* Ignore the next activation, otherwise we will open this url in the
             * current tab as well */
            a->ignore_next_menu_activate = TRUE;
      }

      if (a->direction == GALEON_NAVIGATION_DIRECTION_UP)
      {
            open_up_in_tab (a, 0);
            return TRUE;
      }
      else
      {
            int pos;
            pos = galeon_embed_shistory_get_pos (embed);
            pos = (a->direction == GALEON_NAVIGATION_DIRECTION_BACK) ? pos-1 : pos+1;
            open_back_or_forward_in_new_tab (a, pos);
            return TRUE;
      }
      return FALSE;
}

static void
setup_back_or_forward_menu (GaleonActionNavigation *a, GtkMenuShell *ms, 
                      GaleonActionNavigationDirection dir)
{
      GaleonEmbed *embed = galeon_action_get_embed (GALEON_ACTION (a));
      int pos, count;
      int start, end, accell_count = 0;

      g_return_if_fail (embed != NULL);
      
      pos = galeon_embed_shistory_get_pos (embed);
      count = galeon_embed_shistory_count (embed);

      if (count == 0) return;
      
      if (dir == GALEON_NAVIGATION_DIRECTION_BACK)
      {
            start = pos - 1;
            end = -1;
      }
      else
      {
            start = pos + 1;
            end = count;
      }

      while (start != end)
      {
            char *title, *url;
            GtkWidget *item;

            galeon_embed_shistory_get_nth (embed, start, FALSE, &url, &title);

            item = new_num_accel_menu_item (accell_count, title, url);
            g_free (title);
            g_free (url);

            if (item == NULL) break;

            gtk_menu_shell_append (ms, item);
            g_object_set_data (G_OBJECT (item), "go_nth", GINT_TO_POINTER (start));
            g_signal_connect (item, "activate",
                                  G_CALLBACK (activate_back_or_forward_menu_item_cb), a);
            g_signal_connect (item, "button-press-event",
                                  G_CALLBACK (button_press_event_back_or_forward_menu_item_cb), a);
            gtk_widget_show_all (item);

            accell_count++;
            if (start < end)
            {
                  start++;
            }
            else
            {
                  start--;
            }
      }
}

static void
setup_up_menu (GaleonActionNavigation *a, GtkMenuShell *ms)
{
      GaleonEmbed *embed = galeon_action_get_embed (GALEON_ACTION (a));
      int accell_count = 0;
      GSList *l;
      GSList *li;

      g_return_if_fail (embed != NULL);
      
      l = galeon_embed_get_go_up_list (embed);
      
      for (li = l; li; li = li->next)
      {
            char *url = li->data;
            GtkWidget *item;

            item = new_num_accel_menu_item (accell_count, url, url);
            if (item == NULL) break;

            gtk_menu_shell_append (ms, item);
            g_object_set_data (G_OBJECT(item), "go_nth", GINT_TO_POINTER (accell_count));
            g_signal_connect (item, "activate",
                                  G_CALLBACK (activate_up_menu_item_cb), a);
            g_signal_connect (item, "button-press-event",
                                  G_CALLBACK (button_press_event_up_menu_item_cb), a);
            gtk_widget_show_all (item);

            accell_count++;
      }
      
      g_slist_foreach (l, (GFunc) g_free, NULL);
      g_slist_free (l);
}

static void
galeon_action_navigation_menu_activated_cb (GulToolbutton *w, GaleonActionNavigation *a)
{
      GtkMenuShell *ms = gul_toolbutton_get_menu (w);
      GList *children;
      GList *li;

      children = gtk_container_get_children (GTK_CONTAINER (ms));
      for (li = children; li; li = li->next)
      {
            gtk_container_remove (GTK_CONTAINER (ms), li->data);
      }
      g_list_free (children);
      
      switch (a->direction)
      {
      case GALEON_NAVIGATION_DIRECTION_UP:
            setup_up_menu (a, ms);
            break;
      case GALEON_NAVIGATION_DIRECTION_FORWARD:
      case GALEON_NAVIGATION_DIRECTION_BACK:
            setup_back_or_forward_menu (a, ms, a->direction);
            break;
      default:
            g_assert_not_reached ();
            break;
      }
}

static void
galeon_action_navigation_connect_proxy (GtkAction *a, GtkWidget *proxy)
{
      GaleonActionNavigation *action = GALEON_ACTION_NAVIGATION (a);
      GtkWidget *widget = proxy;

      if (GUL_IS_TOOLBUTTON (proxy))
      {
            widget = gul_toolbutton_get_button (GUL_TOOLBUTTON (proxy));

            g_signal_connect (proxy, "menu-activated", 
                          G_CALLBACK (galeon_action_navigation_menu_activated_cb), a);

            gul_toolbutton_set_show_arrow (GUL_TOOLBUTTON (proxy), action->show_arrow);

      }

      g_signal_connect (widget, "button-press-event",
                    G_CALLBACK (galeon_action_navigation_button_press_event_cb), a);

      (* GTK_ACTION_CLASS (galeon_action_navigation_parent_class)->connect_proxy) (a, proxy);
}

gboolean
galeon_action_navigation_get_show_arrow (GaleonActionNavigation *a)
{
      return a->show_arrow;
}

void
galeon_action_navigation_set_show_arrow (GaleonActionNavigation *a, gboolean value)
{
      GSList *sli;
      a->show_arrow = value;
      for (sli = gtk_action_get_proxies (GTK_ACTION (a)); sli; sli = sli->next)
      {
            GulToolbutton *b = GUL_TOOLBUTTON (sli->data);
            gul_toolbutton_set_show_arrow (b, a->show_arrow);
      }

      galeon_action_navigation_update (GALEON_ACTION (a));
}

GaleonActionNavigationDirection
galeon_action_navigation_get_direction (GaleonActionNavigation *a)
{
      return a->direction;
}

void
galeon_action_navigation_set_direction (GaleonActionNavigation *a,
                              GaleonActionNavigationDirection value)
{
      g_return_if_fail (value == GALEON_NAVIGATION_DIRECTION_BACK 
                    || value == GALEON_NAVIGATION_DIRECTION_FORWARD
                    || value == GALEON_NAVIGATION_DIRECTION_UP);
      
      a->direction = value;
      switch (a->direction)
      {
      case GALEON_NAVIGATION_DIRECTION_UP:
            g_object_set (G_OBJECT (a), "stock_id", GTK_STOCK_GO_UP, NULL);
            g_object_set (G_OBJECT (a), "tooltip", _("Go up"), NULL);
            g_object_set (G_OBJECT (a), "is_important", FALSE, NULL);
            break;
      case GALEON_NAVIGATION_DIRECTION_FORWARD:
            g_object_set (G_OBJECT (a), "stock_id", GTK_STOCK_GO_FORWARD, NULL);
            g_object_set (G_OBJECT (a), "tooltip", _("Go forward"), NULL);
            g_object_set (G_OBJECT (a), "is_important", FALSE, NULL);
            break;
      case GALEON_NAVIGATION_DIRECTION_BACK:
            g_object_set (G_OBJECT (a), "stock_id", GTK_STOCK_GO_BACK, NULL);
            g_object_set (G_OBJECT (a), "tooltip", _("Go back"), NULL);
            g_object_set (G_OBJECT (a), "is_important", TRUE, NULL);
            break;
      }

      galeon_action_navigation_update (GALEON_ACTION (a));
}


Generated by  Doxygen 1.6.0   Back to index