Fix compiler warning
[pspp] / src / ui / gui / pspp-sheet-selection.c
index 60feeeff335d204a0541c8bcf4e9b685bf114bf4..13c69c0ed6ed59dbc05493cdf1b7c5299a0bbea4 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2011, 2012, 2013 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
 
 #include "ui/gui/pspp-sheet-selection.h"
 
+#include "libpspp/range-set.h"
+
 static void pspp_sheet_selection_finalize          (GObject               *object);
 static gint pspp_sheet_selection_real_select_all   (PsppSheetSelection      *selection);
 static gint pspp_sheet_selection_real_unselect_all (PsppSheetSelection      *selection);
 static gint pspp_sheet_selection_real_select_node  (PsppSheetSelection      *selection,
-                                                 GtkRBTree             *tree,
-                                                 GtkRBNode             *node,
+                                                 int node,
                                                  gboolean               select);
 
 enum
@@ -83,23 +84,12 @@ pspp_sheet_selection_class_init (PsppSheetSelectionClass *class)
 static void
 pspp_sheet_selection_init (PsppSheetSelection *selection)
 {
-  selection->type = GTK_SELECTION_SINGLE;
+  selection->type = PSPP_SHEET_SELECTION_SINGLE;
 }
 
 static void
 pspp_sheet_selection_finalize (GObject *object)
 {
-  PsppSheetSelection *selection = PSPP_SHEET_SELECTION (object);
-
-  if (selection->destroy)
-    {
-      GDestroyNotify d = selection->destroy;
-
-      selection->destroy = NULL;
-      d (selection->user_data);
-    }
-
-  /* chain parent_class' handler */
   G_OBJECT_CLASS (pspp_sheet_selection_parent_class)->finalize (object);
 }
 
@@ -168,37 +158,29 @@ _pspp_sheet_selection_set_tree_view (PsppSheetSelection *selection,
  * @type: The selection mode
  *
  * Sets the selection mode of the @selection.  If the previous type was
- * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
- * previously selected.
+ * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, then the
+ * anchor is kept selected, if it was previously selected.
  **/
 void
 pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
-                            GtkSelectionMode  type)
+                            PsppSheetSelectionMode  type)
 {
-  PsppSheetSelectionFunc tmp_func;
   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
 
   if (selection->type == type)
     return;
 
-  
-  if (type == GTK_SELECTION_NONE)
+  if (type == PSPP_SHEET_SELECTION_NONE)
     {
-      /* We do this so that we unconditionally unset all rows
-       */
-      tmp_func = selection->user_func;
-      selection->user_func = NULL;
       pspp_sheet_selection_unselect_all (selection);
-      selection->user_func = tmp_func;
 
       gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
       selection->tree_view->priv->anchor = NULL;
     }
-  else if (type == GTK_SELECTION_SINGLE ||
-          type == GTK_SELECTION_BROWSE)
+  else if (type == PSPP_SHEET_SELECTION_SINGLE ||
+          type == PSPP_SHEET_SELECTION_BROWSE)
     {
-      GtkRBTree *tree = NULL;
-      GtkRBNode *node = NULL;
+      int node = -1;
       gint selected = FALSE;
       GtkTreePath *anchor_path = NULL;
 
@@ -210,25 +192,20 @@ pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
             {
               _pspp_sheet_view_find_node (selection->tree_view,
                                         anchor_path,
-                                        &tree,
                                         &node);
 
-              if (node && PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
+              if (node >= 0 && pspp_sheet_view_node_is_selected (selection->tree_view, node))
                 selected = TRUE;
             }
        }
 
       /* We do this so that we unconditionally unset all rows
        */
-      tmp_func = selection->user_func;
-      selection->user_func = NULL;
       pspp_sheet_selection_unselect_all (selection);
-      selection->user_func = tmp_func;
 
-      if (node && selected)
+      if (node >= 0 && selected)
        _pspp_sheet_selection_internal_select_node (selection,
                                                  node,
-                                                 tree,
                                                  anchor_path,
                                                   0,
                                                  FALSE);
@@ -236,6 +213,8 @@ pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
        gtk_tree_path_free (anchor_path);
     }
 
+  /* XXX unselect all columns when switching to/from rectangular selection? */
+
   selection->type = type;
 }
 
@@ -248,82 +227,14 @@ pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
  *
  * Return value: the current selection mode
  **/
-GtkSelectionMode
+PsppSheetSelectionMode
 pspp_sheet_selection_get_mode (PsppSheetSelection *selection)
 {
-  g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), GTK_SELECTION_SINGLE);
+  g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), PSPP_SHEET_SELECTION_SINGLE);
 
   return selection->type;
 }
 
