[gnome-builder] GbTabGrid: add tab grid for grids of GbTab



commit da2337774a86246d562f4f0e60c2ecc85b89d14f
Author: Christian Hergert <christian hergert me>
Date:   Mon Dec 1 19:44:53 2014 -0800

    GbTabGrid: add tab grid for grids of GbTab

 src/gnome-builder.mk   |    2 +
 src/tabs/gb-tab-grid.c |  881 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/tabs/gb-tab-grid.h |   71 ++++
 3 files changed, 954 insertions(+), 0 deletions(-)
---
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index db55c42..d57aa84 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -145,6 +145,8 @@ libgnome_builder_la_SOURCES = \
        src/tabs/gb-tab-label-private.h \
        src/tabs/gb-tab.c \
        src/tabs/gb-tab.h \
+       src/tabs/gb-tab-grid.c \
+       src/tabs/gb-tab-grid.h \
        src/tabs/gb-tab-stack.c \
        src/tabs/gb-tab-stack.h \
        src/theatrics/gb-box-theatric.c \
diff --git a/src/tabs/gb-tab-grid.c b/src/tabs/gb-tab-grid.c
new file mode 100644
index 0000000..1615d8b
--- /dev/null
+++ b/src/tabs/gb-tab-grid.c
@@ -0,0 +1,881 @@
+/* gb-tab-grid.c
+ *
+ * Copyright (C) 2011 Christian Hergert <chris dronelabs com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "tab-grid"
+
+#include <glib/gi18n.h>
+
+#include "gb-log.h"
+#include "gb-tab.h"
+#include "gb-tab-stack.h"
+#include "gb-tab-grid.h"
+
+struct _GbTabGridPrivate
+{
+  GSimpleActionGroup *actions;
+
+  GtkWidget          *top_hpaned;
+  GbTabStack         *last_focused_stack;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbTabGrid, gb_tab_grid, GTK_TYPE_BIN)
+
+static GtkWidget *
+gb_tab_grid_get_first_stack (GbTabGrid*);
+
+static void
+gb_tab_grid_set_last_focused (GbTabGrid  *grid,
+                              GbTabStack *stack)
+{
+  GbTabGridPrivate *priv;
+
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+  g_return_if_fail (!stack || GB_TAB_STACK (stack));
+
+  priv = grid->priv;
+
+  if (priv->last_focused_stack)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (priv->last_focused_stack),
+                                    (gpointer *)&priv->last_focused_stack);
+      priv->last_focused_stack = NULL;
+    }
+
+  if (stack)
+    {
+      priv->last_focused_stack = stack;
+      g_object_add_weak_pointer (G_OBJECT (stack),
+                                 (gpointer *)&priv->last_focused_stack);
+    }
+}
+
+GtkWidget *
+gb_tab_grid_new (void)
+{
+  return g_object_new (GB_TYPE_TAB_GRID, NULL);
+}
+
+static void
+gb_tab_grid_remove_empty (GbTabGrid *self)
+{
+  GbTabGridPrivate *priv;
+  GtkWidget *paned;
+  GtkWidget *stack;
+  GtkWidget *parent;
+  GtkWidget *child;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  priv = self->priv;
+
+  paned = gtk_paned_get_child2 (GTK_PANED (priv->top_hpaned));
+  g_assert (GTK_IS_PANED (paned));
+
+  while (paned)
+    {
+      stack = gtk_paned_get_child1 (GTK_PANED (paned));
+      g_assert (GB_IS_TAB_STACK (stack));
+
+      if (!gb_tab_stack_get_n_tabs (GB_TAB_STACK (stack)))
+        {
+          child = gtk_paned_get_child2 (GTK_PANED (paned));
+          g_object_ref (child);
+          parent = gtk_widget_get_parent (paned);
+          gtk_container_remove (GTK_CONTAINER (paned), child);
+          gtk_container_remove (GTK_CONTAINER (parent), paned);
+          gtk_paned_add2 (GTK_PANED (parent), child);
+          g_object_unref (child);
+          paned = parent;
+        }
+
+      paned = gtk_paned_get_child2 (GTK_PANED (paned));
+    }
+
+  /*
+   * If everything got removed, re-add a default stack.
+   */
+  if (!gtk_paned_get_child2 (GTK_PANED (priv->top_hpaned)))
+    (void)gb_tab_grid_get_first_stack (self);
+
+  EXIT;
+}
+
+static GtkWidget *
+gb_tab_grid_get_first_stack (GbTabGrid *self)
+{
+  GbTabGridPrivate *priv;
+  GtkWidget *child;
+  GtkWidget *paned;
+
+  ENTRY;
+
+  g_return_val_if_fail (GB_IS_TAB_GRID (self), NULL);
+
+  priv = self->priv;
+
+  if (!(paned = gtk_paned_get_child2 (GTK_PANED (priv->top_hpaned))))
+    {
+      paned = g_object_new (GTK_TYPE_PANED,
+                            "orientation", GTK_ORIENTATION_HORIZONTAL,
+                            "visible", TRUE,
+                            NULL);
+      gtk_paned_add2 (GTK_PANED (priv->top_hpaned), paned);
+      gtk_container_child_set (GTK_CONTAINER (priv->top_hpaned), paned,
+                               "resize", TRUE,
+                               "shrink", FALSE,
+                               NULL);
+      child = g_object_new (GB_TYPE_TAB_STACK,
+                            "visible", TRUE,
+                            NULL);
+      g_signal_connect_swapped (child, "changed",
+                                G_CALLBACK (gb_tab_grid_remove_empty),
+                                self);
+      gtk_paned_add1 (GTK_PANED (paned), child);
+    }
+
+  child = gtk_paned_get_child1 (GTK_PANED (paned));
+
+  RETURN (child);
+}
+
+static GbTabStack *
+gb_tab_grid_get_last_focused (GbTabGrid *grid)
+{
+  g_return_val_if_fail (GB_IS_TAB_GRID (grid), NULL);
+
+  if (!grid->priv->last_focused_stack)
+    return (GbTabStack *)gb_tab_grid_get_first_stack (grid);
+
+  return grid->priv->last_focused_stack;
+}
+
+GbTab *
+gb_tab_grid_get_active (GbTabGrid *grid)
+{
+  GbTabStack *last_focused_stack;
+  GbTab *ret = NULL;
+
+  g_return_val_if_fail (GB_IS_TAB_GRID (grid), NULL);
+
+  last_focused_stack = gb_tab_grid_get_last_focused (grid);
+
+  if (last_focused_stack)
+    ret = gb_tab_stack_get_active (last_focused_stack);
+
+  return ret;
+}
+
+static void
+gb_tab_grid_add (GtkContainer *container,
+                 GtkWidget    *child)
+{
+  GbTabGrid *self = (GbTabGrid *) container;
+  GbTabStack *stack = NULL;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+  g_return_if_fail (GTK_IS_WIDGET (child));
+
+  if (GB_IS_TAB (child))
+    {
+      stack = gb_tab_grid_get_last_focused (self);
+      gtk_container_add (GTK_CONTAINER (stack), child);
+    }
+  else
+    gtk_paned_add1 (GTK_PANED (self->priv->top_hpaned), child);
+}
+
+static GList *
+gb_tab_grid_get_stacks (GbTabGrid *self)
+{
+  GbTabGridPrivate *priv;
+  GtkWidget *child;
+  GtkWidget *paned;
+  GList *list = NULL;
+
+  ENTRY;
+
+  g_return_val_if_fail (GB_IS_TAB_GRID (self), NULL);
+
+  priv = self->priv;
+
+  paned = priv->top_hpaned;
+
+  for (; paned; paned = gtk_paned_get_child2 (GTK_PANED (paned)))
+    {
+      child = gtk_paned_get_child1 (GTK_PANED (paned));
+      if (GB_IS_TAB_STACK (child))
+        list = g_list_append (list, child);
+    }
+
+  RETURN (list);
+}
+
+GList *
+gb_tab_grid_get_tabs (GbTabGrid *self)
+{
+  GList *stacks;
+  GList *iter;
+  GList *ret = NULL;
+
+  ENTRY;
+
+  g_return_val_if_fail (GB_IS_TAB_GRID (self), NULL);
+
+  stacks = gb_tab_grid_get_stacks (self);
+  for (iter = stacks; iter; iter = iter->next)
+    ret = g_list_concat (ret, gb_tab_stack_get_tabs (iter->data));
+  g_list_free (stacks);
+
+  RETURN (ret);
+}
+
+static void
+gb_tab_grid_realign (GbTabGrid *self)
+{
+  GbTabGridPrivate *priv;
+  GtkAllocation alloc;
+  GtkWidget *paned;
+  guint n_paneds = 0;
+  guint width;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  priv = self->priv;
+
+  paned = gtk_paned_get_child2 (GTK_PANED (priv->top_hpaned));
+  do
+    n_paneds++;
+  while ((paned = gtk_paned_get_child2 (GTK_PANED (paned))));
+  g_assert_cmpint (n_paneds, >, 0);
+
+  paned = gtk_paned_get_child2 (GTK_PANED (priv->top_hpaned));
+  gtk_widget_get_allocation (paned, &alloc);
+  width = alloc.width / n_paneds;
+
+  do
+    gtk_paned_set_position (GTK_PANED (paned), width);
+  while ((paned = gtk_paned_get_child2 (GTK_PANED (paned))));
+
+  EXIT;
+}
+
+static GbTabStack *
+gb_tab_grid_prepend_stack (GbTabGrid *self)
+{
+  GbTabGridPrivate *priv;
+  GtkWidget *stack;
+  GtkWidget *paned;
+  GtkWidget *child2;
+
+  ENTRY;
+
+  g_return_val_if_fail (GB_IS_TAB_GRID (self), NULL);
+
+  priv = self->priv;
+
+  stack = g_object_new (GB_TYPE_TAB_STACK,
+                        "visible", TRUE,
+                        NULL);
+  g_signal_connect_swapped (stack, "changed",
+                            G_CALLBACK (gb_tab_grid_remove_empty),
+                            self);
+  child2 = gtk_paned_get_child2 (GTK_PANED (priv->top_hpaned));
+  g_object_ref (child2);
+  gtk_container_remove (GTK_CONTAINER (priv->top_hpaned), child2);
+  paned = g_object_new (GTK_TYPE_PANED,
+                        "orientation", GTK_ORIENTATION_HORIZONTAL,
+                        "visible", TRUE,
+                        NULL);
+  gtk_paned_add1 (GTK_PANED (paned), stack);
+  gtk_paned_add2 (GTK_PANED (paned), child2);
+  gtk_container_child_set (GTK_CONTAINER (paned), stack,
+                           "resize", TRUE,
+                           "shrink", FALSE,
+                           NULL);
+  gtk_container_child_set (GTK_CONTAINER (paned), child2,
+                           "resize", TRUE,
+                           "shrink", FALSE,
+                           NULL);
+  g_object_unref (child2);
+  gtk_paned_add2 (GTK_PANED (priv->top_hpaned), paned);
+
+  gb_tab_grid_realign (self);
+
+  RETURN (GB_TAB_STACK (stack));
+}
+
+static GbTabStack *
+gb_tab_grid_add_stack (GbTabGrid *self)
+{
+  GbTabGridPrivate *priv;
+  GtkWidget *stack_paned;
+  GtkWidget *stack = NULL;
+  GtkWidget *paned;
+
+  ENTRY;
+
+  g_return_val_if_fail (GB_IS_TAB_GRID (self), NULL);
+
+  priv = self->priv;
+
+  stack = g_object_new (GB_TYPE_TAB_STACK,
+                        "visible", TRUE,
+                        NULL);
+  g_signal_connect_swapped (stack, "changed",
+                            G_CALLBACK (gb_tab_grid_remove_empty),
+                            self);
+
+  paned = priv->top_hpaned;
+  while (gtk_paned_get_child2 (GTK_PANED (paned)))
+    paned = gtk_paned_get_child2 (GTK_PANED (paned));
+
+  stack_paned = g_object_new (GTK_TYPE_PANED,
+                              "orientation", GTK_ORIENTATION_HORIZONTAL,
+                              "visible", TRUE,
+                              NULL);
+  gtk_paned_add1 (GTK_PANED (stack_paned), stack);
+  gtk_container_child_set (GTK_CONTAINER (stack_paned), stack,
+                           "resize", TRUE,
+                           "shrink", FALSE,
+                           NULL);
+
+  gtk_paned_add2 (GTK_PANED (paned), stack_paned);
+  gtk_container_child_set (GTK_CONTAINER (paned), stack_paned,
+                           "resize", TRUE,
+                           "shrink", FALSE,
+                           NULL);
+
+  gb_tab_grid_realign (self);
+
+  RETURN (GB_TAB_STACK (stack));
+}
+
+void
+gb_tab_grid_move_tab_right (GbTabGrid *self,
+                            GbTab     *tab)
+{
+  GbTabStack *stack;
+  GList *iter;
+  GList *stacks;
+  GList *children;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+  g_return_if_fail (GB_IS_TAB (tab));
+
+  stacks = gb_tab_grid_get_stacks (self);
+
+  for (iter = stacks; iter; iter = iter->next)
+    {
+      if (gb_tab_stack_contains_tab (iter->data, tab))
+        {
+          /* if we are the last stack and this is the last item,
+           * we can short circuit and do nothing.
+           */
+          if (!iter->next)
+            {
+              guint length;
+
+              children = gb_tab_stack_get_tabs (iter->data);
+              length = g_list_length (children);
+              g_list_free (children);
+
+              if (length == 1)
+                break;
+            }
+
+          g_object_ref (tab);
+          gb_tab_stack_remove_tab (iter->data, tab);
+          if (!iter->next)
+            stack = gb_tab_grid_add_stack (self);
+          else
+            stack = iter->next->data;
+          gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (tab));
+          gtk_widget_grab_focus (GTK_WIDGET (tab));
+          g_object_unref (tab);
+          break;
+        }
+    }
+
+  g_list_free (stacks);
+
+  gb_tab_grid_remove_empty (self);
+
+  EXIT;
+}
+
+void
+gb_tab_grid_move_tab_left (GbTabGrid *self,
+                           GbTab     *tab)
+{
+  GbTabStack *stack;
+  GList *iter;
+  GList *stacks;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+  g_return_if_fail (GB_IS_TAB (tab));
+
+  stacks = gb_tab_grid_get_stacks (self);
+
+  for (iter = stacks; iter; iter = iter->next)
+    {
+      if (gb_tab_stack_contains_tab (iter->data, tab))
+        {
+          g_object_ref (tab);
+          gb_tab_stack_remove_tab (iter->data, tab);
+          if (!iter->prev)
+            stack = gb_tab_grid_prepend_stack (self);
+          else
+            stack = iter->prev->data;
+          gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (tab));
+          gtk_widget_grab_focus (GTK_WIDGET (tab));
+          g_object_unref (tab);
+          break;
+        }
+    }
+
+  g_list_free (stacks);
+
+  gb_tab_grid_remove_empty (self);
+
+  EXIT;
+}
+
+void
+gb_tab_grid_focus_next_view (GbTabGrid *self,
+                             GbTab     *tab)
+{
+  GList *iter;
+  GList *stacks;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+  g_return_if_fail (GB_IS_TAB (tab));
+
+  /* TODO: track focus so we can drop @tab parameter */
+
+  stacks = gb_tab_grid_get_stacks (self);
+  for (iter = stacks; iter; iter = iter->next)
+    {
+      if (gb_tab_stack_contains_tab (iter->data, tab))
+        {
+          if (!gb_tab_stack_focus_next (iter->data))
+            if (iter->next)
+              gb_tab_stack_focus_first (iter->next->data);
+
+          break;
+        }
+    }
+  g_list_free (stacks);
+
+  EXIT;
+}
+
+void
+gb_tab_grid_focus_previous_view (GbTabGrid *self,
+                                 GbTab     *view)
+{
+  GList *iter;
+  GList *stacks;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+  g_return_if_fail (GB_IS_TAB (view));
+
+  /* TODO: track focus so we can drop @tab parameter */
+
+  stacks = gb_tab_grid_get_stacks (self);
+  stacks = g_list_reverse (stacks);
+  for (iter = stacks; iter; iter = iter->next)
+    {
+      if (gb_tab_stack_contains_tab (iter->data, view))
+        {
+          gb_tab_stack_focus_previous (iter->data);
+          break;
+        }
+    }
+  g_list_free (stacks);
+
+  EXIT;
+}
+
+static void
+on_next_tab (GSimpleAction *action,
+             GVariant      *parameters,
+             gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+  if (last_focused_stack)
+    if (!gb_tab_stack_focus_next (last_focused_stack))
+      gb_tab_stack_focus_first  (last_focused_stack);
+
+  EXIT;
+}
+
+static void
+on_previous_tab (GSimpleAction *action,
+                 GVariant      *parameters,
+                 gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+  if (last_focused_stack)
+    if (!gb_tab_stack_focus_previous (last_focused_stack))
+      gb_tab_stack_focus_last (last_focused_stack);
+
+  EXIT;
+}
+
+static void
+on_focus_tab_left (GSimpleAction *action,
+                   GVariant      *parameters,
+                   gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+  GList *stacks;
+  GList *iter;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+  stacks = gb_tab_grid_get_stacks (self);
+
+  for (iter = stacks; iter; iter = iter->next)
+    {
+      if ((iter->data == (void *)last_focused_stack) &&
+          iter->prev && iter->prev->data)
+        {
+          gtk_widget_grab_focus (iter->prev->data);
+          break;
+        }
+    }
+
+  g_list_free (stacks);
+
+  EXIT;
+}
+
+static void
+on_focus_tab_right (GSimpleAction *action,
+                    GVariant      *parameters,
+                    gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+  GList *stacks;
+  GList *iter;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+  stacks = gb_tab_grid_get_stacks (self);
+
+  for (iter = stacks; iter; iter = iter->next)
+    {
+      if ((iter->data == (void *)last_focused_stack) &&
+          iter->next && iter->next->data)
+        {
+          gtk_widget_grab_focus (iter->next->data);
+          break;
+        }
+    }
+
+  g_list_free (stacks);
+
+  EXIT;
+}
+
+static void
+on_move_right (GSimpleAction *action,
+               GVariant      *parameters,
+               gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+
+  if (GB_IS_TAB_STACK (last_focused_stack))
+    {
+      GbTab *active;
+
+      active = gb_tab_stack_get_active (last_focused_stack);
+
+      if (active)
+        gb_tab_grid_move_tab_right (self, active);
+    }
+
+  EXIT;
+}
+
+static void
+on_move_left (GSimpleAction *action,
+              GVariant      *parameters,
+              gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+
+  if (GB_IS_TAB_STACK (last_focused_stack))
+    {
+      GbTab *active;
+
+      active = gb_tab_stack_get_active (last_focused_stack);
+
+      if (active)
+        gb_tab_grid_move_tab_left (self, active);
+    }
+
+  EXIT;
+}
+
+static void
+gb_tab_grid_on_set_focus (GbTabGrid *grid,
+                          GtkWidget *widget,
+                          GtkWindow *window)
+{
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  if (widget)
+    {
+      while (widget && !GB_IS_TAB_STACK (widget))
+        widget = gtk_widget_get_parent (widget);
+
+      if (GB_IS_TAB_STACK (widget))
+        gb_tab_grid_set_last_focused (grid, GB_TAB_STACK (widget));
+    }
+}
+
+static void
+gb_tab_grid_realize (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+  GbTabGrid *grid = (GbTabGrid *)widget;
+
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+
+  GTK_WIDGET_CLASS (gb_tab_grid_parent_class)->realize (widget);
+
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  if (GTK_IS_WINDOW (toplevel))
+    {
+      /*
+       * WORKAROUND:
+       *
+       * We need to register our actions with the toplevel or they wont be
+       * taken into account when activating accelerators. See bugzilla bug
+       * 740682 for a patch to Gtk+.
+       */
+      gtk_widget_insert_action_group (toplevel, "tabs",
+                                      G_ACTION_GROUP (grid->priv->actions));
+
+      /*
+       * Track focus so we know where we are moving to/from in the stack.
+       */
+      g_signal_connect_object (toplevel,
+                               "set-focus",
+                               G_CALLBACK (gb_tab_grid_on_set_focus),
+                               widget,
+                               G_CONNECT_SWAPPED);
+    }
+}
+
+void
+gb_tab_grid_focus_tab (GbTabGrid *grid,
+                       GbTab     *tab)
+{
+  GList *stacks;
+  GList *iter;
+
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+  g_return_if_fail (GB_IS_TAB (tab));
+
+  stacks = gb_tab_grid_get_stacks (grid);
+
+  for (iter = stacks; iter; iter = iter->next)
+    {
+      if (gb_tab_stack_contains_tab (iter->data, tab))
+        {
+          gb_tab_stack_focus_tab (iter->data, tab);
+          break;
+        }
+    }
+}
+
+GbTab *
+gb_tab_grid_find_tab_typed (GbTabGrid *grid,
+                            GType      type)
+{
+  GbTab *ret = NULL;
+  GList *list;
+  GList *iter;
+
+  g_return_val_if_fail (GB_IS_TAB_GRID (grid), NULL);
+  g_return_val_if_fail (g_type_is_a (type, GB_TYPE_TAB), NULL);
+
+  list = gb_tab_grid_get_tabs (grid);
+
+  for (iter = list; iter; iter = iter->next)
+    {
+      if (g_type_is_a (G_TYPE_FROM_INSTANCE (iter->data), type))
+        {
+          ret = iter->data;
+          break;
+        }
+    }
+
+  g_list_free (list);
+
+  return ret;
+}
+
+static void
+gb_tab_grid_grab_focus (GtkWidget *widget)
+{
+  GbTabGrid *grid = (GbTabGrid *)widget;
+  GbTabStack *stack;
+
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+
+  stack = gb_tab_grid_get_last_focused (grid);
+
+  gtk_widget_grab_focus (GTK_WIDGET (stack));
+}
+
+static void
+gb_tab_grid_finalize (GObject *object)
+{
+  GbTabGridPrivate *priv = GB_TAB_GRID (object)->priv;
+
+  g_clear_object (&priv->actions);
+
+  G_OBJECT_CLASS (gb_tab_grid_parent_class)->finalize (object);
+}
+
+/**
+ * gb_tab_grid_class_init:
+ * @klass: (in): A #GbTabGridClass.
+ *
+ * Initializes the #GbTabGridClass and prepares the vtable.
+ */
+static void
+gb_tab_grid_class_init (GbTabGridClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  object_class->finalize = gb_tab_grid_finalize;
+
+  widget_class->realize = gb_tab_grid_realize;
+  widget_class->grab_focus = gb_tab_grid_grab_focus;
+
+  container_class->add = gb_tab_grid_add;
+}
+
+/**
+ * gb_tab_grid_init:
+ * @self: (in): A #GbTabGrid.
+ *
+ * Initializes the newly created #GbTabGrid instance.
+ */
+static void
+gb_tab_grid_init (GbTabGrid *self)
+{
+  static const GActionEntry entries[] = {
+    { "next", on_next_tab },
+    { "previous", on_previous_tab },
+    { "right", on_focus_tab_right },
+    { "left", on_focus_tab_left },
+    { "move-right", on_move_right },
+    { "move-left", on_move_left },
+  };
+  GtkWidget *paned;
+  GtkWidget *stack;
+
+  self->priv = gb_tab_grid_get_instance_private (self);
+
+  self->priv->actions = g_simple_action_group_new ();
+  g_action_map_add_action_entries (G_ACTION_MAP (self->priv->actions),
+                                   entries, G_N_ELEMENTS (entries), self);
+
+  self->priv->top_hpaned =
+    g_object_new (GTK_TYPE_PANED,
+                  "orientation", GTK_ORIENTATION_HORIZONTAL,
+                  "visible", TRUE,
+                  NULL);
+  GTK_CONTAINER_CLASS (gb_tab_grid_parent_class)->add (GTK_CONTAINER (self),
+                                                       self->priv->top_hpaned);
+
+  paned = g_object_new (GTK_TYPE_PANED,
+                        "orientation", GTK_ORIENTATION_HORIZONTAL,
+                        "visible", TRUE,
+                        NULL);
+  gtk_paned_add2 (GTK_PANED (self->priv->top_hpaned), paned);
+
+  stack = g_object_new (GB_TYPE_TAB_STACK,
+                        "visible", TRUE,
+                        NULL);
+  g_signal_connect_swapped (stack, "changed",
+                            G_CALLBACK (gb_tab_grid_remove_empty),
+                            self);
+  gtk_paned_add1 (GTK_PANED (paned), stack);
+  gtk_container_child_set (GTK_CONTAINER (paned), stack,
+                           "resize", TRUE,
+                           "shrink", FALSE,
+                           NULL);
+}
diff --git a/src/tabs/gb-tab-grid.h b/src/tabs/gb-tab-grid.h
new file mode 100644
index 0000000..fa75f6b
--- /dev/null
+++ b/src/tabs/gb-tab-grid.h
@@ -0,0 +1,71 @@
+/* gb-tab-grid.h
+ *
+ * Copyright (C) 2011 Christian Hergert <chris dronelabs com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_TAB_GRID_H
+#define GB_TAB_GRID_H
+
+#include <gtk/gtk.h>
+
+#include "gb-tab.h"
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_TAB_GRID            (gb_tab_grid_get_type())
+#define GB_TAB_GRID(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_TAB_GRID, GbTabGrid))
+#define GB_TAB_GRID_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_TAB_GRID, GbTabGrid const))
+#define GB_TAB_GRID_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GB_TYPE_TAB_GRID, GbTabGridClass))
+#define GB_IS_TAB_GRID(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GB_TYPE_TAB_GRID))
+#define GB_IS_TAB_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GB_TYPE_TAB_GRID))
+#define GB_TAB_GRID_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GB_TYPE_TAB_GRID, GbTabGridClass))
+
+typedef struct _GbTabGrid        GbTabGrid;
+typedef struct _GbTabGridClass   GbTabGridClass;
+typedef struct _GbTabGridPrivate GbTabGridPrivate;
+
+struct _GbTabGrid
+{
+   GtkBin parent;
+
+   /*< private >*/
+   GbTabGridPrivate *priv;
+};
+
+struct _GbTabGridClass
+{
+   GtkBinClass parent_class;
+};
+
+GtkWidget *gb_tab_grid_new                (void);
+GType      gb_tab_grid_get_type           (void) G_GNUC_CONST;
+GbTab     *gb_tab_grid_get_active         (GbTabGrid *grid);
+void       gb_tab_grid_focus_tab          (GbTabGrid *grid,
+                                           GbTab     *tab);
+void       gb_tab_grid_move_tab_left      (GbTabGrid *grid,
+                                           GbTab     *tab);
+void       gb_tab_grid_move_tab_right     (GbTabGrid *grid,
+                                           GbTab     *tab);
+void       gb_tab_grid_focus_next_tab     (GbTabGrid *grid,
+                                           GbTab     *tab);
+void       gb_tab_grid_focus_previous_tab (GbTabGrid *grid,
+                                           GbTab     *tab);
+GbTab     *gb_tab_grid_find_tab_typed     (GbTabGrid *grid,
+                                           GType      type);
+
+G_END_DECLS
+
+#endif /* GB_TAB_GRID_H */


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