[gimp/soc-2010-cage] Add 'Automatic' Tab Style support



commit 277f202e7774a0d2a9848b042ddb78fc5add243f
Author: Martin Nordholts <martinn src gnome org>
Date:   Fri Jun 11 23:18:49 2010 +0200

    Add 'Automatic' Tab Style support
    
    Add support for a new type of Tab Style called 'Automatic'. This tab
    style makes the GimpDockbook use the biggest actual tab style it can
    for its auto tab style dockables, based on its widget allocation.
    
    The tab style candidates for auto tab style are "Status + Blurb",
    "Status + Text" and "Status". A docked widget can also say that it
    wants to use "Icon" instead of "Status" for its auto tab style. The
    'Tool Options' dockable does this. This is to be as backwards
    compatible with the old tab style setup, we make 'automatic' the
    default everywhere.
    
    We have quite a bit of dependency to internal layout code in
    GtkNotebook, but the current code should be pixel perfect and rather
    complete.
    
    Also add a basic regression test.

 app/actions/dockable-actions.c       |    8 +-
 app/gimp-log.c                       |    3 +-
 app/gimp-log.h                       |    4 +-
 app/tests/gimpdir/sessionrc          |    2 +-
 app/tests/gimpdir/sessionrc-expected |    2 +-
 app/tests/test-ui.c                  |   88 ++++++++
 app/widgets/gimpdockable.c           |   62 ++++++-
 app/widgets/gimpdockable.h           |    3 +
 app/widgets/gimpdockbook.c           |  365 ++++++++++++++++++++++++++++++++--
 app/widgets/gimpdockbook.h           |    1 +
 app/widgets/gimpdocked.c             |   15 ++
 app/widgets/gimpdocked.h             |    2 +
 app/widgets/gimptooloptionseditor.c  |   15 ++-
 app/widgets/widgets-enums.c          |    4 +
 app/widgets/widgets-enums.h          |    4 +-
 etc/sessionrc                        |   16 +-
 menus/dockable-menu.xml.in           |    2 +
 17 files changed, 557 insertions(+), 39 deletions(-)
---
diff --git a/app/actions/dockable-actions.c b/app/actions/dockable-actions.c
index 73d0fa5..bf0933e 100644
--- a/app/actions/dockable-actions.c
+++ b/app/actions/dockable-actions.c
@@ -111,7 +111,9 @@ static const GimpRadioActionEntry dockable_tab_style_actions[] =
   TAB_STYLE ("icon-name",
              NC_("tab-style", "I_con & Text"),    GIMP_TAB_STYLE_ICON_NAME),
   TAB_STYLE ("preview-name",
-             NC_("tab-style", "St_atus & Text"),  GIMP_TAB_STYLE_PREVIEW_NAME)
+             NC_("tab-style", "St_atus & Text"),  GIMP_TAB_STYLE_PREVIEW_NAME),
+  TAB_STYLE ("automatic",
+             NC_("tab-style", "Automatic"),       GIMP_TAB_STYLE_AUTOMATIC)
 };
 
 #undef VIEW_SIZE
@@ -176,7 +178,7 @@ dockable_actions_setup (GimpActionGroup *group)
                                        dockable_tab_style_actions,
                                        G_N_ELEMENTS (dockable_tab_style_actions),
                                        NULL,
