2008-03-25 Sven Herzberg <sven@imendio.com>
[hildon] / src / hildon-bread-crumb-trail.c
index e203d93..cf95c52 100644 (file)
@@ -37,6 +37,7 @@
  * implemented if needed. See #HildonBreadCrumb for more details.
  */
 
+#include <gdk/gdkkeysyms.h>
 #include "hildon-marshalers.h"
 #include "hildon-bread-crumb-trail.h"
 #include "hildon-bread-crumb-widget.h"
@@ -47,12 +48,14 @@ struct _HildonBreadCrumbTrailPrivate
 {
   GtkWidget *back_button;
   GList *item_list;
+  GtkWidget *arrow;
 };
 
 /* Signals */
 
 enum {
   CRUMB_CLICKED,
+  MOVE_PARENT,
   LAST_SIGNAL
 };
 
@@ -78,6 +81,9 @@ static void hildon_bread_crumb_trail_finalize (GObject *object);
 static void hildon_bread_crumb_trail_scroll_back (GtkWidget *button,
                                                   HildonBreadCrumbTrail *bct);
 static void hildon_bread_crumb_trail_update_back_button_sensitivity (HildonBreadCrumbTrail *bct);
+static void hildon_bread_crumb_trail_move_parent (HildonBreadCrumbTrail *bct);
+
+static gpointer get_bread_crumb_id (HildonBreadCrumb *item);
 
 static guint bread_crumb_trail_signals[LAST_SIGNAL] = { 0 };
 
@@ -92,6 +98,7 @@ hildon_bread_crumb_trail_class_init (HildonBreadCrumbTrailClass *klass)
   GtkObjectClass *object_class = (GtkObjectClass*)klass;
   GtkWidgetClass *widget_class = (GtkWidgetClass*)klass;
   GtkContainerClass *container_class = (GtkContainerClass*)klass;
+  GtkBindingSet *binding_set;
 
   /* GObject signals */
   gobject_class->finalize = hildon_bread_crumb_trail_finalize;
@@ -105,6 +112,9 @@ hildon_bread_crumb_trail_class_init (HildonBreadCrumbTrailClass *klass)
   container_class->forall = hildon_bread_crumb_trail_forall;
   container_class->remove = hildon_bread_crumb_trail_remove;
 
+  /* HildonBreadCrumbTrail signals */
+  klass->move_parent = hildon_bread_crumb_trail_move_parent;
+
   /* Style properties */
 
 #define _BREAD_CRUMB_TRAIL_MINIMUM_WIDTH 10
@@ -139,7 +149,26 @@ hildon_bread_crumb_trail_class_init (HildonBreadCrumbTrailClass *klass)
                   _hildon_marshal_BOOLEAN__POINTER,
                   G_TYPE_BOOLEAN, 1,
                   G_TYPE_POINTER);
+
+  bread_crumb_trail_signals[MOVE_PARENT] =
+    g_signal_new ("move-parent",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (HildonBreadCrumbTrailClass, move_parent),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
                   
+
+  /* Binding set */
+  binding_set = gtk_binding_set_by_class (widget_class);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+                                "move-parent", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
+                                "move-parent", 0);
+                                
   /* Private data */
   g_type_class_add_private (gobject_class, sizeof (HildonBreadCrumbTrailPrivate));
 }
@@ -155,6 +184,16 @@ hildon_bread_crumb_trail_finalize (GObject *object)
 }
 
 static void
