[baobab/animated-notebook: 1/2] Import CcNotebook widget and enable Clutter



commit e5dbba21a14e041825eea556cbca52a92b4c5bdb
Author: Stefano Facchini <stefano facchini gmail com>
Date:   Thu Oct 18 14:25:02 2012 +0200

    Import CcNotebook widget and enable Clutter

 configure.ac        |    1 +
 src/Makefile.am     |    7 +-
 src/cc-notebook.c   |  578 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/cc-notebook.h   |   70 ++++++
 src/ccnotebook.vapi |   11 +
 src/main.vala       |    2 +
 6 files changed, 668 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index fedd9aa..8164369 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,6 +45,7 @@ YELP_HELP_INIT
 
 PKG_CHECK_MODULES(BAOBAB, [
     gtk+-3.0 >= 3.5.9
+    clutter-gtk-1.0
     gio-2.0 >= 2.30.0
 ])
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 89d842c..cc5f4cb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,6 +12,7 @@ BUILT_SOURCES = baobab-resources.c
 baobab_VALAFLAGS = \
 	--vapidir=$(top_srcdir)/libgd	\
 	--pkg gtk+-3.0		\
+	--pkg clutter-gtk-1.0	\
 	--pkg gio-2.0		\
 	--pkg gio-unix-2.0	\
 	--pkg gd-1.0
@@ -20,7 +21,9 @@ noinst_HEADERS = \
 	baobab-chart.h			\
 	baobab-treemap.h		\
 	baobab-ringschart.h		\
-	egg-list-box.h
+	egg-list-box.h			\
+	cc-notebook.h			\
+	$(NULL)
 
 VALA_SOURCES = \
 	baobab-application.vala		\
@@ -40,10 +43,12 @@ baobab_SOURCES = \
 	baobab.vapi			\
 	config.vapi			\
 	egglistbox.vapi			\
+	ccnotebook.vapi			\
 	baobab-chart.c			\
 	baobab-treemap.c		\
 	baobab-ringschart.c		\
 	egg-list-box.c			\
+	cc-notebook.c			\
 	$(BUILT_SOURCES)
 
 baobab-resources.c: baobab.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies baobab.gresource.xml)
