Code cleanups in HildonTouchSelector
[hildon] / src / hildon-touch-selector.c
index 9c1536d..26b7e13 100644 (file)
  * If you need a selector widget that also accepts user inputs, you
  * can use #HildonTouchSelectorEntry.
  *
- * Internally it uses a #GtkTreeView, but each one has only a #GtkTreeViewColumn,
- * as the column management is made by #HildonTouchSelector
+ * The current selection has a string representation. In the most common cases,
+ * each column model will contain a text column. You can configure
+ * which column in particular using the #HildonTouchSelectorColumn property
+ * #HildonTouchSelectorColumn:text-column
+ *
+ * You can get this string representation using
+ * hildon_touch_selector_get_current_text().
+ * You can configure how the selection is printed with
+ * hildon_touch_selector_set_print_func(), that sets the current hildon touch
+ * selector print function. The widget has a default print function, that
+ * uses the #HildonTouchSelectorColumn:text-column property on each
+ * #HildonTouchSelectorColumn to compose the final representation.
+ *
+ * If you create the selector using hildon_touch_selector_new_text() you
+ * don't need to take care of this property, as the model is created internally.
+ * If you create the selector using hildon_touch_selector_new(), you
+ * need to specify properly the property for your custom model in order to get a
+ * non-empty string representation, or define your custom print function.
+ *
+ * <example>
+ * <title>Creating a HildonTouchSelector</title>
+ * <programlisting>
+ * void
+ * selection_changed (HildonTouchSelector * selector,
+ *                    gpointer *user_data)
+ * {
+ *   gchar *current_selection = NULL;
+ * <!-- -->
+ *   current_selection = hildon_touch_selector_get_current_text (selector);
+ *   g_debug ("Current selection : &percnt;s", current_selection);
+ * }
+ * <!-- -->
+ * static GtkWidget *
+ * create_customized_selector ()
+ * {
+ *   GtkWidget *selector = NULL;
+ *   GSList *icon_list = NULL;
+ *   GtkListStore *store_icons = NULL;
+ *   GSList *item = NULL;
+ *   GtkCellRenderer *renderer = NULL;
+ *   HildonTouchSelectorColumn *column = NULL;
+ * <!-- -->
+ *   selector = hildon_touch_selector_new ();
+ * <!-- -->
+ *   icon_list = gtk_stock_list_ids ();
+ * <!-- -->
+ *   store_icons = gtk_list_store_new (1, G_TYPE_STRING);
+ *   for (item = icon_list; item; item = g_slist_next (item)) {
+ *     GtkTreeIter iter;
+ *     gchar *label = item->data;
+ * <!-- -->
+ *     gtk_list_store_append (store_icons, &amp;iter);
+ *     gtk_list_store_set (store_icons, &amp;iter, 0, label, -1);
+ *     g_free (label);
+ *   }
+ *   g_slist_free (icon_list);
+ * <!-- -->
+ *   renderer = gtk_cell_renderer_pixbuf_new ();
+ *   gtk_cell_renderer_set_fixed_size (renderer, -1, 100);
+ * <!-- -->
+ *   column = hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector),
+ *                                                 GTK_TREE_MODEL (store_icons),
+ *                                                 renderer, "stock-id", 0, NULL);
+ * <!-- -->
+ *   g_object_set (G_OBJECT (column), "text-column", 0, NULL);
+ * <!-- -->
+ *   hildon_touch_selector_set_column_selection_mode (HILDON_TOUCH_SELECTOR (selector),
+ *                                                    HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE);
+ * <!-- -->
+ *   g_signal_connect (G_OBJECT (selector), "changed",
+ *                     G_CALLBACK (selection_changed), NULL);
+ * <!-- -->
+ *   return selector;
+ * }
+ * <!-- -->
+ * static GtkWidget *
+ * create_simple_selector ()
+ * {
+ *   GtkWidget *selector = NULL;
+ *   gint i;
+ * <!-- -->
+ *   selector = hildon_touch_selector_new_text ();
+ *   hildon_touch_selector_set_column_selection_mode (HILDON_TOUCH_SELECTOR (selector),
+ *                                                    HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE);
+ * <!-- -->
+ *   g_signal_connect (G_OBJECT (selector), "changed",
+ *                     G_CALLBACK (selection_changed), NULL);
+ * <!-- -->
+ *   for (i = 1; i <= 10 ; i++) {
+ *     gchar *label = g_strdup_printf ("Item &amp;percnt;d", i);
+ * <!-- -->
+ *     hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
+ *                                        label);
+ * <!-- -->
+ *     g_free (label);
+ *   }
+ * <!-- -->
+ *   return selector;
+ * }
+ * </programlisting>
+ * </example>
  */
 
+/**
+ * SECTION:hildon-touch-selector-column
+ * @short_description: A visible column in a #HildonTouchSelector
+ *
+ * #HildonTouchSelectorColumn object represents a visible column in
+ * #HildonTouchSelector. It allows to manage the cell renderers related to each
+ * column.
+ */
+
+#undef HILDON_DISABLE_DEPRECATED
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 G_DEFINE_TYPE (HildonTouchSelector, hildon_touch_selector, GTK_TYPE_VBOX)
 
-#define CENTER_ON_SELECTED_ITEM_DELAY 50
-
 /*
+ * IMPLEMENTATION NOTES:
  * Struct to maintain the data of each column. The columns are the elements
- * of the widget that belongs properly to the selection behaviour. As
- * the selector contents are arranged in a #GtkHBox, you can add more widgets,
- * like buttons etc. between the columns, but this doesn't belongs to the
- * selection logic
+ * of the widget that belongs properly to the selection behaviour. Although
+ * internally the columns are arranged in a private #GtkHBox, as the selector
+ * itself is a #GtkVBox, you can add more widgets, like buttons etc., so
+ * you finally could have a widget with more elements that the columns, but
+ * this doesn't belongs to the selection logic
  */
 struct _HildonTouchSelectorColumnPrivate
 {
   HildonTouchSelector *parent;    /* the selector that contains this column */
   GtkTreeModel *model;
+  gint text_column;
   GtkTreeView *tree_view;
+  gulong realize_handler;
+  GtkTreePath *initial_path;
 
   GtkWidget *panarea;           /* the pannable widget */
 };
