gui: Make Tab, Shift+Tab skip row head and new-var columns in data sheet. 20121025032015/pspp 20121026032030/pspp 20121027032008/pspp 20121028032032/pspp
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 25 Oct 2012 06:24:25 +0000 (23:24 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 25 Oct 2012 06:24:25 +0000 (23:24 -0700)
Before this commit, Tab and Shift+Tab acted just like Left and Right in
the data sheet, that is, they visit every column except the row head
column and always stay on the same row.  After this commit, Tab and
Shift+Tab skip the row head and new-variable columns.  Also, Tab at the
end of a row goes to the beginning of the next row, and Shift+Tab at the
beginning of a row goes to the end of the previous row.

Suggested by John Darrington.

src/ui/gui/pspp-sheet-view-column.c
src/ui/gui/pspp-sheet-view-column.h
src/ui/gui/pspp-sheet-view.c
src/ui/gui/psppire-data-sheet.c

index c586a4bdaee2e1b0839102f09c43a5e846ad0a0e..a8d9f4891880a8f11dcd0e5dce14aacccf1a4dd0 100644 (file)
@@ -72,7 +72,8 @@ enum
   PROP_QUICK_EDIT,
   PROP_SELECTED,
   PROP_SELECTABLE,
-  PROP_ROW_HEAD
+  PROP_ROW_HEAD,
+  PROP_TABBABLE
 };
 
 enum
@@ -426,6 +427,14 @@ pspp_sheet_view_column_class_init (PsppSheetViewColumnClass *class)
                                                          P_("If true, this column is a \"row head\", equivalent to a column head.  If rectangular selection is enabled, then shift+click and control+click in the column select row ranges and toggle row selection, respectively.  The column should ordinarily include a button cell; clicking on the button will select the row (and deselect all other rows)."),
                                                          FALSE,
                                                          GTK_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_TABBABLE,
+                                   g_param_spec_boolean ("tabbable",
+                                                         P_("Tabbable"),
+                                                         P_("If true, Tab and Shift+Tab visit this column.  If false, Tab and Shift+Tab skip this column."),
+                                                         TRUE,
+                                                         GTK_PARAM_READWRITE));
 }
 
 static void
@@ -468,6 +477,7 @@ pspp_sheet_view_column_init (PsppSheetViewColumn *tree_column)
   tree_column->selected = FALSE;
   tree_column->selectable = TRUE;
   tree_column->row_head = FALSE;
+  tree_column->tabbable = TRUE;
   tree_column->sort_order = GTK_SORT_ASCENDING;
   tree_column->show_sort_indicator = FALSE;
   tree_column->property_changed_signal = 0;
@@ -621,6 +631,11 @@ pspp_sheet_view_column_set_property (GObject         *object,
                                              g_value_get_boolean (value));
       break;
 
+    case PROP_TABBABLE:
+      pspp_sheet_view_column_set_tabbable (tree_column,
+                                           g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -739,6 +754,11 @@ pspp_sheet_view_column_get_property (GObject         *object,
                            pspp_sheet_view_column_get_row_head (tree_column));
       break;
 
+    case PROP_TABBABLE:
+      g_value_set_boolean (value,
+                           pspp_sheet_view_column_get_tabbable (tree_column));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2741,6 +2761,44 @@ pspp_sheet_view_column_get_row_head (PsppSheetViewColumn *tree_column)
 }
 
 
