Index: gtktreeview.c
===================================================================
--- gtktreeview.c	(revision 17911)
+++ gtktreeview.c	(working copy)
@@ -2111,7 +2111,7 @@ gtk_tree_view_size_allocate_columns (Gtk
   gint number_of_expand_columns = 0;
   gboolean column_changed = FALSE;
   gboolean rtl;
-  
+
   tree_view = GTK_TREE_VIEW (widget);
 
   for (last_column = g_list_last (tree_view->priv->columns);
@@ -2382,6 +2382,7 @@ gtk_tree_view_button_press (GtkWidget   
   GdkRectangle cell_area;
   gint vertical_separator;
   gint horizontal_separator;
+  gboolean node_is_selectable;
   gboolean rtl;
 
   g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
@@ -2587,10 +2588,12 @@ gtk_tree_view_button_press (GtkWidget   
       /* select */
       node_selected = GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED);
       pre_val = tree_view->priv->vadjustment->value;
+      node_is_selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
+								  node, path);
 
       /* we only handle selection modifications on the first button press
        */
-      if (event->type == GDK_BUTTON_PRESS)
+      if (event->type == GDK_BUTTON_PRESS && node_is_selectable)
         {
           if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
             tree_view->priv->ctrl_pressed = TRUE;
@@ -2636,9 +2639,9 @@ gtk_tree_view_button_press (GtkWidget   
 	  !tree_view->priv->in_grab &&
 	  tree_view->priv->pressed_button < 0)
         {
-          tree_view->priv->pressed_button = event->button;
-          tree_view->priv->press_start_x = event->x;
-          tree_view->priv->press_start_y = event->y;
+	  tree_view->priv->pressed_button = (node_is_selectable) ? event->button : -1;
+	  tree_view->priv->press_start_x = event->x;
+	  tree_view->priv->press_start_y = event->y;
 
 	  if (tree_view->priv->rubber_banding_enable
 	      && !node_selected
@@ -3633,6 +3636,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)
@@ -3667,6 +3686,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;
 
@@ -4665,7 +4685,9 @@ gtk_tree_view_bin_expose (GtkWidget     
       /* draw the big row-spanning focus rectangle, if needed */
       if (!has_special_cell && node == cursor &&
 	  GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS) &&
-	  GTK_WIDGET_HAS_FOCUS (widget))
+	  GTK_WIDGET_HAS_FOCUS (widget) &&
+	  _gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
+						 node, cursor_path))
         {
 	  gint tmp_y, tmp_height;
 	  gint width;
@@ -7679,6 +7701,74 @@ gtk_tree_view_header_focus (GtkTreeView 
   return (focus_child != NULL);
 }
 
+/* This function returns in 'path' the first
+ * selectable path, if the given path is
+ * already selectable, it's the returned one.
+ */
+static gboolean
+search_first_selectable_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 &&
+         !_gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
+						 node, *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 gboolean
+gtk_tree_view_content_focus (GtkTreeView *tree_view)
+{
+  GtkTreePath *path;
+  gboolean retval = FALSE;
+
+  /* Find a selectable node */
+  path = gtk_tree_path_new_first ();
+  if (search_first_selectable_path (tree_view, &path,
+				    TRUE, NULL, NULL))
+    {
+      gtk_tree_path_free (path);
+      gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+      retval = TRUE;
+    }
+
+  return retval;
+}
+
 static gint
 gtk_tree_view_focus (GtkWidget        *widget,
 		     GtkDirectionType  direction)
@@ -7713,8 +7803,7 @@ gtk_tree_view_focus (GtkWidget        *w
 	  return FALSE;
 	case GTK_DIR_TAB_FORWARD:
 	case GTK_DIR_DOWN:
-	  gtk_widget_grab_focus (widget);
-	  return TRUE;
+	  return gtk_tree_view_content_focus (tree_view);
 	default:
 	  g_assert_not_reached ();
 	  return FALSE;
@@ -7723,11 +7812,8 @@ gtk_tree_view_focus (GtkWidget        *w
 
   /* Case 2. We don't have focus at all. */
   if (!GTK_WIDGET_HAS_FOCUS (container))
-    {
-      if (!gtk_tree_view_header_focus (tree_view, direction, FALSE))
-	gtk_widget_grab_focus (widget);
-      return TRUE;
-    }
+    return (gtk_tree_view_header_focus (tree_view, direction, FALSE) ||
+	    gtk_tree_view_content_focus (tree_view));
 
   /* Case 3. We have focus already. */
   if (direction == GTK_DIR_TAB_BACKWARD)
@@ -9421,20 +9507,20 @@ gtk_tree_view_focus_to_cursor (GtkTreeVi
 
 {
   GtkTreePath *cursor_path;
+  GtkRBTree *tree;
+  GtkRBNode *node;
 
   if ((tree_view->priv->tree == NULL) ||
       (! 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 selectable element */
       GtkTreeSelection *selection;
       GtkTreeModel     *model;
       GList            *selected_rows;
@@ -9448,7 +9534,11 @@ 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_selectable_path (tree_view, &cursor_path,
+					TRUE, NULL, NULL);
+	}
 
       gtk_tree_row_reference_free (tree_view->priv->cursor);
       tree_view->priv->cursor = NULL;
@@ -9458,18 +9548,28 @@ gtk_tree_view_focus_to_cursor (GtkTreeVi
       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)
+  tree = tree_view->priv->tree;
+  node = tree->root;
+
+  if (_gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
+					     node, 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;
+		}
 	    }
 	}
     }
@@ -9524,6 +9624,19 @@ 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_selectable_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.
@@ -9594,6 +9707,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;
@@ -9641,18 +9756,46 @@ 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_selectable_path (tree_view, &cursor_path,
+				      (count != -1),
+				      &cursor_tree, &cursor_node))
+    {
+      /* It looks like we reached the end of the view without finding
+       * a sensitive row.  We will step backwards to find the last
+       * sensitive 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_selectable_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;
   gtk_tree_view_scroll_to_point (tree_view, -1, y);
   _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
 
   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
     gtk_widget_error_bell (GTK_WIDGET (tree_view));
 
+ cleanup:
+  if (cursor_path)
+    gtk_tree_path_free (cursor_path);
+
   gtk_tree_path_free (old_cursor_path);
-  gtk_tree_path_free (cursor_path);
 }
 
 static void
@@ -9777,6 +9920,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 selectable row */
+      path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+      search_first_selectable_path (tree_view, &path,
+				    TRUE, &cursor_tree, &cursor_node);
     }
   else
     {
@@ -9791,9 +9939,15 @@ gtk_tree_view_move_cursor_start_end (Gtk
 	  cursor_node = cursor_tree->root;
 	}
       while (1);
+
+      /* now go backwards to find last selectable row */
+      path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+      search_first_selectable_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))
     {
@@ -9804,8 +9958,11 @@ gtk_tree_view_move_cursor_start_end (Gtk
       gtk_widget_error_bell (GTK_WIDGET (tree_view));
     }
 
+ cleanup:
+  if (path)
+    gtk_tree_path_free (path);
+
   gtk_tree_path_free (old_path);
-  gtk_tree_path_free (path);
 }
 
 static gboolean
@@ -13729,14 +13886,13 @@ gtk_tree_view_search_dialog_hide (GtkWid
       g_source_remove (tree_view->priv->typeselect_flush_timeout);
       tree_view->priv->typeselect_flush_timeout = 0;
     }
-	
+
   if (GTK_WIDGET_VISIBLE (search_dialog))
     {
       /* send focus-in event */
       send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
       gtk_widget_hide (search_dialog);
       gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
-      send_focus_change (GTK_WIDGET (tree_view), TRUE);
     }
 }
 