@@ -89,45 +202,68 @@ struct _HildonTouchSelectorPrivate
 {
   GSList *columns;              /* the selection columns */
   GtkWidget *hbox;              /* the container for the selector's columns */
+  gboolean initial_scroll;      /* whether initial fancy scrolling to selection */
 
   HildonTouchSelectorPrintFunc print_func;
+  gpointer print_user_data;
+  GDestroyNotify print_destroy_func;
 };
 
 enum
 {
-  PROP_HAS_MULTIPLE_SELECTION = 1
+  PROP_HAS_MULTIPLE_SELECTION = 1,
+  PROP_INITIAL_SCROLL
 };
 
 enum
 {
   CHANGED,
+  COLUMNS_CHANGED,
   LAST_SIGNAL
 };
 
 static gint hildon_touch_selector_signals[LAST_SIGNAL] = { 0 };
 
-static void hildon_touch_selector_get_property (GObject * object,
-                                                guint prop_id,
-                                                GValue * value, GParamSpec * pspec);
+static void
+hildon_touch_selector_dispose                   (GObject * object);
 
+static void
+hildon_touch_selector_get_property              (GObject * object,
+                                                 guint prop_id,
+                                                 GValue * value,
+                                                 GParamSpec * pspec);
+static void
+hildon_touch_selector_set_property              (GObject *object,
+                                                 guint prop_id,
+                                                 const GValue *value,
+                                                 GParamSpec *pspec);
 /* gtkwidget */
-static void hildon_touch_selector_map           (GtkWidget * widget);
 
 /* gtkcontainer */
 static void hildon_touch_selector_remove        (GtkContainer * container,
                                                  GtkWidget * widget);
 /* private functions */
