[gtk+] Add an 'action area' to GtkNotebook



commit c7e4a1a012042ab711f04458df3ee517bdf6aa1c
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Nov 8 21:25:05 2009 -0500

    Add an 'action area' to GtkNotebook
    
    Add support for putting widgets in the tab area, before or after
    the tabs. This was requested a long time ago in bug 116650. The
    implementation is the work of Johannes Schmid.

 docs/reference/gtk/gtk-sections.txt |    4 +
 gtk/gtk.symbols                     |    2 +
 gtk/gtknotebook.c                   |  436 +++++++++++++++++++++++++++++++----
 gtk/gtknotebook.h                   |    6 +
 4 files changed, 403 insertions(+), 45 deletions(-)
---
diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt
index 4d8495a..d5d3e05 100644
--- a/docs/reference/gtk/gtk-sections.txt
+++ b/docs/reference/gtk/gtk-sections.txt
@@ -2732,8 +2732,12 @@ gtk_notebook_set_group_id
 gtk_notebook_get_group_id
 gtk_notebook_set_group
 gtk_notebook_get_group
+gtk_notebook_set_action_widget
+gtk_notebook_get_action_widget
 GtkNotebookWindowCreationFunc
 gtk_notebook_set_window_creation_hook
+gtk_notebook_set_action_widget
+gtk_notebook_get_action_widget
 <SUBSECTION Standard>
 GTK_NOTEBOOK
 GTK_IS_NOTEBOOK
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 4b39bde..c1b1e22 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -2674,6 +2674,8 @@ gtk_notebook_get_tab_reorderable
 gtk_notebook_set_tab_reorderable
 gtk_notebook_get_tab_detachable
 gtk_notebook_set_tab_detachable
+gtk_notebook_get_action_widget
+gtk_notebook_set_action_widget
 #endif
 #endif
 
diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
index 4d88d01..2fd8ab0 100644
--- a/gtk/gtknotebook.c
+++ b/gtk/gtknotebook.c
@@ -127,6 +127,12 @@ enum {
   CHILD_PROP_DETACHABLE
 };
 
+enum {
+  ACTION_WIDGET_START,
+  ACTION_WIDGET_END,
+  N_ACTION_WIDGETS
+};
+
 #define GTK_NOTEBOOK_PAGE(_glist_)         ((GtkNotebookPage *)((GList *)(_glist_))->data)
 
 /* some useful defines for calculating coords */
@@ -194,6 +200,8 @@ struct _GtkNotebookPrivate
 
   guint32 timestamp;
 
+  GtkWidget *action_widget[N_ACTION_WIDGETS];
+
   guint during_reorder : 1;
   guint during_detach  : 1;
   guint has_scrolled   : 1;