-/**
- * pspp_sheet_selection_set_select_function:
- * @selection: A #PsppSheetSelection.
- * @func: The selection function.
- * @data: The selection function's data.
- * @destroy: The destroy function for user data.  May be NULL.
- *
- * Sets the selection function.  If set, this function is called before any node
- * is selected or unselected, giving some control over which nodes are selected.
- * The select function should return %TRUE if the state of the node may be toggled,
- * and %FALSE if the state of the node should be left unchanged.
- **/
-void
-pspp_sheet_selection_set_select_function (PsppSheetSelection     *selection,
-                                       PsppSheetSelectionFunc  func,
-                                       gpointer              data,
-                                       GDestroyNotify        destroy)
-{
-  g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
-  g_return_if_fail (func != NULL);
-
-  if (selection->destroy)
-    {
-      GDestroyNotify d = selection->destroy;
-
-      selection->destroy = NULL;
-      d (selection->user_data);
-    }
-
-  selection->user_func = func;
-  selection->user_data = data;
-  selection->destroy = destroy;
-}
-
-/**
- * pspp_sheet_selection_get_select_function:
- * @selection: A #PsppSheetSelection.
- *
- * Returns the current selection function.
- *
- * Return value: The function.
- *
- * Since: 2.14
- **/
-PsppSheetSelectionFunc
-pspp_sheet_selection_get_select_function (PsppSheetSelection *selection)
-{
-  g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
-
-  return selection->user_func;
-}
-
-/**
- * pspp_sheet_selection_get_user_data:
- * @selection: A #PsppSheetSelection.
- *
- * Returns the user data for the selection function.
- *
- * Return value: The user data.
- **/
-gpointer
-pspp_sheet_selection_get_user_data (PsppSheetSelection *selection)
-{
-  g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
-
-  return selection->user_data;
-}
-
 /**
  * pspp_sheet_selection_get_tree_view:
  * @selection: A #PsppSheetSelection
@@ -347,10 +258,11 @@ pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection)
  * @iter: (allow-none): The #GtkTreeIter, or NULL.
  *
  * Sets @iter to the currently selected node if @selection is set to
- * #GTK_SELECTION_SINGLE or #GTK_SELECTION_BROWSE.  @iter may be NULL if you
- * just want to test if @selection has any selected nodes.  @model is filled
- * with the current model as a convenience.  This function will not work if you
- * use @selection is #GTK_SELECTION_MULTIPLE.
+ * #PSPP_SHEET_SELECTION_SINGLE or #PSPP_SHEET_SELECTION_BROWSE.  @iter may be
+ * NULL if you just want to test if @selection has any selected nodes.  @model
+ * is filled with the current model as a convenience.  This function will not
+ * work if @selection's mode is #PSPP_SHEET_SELECTION_MULTIPLE or
+ * #PSPP_SHEET_SELECTION_RECTANGLE.
  *
  * Return value: TRUE, if there is a selected node.
  **/
@@ -359,14 +271,14 @@ pspp_sheet_selection_get_selected (PsppSheetSelection  *selection,
                                 GtkTreeModel     **model,
                                 GtkTreeIter       *iter)
 {
-  GtkRBTree *tree;
-  GtkRBNode *node;
+  int node;
   GtkTreePath *anchor_path;
   gboolean retval;
-  gboolean found_node;
 
   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
-  g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE);
+  g_return_val_if_fail (selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
+                        selection->type != PSPP_SHEET_SELECTION_RECTANGLE,
+                        FALSE);
   g_return_val_if_fail (selection->tree_view != NULL, FALSE);
 
   /* Clear the iter */