-static void _selection_changed_cb               (GtkTreeSelection * selection,
+static void _row_tapped_cb                      (GtkTreeView * tree_view,
+                                                 GtkTreePath * path,
+                                                 gpointer user_data);
+static gchar *_default_print_func               (HildonTouchSelector * selector,
                                                  gpointer user_data);
-static gchar *_default_print_func               (HildonTouchSelector * selector);
 
 static HildonTouchSelectorColumn *_create_new_column (HildonTouchSelector * selector,
                                                  GtkTreeModel * model,
+                                                 gboolean *emit_changed,
                                                  GtkCellRenderer * renderer,
                                                  va_list args);
 static gboolean
-_hildon_touch_selector_center_on_selected_items (gpointer data);
-
+on_realize_cb                                  (GtkWidget *widget,
+                                                gpointer data);
+static void
+hildon_touch_selector_scroll_to (HildonTouchSelectorColumn *column,
+                                 GtkTreeView *tv,
+                                 GtkTreePath *path);
+static gboolean
+_hildon_touch_selector_center_on_selected_items (HildonTouchSelector *selector,
+                                                 HildonTouchSelectorColumn *column);
 static void
 _hildon_touch_selector_set_model                (HildonTouchSelector * selector,
                                                  gint num_column,
@@ -167,25 +303,24 @@ hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
 {
   GObjectClass *gobject_class;
   GtkObjectClass *object_class;
-  GtkWidgetClass *widget_class;
   GtkContainerClass *container_class;
 
-  gobject_class = (GObjectClass *) class;
-  object_class = (GtkObjectClass *) class;
-  widget_class = (GtkWidgetClass *) class;
-  container_class = (GtkContainerClass *) class;
+  gobject_class = G_OBJECT_CLASS (class);
+  object_class = GTK_OBJECT_CLASS (class);
+  container_class = GTK_CONTAINER_CLASS (class);
 
   /* GObject */
-
+  gobject_class->dispose = hildon_touch_selector_dispose;
   gobject_class->get_property = hildon_touch_selector_get_property;
+  gobject_class->set_property = hildon_touch_selector_set_property;
 
   /* GtkWidget */
-  widget_class->map = hildon_touch_selector_map;
 
   /* GtkContainer */
   container_class->remove = hildon_touch_selector_remove;
 
   /* HildonTouchSelector */
+  class->changed = NULL;
   class->set_model = _hildon_touch_selector_set_model;
 
   class->has_multiple_selection = _hildon_touch_selector_has_multiple_selection;
@@ -194,13 +329,13 @@ hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
   /**
    * HildonTouchSelector::changed:
    * @widget: the object which received the signal
+   * @column: the number of the column that has changed
    *
-   * The changed signal is emitted when the active
-   * item is changed. This can be due to the user selecting
-   * a different item from the list, or due to a
-   * call to hildon_touch_selector_select_iter() on
-   * one of the columns.
+   * The "changed" signal is emitted when the active item on any column is changed.
+   * This can be due to the user selecting a different item from the list, or
+   * due to a call to hildon_touch_selector_select_iter() on one of the columns.
    *
+   * Since: 2.2
    */
   hildon_touch_selector_signals[CHANGED] =
     g_signal_new ("changed",
@@ -208,7 +343,24 @@ hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (HildonTouchSelectorClass, changed),
                   NULL, NULL,
-                  gtk_marshal_NONE__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+                  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+  /**
+   * HildonTouchSelector::columns-changed:
+   * @selector: the object which received the signal
+   *
+   * The "columns-changed" signal is emitted when the number
+   * of columns in the #HildonTouchSelector change.
+   *
+   * Since: 2.2
+   */
+  hildon_touch_selector_signals[COLUMNS_CHANGED] =
+    g_signal_new ("columns-changed",
+                  G_OBJECT_CLASS_TYPE (class),
+                  G_SIGNAL_RUN_LAST, 0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
   /* properties */
 
   g_object_class_install_property (gobject_class, PROP_HAS_MULTIPLE_SELECTION,
@@ -223,6 +375,17 @@ hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
                                                          FALSE,
                                                          G_PARAM_READABLE));
 
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
+                                   PROP_INITIAL_SCROLL,
+                                   g_param_spec_boolean ("initial-scroll",
+                                                         "Initial scroll",
+                                                         "Whether to scroll to the"
+                                                         "current selection when"
+                                                         "the selector is first"
+                                                         "shown",
+                                                         TRUE,
+                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
   /* style properties */
   /* We need to ensure fremantle mode for the treeview in order to work
      properly. This is not about the appearance, this is about behaviour */
@@ -239,17 +402,39 @@ hildon_touch_selector_get_property (GObject * object,
                                     guint prop_id,
                                     GValue * value, GParamSpec * pspec)
 {
+  HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
+
   switch (prop_id) {
   case PROP_HAS_MULTIPLE_SELECTION:
     g_value_set_boolean (value,
                          hildon_touch_selector_has_multiple_selection (HILDON_TOUCH_SELECTOR (object)));
     break;
+  case PROP_INITIAL_SCROLL:
+    g_value_set_boolean (value, priv->initial_scroll);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+hildon_touch_selector_set_property (GObject *object, guint prop_id,
+                                    const GValue *value, GParamSpec *pspec)
+{
+  HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
+
+  switch (prop_id) {
+  case PROP_INITIAL_SCROLL:
+    priv->initial_scroll = g_value_get_boolean (value);
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     break;
   }
 }
 
+
 static void
 hildon_touch_selector_init (HildonTouchSelector * selector)
 {
@@ -261,26 +446,46 @@ hildon_touch_selector_init (HildonTouchSelector * selector)
   selector->priv->columns = NULL;
 
   selector->priv->print_func = NULL;
+  selector->priv->print_user_data = NULL;
+  selector->priv->print_destroy_func = NULL;
+  selector->priv->initial_scroll = TRUE;
   selector->priv->hbox = gtk_hbox_new (FALSE, 0);
 
   gtk_box_pack_end (GTK_BOX (selector), selector->priv->hbox,
                     TRUE, TRUE, 0);
   gtk_widget_show (selector->priv->hbox);
-
-  /* FIXME: this is the correct height? A fixed height is the correct 
-     implementation */
-  gtk_widget_set_size_request (GTK_WIDGET (selector), -1, 320);
 }
 
 static void
-hildon_touch_selector_map (GtkWidget * widget)
+hildon_touch_selector_dispose (GObject * object)
 {
-  GTK_WIDGET_CLASS (hildon_touch_selector_parent_class)->map (widget);
+  GObjectClass *gobject_class;
 
-  g_timeout_add (CENTER_ON_SELECTED_ITEM_DELAY,
-                 _hildon_touch_selector_center_on_selected_items, widget);
+  hildon_touch_selector_set_print_func_full (HILDON_TOUCH_SELECTOR (object),
+                                             NULL, NULL, NULL);
+
+  gobject_class = G_OBJECT_CLASS (hildon_touch_selector_parent_class);
+
+  if (gobject_class->dispose)
+    (* gobject_class->dispose) (object);
 }
 
+/*
+ * IMPLEMENTATION NOTES:
+ * Some people sent questions regarding a missing dispose/finalize function on
+ * this widget that could lead to leak memory, so we will clarify this topic.
+ *
+ * This is not required as #HildonTouchSelector extends #GtkContainer. When the
+ * widget is freed, the #GtkContainer freeing memory functions are called. This
+ * process includes remove each widget individually, so all the widgets are
+ * properly freed.
+ *
+ * In the same way, this widget redefines gtk_container->remove function, in
+ * order to free the column related information if it is required.
+ *
+ * Please take a look to hildon_touch_selector_remove for more information.
+ */
+
 /*------------------------------ GtkContainer ------------------------------ */
 
 /*
@@ -290,39 +495,22 @@ static void
 hildon_touch_selector_remove (GtkContainer * container, GtkWidget * widget)
 {
   HildonTouchSelector *selector = NULL;
-  GSList *iter = NULL;
-  gint position = 0;
-  HildonTouchSelectorColumn *current_column = NULL;
-  gint num_columns = 0;
 
   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (container));
-
   selector = HILDON_TOUCH_SELECTOR (container);
-  num_columns = hildon_touch_selector_get_num_columns (selector);
 
-  /* Check if the widget is inside a column and remove
-     it from the list */
-  iter = selector->priv->columns;
-  position = 0;
-  while (iter) {
-    current_column = HILDON_TOUCH_SELECTOR_COLUMN (iter->data);
-    if (widget == current_column->priv->panarea) {
-      current_column = g_slist_nth_data (selector->priv->columns, position);
+  /* Remove the extra data related to the columns, if required. */
+  if (widget == selector->priv->hbox) {
+    g_slist_foreach (selector->priv->columns, (GFunc) g_object_unref, NULL);
 
-      selector->priv->columns = g_slist_remove (selector->priv->columns,
-                                                current_column);
-      g_free (current_column);
+    g_slist_free (selector->priv->columns);
 
-      break;
-    }
-
-    position++;
-    iter = g_slist_next (iter);
-  }
-  if (position >= num_columns) {
-    g_debug ("This widget was not inside the selector column");
+    selector->priv->columns = NULL;
+  } else {
+    g_debug ("Freeing a widget outside the columns logic");
   }
 
+  /* Now remove the widget itself from the container */
   GTK_CONTAINER_CLASS (hildon_touch_selector_parent_class)->remove (container, widget);
 }
 
@@ -334,9 +522,11 @@ hildon_touch_selector_remove (GtkContainer * container, GtkWidget * widget)
  * Default print function
  *
  * Returns: a new string that represents the selected items
+ *
+ * Since: 2.2
  **/
 static gchar *
-_default_print_func (HildonTouchSelector * selector)
+_default_print_func (HildonTouchSelector * selector, gpointer user_data)
 {
   gchar *result = NULL;
   gchar *aux = NULL;
@@ -350,6 +540,8 @@ _default_print_func (HildonTouchSelector * selector)
   GtkTreePath *current_path = NULL;
   GList *selected_rows = NULL;
   gint initial_value = 0;
+  gint text_column = -1;
+  HildonTouchSelectorColumn *column = NULL;
 
   num_columns = hildon_touch_selector_get_num_columns (selector);
 
@@ -360,6 +552,8 @@ _default_print_func (HildonTouchSelector * selector)
     /* In this case we get the first column first */
     selected_rows = hildon_touch_selector_get_selected_rows (selector, 0);
     model = hildon_touch_selector_get_model (selector, 0);
+    column = hildon_touch_selector_get_column (selector, 0);
+    g_object_get (G_OBJECT(column), "text-column", &text_column, NULL);
 
     result = g_strdup_printf ("(");
     i = 0;
@@ -367,7 +561,9 @@ _default_print_func (HildonTouchSelector * selector)
       current_path = item->data;
       gtk_tree_model_get_iter (model, &iter, current_path);
 
-      gtk_tree_model_get (model, &iter, 0, &current_string, -1);
+      if (text_column != -1) {
+        gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
+      }
 
       if (i < g_list_length (selected_rows) - 1) {
         aux = g_strconcat (result, current_string, ",", NULL);
@@ -378,6 +574,12 @@ _default_print_func (HildonTouchSelector * selector)
         g_free (result);
         result = aux;
       }
+
+      if (current_string) {
+        g_free (current_string);
+        current_string = NULL;
+      }
+
       i++;
     }
 
@@ -394,15 +596,26 @@ _default_print_func (HildonTouchSelector * selector)
 
   for (i = initial_value; i < num_columns; i++) {
     model = hildon_touch_selector_get_model (selector, i);
+    column = hildon_touch_selector_get_column (selector, i);
+    g_object_get (G_OBJECT(column), "text-column", &text_column, NULL);
+
     if (hildon_touch_selector_get_selected (selector, i, &iter)) {
+      if (text_column == -1 ) {
+        g_warning ("Trying to use the default print function in HildonTouchSelector, but "
+                   "\"text-column\" property is not set for HildonTouchSelectorColumn %p.", column);
+        current_string = NULL;
+      } else {
+        gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
+      }
 
-      gtk_tree_model_get (model, &iter, 0, &current_string, -1);
-      if (i != 0) {
+      if (i == 0) {
+        result = current_string;
+      } else {
         aux = g_strconcat (result, ":", current_string, NULL);
         g_free (result);
+       g_free (current_string);
+       current_string = NULL;
         result = aux;
-      } else {
-        result = g_strdup_printf ("%s", current_string);
       }
     }
   }
@@ -411,7 +624,7 @@ _default_print_func (HildonTouchSelector * selector)
 }
 
 static void
-_selection_changed_cb (GtkTreeSelection * selection, gpointer user_data)
+_row_tapped_cb (GtkTreeView * tree_view, GtkTreePath * path, gpointer user_data)
 {
   HildonTouchSelector *selector = NULL;
   HildonTouchSelectorColumn *column = NULL;
@@ -431,6 +644,7 @@ _selection_changed_cb (GtkTreeSelection * selection, gpointer user_data)
 static HildonTouchSelectorColumn *
 _create_new_column (HildonTouchSelector * selector,
                     GtkTreeModel * model,
+                   gboolean *emit_changed,
                     GtkCellRenderer * renderer, va_list args)
 {
   HildonTouchSelectorColumn *new_column = NULL;
@@ -456,7 +670,15 @@ _create_new_column (HildonTouchSelector * selector,
     }
   }
 
+#ifdef MAEMO_GTK
   tv = GTK_TREE_VIEW (hildon_gtk_tree_view_new (HILDON_UI_MODE_EDIT));
+#else
+  tv = GTK_TREE_VIEW (gtk_tree_view_new ());
+#endif /* MAEMO_GTK */
+
+  gtk_tree_view_set_enable_search (tv, FALSE);
+  GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (tv), GTK_CAN_FOCUS);
+
   gtk_tree_view_set_model (tv, model);
   gtk_tree_view_set_rules_hint (tv, TRUE);
 
@@ -467,7 +689,7 @@ _create_new_column (HildonTouchSelector * selector,
 
   panarea = hildon_pannable_area_new ();
 
-  g_object_set (G_OBJECT (panarea), "vscrollbar-policy", GTK_POLICY_NEVER,
+  g_object_set (G_OBJECT (panarea),
                 "initial-hint", FALSE, NULL);
 
   gtk_container_add (GTK_CONTAINER (panarea), GTK_WIDGET (tv));
@@ -475,20 +697,24 @@ _create_new_column (HildonTouchSelector * selector,
   new_column->priv->model = model;
   new_column->priv->tree_view = tv;
   new_column->priv->panarea = panarea;
+  new_column->priv->realize_handler = 0;
+  new_column->priv->initial_path = NULL;
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
 
   /* select the first item */
+  *emit_changed = FALSE;
   if (gtk_tree_model_get_iter_first (model, &iter)) {
     gtk_tree_selection_select_iter (selection, &iter);
+    *emit_changed = TRUE;
   }
 
   gtk_widget_grab_focus (GTK_WIDGET (tv));
 
-  /* connect to the changed signal connection */
-  g_signal_connect (G_OBJECT (selection), "changed",
-                    G_CALLBACK (_selection_changed_cb), new_column);
+  /* connect to the hildon-row-tapped signal connection */
+  g_signal_connect (G_OBJECT (tv), "hildon-row-tapped",
+                    G_CALLBACK (_row_tapped_cb), new_column);
 
   return new_column;
 }
@@ -499,14 +725,51 @@ G_DEFINE_TYPE_WITH_CODE (HildonTouchSelectorColumn, hildon_touch_selector_column
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
                                                hildon_touch_selector_column_cell_layout_init))
 
+enum
+{
+  PROP_TEXT_COLUMN = 1
+};
+
+static void
+hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass);
+
 static void
-hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *class)
+hildon_touch_selector_column_get_property (GObject *object, guint property_id,
+                                           GValue *value, GParamSpec *pspec);
+
+static void
+hildon_touch_selector_column_set_property  (GObject *object, guint property_id,
+                                            const GValue *value, GParamSpec *pspec);
+
+static void
+hildon_touch_selector_column_set_text_column (HildonTouchSelectorColumn *column,
+                                              gint text_column);
+static gint
+hildon_touch_selector_column_get_text_column (HildonTouchSelectorColumn *column);
+
+
+static void
+hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass)
 {
-  GObjectClass *object_class;
+  GObjectClass *gobject_class = NULL;
 
-  object_class = (GObjectClass*) class;
+  gobject_class = G_OBJECT_CLASS (klass);
 
-  g_type_class_add_private (object_class, sizeof (HildonTouchSelectorColumnPrivate));
+  g_type_class_add_private (gobject_class, sizeof (HildonTouchSelectorColumnPrivate));
+
+  /* GObject */
+  gobject_class->get_property = hildon_touch_selector_column_get_property;
+  gobject_class->set_property = hildon_touch_selector_column_set_property;
+
+  g_object_class_install_property (G_OBJECT_CLASS(klass),
+                                   PROP_TEXT_COLUMN,
+                                   g_param_spec_int ("text-column",
+                                                     "Text Column",
+                                                     "A column in the data source model to get the strings from.",
+                                                     -1,
+                                                     G_MAXINT,
+                                                     -1,
+                                                     G_PARAM_READWRITE));
 }
 
 static void
@@ -514,10 +777,59 @@ hildon_touch_selector_column_init (HildonTouchSelectorColumn *column)
 {
   column->priv = G_TYPE_INSTANCE_GET_PRIVATE (column, HILDON_TYPE_TOUCH_SELECTOR_COLUMN,
                                               HildonTouchSelectorColumnPrivate);
+  column->priv->text_column = -1;
 }
 
+static void
+hildon_touch_selector_column_set_text_column (HildonTouchSelectorColumn *column,
+                                              gint text_column)
+{
+  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column));
+  g_return_if_fail (text_column >= -1);
+
+  column->priv->text_column = text_column;
+
+  g_object_notify (G_OBJECT (column), "text-column");
+}
+
+static gint
+hildon_touch_selector_column_get_text_column (HildonTouchSelectorColumn *column)
+{
+  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column), -1);
+
+  return column->priv->text_column;
+}
 
 static void
+hildon_touch_selector_column_get_property (GObject *object, guint property_id,
+                                           GValue *value, GParamSpec *pspec)
+{
+  switch (property_id) {
+  case PROP_TEXT_COLUMN:
+    g_value_set_int (value,
+                     hildon_touch_selector_column_get_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object)));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+  }
+}
+
+static void
+hildon_touch_selector_column_set_property (GObject *object, guint property_id,
+                                           const GValue *value, GParamSpec *pspec)
+{
+  switch (property_id) {
+  case PROP_TEXT_COLUMN:
+    hildon_touch_selector_column_set_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object),
+                                                  g_value_get_int (value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+  }
+}
+
+/* ------------------------ GtkCellLayout implementation -------------------- */
+static void
 hildon_touch_selector_column_cell_layout_init (GtkCellLayoutIface      *iface)
 {
   iface->pack_start         = hildon_touch_selector_column_cell_layout_pack_start;
@@ -667,6 +979,8 @@ hildon_touch_selector_column_cell_layout_get_cells (GtkCellLayout         *cell_
  * Creates a new empty #HildonTouchSelector.
  *
  * Returns: a new #HildonTouchSelector.
+ *
+ * Since: 2.2
  **/
 GtkWidget *
 hildon_touch_selector_new (void)
@@ -682,18 +996,23 @@ hildon_touch_selector_new (void)
  * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
  *
  * Returns: A new #HildonTouchSelector
+ *
+ * Since: 2.2
  **/
 GtkWidget *
 hildon_touch_selector_new_text (void)
 {
   GtkWidget *selector;
   GtkListStore *store;
+  HildonTouchSelectorColumn *column = NULL;
 
   selector = hildon_touch_selector_new ();
   store = gtk_list_store_new (1, G_TYPE_STRING);
 
-  hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
-                                            GTK_TREE_MODEL (store), TRUE);
+  column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
+                                                     GTK_TREE_MODEL (store), TRUE);
+
+  g_object_set (G_OBJECT (column), "text-column", 0, NULL);
 
   return selector;
 }