+/**
+ * pspp_sheet_view_column_set_tabbable:
+ * @tree_column: A #PsppSheetViewColumn
+ * @tabbable: If true, the column is "tabbable", meaning that Tab and Shift+Tab
+ * in the sheet visit this column.  If false, Tab and Shift+Tab skip this
+ * column.
+ **/
+void
+pspp_sheet_view_column_set_tabbable (PsppSheetViewColumn *tree_column,
+                                     gboolean           tabbable)
+{
+  g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
+
+  tabbable = !!tabbable;
+  if (tree_column->tabbable != tabbable)
+    {
+      tree_column->tabbable = tabbable;
+      g_object_notify (G_OBJECT (tree_column), "tabbable");
+    }
+}
+
+/**
+ * pspp_sheet_view_column_get_tabbable:
+ * @tree_column: A #PsppSheetViewColumn
+ *
+ * Returns %TRUE if the column is tabbable.
+ *
+ * Return value: %TRUE if the column is tabbable.
+ **/
+gboolean
+pspp_sheet_view_column_get_tabbable (PsppSheetViewColumn *tree_column)
+{
+  g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
+
+  return tree_column->tabbable;
+}
+
+
 /**
  * pspp_sheet_view_column_set_sort_column_id:
  * @tree_column: a #PsppSheetViewColumn
index 743440f6820e4b52f124135041c7ab2fe184d8b6..f18fee1d104e2d6b0ddb57c26c6de2870a1ba0e0 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2011 Free Software Foundation, Inc.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -110,6 +110,7 @@ struct _PsppSheetViewColumn
   guint GSEAL (selected)            : 1;
   guint GSEAL (selectable)          : 1;
   guint GSEAL (row_head)            : 1;
+  guint GSEAL (tabbable)            : 1;
   guint GSEAL (need_button)         : 1;
 };
 
@@ -214,6 +215,9 @@ gboolean                pspp_sheet_view_column_get_selectable     (PsppSheetView
 void                    pspp_sheet_view_column_set_row_head     (PsppSheetViewColumn       *tree_column,
                                                                  gboolean                 row_head);
 gboolean                pspp_sheet_view_column_get_row_head     (PsppSheetViewColumn       *tree_column);
+void                    pspp_sheet_view_column_set_tabbable     (PsppSheetViewColumn       *tree_column,
+                                                                 gboolean                 tabbable);
+gboolean                pspp_sheet_view_column_get_tabbable     (PsppSheetViewColumn       *tree_column);
 
 
 
index 32627beffdcf2bd8c2db1a8a30f80e148dc80bda..c06abeb2a6681214ed53b6785d31ed766c47cd2f 100644 (file)
@@ -315,12 +315,14 @@ static void     pspp_sheet_view_clamp_column_visible           (PsppSheetView
 static gboolean pspp_sheet_view_maybe_begin_dragging_row       (PsppSheetView        *tree_view,
                                                              GdkEventMotion     *event);
 static void     pspp_sheet_view_focus_to_cursor                (PsppSheetView        *tree_view);
-static void     pspp_sheet_view_move_cursor_up_down            (PsppSheetView        *tree_view,
+static gboolean pspp_sheet_view_move_cursor_up_down            (PsppSheetView        *tree_view,
                                                              gint                count);
 static void     pspp_sheet_view_move_cursor_page_up_down       (PsppSheetView        *tree_view,
                                                              gint                count);
 static void     pspp_sheet_view_move_cursor_left_right         (PsppSheetView        *tree_view,
                                                              gint                count);
+static void     pspp_sheet_view_move_cursor_tab                (PsppSheetView        *tree_view,
+                                                             gint                count);
 static void     pspp_sheet_view_move_cursor_start_end          (PsppSheetView        *tree_view,
                                                              gint                count);
 static void     pspp_sheet_view_real_set_cursor                (PsppSheetView        *tree_view,
@@ -921,6 +923,14 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class)
                                     G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
                                     G_TYPE_INT, -1);
 
+      gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
+                                    G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
+                                    G_TYPE_INT, 1);
+
+      gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
+                                    G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
+                                    G_TYPE_INT, -1);
+
       gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
                                     G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
                                     G_TYPE_INT, 1);
@@ -6976,8 +6986,9 @@ pspp_sheet_view_real_move_cursor (PsppSheetView       *tree_view,
 
   switch (step)
     {
-      /* currently we make no distinction.  When we go bi-di, we need to */
     case GTK_MOVEMENT_LOGICAL_POSITIONS:
+      pspp_sheet_view_move_cursor_tab (tree_view, count);
+      break;
     case GTK_MOVEMENT_VISUAL_POSITIONS:
       pspp_sheet_view_move_cursor_left_right (tree_view, count);
       break;
@@ -7794,7 +7805,7 @@ pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
     }
 }
 
-static void
+static gboolean
 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
                                   gint         count)
 {
@@ -7805,19 +7816,19 @@ pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
   gboolean grab_focus = TRUE;
 
   if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
-    return;
+    return FALSE;
 
   cursor_path = NULL;
   if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
     /* FIXME: we lost the cursor; should we get the first? */
-    return;
+    return FALSE;
 
   cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
 
   if (cursor_node < 0)
     /* FIXME: we lost the cursor; should we get the first? */
-    return;
+    return FALSE;
 
   selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
 
@@ -7909,6 +7920,8 @@ pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
 
   if (grab_focus)
     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+
+  return new_cursor_node >= 0;
 }
 
 static void
@@ -8097,6 +8110,113 @@ pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
                                      tree_view->priv->focus_column, TRUE);
 }
 
