[gnome-builder/editor-layout] wip on new tab design
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/editor-layout] wip on new tab design
- Date: Wed, 26 Nov 2014 03:48:20 +0000 (UTC)
commit 5bd5794a867ed8d2410ed98fe5c11c0ac176eb7b
Author: Christian Hergert <christian hergert me>
Date: Mon Nov 24 20:35:15 2014 -0800
wip on new tab design
src/editor/gb-editor-workspace.c | 2 +
src/gnome-builder.mk | 4 +
src/resources/css/builder.Adwaita.css | 14 +-
src/resources/ui/gb-editor-workspace.ui | 7 +
src/resources/ui/gb-tab.ui | 32 +-
src/tabs/gb-tab-grid.c | 441 +++++++++++++++++++
src/tabs/gb-tab-grid.h | 62 +++
src/tabs/gb-tab-stack.c | 713 +++++++++++++++++++++++++++++++
src/tabs/gb-tab-stack.h | 69 +++
src/tabs/gb-tab.c | 6 +
src/tabs/gb-tab.h | 1 +
11 files changed, 1330 insertions(+), 21 deletions(-)
---
diff --git a/src/editor/gb-editor-workspace.c b/src/editor/gb-editor-workspace.c
index eeaeed0..78d6adc 100644
--- a/src/editor/gb-editor-workspace.c
+++ b/src/editor/gb-editor-workspace.c
@@ -22,6 +22,7 @@
#include "gb-editor-workspace.h"
#include "gb-editor-workspace-private.h"
#include "gb-multi-notebook.h"
+#include "gb-tab-grid.h"
#include "gb-tree.h"
enum {
@@ -111,6 +112,7 @@ gb_editor_workspace_class_init (GbEditorWorkspaceClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GbEditorWorkspace, paned);
g_type_ensure (GB_TYPE_MULTI_NOTEBOOK);
+ g_type_ensure (GB_TYPE_TAB_GRID);
g_type_ensure (GB_TYPE_TREE);
}
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index 18e42f7..e68d797 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -145,6 +145,10 @@ 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 \
src/theatrics/gb-box-theatric.h \
src/tree/gb-tree.c \
diff --git a/src/resources/css/builder.Adwaita.css b/src/resources/css/builder.Adwaita.css
index 8675466..2fc5ad8 100644
--- a/src/resources/css/builder.Adwaita.css
+++ b/src/resources/css/builder.Adwaita.css
@@ -172,16 +172,18 @@ GbWorkbench GtkHeaderBar {
/*
* Tab header styling.
*/
-GbTab .tab-close-button {
- border-right: none;
- border-top-right-radius: 0px;
- border-bottom-right-radius: 0px;
-}
-GbTab .tab-drag-button {
+GtkComboBox.tab-header-first .button {
border-left: none;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
}
+.tab-header-last {
+ border-right: none;
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
+}
+
+
#project-title {
border-left: none;
diff --git a/src/resources/ui/gb-editor-workspace.ui b/src/resources/ui/gb-editor-workspace.ui
index 083c1ce..9b7eb13 100644
--- a/src/resources/ui/gb-editor-workspace.ui
+++ b/src/resources/ui/gb-editor-workspace.ui
@@ -37,8 +37,15 @@
</object>
</child>
<child>
+ <object class="GbTabGrid" id="tab_grid">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ <child>
<object class="GbMultiNotebook" id="multi_notebook">
<property name="group-name">GB_EDITOR_WORKSPACE</property>
+ <property name="hexpand">True</property>
<property name="visible">True</property>
<property name="show-tabs">False</property>
</object>
diff --git a/src/resources/ui/gb-tab.ui b/src/resources/ui/gb-tab.ui
index c879154..2c43c83 100644
--- a/src/resources/ui/gb-tab.ui
+++ b/src/resources/ui/gb-tab.ui
@@ -11,12 +11,26 @@
<class name="linked"/>
</style>
<child>
+ <object class="GtkComboBox" id="documents_combo">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <style>
+ <class name="tab-header-first"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="symbols_combo">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ <child>
<object class="GtkButton" id="drag_button">
<property name="visible">True</property>
<property name="hexpand">False</property>
<style>
<class name="image-button"/>
- <class name="tab-drag-button"/>
</style>
<child>
<object class="GtkImage">
@@ -28,23 +42,12 @@
</object>
</child>
<child>
- <object class="GtkComboBox" id="documents_combo">
- <property name="visible">True</property>
- <property name="hexpand">True</property>
- </object>
- </child>
- <child>
- <object class="GtkComboBox" id="symbols_combo">
- <property name="visible">True</property>
- <property name="hexpand">True</property>
- </object>
- </child>
- <child>
<object class="GtkButton" id="split_button">
<property name="visible">True</property>
<property name="hexpand">False</property>
<style>
<class name="image-button"/>
+ <class name="tab-header-last"/>
</style>
<child>
<object class="GtkImage">
@@ -57,10 +60,9 @@
</child>
<child>
<object class="GtkButton" id="close_button">
- <property name="visible">True</property>
+ <property name="visible">False</property>
<property name="hexpand">False</property>
<style>
- <class name="tab-close-button"/>
<class name="image-button"/>
</style>
<child>
diff --git a/src/tabs/gb-tab-grid.c b/src/tabs/gb-tab-grid.c
new file mode 100644
index 0000000..c04372d
--- /dev/null
+++ b/src/tabs/gb-tab-grid.c
@@ -0,0 +1,441 @@
+/* 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/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "gb-log.h"
+#include "gb-tab.h"
+#include "gb-tab-stack.h"
+#include "gb-tab-grid.h"
+
+struct _GbTabGridPrivate
+{
+ GtkWidget *top_hpaned;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbTabGrid, gb_tab_grid, GTK_TYPE_BIN)
+
+static GtkWidget *
+gb_tab_grid_get_first_stack (GbTabGrid*);
+
+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 void
+gb_tab_grid_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ GbTabGridPrivate *priv;
+ GbTabGrid *self = (GbTabGrid *) container;
+ GtkWidget *stack = NULL;
+ GtkWidget *toplevel;
+
+ g_return_if_fail (GB_IS_TAB_GRID (self));
+ g_return_if_fail (GTK_IS_WIDGET (child));
+
+ priv = self->priv;
+
+ if (GB_IS_TAB (child))
+ {
+ /*
+ * Try to find the currently focused view.
+ */
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+ if (toplevel && GTK_IS_WINDOW (toplevel))
+ {
+ if ((stack = gtk_window_get_focus (GTK_WINDOW (toplevel))))
+ while (stack && !GB_IS_TAB_STACK (stack))
+ stack = gtk_widget_get_parent (stack);
+
+ }
+
+ if (!stack)
+ stack = gb_tab_grid_get_first_stack (self);
+
+ gtk_container_add (GTK_CONTAINER (stack), child);
+ }
+ else
+ gtk_paned_add1 (GTK_PANED (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_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;
+
+ 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->next)
+ stack = gb_tab_grid_add_stack (self);
+ else
+ stack = iter->next->data;
+#if 0
+ gb_tab_stack_add_tab (stack, tab);
+#endif
+ gtk_container_add (GTK_CONTAINER (stack), 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;
+}
+
+/**
+ * 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)
+{
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+ 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)
+{
+ GtkWidget *paned;
+ GtkWidget *stack;
+
+ self->priv = gb_tab_grid_get_instance_private (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..cebb5a0
--- /dev/null
+++ b/src/tabs/gb-tab-grid.h
@@ -0,0 +1,62 @@
+/* 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>
+
+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;
+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);
+
+G_END_DECLS
+
+#endif /* GB_TAB_GRID_H */
diff --git a/src/tabs/gb-tab-stack.c b/src/tabs/gb-tab-stack.c
new file mode 100644
index 0000000..16f6039
--- /dev/null
+++ b/src/tabs/gb-tab-stack.c
@@ -0,0 +1,713 @@
+/* gb-tab-stack.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/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "gb-log.h"
+#include "gb-tab.h"
+#include "gb-tab-stack.h"
+
+struct _GbTabStackPrivate
+{
+ GtkListStore *tabs;
+ GtkWidget *combo;
+ GtkWidget *controls;
+ GtkWidget *close;
+ GtkWidget *notebook;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbTabStack, gb_tab_stack, GTK_TYPE_BOX)
+
+enum
+{
+ PROP_0,
+ LAST_PROP
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+//static GParamSpec *gParamSpecs[LAST_PROP];
+static guint gSignals[LAST_SIGNAL];
+
+guint
+gb_tab_stack_get_n_tabs (GbTabStack *stack)
+{
+ guint ret;
+
+ ENTRY;
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), 0);
+ ret = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(stack->priv->tabs), NULL);
+ RETURN(ret);
+}
+
+gboolean
+gb_tab_stack_focus_first (GbTabStack *stack)
+{
+ GbTabStackPrivate *priv;
+ GtkTreeIter iter;
+
+ ENTRY;
+
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), FALSE);
+
+ priv = stack->priv;
+
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->tabs), &iter)) {
+ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combo), &iter);
+ RETURN(TRUE);
+ }
+
+ RETURN(FALSE);
+}
+
+gboolean
+gb_tab_stack_focus_next (GbTabStack *stack)
+{
+ GbTabStackPrivate *priv;
+ guint n_tabs;
+ gint idx;
+
+ ENTRY;
+
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), FALSE);
+
+ priv = stack->priv;
+
+ if ((idx = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combo))) >= 0) {
+ n_tabs = gb_tab_stack_get_n_tabs(stack);
+ if ((idx + 1) < n_tabs) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combo), idx + 1);
+ RETURN(TRUE);
+ }
+ }
+
+ RETURN(FALSE);
+}
+
+gboolean
+gb_tab_stack_focus_previous (GbTabStack *stack)
+{
+ GbTabStackPrivate *priv;
+ gint idx;
+
+ ENTRY;
+
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), FALSE);
+
+ priv = stack->priv;
+
+ if ((idx = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combo))) > 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combo), idx - 1);
+ RETURN(TRUE);
+ }
+
+ RETURN(FALSE);
+}
+
+gboolean
+gb_tab_stack_focus_view (GbTabStack *stack,
+ GbTab *view)
+{
+ GbTabStackPrivate *priv;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GObject *object;
+
+ ENTRY;
+
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), FALSE);
+
+ priv = stack->priv;
+
+ model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->combo));
+
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ gtk_tree_model_get(model, &iter, 0, &object, -1);
+ g_object_unref(object);
+ if (object == (GObject *)view) {
+ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combo), &iter);
+ RETURN(TRUE);
+ }
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ RETURN(FALSE);
+}
+
+gboolean
+gb_tab_stack_contains_tab (GbTabStack *stack,
+ GbTab *view)
+{
+ GbTabStackPrivate *priv;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GObject *object;
+
+ ENTRY;
+
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), FALSE);
+ g_return_val_if_fail(GB_IS_TAB(view), FALSE);
+
+ priv = stack->priv;
+
+ model = GTK_TREE_MODEL(priv->tabs);
+
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ gtk_tree_model_get(model, &iter, 0, &object, -1);
+ g_object_unref(object);
+ if (object == (GObject *)view) {
+ RETURN(TRUE);
+ }
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ RETURN(FALSE);
+}
+
+static void
+gb_tab_stack_set_page (GbTabStack *stack,
+ gint page)
+{
+ GbTabStackPrivate *priv;
+ GtkWidget *controls;
+ GtkWidget *view;
+ gint current;
+
+ ENTRY;
+
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+ g_return_if_fail(page >= 0);
+
+ priv = stack->priv;
+
+ current = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->controls));
+ if (current >= 0) {
+ controls = gtk_notebook_get_nth_page(GTK_NOTEBOOK(priv->controls), current);
+ gtk_widget_hide(controls);
+ }
+
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), page);
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->controls), page);
+
+ if ((controls = gtk_notebook_get_nth_page(GTK_NOTEBOOK(priv->controls), page))) {
+ gtk_widget_show(controls);
+ }
+
+ if ((view = gtk_notebook_get_nth_page(GTK_NOTEBOOK(priv->notebook), page))) {
+ gtk_widget_grab_focus(view);
+ }
+
+ EXIT;
+}
+
+void
+gb_tab_stack_remove_tab (GbTabStack *stack,
+ GbTab *view)
+{
+ GbTabStackPrivate *priv;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *controls;
+ GObject *object;
+ gint page = 0;
+ gboolean active = FALSE;
+
+ ENTRY;
+
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+ g_return_if_fail(GB_IS_TAB(view));
+
+ priv = stack->priv;
+
+ if (!gb_tab_stack_contains_tab (stack, view)) {
+ g_warning ("%s(): view is missing from stack.", G_STRFUNC);
+ EXIT;
+ }
+
+ /*
+ * TODO: Disconnect signals.
+ */
+
+ /*
+ * Remove the view from the drop down list.
+ */
+ model = GTK_TREE_MODEL(priv->tabs);
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ gtk_tree_model_get(model, &iter, 0, &object, -1);
+ g_object_unref(object);
+ if (object == (GObject *)view) {
+ active = (page == gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combo)));
+ gtk_list_store_remove(priv->tabs, &iter);
+ break;
+ }
+ page++;
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ /*
+ * Remove the controls from the notebook.
+ */
+ if ((controls = gb_tab_get_controls(view))) {
+ gtk_container_remove(GTK_CONTAINER(priv->controls), controls);
+ }
+
+ /*
+ * Remove the view from the notebook.
+ */
+ gtk_container_remove(GTK_CONTAINER(priv->notebook), GTK_WIDGET(view));
+
+ /*
+ * Hide the close button if there are no views left.
+ */
+ if (!gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook))) {
+ gtk_widget_hide(priv->close);
+ }
+
+ /*
+ * Try to set the page to the new item in the same slot if we can,
+ * otherwise the item previous.
+ */
+ if (active) {
+ page = MIN(page, gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)) - 1);
+ if (page >= 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combo), page);
+ }
+ }
+
+ g_signal_emit(stack, gSignals[CHANGED], 0);
+
+ EXIT;
+}
+
+static void
+gb_tab_stack_remove (GtkContainer *container,
+ GtkWidget *child)
+{
+ GbTabStack *stack = (GbTabStack *)container;
+
+ ENTRY;
+
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+ g_return_if_fail(GTK_IS_WIDGET(child));
+
+ if (GB_IS_TAB(child)) {
+ gb_tab_stack_remove_tab (stack, GB_TAB(child));
+ } else {
+ GTK_CONTAINER_CLASS(gb_tab_stack_parent_class)->remove(container, child);
+ }
+
+ EXIT;
+}
+
+/**
+ * gb_tab_stack_add_view:
+ * @stack: (in): A #GbTabStack.
+ * @view: (in): A #GbTab.
+ *
+ * Adds a view to the stack. An item is added to the #GtkComboBox
+ * for the view. When selected from the combo box, the view will be
+ * raised in the stack.
+ *
+ * The stack will take ownership of any of the controls provided by
+ * the view. In the case the view is removed from the stack, the
+ * controls will no longer be children of the stack. TODO
+ */
+static void
+gb_tab_stack_add_view (GbTabStack *stack,
+ GbTab *view)
+{
+ GbTabStackPrivate *priv;
+ GtkTreeIter iter;
+ gint page;
+
+ ENTRY;
+
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+ g_return_if_fail(GB_IS_TAB(view));
+
+ priv = stack->priv;
+
+ gtk_container_add(GTK_CONTAINER(priv->notebook), GTK_WIDGET(view));
+
+ gtk_list_store_append(priv->tabs, &iter);
+ gtk_list_store_set(priv->tabs, &iter, 0, view, -1);
+ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combo), &iter);
+ gtk_container_add(GTK_CONTAINER(priv->controls), gb_tab_get_controls(view));
+
+ g_signal_connect_swapped(view, "notify::can-save",
+ G_CALLBACK(gtk_widget_queue_draw),
+ priv->combo);
+ g_signal_connect_swapped(view, "notify::name",
+ G_CALLBACK(gtk_widget_queue_draw),
+ priv->combo);
+ g_signal_connect_swapped(view, "closed",
+ G_CALLBACK(gb_tab_stack_remove_tab),
+ stack);
+
+ if ((page = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)))) {
+ gb_tab_stack_set_page(stack, page - 1);
+ }
+
+ gtk_widget_show(priv->close);
+
+ g_signal_emit(stack, gSignals[CHANGED], 0);
+
+ EXIT;
+}
+
+/**
+ * gb_tab_stack_add:
+ * @container: (in): A #GbTabStack.
+ * @child: (in): A #GtkWidget.
+ *
+ * Handle the addition of a child to the view. If the child is a view,
+ * then we pack it into our internal notebook.
+ */
+static void
+gb_tab_stack_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ GbTabStack *stack = (GbTabStack *)container;
+
+ ENTRY;
+
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+ g_return_if_fail(GTK_IS_WIDGET(child));
+
+ if (GB_IS_TAB(child)) {
+ gb_tab_stack_add_view(stack, GB_TAB(child));
+ } else {
+ GTK_CONTAINER_CLASS(gb_tab_stack_parent_class)->add(container, child);
+ }
+
+ EXIT;
+}
+
+/**
+ * gb_tab_stack_get_active:
+ * @stack: (in): A #GbTabStack.
+ *
+ * Gets the active view based on the current focus.
+ *
+ * Returns: (transfer none): A #GtkWidget or %NULL.
+ */
+GtkWidget *
+gb_tab_stack_get_active (GbTabStack *stack)
+{
+ GtkWidget *toplevel;
+ GtkWidget *focus = NULL;
+
+ ENTRY;
+
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), NULL);
+
+ if ((toplevel = gtk_widget_get_toplevel(GTK_WIDGET(stack)))) {
+ if ((focus = gtk_window_get_focus(GTK_WINDOW(toplevel)))) {
+ while (focus && !GB_IS_TAB(focus)) {
+ focus = gtk_widget_get_parent(focus);
+ }
+ }
+ }
+
+ RETURN(focus);
+}
+
+static void
+gb_tab_stack_icon_name_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GbTab *view;
+
+ gtk_tree_model_get(tree_model, iter, 0, &view, -1);
+ g_assert(GB_IS_TAB(view));
+ g_object_set(cell, "icon-name", gb_tab_get_icon_name(view), NULL);
+ g_object_unref(view);
+}
+
+static void
+gb_tab_stack_name_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *text;
+ GbTab *view;
+
+ gtk_tree_model_get(tree_model, iter, 0, &view, -1);
+ g_assert(GB_IS_TAB(view));
+ text = g_strdup_printf("%s%s",
+ gb_tab_get_title (view),
+ gb_tab_get_dirty (view) ? " •" : "");
+ g_object_set(cell, "text", text, NULL);
+ g_object_unref(view);
+ g_free(text);
+}
+
+static void
+gb_tab_stack_combo_changed (GbTabStack *stack,
+ GtkComboBox *combo)
+{
+ gint page;
+
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+ g_return_if_fail(GTK_IS_COMBO_BOX(combo));
+
+ if ((page = gtk_combo_box_get_active(combo)) >= 0) {
+ gb_tab_stack_set_page(stack, page);
+ gtk_widget_grab_focus(stack->priv->notebook);
+ }
+}
+
+static void
+gb_tab_stack_grab_focus (GtkWidget *widget)
+{
+ GbTabStack *stack = (GbTabStack *)widget;
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+ gtk_widget_grab_focus(stack->priv->notebook);
+}
+
+static void
+gb_tab_stack_close_current (GbTabStack *stack,
+ GtkButton *button)
+{
+ GbTabStackPrivate *priv;
+ GtkWidget *child;
+ gint page;
+
+ ENTRY;
+
+ g_return_if_fail(GB_IS_TAB_STACK(stack));
+
+ priv = stack->priv;
+
+ page = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->notebook));
+ if (page >= 0) {
+ child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(priv->notebook), page);
+ if (GB_IS_TAB(child)) {
+ gb_tab_close(GB_TAB(child));
+ }
+ }
+
+ EXIT;
+}
+
+/**
+ * gb_tab_stack_get_tabs:
+ * @stack: (in): A #GbTabStack.
+ *
+ * Get all views in the stack.
+ *
+ * Returns: (transfer container) (element-type GbTab*): A #GList.
+ */
+GList *
+gb_tab_stack_get_tabs (GbTabStack *stack)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GbTab *view;
+ GList *ret = NULL;
+
+ ENTRY;
+
+ g_return_val_if_fail(GB_IS_TAB_STACK(stack), NULL);
+
+ model = GTK_TREE_MODEL(stack->priv->tabs);
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ /*
+ * The code below looks unsafe. However, the model holds our
+ * reference to the view, so it is fine.
+ */
+ gtk_tree_model_get(model, &iter, 0, &view, -1);
+ ret = g_list_append(ret, view);
+ g_object_unref(view);
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ RETURN(ret);
+}
+
+/**
+ * gb_tab_stack_finalize:
+ * @object: (in): A #GbTabStack.
+ *
+ * Finalizer for a #GbTabStack instance. Frees any resources held by
+ * the instance.
+ */
+static void
+gb_tab_stack_finalize (GObject *object)
+{
+ GbTabStackPrivate *priv;
+
+ ENTRY;
+
+ priv = GB_TAB_STACK(object)->priv;
+
+ g_clear_object(&priv->tabs);
+
+ G_OBJECT_CLASS(gb_tab_stack_parent_class)->finalize(object);
+
+ EXIT;
+}
+
+/**
+ * gb_tab_stack_class_init:
+ * @klass: (in): A #GbTabStackClass.
+ *
+ * Initializes the #GbTabStackClass and prepares the vtable.
+ */
+static void
+gb_tab_stack_class_init (GbTabStackClass *klass)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+ object_class->finalize = gb_tab_stack_finalize;
+
+ widget_class = GTK_WIDGET_CLASS(klass);
+ widget_class->grab_focus = gb_tab_stack_grab_focus;
+
+ container_class = GTK_CONTAINER_CLASS(klass);
+ container_class->add = gb_tab_stack_add;
+ container_class->remove = gb_tab_stack_remove;
+
+ /**
+ * GbTabStack::changed:
+ *
+ * The "changed" signal is emitted when the children of the stack have
+ * changed.
+ */
+ gSignals[CHANGED] = g_signal_new("changed",
+ GB_TYPE_TAB_STACK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+/**
+ * gb_tab_stack_init:
+ * @stack: (in): A #GbTabStack.
+ *
+ * Initializes the newly created #GbTabStack instance.
+ */
+static void
+gb_tab_stack_init (GbTabStack *stack)
+{
+ GbTabStackPrivate *priv;
+ GtkCellRenderer *cell;
+ GtkWidget *hbox;
+
+ stack->priv = priv = gb_tab_stack_get_instance_private (stack);
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (stack),
+ GTK_ORIENTATION_VERTICAL);
+
+ priv->tabs = gtk_list_store_new(1, GB_TYPE_TAB);
+
+ hbox = g_object_new(GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add_with_properties(GTK_CONTAINER(stack), hbox,
+ "expand", FALSE,
+ NULL);
+
+ priv->combo = g_object_new(GTK_TYPE_COMBO_BOX,
+ "has-frame", FALSE,
+ "height-request", 30,
+ "model", priv->tabs,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add_with_properties(GTK_CONTAINER(hbox), priv->combo,
+ "expand", TRUE,
+ NULL);
+ g_signal_connect_swapped(priv->combo, "changed",
+ G_CALLBACK(gb_tab_stack_combo_changed),
+ stack);
+
+ priv->controls = g_object_new(GTK_TYPE_NOTEBOOK,
+ "visible", TRUE,
+ "show-border", FALSE,
+ "show-tabs", FALSE,
+ NULL);
+ gtk_container_add_with_properties(GTK_CONTAINER(hbox), priv->controls,
+ "expand", FALSE,
+ NULL);
+
+ priv->close = g_object_new(GTK_TYPE_BUTTON,
+ "child", g_object_new(GTK_TYPE_IMAGE,
+ "icon-name", "window-close-symbolic",
+ "icon-size", GTK_ICON_SIZE_MENU,
+ "visible", TRUE,
+ "tooltip-text", _("Close the current view."),
+ NULL),
+ "visible", FALSE,
+ NULL);
+ g_signal_connect_swapped(priv->close, "clicked",
+ G_CALLBACK(gb_tab_stack_close_current),
+ stack);
+ gtk_container_add_with_properties(GTK_CONTAINER(hbox), priv->close,
+ "expand", FALSE,
+ NULL);
+
+ cell = g_object_new(GTK_TYPE_CELL_RENDERER_PIXBUF,
+ "icon-name", "text-x-generic",
+ "width", 24,
+ "xalign", 0.5f,
+ "xpad", 3,
+ NULL);
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combo), cell, FALSE);
+ gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(priv->combo), cell,
+ gb_tab_stack_icon_name_func,
+ NULL, NULL);
+
+ cell = g_object_new(GTK_TYPE_CELL_RENDERER_TEXT,
+ "size-points", 9.0,
+ "xpad", 3,
+ NULL);
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combo), cell, TRUE);
+ gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(priv->combo), cell,
+ gb_tab_stack_name_func,
+ NULL, NULL);
+
+ priv->notebook = g_object_new(GTK_TYPE_NOTEBOOK,
+ "visible", TRUE,
+ "show-border", FALSE,
+ "show-tabs", FALSE,
+ NULL);
+ gtk_container_add_with_properties(GTK_CONTAINER(stack), priv->notebook,
+ "expand", TRUE,
+ NULL);
+}
diff --git a/src/tabs/gb-tab-stack.h b/src/tabs/gb-tab-stack.h
new file mode 100644
index 0000000..6d13fa5
--- /dev/null
+++ b/src/tabs/gb-tab-stack.h
@@ -0,0 +1,69 @@
+/* gb-tab-stack.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_STACK_H
+#define GB_TAB_STACK_H
+
+#include <gtk/gtk.h>
+
+#include "gb-tab.h"
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_TAB_STACK (gb_tab_stack_get_type())
+#define GB_TAB_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_TAB_STACK, GbTabStack))
+#define GB_TAB_STACK_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_TAB_STACK, GbTabStack
const))
+#define GB_TAB_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GB_TYPE_TAB_STACK, GbTabStackClass))
+#define GB_IS_TAB_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GB_TYPE_TAB_STACK))
+#define GB_IS_TAB_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GB_TYPE_TAB_STACK))
+#define GB_TAB_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GB_TYPE_TAB_STACK, GbTabStackClass))
+
+typedef struct _GbTabStack GbTabStack;
+typedef struct _GbTabStackClass GbTabStackClass;
+typedef struct _GbTabStackPrivate GbTabStackPrivate;
+
+struct _GbTabStack
+{
+ GtkVBox parent;
+
+ /*< private >*/
+ GbTabStackPrivate *priv;
+};
+
+struct _GbTabStackClass
+{
+ GtkVBoxClass parent_class;
+};
+
+GType gb_tab_stack_get_type (void) G_GNUC_CONST;
+GtkWidget *gb_tab_stack_get_active (GbTabStack *stack);
+gboolean gb_tab_stack_contains_tab (GbTabStack *stack,
+ GbTab *tab);
+void gb_tab_stack_remove_tab (GbTabStack *stack,
+ GbTab *tab);
+guint gb_tab_stack_get_n_tabs (GbTabStack *stack);
+gboolean gb_tab_stack_focus_first (GbTabStack *stack);
+gboolean gb_tab_stack_focus_next (GbTabStack *stack);
+gboolean gb_tab_stack_focus_previous (GbTabStack *stack);
+gboolean gb_tab_stack_focus_tab (GbTabStack *stack,
+ GbTab *tab);
+GList *gb_tab_stack_get_tabs (GbTabStack *stack);
+
+G_END_DECLS
+
+#endif /* GB_TAB_STACK_H */
diff --git a/src/tabs/gb-tab.c b/src/tabs/gb-tab.c
index 01ef233..508c669 100644
--- a/src/tabs/gb-tab.c
+++ b/src/tabs/gb-tab.c
@@ -74,6 +74,12 @@ gb_tab_get_header_area (GbTab *tab)
}
GtkWidget *
+gb_tab_get_controls(GbTab *tab)
+{
+ return tab->priv->header_box;
+}
+
+GtkWidget *
gb_tab_get_footer_area (GbTab *tab)
{
g_return_val_if_fail (GB_IS_TAB (tab), NULL);
diff --git a/src/tabs/gb-tab.h b/src/tabs/gb-tab.h
index f23a9b0..316a389 100644
--- a/src/tabs/gb-tab.h
+++ b/src/tabs/gb-tab.h
@@ -65,6 +65,7 @@ void gb_tab_set_dirty (GbTab *tab,
void gb_tab_freeze_drag (GbTab *tab);
void gb_tab_thaw_drag (GbTab *tab);
void gb_tab_close (GbTab *tab);
+GtkWidget *gb_tab_get_controls (GbTab *tab);
GtkWidget *gb_tab_get_header_area (GbTab *tab);
GtkWidget *gb_tab_get_footer_area (GbTab *tab);
GtkWidget *gb_tab_get_content_area (GbTab *tab);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]