diff --git a/src/cc-notebook.c b/src/cc-notebook.c
new file mode 100644
index 0000000..0cfbaf2
--- /dev/null
+++ b/src/cc-notebook.c
@@ -0,0 +1,578 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright  2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ *      Bastien Nocera <hadess hadess net>
+ *      Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "cc-notebook.h"
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CC_TYPE_NOTEBOOK, CcNotebookPrivate))
+
+/*
+ * Structure:
+ *
+ *   Notebook
+ *   +---- GtkClutterEmbed
+ *         +---- ClutterStage
+ *               +---- ClutterScrollActor:scroll
+ *                     +---- ClutterActor:bin
+ *                           +---- ClutterActor:frame<ClutterBinLayout>
+ *                                 +---- GtkClutterActor:embed<GtkWidget>
+ *
+ * the frame element is needed to make the GtkClutterActor contents fill the allocation
+ */
+
+struct _CcNotebookPrivate
+{
+        GtkWidget *embed;
+
+        ClutterActor *stage;
+        ClutterActor *scroll;
+        ClutterActor *bin;
+
+        int last_width;
+
+        GtkWidget *selected_page;
+        GList *pages; /* GList of GtkWidgets */
+        GList *removed_pages; /* GList of RemoveData, see setup_delayed_remove() */
+};
+
+enum
+{
+        PROP_0,
+        PROP_CURRENT_PAGE,
+        LAST_PROP
+};
+
+static GParamSpec *obj_props[LAST_PROP] = { NULL, };
+
+static void
+cc_notebook_buildable_add_child (GtkBuildable  *buildable,
+                                 GtkBuilder    *builder,
+                                 GObject       *child,
+                                 const gchar   *type);
+
+static void
+cc_notebook_buildable_init (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (CcNotebook, cc_notebook, GTK_TYPE_BOX,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+                                                cc_notebook_buildable_init))
+
+static void
+cc_notebook_get_property (GObject    *gobject,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+        CcNotebookPrivate *priv = CC_NOTEBOOK (gobject)->priv;
+
+        switch (prop_id) {
+        case PROP_CURRENT_PAGE:
+                g_value_set_pointer (value, priv->selected_page);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+        }
+}
+
+static void
+cc_notebook_set_property (GObject      *gobject,
+			  guint         prop_id,
+			  const GValue *value,
+			  GParamSpec   *pspec)
+{
+        CcNotebook *self = CC_NOTEBOOK (gobject);
+
+        switch (prop_id) {
+        case PROP_CURRENT_PAGE:
+                cc_notebook_select_page (self, g_value_get_pointer (value), TRUE);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+        }
+}
+
+static void
+cc_notebook_finalize (GObject *gobject)
+{
+        CcNotebook *self = CC_NOTEBOOK (gobject);
+
+	g_list_free_full (self->priv->removed_pages, (GDestroyNotify) g_free);
+	self->priv->removed_pages = NULL;
+
+	g_list_free (self->priv->pages);
+	self->priv->pages = NULL;
+
+	G_OBJECT_CLASS (cc_notebook_parent_class)->finalize (gobject);
+}
+
+static GtkSizeRequestMode
+cc_notebook_get_request_mode (GtkWidget *widget)
+{
+	CcNotebook *notebook;
+	GtkWidget *target;
+
+	notebook = CC_NOTEBOOK (widget);
+
+	target = notebook->priv->selected_page ? notebook->priv->selected_page : notebook->priv->embed;
+
+	return gtk_widget_get_request_mode (target);
+}
+
+static void
+cc_notebook_get_preferred_height (GtkWidget       *widget,
+				  gint            *minimum_height,
+				  gint            *natural_height)
+{
+	CcNotebook *notebook;
+	GList *l;
+
+	notebook = CC_NOTEBOOK (widget);
+
+	if (notebook->priv->selected_page == NULL) {
+		gtk_widget_get_preferred_height (notebook->priv->embed, minimum_height, natural_height);
+		return;
+	}
+
+	*minimum_height = 0;
+	*natural_height = 0;
+	for (l = notebook->priv->pages; l != NULL; l = l->next) {
+		GtkWidget *page = l->data;
+		int page_min, page_nat;
+
+		gtk_widget_get_preferred_height (page, &page_min, &page_nat);
+		*minimum_height = MAX(page_min, *minimum_height);
+		*natural_height = MAX(page_nat, *natural_height);
+	}
+}
+
+static void
+cc_notebook_get_preferred_width_for_height (GtkWidget       *widget,
+					    gint             height,
+					    gint            *minimum_width,
+					    gint            *natural_width)
+{
+	CcNotebook *notebook;
+	GList *l;
+
+	notebook = CC_NOTEBOOK (widget);
+
+	if (notebook->priv->selected_page == NULL) {
+		gtk_widget_get_preferred_width_for_height (notebook->priv->embed, height, minimum_width, natural_width);
+		return;
+	}
+
+	*minimum_width = 0;
+	*natural_width = 0;
+	for (l = notebook->priv->pages; l != NULL; l = l->next) {
+		GtkWidget *page = l->data;
+		int page_min, page_nat;
+
+		gtk_widget_get_preferred_width_for_height (page, height, &page_min, &page_nat);
+		*minimum_width = MAX(page_min, *minimum_width);
+		*natural_width = MAX(page_nat, *natural_width);
+	}
+}
+
+static void
+cc_notebook_get_preferred_width (GtkWidget       *widget,
+				 gint            *minimum_width,
+				 gint            *natural_width)
+{
+	CcNotebook *notebook;
+	GList *l;
+
+	notebook = CC_NOTEBOOK (widget);
+
+	if (notebook->priv->selected_page == NULL) {
+		gtk_widget_get_preferred_width (notebook->priv->embed, minimum_width, natural_width);
+		return;
+	}
+
+	*minimum_width = 0;
+	*natural_width = 0;
+	for (l = notebook->priv->pages; l != NULL; l = l->next) {
+		GtkWidget *page = l->data;
+		int page_min, page_nat;
+
+		gtk_widget_get_preferred_width (page, &page_min, &page_nat);
+		*minimum_width = MAX(page_min, *minimum_width);
+		*natural_width = MAX(page_nat, *natural_width);
+	}
+}
+
+static void
+cc_notebook_get_preferred_height_for_width (GtkWidget       *widget,
+					    gint             width,
+					    gint            *minimum_height,
+					    gint            *natural_height)
+{
+	CcNotebook *notebook;
+	GList *l;
+
+	notebook = CC_NOTEBOOK (widget);
+
+	if (notebook->priv->selected_page == NULL) {
+		gtk_widget_get_preferred_height_for_width (notebook->priv->embed, width, minimum_height, natural_height);
+		return;
+	}
+
+	*minimum_height = 0;
+	*natural_height = 0;
+	for (l = notebook->priv->pages; l != NULL; l = l->next) {
+		GtkWidget *page = l->data;
+		int page_min, page_nat;
+
+		gtk_widget_get_preferred_height_for_width (page, width, &page_min, &page_nat);
+		*minimum_height = MAX(page_min, *minimum_height);
+		*natural_height = MAX(page_nat, *natural_height);
+	}
+}
+
+static gboolean
+cc_notebook_focus (GtkWidget        *widget,
+		   GtkDirectionType  direction)
+{
+	CcNotebook *notebook;
+	GtkWidget *child;
+
+	notebook = CC_NOTEBOOK (widget);
+	child = notebook->priv->selected_page;
+
+	if (child == NULL)
+		return FALSE;
+
+	/* HACK: the default GtkContainer implementation is fine by us
+	 * and there's no way to get to it without excessive copy/paste */
+	return GTK_WIDGET_GET_CLASS (child)->focus (child, direction);
+}
+
+static void
+cc_notebook_class_init (CcNotebookClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (CcNotebookPrivate));
+
+        obj_props[PROP_CURRENT_PAGE] =
+                g_param_spec_pointer (g_intern_static_string ("current-page"),
+				      "Current Page",
+				      "The currently selected page widget",
+				      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+        gobject_class->get_property = cc_notebook_get_property;
+        gobject_class->set_property = cc_notebook_set_property;
+        gobject_class->finalize = cc_notebook_finalize;
+        g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);
+
+	widget_class->get_request_mode = cc_notebook_get_request_mode;
+	widget_class->get_preferred_height = cc_notebook_get_preferred_height;
+	widget_class->get_preferred_width_for_height = cc_notebook_get_preferred_width_for_height;
+	widget_class->get_preferred_width = cc_notebook_get_preferred_width;
+	widget_class->get_preferred_height_for_width = cc_notebook_get_preferred_height_for_width;
+	widget_class->focus = cc_notebook_focus;
+}
+
+static void
+on_embed_size_allocate (GtkWidget     *embed,
+                        GtkAllocation *allocation,
+                        CcNotebook  *self)
+{
+        ClutterActorIter iter;
+        ClutterActor *child;
+        ClutterActor *frame;
+        float page_w, page_h;
+        float offset = 0.f;
+        ClutterPoint pos;
+
+        if (self->priv->selected_page == NULL)
+		return;
+
+        self->priv->last_width = allocation->width;
+
+        page_w = allocation->width;
+        page_h = allocation->height;
+
+        clutter_actor_iter_init (&iter, self->priv->bin);
+        while (clutter_actor_iter_next (&iter, &child)) {
+                clutter_actor_set_x (child, offset);
+                clutter_actor_set_size (child, page_w, page_h);
+
+                offset += page_w;
+        }
+
+	/* This stops the non-animated scrolling from happening
+	 * if we're still scrolling there */
+	if (clutter_actor_get_transition (self->priv->scroll, "scroll-to") != NULL)
+		return;
+
+	frame = g_object_get_data (G_OBJECT (self->priv->selected_page),
+				   "cc-notebook-frame");
+
+        pos.y = 0;
+        pos.x = clutter_actor_get_x (frame);
+        g_debug ("Scrolling to (%lf,%lf) in allocation", pos.x, pos.y);
+        clutter_scroll_actor_scroll_to_point (CLUTTER_SCROLL_ACTOR (self->priv->scroll), &pos);
+}
+
+static void
+cc_notebook_init (CcNotebook *self)
+{
+        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CC_TYPE_NOTEBOOK, CcNotebookPrivate);
+
+        gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+
+        self->priv->embed = gtk_clutter_embed_new ();
+        gtk_widget_push_composite_child ();
+        gtk_container_add (GTK_CONTAINER (self), self->priv->embed);
+        gtk_widget_pop_composite_child ();
+        g_signal_connect (self->priv->embed, "size-allocate", G_CALLBACK (on_embed_size_allocate), self);
+        gtk_widget_show (self->priv->embed);
+
+        self->priv->stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (self->priv->embed));
+
+        self->priv->scroll = clutter_scroll_actor_new ();
+        clutter_scroll_actor_set_scroll_mode (CLUTTER_SCROLL_ACTOR (self->priv->scroll),
+                                                CLUTTER_SCROLL_HORIZONTALLY);
+        clutter_actor_add_constraint (self->priv->scroll, clutter_bind_constraint_new (self->priv->stage, CLUTTER_BIND_SIZE, 0.f));
+        clutter_actor_add_child (self->priv->stage, self->priv->scroll);
+
+        self->priv->bin = clutter_actor_new ();
+        clutter_actor_add_child (self->priv->scroll, self->priv->bin);
+
+        self->priv->selected_page = NULL;
+        gtk_widget_set_name (GTK_WIDGET (self), "GtkBox");
+}
+
+GtkWidget *
+cc_notebook_new (void)
+{
+        return g_object_new (CC_TYPE_NOTEBOOK, NULL);
+}
+
+static void
+_cc_notebook_select_page (CcNotebook *self,
+			  GtkWidget  *widget,
+			  int         index,
+			  gboolean    animate)
+{
+        ClutterPoint pos;
+
+        g_return_if_fail (CC_IS_NOTEBOOK (self));
+        g_return_if_fail (GTK_IS_WIDGET (widget));
+
+        pos.y = 0;
+        pos.x = self->priv->last_width * index;
+
+        if (clutter_actor_get_transition (self->priv->scroll, "scroll-to") != NULL) {
+                g_debug ("Cancelling previous scroll animation");
+                clutter_actor_remove_transition (self->priv->scroll, "scroll-to");
+        }
+
+        clutter_actor_save_easing_state (self->priv->scroll);
+        if (animate)
+		clutter_actor_set_easing_duration (self->priv->scroll, 500);
+	else
+		clutter_actor_set_easing_duration (self->priv->scroll, 0);
+
+        g_debug ("Scrolling to (%lf,%lf) %s animation in page selection", pos.x, pos.y,
+		 animate ? "with" : "without");
+        clutter_scroll_actor_scroll_to_point (CLUTTER_SCROLL_ACTOR (self->priv->scroll), &pos);
+
+	clutter_actor_restore_easing_state (self->priv->scroll);
+
+        /* Remember the last selected page */
+        self->priv->selected_page = widget;
+
+        g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURRENT_PAGE]);
+}
+
+void
+cc_notebook_select_page (CcNotebook *self,
+                         GtkWidget  *widget,
+                         gboolean    animate)
+{
+	int i, n_children;
+	GList *children, *l;
+	ClutterActor *frame;
+	gboolean found;
+
+        if (widget == self->priv->selected_page)
+		return;
+
+	found = FALSE;
+	frame = g_object_get_data (G_OBJECT (widget), "cc-notebook-frame");
+
+        n_children = clutter_actor_get_n_children (self->priv->bin);
+        children = clutter_actor_get_children (self->priv->bin);
+        for (i = 0, l = children; i < n_children; i++, l = l->next) {
+		if (frame == l->data) {
+			_cc_notebook_select_page (self, widget, i, animate);
+			found = TRUE;
+			break;
+		}
+	}
+	g_list_free (children);
+	if (found == FALSE)
+		g_warning ("Could not find widget '%p' in CcNotebook '%p'", widget, self);
+}
+
+static void
+widget_destroyed (GtkWidget *widget,
+                  gpointer   user_data)
+{
+        CcNotebook *notebook = g_object_get_data (G_OBJECT (widget), "cc-notebook");
+        cc_notebook_remove_page (notebook, widget);
+}
+
+void
+cc_notebook_add_page (CcNotebook *self,
+                      GtkWidget  *widget)
+{
+        ClutterActor *frame;
+        ClutterActor *embed;
+        int res;
+
+        g_return_if_fail (CC_IS_NOTEBOOK (self));
+        g_return_if_fail (GTK_IS_WIDGET (widget));
+
+        frame = clutter_actor_new ();
+        clutter_actor_set_layout_manager (frame, clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_FILL,
+                                                                         CLUTTER_BIN_ALIGNMENT_FILL));
+
+        embed = gtk_clutter_actor_new_with_contents (widget);
+        g_object_set_data (G_OBJECT (widget), "cc-notebook-frame", frame);
+        g_object_set_data (G_OBJECT (widget), "cc-notebook", self);
+        g_signal_connect (widget, "destroy", G_CALLBACK (widget_destroyed), NULL);
+        clutter_actor_add_child (frame, embed);
+        gtk_widget_show (widget);
+
+        res = clutter_actor_get_n_children (self->priv->bin);
+        clutter_actor_insert_child_at_index (self->priv->bin, frame, res);
+
+        self->priv->pages = g_list_prepend (self->priv->pages, widget);
+
+        if (self->priv->selected_page == NULL)
+		_cc_notebook_select_page (self, widget, res, FALSE);
+
+        gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+typedef struct {
+	CcNotebook   *notebook;
+	ClutterActor *frame;
+} RemoveData;
+
+static void
+remove_on_complete (ClutterTimeline *timeline,
+		    RemoveData      *data)
+{
+	data->notebook->priv->removed_pages = g_list_remove (data->notebook->priv->removed_pages, data);
+	clutter_actor_destroy (data->frame);
+	g_free (data);
+}
+
+static gboolean
+setup_delayed_remove (CcNotebook   *self,
+		      ClutterActor *frame)
+{
+        ClutterTransition *transition;
+        RemoveData *data;
+
+	transition = clutter_actor_get_transition (self->priv->scroll, "scroll-to");
+	if (transition == NULL)
+		return FALSE;
+
+	data = g_new0 (RemoveData, 1);
+	data->notebook = self;
+	data->frame = frame;
+
+	self->priv->removed_pages = g_list_prepend (self->priv->removed_pages, data);
+	g_signal_connect (transition, "completed",
+			  G_CALLBACK (remove_on_complete), data);
+
+	return TRUE;
+}
+
+void
+cc_notebook_remove_page (CcNotebook *self,
+                         GtkWidget  *widget)
+{
+        ClutterActorIter iter;
+        ClutterActor *child, *frame;
+        int index;
+
+        g_return_if_fail (CC_IS_NOTEBOOK (self));
+        g_return_if_fail (GTK_IS_WIDGET (widget));
+
+        frame = g_object_get_data (G_OBJECT (widget), "cc-notebook-frame");
+
+	index = 0;
+        clutter_actor_iter_init (&iter, self->priv->bin);
+        while (clutter_actor_iter_next (&iter, &child)) {
+                if (frame == child) {
+			if (setup_delayed_remove (self, frame) == FALSE)
+				clutter_actor_iter_remove (&iter);
+                        break;
+		}
+
+		index++;
+        }
+
+        self->priv->pages = g_list_remove (self->priv->pages, widget);
+
+        if (widget == self->priv->selected_page)
+                self->priv->selected_page = NULL;
+
+        gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+GtkWidget *
+cc_notebook_get_selected_page (CcNotebook *self)
+{
+        g_return_val_if_fail (CC_IS_NOTEBOOK (self), NULL);
+
+        return self->priv->selected_page;
+}
+
+static void
+cc_notebook_buildable_add_child (GtkBuildable  *buildable,
+                                 GtkBuilder    *builder,
+                                 GObject       *child,
+                                 const gchar   *type)
+{
+        CcNotebook *notebook = CC_NOTEBOOK (buildable);
+        cc_notebook_add_page (notebook, GTK_WIDGET (child));
+}
+
+static void
+cc_notebook_buildable_init (GtkBuildableIface *iface)
+{
+        iface->add_child = cc_notebook_buildable_add_child;
+}
diff --git a/src/cc-notebook.h b/src/cc-notebook.h
new file mode 100644
index 0000000..19dbbf1
--- /dev/null
+++ b/src/cc-notebook.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright  2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ *      Bastien Nocera <hadess hadess net>
+ */
+
+#ifndef _CC_NOTEBOOK_H_
+#define _CC_NOTEBOOK_H_
+
+#include <gtk/gtk.h>
+#include <clutter-gtk/clutter-gtk.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_NOTEBOOK                (cc_notebook_get_type ())
+#define CC_NOTEBOOK(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_NOTEBOOK, CcNotebook))
+#define CC_IS_NOTEBOOK(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_NOTEBOOK))
+#define CC_NOTEBOOK_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_NOTEBOOK, CcNotebookClass))
+#define CC_IS_NOTEBOOK_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_NOTEBOOK))
+#define CC_NOTEBOOK_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_NOTEBOOK, CcNotebookClass))
+
+typedef struct _CcNotebook            CcNotebook;
+typedef struct _CcNotebookPrivate     CcNotebookPrivate;
+typedef struct _CcNotebookClass       CcNotebookClass;
+
+struct _CcNotebook
+{
+        GtkBox parent_class;
+
+        CcNotebookPrivate *priv;
+};
+
+struct _CcNotebookClass
+{
+        GtkBoxClass parent_class;
+};
+
+GType           cc_notebook_get_type                    (void) G_GNUC_CONST;
+
+GtkWidget *     cc_notebook_new                         (void);
+
+void            cc_notebook_add_page                    (CcNotebook *self,
+                                                         GtkWidget  *widget);
+void            cc_notebook_remove_page                 (CcNotebook *self,
+                                                         GtkWidget  *widget);
+
+void            cc_notebook_select_page                 (CcNotebook *self,
+                                                         GtkWidget  *widget,
+                                                         gboolean    animate);
+
+GtkWidget *     cc_notebook_get_selected_page           (CcNotebook *self);
+
+G_END_DECLS
+
+#endif /* _CC_NOTEBOOK_H_ */
diff --git a/src/ccnotebook.vapi b/src/ccnotebook.vapi
new file mode 100644
index 0000000..8a3e3ca
--- /dev/null
+++ b/src/ccnotebook.vapi
@@ -0,0 +1,11 @@
+namespace Cc {
+	[CCode (cheader_filename = "cc-notebook.h")]
+	public class Notebook : Gtk.Box {
+		[CCode (has_construct_function = false)]
+		public Notebook ();
+		public void add_page (Gtk.Widget widget);
+		public void remove_page (Gtk.Widget widget);
+		public void select_page (Gtk.Widget widget, bool animate);
+		public Gtk.Widget get_selected_page ();
+	}
+}
diff --git a/src/main.vala b/src/main.vala
index 6cd99cd..0436115 100644
--- a/src/main.vala
+++ b/src/main.vala
@@ -23,6 +23,8 @@ int main (string[] args) {
     Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
     Intl.textdomain (Config.GETTEXT_PACKAGE);
 
+    GtkClutter.init (ref args);
+
     var baobab = new Baobab.Application ();
     return baobab.run (args);
 }



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