+static gboolean
+try_move_cursor_tab (PsppSheetView *tree_view,
+                     gboolean start_at_focus_column,
+                     gint count)
+{
+  PsppSheetViewColumn *column;
+  GtkTreeIter iter;
+  int cursor_node = -1;
+  GtkTreePath *cursor_path = NULL;
+  gboolean rtl;
+  GList *list;
+
+  if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
+    cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
+  else
+    return TRUE;
+
+  _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
+  if (cursor_node < 0)
+    return TRUE;
+  if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
+    {
+      gtk_tree_path_free (cursor_path);
+      return TRUE;
+    }
+  gtk_tree_path_free (cursor_path);
+
+  rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
+  if (start_at_focus_column)
+    {
+      list = (rtl
+              ? g_list_last (tree_view->priv->columns)
+              : g_list_first (tree_view->priv->columns));
+      if (tree_view->priv->focus_column)
+        {
+          for (; list; list = (rtl ? list->prev : list->next))
+            {
+              if (list->data == tree_view->priv->focus_column)
+                break;
+            }
+        }
+    }
+  else
+    {
+      list = (rtl ^ (count == 1)
+              ? g_list_first (tree_view->priv->columns)
+              : g_list_last (tree_view->priv->columns));
+    }
+
+  while (list)
+    {
+      gboolean left, right;
+
+      column = list->data;
+      if (column->visible == FALSE || !column->tabbable)
+       goto loop_end;
+
+      pspp_sheet_view_column_cell_set_cell_data (column,
+                                                 tree_view->priv->model,
+                                                 &iter);
+
+      if (rtl)
+        {
+         right = list->prev ? TRUE : FALSE;
+         left = list->next ? TRUE : FALSE;
+       }
+      else
+        {
+         left = list->prev ? TRUE : FALSE;
+         right = list->next ? TRUE : FALSE;
+        }
+
+      if (column->tabbable
+          && _pspp_sheet_view_column_cell_focus (column, count, left, right))
+       {
+         tree_view->priv->focus_column = column;
+          _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
+          g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
+          gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+          return TRUE;
+       }
+    loop_end:
+      if (count == 1)
+       list = rtl ? list->prev : list->next;
+      else
+       list = rtl ? list->next : list->prev;
+    }
+
+  return FALSE;
+}
+
+static void
+pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
+                                 gint         count)
+{
+  if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
+    return;
+
+  if (!try_move_cursor_tab (tree_view, TRUE, count)
+      && pspp_sheet_view_move_cursor_up_down (tree_view, count)
+      && !try_move_cursor_tab (tree_view, FALSE, count))
+    gtk_widget_error_bell (GTK_WIDGET (tree_view));
+
+  pspp_sheet_view_clamp_column_visible (tree_view,
+                                        tree_view->priv->focus_column, TRUE);
+}
+
 static void
 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
                                     gint         count)
@@ -12181,9 +12301,10 @@ pspp_sheet_view_event (GtkWidget *widget,
       keyval = GDK_Down;
       break;
 
-    case GDK_Tab:
+    case GDK_Tab:       case GDK_KP_Tab:
     case GDK_ISO_Left_Tab:
-      keyval = event->state & GDK_SHIFT_MASK ? GDK_Left : GDK_Right;
+      keyval = GDK_Tab;
+      state |= event->state & GDK_SHIFT_MASK;
       break;
 
     default:
index 0ff5974d3da9ac5e18b9088408ae4de60a3a2c0b..6714bfb90ef1b29998dc2dafb8aeeb9b3fcf159e 100644 (file)
@@ -228,6 +228,7 @@ make_row_number_column (PsppireDataSheet *data_sheet,
                                                        renderer, NULL);
   pspp_sheet_view_column_set_selectable (column, TRUE);
   pspp_sheet_view_column_set_row_head (column, TRUE);
+  pspp_sheet_view_column_set_tabbable (column, FALSE);
   pspp_sheet_view_column_set_clickable (column, TRUE);
   pspp_sheet_view_column_set_cell_data_func (
     column, renderer, render_row_number_cell, ds, NULL);
@@ -757,6 +758,7 @@ make_new_variable_column (PsppireDataSheet *data_sheet,
   width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
   pspp_sheet_view_column_set_min_width (column, 10);
   pspp_sheet_view_column_set_fixed_width (column, width);
+  pspp_sheet_view_column_set_tabbable (column, FALSE);
 
   g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
   g_signal_connect (column, "button-press-event",