+hildon_bread_crumb_trail_move_parent (HildonBreadCrumbTrail *bct)
+{
+  if (g_list_length (bct->priv->item_list) > 1)
+    {
+      g_signal_emit_by_name (HILDON_BREAD_CRUMB (bct->priv->item_list->next->data),
+                             "crumb-activated");
+    }
+}
+
+static void
 hildon_bread_crumb_trail_size_request (GtkWidget *widget,
                                        GtkRequisition *requisition)
 {
@@ -226,6 +265,10 @@ hildon_bread_crumb_trail_size_allocate (GtkWidget *widget,
   GList *p, *first_show, *first_hide;
   gint back_button_size;
   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL (widget)->priv;
+  gboolean rtl;
+
+  /* Get the rtl status */
+  rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
 
   widget->allocation = *allocation;
 
@@ -233,14 +276,24 @@ hildon_bread_crumb_trail_size_allocate (GtkWidget *widget,
   allocation_width = allocation->width - 2 * border_width;
 
   /* Allocate the back button */
-  child_allocation.x = allocation->x + border_width;
+  if (rtl)
+    child_allocation.x = allocation->width - border_width;
+  else
+    child_allocation.x = allocation->x + border_width;
+
   child_allocation.y = allocation->y + border_width;
   gtk_widget_get_child_requisition (priv->back_button, &child_requisition);
   /* We want the back button to be a square */
   back_button_size = MAX (child_requisition.width, child_requisition.height);
   child_allocation.width = child_allocation.height = back_button_size;
+
+  if (rtl)
+    child_allocation.x -= back_button_size;
+
   gtk_widget_size_allocate (priv->back_button, &child_allocation);
-  child_allocation.x += back_button_size;
+
+  if (!rtl)
+    child_allocation.x += back_button_size;
 
   /* If there are no buttons there's nothing else to do */
   if (priv->item_list == NULL)
@@ -283,10 +336,15 @@ hildon_bread_crumb_trail_size_allocate (GtkWidget *widget,
           if (child_allocation.width > req.width)
             {
               first_hide = p->next;
-              gtk_widget_size_allocate (child, &child_allocation);
-              gtk_widget_show (child);
               gtk_widget_set_child_visible (child, TRUE);
-              child_allocation.x += child_allocation.width;
+
+              if (rtl)
+                child_allocation.x -= child_allocation.width;
+
+              gtk_widget_size_allocate (child, &child_allocation);
+
+              if (!rtl)
+                child_allocation.x += child_allocation.width;
             }
           else
             {
@@ -321,10 +379,15 @@ hildon_bread_crumb_trail_size_allocate (GtkWidget *widget,
         }
 
       child_allocation.width = natural_width;
-      gtk_widget_size_allocate (child, &child_allocation);
-      gtk_widget_show (child);
       gtk_widget_set_child_visible (child, TRUE);
-      child_allocation.x += child_allocation.width;
+
+      if (rtl)
+        child_allocation.x -= child_allocation.width;
+
+      gtk_widget_size_allocate (child, &child_allocation);
+
+      if (!rtl)
+        child_allocation.x += child_allocation.width;
     }
 
   for (p = first_hide; p; p = p->next)
@@ -332,7 +395,6 @@ hildon_bread_crumb_trail_size_allocate (GtkWidget *widget,
       item = HILDON_BREAD_CRUMB (p->data);
       child = GTK_WIDGET (item);
 
-      gtk_widget_hide (GTK_WIDGET (item));
       gtk_widget_set_child_visible (GTK_WIDGET (item), FALSE);
     }
 }
@@ -356,6 +418,7 @@ crumb_activated_cb (GtkWidget *button,
   if (signal_handled == FALSE)
     {
       GtkWidget *child;
+      gboolean focus_last_item = FALSE;
       HildonBreadCrumbTrailPrivate *priv;
 
       priv = bct->priv;
@@ -363,12 +426,21 @@ crumb_activated_cb (GtkWidget *button,
       child = GTK_WIDGET (priv->item_list->data);
 
       /* We remove the tip of the list until we hit the clicked button */
-      while (child != button)
+      while (child && child != button)
         {
+          if (GTK_WIDGET_HAS_FOCUS (child))
+            focus_last_item = TRUE;
+
           gtk_container_remove (GTK_CONTAINER (bct), child);
 
+          if (priv->item_list == NULL)
+              return;
+
           child = GTK_WIDGET (priv->item_list->data);
         }
+
+      if (focus_last_item)
+        gtk_widget_grab_focus (GTK_WIDGET (bct->priv->item_list->data));
     }
 }
 
@@ -424,7 +496,7 @@ static void
 hildon_bread_crumb_trail_remove (GtkContainer *container,
                                  GtkWidget *widget)
 {
-  GList *p;
+  GList *p, *next;
   HildonBreadCrumbTrailPrivate *priv;
   gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
 
@@ -434,14 +506,15 @@ hildon_bread_crumb_trail_remove (GtkContainer *container,
 
   while (p)
     {
+      next = p->next;
+
       if (widget == GTK_WIDGET (p->data))
         {
           g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (crumb_activated_cb),
                                                 HILDON_BREAD_CRUMB_TRAIL (container));
           gtk_widget_unparent (widget);
 
-          priv->item_list = g_list_remove_link (priv->item_list, p);
-          g_list_free (p);
+          priv->item_list = g_list_delete_link (priv->item_list, p);
 
           hildon_bread_crumb_trail_update_back_button_sensitivity (HILDON_BREAD_CRUMB_TRAIL (container));
 
@@ -451,7 +524,7 @@ hildon_bread_crumb_trail_remove (GtkContainer *container,
             }
         }
 
-      p = p->next;
+      p = next;
     }
 }
 
@@ -463,7 +536,7 @@ hildon_bread_crumb_trail_update_back_button_sensitivity (HildonBreadCrumbTrail *
 
   list_length = g_list_length (priv->item_list);
 
-  if (list_length == 0 || list_length == 1)
+  if (list_length <= 1)
     {
       gtk_widget_set_sensitive (priv->back_button, FALSE);
     }
@@ -487,6 +560,7 @@ create_back_button (HildonBreadCrumbTrail *bct)
   gtk_widget_set_sensitive (button, FALSE);
 
   arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
+  bct->priv->arrow = arrow;
   gtk_widget_style_get (GTK_WIDGET (bct),
                         "arrow-size", &arrow_size,
                         NULL);
@@ -522,14 +596,7 @@ static void
 hildon_bread_crumb_trail_scroll_back (GtkWidget *button,
                                       HildonBreadCrumbTrail *bct)
 {
-  HildonBreadCrumb *item;
-
-  hildon_bread_crumb_trail_pop (bct);
-
-  item = HILDON_BREAD_CRUMB (bct->priv->item_list->data);
-
-  g_signal_emit (bct, bread_crumb_trail_signals[CRUMB_CLICKED], 0,
-                 get_bread_crumb_id (item));
+  hildon_bread_crumb_trail_move_parent (bct);
 }
 
 static void
@@ -616,11 +683,15 @@ hildon_bread_crumb_trail_push_text (HildonBreadCrumbTrail *bct,
   g_return_if_fail (text != NULL);
 
   widget = _hildon_bread_crumb_widget_new_with_text (text);
+  if (bct->priv->item_list == NULL)
+    {
+      g_object_set (G_OBJECT (widget), "show-separator", FALSE, NULL);
+    }
   attach_bread_crumb (bct, widget, id, destroy);
 }
 
 /**
- * hildon_bread_crumb_trail_push_text:
+ * hildon_bread_crumb_trail_push_icon:
  * @bct: pointer to #HildonBreadCrumbTrail
  * @text: content of the new bread crumb
  * @icon: a widget to set as the icon in the bread crumb
@@ -647,6 +718,10 @@ hildon_bread_crumb_trail_push_icon (HildonBreadCrumbTrail *bct,
   g_return_if_fail (GTK_IS_WIDGET (icon));
 
   widget = _hildon_bread_crumb_widget_new_with_icon (icon, text);
+  if (bct->priv->item_list == NULL)
+    {
+      g_object_set (G_OBJECT (widget), "show-separator", FALSE, NULL);
+    }
   attach_bread_crumb (bct, widget, id, destroy);
 }
 
@@ -703,4 +778,13 @@ hildon_bread_crumb_trail_clear (HildonBreadCrumbTrail *bct)
     {
       hildon_bread_crumb_trail_pop (bct);
     }
+
+  /*
+    Sensitivity hack from hell. We need to do this while
+     http://bugzilla.gnome.org/show_bug.cgi?id=56070 is not
+     fixed to allow repeated clicking on the back button if
+     someone clears and rebuilds the trail when it's clicked
+  */
+  gtk_widget_hide (priv->back_button);
+  gtk_widget_show (priv->back_button);
 }