@@ -1627,11 +1635,14 @@ static gboolean
 gtk_notebook_get_event_window_position (GtkNotebook  *notebook,
 					GdkRectangle *rectangle)
 {
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   GtkWidget *widget = GTK_WIDGET (notebook);
   gint border_width = GTK_CONTAINER (notebook)->border_width;
   GtkNotebookPage *visible_page = NULL;
   GList *tmp_list;
   gint tab_pos = get_effective_tab_pos (notebook);
+  gboolean is_rtl;
+  gint i;
 
   for (tmp_list = notebook->children; tmp_list; tmp_list = tmp_list->next)
     {
@@ -1647,9 +1658,10 @@ gtk_notebook_get_event_window_position (GtkNotebook  *notebook,
     {
       if (rectangle)
 	{
+	  is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
 	  rectangle->x = widget->allocation.x + border_width;
 	  rectangle->y = widget->allocation.y + border_width;
-	  
+
 	  switch (tab_pos)
 	    {
 	    case GTK_POS_TOP:
@@ -1658,6 +1670,18 @@ gtk_notebook_get_event_window_position (GtkNotebook  *notebook,
 	      rectangle->height = visible_page->requisition.height;
 	      if (tab_pos == GTK_POS_BOTTOM)
 		rectangle->y += widget->allocation.height - 2 * border_width - rectangle->height;
+
+              for (i = 0; i < N_ACTION_WIDGETS; i++)
+                {
+                  if (priv->action_widget[i] &&
+                      GTK_WIDGET_VISIBLE (priv->action_widget[i]))
+                    {
+                      rectangle->width -= priv->action_widget[i]->allocation.width;
+                      if ((!is_rtl && i == ACTION_WIDGET_START) ||
+                          (is_rtl && i == ACTION_WIDGET_END))
+                        rectangle->x += priv->action_widget[i]->allocation.width;
+                    }
+                }
 	      break;
 	    case GTK_POS_LEFT:
 	    case GTK_POS_RIGHT:
@@ -1665,7 +1689,19 @@ gtk_notebook_get_event_window_position (GtkNotebook  *notebook,
 	      rectangle->height = widget->allocation.height - 2 * border_width;
 	      if (tab_pos == GTK_POS_RIGHT)
 		rectangle->x += widget->allocation.width - 2 * border_width - rectangle->width;
-	      break;
+
+              for (i = 0; i < N_ACTION_WIDGETS; i++)
+                {
+                  if (priv->action_widget[i] &&
+                      GTK_WIDGET_VISIBLE (priv->action_widget[i]))
+                    {
+                      rectangle->height -= priv->action_widget[i]->allocation.height;
+
+                      if (i == ACTION_WIDGET_START)
+                        rectangle->y += priv->action_widget[i]->allocation.height;
+                    }
+                }
+              break;
 	    }
 	}
 
@@ -1686,19 +1722,33 @@ gtk_notebook_get_event_window_position (GtkNotebook  *notebook,
 static void
 gtk_notebook_map (GtkWidget *widget)
 {
+  GtkNotebookPrivate *priv;
   GtkNotebook *notebook;
   GtkNotebookPage *page;
   GList *children;
+  gint i;
 
   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
 
   notebook = GTK_NOTEBOOK (widget);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
 
   if (notebook->cur_page && 
       GTK_WIDGET_VISIBLE (notebook->cur_page->child) &&
       !GTK_WIDGET_MAPPED (notebook->cur_page->child))
     gtk_widget_map (notebook->cur_page->child);
 
+  if (notebook->show_tabs)
+  {
+    for (i = 0; i < 2; i++)
+    {
+      if (priv->action_widget[i] &&
+          GTK_WIDGET_VISIBLE (priv->action_widget[i]) &&
+          !GTK_WIDGET_MAPPED (priv->action_widget[i]))
+        gtk_widget_map (priv->action_widget[i]);
+    }
+  }
+    
   if (notebook->scrollable)
     gtk_notebook_pages_allocate (notebook);
   else
@@ -1796,10 +1846,12 @@ static void
 gtk_notebook_size_request (GtkWidget      *widget,
 			   GtkRequisition *requisition)
 {
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
   GtkNotebookPage *page;
   GList *children;
   GtkRequisition child_requisition;
+  GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
   gboolean switch_page = FALSE;
   gint vis_pages;
   gint focus_width;
@@ -1861,6 +1913,7 @@ gtk_notebook_size_request (GtkWidget      *widget,
 	  gint tab_height = 0;
 	  gint tab_max = 0;
 	  gint padding;
+          gint i;
 	  
 	  for (children = notebook->children; children;
 	       children = children->next)
@@ -1908,6 +1961,16 @@ gtk_notebook_size_request (GtkWidget      *widget,
 
 	  if (vis_pages)
 	    {
+              for (i = 0; i < N_ACTION_WIDGETS; i++)
+                {
+                  if (priv->action_widget[i])
+                    {
+                      gtk_widget_size_request (priv->action_widget[i], &action_widget_requisition[i]);
+                      action_widget_requisition[i].width += widget->style->xthickness;
+                      action_widget_requisition[i].height += widget->style->ythickness;
+                    }
+                }
+
 	      switch (notebook->tab_pos)
 		{
 		case GTK_POS_TOP:
@@ -1919,6 +1982,9 @@ gtk_notebook_size_request (GtkWidget      *widget,
 		      widget->requisition.width < tab_width)
 		    tab_height = MAX (tab_height, scroll_arrow_hlength);
 
+                  tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
+                  tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
+
 		  padding = 2 * (tab_curvature + focus_width +
 				 notebook->tab_hborder) - tab_overlap;
 		  tab_max += padding;
@@ -1951,6 +2017,8 @@ gtk_notebook_size_request (GtkWidget      *widget,
                     widget->requisition.width = MAX (widget->requisition.width,
                                                      tab_width + tab_overlap);
 
+		  widget->requisition.width += action_widget_requisition[ACTION_WIDGET_START].width;
+		  widget->requisition.width += action_widget_requisition[ACTION_WIDGET_END].width;
 		  widget->requisition.height += tab_height;
 		  break;
 		case GTK_POS_LEFT:
@@ -1963,6 +2031,9 @@ gtk_notebook_size_request (GtkWidget      *widget,
 		    tab_width = MAX (tab_width,
                                      arrow_spacing + 2 * scroll_arrow_vlength);
 
+		  tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
+		  tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
+
 		  padding = 2 * (tab_curvature + focus_width +
 				 notebook->tab_vborder) - tab_overlap;
 		  tab_max += padding;
@@ -1975,7 +2046,7 @@ gtk_notebook_size_request (GtkWidget      *widget,
 		      if (!GTK_WIDGET_VISIBLE (page->child))
 			continue;
 
-		      page->requisition.width   = tab_width;
+		      page->requisition.width = tab_width;
 
 		      if (notebook->homogeneous)
 			page->requisition.height = tab_max;
@@ -1989,8 +2060,6 @@ gtk_notebook_size_request (GtkWidget      *widget,
 		      widget->requisition.height < tab_height)
 		    tab_height = tab_max + (2 * scroll_arrow_vlength + arrow_spacing);
 
-		  widget->requisition.width += tab_width;
-
                   if (notebook->homogeneous && !notebook->scrollable)
                     widget->requisition.height =
 		      MAX (widget->requisition.height,
@@ -2005,6 +2074,10 @@ gtk_notebook_size_request (GtkWidget      *widget,
 		  widget->requisition.height = MAX (widget->requisition.height,
 						    vis_pages * tab_max +
 						    tab_overlap);
+
+		  widget->requisition.height += action_widget_requisition[ACTION_WIDGET_START].height;
+		  widget->requisition.height += action_widget_requisition[ACTION_WIDGET_END].height;
+		  widget->requisition.width += tab_width;
 		  break;
 		}
 	    }
@@ -2061,9 +2134,14 @@ static void
 gtk_notebook_size_allocate (GtkWidget     *widget,
 			    GtkAllocation *allocation)
 {
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
   gint tab_pos = get_effective_tab_pos (notebook);
+  gboolean is_rtl;
+  gint focus_width;
 
+  gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
+  
   widget->allocation = *allocation;
   if (GTK_WIDGET_REALIZED (widget))
     {
@@ -2087,6 +2165,7 @@ gtk_notebook_size_allocate (GtkWidget     *widget,
       GtkNotebookPage *page;
       GtkAllocation child_allocation;
       GList *children;
+      gint i;
       
       child_allocation.x = widget->allocation.x + border_width;
       child_allocation.y = widget->allocation.y + border_width;
@@ -2121,6 +2200,55 @@ gtk_notebook_size_allocate (GtkWidget     *widget,
 			 notebook->cur_page->requisition.width);
 		  break;
 		}
+
+              for (i = 0; i < N_ACTION_WIDGETS; i++)
+                {
+                  GtkAllocation widget_allocation;
+
+                  if (!priv->action_widget[i])
+                    continue;
+
+		  widget_allocation.x = widget->allocation.x + border_width;
+		  widget_allocation.y = widget->allocation.y + border_width;
+		  is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+
+		  switch (tab_pos)
+		    {
+		    case GTK_POS_BOTTOM:
+		      widget_allocation.y +=
+			widget->allocation.height - 2 * border_width - notebook->cur_page->requisition.height;
+		      /* fall through */
+		    case GTK_POS_TOP:
+		      widget_allocation.width = priv->action_widget[i]->requisition.width;
+		      widget_allocation.height = notebook->cur_page->requisition.height - widget->style->ythickness;
+
+		      if ((i == ACTION_WIDGET_START && is_rtl) ||
+                          (i == ACTION_WIDGET_END && !is_rtl))
+			widget_allocation.x +=
+			  widget->allocation.width - 2 * border_width -
+			  priv->action_widget[i]->requisition.width;
+                      if (tab_pos == GTK_POS_TOP) /* no fall through */
+                          widget_allocation.y += 2 * focus_width;
+		      break;
+		    case GTK_POS_RIGHT:
+		      widget_allocation.x +=
+			widget->allocation.width - 2 * border_width - notebook->cur_page->requisition.width;
+		      /* fall through */
+		    case GTK_POS_LEFT:
+		      widget_allocation.height = priv->action_widget[i]->requisition.height;
+		      widget_allocation.width = notebook->cur_page->requisition.width - widget->style->xthickness;
+
+                      if (i == ACTION_WIDGET_END)
+                        widget_allocation.y +=
+                          widget->allocation.height - 2 * border_width -
+                          priv->action_widget[i]->requisition.height;
+                      if (tab_pos == GTK_POS_LEFT) /* no fall through */  
+                        widget_allocation.x += 2 * focus_width;
+		      break;
+		    }
+
+		  gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
+		}
 	    }
 	}
 
@@ -2144,6 +2272,7 @@ gtk_notebook_expose (GtkWidget      *widget,
 {
   GtkNotebook *notebook;
   GtkNotebookPrivate *priv;
+  gint i;
 
   notebook = GTK_NOTEBOOK (widget);
   priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
@@ -2201,6 +2330,16 @@ gtk_notebook_expose (GtkWidget      *widget,
 	gtk_container_propagate_expose (GTK_CONTAINER (notebook),
 					notebook->cur_page->child,
 					event);
+      if (notebook->show_tabs)
+      {
+        for (i = 0; i < N_ACTION_WIDGETS; i++)
+        {
+          if (priv->action_widget[i] &&
+              GTK_WIDGET_DRAWABLE (priv->action_widget[i]))
+            gtk_container_propagate_expose (GTK_CONTAINER (notebook),
+                                            priv->action_widget[i], event);
+        }
+      }
     }
 
   return FALSE;
@@ -2423,21 +2562,29 @@ static gboolean
 gtk_notebook_scroll (GtkWidget      *widget,
                      GdkEventScroll *event)
 {
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
-
-  GtkWidget* child;
-  GtkWidget* originator;
+  GtkWidget *child, *event_widget;
+  gint i;
 
   if (!notebook->cur_page)
     return FALSE;
 
   child = notebook->cur_page->child;
-  originator = gtk_get_event_widget ((GdkEvent *)event);
+  event_widget = gtk_get_event_widget ((GdkEvent *)event);
 
   /* ignore scroll events from the content of the page */
-  if (!originator || gtk_widget_is_ancestor (originator, child) || originator == child)
+  if (!event_widget || gtk_widget_is_ancestor (event_widget, child) || event_widget == child)
     return FALSE;
-  
+
+  /* nor from the action area */
+  for (i = 0; i < 2; i++)
+    {
+      if (event_widget == priv->action_widget[i] ||
+          gtk_widget_is_ancestor (event_widget, priv->action_widget[i]))
+        return FALSE;
+    }
+
   switch (event->direction)
     {
     case GDK_SCROLL_RIGHT:
@@ -2857,24 +3004,27 @@ static GtkNotebookPointerPosition
 get_pointer_position (GtkNotebook *notebook)
 {
   GtkWidget *widget = (GtkWidget *) notebook;
-  GtkContainer *container = (GtkContainer *) notebook;
   GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  gint wx, wy, width, height;
   gboolean is_rtl;
 
   if (!notebook->scrollable)
     return POINTER_BETWEEN;
 
+  gdk_window_get_position (notebook->event_window, &wx, &wy);
+  gdk_drawable_get_size (GDK_DRAWABLE (notebook->event_window), &width, &height);
+
   if (notebook->tab_pos == GTK_POS_TOP ||
       notebook->tab_pos == GTK_POS_BOTTOM)
     {
       gint x;
 
       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
-      x = priv->mouse_x - widget->allocation.x;
+      x = priv->mouse_x - wx;
 
-      if (x > widget->allocation.width - 2 * container->border_width - SCROLL_THRESHOLD)
+      if (x > width - SCROLL_THRESHOLD)
 	return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
-      else if (x < SCROLL_THRESHOLD + container->border_width)
+      else if (x < SCROLL_THRESHOLD)
 	return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
       else
 	return POINTER_BETWEEN;
@@ -2883,10 +3033,10 @@ get_pointer_position (GtkNotebook *notebook)
     {
       gint y;
 
-      y = priv->mouse_y - widget->allocation.y;
-      if (y > widget->allocation.height - 2 * container->border_width - SCROLL_THRESHOLD)
+      y = priv->mouse_y - wy;
+      if (y > height - SCROLL_THRESHOLD)
 	return POINTER_AFTER;
-      else if (y < SCROLL_THRESHOLD + container->border_width)
+      else if (y < SCROLL_THRESHOLD)
 	return POINTER_BEFORE;
       else
 	return POINTER_BETWEEN;
@@ -3813,8 +3963,8 @@ focus_tabs_move (GtkNotebook     *notebook,
 }
 
 static gboolean
-focus_child_in (GtkNotebook     *notebook,
-		GtkDirectionType direction)
+focus_child_in (GtkNotebook      *notebook,
+		GtkDirectionType  direction)
 {
   if (notebook->cur_page)
     return gtk_widget_child_focus (notebook->cur_page->child, direction);
@@ -3822,22 +3972,52 @@ focus_child_in (GtkNotebook     *notebook,
     return FALSE;
 }
 
+static gboolean
+focus_action_in (GtkNotebook      *notebook,
+                 gint              action,
+                 GtkDirectionType  direction)
+{
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+  if (priv->action_widget[action] &&
+      GTK_WIDGET_VISIBLE (priv->action_widget[action]))
+    return gtk_widget_child_focus (priv->action_widget[action], direction);
+  else
+    return FALSE;
+}
+
 /* Focus in the notebook can either be on the pages, or on
- * the tabs.
+ * the tabs or on the action_widgets.
  */
 static gint
 gtk_notebook_focus (GtkWidget        *widget,
 		    GtkDirectionType  direction)
 {
+  GtkNotebookPrivate *priv;
   GtkWidget *old_focus_child;
   GtkNotebook *notebook;
   GtkDirectionType effective_direction;
+  gint first_action;
+  gint last_action;
 
   gboolean widget_is_focus;
   GtkContainer *container;
 
   container = GTK_CONTAINER (widget);
   notebook = GTK_NOTEBOOK (container);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+  if (notebook->tab_pos == GTK_POS_TOP ||
+      notebook->tab_pos == GTK_POS_LEFT)
+    {
+      first_action = ACTION_WIDGET_START;
+      last_action = ACTION_WIDGET_END;
+    }
+  else
+    {
+      first_action = ACTION_WIDGET_END;
+      last_action = ACTION_WIDGET_START;
+    }
 
   if (notebook->focus_out)
     {
@@ -3846,42 +4026,104 @@ gtk_notebook_focus (GtkWidget        *widget,
     }
 
   widget_is_focus = gtk_widget_is_focus (widget);
-  old_focus_child = container->focus_child; 
+  old_focus_child = container->focus_child;
 
   effective_direction = get_effective_direction (notebook, direction);
 
-  if (old_focus_child)		/* Focus on page child */
+  if (old_focus_child)		/* Focus on page child or action widget */
     {
       if (gtk_widget_child_focus (old_focus_child, direction))
         return TRUE;
-      
-      switch (effective_direction)
+
+      if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
 	{
-	case GTK_DIR_TAB_BACKWARD:
-	case GTK_DIR_UP:
-	  /* Focus onto the tabs */
-	  return focus_tabs_in (notebook);
-	case GTK_DIR_DOWN:
-	case GTK_DIR_TAB_FORWARD:
-	case GTK_DIR_LEFT:
-	case GTK_DIR_RIGHT:
-	  return FALSE;
+	  switch (effective_direction)
+	    {
+	    case GTK_DIR_DOWN:
+              return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
+	    case GTK_DIR_RIGHT:
+	      return focus_tabs_in (notebook);
+	    case GTK_DIR_LEFT:
+              return FALSE;
+	    case GTK_DIR_UP:
+	      return FALSE;
+            default:
+              switch (direction)
+                {
+	        case GTK_DIR_TAB_FORWARD:
+                  if ((notebook->tab_pos == GTK_POS_RIGHT || notebook->tab_pos == GTK_POS_BOTTOM) &&
+                      focus_child_in (notebook, direction))
+                    return TRUE;
+	          return focus_tabs_in (notebook);
+                case GTK_DIR_TAB_BACKWARD:
+                  return FALSE;
+                default:
+                  g_assert_not_reached ();
+                }
+	    }
+	}
+      else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
+	{
+	  switch (effective_direction)
+	    {
+	    case GTK_DIR_DOWN:
+              return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
+	    case GTK_DIR_RIGHT:
+              return FALSE;
+	    case GTK_DIR_LEFT:
+	      return focus_tabs_in (notebook);
+	    case GTK_DIR_UP:
+	      return FALSE;
+            default:
+              switch (direction)
+                {
+	        case GTK_DIR_TAB_FORWARD:
+                  return FALSE;
+                case GTK_DIR_TAB_BACKWARD:
+                  if ((notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_LEFT) &&
+                      focus_child_in (notebook, direction))
+                    return TRUE;
+	          return focus_tabs_in (notebook);
+                default:
+                  g_assert_not_reached ();
+                }
+	    }
+	}
+      else
+	{
+	  switch (effective_direction)
+	    {
+	    case GTK_DIR_TAB_BACKWARD:
+	    case GTK_DIR_UP:
+	      /* Focus onto the tabs */
+	      return focus_tabs_in (notebook);
+	    case GTK_DIR_DOWN:
+	    case GTK_DIR_LEFT:
+	    case GTK_DIR_RIGHT:
+	      return FALSE;
+	    case GTK_DIR_TAB_FORWARD:
+              return focus_action_in (notebook, last_action, direction);
+	    }
 	}
     }
   else if (widget_is_focus)	/* Focus was on tabs */
     {
       switch (effective_direction)
 	{
-	case GTK_DIR_TAB_BACKWARD:
-	case GTK_DIR_UP:
+        case GTK_DIR_TAB_BACKWARD:
+              return focus_action_in (notebook, first_action, direction);
+        case GTK_DIR_UP:
 	  return FALSE;
-	case GTK_DIR_TAB_FORWARD:
+        case GTK_DIR_TAB_FORWARD:
+          if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
+            return TRUE;
+          return focus_action_in (notebook, last_action, direction);
 	case GTK_DIR_DOWN:
 	  /* We use TAB_FORWARD rather than direction so that we focus a more
 	   * predictable widget for the user; users may be using arrow focusing
 	   * in this situation even if they don't usually use arrow focusing.
 	   */
-	  return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
+          return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
 	case GTK_DIR_LEFT:
 	  return focus_tabs_move (notebook, direction, STEP_PREV);
 	case GTK_DIR_RIGHT:
@@ -3894,18 +4136,25 @@ gtk_notebook_focus (GtkWidget        *widget,
 	{
 	case GTK_DIR_TAB_FORWARD:
 	case GTK_DIR_DOWN:
+          if (focus_action_in (notebook, first_action, direction))
+            return TRUE;
 	  if (focus_tabs_in (notebook))
 	    return TRUE;
+          if (focus_action_in (notebook, last_action, direction))
+            return TRUE;
 	  if (focus_child_in (notebook, direction))
 	    return TRUE;
 	  return FALSE;
 	case GTK_DIR_TAB_BACKWARD:
-	case GTK_DIR_UP:
+          if (focus_action_in (notebook, last_action, direction))
+            return TRUE;
 	  if (focus_child_in (notebook, direction))
 	    return TRUE;
 	  if (focus_tabs_in (notebook))
 	    return TRUE;
-	  return FALSE;
+          if (focus_action_in (notebook, first_action, direction))
+            return TRUE;
+	case GTK_DIR_UP:
 	case GTK_DIR_LEFT:
 	case GTK_DIR_RIGHT:
 	  return focus_child_in (notebook, direction);
@@ -3914,7 +4163,7 @@ gtk_notebook_focus (GtkWidget        *widget,
 
   g_assert_not_reached ();
   return FALSE;
-}  
+}
 
 static void
 gtk_notebook_set_focus_child (GtkContainer *container,
@@ -3988,10 +4237,13 @@ gtk_notebook_forall (GtkContainer *container,
 		     GtkCallback   callback,
 		     gpointer      callback_data)
 {
+  GtkNotebookPrivate *priv;
   GtkNotebook *notebook;
   GList *children;
+  gint i;
 
   notebook = GTK_NOTEBOOK (container);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
 
   children = notebook->children;
   while (children)
@@ -4008,6 +4260,12 @@ gtk_notebook_forall (GtkContainer *container,
 	    (* callback) (page->tab_label, callback_data);
 	}
     }
+
+  for (i = 0; i < N_ACTION_WIDGETS; i++)
+    {
+      if (priv->action_widget[i])
+        (* callback) (priv->action_widget[i], callback_data);
+    }
 }
 
 static GType
@@ -4831,10 +5089,13 @@ gtk_notebook_tab_space (GtkNotebook *notebook,
   gint arrow_spacing;
   gint scroll_arrow_hlength;
   gint scroll_arrow_vlength;
+  gboolean is_rtl;
+  gint i;
 
   widget = GTK_WIDGET (notebook);
   priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   children = notebook->children;
+  is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
 
   gtk_widget_style_get (GTK_WIDGET (notebook),
                         "arrow-spacing", &arrow_spacing,
@@ -4849,6 +5110,18 @@ gtk_notebook_tab_space (GtkNotebook *notebook,
       *min = widget->allocation.x + GTK_CONTAINER (notebook)->border_width;
       *max = widget->allocation.x + widget->allocation.width - GTK_CONTAINER (notebook)->border_width;
 
+      for (i = 0; i < N_ACTION_WIDGETS; i++)
+        {
+          if (priv->action_widget[i])
+            {
+              if ((i == ACTION_WIDGET_START && !is_rtl) ||
+                  (i == ACTION_WIDGET_END && is_rtl))
+                *min += priv->action_widget[i]->allocation.width + widget->style->xthickness;
+              else
+                *max -= priv->action_widget[i]->allocation.width + widget->style->xthickness;
+            }
+        }
+
       while (children)
 	{
           GtkNotebookPage *page;
@@ -4866,6 +5139,17 @@ gtk_notebook_tab_space (GtkNotebook *notebook,
       *min = widget->allocation.y + GTK_CONTAINER (notebook)->border_width;
       *max = widget->allocation.y + widget->allocation.height - GTK_CONTAINER (notebook)->border_width;
 
+      for (i = 0; i < N_ACTION_WIDGETS; i++)
+        {
+          if (priv->action_widget[i])
+            {
+              if (i == ACTION_WIDGET_START)
+                *min += priv->action_widget[i]->allocation.height + widget->style->ythickness;
+              else
+                *max -= priv->action_widget[i]->allocation.height + widget->style->ythickness;
+            }
+        }
+
       while (children)
 	{
           GtkNotebookPage *page;
@@ -4895,8 +5179,7 @@ gtk_notebook_tab_space (GtkNotebook *notebook,
 	      *show_arrows = TRUE;
 
 	      /* take arrows into account */
-	      *tab_space = widget->allocation.width - tab_overlap -
-		2 * GTK_CONTAINER (notebook)->border_width;
+	      *tab_space = *max - *min - tab_overlap;
 
 	      if (notebook->has_after_previous)
 		{
@@ -4930,8 +5213,7 @@ gtk_notebook_tab_space (GtkNotebook *notebook,
 	      *show_arrows = TRUE;
 
 	      /* take arrows into account */
-	      *tab_space = widget->allocation.height -
-		tab_overlap - 2 * GTK_CONTAINER (notebook)->border_width;
+	      *tab_space = *max - *min - tab_overlap;
 
 	      if (notebook->has_after_previous || notebook->has_after_next)
 		{
@@ -7623,5 +7905,69 @@ gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
     }
 }
 
+/**
+ * gtk_notebook_get_action_widget:
+ * @notebook: a #GtkNotebook
+ * @pack_type: pack type of the action widget to receive
+ *
+ * Gets one of the action widgets. See gtk_notebook_set_action_widget().
+ *
+ * Returns: The action widget with the given @pack_type or
+ *     %NULL when this action widget has not been set
+ *
+ * Since: 2.20
+ */
+GtkWidget*
+gtk_notebook_get_action_widget (GtkNotebook *notebook,
+                                GtkPackType  pack_type)
+{
+  GtkNotebookPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  return priv->action_widget[pack_type];
+}
+
+/**
+ * gtk_notebook_set_action_widget:
+ * @notebook: a #GtkNotebook
+ * @widget: a #GtkWidget
+ * @pack_type: pack type of the action widget
+ *
+ * Adds @widget as action_widget to the notebook tab space. Depending
+ * on the pack type the widget will be added before or after the tabs
+ *
+ * Since: 2.20
+ */
+void
+gtk_notebook_set_action_widget (GtkNotebook *notebook,
+				GtkWidget   *widget,
+                                GtkPackType  pack_type)
+{
+  GtkNotebookPrivate *priv;
+
+  g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
+  g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
+  g_return_if_fail (!widget || widget->parent == NULL);
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+  if (priv->action_widget[pack_type])
+    gtk_widget_unparent (priv->action_widget[pack_type]);
+
+  priv->action_widget[pack_type] = widget;
+
+  if (widget)
+    {
+      gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
+
+      if (GTK_WIDGET_REALIZED (GTK_WIDGET (notebook)))
+	gtk_widget_realize (widget);
+    }
+
+  gtk_widget_queue_resize (GTK_WIDGET (notebook));
+}
+
 #define __GTK_NOTEBOOK_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtknotebook.h b/gtk/gtknotebook.h
index 41bbe66..0a14a75 100644
--- a/gtk/gtknotebook.h
+++ b/gtk/gtknotebook.h
@@ -287,6 +287,12 @@ void gtk_notebook_set_tab_detachable      (GtkNotebook *notebook,
 					   GtkWidget   *child,
 					   gboolean     detachable);
 
+GtkWidget* gtk_notebook_get_action_widget (GtkNotebook *notebook,
+                                           GtkPackType  pack_type);
+void       gtk_notebook_set_action_widget (GtkNotebook *notebook,
+                                           GtkWidget   *widget,
+                                           GtkPackType  pack_type);
+
 #ifndef GTK_DISABLE_DEPRECATED
 #define	gtk_notebook_current_page               gtk_notebook_get_current_page
 #define gtk_notebook_set_page                   gtk_notebook_set_current_page



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]