-                                       GIMP_TAB_STYLE_PREVIEW,
+                                       GIMP_TAB_STYLE_AUTOMATIC,
                                        G_CALLBACK (dockable_tab_style_cmd_callback));
 
   gimp_action_group_add_radio_actions (group, "dockable-action",
@@ -344,6 +346,8 @@ dockable_actions_update (GimpActionGroup *group,
         SET_ACTIVE ("dockable-tab-style-icon-name", TRUE);
       else if (tab_style == GIMP_TAB_STYLE_PREVIEW_NAME)
         SET_ACTIVE ("dockable-tab-style-preview-name", TRUE);
+      else if (tab_style == GIMP_TAB_STYLE_AUTOMATIC)
+        SET_ACTIVE ("dockable-tab-style-automatic", TRUE);
 
       SET_SENSITIVE ("dockable-tab-style-preview",
                      docked_iface->get_preview);
diff --git a/app/gimp-log.c b/app/gimp-log.c
index 96b5c69..3b0eb7a 100644
--- a/app/gimp-log.c
+++ b/app/gimp-log.c
@@ -51,7 +51,8 @@ gimp_log_init (void)
         { "floating-selection", GIMP_LOG_FLOATING_SELECTION },
         { "shm",                GIMP_LOG_SHM                },
         { "text-editing",       GIMP_LOG_TEXT_EDITING       },
-        { "key-events",         GIMP_LOG_KEY_EVENTS         }
+        { "key-events",         GIMP_LOG_KEY_EVENTS         },
+        { "auto-tab-style",     GIMP_LOG_AUTO_TAB_STYLE     }
       };
 
       /*  g_parse_debug_string() has special treatment of the string 'help',
diff --git a/app/gimp-log.h b/app/gimp-log.h
index 8113e32..8aa7444 100644
--- a/app/gimp-log.h
+++ b/app/gimp-log.h
@@ -35,7 +35,8 @@ typedef enum
   GIMP_LOG_FLOATING_SELECTION = 1 << 11,
   GIMP_LOG_SHM                = 1 << 12,
   GIMP_LOG_TEXT_EDITING       = 1 << 13,
-  GIMP_LOG_KEY_EVENTS         = 1 << 14
+  GIMP_LOG_KEY_EVENTS         = 1 << 14,
+  GIMP_LOG_AUTO_TAB_STYLE     = 1 << 15
 } GimpLogFlags;
 
 
@@ -91,6 +92,7 @@ void   gimp_logv     (const gchar *function,
 #define SHM                GIMP_LOG_SHM
 #define TEXT_EDITING       GIMP_LOG_TEXT_EDITING
 #define KEY_EVENTS         GIMP_LOG_KEY_EVENTS
+#define AUTO_TAB_STYLE     GIMP_LOG_AUTO_TAB_STYLE
 
 #if 0 /* last resort */
 #  define GIMP_LOG /* nothing => no varargs, no log */
diff --git a/app/tests/gimpdir/sessionrc b/app/tests/gimpdir/sessionrc
index c3942ea..0e02373 100644
--- a/app/tests/gimpdir/sessionrc
+++ b/app/tests/gimpdir/sessionrc
@@ -67,7 +67,7 @@
         (book
             (current-page 0)
             (dockable "gimp-channel-list"
-                (tab-style icon)
+                (tab-style automatic)
                 (preview-size 32)))
         (book
             (position 162)
diff --git a/app/tests/gimpdir/sessionrc-expected b/app/tests/gimpdir/sessionrc-expected
index 52b6f48..d2282b6 100644
--- a/app/tests/gimpdir/sessionrc-expected
+++ b/app/tests/gimpdir/sessionrc-expected
@@ -71,7 +71,7 @@
         (book
             (current-page 0)
             (dockable "gimp-channel-list"
-                (tab-style icon)
+                (tab-style automatic)
                 (preview-size 32)))
         (book
             (position 162)
diff --git a/app/tests/test-ui.c b/app/tests/test-ui.c
index fa222dd..594d8fc 100644
--- a/app/tests/test-ui.c
+++ b/app/tests/test-ui.c
@@ -16,6 +16,7 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
@@ -31,6 +32,8 @@
 #include "display/gimpimagewindow.h"
 
 #include "widgets/gimpdialogfactory.h"
+#include "widgets/gimpdockable.h"
+#include "widgets/gimpdockbook.h"
 #include "widgets/gimpdocked.h"
 #include "widgets/gimpdockwindow.h"
 #include "widgets/gimphelp-ids.h"
@@ -66,6 +69,8 @@ typedef struct
 
 static void            gimp_ui_tool_options_editor_updates      (GimpTestFixture   *fixture,
                                                                  gconstpointer      data);
+static void            gimp_ui_automatic_tab_style              (GimpTestFixture   *fixture,
+                                                                 gconstpointer      data);
 static void            gimp_ui_create_new_image_via_dialog      (GimpTestFixture   *fixture,
                                                                  gconstpointer      data);
 static void            gimp_ui_restore_recently_closed_dock     (GimpTestFixture   *fixture,
@@ -112,6 +117,12 @@ int main(int argc, char **argv)
               NULL,
               gimp_ui_tool_options_editor_updates,
               NULL);
+  g_test_add ("/gimp-ui/automatic-tab-style",
+              GimpTestFixture,
+              gimp,
+              NULL,
+              gimp_ui_automatic_tab_style,
+              NULL);
   g_test_add ("/gimp-ui/create-new-image-via-dialog",
               GimpTestFixture,
               gimp,
@@ -155,6 +166,7 @@ int main(int argc, char **argv)
               gimp_ui_switch_back_to_multi_window_mode,
               NULL);
 
+
   /* Run the tests and return status */
   result = g_test_run ();
 
@@ -217,6 +229,82 @@ gimp_ui_tool_options_editor_updates (GimpTestFixture *fixture,
                    help_id);
 }
 
+static GtkWidget *
+gimp_ui_get_dialog (const gchar *identifier)
+{
+  GtkWidget *result = NULL;
+  GList     *iter;
+
+  for (iter = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ());
+       iter;
+       iter = g_list_next (iter))
+    {
+      GtkWidget *dialog = GTK_WIDGET (iter->data);
+      GimpDialogFactoryEntry *entry = NULL;
+
+      gimp_dialog_factory_from_widget (dialog, &entry);
+
+      if (strcmp (entry->identifier, identifier) == 0)
+        {
+          result = dialog;
+          break;
+        }
+    }
+
+  return result;
+}
+
+static void
+gimp_ui_automatic_tab_style (GimpTestFixture *fixture,
+                             gconstpointer    data)
+{
+  GtkWidget    *channel_dockable = gimp_ui_get_dialog ("gimp-channel-list");
+  GimpDockable *dockable;
+  GimpUIManager *ui_manager;
+  g_assert (channel_dockable != NULL);
+
+  dockable = GIMP_DOCKABLE (channel_dockable);
+
+  /* The channel dockable is the only dockable, it has enough space
+   * for the icon-blurb
+   */
+  g_assert_cmpint (GIMP_TAB_STYLE_ICON_BLURB,
+                   ==,
+                   gimp_dockable_get_actual_tab_style (dockable));
+
+  /* Add some dockables to the channel dockable dockbook */
+  ui_manager =
+    gimp_dockbook_get_ui_manager (gimp_dockable_get_dockbook (dockable));
+  gimp_ui_manager_activate_action (ui_manager,
+                                   "dockable",
+                                   "dialogs-sample-points");
+  gimp_ui_manager_activate_action (ui_manager,
+                                   "dockable",
+                                   "dialogs-vectors");
+  gimp_test_run_mainloop_until_idle ();
+
+  /* Now there is not enough space to have icon-blurb for channel
+   * dockable, make sure it's just an icon now
+   */
+  g_assert_cmpint (GIMP_TAB_STYLE_ICON,
+                   ==,
+                   gimp_dockable_get_actual_tab_style (dockable));
+
+  /* Close the two dockables we added */
+  gimp_ui_manager_activate_action (ui_manager,
+                                   "dockable",
+                                   "dockable-close-tab");
+  gimp_ui_manager_activate_action (ui_manager,
+                                   "dockable",
+                                   "dockable-close-tab");
+  gimp_test_run_mainloop_until_idle ();
+
+  /* We should now be back on icon-blurb */
+  g_assert_cmpint (GIMP_TAB_STYLE_ICON_BLURB,
+                   ==,
+                   gimp_dockable_get_actual_tab_style (dockable));
+}
+
 static void
 gimp_ui_create_new_image_via_dialog (GimpTestFixture *fixture,
                                      gconstpointer    data)
diff --git a/app/widgets/gimpdockable.c b/app/widgets/gimpdockable.c
index 1f276c0..f2936b1 100644
--- a/app/widgets/gimpdockable.c
+++ b/app/widgets/gimpdockable.c
@@ -62,6 +62,7 @@ struct _GimpDockablePrivate
   gchar        *stock_id;
   gchar        *help_id;
   GimpTabStyle  tab_style;
+  GimpTabStyle  actual_tab_style;
   gboolean      locked;
 
   GimpDockbook *dockbook;
@@ -215,9 +216,10 @@ gimp_dockable_init (GimpDockable *dockable)
   dockable->p = G_TYPE_INSTANCE_GET_PRIVATE (dockable,
                                              GIMP_TYPE_DOCKABLE,
                                              GimpDockablePrivate);
-  dockable->p->tab_style = GIMP_TAB_STYLE_PREVIEW;
-  dockable->p->drag_x    = GIMP_DOCKABLE_DRAG_OFFSET;
-  dockable->p->drag_y    = GIMP_DOCKABLE_DRAG_OFFSET;
+  dockable->p->tab_style        = GIMP_TAB_STYLE_AUTOMATIC;
+  dockable->p->actual_tab_style = GIMP_TAB_STYLE_UNDEFINED;
+  dockable->p->drag_x           = GIMP_DOCKABLE_DRAG_OFFSET;
+  dockable->p->drag_y           = GIMP_DOCKABLE_DRAG_OFFSET;
 
   gtk_widget_push_composite_child ();
   dockable->p->menu_button = gtk_button_new ();
@@ -884,6 +886,11 @@ gimp_dockable_new_tab_widget_internal (GimpDockable *dockable,
       gtk_box_pack_start (GTK_BOX (tab_widget), label, FALSE, FALSE, 0);
       gtk_widget_show (label);
       break;
+
+    case GIMP_TAB_STYLE_UNDEFINED:
+    case GIMP_TAB_STYLE_AUTOMATIC:
+      g_warning ("Tab style error, unexpected code path taken, fix!");
+      break;
     }
 
   return tab_widget;
@@ -946,6 +953,22 @@ gimp_dockable_get_tab_style (GimpDockable *dockable)
   return dockable->p->tab_style;
 }
 
