summaryrefslogtreecommitdiffstats
path: root/gedit/gedit-notebook-stack-switcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'gedit/gedit-notebook-stack-switcher.c')
-rw-r--r--gedit/gedit-notebook-stack-switcher.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/gedit/gedit-notebook-stack-switcher.c b/gedit/gedit-notebook-stack-switcher.c
new file mode 100644
index 0000000..ecb661a
--- /dev/null
+++ b/gedit/gedit-notebook-stack-switcher.c
@@ -0,0 +1,362 @@
+/*
+ * gedit-notebook-stack-switcher.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2014 - Paolo Borelli
+ *
+ * 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 2 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 "config.h"
+
+#include "gedit-notebook-stack-switcher.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+/*
+ * This widget is a rather ugly kludge: it uses a GtkNotebook full of empty
+ * pages to create a stack switcher for the bottom pane. This is needed
+ * because we want to expose GtkStack in the API but we want the tabs styled
+ * as notebook tabs. Hopefully Gtk itself will grow a "tabs" stack switcher...
+ */
+
+struct _GeditNotebookStackSwitcherPrivate
+{
+ GtkWidget *notebook;
+ GtkStack *stack;
+};
+
+enum {
+ PROP_0,
+ PROP_STACK
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GeditNotebookStackSwitcher, gedit_notebook_stack_switcher, GTK_TYPE_BIN)
+
+static void
+gedit_notebook_stack_switcher_init (GeditNotebookStackSwitcher *switcher)
+{
+ GeditNotebookStackSwitcherPrivate *priv;
+
+ priv = gedit_notebook_stack_switcher_get_instance_private (switcher);
+ switcher->priv = priv;
+
+ priv->notebook = gtk_notebook_new ();
+
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (priv->notebook), GTK_POS_BOTTOM);
+ gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (priv->notebook), 0);
+
+ gtk_widget_show (priv->notebook);
+ gtk_container_add (GTK_CONTAINER (switcher), priv->notebook);
+}
+
+static GtkWidget *
+find_notebook_child (GeditNotebookStackSwitcher *switcher,
+ GtkWidget *stack_child)
+{
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+ GList *pages;
+ GList *p;
+ GtkWidget *ret = NULL;
+
+ if (stack_child == NULL)
+ {
+ return NULL;
+ }
+
+ pages = gtk_container_get_children (GTK_CONTAINER (priv->notebook));
+ for (p = pages; p != NULL; p = p->next)
+ {
+ GtkWidget *child;
+
+ child = g_object_get_data (p->data, "stack-child");
+ if (stack_child == child)
+ {
+ ret = p->data;
+ break;
+ }
+ }
+
+ g_list_free (pages);
+
+ return ret;
+}
+
+static void
+sync_label (GeditNotebookStackSwitcher *switcher,
+ GtkWidget *stack_child,
+ GtkWidget *notebook_child)
+{
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+
+ if (stack_child != NULL && notebook_child != NULL)
+ {
+ gchar *title;
+
+ gtk_widget_set_visible (notebook_child,
+ gtk_widget_get_visible (stack_child));
+
+ gtk_container_child_get (GTK_CONTAINER (priv->stack), stack_child,
+ "title", &title,
+ NULL);
+
+ gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (priv->notebook),
+ notebook_child,
+ title);
+
+ g_free (title);
+ }
+}
+
+static void
+on_child_prop_changed (GtkWidget *widget,
+ GParamSpec *pspec,
+ GeditNotebookStackSwitcher *switcher)
+{
+ GtkWidget *nb_child;
+
+ nb_child = find_notebook_child (switcher, widget);
+ sync_label (switcher, widget, nb_child);
+}
+
+static void
+on_child_changed (GtkWidget *widget,
+ GParamSpec *pspec,
+ GeditNotebookStackSwitcher *switcher)
+{
+ GtkNotebook *notebook;
+ GtkWidget *child;
+ GtkWidget *nb_child;
+ gint nb_page;
+
+ notebook = GTK_NOTEBOOK (switcher->priv->notebook);
+
+ child = gtk_stack_get_visible_child (GTK_STACK (widget));
+ nb_child = find_notebook_child (switcher, child);
+
+ nb_page = gtk_notebook_page_num (notebook, nb_child);
+
+ g_signal_handlers_block_by_func (widget, on_child_prop_changed, switcher);
+ gtk_notebook_set_current_page (notebook, nb_page);
+ g_signal_handlers_unblock_by_func (widget, on_child_prop_changed, switcher);
+
+ sync_label (switcher, child, nb_child);
+}
+
+static void
+on_stack_child_added (GtkStack *stack,
+ GtkWidget *widget,
+ GeditNotebookStackSwitcher *switcher)
+{
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+ GtkWidget *dummy;
+
+ dummy = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ g_object_set_data (G_OBJECT (dummy), "stack-child", widget);
+ gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook),
+ dummy,
+ NULL);
+
+ g_signal_connect (widget, "notify::visible",
+ G_CALLBACK (on_child_prop_changed), switcher);
+ g_signal_connect (widget, "child-notify::title",
+ G_CALLBACK (on_child_prop_changed), switcher);
+
+ sync_label (switcher, widget, dummy);
+}
+
+static void
+on_stack_child_removed (GtkStack *stack,
+ GtkWidget *widget,
+ GeditNotebookStackSwitcher *switcher)
+{
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+ GtkWidget *nb_child;
+
+ g_signal_handlers_disconnect_by_func (widget, on_child_prop_changed, switcher);
+
+ nb_child = find_notebook_child (switcher, widget);
+ gtk_container_remove (GTK_CONTAINER (priv->notebook), nb_child);
+}
+
+static void
+on_notebook_switch_page (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ GeditNotebookStackSwitcher *switcher)
+{
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+ GtkWidget *child;
+
+ child = g_object_get_data (G_OBJECT (page), "stack-child");
+
+ /* NOTE: we make the assumption here that if there is no visible child
+ * it means that the child does not contain any child already, this is
+ * to avoid an assertion when closing gedit, since the remove signal
+ * runs first on the stack handler so we try to set a visible child
+ * when the stack already does not handle that child
+ */
+ if (child != NULL && gtk_stack_get_visible_child (priv->stack) != NULL)
+ {
+ gtk_stack_set_visible_child (priv->stack, child);
+ }
+}
+
+static void
+disconnect_signals (GeditNotebookStackSwitcher *switcher)
+{
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+
+ g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_added, switcher);
+ g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_removed, switcher);
+ g_signal_handlers_disconnect_by_func (priv->stack, on_child_changed, switcher);
+ g_signal_handlers_disconnect_by_func (priv->stack, disconnect_signals, switcher);
+ g_signal_handlers_disconnect_by_func (priv->notebook, on_notebook_switch_page, switcher);
+}
+
+static void
+connect_signals (GeditNotebookStackSwitcher *switcher)
+{
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+
+ g_signal_connect (priv->stack, "add",
+ G_CALLBACK (on_stack_child_added), switcher);
+ g_signal_connect (priv->stack, "remove",
+ G_CALLBACK (on_stack_child_removed), switcher);
+ g_signal_connect (priv->stack, "notify::visible-child",
+ G_CALLBACK (on_child_changed), switcher);
+ g_signal_connect_swapped (priv->stack, "destroy",
+ G_CALLBACK (disconnect_signals), switcher);
+ g_signal_connect (priv->notebook, "switch-page",
+ G_CALLBACK (on_notebook_switch_page), switcher);
+}
+
+void
+gedit_notebook_stack_switcher_set_stack (GeditNotebookStackSwitcher *switcher,
+ GtkStack *stack)
+{
+ GeditNotebookStackSwitcherPrivate *priv;
+
+ g_return_if_fail (GEDIT_IS_NOTEBOOK_STACK_SWITCHER (switcher));
+ g_return_if_fail (stack == NULL || GTK_IS_STACK (stack));
+
+ priv = switcher->priv;
+
+ if (priv->stack == stack)
+ return;
+
+ if (priv->stack)
+ {
+ disconnect_signals (switcher);
+ g_clear_object (&priv->stack);
+ }
+
+ if (stack)
+ {
+ priv->stack = g_object_ref (stack);
+ connect_signals (switcher);
+ }
+
+ g_object_notify (G_OBJECT (switcher), "stack");
+}
+
+GtkStack *
+gedit_notebook_stack_switcher_get_stack (GeditNotebookStackSwitcher *switcher)
+{
+ g_return_val_if_fail (GEDIT_IS_NOTEBOOK_STACK_SWITCHER (switcher), NULL);
+
+ return switcher->priv->stack;
+}
+
+static void
+gedit_notebook_stack_switcher_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GeditNotebookStackSwitcher *switcher = GEDIT_NOTEBOOK_STACK_SWITCHER (object);
+ GeditNotebookStackSwitcherPrivate *priv = switcher->priv;
+
+ switch (prop_id)
+ {
+ case PROP_STACK:
+ g_value_set_object (value, priv->stack);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_notebook_stack_switcher_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GeditNotebookStackSwitcher *switcher = GEDIT_NOTEBOOK_STACK_SWITCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_STACK:
+ gedit_notebook_stack_switcher_set_stack (switcher, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_notebook_stack_switcher_dispose (GObject *object)
+{
+ GeditNotebookStackSwitcher *switcher = GEDIT_NOTEBOOK_STACK_SWITCHER (object);
+
+ gedit_notebook_stack_switcher_set_stack (switcher, NULL);
+
+ G_OBJECT_CLASS (gedit_notebook_stack_switcher_parent_class)->dispose (object);
+}
+
+static void
+gedit_notebook_stack_switcher_class_init (GeditNotebookStackSwitcherClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gedit_notebook_stack_switcher_get_property;
+ object_class->set_property = gedit_notebook_stack_switcher_set_property;
+ object_class->dispose = gedit_notebook_stack_switcher_dispose;
+
+ g_object_class_install_property (object_class,
+ PROP_STACK,
+ g_param_spec_object ("stack",
+ "Stack",
+ "Stack",
+ GTK_TYPE_STACK,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+GtkWidget *
+gedit_notebook_stack_switcher_new (void)
+{
+ return g_object_new (GEDIT_TYPE_NOTEBOOK_STACK_SWITCHER, NULL);
+}
+
+/* ex:set ts=8 noet: */