@@ -386,12 +298,11 @@ pspp_sheet_selection_get_selected (PsppSheetSelection  *selection,
 
   retval = FALSE;
 
-  found_node = !_pspp_sheet_view_find_node (selection->tree_view,
-                                          anchor_path,
-                                          &tree,
-                                          &node);
+  _pspp_sheet_view_find_node (selection->tree_view,
+                              anchor_path,
+                              &node);
 
-  if (found_node && PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
+  if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
     {
       /* we only want to return the anchor if it exists in the rbtree and
        * is selected.
@@ -439,10 +350,9 @@ GList *
 pspp_sheet_selection_get_selected_rows (PsppSheetSelection   *selection,
                                       GtkTreeModel      **model)
 {
+  const struct range_tower_node *node;
+  unsigned long int start;
   GList *list = NULL;
-  GtkRBTree *tree = NULL;
-  GtkRBNode *node = NULL;
-  GtkTreePath *path;
 
   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
   g_return_val_if_fail (selection->tree_view != NULL, NULL);
@@ -450,13 +360,13 @@ pspp_sheet_selection_get_selected_rows (PsppSheetSelection   *selection,
   if (model)
     *model = selection->tree_view->priv->model;
 
-  if (selection->tree_view->priv->tree == NULL ||
-      selection->tree_view->priv->tree->root == NULL)
+  if (selection->tree_view->priv->row_count == 0)
     return NULL;
 
-  if (selection->type == GTK_SELECTION_NONE)
+  if (selection->type == PSPP_SHEET_SELECTION_NONE)
     return NULL;
-  else if (selection->type != GTK_SELECTION_MULTIPLE)
+  else if (selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
+           selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
     {
       GtkTreeIter iter;
 
@@ -473,82 +383,18 @@ pspp_sheet_selection_get_selected_rows (PsppSheetSelection   *selection,
       return NULL;
     }
 
-  tree = selection->tree_view->priv->tree;
-  node = selection->tree_view->priv->tree->root;
-
-  while (node->left != tree->nil)
-    node = node->left;
-  path = gtk_tree_path_new_first ();
-
-  do
+  RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
     {
-      if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
-       list = g_list_prepend (list, gtk_tree_path_copy (path));
-
-      if (node->children)
-        {
-         tree = node->children;
-         node = tree->root;
-
-         while (node->left != tree->nil)
-           node = node->left;
-
-         gtk_tree_path_append_index (path, 0);
-       }
-      else
-        {
-         gboolean done = FALSE;
-
-         do
-           {
-             node = _pspp_rbtree_next (tree, node);
-             if (node != NULL)
-               {
-                 done = TRUE;
-                 gtk_tree_path_next (path);
-               }
-             else
-               {
-                 node = tree->parent_node;
-                 tree = tree->parent_tree;
-
-                 if (!tree)
-                   {
-                     gtk_tree_path_free (path);
+      unsigned long int width = range_tower_node_get_width (node);
+      unsigned long int index;
 
-                     goto done; 
-                   }
-
-                 gtk_tree_path_up (path);
-               }
-           }
-         while (!done);
-       }
+      for (index = start; index < start + width; index++)
+        list = g_list_prepend (list, gtk_tree_path_new_from_indices (index, -1));
     }
-  while (TRUE);
-
-  gtk_tree_path_free (path);
 
- done:
   return g_list_reverse (list);
 }
 
-static void
-pspp_sheet_selection_count_selected_rows_helper (GtkRBTree *tree,
-                                              GtkRBNode *node,
-                                              gpointer   data)
-{
-  gint *count = (gint *)data;
-
-  if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
-    (*count)++;
-
-  if (node->children)
-    _pspp_rbtree_traverse (node->children, node->children->root,
-                         G_PRE_ORDER,
-                         pspp_sheet_selection_count_selected_rows_helper, data);
-}
-
 /**
  * pspp_sheet_selection_count_selected_rows:
  * @selection: A #PsppSheetSelection.
@@ -562,17 +408,18 @@ pspp_sheet_selection_count_selected_rows_helper (GtkRBTree *tree,
 gint
 pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection)
 {
+  const struct range_tower_node *node;
+  unsigned long int start;
   gint count = 0;
 
   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), 0);
   g_return_val_if_fail (selection->tree_view != NULL, 0);
 
-  if (selection->tree_view->priv->tree == NULL ||
-      selection->tree_view->priv->tree->root == NULL)
+  if (selection->tree_view->priv->row_count == 0)
     return 0;
 
-  if (selection->type == GTK_SELECTION_SINGLE ||
-      selection->type == GTK_SELECTION_BROWSE)
+  if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
+      selection->type == PSPP_SHEET_SELECTION_BROWSE)
     {
       if (pspp_sheet_selection_get_selected (selection, NULL, NULL))
        return 1;
@@ -580,11 +427,9 @@ pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection)
        return 0;
     }
 
-  _pspp_rbtree_traverse (selection->tree_view->priv->tree,
-                        selection->tree_view->priv->tree->root,
-                       G_PRE_ORDER,
-                       pspp_sheet_selection_count_selected_rows_helper,
-                       &count);
+  count = 0;
+  RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
+    count += range_tower_node_get_width (node);
 
   return count;
 }
@@ -613,9 +458,9 @@ pspp_sheet_selection_selected_foreach (PsppSheetSelection            *selection,
                                     PsppSheetSelectionForeachFunc  func,
                                     gpointer                     data)
 {
+  const struct range_tower_node *node;
+  unsigned long int start;
   GtkTreePath *path;
-  GtkRBTree *tree;
-  GtkRBNode *node;
   GtkTreeIter iter;
   GtkTreeModel *model;
 
@@ -626,12 +471,11 @@ pspp_sheet_selection_selected_foreach (PsppSheetSelection            *selection,
   g_return_if_fail (selection->tree_view != NULL);
 
   if (func == NULL ||
-      selection->tree_view->priv->tree == NULL ||
-      selection->tree_view->priv->tree->root == NULL)
+      selection->tree_view->priv->row_count == 0)
     return;
 
-  if (selection->type == GTK_SELECTION_SINGLE ||
-      selection->type == GTK_SELECTION_BROWSE)
+  if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
+      selection->type == PSPP_SHEET_SELECTION_BROWSE)
     {
       if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
        {
@@ -643,12 +487,6 @@ pspp_sheet_selection_selected_foreach (PsppSheetSelection            *selection,
       return;
     }
 
-  tree = selection->tree_view->priv->tree;
-  node = selection->tree_view->priv->tree->root;
-  
-  while (node->left != tree->nil)
-    node = node->left;
-
   model = selection->tree_view->priv->model;
   g_object_ref (model);
 
@@ -666,66 +504,22 @@ pspp_sheet_selection_selected_foreach (PsppSheetSelection            *selection,
                                         G_CALLBACK (model_changed), 
                                         &stop);
 
-  /* find the node internally */
-  path = gtk_tree_path_new_first ();
-
-  do
+  RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
     {
-      if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
+      unsigned long int width = range_tower_node_get_width (node);
+      unsigned long int index;
+
+      for (index = start; index < start + width; index++)
         {
+          GtkTreePath *path;
+          GtkTreeIter iter;
+
+          path = gtk_tree_path_new_from_indices (index, -1);
           gtk_tree_model_get_iter (model, &iter, path);
          (* func) (model, path, &iter, data);
+          gtk_tree_path_free (path);
         }
-
-      if (stop)
-       goto out;
-
-      if (node->children)
-       {
-         tree = node->children;
-         node = tree->root;
-
-         while (node->left != tree->nil)
-           node = node->left;
-
-         gtk_tree_path_append_index (path, 0);
-       }
-      else
-       {
-         gboolean done = FALSE;
-
-         do
-           {
-             node = _pspp_rbtree_next (tree, node);
-             if (node != NULL)
-               {
-                 done = TRUE;
-                 gtk_tree_path_next (path);
-               }
-             else
-               {
-                 node = tree->parent_node;
-                 tree = tree->parent_tree;
-
-                 if (tree == NULL)
-                   {
-                     /* we've run out of tree */
-                     /* We're done with this function */
-
-                     goto out;
-                   }
-
-                 gtk_tree_path_up (path);
-               }
-           }
-         while (!done);
-       }
     }
-  while (TRUE);
-
-out:
-  if (path)
-    gtk_tree_path_free (path);
 
   g_signal_handler_disconnect (model, inserted_id);
   g_signal_handler_disconnect (model, deleted_id);
@@ -752,30 +546,26 @@ void
 pspp_sheet_selection_select_path (PsppSheetSelection *selection,
                                GtkTreePath      *path)
 {
-  GtkRBNode *node;
-  GtkRBTree *tree;
-  gboolean ret;
-  GtkTreeSelectMode mode = 0;
+  int node;
+  PsppSheetSelectMode mode = 0;
 
   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
   g_return_if_fail (selection->tree_view != NULL);
   g_return_if_fail (path != NULL);
 
-  ret = _pspp_sheet_view_find_node (selection->tree_view,
-                                 path,
-                                 &tree,
-                                 &node);
+   _pspp_sheet_view_find_node (selection->tree_view,
+                               path,
+                               &node);
 
-  if (node == NULL || PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) ||
-      ret == TRUE)
+  if (node < 0 || pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
     return;
 
-  if (selection->type == GTK_SELECTION_MULTIPLE)
-    mode = GTK_TREE_SELECT_MODE_TOGGLE;
+  if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
+      selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
+    mode = PSPP_SHEET_SELECT_MODE_TOGGLE;
 
   _pspp_sheet_selection_internal_select_node (selection,
                                            node,
-                                           tree,
                                            path,
                                             mode,
                                            FALSE);
@@ -792,28 +582,23 @@ void
 pspp_sheet_selection_unselect_path (PsppSheetSelection *selection,
                                  GtkTreePath      *path)
 {
-  GtkRBNode *node;
-  GtkRBTree *tree;
-  gboolean ret;
+  int node;
 
   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
   g_return_if_fail (selection->tree_view != NULL);
   g_return_if_fail (path != NULL);
 
-  ret = _pspp_sheet_view_find_node (selection->tree_view,
-                                 path,
-                                 &tree,
-                                 &node);
+  _pspp_sheet_view_find_node (selection->tree_view,
+                              path,
+                              &node);
 
-  if (node == NULL || !PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) ||
-      ret == TRUE)
+  if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
     return;
 
   _pspp_sheet_selection_internal_select_node (selection,
                                            node,
-                                           tree,
                                            path,
-                                            GTK_TREE_SELECT_MODE_TOGGLE,
+                                            PSPP_SHEET_SELECT_MODE_TOGGLE,
                                            TRUE);
 }
 
@@ -888,9 +673,7 @@ gboolean
 pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection,
                                     GtkTreePath      *path)
 {
-  GtkRBNode *node;
-  GtkRBTree *tree;
-  gboolean ret;
+  int node;
 
   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
   g_return_val_if_fail (path != NULL, FALSE);
@@ -899,13 +682,11 @@ pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection,
   if (selection->tree_view->priv->model == NULL)
     return FALSE;
 
-  ret = _pspp_sheet_view_find_node (selection->tree_view,
+  _pspp_sheet_view_find_node (selection->tree_view,
                                  path,
-                                 &tree,
                                  &node);
 
-  if ((node == NULL) || !PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) ||
-      ret == TRUE)
+  if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
     return FALSE;
 
   return TRUE;
@@ -943,68 +724,39 @@ pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection,
 }
 
 
-/* Wish I was in python, right now... */
-struct _TempTuple {
-  PsppSheetSelection *selection;
-  gint dirty;
-};
-
-static void
-select_all_helper (GtkRBTree  *tree,
-                  GtkRBNode  *node,
-                  gpointer    data)
-{
-  struct _TempTuple *tuple = data;
-
-  if (node->children)
-    _pspp_rbtree_traverse (node->children,
-                         node->children->root,
-                         G_PRE_ORDER,
-                         select_all_helper,
-                         data);
-  if (!PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
-    {
-      tuple->dirty = pspp_sheet_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
-    }
-}
-
-
 /* We have a real_{un,}select_all function that doesn't emit the signal, so we
  * can use it in other places without fear of the signal being emitted.
  */
 static gint
 pspp_sheet_selection_real_select_all (PsppSheetSelection *selection)
 {
-  struct _TempTuple *tuple;
+  const struct range_tower_node *node;
+  int row_count = selection->tree_view->priv->row_count;
 
-  if (selection->tree_view->priv->tree == NULL)
+  if (row_count == 0)
     return FALSE;
 
-  /* Mark all nodes selected */
-  tuple = g_new (struct _TempTuple, 1);
-  tuple->selection = selection;
-  tuple->dirty = FALSE;
-
-  _pspp_rbtree_traverse (selection->tree_view->priv->tree,
-                       selection->tree_view->priv->tree->root,
-                       G_PRE_ORDER,
-                       select_all_helper,
-                       tuple);
-  if (tuple->dirty)
-    {
-      g_free (tuple);
-      return TRUE;
-    }
-  g_free (tuple);
-  return FALSE;
+  node = range_tower_first (selection->tree_view->priv->selected);
+  if (node
+      && range_tower_node_get_start (node) == 0
+      && range_tower_node_get_width (node) >= row_count)
+    return FALSE;
+
+  range_tower_set1 (selection->tree_view->priv->selected, 0, row_count);
+  pspp_sheet_selection_select_all_columns (selection);
+
+  /* XXX we could invalidate individual visible rows instead */
+  gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
+
+  return TRUE;
 }
 
 /**
  * pspp_sheet_selection_select_all:
  * @selection: A #PsppSheetSelection.
  *
- * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
- * mode.
+ * Selects all the nodes and column. @selection must be set to
+ * #PSPP_SHEET_SELECTION_MULTIPLE or  #PSPP_SHEET_SELECTION_RECTANGLE mode.
  **/
 void
 pspp_sheet_selection_select_all (PsppSheetSelection *selection)
@@ -1012,44 +764,23 @@ pspp_sheet_selection_select_all (PsppSheetSelection *selection)
   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
   g_return_if_fail (selection->tree_view != NULL);
 
-  if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
+  if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
     return;
 
-  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
+  g_return_if_fail (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
+                    selection->type == PSPP_SHEET_SELECTION_RECTANGLE);
 
   if (pspp_sheet_selection_real_select_all (selection))
     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
 }
 
-static void
-unselect_all_helper (GtkRBTree  *tree,
-                    GtkRBNode  *node,
-                    gpointer    data)
-{
-  struct _TempTuple *tuple = data;
-
-  if (node->children)
-    _pspp_rbtree_traverse (node->children,
-                         node->children->root,
-                         G_PRE_ORDER,
-                         unselect_all_helper,
-                         data);
-  if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
-    {
-      tuple->dirty = pspp_sheet_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
-    }
-}
-
 static gint
 pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
 {
-  struct _TempTuple *tuple;
-
-  if (selection->type == GTK_SELECTION_SINGLE ||
-      selection->type == GTK_SELECTION_BROWSE)
+  if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
+      selection->type == PSPP_SHEET_SELECTION_BROWSE)
     {
-      GtkRBTree *tree = NULL;
-      GtkRBNode *node = NULL;
+      int node = -1;
       GtkTreePath *anchor_path;
 
       if (selection->tree_view->priv->anchor == NULL)
@@ -1062,17 +793,16 @@ pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
 
       _pspp_sheet_view_find_node (selection->tree_view,
                                 anchor_path,
-                               &tree,
                                &node);
 
       gtk_tree_path_free (anchor_path);
 
-      if (tree == NULL)
+      if (node < 0)
         return FALSE;
 
-      if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
+      if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
        {
-         if (pspp_sheet_selection_real_select_node (selection, tree, node, FALSE))
+         if (pspp_sheet_selection_real_select_node (selection, node, FALSE))
            {
              gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
              selection->tree_view->priv->anchor = NULL;
@@ -1081,25 +811,17 @@ pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
        }
       return FALSE;
     }
+  else if (range_tower_is_empty (selection->tree_view->priv->selected))
+    return FALSE;
   else
     {
-      tuple = g_new (struct _TempTuple, 1);
-      tuple->selection = selection;
-      tuple->dirty = FALSE;
+      range_tower_set0 (selection->tree_view->priv->selected, 0, ULONG_MAX);
+      pspp_sheet_selection_unselect_all_columns (selection);
 
-      _pspp_rbtree_traverse (selection->tree_view->priv->tree,
-                            selection->tree_view->priv->tree->root,
-                            G_PRE_ORDER,
-                            unselect_all_helper,
-                            tuple);
+      /* XXX we could invalidate individual visible rows instead */
+      gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
 
-      if (tuple->dirty)
-        {
-          g_free (tuple);
-          return TRUE;
-        }
-      g_free (tuple);
-      return FALSE;
+      return TRUE;
     }
 }
 
@@ -1107,7 +829,7 @@ pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
  * pspp_sheet_selection_unselect_all:
  * @selection: A #PsppSheetSelection.
  *
- * Unselects all the nodes.
+ * Unselects all the nodes and columns.
  **/
 void
 pspp_sheet_selection_unselect_all (PsppSheetSelection *selection)
@@ -1115,7 +837,7 @@ pspp_sheet_selection_unselect_all (PsppSheetSelection *selection)
   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
   g_return_if_fail (selection->tree_view != NULL);
 
-  if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
+  if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
     return;
   
   if (pspp_sheet_selection_real_unselect_all (selection))
@@ -1134,8 +856,7 @@ pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
                                      GtkTreePath      *start_path,
                                      GtkTreePath      *end_path)
 {
-  GtkRBNode *start_node, *end_node;
-  GtkRBTree *start_tree, *end_tree;
+  int start_node, end_node;
   GtkTreePath *anchor_path = NULL;
   gboolean dirty = FALSE;
 
@@ -1144,38 +865,32 @@ pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
     case 1:
       _pspp_sheet_view_find_node (selection->tree_view,
                                end_path,
-                               &start_tree,
                                &start_node);
       _pspp_sheet_view_find_node (selection->tree_view,
                                start_path,
-                               &end_tree,
                                &end_node);
       anchor_path = start_path;
       break;
     case 0:
       _pspp_sheet_view_find_node (selection->tree_view,
                                start_path,
-                               &start_tree,
                                &start_node);
-      end_tree = start_tree;
       end_node = start_node;
       anchor_path = start_path;
       break;
     case -1:
       _pspp_sheet_view_find_node (selection->tree_view,
                                start_path,
-                               &start_tree,
                                &start_node);
       _pspp_sheet_view_find_node (selection->tree_view,
                                end_path,
-                               &end_tree,
                                &end_node);
       anchor_path = start_path;
       break;
     }
 
-  g_return_val_if_fail (start_node != NULL, FALSE);
-  g_return_val_if_fail (end_node != NULL, FALSE);
+  g_return_val_if_fail (start_node >= 0, FALSE);
+  g_return_val_if_fail (end_node >= 0, FALSE);
 
   if (anchor_path)
     {
@@ -1190,28 +905,18 @@ pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
 
   do
     {
-      dirty |= pspp_sheet_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
+      dirty |= pspp_sheet_selection_real_select_node (selection, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
 
       if (start_node == end_node)
        break;
 
-      if (start_node->children)
-       {
-         start_tree = start_node->children;
-         start_node = start_tree->root;
-         while (start_node->left != start_tree->nil)
-           start_node = start_node->left;
-       }
-      else
-       {
-         _pspp_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
-         if (start_tree == NULL)
-           {
-             /* we just ran out of tree.  That means someone passed in bogus values.
-              */
-             return dirty;
-           }
-       }
+      start_node = pspp_sheet_view_node_next (selection->tree_view, start_node);
+      if (start_node < 0)
+        {
+          /* we just ran out of tree.  That means someone passed in bogus values.
+           */
+          return dirty;
+        }
     }
   while (TRUE);
 
@@ -1225,7 +930,8 @@ pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
  * @end_path: The final node of the range.
  *
  * Selects a range of nodes, determined by @start_path and @end_path inclusive.
- * @selection must be set to #GTK_SELECTION_MULTIPLE mode. 
+ * @selection must be set to #PSPP_SHEET_SELECTION_MULTIPLE or
+ * #PSPP_SHEET_SELECTION_RECTANGLE mode.
  **/
 void
 pspp_sheet_selection_select_range (PsppSheetSelection *selection,
@@ -1234,7 +940,8 @@ pspp_sheet_selection_select_range (PsppSheetSelection *selection,
 {
   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
   g_return_if_fail (selection->tree_view != NULL);
-  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
+  g_return_if_fail (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
+                    selection->type == PSPP_SHEET_SELECTION_RECTANGLE);
   g_return_if_fail (selection->tree_view->priv->model != NULL);
 
   if (pspp_sheet_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
@@ -1265,35 +972,23 @@ pspp_sheet_selection_unselect_range (PsppSheetSelection *selection,
     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
 }
 
-gboolean
-_pspp_sheet_selection_row_is_selectable (PsppSheetSelection *selection,
-                                      GtkRBNode        *node,
-                                      GtkTreePath      *path)
+struct range_set *
+pspp_sheet_selection_get_range_set (PsppSheetSelection *selection)
 {
-  GtkTreeIter iter;
-  gboolean sensitive = FALSE;
-
-  if (!gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path))
-    sensitive = TRUE;
-
-  if (!sensitive && selection->tree_view->priv->row_separator_func)
-    {
-      /* never allow separators to be selected */
-      if ((* selection->tree_view->priv->row_separator_func) (selection->tree_view->priv->model,
-                                                             &iter,
-                                                             selection->tree_view->priv->row_separator_data))
-       return FALSE;
-    }
-
-  if (selection->user_func)
-    return (*selection->user_func) (selection, selection->tree_view->priv->model, path,
-                                   PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED),
-                                   selection->user_data);
-  else
-    return TRUE;
+  const struct range_tower_node *node;
+  unsigned long int start;
+  struct range_set *set;
+
+  g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection),
+                        range_set_create ());
+  g_return_val_if_fail (selection->tree_view != NULL, range_set_create ());
+
+  set = range_set_create ();
+  RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
+    range_set_set1 (set, start, range_tower_node_get_width (node));
+  return set;
 }
 
-
 /* Called internally by gtktreeview.c It handles actually selecting the tree.
  */
 
@@ -1304,35 +999,33 @@ _pspp_sheet_selection_row_is_selectable (PsppSheetSelection *selection,
  */
 void
 _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
-                                         GtkRBNode        *node,
-                                         GtkRBTree        *tree,
+                                         int               node,
                                          GtkTreePath      *path,
-                                          GtkTreeSelectMode mode,
+                                          PsppSheetSelectMode mode,
                                          gboolean          override_browse_mode)
 {
-  gint flags;
   gint dirty = FALSE;
   GtkTreePath *anchor_path = NULL;
 
-  if (selection->type == GTK_SELECTION_NONE)
+  if (selection->type == PSPP_SHEET_SELECTION_NONE)
     return;
 
   if (selection->tree_view->priv->anchor)
     anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
 
-  if (selection->type == GTK_SELECTION_SINGLE ||
-      selection->type == GTK_SELECTION_BROWSE)
+  if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
+      selection->type == PSPP_SHEET_SELECTION_BROWSE)
     {
       /* just unselect */
-      if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode)
+      if (selection->type == PSPP_SHEET_SELECTION_BROWSE && override_browse_mode)
         {
          dirty = pspp_sheet_selection_real_unselect_all (selection);
        }
       /* Did we try to select the same node again? */
-      else if (selection->type == GTK_SELECTION_SINGLE &&
+      else if (selection->type == PSPP_SHEET_SELECTION_SINGLE &&
               anchor_path && gtk_tree_path_compare (path, anchor_path) == 0)
        {
-         if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
+         if ((mode & PSPP_SHEET_SELECT_MODE_TOGGLE) == PSPP_SHEET_SELECT_MODE_TOGGLE)
            {
              dirty = pspp_sheet_selection_real_unselect_all (selection);
            }
@@ -1341,15 +1034,7 @@ _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
        {
          if (anchor_path)
            {
-             /* We only want to select the new node if we can unselect the old one,
-              * and we can select the new one. */
-             dirty = _pspp_sheet_selection_row_is_selectable (selection, node, path);
-
-             /* if dirty is FALSE, we weren't able to select the new one, otherwise, we try to
-              * unselect the new one
-              */
-             if (dirty)
-               dirty = pspp_sheet_selection_real_unselect_all (selection);
+              dirty = pspp_sheet_selection_real_unselect_all (selection);
 
              /* if dirty is TRUE at this point, we successfully unselected the
               * old one, and can then select the new one */
@@ -1361,7 +1046,7 @@ _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
                       selection->tree_view->priv->anchor = NULL;
                     }
 
-                 if (pspp_sheet_selection_real_select_node (selection, tree, node, TRUE))
+                 if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
                    {
                      selection->tree_view->priv->anchor =
                        gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
@@ -1370,7 +1055,7 @@ _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
            }
          else
            {
-             if (pspp_sheet_selection_real_select_node (selection, tree, node, TRUE))
+             if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
                {
                  dirty = TRUE;
                  if (selection->tree_view->priv->anchor)
@@ -1382,9 +1067,10 @@ _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
            }
        }
     }
-  else if (selection->type == GTK_SELECTION_MULTIPLE)
+  else if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
+           selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
     {
-      if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
+      if ((mode & PSPP_SHEET_SELECT_MODE_EXTEND) == PSPP_SHEET_SELECT_MODE_EXTEND
           && (anchor_path == NULL))
        {
          if (selection->tree_view->priv->anchor)
@@ -1392,29 +1078,29 @@ _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
 
          selection->tree_view->priv->anchor =
            gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
-         dirty = pspp_sheet_selection_real_select_node (selection, tree, node, TRUE);
+         dirty = pspp_sheet_selection_real_select_node (selection, node, TRUE);
        }
-      else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
+      else if ((mode & (PSPP_SHEET_SELECT_MODE_EXTEND | PSPP_SHEET_SELECT_MODE_TOGGLE)) == (PSPP_SHEET_SELECT_MODE_EXTEND | PSPP_SHEET_SELECT_MODE_TOGGLE))
        {
          pspp_sheet_selection_select_range (selection,
                                           anchor_path,
                                           path);
        }
-      else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
+      else if ((mode & PSPP_SHEET_SELECT_MODE_TOGGLE) == PSPP_SHEET_SELECT_MODE_TOGGLE)
        {
-         flags = node->flags;
+          bool selected = pspp_sheet_view_node_is_selected (selection->tree_view, node);
          if (selection->tree_view->priv->anchor)
            gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
 
          selection->tree_view->priv->anchor =
            gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
 
-         if ((flags & PSPP_RBNODE_IS_SELECTED) == PSPP_RBNODE_IS_SELECTED)
-           dirty |= pspp_sheet_selection_real_select_node (selection, tree, node, FALSE);
+         if (selected)
+           dirty |= pspp_sheet_selection_real_select_node (selection, node, FALSE);
          else
-           dirty |= pspp_sheet_selection_real_select_node (selection, tree, node, TRUE);
+           dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
        }
-      else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND)
+      else if ((mode & PSPP_SHEET_SELECT_MODE_EXTEND) == PSPP_SHEET_SELECT_MODE_EXTEND)
        {
          dirty = pspp_sheet_selection_real_unselect_all (selection);
          dirty |= pspp_sheet_selection_real_modify_range (selection,
@@ -1432,7 +1118,7 @@ _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
          selection->tree_view->priv->anchor =
            gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
 
-         dirty |= pspp_sheet_selection_real_select_node (selection, tree, node, TRUE);
+         dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
        }
     }
 
@@ -1455,30 +1141,163 @@ _pspp_sheet_selection_emit_changed (PsppSheetSelection *selection)
 
 static gint
 pspp_sheet_selection_real_select_node (PsppSheetSelection *selection,
-                                    GtkRBTree        *tree,
-                                    GtkRBNode        *node,
+                                    int               node,
                                     gboolean          select)
 {
-  gboolean toggle = FALSE;
-  GtkTreePath *path = NULL;
-
   select = !! select;
+  if (pspp_sheet_view_node_is_selected (selection->tree_view, node) != select)
+    {
+      if (select)
+        pspp_sheet_view_node_select (selection->tree_view, node);
+      else
+        pspp_sheet_view_node_unselect (selection->tree_view, node);
+
+      _pspp_sheet_view_queue_draw_node (selection->tree_view, node, NULL);
+      
+      return TRUE;
+    }
 
-  if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) != select)
+  return FALSE;
+}
+
+void
+pspp_sheet_selection_unselect_all_columns (PsppSheetSelection *selection)
+{
+  PsppSheetView *sheet_view = selection->tree_view;
+  gboolean changed;
+  GList *list;
+
+  changed = FALSE;
+  for (list = sheet_view->priv->columns; list; list = list->next)
     {
-      path = _pspp_sheet_view_find_path (selection->tree_view, tree, node);
-      toggle = _pspp_sheet_selection_row_is_selectable (selection, node, path);
-      gtk_tree_path_free (path);
+      PsppSheetViewColumn *column = list->data;
+      if (column->selected)
+        {
+          column->selected = FALSE;
+          changed = TRUE;
+        }
     }
+  if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
+    {
+      gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+      _pspp_sheet_selection_emit_changed (selection);
+    }
+}
+
+GList *
+pspp_sheet_selection_get_selected_columns (PsppSheetSelection *selection)
+{
+  PsppSheetView *sheet_view = selection->tree_view;
+  GList *selected_columns = NULL;
+  GList *iter;
+
+  g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
+  g_return_val_if_fail (selection->tree_view != NULL, NULL);
+
+  if (selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
+    return NULL;
 
-  if (toggle)
+  for (iter = sheet_view->priv->columns; iter; iter = iter->next)
     {
-      node->flags ^= PSPP_RBNODE_IS_SELECTED;
+      PsppSheetViewColumn *column = iter->data;
+      if (column->selected)
+        selected_columns = g_list_prepend (selected_columns, column);
+    }
+  return g_list_reverse (selected_columns);
+}
 
-      _pspp_sheet_view_queue_draw_node (selection->tree_view, tree, node, NULL);
-      
-      return TRUE;
+gint
+pspp_sheet_selection_count_selected_columns (PsppSheetSelection *selection)
+{
+  PsppSheetView *sheet_view = selection->tree_view;
+  GList *list;
+  gint n;
+
+  n = 0;
+  for (list = sheet_view->priv->columns; list; list = list->next)
+    {
+      PsppSheetViewColumn *column = list->data;
+      if (column->selected)
+        n++;
     }
+  return n;
+}
 
-  return FALSE;
+void
+pspp_sheet_selection_select_all_columns (PsppSheetSelection *selection)
+{
+  PsppSheetView *sheet_view = selection->tree_view;
+  gboolean changed;
+  GList *list;
+
+  changed = FALSE;
+  for (list = sheet_view->priv->columns; list; list = list->next)
+    {
+      PsppSheetViewColumn *column = list->data;
+      if (!column->selected && column->selectable)
+        {
+          /* XXX should use pspp_sheet_view_column_set_selected() here (and
+             elsewhere) but we want to call
+             _pspp_sheet_selection_emit_changed() only once for all the
+             columns. */
+          column->selected = TRUE;
+          changed = TRUE;
+        }
+    }
+  if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
+    {
+      _pspp_sheet_selection_emit_changed (selection);
+      gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+    }
+}
+
+void
+pspp_sheet_selection_select_column (PsppSheetSelection        *selection,
+                                    PsppSheetViewColumn       *column)
+{
+  if (!column->selected && column->selectable)
+    {
+      column->selected = TRUE;
+      if (selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
+        {
+          _pspp_sheet_selection_emit_changed (selection);
+          gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+        }
+    }
+}
+
+void
+pspp_sheet_selection_select_column_range  (PsppSheetSelection        *selection,
+                                           PsppSheetViewColumn       *first,
+                                           PsppSheetViewColumn       *last)
+{
+  PsppSheetView *sheet_view = selection->tree_view;
+  gboolean in_range;
+  gboolean changed;
+  GList *list;
+
+  in_range = FALSE;
+  changed = FALSE;
+  for (list = sheet_view->priv->columns; list; list = list->next)
+    {
+      PsppSheetViewColumn *column = list->data;
+      gboolean c0 = column == first;
+      gboolean c1 = column == last;
+
+      if (in_range || c0 || c1)
+        {
+          if (!column->selected && column->selectable)
+            {
+              column->selected = TRUE;
+              changed = TRUE;
+            }
+        }
+
+      in_range = in_range ^ c0 ^ c1;
+    }
+  if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
+    {
+      _pspp_sheet_selection_emit_changed (selection);
+      gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+    }
 }