+/**
+ * gimp_dockable_get_actual_tab_style:
+ * @dockable:
+ *
+ * Get actual tab style, i.e. never "automatic". This state should
+ * actually be hold on a per-dockbook basis, but at this point that
+ * feels like over-engineering...
+ **/
+GimpTabStyle
+gimp_dockable_get_actual_tab_style (GimpDockable *dockable)
+{
+  g_return_val_if_fail (GIMP_IS_DOCKABLE (dockable), -1);
+
+  return dockable->p->actual_tab_style;
+}
+
 const gchar *
 gimp_dockable_get_name (GimpDockable *dockable)
 {
@@ -1072,6 +1095,39 @@ gimp_dockable_set_tab_style (GimpDockable *dockable,
   g_return_if_fail (GIMP_IS_DOCKABLE (dockable));
 
   dockable->p->tab_style = gimp_dockable_convert_tab_style (dockable, tab_style);
+
+  if (tab_style == GIMP_TAB_STYLE_AUTOMATIC)
+    gimp_dockable_set_actual_tab_style (dockable, GIMP_TAB_STYLE_UNDEFINED);
+  else
+    gimp_dockable_set_actual_tab_style (dockable, tab_style);
+
+  if (dockable->p->dockbook)
+    gimp_dockbook_update_auto_tab_style (dockable->p->dockbook);
+}
+
+/**
+ * gimp_dockable_set_actual_tab_style:
+ * @dockable:
+ * @tab_style:
+ *
+ * Sets actual tab style, meant for those that decides what
+ * "automatic" tab style means.
+ *
+ * Returns: %TRUE if changed, %FALSE otherwise.
+ **/
+gboolean
+gimp_dockable_set_actual_tab_style (GimpDockable *dockable,
+                                    GimpTabStyle  tab_style)
+{
+  GimpTabStyle new_tab_style = gimp_dockable_convert_tab_style (dockable, tab_style);
+  GimpTabStyle old_tab_style = dockable->p->actual_tab_style;
+  
+  g_return_val_if_fail (GIMP_IS_DOCKABLE (dockable), FALSE);
+  g_return_val_if_fail (tab_style != GIMP_TAB_STYLE_AUTOMATIC, FALSE);
+
+  dockable->p->actual_tab_style = new_tab_style;
+
+  return new_tab_style != old_tab_style;
 }
 
 GtkWidget *
diff --git a/app/widgets/gimpdockable.h b/app/widgets/gimpdockable.h
index a285b9e..52ea00e 100644
--- a/app/widgets/gimpdockable.h
+++ b/app/widgets/gimpdockable.h
@@ -88,6 +88,9 @@ gboolean        gimp_dockable_is_locked               (GimpDockable   *dockable)
                                                       
 void            gimp_dockable_set_tab_style           (GimpDockable   *dockable,
                                                        GimpTabStyle    tab_style);
+gboolean        gimp_dockable_set_actual_tab_style    (GimpDockable   *dockable,
+                                                       GimpTabStyle    tab_style);
+GimpTabStyle    gimp_dockable_get_actual_tab_style    (GimpDockable   *dockable);
 GtkWidget     * gimp_dockable_create_tab_widget       (GimpDockable   *dockable,
                                                        GimpContext    *context,
                                                        GimpTabStyle    tab_style,
diff --git a/app/widgets/gimpdockbook.c b/app/widgets/gimpdockbook.c
index 943e1ff..0fce11e 100644
--- a/app/widgets/gimpdockbook.c
+++ b/app/widgets/gimpdockbook.c
@@ -47,12 +47,13 @@
 
 #include "gimp-log.h"
 
-#define DEFAULT_TAB_BORDER     0
-#define DEFAULT_TAB_ICON_SIZE  GTK_ICON_SIZE_BUTTON
-#define DND_WIDGET_ICON_SIZE   GTK_ICON_SIZE_BUTTON
-#define MENU_WIDGET_ICON_SIZE  GTK_ICON_SIZE_MENU
-#define MENU_WIDGET_SPACING    4
-#define TAB_HOVER_TIMEOUT      500
+#define DEFAULT_TAB_BORDER          0
+#define DEFAULT_TAB_ICON_SIZE       GTK_ICON_SIZE_BUTTON
+#define DND_WIDGET_ICON_SIZE        GTK_ICON_SIZE_BUTTON
+#define MENU_WIDGET_ICON_SIZE       GTK_ICON_SIZE_MENU
+#define MENU_WIDGET_SPACING         4
+#define TAB_HOVER_TIMEOUT           500
+#define GIMP_N_TAB_STYLE_CANDIDATES 3 /* G_N_ELEMENTS (gimp_tab_style_candidates); */
 
 
 enum
@@ -65,18 +66,34 @@ enum
 
 struct _GimpDockbookPrivate
 {
-  GimpDock      *dock;
-  GimpUIManager *ui_manager;
+  GimpDock       *dock;
+  GimpUIManager  *ui_manager;
 
-  guint          tab_hover_timeout;
-  GimpDockable  *tab_hover_dockable;
+  guint           tab_hover_timeout;
+  GimpDockable   *tab_hover_dockable;
 
-  GimpPanedBox  *drag_handler;
+  GimpPanedBox   *drag_handler;
+
+  /* Cache for "what actual tab style for automatic styles can we use
+   * for a given dockbook width
+   */
+  gint            min_width_for_style[GIMP_N_TAB_STYLE_CANDIDATES];
+
+  /* We need a list separate from the GtkContainer children list,
+   * because we need to do calculations for all dockables before we
+   * can add a dockable as a child, namely automatic tab style
+   * calculations
+   */
+  GList          *dockables;
+
+  GtkWidget      *menu_button;
 };
 
 
 static void         gimp_dockbook_dispose                     (GObject        *object);
 static void         gimp_dockbook_finalize                    (GObject        *object);
+static void         gimp_dockbook_size_allocate               (GtkWidget      *widget,
+                                                               GtkAllocation  *allocation);
 static void         gimp_dockbook_style_set                   (GtkWidget      *widget,
                                                                GtkStyle       *prev_style);
 static void         gimp_dockbook_drag_leave                  (GtkWidget      *widget,
@@ -98,7 +115,8 @@ static void         gimp_dockbook_dockable_removed            (GimpDockbook   *d
                                                                GimpDockable   *dockable);
 static void         gimp_dockbook_update_tabs                 (GimpDockbook   *dockbook,
                                                                gboolean        added);
-static void         gimp_dockbook_recreate_tab_widgets        (GimpDockbook   *dockbook);
+static void         gimp_dockbook_recreate_tab_widgets        (GimpDockbook   *dockbook,
+                                                               gboolean        only_auto);
 static void         gimp_dockbook_tab_drag_source_setup       (GtkWidget      *widget,
                                                                GimpDockable   *dockable);
 static void         gimp_dockbook_tab_drag_begin              (GtkWidget      *widget,
@@ -122,6 +140,10 @@ static gboolean     gimp_dockbook_tab_drag_drop               (GtkWidget      *w
                                                                gint            x,
                                                                gint            y,
                                                                guint           time);
+static GimpTabStyle gimp_dockbook_tab_style_to_prefered       (GimpTabStyle    tab_style,
+                                                               GimpDockable   *dockable);
+static void         gimp_dockbook_refresh_tab_layout_lut      (GimpDockbook   *dockbook);
+static void         gimp_dockbook_update_automatic_tab_style  (GimpDockbook   *dockbook);
 static GtkWidget *  gimp_dockable_create_event_box_tab_widget (GimpDockable   *dockable,
                                                                GimpContext    *context,
                                                                GimpTabStyle    tab_style,
@@ -136,6 +158,7 @@ static void         gimp_dockbook_tab_locked_notify           (GimpDockable   *d
                                                                GimpDockbook   *dockbook);
 static void         gimp_dockbook_help_func                   (const gchar    *help_id,
                                                                gpointer        help_data);
+static const gchar *gimp_dockbook_get_tab_style_name          (GimpTabStyle    tab_style);
 
 
 G_DEFINE_TYPE (GimpDockbook, gimp_dockbook, GTK_TYPE_NOTEBOOK)
@@ -146,6 +169,15 @@ static guint dockbook_signals[LAST_SIGNAL] = { 0 };
 
 static const GtkTargetEntry dialog_target_table[] = { GIMP_TARGET_DIALOG };
 
+/* List of candidates for the automatic style, starting with the
+ * biggest first
+ */
+static GimpTabStyle gimp_tab_style_candidates[] = {
+  GIMP_TAB_STYLE_PREVIEW_BLURB,
+  GIMP_TAB_STYLE_PREVIEW_NAME,
+  GIMP_TAB_STYLE_PREVIEW,
+};
+
 
 static void
 gimp_dockbook_class_init (GimpDockbookClass *klass)
@@ -153,6 +185,9 @@ gimp_dockbook_class_init (GimpDockbookClass *klass)
   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+  if (G_N_ELEMENTS (gimp_tab_style_candidates) != GIMP_N_TAB_STYLE_CANDIDATES)
+    g_error ("Update GIMP_N_TAB_STYLE_CANDIDATES");
+
   dockbook_signals[DOCKABLE_ADDED] =
     g_signal_new ("dockable-added",
                   G_TYPE_FROM_CLASS (klass),
@@ -186,10 +221,11 @@ gimp_dockbook_class_init (GimpDockbookClass *klass)
   object_class->dispose     = gimp_dockbook_dispose;
   object_class->finalize    = gimp_dockbook_finalize;
 
-  widget_class->style_set   = gimp_dockbook_style_set;
-  widget_class->drag_leave  = gimp_dockbook_drag_leave;
-  widget_class->drag_motion = gimp_dockbook_drag_motion;
-  widget_class->drag_drop   = gimp_dockbook_drag_drop;
+  widget_class->size_allocate = gimp_dockbook_size_allocate;
+  widget_class->style_set     = gimp_dockbook_style_set;
+  widget_class->drag_leave    = gimp_dockbook_drag_leave;
+  widget_class->drag_motion   = gimp_dockbook_drag_motion;
+  widget_class->drag_drop     = gimp_dockbook_drag_drop;
 
   klass->dockable_added     = gimp_dockbook_dockable_added;
   klass->dockable_removed   = gimp_dockbook_dockable_removed;
@@ -255,6 +291,18 @@ gimp_dockbook_finalize (GObject *object)
 }
 
 static void
+gimp_dockbook_size_allocate (GtkWidget      *widget,
+                             GtkAllocation  *allocation)
+{
+  GimpDockbook *dockbook = GIMP_DOCKBOOK (widget);
+
+  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+  /* Update tab styles, also recreates if changed */
+  gimp_dockbook_update_automatic_tab_style (dockbook);
+}
+
+static void
 gimp_dockbook_style_set (GtkWidget *widget,
                          GtkStyle  *prev_style)
 {
@@ -279,7 +327,8 @@ gimp_dockbook_style_set (GtkWidget *widget,
                 "tab-border", tab_border,
                 NULL);
 
-  gimp_dockbook_recreate_tab_widgets (GIMP_DOCKBOOK (widget));
+  gimp_dockbook_recreate_tab_widgets (GIMP_DOCKBOOK (widget),
+                                      FALSE /*only_auto*/);
 }
 
 static void
@@ -382,6 +431,241 @@ gimp_dockbook_update_tabs (GimpDockbook *dockbook,
     }
 }
 
+/**
+ * gimp_dockbook_get_dockable_tab_width:
+ * @dockable:
+ * @tab_style:
+ *
+ * Returns: Width of tab when the dockable is using the specified tab
+ *          style.
+ **/
+static gint
+gimp_dockbook_get_dockable_tab_width (GimpDockbook *dockbook,
+                                      GimpDockable *dockable,
+                                      GimpTabStyle  tab_style)
+{
+  GtkRequisition  dockable_request;
+  GtkWidget      *tab_widget;
+
+  tab_widget =
+    gimp_dockable_create_event_box_tab_widget (dockable,
+                                               gimp_dock_get_context (dockbook->p->dock),
+                                               tab_style,
+                                               gimp_dockbook_get_tab_icon_size (dockbook));
+
+  /* So font-scale is applied. We can't apply styles without having a
+   * GdkScreen :(
+   */
+  gimp_dock_temp_add (dockbook->p->dock, tab_widget);
+
+  gtk_widget_size_request (tab_widget, &dockable_request);
+
+  /* Also destroys the widget */
+  gimp_dock_temp_remove (dockbook->p->dock, tab_widget);
+
+  return dockable_request.width;
+}
+
+/**
+ * gimp_dockbook_tab_style_to_prefered:
+ * @tab_style:
+ * @dockable:
+ *
+ * The list of tab styles to try in automatic mode only consists of
+ * preview styles. For some dockables, like the tool options dockable,
+ * we rather want to use the icon tab styles for the automatic
+ * mode. This function is used to convert tab styles for such
+ * dockables.
+ *
+ * Returns: An icon tab style if the dockable prefers icon tab styles
+ *          in automatic mode.
+ **/
+static GimpTabStyle
+gimp_dockbook_tab_style_to_prefered (GimpTabStyle  tab_style,
+                                     GimpDockable *dockable)
+{
+  GimpDocked *docked = GIMP_DOCKED (gtk_bin_get_child (GTK_BIN (dockable)));
+
+  if (gimp_docked_get_prefer_icon (docked))
+    tab_style = gimp_preview_tab_style_to_icon (tab_style);
+
+  return tab_style;
+}
+
+/**
+ * gimp_dockbook_refresh_tab_layout_lut:
+ * @dockbook:
+ *
+ * For each given set of tab widgets, there is a fixed mapping between
+ * the width of the dockbook and the actual tab style to use for auto
+ * tab widgets. This function refreshes that look-up table.
+ **/
+static void
+gimp_dockbook_refresh_tab_layout_lut (GimpDockbook *dockbook)
+{
+  GList *auto_dockables        = NULL;
+  GList *iter                  = NULL;
+  gint   fixed_tab_style_space = 0;
+  int    i                     = 0;
+
+  /* Calculate space taken by dockables with fixed tab styles */
+  fixed_tab_style_space = 0;
+  for (iter = dockbook->p->dockables; iter; iter = g_list_next (iter))
+    {
+      GimpDockable *dockable  = GIMP_DOCKABLE (iter->data);
+      GimpTabStyle  tab_style = gimp_dockable_get_tab_style (dockable);
+
+      if (tab_style == GIMP_TAB_STYLE_AUTOMATIC)
+        auto_dockables = g_list_prepend (auto_dockables, dockable);
+      else
+        fixed_tab_style_space +=
+          gimp_dockbook_get_dockable_tab_width (dockbook,
+                                                dockable,
+                                                tab_style);
+    }
+
+  /* Calculate space taken with auto tab style for all candidates */
+  for (i = 0; i < GIMP_N_TAB_STYLE_CANDIDATES; i++)
+    {
+      gint         size_with_candidate = 0;
+      GimpTabStyle candidate           = gimp_tab_style_candidates[i];
+
+      for (iter = auto_dockables; iter; iter = g_list_next (iter))
+        {
+          GimpDockable *dockable = GIMP_DOCKABLE (iter->data);
+          GimpTabStyle  style_to_use;
+
+          style_to_use = gimp_dockbook_tab_style_to_prefered (candidate,
+                                                              dockable);
+          size_with_candidate +=
+            gimp_dockbook_get_dockable_tab_width (dockbook,
+                                                  dockable,
+                                                  style_to_use);
+        }
+
+      dockbook->p->min_width_for_style[i] =
+        fixed_tab_style_space + size_with_candidate;
+
+      GIMP_LOG (AUTO_TAB_STYLE, "Total tab space taken for auto tab style %s = %d",
+                gimp_dockbook_get_tab_style_name (candidate),
+                dockbook->p->min_width_for_style[i]);
+    }
+
+  g_list_free (auto_dockables);
+}
+
+/**
+ * gimp_dockbook_update_automatic_tab_style:
+ * @dockbook:
+ *
+ * Based on widget allocation, sets actual tab style for dockables
+ * with automatic tab styles. Takes care of recreating tab widgets if
+ * necessary.
+ **/
+static void
+gimp_dockbook_update_automatic_tab_style (GimpDockbook *dockbook)
+{
+  GtkWidget    *widget              = GTK_WIDGET (dockbook);
+  gboolean      changed             = FALSE;
+  GList        *iter                = NULL;
+  GtkAllocation dockbook_allocation = { 0, };
+  GtkAllocation button_allocation   = { 0, };
+  GimpTabStyle  tab_style           = 0;
+  int           i                   = 0;
+  gint          available_space     = 0;
+  guint         tab_hborder         = 0;
+  gint          xthickness          = 0;
+  gint          tab_curvature       = 0;
+  gint          focus_width         = 0;
+  gint          tab_overlap         = 0;
+  gint          tab_padding         = 0;
+  gint          border_loss         = 0;
+  gint          action_widget_size  = 0;
+
+  xthickness = gtk_widget_get_style (widget)->xthickness;
+  g_object_get (widget,
+                "tab-hborder", &tab_hborder,
+                NULL);
+  gtk_widget_style_get (widget,
+                        "tab-curvature",    &tab_curvature,
+                        "focus-line-width", &focus_width,
+                        "tab-overlap",      &tab_overlap,
+                        NULL);
+  gtk_widget_get_allocation (dockbook->p->menu_button,
+                             &button_allocation);
+
+  /* Calculate available space. Based on code in GTK+ internal
+   * functions gtk_notebook_size_request() and
+   * gtk_notebook_pages_allocate()
+   */
+  gtk_widget_get_allocation (widget, &dockbook_allocation);
+
+  /* Border on both sides */
+  border_loss = gtk_container_get_border_width (GTK_CONTAINER (dockbook)) * 2;
+
+  /* Space taken by action widget */
+  action_widget_size = button_allocation.width + xthickness;
+
+  /* Space taken by the tabs but not the tab widgets themselves */
+  tab_padding = gtk_notebook_get_n_pages (GTK_NOTEBOOK (dockbook)) *
+                (2 * (xthickness + tab_curvature + focus_width + tab_hborder) -
+                 tab_overlap);
+
+  available_space = dockbook_allocation.width
+    - border_loss
+    - action_widget_size
+    - tab_padding
+    - tab_overlap;
+
+  GIMP_LOG (AUTO_TAB_STYLE, "\n"
+            "  available_space             = %d where\n"
+            "    dockbook_allocation.width = %d\n"
+            "    border_loss               = %d\n"
+            "    action_widget_size        = %d\n"
+            "    tab_padding               = %d\n"
+            "    tab_overlap               = %d\n",
+            available_space,
+            dockbook_allocation.width,
+            border_loss,
+            action_widget_size,
+            tab_padding,
+            tab_overlap);
+
+  /* Try all candidates, if we don't get any hit we still end up on
+   * the smallest style (which we always fall back to if we don't get
+   * a better match)
+   */
+  for (i = 0; i < GIMP_N_TAB_STYLE_CANDIDATES; i++)
+    {
+      tab_style = gimp_tab_style_candidates[i];
+      if (available_space > dockbook->p->min_width_for_style[i])
+        {
+          GIMP_LOG (AUTO_TAB_STYLE, "Choosing tab style %s",
+                    gimp_dockbook_get_tab_style_name (tab_style));
+          break;
+        }
+    }
+
+  for (iter = dockbook->p->dockables; iter; iter = g_list_next (iter))
+    {
+      GimpDockable *dockable         = GIMP_DOCKABLE (iter->data);
+      GimpTabStyle  actual_tab_style = tab_style;
+
+      if (gimp_dockable_get_tab_style (dockable) != GIMP_TAB_STYLE_AUTOMATIC)
+        continue;
+
+      actual_tab_style = gimp_dockbook_tab_style_to_prefered (tab_style,
+                                                              dockable);
+
+      if (gimp_dockable_set_actual_tab_style (dockable, actual_tab_style))
+        changed = TRUE;
+    }
+
+  if (changed)
+    gimp_dockbook_recreate_tab_widgets (dockbook,
+                                        TRUE /*only_auto*/);
+}
+
 GtkWidget *
 gimp_dockbook_new (GimpMenuFactory *menu_factory)
 {
@@ -443,6 +727,16 @@ gimp_dockbook_add (GimpDockbook *dockbook,
 
   GIMP_LOG (DND, "Adding GimpDockable %p to GimpDockbook %p", dockable, dockbook);
 
+  /* Add to internal list before doing automatic tab style
+   * calculations
+   */
+  dockbook->p->dockables = g_list_insert (dockbook->p->dockables,
+                                          dockable,
+                                          position);
+
+  gimp_dockbook_update_auto_tab_style (dockbook);  
+
+  /* Create the new tab widget, it will get the correct tab style now */
   tab_widget = gimp_dockbook_create_tab_widget (dockbook, dockable);
 
   g_return_if_fail (GTK_IS_WIDGET (tab_widget));
@@ -515,11 +809,15 @@ gimp_dockbook_remove (GimpDockbook *dockbook,
   gimp_dockable_set_context (dockable, NULL);
 
   gtk_container_remove (GTK_CONTAINER (dockbook), GTK_WIDGET (dockable));
+  dockbook->p->dockables = g_list_remove (dockbook->p->dockables,
+                                          dockable);
 
   g_signal_emit (dockbook, dockbook_signals[DOCKABLE_REMOVED], 0, dockable);
 
   g_object_unref (dockable);
 
+  gimp_dockbook_update_auto_tab_style (dockbook);
+
   children = gtk_container_get_children (GTK_CONTAINER (dockbook));
 
   if (! g_list_length (children))
@@ -565,7 +863,7 @@ gimp_dockbook_create_tab_widget (GimpDockbook *dockbook,
   tab_widget =
     gimp_dockable_create_event_box_tab_widget (dockable,
                                                gimp_dock_get_context (dockbook->p->dock),
-                                               gimp_dockable_get_tab_style (dockable),
+                                               gimp_dockable_get_actual_tab_style (dockable),
                                                gimp_dockbook_get_tab_icon_size (dockbook));
 
   /* EEK */
@@ -649,6 +947,23 @@ gimp_dockbook_create_tab_widget (GimpDockbook *dockbook,
   return tab_widget;
 }
 
+/**
+ * gimp_dockbook_update_auto_tab_style:
+ * @dockbook:
+ *
+ * Refresh the table that we use to map dockbook width to actual auto
+ * tab style, then update auto tabs (also recreate tab widgets if
+ * necessary).
+ **/
+void
+gimp_dockbook_update_auto_tab_style (GimpDockbook *dockbook)
+{
+  g_return_if_fail (GIMP_IS_DOCKBOOK (dockbook));
+
+  gimp_dockbook_refresh_tab_layout_lut (dockbook);
+  gimp_dockbook_update_automatic_tab_style (dockbook);
+}
+
 gboolean
 gimp_dockbook_drop_dockable (GimpDockbook *dockbook,
                              GtkWidget    *drag_source)
@@ -730,7 +1045,8 @@ gimp_dockbook_drag_source_to_dockable (GtkWidget *drag_source)
 /*  tab DND source side  */
 
 static void
-gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook)
+gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook,
+                                    gboolean      only_auto)
 {
   GList *dockables = gtk_container_get_children (GTK_CONTAINER (dockbook));
   GList *iter      = NULL;
@@ -740,6 +1056,10 @@ gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook)
       GimpDockable *dockable = GIMP_DOCKABLE (iter->data);
       GtkWidget *tab_widget;
 
+      if (only_auto &&
+          ! gimp_dockable_get_tab_style (dockable) == GIMP_TAB_STYLE_AUTOMATIC)
+        continue;
+
       tab_widget = gimp_dockbook_create_tab_widget (dockbook, dockable);
 
       gtk_notebook_set_tab_label (GTK_NOTEBOOK (dockbook),
@@ -1109,3 +1429,10 @@ gimp_dockbook_help_func (const gchar *help_id,
   else
     gimp_standard_help_func (GIMP_HELP_DOCK, NULL);
 }
+
+static const gchar *
+gimp_dockbook_get_tab_style_name (GimpTabStyle tab_style)
+{
+  return g_enum_get_value (g_type_class_peek (GIMP_TYPE_TAB_STYLE),
+                           tab_style)->value_name;  
+}
diff --git a/app/widgets/gimpdockbook.h b/app/widgets/gimpdockbook.h
index f43b680..e3249fe 100644
--- a/app/widgets/gimpdockbook.h
+++ b/app/widgets/gimpdockbook.h
@@ -74,6 +74,7 @@ void            gimp_dockbook_update_with_context       (GimpDockbook    *dockbo
                                                          GimpContext     *context);
 GtkWidget    *  gimp_dockbook_create_tab_widget         (GimpDockbook    *dockbook,
                                                          GimpDockable    *dockable);
+void            gimp_dockbook_update_auto_tab_style     (GimpDockbook    *dockbook);
 gboolean        gimp_dockbook_drop_dockable             (GimpDockbook    *dockbook,
                                                          GtkWidget       *drag_source);
 void            gimp_dockbook_set_drag_handler          (GimpDockbook    *dockbook,
diff --git a/app/widgets/gimpdocked.c b/app/widgets/gimpdocked.c
index 81e57b1..14d8246 100644
--- a/app/widgets/gimpdocked.c
+++ b/app/widgets/gimpdocked.c
@@ -191,6 +191,21 @@ gimp_docked_get_preview (GimpDocked  *docked,
   return NULL;
 }
 
+gboolean
+gimp_docked_get_prefer_icon (GimpDocked *docked)
+{
+  GimpDockedInterface *docked_iface;
+
+  g_return_val_if_fail (GIMP_IS_DOCKED (docked), FALSE);
+
+  docked_iface = GIMP_DOCKED_GET_INTERFACE (docked);
+
+  if (docked_iface->get_prefer_icon)
+    return docked_iface->get_prefer_icon (docked);
+
+  return FALSE;
+}
+
 GimpUIManager *
 gimp_docked_get_menu (GimpDocked     *docked,
                       const gchar   **ui_path,
diff --git a/app/widgets/gimpdocked.h b/app/widgets/gimpdocked.h
index 04d73af..fe9e269 100644
--- a/app/widgets/gimpdocked.h
+++ b/app/widgets/gimpdocked.h
@@ -50,6 +50,7 @@ struct _GimpDockedInterface
   GtkWidget     * (* get_preview)         (GimpDocked   *docked,
                                            GimpContext  *context,
                                            GtkIconSize   size);
+  gboolean        (* get_prefer_icon)     (GimpDocked   *docked);
   GimpUIManager * (* get_menu)            (GimpDocked   *docked,
                                            const gchar **ui_path,
                                            gpointer     *popup_data);
@@ -76,6 +77,7 @@ GList         * gimp_docked_get_aux_info        (GimpDocked   *docked);
 GtkWidget     * gimp_docked_get_preview         (GimpDocked   *docked,
                                                  GimpContext  *context,
                                                  GtkIconSize   size);
+gboolean        gimp_docked_get_prefer_icon     (GimpDocked   *docked);
 GimpUIManager * gimp_docked_get_menu            (GimpDocked   *docked,
                                                  const gchar **ui_path,
                                                  gpointer     *popup_data);
diff --git a/app/widgets/gimptooloptionseditor.c b/app/widgets/gimptooloptionseditor.c
index 3fd282d..63154bd 100644
--- a/app/widgets/gimptooloptionseditor.c
+++ b/app/widgets/gimptooloptionseditor.c
@@ -88,6 +88,7 @@ static GtkWidget * gimp_tool_options_editor_get_preview       (GimpDocked
                                                                GimpContext           *context,
                                                                GtkIconSize            size);
 static gchar     * gimp_tool_options_editor_get_title         (GimpDocked            *docked);
+static gboolean    gimp_tool_options_editor_get_prefer_icon   (GimpDocked            *docked);
 static void        gimp_tool_options_editor_save_clicked      (GtkWidget             *widget,
                                                                GimpToolOptionsEditor *editor);
 static void        gimp_tool_options_editor_restore_clicked   (GtkWidget             *widget,
@@ -184,8 +185,9 @@ gimp_tool_options_editor_init (GimpToolOptionsEditor *editor)
 static void
 gimp_tool_options_editor_docked_iface_init (GimpDockedInterface *docked_iface)
 {
-  docked_iface->get_preview = gimp_tool_options_editor_get_preview;
-  docked_iface->get_title   = gimp_tool_options_editor_get_title;
+  docked_iface->get_preview     = gimp_tool_options_editor_get_preview;
+  docked_iface->get_title       = gimp_tool_options_editor_get_title;
+  docked_iface->get_prefer_icon = gimp_tool_options_editor_get_prefer_icon;
 }
 
 static GObject *
@@ -349,6 +351,15 @@ gimp_tool_options_editor_get_title (GimpDocked *docked)
   return tool_info ? g_strdup (tool_info->blurb) : NULL;
 }
 
+static gboolean
+gimp_tool_options_editor_get_prefer_icon (GimpDocked *docked)
+{
+  /* We support get_preview() for tab tyles, but we prefer to show our
+   * icon
+   */
+  return TRUE;
+}
+
 
 /*  public functions  */
 
diff --git a/app/widgets/widgets-enums.c b/app/widgets/widgets-enums.c
index 826edb1..9f6d07b 100644
--- a/app/widgets/widgets-enums.c
+++ b/app/widgets/widgets-enums.c
@@ -205,6 +205,8 @@ gimp_tab_style_get_type (void)
     { GIMP_TAB_STYLE_ICON_BLURB, "GIMP_TAB_STYLE_ICON_BLURB", "icon-blurb" },
     { GIMP_TAB_STYLE_PREVIEW_NAME, "GIMP_TAB_STYLE_PREVIEW_NAME", "preview-name" },
     { GIMP_TAB_STYLE_PREVIEW_BLURB, "GIMP_TAB_STYLE_PREVIEW_BLURB", "preview-blurb" },
+    { GIMP_TAB_STYLE_UNDEFINED, "GIMP_TAB_STYLE_UNDEFINED", "undefined" },
+    { GIMP_TAB_STYLE_AUTOMATIC, "GIMP_TAB_STYLE_AUTOMATIC", "automatic" },
     { 0, NULL, NULL }
   };
 
@@ -218,6 +220,8 @@ gimp_tab_style_get_type (void)
     { GIMP_TAB_STYLE_ICON_BLURB, NC_("tab-style", "Icon & desc"), NULL },
     { GIMP_TAB_STYLE_PREVIEW_NAME, NC_("tab-style", "Status & text"), NULL },
     { GIMP_TAB_STYLE_PREVIEW_BLURB, NC_("tab-style", "Status & desc"), NULL },
+    { GIMP_TAB_STYLE_UNDEFINED, NC_("tab-style", "Undefined"), NULL },
+    { GIMP_TAB_STYLE_AUTOMATIC, NC_("tab-style", "Automatic"), NULL },
     { 0, NULL, NULL }
   };
 
diff --git a/app/widgets/widgets-enums.h b/app/widgets/widgets-enums.h
index 56ffb4e..894214d 100644
--- a/app/widgets/widgets-enums.h
+++ b/app/widgets/widgets-enums.h
@@ -107,7 +107,9 @@ typedef enum
   GIMP_TAB_STYLE_ICON_NAME,     /*< desc="Icon & text"    >*/
   GIMP_TAB_STYLE_ICON_BLURB,    /*< desc="Icon & desc"    >*/
   GIMP_TAB_STYLE_PREVIEW_NAME,  /*< desc="Status & text"  >*/
-  GIMP_TAB_STYLE_PREVIEW_BLURB  /*< desc="Status & desc"  >*/
+  GIMP_TAB_STYLE_PREVIEW_BLURB, /*< desc="Status & desc"  >*/
+  GIMP_TAB_STYLE_UNDEFINED,     /*< desc="Undefined"      >*/
+  GIMP_TAB_STYLE_AUTOMATIC      /*< desc="Automatic"      >*/
 } GimpTabStyle;
 
 
diff --git a/etc/sessionrc b/etc/sessionrc
index f567316..f41c3bf 100644
--- a/etc/sessionrc
+++ b/etc/sessionrc
@@ -12,7 +12,7 @@
     (gimp-toolbox
         (book
             (dockable "gimp-tool-options"
-                (tab-style icon)))))
+                (tab-style automatic)))))
 (session-info "dock" "gimp-dock-window"
     (position -0 0)
     (size 210 820)
@@ -20,21 +20,21 @@
     (gimp-dock
         (book
             (dockable "gimp-layer-list"
-	        (tab-style icon))
+                (tab-style automatic))
             (dockable "gimp-channel-list"
-	        (tab-style icon))
+                (tab-style automatic))
             (dockable "gimp-vectors-list"
-	        (tab-style icon))
+                (tab-style automatic))
             (dockable "gimp-undo-history"
-	        (tab-style icon)))
+                (tab-style automatic)))
         (book
             (position 420)
             (dockable "gimp-brush-grid"
-	        (tab-style preview))
+                (tab-style automatic))
             (dockable "gimp-pattern-grid"
-	        (tab-style preview))
+                (tab-style automatic))
             (dockable "gimp-gradient-list"
-	        (tab-style preview)))))
+                (tab-style automatic)))))
 (session-info "toplevel"
     (factory-entry "gimp-empty-image-window")
     (position 410 370)
diff --git a/menus/dockable-menu.xml.in b/menus/dockable-menu.xml.in
index 29301d1..55eff39 100644
--- a/menus/dockable-menu.xml.in
+++ b/menus/dockable-menu.xml.in
@@ -27,6 +27,8 @@
       <menuitem action="dockable-tab-style-name" />
       <menuitem action="dockable-tab-style-icon-name" />
       <menuitem action="dockable-tab-style-preview-name" />
+      <separator />
+      <menuitem action="dockable-tab-style-automatic" />
     </menu>
     <menuitem action="dockable-view-type-list" />
     <menuitem action="dockable-view-type-grid" />



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