Index: gtktreeview.c
===================================================================
--- gtktreeview.c	(revision 18610)
+++ gtktreeview.c	(working copy)
@@ -2433,6 +2433,30 @@ grab_focus_and_unset_draw_keyfocus (GtkT
   GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
 }
 
+static inline gboolean
+row_is_separator (GtkTreeView *tree_view,
+		  GtkTreeIter *iter,
+		  GtkTreePath *path)
+{
+  gboolean is_separator = FALSE;
+
+  if (tree_view->priv->row_separator_func)
+    {
+      GtkTreeIter tmpiter;
+
+      if (iter)
+	tmpiter = *iter;
+      else
+	gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, path);
+
+      is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
+							      &tmpiter,
+							      tree_view->priv->row_separator_data);
+    }
+
+  return is_separator;
+}
+
 static gboolean
 gtk_tree_view_button_press (GtkWidget      *widget,
 			    GdkEventButton *event)
@@ -2445,6 +2469,7 @@ gtk_tree_view_button_press (GtkWidget   
   GdkRectangle cell_area;
   gint vertical_separator;
   gint horizontal_separator;
+  gboolean path_is_selectable;
   gboolean rtl;
 
   g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
@@ -2524,6 +2549,15 @@ gtk_tree_view_button_press (GtkWidget   
 
       /* Get the path and the node */
       path = _gtk_tree_view_find_path (tree_view, tree, node);
+      path_is_selectable = !row_is_separator (tree_view, NULL, path);
+
+      if (!path_is_selectable)
+	{
+	  gtk_tree_path_free (path);
+	  grab_focus_and_unset_draw_keyfocus (tree_view);
+	  return TRUE;
+	}
+
       depth = gtk_tree_path_get_depth (path);
       background_area.y = y_offset + event->y;
       background_area.height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
@@ -3696,6 +3730,22 @@ gtk_tree_view_update_rubber_band_selecti
 
   do
     {
+      /* Small optimization by assuming insensitive nodes are never
+       * selected.
+       */
+      if (!GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
+        {
+	  GtkTreePath *path;
+	  gboolean selectable;
+
+	  path = _gtk_tree_view_find_path (tree_view, start_tree, start_node);
+	  selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection, start_node, path);
+	  gtk_tree_path_free (path);
+
+	  if (!selectable)
+	    goto node_not_selectable;
+	}
+
       if (select)
         {
 	  if (tree_view->priv->rubber_band_shift)
@@ -3730,6 +3780,7 @@ gtk_tree_view_update_rubber_band_selecti
 
       _gtk_tree_view_queue_draw_node (tree_view, start_tree, start_node, NULL);
 
+node_not_selectable:
       if (start_node == end_node)
 	break;
 
@@ -4274,12 +4325,7 @@ gtk_tree_view_bin_expose (GtkWidget     
       gboolean is_first = FALSE;
       gboolean is_last = FALSE;
       
-      if (tree_view->priv->row_separator_func)
-	{
-	  is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
-								  &iter,
-								  tree_view->priv->row_separator_data);
-	}
+      is_separator = row_is_separator (tree_view, &iter, NULL);
 
       max_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
 
@@ -5519,12 +5565,7 @@ validate_row (GtkTreeView *tree_view,
       ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
     return FALSE;
 
-  if (tree_view->priv->row_separator_func)
-    {
-      is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
-							      iter,
-							      tree_view->priv->row_separator_data);
-    }
+  is_separator = row_is_separator (tree_view, iter, NULL);
 
   gtk_widget_style_get (GTK_WIDGET (tree_view),
 			"focus-padding", &focus_pad,
@@ -7746,6 +7787,52 @@ gtk_tree_view_header_focus (GtkTreeView 
   return (focus_child != NULL);
 }
 
+/* This function returns in 'path' the first focusable path, if the given path
+ * is already focusable, it's the returned one.
+ */
+static gboolean
+search_first_focusable_path (GtkTreeView  *tree_view,
+			     GtkTreePath **path,
+			     gboolean      search_forward,
+			     GtkRBTree   **new_tree,
+			     GtkRBNode   **new_node)
+{
+  GtkRBTree *tree = NULL;
+  GtkRBNode *node = NULL;
+
+  if (!path || !*path)
+    return FALSE;
+
+  _gtk_tree_view_find_node (tree_view, *path, &tree, &node);
+
+  if (!tree || !node)
+    return FALSE;
+
+  while (node && row_is_separator (tree_view, NULL, *path))
+    {
+      if (search_forward)
+	_gtk_rbtree_next_full (tree, node, &tree, &node);
+      else
+	_gtk_rbtree_prev_full (tree, node, &tree, &node);
+
+      if (*path)
+	gtk_tree_path_free (*path);
+
+      if (node)
+	*path = _gtk_tree_view_find_path (tree_view, tree, node);
+      else
+	*path = NULL;
+    }
+
+  if (new_tree)
+    *new_tree = tree;
+
+  if (new_node)
+    *new_node = node;
+
+  return (*path != NULL);
+}
+
 static gint
 gtk_tree_view_focus (GtkWidget        *widget,
 		     GtkDirectionType  direction)
@@ -9496,21 +9583,22 @@ gtk_tree_view_focus_to_cursor (GtkTreeVi
       (! GTK_WIDGET_REALIZED (tree_view)))
     return;
 
-  GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
-
   cursor_path = NULL;
   if (tree_view->priv->cursor)
     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
 
   if (cursor_path == NULL)
     {
-      /* Consult the selection before defaulting to the first element */
+      /* Consult the selection before defaulting to the
+       * first focusable element
+       */
+      GList *selected_rows;
+      GtkTreeModel *model;
       GtkTreeSelection *selection;
-      GtkTreeModel     *model;
-      GList            *selected_rows;
 
       selection = gtk_tree_view_get_selection (tree_view);
       selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
       if (selected_rows)
 	{
           cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
@@ -9518,28 +9606,41 @@ gtk_tree_view_focus_to_cursor (GtkTreeVi
 	  g_list_free (selected_rows);
         }
       else
-	cursor_path = gtk_tree_path_new_first ();
+	{
+	  cursor_path = gtk_tree_path_new_first ();
+	  search_first_focusable_path (tree_view, &cursor_path,
+				       TRUE, NULL, NULL);
+	}
 
       gtk_tree_row_reference_free (tree_view->priv->cursor);
       tree_view->priv->cursor = NULL;
 
-      if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE)
-	gtk_tree_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
-      else
-	gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
+      if (cursor_path)
+	{
+	  if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE)
+	    gtk_tree_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
+	  else
+	    gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
+	}
     }
-  gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
-  gtk_tree_path_free (cursor_path);
 
-  if (tree_view->priv->focus_column == NULL)
+  if (cursor_path)
     {
-      GList *list;
-      for (list = tree_view->priv->columns; list; list = list->next)
+      GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+
+      gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
+      gtk_tree_path_free (cursor_path);
+
+      if (tree_view->priv->focus_column == NULL)
 	{
-	  if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
+	  GList *list;
+	  for (list = tree_view->priv->columns; list; list = list->next)
 	    {
-	      tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
-	      break;
+	      if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
+		{
+		  tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
+		  break;
+		}
 	    }
 	}
     }
@@ -9594,6 +9695,20 @@ gtk_tree_view_move_cursor_up_down (GtkTr
 			       &new_cursor_tree, &new_cursor_node);
     }
 
+  if (new_cursor_node)
+    {
+      cursor_path = _gtk_tree_view_find_path (tree_view,
+					      new_cursor_tree, new_cursor_node);
+
+      search_first_focusable_path (tree_view, &cursor_path,
+				   (count != -1),
+				   &new_cursor_tree,
+				   &new_cursor_node);
+
+      if (cursor_path)
+	gtk_tree_path_free (cursor_path);
+    }
+
   /*
    * If the list has only one item and multi-selection is set then select
    * the row (if not yet selected).
@@ -9665,6 +9780,8 @@ gtk_tree_view_move_cursor_page_up_down (
   GtkRBNode *cursor_node = NULL;
   GtkTreePath *old_cursor_path = NULL;
   GtkTreePath *cursor_path = NULL;
+  GtkRBTree *start_cursor_tree = NULL;
+  GtkRBNode *start_cursor_node = NULL;
   gint y;
   gint window_y;
   gint vertical_separator;
@@ -9712,7 +9829,33 @@ gtk_tree_view_move_cursor_page_up_down (
 
   y -= tree_view->priv->cursor_offset;
   cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
-  g_return_if_fail (cursor_path != NULL);
+
+  start_cursor_tree = cursor_tree;
+  start_cursor_node = cursor_node;
+
+  if (! search_first_focusable_path (tree_view, &cursor_path,
+				     (count != -1),
+				     &cursor_tree, &cursor_node))
+    {
+      /* It looks like we reached the end of the view without finding
+       * a focusable row.  We will step backwards to find the last
+       * focusable row.
+       */
+      cursor_tree = start_cursor_tree;
+      cursor_node = start_cursor_node;
+      cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+
+      search_first_focusable_path (tree_view, &cursor_path,
+				   (count == -1),
+				   &cursor_tree, &cursor_node);
+    }
+
+  if (!cursor_path)
+    goto cleanup;
+
+  /* update y */
+  y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
+
   gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
 
   y -= window_y;
@@ -9722,6 +9865,7 @@ gtk_tree_view_move_cursor_page_up_down (
   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
     gtk_widget_error_bell (GTK_WIDGET (tree_view));
 
+cleanup:
   gtk_tree_path_free (old_cursor_path);
   gtk_tree_path_free (cursor_path);
 }
@@ -9848,6 +9992,11 @@ gtk_tree_view_move_cursor_start_end (Gtk
     {
       while (cursor_node && cursor_node->left != cursor_tree->nil)
 	cursor_node = cursor_node->left;
+
+      /* Now go forward to find the first focusable row. */
+      path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+      search_first_focusable_path (tree_view, &path,
+				   TRUE, &cursor_tree, &cursor_node);
     }
   else
     {
@@ -9862,9 +10011,15 @@ gtk_tree_view_move_cursor_start_end (Gtk
 	  cursor_node = cursor_tree->root;
 	}
       while (1);
+
+      /* Now go backwards to find last focusable row. */
+      path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+      search_first_focusable_path (tree_view, &path,
+				   FALSE, &cursor_tree, &cursor_node);
     }
 
-  path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+  if (!path)
+    goto cleanup;
 
   if (gtk_tree_path_compare (old_path, path))
     {
@@ -9875,6 +10030,7 @@ gtk_tree_view_move_cursor_start_end (Gtk
       gtk_widget_error_bell (GTK_WIDGET (tree_view));
     }
 
+cleanup:
   gtk_tree_path_free (old_path);
   gtk_tree_path_free (path);
 }
@@ -12352,11 +12508,18 @@ gtk_tree_view_real_set_cursor (GtkTreeVi
     }
 
   gtk_tree_row_reference_free (tree_view->priv->cursor);
+  tree_view->priv->cursor = NULL;
+
+  /* One cannot set the cursor on a separator. */
+  if (!row_is_separator (tree_view, NULL, path))
+    {
+      tree_view->priv->cursor =
+	gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
+					  tree_view->priv->model,
+					  path);
+      _gtk_tree_view_find_node (tree_view, path, &tree, &node);
+    }
 
-  tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
-							      tree_view->priv->model,
-							      path);
-  _gtk_tree_view_find_node (tree_view, path, &tree, &node);
   if (tree != NULL)
     {
       GtkRBTree *new_tree = NULL;
@@ -13550,12 +13713,7 @@ gtk_tree_view_create_row_drag_icon (GtkT
                                 path))
     return NULL;
   
-  if (tree_view->priv->row_separator_func)
-    {
-      is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
-							      &iter,
-							      tree_view->priv->row_separator_data);
-    }
+  is_separator = row_is_separator (tree_view, &iter, NULL);
 
   cell_offset = x;
 