@@ -705,6 +1024,8 @@ hildon_touch_selector_new_text (void)
  *
  * Appends a new entry in a #HildonTouchSelector created with
  * hildon_touch_selector_new_text().
+ *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_append_text (HildonTouchSelector * selector,
@@ -731,6 +1052,8 @@ hildon_touch_selector_append_text (HildonTouchSelector * selector,
  *
  * Prepends a new entry in a #HildonTouchSelector created with
  * hildon_touch_selector_new_text().
+ *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_prepend_text (HildonTouchSelector * selector,
@@ -756,9 +1079,10 @@ hildon_touch_selector_prepend_text (HildonTouchSelector * selector,
  * @position: the position to insert @text.
  * @text: A non %NULL text string.
  *
- * Inserts a new entry in particular position of a #HildoTouchSelector created
- * with hildon_touch_selector_new_text().
+ * Inserts a new entry in a particular position of a
+ * #HildonTouchSelector created with hildon_touch_selector_new_text().
  *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_insert_text (HildonTouchSelector * selector,
@@ -806,6 +1130,8 @@ hildon_touch_selector_insert_text (HildonTouchSelector * selector,
  * the data received.
  *
  * Returns: the new column added added, %NULL otherwise.
+ *
+ * Since: 2.2
  **/
 
 HildonTouchSelectorColumn*
@@ -815,6 +1141,8 @@ hildon_touch_selector_append_column (HildonTouchSelector * selector,
 {
   va_list args;
   HildonTouchSelectorColumn *new_column = NULL;
+  gboolean emit_changed = FALSE;
+  gint colnum;
 
   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
@@ -822,8 +1150,7 @@ hildon_touch_selector_append_column (HildonTouchSelector * selector,
   if (model != NULL) {
 
     va_start (args, cell_renderer);
-    new_column = _create_new_column (selector, model, cell_renderer, args);
-    g_object_ref_sink (new_column);
+    new_column = _create_new_column (selector, model, &emit_changed, cell_renderer, args);
     va_end (args);
 
     selector->priv->columns = g_slist_append (selector->priv->columns,
@@ -833,10 +1160,21 @@ hildon_touch_selector_append_column (HildonTouchSelector * selector,
                         TRUE, TRUE, 6);
 
     gtk_widget_show_all (new_column->priv->panarea);
+
+    if (selector->priv->initial_scroll) {
+      _hildon_touch_selector_center_on_selected_items (selector, new_column);
+    }
+
   } else {
     return NULL;
   }
 
+  g_signal_emit (selector, hildon_touch_selector_signals[COLUMNS_CHANGED], 0);
+  if (emit_changed) {
+    colnum = g_slist_length (selector->priv->columns);
+    g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, colnum);
+  }
+
   return new_column;
 }
 
@@ -851,31 +1189,27 @@ hildon_touch_selector_append_column (HildonTouchSelector * selector,
  * widget.
  *
  * Returns: the new column added, NULL otherwise.
+ *
+ * Since: 2.2
  **/
 HildonTouchSelectorColumn*
 hildon_touch_selector_append_text_column (HildonTouchSelector * selector,
                                           GtkTreeModel * model, gboolean center)
 {
-  GtkCellRenderer *renderer = NULL;
-  GValue val = { 0, };
+  gfloat xalign = center ? 0.5 : 0.0;
+  GtkCellRenderer *renderer;
 
-  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
-  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
+  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
+  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
 
-  if (model != NULL) {
-    renderer = gtk_cell_renderer_text_new ();
+  renderer = gtk_cell_renderer_text_new ();
 
-    if (center) {
-      g_value_init (&val, G_TYPE_FLOAT);
-      g_value_set_float (&val, 0.5);
-      g_object_set_property (G_OBJECT (renderer), "xalign", &val);
-    }
+  g_object_set (renderer,
+                "xalign", xalign,
+                NULL);
 
-    return hildon_touch_selector_append_column (selector, model, renderer,
-                                                "text", 0, NULL);
-  } else {
-    return FALSE;
-  }
+  return hildon_touch_selector_append_column (selector, model, renderer,
+                                              "text", 0, NULL);
 }
 
 /**
@@ -886,6 +1220,8 @@ hildon_touch_selector_append_text_column (HildonTouchSelector * selector,
  * Removes a column from @selector.
  *
  * Returns: %TRUE if the column was removed, %FALSE otherwise
+ *
+ * Since: 2.2
  **/
 gboolean
 hildon_touch_selector_remove_column (HildonTouchSelector * selector, gint column)
@@ -904,6 +1240,8 @@ hildon_touch_selector_remove_column (HildonTouchSelector * selector, gint column
   priv->columns = g_slist_remove (priv->columns, current_column);
   g_object_unref (current_column);
 
+  g_signal_emit (selector, hildon_touch_selector_signals[COLUMNS_CHANGED], 0);
+
   return TRUE;
 }
 
@@ -922,6 +1260,7 @@ hildon_touch_selector_remove_column (HildonTouchSelector * selector, gint column
  *             interface instead. See
  *             hildon_touch_selector_get_column().
  *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_set_column_attributes (HildonTouchSelector * selector,
@@ -971,6 +1310,8 @@ hildon_touch_selector_set_column_attributes (HildonTouchSelector * selector,
  * Gets the number of columns in the #HildonTouchSelector.
  *
  * Returns: the number of columns in @selector.
+ *
+ * Since: 2.2
  **/
 gint
 hildon_touch_selector_get_num_columns (HildonTouchSelector * selector)
@@ -987,13 +1328,15 @@ hildon_touch_selector_get_num_columns (HildonTouchSelector * selector)
  * Gets the selection mode of @selector.
  *
  * Returns: one of #HildonTouchSelectorSelectionMode
+ *
+ * Since: 2.2
  **/
 HildonTouchSelectorSelectionMode
 hildon_touch_selector_get_column_selection_mode (HildonTouchSelector * selector)
 {
   HildonTouchSelectorSelectionMode result =
     HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
-  GtkSelectionMode treeview_mode = GTK_SELECTION_SINGLE;
+  GtkSelectionMode treeview_mode = GTK_SELECTION_BROWSE;
   HildonTouchSelectorColumn *column = NULL;
   GtkTreeSelection *selection = NULL;
 
@@ -1022,6 +1365,8 @@ hildon_touch_selector_get_column_selection_mode (HildonTouchSelector * selector)
  * @mode: the #HildonTouchSelectorMode for @selector
  *
  * Sets the selection mode for @selector. See #HildonTouchSelectorSelectionMode.
+ *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
@@ -1032,17 +1377,24 @@ hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
   GtkTreeSelection *selection = NULL;
   GtkSelectionMode treeview_mode = GTK_SELECTION_MULTIPLE;
   GtkTreeIter iter;
+  HildonTouchSelectorSelectionMode current_mode;
 
   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
   g_return_if_fail (hildon_touch_selector_get_num_columns (selector) > 0);
 
+  current_mode = hildon_touch_selector_get_column_selection_mode (selector);
+
+  if (current_mode == mode) {
+    return;
+  }
+
   column = HILDON_TOUCH_SELECTOR_COLUMN ((g_slist_nth (selector->priv->columns, 0))->data);
   tv = column->priv->tree_view;
 
   if (tv) {
     switch (mode) {
     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE:
-      treeview_mode = GTK_SELECTION_SINGLE;
+      treeview_mode = GTK_SELECTION_BROWSE;
       break;
     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE:
       treeview_mode = GTK_SELECTION_MULTIPLE;
@@ -1056,6 +1408,8 @@ hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
     gtk_tree_model_get_iter_first (column->priv->model, &iter);
     gtk_tree_selection_unselect_all (selection);
     gtk_tree_selection_select_iter (selection, &iter);
+
+    g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, column);
   }
 
 }
@@ -1071,6 +1425,7 @@ hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
  * selected in each column in @selector. Use this to override this method if you
  * need a particular representation for your application.
  *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_set_print_func (HildonTouchSelector * selector,
@@ -1078,7 +1433,44 @@ hildon_touch_selector_set_print_func (HildonTouchSelector * selector,
 {
   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
 
+  hildon_touch_selector_set_print_func_full (selector, func, NULL, NULL);
+}
+
+/**
+ * hildon_touch_selector_set_print_func_full:
+ * @selector: a #HildonTouchSelector
+ * @func: a #HildonTouchSelectorPrintFunc function
+ * @user_data: a pointer to user data or %NULL
+ * @destroy_func: a callback for freeing the user data or %NULL
+ *
+ * Sets the function to be used by hildon_touch_selector_get_current_text()
+ * to produce a text representation of the currently selected items in @selector.
+ * The default function will return a concatenation of comma separated items
+ * selected in each column in @selector. Use this to override this method if you
+ * need a particular representation for your application.
+ *
+ * Since: 2.2
+ **/
+void
+hildon_touch_selector_set_print_func_full (HildonTouchSelector          *selector,
+                                           HildonTouchSelectorPrintFunc  func,
+                                           gpointer                      user_data,
+                                           GDestroyNotify                destroy_func)
+{
+  gpointer       old_user_data;
+  GDestroyNotify old_destroy_func;
+
+  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
+
+  old_user_data = selector->priv->print_user_data;
+  old_destroy_func = selector->priv->print_destroy_func;
+
   selector->priv->print_func = func;
+  selector->priv->print_user_data = user_data;
+  selector->priv->print_destroy_func = destroy_func;
+
+  if (old_destroy_func && old_user_data != user_data)
+    (*old_destroy_func) (old_user_data);
 }
 
 /**
@@ -1109,6 +1501,8 @@ hildon_touch_selector_get_print_func (HildonTouchSelector * selector)
  * column number is taken from @column.
  *
  * @selector must be in %HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE
+ *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_set_active                (HildonTouchSelector *selector,
@@ -1133,6 +1527,8 @@ hildon_touch_selector_set_active                (HildonTouchSelector *selector,
   if (index != -1)
     gtk_tree_selection_select_path (selection, path);
 
+  g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, column);
+
   gtk_tree_path_free (path);
 }
 
@@ -1148,6 +1544,8 @@ hildon_touch_selector_set_active                (HildonTouchSelector *selector,
  *
  * Returns: an integer which is the index of the currently active
  * item, or -1 if there's no active item.
+ *
+ * Since: 2.2
  **/
 gint
 hildon_touch_selector_get_active                (HildonTouchSelector *selector,
@@ -1169,9 +1567,9 @@ hildon_touch_selector_get_active                (HildonTouchSelector *selector,
   current_column = g_slist_nth_data (selector->priv->columns, column);
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (current_column->priv->tree_view));
+  g_return_val_if_fail (gtk_tree_selection_get_selected (selection, NULL, &iter), -1);
 
-  gtk_tree_selection_get_selected (selection, NULL, &iter);
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (current_column->priv->tree_view));
   path = gtk_tree_model_get_path (model, &iter);
   index = (gtk_tree_path_get_indices (path))[0];
 
@@ -1197,6 +1595,8 @@ hildon_touch_selector_get_active                (HildonTouchSelector *selector,
  * See gtk_tree_selection_get_selected() for more information.
  *
  * Returns: %TRUE if @iter was correctly set, %FALSE otherwise
+ *
+ * Since: 2.2
  **/
 gboolean
 hildon_touch_selector_get_selected (HildonTouchSelector * selector,
@@ -1233,6 +1633,7 @@ hildon_touch_selector_get_selected (HildonTouchSelector * selector,
  * Sets the currently selected item in the column @column to the one pointed by @iter,
  * optionally smoothly scrolling to it.
  *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_select_iter (HildonTouchSelector * selector,
@@ -1241,11 +1642,9 @@ hildon_touch_selector_select_iter (HildonTouchSelector * selector,
 {
   GtkTreePath *path;
   GtkTreeModel *model;
-  GdkRectangle rect;
   HildonTouchSelectorColumn *current_column = NULL;
   GtkTreeView *tv = NULL;
   GtkTreeSelection *selection = NULL;
-  gint y;
 
   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
@@ -1260,13 +1659,11 @@ hildon_touch_selector_select_iter (HildonTouchSelector * selector,
   gtk_tree_selection_select_iter (selection, iter);
 
   if (scroll_to) {
-    gtk_tree_view_get_background_area (tv,
-                                       path, NULL, &rect);
-    gtk_tree_view_convert_bin_window_to_tree_coords (tv,
-                                                     0, rect.y, NULL, &y);
-    hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA (current_column->priv->panarea),
-                                    -1, y);
+    hildon_touch_selector_scroll_to (current_column, tv, path);
   }
+
+  g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, column);
+
   gtk_tree_path_free (path);
 }
 
@@ -1278,6 +1675,7 @@ hildon_touch_selector_select_iter (HildonTouchSelector * selector,
  *
  * Unselect the item pointed by @iter in the column @column
  *
+ * Since: 2.2
  **/
 
 void hildon_touch_selector_unselect_iter (HildonTouchSelector * selector,
@@ -1293,6 +1691,34 @@ void hildon_touch_selector_unselect_iter (HildonTouchSelector * selector,
   current_column = g_slist_nth_data (selector->priv->columns, column);
   selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
   gtk_tree_selection_unselect_iter (selection, iter);
+
+  g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, column);
+}
+
+/**
+ * hildon_touch_selector_unselect_all:
+ * @selector: a #HildonTouchSelector
+ * @column: the position of the column to get the selected rows from
+ *
+ * Unselects all the selected items in the column @column.
+ *
+ * Since: 2.2
+ **/
+void
+hildon_touch_selector_unselect_all (HildonTouchSelector * selector,
+                                    gint column)
+{
+  HildonTouchSelectorColumn *current_column = NULL;
+  GtkTreeSelection *selection = NULL;
+
+  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
+  g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
+
+  current_column = g_slist_nth_data (selector->priv->columns, column);
+  selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
+  gtk_tree_selection_unselect_all (selection);
+
+  g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, column);
 }
 
 /**
@@ -1309,6 +1735,7 @@ void hildon_touch_selector_unselect_iter (HildonTouchSelector * selector,
  *
  * Returns: A new #GList containing a #GtkTreePath for each selected row in the column @column.
  *
+ * Since: 2.2
  **/
 GList *
 hildon_touch_selector_get_selected_rows (HildonTouchSelector * selector,
@@ -1338,6 +1765,8 @@ hildon_touch_selector_get_selected_rows (HildonTouchSelector * selector,
  * Gets the model of a column of @selector.
  *
  * Returns: the #GtkTreeModel for the column @column of @selector.
+ *
+ * Since: 2.2
  **/
 GtkTreeModel *
 hildon_touch_selector_get_model (HildonTouchSelector * selector, gint column)
@@ -1374,6 +1803,8 @@ _hildon_touch_selector_set_model (HildonTouchSelector * selector,
  * @model: a #GtkTreeModel
  *
  * Sets the #GtkTreeModel for a particular column in @model.
+ *
+ * Since: 2.2
  **/
 void
 hildon_touch_selector_set_model (HildonTouchSelector * selector,
@@ -1393,6 +1824,8 @@ hildon_touch_selector_set_model (HildonTouchSelector * selector,
  * each column of @selector. See hildon_touch_selector_set_print_func().
  *
  * Returns: a newly allocated string.
+ *
+ * Since: 2.2
  **/
 gchar *
 hildon_touch_selector_get_current_text (HildonTouchSelector * selector)
@@ -1401,68 +1834,175 @@ hildon_touch_selector_get_current_text (HildonTouchSelector * selector)
   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
 
   if (selector->priv->print_func) {
-    result = (*selector->priv->print_func) (selector);
+    result = (*selector->priv->print_func) (selector, selector->priv->print_user_data);
   } else {
-    result = _default_print_func (selector);
+    result = _default_print_func (selector, NULL);
   }
 
   return result;
 }
 
+static void
+search_nearest_element (HildonPannableArea *panarea,
+                        GtkTreeView *tv,
+                        GList *selected_rows,
+                        GtkTreePath **nearest_path)
+{
+  GtkAdjustment *adj = NULL;
+  gdouble target_value = 0;
+  GdkRectangle rect;
+  GList *iter = NULL;
+  GtkTreePath *path = NULL;
+  gint y = -1;
+  gdouble nearest_distance = -1;
+  gdouble current_distance = -1;
+  GtkTreePath *result_path = NULL;
+
+  g_assert (nearest_path != NULL);
+
+  if (selected_rows == NULL) {
+    *nearest_path = NULL;
+    return;
+  }
+
+  adj = hildon_pannable_area_get_vadjustment (panarea);
+  g_return_if_fail (adj != NULL);
+
+  /* we add this in order to check the nearest to the center of
+     the visible area */
+  target_value = gtk_adjustment_get_value (adj) + adj->page_size/2;
+
+  path = result_path = selected_rows->data;
+  gtk_tree_view_get_background_area (tv, path, NULL, &rect);
+  gtk_tree_view_convert_bin_window_to_tree_coords (tv, 0, rect.y, NULL, &y);
+  nearest_distance = abs (target_value - y);
+
+  for (iter = selected_rows->next; iter; iter = g_list_next (iter)) {
+    gtk_tree_view_get_background_area (tv, path, NULL, &rect);
+    gtk_tree_view_convert_bin_window_to_tree_coords (tv, 0, rect.y, NULL, &y);
+    current_distance = abs (target_value - y);
+    if (current_distance < nearest_distance) {
+      nearest_distance = current_distance;
+      result_path = path;
+    }
+  }
+
+  *nearest_path = result_path;
+}
+
 static gboolean
-_hildon_touch_selector_center_on_selected_items (gpointer data)
+on_realize_cb                                  (GtkWidget *widget,
+                                                gpointer data)
 {
-  HildonTouchSelector *selector = NULL;
   HildonTouchSelectorColumn *column = NULL;
-  GSList *iter_column = NULL;
-  GtkTreeIter iter;
-  GtkTreePath *path;
   GdkRectangle rect;
   gint y;
-  gint i;
-  HildonTouchSelectorSelectionMode selection_mode;
 
-  /* ensure to center on the initial values */
-  selector = HILDON_TOUCH_SELECTOR (data);
+  column = HILDON_TOUCH_SELECTOR_COLUMN (data);
 
-  selection_mode = hildon_touch_selector_get_column_selection_mode (selector);
+  if (column->priv->initial_path) {
+    gtk_tree_view_get_background_area (GTK_TREE_VIEW (column->priv->tree_view),
+                                       column->priv->initial_path, NULL, &rect);
+    gtk_tree_view_convert_bin_window_to_tree_coords
+      (GTK_TREE_VIEW (column->priv->tree_view),
+       0, rect.y, NULL, &y);
 
-  iter_column = selector->priv->columns;
-  i = 0;
-  while (iter_column) {
-    column = HILDON_TOUCH_SELECTOR_COLUMN (iter_column->data);
+    hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA (column->priv->panarea),
+                                    -1, y);
 
-    if ((i == 0)
-        && (selection_mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)) {
-      break;
+    gtk_tree_path_free (column->priv->initial_path);
+
+    column->priv->initial_path = NULL;
+
+  }
+
+  g_signal_handler_disconnect (column->priv->panarea,
+                               column->priv->realize_handler);
+
+  return FALSE;
+}
+
+static void
+hildon_touch_selector_scroll_to (HildonTouchSelectorColumn *column,
+                                 GtkTreeView *tv,
+                                 GtkTreePath *path)
+{
+  if (GTK_WIDGET_REALIZED (column->priv->panarea)) {
+    GdkRectangle rect;
+    gint y;
+
+    gtk_tree_view_get_background_area (tv,
+                                       path, NULL, &rect);
+    gtk_tree_view_convert_bin_window_to_tree_coords (tv,
+                                                     0, rect.y, NULL, &y);
+
+    hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA
+                                    (column->priv->panarea), -1, y);
+  } else {
+    if (column->priv->realize_handler != 0) {
+
+      if (column->priv->initial_path) {
+        gtk_tree_path_free (column->priv->initial_path);
+        column->priv->initial_path = NULL;
+      }
+
+      g_signal_handler_disconnect (column->priv->panarea,
+                                   column->priv->realize_handler);
+      column->priv->realize_handler = 0;
     }
-    if (hildon_touch_selector_get_selected (selector, i, &iter)) {
-      path = gtk_tree_model_get_path (column->priv->model, &iter);
-      gtk_tree_view_get_background_area (GTK_TREE_VIEW
-                                         (column->priv->tree_view), path, NULL,
-                                         &rect);
 
-      gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW
-                                                       (column->priv->tree_view), 0,
-                                                       rect.y, NULL, &y);
+    column->priv->initial_path = gtk_tree_path_copy (path);
+    column->priv->realize_handler =
+      g_signal_connect_after (G_OBJECT (column->priv->panarea), "realize",
+                              G_CALLBACK (on_realize_cb),
+                              column);
+  }
+}
 
-      hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA
-                                      (column->priv->panarea), -1, y);
+/**
+ *
+ * Center on the selected item of a concrete column
+ *
+ * Returns: TRUE if was able to do that
+ *          FALSE otherwise
+ */
+static gboolean
+_hildon_touch_selector_center_on_selected_items (HildonTouchSelector *selector,
+                                                 HildonTouchSelectorColumn *column)
+{
+  GtkTreePath *path = NULL;
+  GList *selected_rows = NULL;
+  gint num_column = -1;
+
+  num_column = g_slist_index (selector->priv->columns, column);
 
-      gtk_tree_path_free (path);
+  selected_rows = hildon_touch_selector_get_selected_rows (selector, num_column);
+  if (selected_rows) {
+    search_nearest_element (HILDON_PANNABLE_AREA (column->priv->panarea),
+                             GTK_TREE_VIEW (column->priv->tree_view),
+                             selected_rows,
+                             &path);
+
+    if (path != NULL) {
+      hildon_touch_selector_scroll_to (column,
+                                       GTK_TREE_VIEW (column->priv->tree_view),
+                                       path);
+    } else {
+      return FALSE;
     }
-    iter_column = iter_column->next;
-    i++;
+
+    g_list_foreach (selected_rows, (GFunc) (gtk_tree_path_free), NULL);
+    g_list_free (selected_rows);
   }
 
-  return FALSE;
+  return TRUE;
 }
 
 static gboolean
 _hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
 {
-  HildonTouchSelectorSelectionMode mode = HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
-  gint n_columns = 0;
+  HildonTouchSelectorSelectionMode mode;
+  gint n_columns;
 
   n_columns = hildon_touch_selector_get_num_columns (selector);
   mode = hildon_touch_selector_get_column_selection_mode (selector);
@@ -1484,6 +2024,8 @@ _hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
  * its internal #HildonTouchSelector has multiple columns, for instance.
  *
  * Returns: %TRUE if @selector requires multiple selection steps.
+ *
+ * Since: 2.2
  **/
 gboolean
 hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
@@ -1499,12 +2041,12 @@ hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
  * @selector: A #HildonTouchSelector
  * @column: a column number
  *
- * Returns the @column<!-- -->-th #HildonTouchSelectorColumn in @selector
- *
- *
  * Use this method to retrieve a #HildonTouchSelectorColumn. Then, you can use
  * the #GtkCellLayout interface to set up the layout of the column.
  *
+ * Returns: the @column<!-- -->-th #HildonTouchSelectorColumn in @selector
+ *
+ * Since: 2.2
  **/
 HildonTouchSelectorColumn *
 hildon_touch_selector_get_column (HildonTouchSelector * selector,
@@ -1518,3 +2060,29 @@ hildon_touch_selector_get_column (HildonTouchSelector * selector,
 
   return g_slist_nth_data (selector->priv->columns, column);
 }
+
+
+/**
+ * hildon_touch_selector_center_on_selected:
+ * @selector: a #HildonTouchSelector
+ *
+ * Ensures all the columns in a #HildonTouchSelector show a selected
+ * item. If one of the columns is in
+ * %HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE mode, that column
+ * will be scrolled to ensure the selected item that is closest to the
+ * currently visible area is shown.
+ *
+ * Since: 2.2
+ **/
+void
+hildon_touch_selector_center_on_selected         (HildonTouchSelector *selector)
+{
+  GSList *iter = NULL;
+
+  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
+
+  for (iter = selector->priv->columns; iter; iter = g_slist_next (iter)) {
+    _hildon_touch_selector_center_on_selected_items (selector,
+                                                    HILDON_TOUCH_SELECTOR_COLUMN (iter->data));
+  }
+}