[gnome-shell/nbtk-introduction: 1/8] Import nbtk core
- From: Owen Taylor <otaylor src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-shell/nbtk-introduction: 1/8] Import nbtk core
- Date: Wed, 16 Sep 2009 22:07:47 +0000 (UTC)
commit 9f79296276fa30764a435de131001c88b8c2ec7e
Author: Colin Walters <walters verbum org>
Date: Tue Sep 8 15:47:30 2009 -0400
Import nbtk core
Import the core NbtkWidget/NbtkBin and their dependencies.
https://bugzilla.gnome.org/show_bug.cgi?id=591245
configure.ac | 1 +
src/Makefile-nbtk.am | 101 +++
src/Makefile.am | 35 +-
src/nbtk/nbtk-bin.c | 783 ++++++++++++++++++++++++
src/nbtk/nbtk-bin.h | 92 +++
src/nbtk/nbtk-enum-types.c.in | 30 +
src/nbtk/nbtk-enum-types.h.in | 29 +
src/nbtk/nbtk-marshal.list | 12 +
src/nbtk/nbtk-private.c | 111 ++++
src/nbtk/nbtk-private.h | 49 ++
src/nbtk/nbtk-stylable.c | 849 ++++++++++++++++++++++++++
src/nbtk/nbtk-stylable.h | 123 ++++
src/nbtk/nbtk-style.c | 742 ++++++++++++++++++++++
src/nbtk/nbtk-style.h | 94 +++
src/nbtk/nbtk-subtexture.c | 575 +++++++++++++++++
src/nbtk/nbtk-subtexture.h | 92 +++
src/nbtk/nbtk-texture-cache.c | 456 ++++++++++++++
src/nbtk/nbtk-texture-cache.h | 94 +++
src/nbtk/nbtk-texture-frame.c | 620 +++++++++++++++++++
src/nbtk/nbtk-texture-frame.h | 92 +++
src/nbtk/nbtk-tooltip.c | 710 +++++++++++++++++++++
src/nbtk/nbtk-tooltip.h | 79 +++
src/nbtk/nbtk-types.h | 83 +++
src/nbtk/nbtk-widget.c | 1354 +++++++++++++++++++++++++++++++++++++++++
src/nbtk/nbtk-widget.h | 107 ++++
25 files changed, 7307 insertions(+), 6 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d9d2617..edb9603 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
gobject-introspection-1.0 >= 0.6.5)
PKG_CHECK_MODULES(TIDY, clutter-1.0)
+PKG_CHECK_MODULES(NBTK, clutter-1.0 gtk+-2.0 libccss-1 >= 0.3.1)
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
diff --git a/src/Makefile-nbtk.am b/src/Makefile-nbtk.am
new file mode 100644
index 0000000..adfec21
--- /dev/null
+++ b/src/Makefile-nbtk.am
@@ -0,0 +1,101 @@
+nbtk_cflags = \
+ -I$(top_srcdir)/src \
+ -DPREFIX=\""$(prefix)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DG_DISABLE_DEPRECATED \
+ -DG_LOG_DOMAIN=\"Nbtk\" \
+ -DNBTK_COMPILATION \
+ $(NBTK_CFLAGS) \
+ $(NULL)
+
+nbtk_built_sources = \
+ nbtk-enum-types.h \
+ nbtk-enum-types.c \
+ nbtk-marshal.h \
+ nbtk-marshal.c
+
+BUILT_SOURCES += $(nbtk_built_sources)
+
+EXTRA_DIST += \
+ nbtk/nbtk-marshal.list \
+ nbtk/nbtk-enum-types.h.in \
+ nbtk/nbtk-enum-types.c.in
+
+CLEANFILES += stamp-nbtk-marshal.h stamp-nbtk-enum-types.h
+
+nbtk-marshal.h: stamp-nbtk-marshal.h
+ @true
+stamp-nbtk-marshal.h: Makefile nbtk/nbtk-marshal.list
+ $(GLIB_GENMARSHAL) \
+ --prefix=_nbtk_marshal \
+ --header \
+ $(srcdir)/nbtk/nbtk-marshal.list > $ tmp && \
+ (cmp -s $ tmp nbtk-marshal.h || cp -f $ tmp nbtk-marshal.h) && \
+ rm -f $ tmp && \
+ echo timestamp > $(@F)
+
+nbtk-marshal.c: Makefile nbtk/nbtk-marshal.list
+ (echo "#include \"nbtk-marshal.h\"" ; \
+ $(GLIB_GENMARSHAL) \
+ --prefix=_nbtk_marshal \
+ --body \
+ $(srcdir)/nbtk/nbtk-marshal.list ) > $ tmp && \
+ cp -f $ tmp nbtk-marshal.c && \
+ rm -f $ tmp
+
+nbtk-enum-types.h: stamp-nbtk-enum-types.h Makefile
+ @true
+stamp-nbtk-enum-types.h: $(source_h) nbtk/nbtk-enum-types.h.in
+ ( cd $(srcdir) && \
+ $(GLIB_MKENUMS) \
+ --template nbtk/nbtk-enum-types.h.in \
+ $(nbtk_source_h) ) >> $ tmp && \
+ (cmp -s $ tmp nbtk-enum-types.h || cp $ tmp nbtk-enum-types.h) && \
+ rm -f $ tmp && \
+ echo timestamp > $(@F)
+
+nbtk-enum-types.c: stamp-nbtk-enum-types.h nbtk/nbtk-enum-types.c.in
+ ( cd $(srcdir) && \
+ $(GLIB_MKENUMS) \
+ --template nbtk/nbtk-enum-types.c.in \
+ $(nbtk_source_h) ) >> $ tmp && \
+ cp $ tmp $@ && \
+ rm -f $ tmp
+
+# please, keep this sorted alphabetically
+nbtk_source_h = \
+ nbtk/nbtk-bin.h \
+ nbtk/nbtk-private.h \
+ nbtk/nbtk-stylable.h \
+ nbtk/nbtk-style.h \
+ nbtk/nbtk-subtexture.h \
+ nbtk/nbtk-texture-cache.h \
+ nbtk/nbtk-texture-frame.h \
+ nbtk/nbtk-tooltip.h \
+ nbtk/nbtk-types.h \
+ nbtk/nbtk-widget.h \
+ $(NULL)
+
+# please, keep this sorted alphabetically
+nbtk_source_c = \
+ nbtk/nbtk-bin.c \
+ nbtk/nbtk-private.c \
+ nbtk/nbtk-stylable.c \
+ nbtk/nbtk-style.c \
+ nbtk/nbtk-subtexture.c \
+ nbtk/nbtk-texture-cache.c \
+ nbtk/nbtk-texture-frame.c \
+ nbtk/nbtk-tooltip.c \
+ nbtk/nbtk-widget.c \
+ $(NULL)
+
+noinst_LTLIBRARIES += libnbtk-1.0.la
+
+libnbtk_1_0_la_LIBADD = $(NBTK_LIBS)
+libnbtk_1_0_la_SOURCES = \
+ $(nbtk_source_c) \
+ $(nbtk_source_h) \
+ $(nbtk_built_sources) \
+ $(NULL)
+libnbtk_1_0_la_CPPFLAGS = $(nbtk_cflags)
+libnbtk_1_0_la_LDFLAGS = $(LDADD)
diff --git a/src/Makefile.am b/src/Makefile.am
index da7de38..760cb15 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,6 +24,7 @@ EXTRA_DIST += gnome-shell.in
include Makefile-big.am
include Makefile-gdmuser.am
+include Makefile-nbtk.am
include Makefile-tray.am
gnome_shell_cflags = \
@@ -152,14 +153,15 @@ libgnome_shell_la_LIBADD = \
$(MUTTER_PLUGIN_LIBS) \
$(LIBGNOMEUI_LIBS) \
libbig-1.0.la \
+ libnbtk-1.0.la \
libgdmuser-1.0.la \
libtray.la
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
typelibdir = $(pkglibdir)
-typelib_DATA = Shell-0.1.typelib Big-1.0.typelib
+typelib_DATA = Shell-0.1.typelib Big-1.0.typelib Nbtk-1.0.typelib
-Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
+Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir Nbtk-1.0.gir libgnome-shell.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Shell \
--nsversion=0.1 \
@@ -169,6 +171,7 @@ Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
--libtool="$(LIBTOOL)" \
--add-include-path=$(builddir) \
--include=Big-1.0 \
+ --include=Nbtk-1.0 \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
$(addprefix $(srcdir)/,$(libgnome_shell_la_gir_sources)) \
@@ -179,14 +182,14 @@ CLEANFILES += Shell-0.1.gir
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
# (not the fake library, since we've already done the rewriting)
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
- $(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
+ $(AM_V_GEN) \
$(G_IR_COMPILER) \
--includedir=. \
--includedir=$(MUTTER_LIB_DIR)/mutter/ \
Shell-0.1.gir -o $@
CLEANFILES += Shell-0.1.typelib
-Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)/big-enum-types.h Makefile
+Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Big \
--nsversion=1.0 \
@@ -203,6 +206,26 @@ Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)
CLEANFILES += Big-1.0.gir
Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
- $(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
- $(G_IR_COMPILER) Big-1.0.gir -o $@
+ $(AM_V_GEN) $(G_IR_COMPILER) Big-1.0.gir -o $@
CLEANFILES += Big-1.0.typelib
+
+Nbtk-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libnbtk-1.0.la Makefile
+ $(AM_V_GEN) $(G_IR_SCANNER) \
+ --namespace=Nbtk \
+ --nsversion=1.0 \
+ --include=Clutter-1.0 \
+ --include=Gtk-2.0 \
+ --libtool="$(LIBTOOL)" \
+ --program=mutter \
+ --program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
+ -DNBTK_COMPILATION \
+ $(addprefix $(srcdir)/,$(nbtk_source_h)) \
+ $(addprefix $(srcdir)/,$(nbtk_source_c)) \
+ $(srcdir)/nbtk-enum-types.h \
+ $(NBTK_CFLAGS) \
+ -o $@
+CLEANFILES += Nbtk-1.0.gir
+
+Nbtk-1.0.typelib: Nbtk-1.0.gir
+ $(AM_V_GEN) $(G_IR_COMPILER) Nbtk-1.0.gir -o $@
+CLEANFILES += Nbtk-1.0.typelib
diff --git a/src/nbtk/nbtk-bin.c b/src/nbtk/nbtk-bin.c
new file mode 100644
index 0000000..0bd1dab
--- /dev/null
+++ b/src/nbtk/nbtk-bin.c
@@ -0,0 +1,783 @@
+/*
+ * nbtk-bin.c: Basic container actor
+ *
+ * Copyright (c) 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi linux intel com>
+ *
+ */
+
+/**
+ * SECTION:nbtk-bin
+ * @short_description: a simple container with one actor
+ *
+ * #NbtkBin is a simple container capable of having only one
+ * #ClutterActor as a child.
+ *
+ * #NbtkBin inherits from #NbtkWidget, so it is fully themable.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "nbtk-bin.h"
+#include "nbtk-enum-types.h"
+#include "nbtk-private.h"
+#include "nbtk-stylable.h"
+
+#define NBTK_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_BIN, NbtkBinPrivate))
+
+struct _NbtkBinPrivate
+{
+ ClutterActor *child;
+
+ NbtkAlignment x_align;
+ NbtkAlignment y_align;
+
+ guint x_fill : 1;
+ guint y_fill : 1;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_CHILD,
+ PROP_X_ALIGN,
+ PROP_Y_ALIGN,
+ PROP_X_FILL,
+ PROP_Y_FILL
+};
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NbtkBin, nbtk_bin, NBTK_TYPE_WIDGET,
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init));
+
+void
+_nbtk_bin_get_align_factors (NbtkBin *bin,
+ gdouble *x_align,
+ gdouble *y_align)
+{
+ NbtkBinPrivate *priv = bin->priv;
+ gdouble factor;
+
+ switch (priv->x_align)
+ {
+ case NBTK_ALIGN_LEFT:
+ factor = 0.0;
+ break;
+
+ case NBTK_ALIGN_CENTER:
+ factor = 0.5;
+ break;
+
+ case NBTK_ALIGN_RIGHT:
+ factor = 1.0;
+ break;
+
+ default:
+ factor = 0.0;
+ break;
+ }
+
+ if (x_align)
+ *x_align = factor;
+
+ switch (priv->y_align)
+ {
+ case NBTK_ALIGN_TOP:
+ factor = 0.0;
+ break;
+
+ case NBTK_ALIGN_CENTER:
+ factor = 0.5;
+ break;
+
+ case NBTK_ALIGN_BOTTOM:
+ factor = 1.0;
+ break;
+
+ default:
+ factor = 0.0;
+ break;
+ }
+
+ if (y_align)
+ *y_align = factor;
+}
+
+static void
+nbtk_bin_add (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ nbtk_bin_set_child (NBTK_BIN (container), actor);
+}
+
+static void
+nbtk_bin_remove (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (container)->priv;
+
+ if (priv->child == actor)
+ nbtk_bin_set_child (NBTK_BIN (container), NULL);
+}
+
+static void
+nbtk_bin_foreach (ClutterContainer *container,
+ ClutterCallback callback,
+ gpointer user_data)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (container)->priv;
+
+ if (priv->child)
+ callback (priv->child, user_data);
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ iface->add = nbtk_bin_add;
+ iface->remove = nbtk_bin_remove;
+ iface->foreach = nbtk_bin_foreach;
+}
+
+static void
+nbtk_bin_paint (ClutterActor *self)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+
+ /* allow NbtkWidget to paint the background */
+ CLUTTER_ACTOR_CLASS (nbtk_bin_parent_class)->paint (self);
+
+ /* the pain our child */
+ if (priv->child)
+ clutter_actor_paint (priv->child);
+}
+
+static void
+nbtk_bin_pick (ClutterActor *self,
+ const ClutterColor *pick_color)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+
+ /* get the default pick implementation */
+ CLUTTER_ACTOR_CLASS (nbtk_bin_parent_class)->pick (self, pick_color);
+
+ if (priv->child)
+ clutter_actor_paint (priv->child);
+}
+
+static void
+nbtk_bin_allocate (ClutterActor *self,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+
+ CLUTTER_ACTOR_CLASS (nbtk_bin_parent_class)->allocate (self, box,
+ flags);
+
+ if (priv->child)
+ {
+ gfloat natural_width, natural_height;
+ gfloat min_width, min_height;
+ gfloat child_width, child_height;
+ gfloat available_width, available_height;
+ ClutterRequestMode request;
+ ClutterActorBox allocation = { 0, };
+ NbtkPadding padding = { 0, };
+ gdouble x_align, y_align;
+
+ _nbtk_bin_get_align_factors (NBTK_BIN (self), &x_align, &y_align);
+
+ nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+ available_width = box->x2 - box->x1
+ - padding.left - padding.right;
+ available_height = box->y2 - box->y1
+ - padding.top - padding.bottom;
+
+ if (available_width < 0)
+ available_width = 0;
+
+ if (available_height < 0)
+ available_height = 0;
+
+ if (priv->x_fill)
+ {
+ allocation.x1 = (int) padding.top;
+ allocation.x2 = (int) (allocation.x1 + available_width);
+ }
+
+ if (priv->y_fill)
+ {
+ allocation.y1 = (int) padding.right;
+ allocation.y2 = (int) (allocation.y1 + available_height);
+ }
+
+ /* if we are filling horizontally and vertically then we're done */
+ if (priv->x_fill && priv->y_fill)
+ {
+ clutter_actor_allocate (priv->child, &allocation, flags);
+ return;
+ }
+
+ request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
+ g_object_get (G_OBJECT (priv->child), "request-mode", &request, NULL);
+
+ if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ clutter_actor_get_preferred_width (priv->child, available_height,
+ &min_width,
+ &natural_width);
+
+ child_width = CLAMP (natural_width, min_width, available_width);
+
+ clutter_actor_get_preferred_height (priv->child, child_width,
+ &min_height,
+ &natural_height);
+
+ child_height = CLAMP (natural_height, min_height, available_height);
+ }
+ else
+ {
+ clutter_actor_get_preferred_height (priv->child, available_width,
+ &min_height,
+ &natural_height);
+
+ child_height = CLAMP (natural_height, min_height, available_height);
+
+ clutter_actor_get_preferred_width (priv->child, child_height,
+ &min_width,
+ &natural_width);
+
+ child_width = CLAMP (natural_width, min_width, available_width);
+ }
+
+ if (!priv->x_fill)
+ {
+ allocation.x1 = (int) ((available_width - child_width) * x_align
+ + padding.left);
+ allocation.x2 = allocation.x1 + child_width;
+ }
+
+ if (!priv->y_fill)
+ {
+ allocation.y1 = (int) ((available_height - child_height) * y_align
+ + padding.top);
+ allocation.y2 = allocation.y1 + child_height;
+ }
+
+ clutter_actor_allocate (priv->child, &allocation, flags);
+ }
+}
+
+static void
+nbtk_bin_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+ gfloat min_width, natural_width;
+ NbtkPadding padding = { 0, };
+
+ nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+ min_width = natural_width = padding.left + padding.right;
+
+ if (priv->child == NULL)
+ {
+ if (min_width_p)
+ *min_width_p = min_width;
+
+ if (natural_width_p)
+ *natural_width_p = natural_width;
+ }
+ else
+ {
+ clutter_actor_get_preferred_width (priv->child, for_height,
+ min_width_p,
+ natural_width_p);
+
+ if (min_width_p)
+ *min_width_p += min_width;
+
+ if (natural_width_p)
+ *natural_width_p += natural_width;
+ }
+}
+
+static void
+nbtk_bin_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+ gfloat min_height, natural_height;
+ NbtkPadding padding = { 0, };
+
+ nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+ min_height = natural_height = padding.top + padding.bottom;
+
+ if (priv->child == NULL)
+ {
+ if (min_height_p)
+ *min_height_p = min_height;
+
+ if (natural_height_p)
+ *natural_height_p = natural_height;
+ }
+ else
+ {
+ clutter_actor_get_preferred_height (priv->child, for_width,
+ min_height_p,
+ natural_height_p);
+
+ if (min_height_p)
+ *min_height_p += min_height;
+
+ if (natural_height_p)
+ *natural_height_p += natural_height;
+ }
+}
+
+static void
+nbtk_bin_dispose (GObject *gobject)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (gobject)->priv;
+
+ if (priv->child)
+ {
+ clutter_actor_unparent (priv->child);
+ priv->child = NULL;
+ }
+
+ G_OBJECT_CLASS (nbtk_bin_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_bin_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkBin *bin = NBTK_BIN (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD:
+ nbtk_bin_set_child (bin, g_value_get_object (value));
+ break;
+
+ case PROP_X_ALIGN:
+ nbtk_bin_set_alignment (bin,
+ g_value_get_enum (value),
+ bin->priv->y_align);
+ break;
+
+ case PROP_Y_ALIGN:
+ nbtk_bin_set_alignment (bin,
+ bin->priv->x_align,
+ g_value_get_enum (value));
+ break;
+
+ case PROP_X_FILL:
+ nbtk_bin_set_fill (bin,
+ g_value_get_boolean (value),
+ bin->priv->y_fill);
+ break;
+
+ case PROP_Y_FILL:
+ nbtk_bin_set_fill (bin,
+ bin->priv->y_fill,
+ g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+nbtk_bin_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkBinPrivate *priv = NBTK_BIN (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_CHILD:
+ g_value_set_object (value, priv->child);
+ break;
+
+ case PROP_X_FILL:
+ g_value_set_boolean (value, priv->x_fill);
+ break;
+
+ case PROP_Y_FILL:
+ g_value_set_boolean (value, priv->y_fill);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+nbtk_bin_class_init (NbtkBinClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (NbtkBinPrivate));
+
+ gobject_class->set_property = nbtk_bin_set_property;
+ gobject_class->get_property = nbtk_bin_get_property;
+ gobject_class->dispose = nbtk_bin_dispose;
+
+ actor_class->get_preferred_width = nbtk_bin_get_preferred_width;
+ actor_class->get_preferred_height = nbtk_bin_get_preferred_height;
+ actor_class->allocate = nbtk_bin_allocate;
+ actor_class->paint = nbtk_bin_paint;
+ actor_class->pick = nbtk_bin_pick;
+
+ /**
+ * NbtkBin:child:
+ *
+ * The child #ClutterActor of the #NbtkBin container.
+ */
+ pspec = g_param_spec_object ("child",
+ "Child",
+ "The child of the Bin",
+ CLUTTER_TYPE_ACTOR,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
+
+ /**
+ * NbtkBin:x-align:
+ *
+ * The horizontal alignment of the #NbtkBin child.
+ */
+ pspec = g_param_spec_enum ("x-align",
+ "X Align",
+ "The horizontal alignment",
+ NBTK_TYPE_ALIGNMENT,
+ NBTK_ALIGN_CENTER,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
+
+ /**
+ * NbtkBin:y-align:
+ *
+ * The vertical alignment of the #NbtkBin child.
+ */
+ pspec = g_param_spec_enum ("y-align",
+ "Y Align",
+ "The vertical alignment",
+ NBTK_TYPE_ALIGNMENT,
+ NBTK_ALIGN_CENTER,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
+
+ /**
+ * NbtkBin:x-fill:
+ *
+ * Whether the child should fill the horizontal allocation
+ */
+ pspec = g_param_spec_boolean ("x-fill",
+ "X Fill",
+ "Whether the child should fill the "
+ "horizontal allocation",
+ FALSE,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_X_FILL, pspec);
+
+ /**
+ * NbtkBin:y-fill:
+ *
+ * Whether the child should fill the vertical allocation
+ */
+ pspec = g_param_spec_boolean ("y-fill",
+ "Y Fill",
+ "Whether the child should fill the "
+ "vertical allocation",
+ FALSE,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec);
+}
+
+static void
+nbtk_bin_init (NbtkBin *bin)
+{
+ bin->priv = NBTK_BIN_GET_PRIVATE (bin);
+
+ bin->priv->x_align = NBTK_ALIGN_CENTER;
+ bin->priv->y_align = NBTK_ALIGN_CENTER;
+}
+
+/**
+ * nbtk_bin_new:
+ *
+ * Creates a new #NbtkBin, a simple container for one child.
+ *
+ * Return value: the newly created #NbtkBin actor
+ */
+NbtkWidget *
+nbtk_bin_new (void)
+{
+ return g_object_new (NBTK_TYPE_BIN, NULL);
+}
+
+/**
+ * nbtk_bin_set_child:
+ * @bin: a #NbtkBin
+ * @child: a #ClutterActor, or %NULL
+ *
+ * Sets @child as the child of @bin.
+ *
+ * If @bin already has a child, the previous child is removed.
+ */
+void
+nbtk_bin_set_child (NbtkBin *bin,
+ ClutterActor *child)
+{
+ NbtkBinPrivate *priv;
+
+ g_return_if_fail (NBTK_IS_BIN (bin));
+ g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
+
+ priv = bin->priv;
+
+ if (priv->child == child)
+ return;
+
+ if (priv->child)
+ {
+ ClutterActor *old_child = priv->child;
+
+ g_object_ref (old_child);
+
+ priv->child = NULL;
+ clutter_actor_unparent (old_child);
+
+ g_signal_emit_by_name (bin, "actor-removed", old_child);
+
+ g_object_unref (old_child);
+ }
+
+ if (child)
+ {
+ priv->child = child;
+ clutter_actor_set_parent (child, CLUTTER_ACTOR (bin));
+
+ g_signal_emit_by_name (bin, "actor-added", priv->child);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+ g_object_notify (G_OBJECT (bin), "child");
+}
+
+/**
+ * nbtk_bin_get_child:
+ * @bin: a #NbtkBin
+ *
+ * Retrieves a pointer to the child of @bin.
+ *
+ * Return value: a #ClutterActor, or %NULL
+ */
+ClutterActor *
+nbtk_bin_get_child (NbtkBin *bin)
+{
+ g_return_val_if_fail (NBTK_IS_BIN (bin), NULL);
+
+ return bin->priv->child;
+}
+
+/**
+ * nbtk_bin_set_alignment:
+ * @bin: a #NbtkBin
+ * @x_align: horizontal alignment
+ * @y_align: vertical alignment
+ *
+ * Sets the horizontal and vertical alignment of the child
+ * inside a #NbtkBin.
+ */
+void
+nbtk_bin_set_alignment (NbtkBin *bin,
+ NbtkAlignment x_align,
+ NbtkAlignment y_align)
+{
+ NbtkBinPrivate *priv;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (NBTK_IS_BIN (bin));
+
+ priv = bin->priv;
+
+ g_object_freeze_notify (G_OBJECT (bin));
+
+ if (priv->x_align != x_align)
+ {
+ priv->x_align = x_align;
+ g_object_notify (G_OBJECT (bin), "x-align");
+ changed = TRUE;
+ }
+
+ if (priv->y_align != y_align)
+ {
+ priv->y_align = y_align;
+ g_object_notify (G_OBJECT (bin), "y-align");
+ changed = TRUE;
+ }
+
+ if (changed)
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+ g_object_thaw_notify (G_OBJECT (bin));
+}
+
+/**
+ * nbtk_bin_get_alignment:
+ * @bin: a #NbtkBin
+ * @x_align: return location for the horizontal alignment, or %NULL
+ * @y_align: return location for the vertical alignment, or %NULL
+ *
+ * Retrieves the horizontal and vertical alignment of the child
+ * inside a #NbtkBin, as set by nbtk_bin_set_alignment().
+ */
+void
+nbtk_bin_get_alignment (NbtkBin *bin,
+ NbtkAlignment *x_align,
+ NbtkAlignment *y_align)
+{
+ NbtkBinPrivate *priv;
+
+ g_return_if_fail (NBTK_IS_BIN (bin));
+
+ priv = bin->priv;
+
+ if (x_align)
+ *x_align = priv->x_align;
+
+ if (y_align)
+ *y_align = priv->y_align;
+}
+
+/**
+ * nbtk_bin_set_fill:
+ * @bin: a #NbtkBin
+ * @x_fill: %TRUE if the child should fill horizontally the @bin
+ * @y_fill: %TRUE if the child should fill vertically the @bin
+ *
+ * Sets whether the child of @bin should fill out the horizontal
+ * and/or vertical allocation of the parent
+ */
+void
+nbtk_bin_set_fill (NbtkBin *bin,
+ gboolean x_fill,
+ gboolean y_fill)
+{
+ NbtkBinPrivate *priv;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (NBTK_IS_BIN (bin));
+
+ priv = bin->priv;
+
+ g_object_freeze_notify (G_OBJECT (bin));
+
+ if (priv->x_fill != x_fill)
+ {
+ priv->x_fill = x_fill;
+ changed = TRUE;
+
+ g_object_notify (G_OBJECT (bin), "x-fill");
+ }
+
+ if (priv->y_fill != y_fill)
+ {
+ priv->y_fill = y_fill;
+ changed = TRUE;
+
+ g_object_notify (G_OBJECT (bin), "y-fill");
+ }
+
+ if (changed)
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+ g_object_thaw_notify (G_OBJECT (bin));
+}
+
+/**
+ * nbtk_bin_get_fill:
+ * @bin: a #NbtkBin
+ * @x_fill: (out): return location for the horizontal fill, or %NULL
+ * @y_fill: (out): return location for the vertical fill, or %NULL
+ *
+ * Retrieves the horizontal and vertical fill settings
+ */
+void
+nbtk_bin_get_fill (NbtkBin *bin,
+ gboolean *x_fill,
+ gboolean *y_fill)
+{
+ g_return_if_fail (NBTK_IS_BIN (bin));
+
+ if (x_fill)
+ *x_fill = bin->priv->x_fill;
+
+ if (y_fill)
+ *y_fill = bin->priv->y_fill;
+}
+
+static gpointer
+nbtk_padding_copy (gpointer data)
+{
+ return g_slice_dup (NbtkPadding, data);
+}
+
+static void
+nbtk_padding_free (gpointer data)
+{
+ if (G_LIKELY (data))
+ g_slice_free (NbtkPadding, data);
+}
+
+GType
+nbtk_padding_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (G_UNLIKELY (our_type == 0))
+ our_type = g_boxed_type_register_static (I_("NbtkPadding"),
+ nbtk_padding_copy,
+ nbtk_padding_free);
+
+ return our_type;
+}
diff --git a/src/nbtk/nbtk-bin.h b/src/nbtk/nbtk-bin.h
new file mode 100644
index 0000000..0e7e530
--- /dev/null
+++ b/src/nbtk/nbtk-bin.h
@@ -0,0 +1,92 @@
+/*
+ * nbtk-bin.h: Basic container actor
+ *
+ * Copyright 2009, 2008 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi linux intel com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_BIN_H__
+#define __NBTK_BIN_H__
+
+#include <nbtk/nbtk-types.h>
+#include <nbtk/nbtk-widget.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_BIN (nbtk_bin_get_type ())
+#define NBTK_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_BIN, NbtkBin))
+#define NBTK_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_BIN))
+#define NBTK_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_BIN, NbtkBinClass))
+#define NBTK_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_BIN))
+#define NBTK_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_BIN, NbtkBinClass))
+
+typedef struct _NbtkBin NbtkBin;
+typedef struct _NbtkBinPrivate NbtkBinPrivate;
+typedef struct _NbtkBinClass NbtkBinClass;
+
+/**
+ * NbtkBin:
+ *
+ * The #NbtkBin struct contains only private data
+ */
+struct _NbtkBin
+{
+ /*< private >*/
+ NbtkWidget parent_instance;
+
+ NbtkBinPrivate *priv;
+};
+
+/**
+ * NbtkBinClass:
+ *
+ * The #NbtkBinClass struct contains only private data
+ */
+struct _NbtkBinClass
+{
+ /*< private >*/
+ NbtkWidgetClass parent_class;
+};
+
+GType nbtk_bin_get_type (void) G_GNUC_CONST;
+
+NbtkWidget *nbtk_bin_new (void);
+void nbtk_bin_set_child (NbtkBin *bin,
+ ClutterActor *child);
+ClutterActor *nbtk_bin_get_child (NbtkBin *bin);
+void nbtk_bin_set_alignment (NbtkBin *bin,
+ NbtkAlignment x_align,
+ NbtkAlignment y_align);
+void nbtk_bin_get_alignment (NbtkBin *bin,
+ NbtkAlignment *x_align,
+ NbtkAlignment *y_align);
+void nbtk_bin_set_fill (NbtkBin *bin,
+ gboolean x_fill,
+ gboolean y_fill);
+void nbtk_bin_get_fill (NbtkBin *bin,
+ gboolean *x_fill,
+ gboolean *y_fill);
+
+G_END_DECLS
+
+#endif /* __NBTK_BIN_H__ */
diff --git a/src/nbtk/nbtk-enum-types.c.in b/src/nbtk/nbtk-enum-types.c.in
new file mode 100644
index 0000000..22a6177
--- /dev/null
+++ b/src/nbtk/nbtk-enum-types.c.in
@@ -0,0 +1,30 @@
+/*** BEGIN file-header ***/
+#include "nbtk-enum-types.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+#include "@filename@"
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ enum_name@_get_type(void) {
+ static GType enum_type_id = 0;
+ if (G_UNLIKELY (!enum_type_id))
+ {
+ static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ enum_type_id = g_ type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+ return enum_type_id;
+}
+/*** END value-tail ***/
diff --git a/src/nbtk/nbtk-enum-types.h.in b/src/nbtk/nbtk-enum-types.h.in
new file mode 100644
index 0000000..4befa1c
--- /dev/null
+++ b/src/nbtk/nbtk-enum-types.h.in
@@ -0,0 +1,29 @@
+/*** BEGIN file-header ***/
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_ENUM_TYPES_H__
+#define __NBTK_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* !__NBTK_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define NBTK_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+
+/*** END value-header ***/
diff --git a/src/nbtk/nbtk-marshal.list b/src/nbtk/nbtk-marshal.list
new file mode 100644
index 0000000..9b3bed6
--- /dev/null
+++ b/src/nbtk/nbtk-marshal.list
@@ -0,0 +1,12 @@
+VOID:OBJECT
+VOID:VOID
+VOID:PARAM
+VOID:POINTER
+VOID:UINT
+VOID:UINT,UINT
+VOID:OBJECT,OBJECT
+VOID:STRING,OBJECT
+VOID:OBJECT,OBJECT,INT,INT
+VOID:OBJECT,FLOAT,FLOAT,INT,ENUM
+VOID:FLOAT,FLOAT,INT,ENUM
+VOID:FLOAT,FLOAT
diff --git a/src/nbtk/nbtk-private.c b/src/nbtk/nbtk-private.c
new file mode 100644
index 0000000..9f1a8cb
--- /dev/null
+++ b/src/nbtk/nbtk-private.c
@@ -0,0 +1,111 @@
+#include "nbtk-private.h"
+
+/* Utility function to modify a child allocation box with respect to the
+ * x/y-fill child properties. Expects childbox to contain the available
+ * allocation space.
+ */
+void
+_nbtk_allocate_fill (ClutterActor *child,
+ ClutterActorBox *childbox,
+ NbtkAlign x_alignment,
+ NbtkAlign y_alignment,
+ gboolean x_fill,
+ gboolean y_fill)
+{
+ gfloat natural_width, natural_height;
+ gfloat min_width, min_height;
+ gfloat child_width, child_height;
+ gfloat available_width, available_height;
+ ClutterRequestMode request;
+ ClutterActorBox allocation = { 0, };
+ gdouble x_align, y_align;
+
+ if (x_alignment == NBTK_ALIGN_START)
+ x_align = 0.0;
+ else if (x_alignment == NBTK_ALIGN_MIDDLE)
+ x_align = 0.5;
+ else
+ x_align = 1.0;
+
+ if (y_alignment == NBTK_ALIGN_START)
+ y_align = 0.0;
+ else if (y_alignment == NBTK_ALIGN_MIDDLE)
+ y_align = 0.5;
+ else
+ y_align = 1.0;
+
+ available_width = childbox->x2 - childbox->x1;
+ available_height = childbox->y2 - childbox->y1;
+
+ if (available_width < 0)
+ available_width = 0;
+
+ if (available_height < 0)
+ available_height = 0;
+
+ if (x_fill)
+ {
+ allocation.x1 = childbox->x1;
+ allocation.x2 = (int) (allocation.x1 + available_width);
+ }
+
+ if (y_fill)
+ {
+ allocation.y1 = childbox->y1;
+ allocation.y2 = (int) (allocation.y1 + available_height);
+ }
+
+ /* if we are filling horizontally and vertically then we're done */
+ if (x_fill && y_fill)
+ {
+ *childbox = allocation;
+ return;
+ }
+
+ request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
+ g_object_get (G_OBJECT (child), "request-mode", &request, NULL);
+
+ if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ clutter_actor_get_preferred_width (child, available_height,
+ &min_width,
+ &natural_width);
+
+ child_width = CLAMP (natural_width, min_width, available_width);
+
+ clutter_actor_get_preferred_height (child, child_width,
+ &min_height,
+ &natural_height);
+
+ child_height = CLAMP (natural_height, min_height, available_height);
+ }
+ else
+ {
+ clutter_actor_get_preferred_height (child, available_width,
+ &min_height,
+ &natural_height);
+
+ child_height = CLAMP (natural_height, min_height, available_height);
+
+ clutter_actor_get_preferred_width (child, child_height,
+ &min_width,
+ &natural_width);
+
+ child_width = CLAMP (natural_width, min_width, available_width);
+ }
+
+ if (!x_fill)
+ {
+ allocation.x1 = childbox->x1 + (int) ((available_width - child_width) * x_align);
+ allocation.x2 = allocation.x1 + (int) child_width;
+ }
+
+ if (!y_fill)
+ {
+ allocation.y1 = childbox->y1 + (int) ((available_height - child_height) * y_align);
+ allocation.y2 = allocation.y1 + (int) child_height;
+ }
+
+ *childbox = allocation;
+
+}
diff --git a/src/nbtk/nbtk-private.h b/src/nbtk/nbtk-private.h
new file mode 100644
index 0000000..2a6579b
--- /dev/null
+++ b/src/nbtk/nbtk-private.h
@@ -0,0 +1,49 @@
+/*
+ * nbtk-private.h: Private declarations
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __NBTK_PRIVATE_H__
+#define __NBTK_PRIVATE_H__
+
+#include <glib.h>
+#include "nbtk-widget.h"
+#include "nbtk-bin.h"
+
+G_BEGIN_DECLS
+
+#define I_(str) (g_intern_static_string ((str)))
+
+#define NBTK_PARAM_READABLE \
+ (G_PARAM_READABLE | \
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+#define NBTK_PARAM_READWRITE \
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | \
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+G_END_DECLS
+
+ClutterActor *_nbtk_widget_get_dnd_clone (NbtkWidget *widget);
+void _nbtk_bin_get_align_factors (NbtkBin *bin, gdouble *x_align, gdouble *y_align);
+
+void _nbtk_allocate_fill (ClutterActor *child, ClutterActorBox *childbox, NbtkAlign x_align, NbtkAlign y_align, gboolean x_fill, gboolean y_fill);
+
+#endif /* __NBTK_PRIVATE_H__ */
diff --git a/src/nbtk/nbtk-stylable.c b/src/nbtk/nbtk-stylable.c
new file mode 100644
index 0000000..b3ca7fc
--- /dev/null
+++ b/src/nbtk/nbtk-stylable.c
@@ -0,0 +1,849 @@
+/*
+ * nbtk-stylable.c: Interface for stylable objects
+ *
+ * Copyright 2008 Intel Corporation
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ * Thomas Wood <thomas linux intel com>
+ *
+ */
+
+/**
+ * SECTION:nbtk-stylable
+ * @short_description: Interface for stylable objects
+ *
+ * Stylable objects are classes that can have "style properties", that is
+ * properties that can be changed by attaching a #NbtkStyle to them.
+ *
+ * Objects can choose to subclass #NbtkWidget, and thus inherit all the
+ * #NbtkWidget style properties; or they can subclass #NbtkWidget and
+ * reimplement the #NbtkStylable interface to add new style properties
+ * specific for them (and their subclasses); or, finally, they can simply
+ * subclass #GObject and implement #NbtkStylable to install new properties.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <gobject/gvaluecollector.h>
+#include <gobject/gobjectnotifyqueue.c>
+
+#include "nbtk-marshal.h"
+#include "nbtk-private.h"
+#include "nbtk-stylable.h"
+
+enum
+{
+ STYLE_CHANGED,
+ STYLE_NOTIFY,
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+static GObjectNotifyContext property_notify_context = { 0, };
+
+static GParamSpecPool *style_property_spec_pool = NULL;
+
+static GQuark quark_real_owner = 0;
+static GQuark quark_style = 0;
+
+static guint stylable_signals[LAST_SIGNAL] = { 0, };
+
+static void
+nbtk_stylable_notify_dispatcher (GObject *gobject,
+ guint n_pspecs,
+ GParamSpec **pspecs)
+{
+ guint i;
+
+ for (i = 0; i < n_pspecs; i++)
+ g_signal_emit (gobject, stylable_signals[STYLE_NOTIFY],
+ g_quark_from_string (pspecs[i]->name),
+ pspecs[i]);
+}
+
+static void
+nbtk_stylable_base_finalize (gpointer g_iface)
+{
+ GList *list, *node;
+
+ list = g_param_spec_pool_list_owned (style_property_spec_pool,
+ G_TYPE_FROM_INTERFACE (g_iface));
+
+ for (node = list; node; node = node->next)
+ {
+ GParamSpec *pspec = node->data;
+
+ g_param_spec_pool_remove (style_property_spec_pool, pspec);
+ g_param_spec_unref (pspec);
+ }
+
+ g_list_free (list);
+}
+
+static void
+nbtk_stylable_base_init (gpointer g_iface)
+{
+ static gboolean initialised = FALSE;
+
+ if (G_UNLIKELY (!initialised))
+ {
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+
+ initialised = TRUE;
+
+ quark_real_owner = g_quark_from_static_string ("nbtk-stylable-real-owner-quark");
+ quark_style = g_quark_from_static_string ("nbtk-stylable-style-quark");
+
+ style_property_spec_pool = g_param_spec_pool_new (FALSE);
+
+ property_notify_context.quark_notify_queue = g_quark_from_static_string ("NbtkStylable-style-property-notify-queue");
+ property_notify_context.dispatcher = nbtk_stylable_notify_dispatcher;
+
+ /**
+ * NbtkStylable:style:
+ *
+ * The #NbtkStyle attached to a stylable object.
+ */
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("style",
+ "Style",
+ "A style object",
+ NBTK_TYPE_STYLE,
+ NBTK_PARAM_READWRITE));
+
+ /**
+ * NbtkStylable::style-changed:
+ * @stylable: the #NbtkStylable that received the signal
+ * @old_style: the previously set #NbtkStyle for @stylable
+ *
+ * The ::style-changed signal is emitted each time one of the style
+ * properties have changed.
+ */
+ stylable_signals[STYLE_CHANGED] =
+ g_signal_new (I_("style-changed"),
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NbtkStylableIface, style_changed),
+ NULL, NULL,
+ _nbtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NbtkStylable::stylable-changed:
+ * @actor: the actor that received the signal
+ *
+ * The ::changed signal is emitted each time any of the properties of the
+ * stylable has changed.
+ */
+ stylable_signals[CHANGED] =
+ g_signal_new (I_("stylable-changed"),
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NbtkStylableIface, stylable_changed),
+ NULL, NULL,
+ _nbtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ stylable_signals[STYLE_NOTIFY] =
+ g_signal_new (I_("style-notify"),
+ iface_type,
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (NbtkStylableIface, style_notify),
+ NULL, NULL,
+ _nbtk_marshal_VOID__PARAM,
+ G_TYPE_NONE, 1,
+ G_TYPE_PARAM);
+ }
+}
+
+GType
+nbtk_stylable_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (G_UNLIKELY (our_type == 0))
+ {
+ GTypeInfo stylable_info = {
+ sizeof (NbtkStylableIface),
+ nbtk_stylable_base_init,
+ nbtk_stylable_base_finalize
+ };
+
+ our_type = g_type_register_static (G_TYPE_INTERFACE,
+ I_("NbtkStylable"),
+ &stylable_info, 0);
+ }
+
+ return our_type;
+}
+
+void
+nbtk_stylable_freeze_notify (NbtkStylable *stylable)
+{
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+
+ g_object_ref (stylable);
+ g_object_notify_queue_freeze (G_OBJECT (stylable), &property_notify_context);
+ g_object_unref (stylable);
+}
+
+void
+nbtk_stylable_thaw_notify (NbtkStylable *stylable)
+{
+ GObjectNotifyQueue *nqueue;
+
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+
+ g_object_ref (stylable);
+
+ nqueue = g_object_notify_queue_from_object (G_OBJECT (stylable),
+ &property_notify_context);
+
+ if (!nqueue || !nqueue->freeze_count)
+ g_warning ("%s: property-changed notification for %s(%p) is not frozen",
+ G_STRFUNC, G_OBJECT_TYPE_NAME (stylable), stylable);
+ else
+ g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
+
+ g_object_unref (stylable);
+}
+
+void
+nbtk_stylable_notify (NbtkStylable *stylable,
+ const gchar *property_name)
+{
+ GParamSpec *pspec;
+
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+ g_return_if_fail (property_name != NULL);
+
+ g_object_ref (stylable);
+
+ pspec = g_param_spec_pool_lookup (style_property_spec_pool,
+ property_name,
+ G_OBJECT_TYPE (stylable),
+ TRUE);
+
+ if (!pspec)
+ g_warning ("%s: object class `%s' has no style property named `%s'",
+ G_STRFUNC,
+ G_OBJECT_TYPE_NAME (stylable),
+ property_name);
+ else
+ {
+ GObjectNotifyQueue *nqueue;
+
+ nqueue = g_object_notify_queue_freeze (G_OBJECT (stylable),
+ &property_notify_context);
+ g_object_notify_queue_add (G_OBJECT (stylable), nqueue, pspec);
+ g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
+ }
+
+ g_object_unref (stylable);
+}
+
+/**
+ * nbtk_stylable_iface_install_property:
+ * @iface: a #NbtkStylableIface
+ * @owner_type: #GType of the style property owner
+ * @pspec: a #GParamSpec
+ *
+ * Installs a property for @owner_type using @pspec as the property
+ * description.
+ *
+ * This function should be used inside the #NbtkStylableIface initialization
+ * function of a class, for instance:
+ *
+ * <informalexample><programlisting>
+ * G_DEFINE_TYPE_WITH_CODE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR,
+ * G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE,
+ * nbtk_stylable_init));
+ * ...
+ * static void
+ * nbtk_stylable_init (NbtkStylableIface *iface)
+ * {
+ * static gboolean is_initialized = FALSE;
+ *
+ * if (!is_initialized)
+ * {
+ * ...
+ * nbtk_stylable_iface_install_property (stylable,
+ * FOO_TYPE_ACTOR,
+ * g_param_spec_int ("x-spacing",
+ * "X Spacing",
+ * "Horizontal spacing",
+ * -1, G_MAXINT,
+ * 2,
+ * G_PARAM_READWRITE));
+ * ...
+ * }
+ * }
+ * </programlisting></informalexample>
+ */
+void
+nbtk_stylable_iface_install_property (NbtkStylableIface *iface,
+ GType owner_type,
+ GParamSpec *pspec)
+{
+ g_return_if_fail (NBTK_IS_STYLABLE_IFACE (iface));
+ g_return_if_fail (owner_type != G_TYPE_INVALID);
+ g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+ g_return_if_fail (pspec->flags & G_PARAM_READABLE);
+ g_return_if_fail (!(pspec->flags & (G_PARAM_CONSTRUCT_ONLY | G_PARAM_CONSTRUCT
+)));
+
+ if (g_param_spec_pool_lookup (style_property_spec_pool, pspec->name,
+ owner_type,
+ FALSE))
+ {
+ g_warning ("%s: class `%s' already contains a style property named `%s'",
+ G_STRLOC,
+ g_type_name (owner_type),
+ pspec->name);
+ return;
+ }
+
+ g_param_spec_ref_sink (pspec);
+ g_param_spec_set_qdata_full (pspec, quark_real_owner,
+ g_strdup (g_type_name (owner_type)),
+ g_free);
+
+ g_param_spec_pool_insert (style_property_spec_pool,
+ pspec,
+ owner_type);
+}
+
+/**
+ * nbtk_stylable_list_properties:
+ * @stylable: a #NbtkStylable
+ * @n_props: return location for the number of properties, or %NULL
+ *
+ * Retrieves all the #GParamSpec<!-- -->s installed by @stylable.
+ *
+ * Return value: an array of #GParamSpec<!-- -->s. Free it with
+ * g_free() when done.
+ */
+GParamSpec **
+nbtk_stylable_list_properties (NbtkStylable *stylable,
+ guint *n_props)
+{
+ GParamSpec **pspecs = NULL;
+ guint n;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ pspecs = g_param_spec_pool_list (style_property_spec_pool,
+ G_OBJECT_TYPE (stylable),
+ &n);
+ if (n_props)
+ *n_props = n;
+
+ return pspecs;
+}
+
+/**
+ * nbtk_stylable_find_property:
+ * @stylable: a #NbtkStylable
+ * @property_name: the name of the property to find
+ *
+ * Finds the #GParamSpec installed by @stylable for the property
+ * with @property_name.
+ *
+ * Return value: a #GParamSpec for the given property, or %NULL if
+ * no property with that name was found
+ */
+GParamSpec *
+nbtk_stylable_find_property (NbtkStylable *stylable,
+ const gchar *property_name)
+{
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ return g_param_spec_pool_lookup (style_property_spec_pool,
+ property_name,
+ G_OBJECT_TYPE (stylable),
+ TRUE);
+}
+
+static inline void
+nbtk_stylable_get_property_internal (NbtkStylable *stylable,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ NbtkStyle *style;
+ GValue real_value = { 0, };
+
+ style = nbtk_stylable_get_style (stylable);
+
+ if (!style)
+ {
+ g_value_reset (value);
+ return;
+ }
+
+ nbtk_style_get_property (style, stylable, pspec, &real_value);
+
+ g_value_copy (&real_value, value);
+ g_value_unset (&real_value);
+
+}
+
+/**
+ * nbtk_stylable_get_property:
+ * @stylable: a #NbtkStylable
+ * @property_name: the name of the property
+ * @value: return location for an empty #GValue
+ *
+ * Retrieves the value of @property_name for @stylable, and puts it
+ * into @value.
+ */
+void
+nbtk_stylable_get_property (NbtkStylable *stylable,
+ const gchar *property_name,
+ GValue *value)
+{
+ GParamSpec *pspec;
+
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+ g_return_if_fail (property_name != NULL);
+ g_return_if_fail (value != NULL);
+
+ pspec = nbtk_stylable_find_property (stylable, property_name);
+ if (!pspec)
+ {
+ g_warning ("Stylable class `%s' doesn't have a property named `%s'",
+ g_type_name (G_OBJECT_TYPE (stylable)),
+ property_name);
+ return;
+ }
+
+ if (!(pspec->flags & G_PARAM_READABLE))
+ {
+ g_warning ("Style property `%s' of class `%s' is not readable",
+ pspec->name,
+ g_type_name (G_OBJECT_TYPE (stylable)));
+ return;
+ }
+
+ if (G_VALUE_TYPE (value) != G_PARAM_SPEC_VALUE_TYPE (pspec))
+ {
+ g_warning ("Passed value is not of the requested type `%s' for "
+ "the style property `%s' of class `%s'",
+ g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
+ pspec->name,
+ g_type_name (G_OBJECT_TYPE (stylable)));
+ return;
+ }
+
+ nbtk_stylable_get_property_internal (stylable, pspec, value);
+}
+
+/**
+ * nbtk_stylable_get:
+ * @stylable: a #NbtkStylable
+ * @first_property_name: name of the first property to get
+ * @Varargs: return location for the first property, followed optionally
+ * by more name/return location pairs, followed by %NULL
+ *
+ * Gets the style properties for @stylable.
+ *
+ * In general, a copy is made of the property contents and the called
+ * is responsible for freeing the memory in the appropriate manner for
+ * the property type.
+ *
+ * <example>
+ * <title>Using nbtk_stylable_get(<!-- -->)</title>
+ * <para>An example of using nbtk_stylable_get() to get the contents of
+ * two style properties - one of type #G_TYPE_INT and one of type
+ * #CLUTTER_TYPE_COLOR:</para>
+ * <programlisting>
+ * gint x_spacing;
+ * ClutterColor *bg_color;
+ *
+ * nbtk_stylable_get (stylable,
+ * "x-spacing", &x_spacing,
+ * "bg-color", &bg_color,
+ * NULL);
+ *
+ * /<!-- -->* do something with x_spacing and bg_color *<!-- -->/
+ *
+ * clutter_color_free (bg_color);
+ * </programlisting>
+ * </example>
+ */
+void
+nbtk_stylable_get (NbtkStylable *stylable,
+ const gchar *first_property_name,
+ ...)
+{
+ NbtkStyle *style;
+ va_list args;
+
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+ g_return_if_fail (first_property_name != NULL);
+
+ style = nbtk_stylable_get_style (stylable);
+
+ va_start (args, first_property_name);
+ nbtk_style_get_valist (style, stylable, first_property_name, args);
+ va_end (args);
+}
+
+/**
+ * nbtk_stylable_get_default_value:
+ * @stylable: a #NbtkStylable
+ * @property_name: name of the property to query
+ * @value_out: return location for the default value
+ *
+ * Query @stylable for the default value of property @property_name and
+ * fill @value_out with the result.
+ *
+ * Returns: %TRUE if property @property_name exists and the default value has
+ * been returned.
+ */
+gboolean
+nbtk_stylable_get_default_value (NbtkStylable *stylable,
+ const gchar *property_name,
+ GValue *value_out)
+{
+ GParamSpec *pspec;
+
+ pspec = nbtk_stylable_find_property (stylable, property_name);
+ if (!pspec)
+ {
+ g_warning ("%s: no style property named `%s' found for class `%s'",
+ G_STRLOC,
+ property_name,
+ g_type_name (G_OBJECT_TYPE (stylable)));
+ return FALSE;
+ }
+
+ if (!(pspec->flags & G_PARAM_READABLE))
+ {
+ g_warning ("Style property `%s' of class `%s' is not readable",
+ pspec->name,
+ g_type_name (G_OBJECT_TYPE (stylable)));
+ return FALSE;
+ }
+
+ g_value_init (value_out, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ g_param_value_set_default (pspec, value_out);
+ return TRUE;
+}
+
+/**
+ * nbtk_stylable_get_style:
+ * @stylable: a #NbtkStylable
+ *
+ * Retrieves the #NbtkStyle used by @stylable. This function does not
+ * alter the reference count of the returned object.
+ *
+ * Return value: a #NbtkStyle
+ */
+NbtkStyle *
+nbtk_stylable_get_style (NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+ if (iface->get_style)
+ return iface->get_style (stylable);
+
+ return g_object_get_data (G_OBJECT (stylable), "nbtk-stylable-style");
+}
+
+/**
+ * nbtk_stylable_set_style:
+ * @stylable: a #NbtkStylable
+ * @style: a #NbtkStyle
+ *
+ * Sets @style as the new #NbtkStyle to be used by @stylable.
+ *
+ * The #NbtkStylable will take ownership of the passed #NbtkStyle.
+ *
+ * After the #NbtkStle has been set, the NbtkStylable::style-set signal
+ * will be emitted.
+ */
+void
+nbtk_stylable_set_style (NbtkStylable *stylable,
+ NbtkStyle *style)
+{
+ NbtkStylableIface *iface;
+ NbtkStyle *old_style;
+
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+ g_return_if_fail (NBTK_IS_STYLE (style));
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ old_style = nbtk_stylable_get_style (stylable);
+ g_object_ref (old_style);
+
+ if (iface->set_style)
+ iface->set_style (stylable, style);
+ else
+ {
+ g_object_set_qdata_full (G_OBJECT (stylable),
+ quark_style,
+ g_object_ref_sink (style),
+ g_object_unref);
+ }
+
+ g_signal_emit (stylable, stylable_signals[STYLE_CHANGED], 0, old_style);
+ g_object_unref (old_style);
+
+ g_object_notify (G_OBJECT (stylable), "style");
+}
+
+/**
+ * nbtk_stylable_get_container:
+ * @stylable: a #NbtkStylable
+ *
+ * Obtain the parent #NbtkStylable that contains @stylable.
+ *
+ * Return value: The parent #NbtkStylable
+ */
+NbtkStylable*
+nbtk_stylable_get_container (NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ if (iface->get_container)
+ return iface->get_container (stylable);
+ else
+ return NULL;
+}
+
+/**
+ * nbtk_stylable_get_base_style:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the parent ancestor #NbtkStylable of @stylable.
+ *
+ * Return value: the parent #NbtkStylable
+ */
+NbtkStylable*
+nbtk_stylable_get_base_style (NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ if (iface->get_base_style)
+ return iface->get_base_style (stylable);
+ else
+ return NULL;
+}
+
+
+/**
+ * nbtk_stylable_get_style_id:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the ID value of @stylable
+ *
+ * Return value: the id of @stylable
+ */
+const gchar*
+nbtk_stylable_get_style_id (NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ if (iface->get_style_id)
+ return iface->get_style_id (stylable);
+ else
+ return NULL;
+}
+
+/**
+ * nbtk_stylable_get_style_type:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the type name of @stylable
+ *
+ * Return value: the type name of @stylable
+ */
+const gchar*
+nbtk_stylable_get_style_type (NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ if (iface->get_style_type)
+ return iface->get_style_type (stylable);
+ else
+ return G_OBJECT_TYPE_NAME (stylable);
+}
+
+/**
+ * nbtk_stylable_get_style_class:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the style class name of @stylable
+ *
+ * Return value: the type name of @stylable
+ */
+const gchar*
+nbtk_stylable_get_style_class (NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ if (iface->get_style_class)
+ return iface->get_style_class (stylable);
+ else
+ return NULL;
+}
+
+/**
+ * nbtk_stylable_get_pseudo_class:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the pseudo class name of @stylable
+ *
+ * Return value: the pseudo class name of @stylable
+ */
+const gchar*
+nbtk_stylable_get_pseudo_class (NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ if (iface->get_pseudo_class)
+ return iface->get_pseudo_class (stylable);
+ else
+ return NULL;
+}
+
+/**
+ * nbtk_stylable_get_attribute:
+ * @stylable: a #NbtkStylable
+ * @name: attribute name
+ *
+ * Get the named attribute from @stylable
+ *
+ * Return value: the value of the attribute
+ */
+gchar*
+nbtk_stylable_get_attribute (NbtkStylable *stylable,
+ const gchar *name)
+{
+ NbtkStylableIface *iface;
+ GValue value = { 0, };
+ GValue string_value = { 0, };
+ gchar *ret;
+ GParamSpec *pspec;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+ if (iface->get_attribute)
+ return iface->get_attribute (stylable, name);
+
+ /* look up a generic gobject property */
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (stylable), name);
+
+ /* if no such property exists, return NULL */
+ if (pspec == NULL)
+ return NULL;
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ g_object_get_property (G_OBJECT (stylable), name, &value);
+
+ g_value_init (&string_value, G_TYPE_STRING);
+ if (g_value_transform (&value, &string_value))
+ ret = g_strdup (g_value_get_string (&string_value));
+ else
+ ret = NULL;
+
+ g_value_unset (&value);
+ g_value_unset (&string_value);
+
+ return ret;
+}
+
+/**
+ * nbtk_stylable_get_viewport:
+ * @stylable: a #NbtkStylable
+ * @x: location to store X coordinate
+ * @y: location to store Y coordinate
+ * @width: location to store width
+ * @height: location to store height
+ *
+ * Obtain the position and dimensions of @stylable.
+ *
+ * Return value: true if the function succeeded
+ */
+gboolean
+nbtk_stylable_get_viewport (NbtkStylable *stylable,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height)
+{
+ NbtkStylableIface *iface;
+
+ g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), FALSE);
+
+ iface = NBTK_STYLABLE_GET_IFACE (stylable);
+ if (iface->get_viewport)
+ return iface->get_viewport (stylable, x, y, width, height);
+ else
+ return FALSE;
+}
+
+
+/**
+ * nbtk_stylable_changed:
+ * @stylable: A #NbtkStylable
+ *
+ * Emit the "stylable-changed" signal on @stylable
+ */
+void
+nbtk_stylable_changed (NbtkStylable *stylable)
+{
+ g_signal_emit (stylable, stylable_signals[CHANGED], 0, NULL);
+}
diff --git a/src/nbtk/nbtk-stylable.h b/src/nbtk/nbtk-stylable.h
new file mode 100644
index 0000000..9a8dd1e
--- /dev/null
+++ b/src/nbtk/nbtk-stylable.h
@@ -0,0 +1,123 @@
+/*
+ * nbtk-stylable.h: Interface for stylable objects
+ *
+ * Copyright 2008, 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ * Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_STYLABLE_H__
+#define __NBTK_STYLABLE_H__
+
+#include <glib-object.h>
+#include <nbtk/nbtk-style.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_STYLABLE (nbtk_stylable_get_type ())
+#define NBTK_STYLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_STYLABLE, NbtkStylable))
+#define NBTK_IS_STYLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_STYLABLE))
+#define NBTK_STYLABLE_IFACE(iface) (G_TYPE_CHECK_CLASS_CAST ((iface), NBTK_TYPE_STYLABLE, NbtkStylableIface))
+#define NBTK_IS_STYLABLE_IFACE(iface) (G_TYPE_CHECK_CLASS_TYPE ((iface), NBTK_TYPE_STYLABLE))
+#define NBTK_STYLABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NBTK_TYPE_STYLABLE, NbtkStylableIface))
+
+/* NbtkStylableIface is defined in nbtk-style.h */
+
+struct _NbtkStylableIface
+{
+ GTypeInterface g_iface;
+
+ /* virtual functions */
+ NbtkStyle *(* get_style) (NbtkStylable *stylable);
+ void (* set_style) (NbtkStylable *stylable,
+ NbtkStyle *style);
+
+ /* context virtual functions */
+ NbtkStylable *(*get_container) (NbtkStylable *stylable);
+ NbtkStylable *(*get_base_style) (NbtkStylable *stylable);
+ const gchar *(*get_style_id) (NbtkStylable *stylable);
+ const gchar *(*get_style_type) (NbtkStylable *stylable);
+ const gchar *(*get_style_class) (NbtkStylable *stylable);
+ const gchar *(*get_pseudo_class) (NbtkStylable *stylable);
+ gchar *(*get_attribute) (NbtkStylable *stylable,
+ const gchar *name);
+ gboolean (*get_viewport) (NbtkStylable *stylable,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height);
+
+ /* signals, not vfuncs */
+ void (* style_notify) (NbtkStylable *stylable,
+ GParamSpec *pspec);
+ void (* style_changed) (NbtkStylable *stylable);
+
+ void (* stylable_changed) (NbtkStylable *stylable);
+};
+
+GType nbtk_stylable_get_type (void) G_GNUC_CONST;
+
+void nbtk_stylable_iface_install_property (NbtkStylableIface *iface,
+ GType owner_type,
+ GParamSpec *pspec);
+
+void nbtk_stylable_freeze_notify (NbtkStylable *stylable);
+void nbtk_stylable_notify (NbtkStylable *stylable,
+ const gchar *property_name);
+void nbtk_stylable_thaw_notify (NbtkStylable *stylable);
+GParamSpec **nbtk_stylable_list_properties (NbtkStylable *stylable,
+ guint *n_props);
+GParamSpec * nbtk_stylable_find_property (NbtkStylable *stylable,
+ const gchar *property_name);
+void nbtk_stylable_set_style (NbtkStylable *stylable,
+ NbtkStyle *style);
+NbtkStyle * nbtk_stylable_get_style (NbtkStylable *stylable);
+
+void nbtk_stylable_get (NbtkStylable *stylable,
+ const gchar *first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+void nbtk_stylable_get_property (NbtkStylable *stylable,
+ const gchar *property_name,
+ GValue *value);
+gboolean nbtk_stylable_get_default_value (NbtkStylable *stylable,
+ const gchar *property_name,
+ GValue *value_out);
+
+NbtkStylable* nbtk_stylable_get_container (NbtkStylable *stylable);
+NbtkStylable* nbtk_stylable_get_base_style (NbtkStylable *stylable);
+const gchar* nbtk_stylable_get_style_id (NbtkStylable *stylable);
+const gchar* nbtk_stylable_get_style_type (NbtkStylable *stylable);
+const gchar* nbtk_stylable_get_style_class (NbtkStylable *stylable);
+const gchar* nbtk_stylable_get_pseudo_class (NbtkStylable *stylable);
+gchar* nbtk_stylable_get_attribute (NbtkStylable *stylable,
+ const gchar *name);
+gboolean nbtk_stylable_get_viewport (NbtkStylable *stylable,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height);
+
+void nbtk_stylable_changed (NbtkStylable *stylable);
+G_END_DECLS
+
+#endif /* __NBTK_STYLABLE_H__ */
diff --git a/src/nbtk/nbtk-style.c b/src/nbtk/nbtk-style.c
new file mode 100644
index 0000000..f5eeb3b
--- /dev/null
+++ b/src/nbtk/nbtk-style.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/**
+ * SECTION:nbtk-style
+ * @short_description: a data store for style properties
+ *
+ * #NbtkStyle is a property data store that can read properties from a style
+ * sheet. It is queried with objects that implement the NbtkStylable
+ * interface.
+ */
+
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <gobject/gvaluecollector.h>
+#include <glib/gi18n-lib.h>
+
+#include <clutter/clutter.h>
+
+#include <ccss/ccss.h>
+
+#include "nbtk-stylable.h"
+#include "nbtk-style.h"
+#include "nbtk-types.h"
+#include "nbtk-marshal.h"
+#include "nbtk-widget.h"
+
+enum
+{
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+#define NBTK_STYLE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_STYLE, NbtkStylePrivate))
+
+#define NBTK_STYLE_ERROR g_style_error_quark ()
+
+typedef struct {
+ GType value_type;
+ gchar *value_name;
+ GValue value;
+} StyleProperty;
+
+struct _NbtkStylePrivate
+{
+ ccss_stylesheet_t *stylesheet;
+ GList *image_paths;
+
+ GHashTable *style_hash;
+ GHashTable *node_hash;
+};
+
+typedef struct {
+ ccss_node_t parent;
+ NbtkStylable *stylable;
+ NbtkStylableIface *iface;
+} nbtk_style_node_t;
+
+static ccss_function_t const * peek_css_functions (void);
+
+static ccss_node_class_t * peek_node_class (void);
+
+static guint style_signals[LAST_SIGNAL] = { 0, };
+
+static NbtkStyle *default_style = NULL;
+
+G_DEFINE_TYPE (NbtkStyle, nbtk_style, G_TYPE_OBJECT);
+
+static GQuark
+g_style_error_quark (void)
+{
+ return g_quark_from_static_string ("nbtk-style-error-quark");
+}
+
+static gboolean
+nbtk_style_real_load_from_file (NbtkStyle *style,
+ const gchar *filename,
+ GError **error,
+ gint priority)
+{
+ NbtkStylePrivate *priv;
+ ccss_grammar_t *grammar;
+ GError *internal_error;
+ gchar *path;
+ GList *l;
+
+ g_return_val_if_fail (NBTK_IS_STYLE (style), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ priv = NBTK_STYLE (style)->priv;
+
+ if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ {
+ internal_error = g_error_new (NBTK_STYLE_ERROR,
+ NBTK_STYLE_ERROR_INVALID_FILE,
+ _("Invalid theme file '%s'"), filename);
+ g_propagate_error (error, internal_error);
+ return FALSE;
+ }
+
+
+ /* add the path of the stylesheet to the search path */
+ path = g_path_get_dirname (filename);
+
+ /* make sure path is valid */
+ if (!path)
+ return TRUE;
+
+ for (l = priv->image_paths; l; l = l->next)
+ {
+ if (g_str_equal ((gchar *)l->data, path))
+ {
+ /* we have this path already */
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ /* Add the new path */
+ if (path)
+ priv->image_paths = g_list_append (priv->image_paths, path);
+
+ /* now load the stylesheet */
+ if (!priv->stylesheet)
+ {
+ grammar = ccss_grammar_create_css ();
+ ccss_grammar_add_functions (grammar, peek_css_functions ());
+ priv->stylesheet = ccss_grammar_create_stylesheet_from_file (grammar,
+ filename,
+ path);
+ ccss_grammar_destroy (grammar);
+ }
+ else
+ {
+ ccss_stylesheet_add_from_file (priv->stylesheet, filename, priority, path);
+ }
+
+ g_signal_emit (style, style_signals[CHANGED], 0, NULL);
+
+ return TRUE;
+}
+
+/**
+ * nbtk_style_load_from_file:
+ * @style: a #NbtkStyle
+ * @filename: filename of the style sheet to load
+ * @error: a #GError or #NULL
+ *
+ * Load style information from the specified file.
+ *
+ * returns: TRUE if the style information was loaded successfully. Returns
+ * FALSE on error.
+ */
+gboolean
+nbtk_style_load_from_file (NbtkStyle *style,
+ const gchar *filename,
+ GError **error)
+{
+ return nbtk_style_real_load_from_file (style, filename, error,
+ CCSS_STYLESHEET_AUTHOR);
+}
+
+static void
+nbtk_style_finalize (GObject *gobject)
+{
+ NbtkStylePrivate *priv = ((NbtkStyle *)gobject)->priv;
+ GList *l;
+
+ for (l = priv->image_paths; l; l = g_list_delete_link (l, l))
+ {
+ g_free (l->data);
+ }
+
+ G_OBJECT_CLASS (nbtk_style_parent_class)->finalize (gobject);
+}
+
+static void
+nbtk_style_class_init (NbtkStyleClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NbtkStylePrivate));
+
+ gobject_class->finalize = nbtk_style_finalize;
+
+ /**
+ * NbtkStyle::changed:
+ *
+ * Indicates that the style data has changed in some way. For example, a new
+ * stylesheet may have been loaded.
+ */
+
+ style_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NbtkStyleClass, changed),
+ NULL, NULL,
+ _nbtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+/* url loader for libccss */
+static char *
+ccss_url (GSList const *args,
+ void *user_data)
+{
+ const gchar *given_path, *filename;
+ gchar *test_path;
+
+ g_return_val_if_fail (args, NULL);
+
+ given_path = (char const *) args->data;
+
+ /* we can only deal with local paths */
+ if (!g_str_has_prefix (given_path, "file://"))
+ return NULL;
+ filename = &given_path[7];
+
+ /*
+ * Handle absolute paths correctly
+ */
+ if (*filename == '/')
+ return strdup (filename);
+
+ /* first try looking in the theme dir */
+ test_path = g_build_filename (g_get_user_config_dir (),
+ "nbtk",
+ filename,
+ NULL);
+ if (g_file_test (test_path, G_FILE_TEST_IS_REGULAR))
+ return test_path;
+ g_free (test_path);
+
+ if (user_data)
+ {
+ test_path = g_build_filename ((gchar *) user_data, filename, NULL);
+
+ if (g_file_test (test_path, G_FILE_TEST_IS_REGULAR))
+ return test_path;
+
+ g_free (test_path);
+ }
+ else
+ {
+ g_warning ("No path available css url resolver!");
+ }
+
+ /* couldn't find the image anywhere, so just return the filename */
+ return strdup (given_path);
+}
+
+static ccss_function_t const *
+peek_css_functions (void)
+{
+ static ccss_function_t const ccss_functions[] =
+ {
+ { "url", ccss_url },
+ { NULL }
+ };
+
+ return ccss_functions;
+}
+
+
+static void
+nbtk_style_init (NbtkStyle *style)
+{
+ NbtkStylePrivate *priv;
+
+ style->priv = priv = NBTK_STYLE_GET_PRIVATE (style);
+
+ /* create a hash table to look up pointer keys and values */
+ style->priv->node_hash = g_hash_table_new_full (NULL, NULL,
+ NULL, g_free);
+ style->priv->style_hash = g_hash_table_new_full (NULL, NULL,
+ NULL, (GDestroyNotify) ccss_style_destroy);
+
+}
+
+/**
+ * nbtk_style_new:
+ *
+ * Creates a new #NbtkStyle object. This must be freed using #g_object_unref
+ * when no longer required.
+ *
+ * Returns: a newly allocated #NbtkStyle
+ */
+NbtkStyle *
+nbtk_style_new (void)
+{
+ return g_object_new (NBTK_TYPE_STYLE, NULL);
+}
+
+/**
+ * nbtk_style_get_default:
+ *
+ * Return the default NbtkStyle object. This includes the current theme (if
+ * any).
+ *
+ * Returns: a #NbtkStyle object. This must not be freed or unref'd by
+ * applications
+ */
+NbtkStyle *
+nbtk_style_get_default (void)
+{
+ if (G_LIKELY (default_style))
+ return default_style;
+
+ default_style = g_object_new (NBTK_TYPE_STYLE, NULL);
+
+ return default_style;
+}
+
+/* functions for ccss */
+
+static nbtk_style_node_t *
+get_container (nbtk_style_node_t *node)
+{
+ nbtk_style_node_t *container;
+ ClutterActor *parent;
+
+ g_return_val_if_fail (node, NULL);
+ g_return_val_if_fail (node->iface, NULL);
+ g_return_val_if_fail (node->stylable, NULL);
+
+ parent = clutter_actor_get_parent (CLUTTER_ACTOR (node->stylable));
+ while (parent && !NBTK_IS_WIDGET (parent))
+ parent = clutter_actor_get_parent (CLUTTER_ACTOR (parent));
+
+ if (!parent)
+ return NULL;
+
+ container = g_new0 (nbtk_style_node_t, 1);
+ ccss_node_init ((ccss_node_t*) container, peek_node_class ());
+ container->iface = node->iface;
+ container->stylable = NBTK_STYLABLE (parent);
+
+ return container;
+}
+
+static const gchar*
+get_style_id (nbtk_style_node_t *node)
+{
+ return nbtk_stylable_get_style_id (node->stylable);
+}
+
+static const gchar*
+get_style_type (nbtk_style_node_t *node)
+{
+ return nbtk_stylable_get_style_type (node->stylable);
+}
+
+static const gchar*
+get_style_class (nbtk_style_node_t *node)
+{
+ return nbtk_stylable_get_style_class (node->stylable);
+}
+
+static const gchar*
+get_pseudo_class (nbtk_style_node_t *node)
+{
+ return nbtk_stylable_get_pseudo_class (node->stylable);
+}
+
+static const gchar*
+get_attribute (nbtk_style_node_t *node, const char *name)
+{
+ return nbtk_stylable_get_attribute (node->stylable, name);
+}
+
+static void
+release (nbtk_style_node_t *node)
+{
+ g_return_if_fail (node);
+
+ g_free (node);
+}
+
+static ccss_node_class_t *
+peek_node_class (void)
+{
+ static ccss_node_class_t _node_class = {
+ .is_a = NULL,
+ .get_container = (ccss_node_get_container_f) get_container,
+ .get_id = (ccss_node_get_id_f) get_style_id,
+ .get_type = (ccss_node_get_type_f) get_style_type,
+ .get_class = (ccss_node_get_class_f) get_style_class,
+ .get_pseudo_class = (ccss_node_get_pseudo_class_f) get_pseudo_class,
+ .get_viewport = NULL,// (ccss_node_get_viewport_f) get_viewport,
+ .get_attribute = (ccss_node_get_attribute_f) get_attribute,
+ .release = (ccss_node_release_f) release
+ };
+
+ return &_node_class;
+}
+
+static void
+nbtk_style_fetch_ccss_property (ccss_style_t *ccss_style,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ gboolean value_set = FALSE;
+
+ g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+ if (G_PARAM_SPEC_VALUE_TYPE (pspec))
+ {
+ double number;
+
+ if (G_IS_PARAM_SPEC_INT (pspec))
+ {
+ if (ccss_style_get_double (ccss_style, pspec->name, &number))
+ {
+ g_value_set_int (value, (gint) number);
+ value_set = TRUE;
+ }
+ }
+ else if (G_IS_PARAM_SPEC_UINT (pspec))
+ {
+ if (ccss_style_get_double (ccss_style, pspec->name, &number))
+ {
+ g_value_set_uint (value, (guint) number);
+ value_set = TRUE;
+ }
+ }
+ else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == NBTK_TYPE_BORDER_IMAGE &&
+ !g_strcmp0 ("border-image", pspec->name))
+ {
+ ccss_border_image_t const *border_image;
+
+ if (ccss_style_get_property (ccss_style,
+ "border-image",
+ (ccss_property_base_t const **) &border_image))
+ {
+ if (border_image &&
+ border_image->base.state == CCSS_PROPERTY_STATE_SET)
+ {
+ g_value_set_boxed (value, border_image);
+ value_set = TRUE;
+ }
+ }
+ }
+ else if (NBTK_TYPE_PADDING == G_PARAM_SPEC_VALUE_TYPE (pspec) &&
+ 0 == g_strcmp0 ("padding", pspec->name))
+ {
+ NbtkPadding padding = { 0, };
+ gboolean padding_set = 0;
+
+ if (ccss_style_get_double (ccss_style, "padding-top", &number))
+ {
+ padding.top = number;
+ padding_set = TRUE;
+ }
+
+ if (ccss_style_get_double (ccss_style, "padding-right", &number))
+ {
+ padding.right = number;
+ padding_set = TRUE;
+ }
+
+ if (ccss_style_get_double (ccss_style, "padding-bottom", &number))
+ {
+ padding.bottom = number;
+ padding_set = TRUE;
+ }
+
+ if (ccss_style_get_double (ccss_style, "padding-left", &number))
+ {
+ padding.left = number;
+ padding_set = TRUE;
+ }
+
+ if (padding_set)
+ {
+ g_value_set_boxed (value, &padding);
+ value_set = TRUE;
+ }
+ }
+ else
+ {
+ gchar *string = NULL;
+
+ ccss_style_get_string (ccss_style, pspec->name, &string);
+
+ if (string)
+ {
+ if (CLUTTER_IS_PARAM_SPEC_COLOR (pspec))
+ {
+ ClutterColor color = { 0, };
+
+ clutter_color_from_string (&color, string);
+ clutter_value_set_color (value, &color);
+
+ value_set = TRUE;
+ }
+ else
+ if (G_IS_PARAM_SPEC_STRING (pspec))
+ {
+ g_value_set_string (value, string);
+ value_set = TRUE;
+ }
+ g_free (string);
+ }
+ }
+ }
+
+ /* no value was found in css, so copy in the default value */
+ if (!value_set)
+ g_param_value_set_default (pspec, value);
+}
+
+static ccss_style_t*
+nbtk_style_get_ccss_query (NbtkStyle *style,
+ NbtkStylable *stylable)
+{
+ NbtkStylableIface *iface = NBTK_STYLABLE_GET_IFACE (stylable);
+ ccss_style_t *ccss_style;
+ nbtk_style_node_t *ccss_node;
+
+ ccss_node = g_hash_table_lookup (style->priv->node_hash, stylable);
+
+ if (!ccss_node)
+ {
+ ccss_node = g_new0 (nbtk_style_node_t, 1);
+ ccss_node_init ((ccss_node_t*) ccss_node, peek_node_class ());
+ ccss_node->iface = iface;
+ ccss_node->stylable = stylable;
+
+ g_hash_table_insert (style->priv->node_hash, stylable, ccss_node);
+ g_signal_connect_swapped (stylable, "stylable-changed",
+ G_CALLBACK (g_hash_table_remove),
+ style->priv->node_hash);
+
+
+ g_object_weak_ref ((GObject*) stylable,
+ (GWeakNotify) g_hash_table_remove, style->priv->node_hash);
+ }
+
+
+ ccss_style = g_hash_table_lookup (style->priv->style_hash, stylable);
+
+ if (!ccss_style)
+ {
+ ccss_style = ccss_stylesheet_query (style->priv->stylesheet,
+ (ccss_node_t *) ccss_node);
+
+ g_hash_table_insert (style->priv->style_hash, stylable, ccss_style);
+
+ /* remove the cache if the stylable changes */
+ g_signal_connect_swapped (stylable, "stylable-changed",
+ G_CALLBACK (g_hash_table_remove),
+ style->priv->style_hash);
+
+ g_object_weak_ref ((GObject*) stylable,
+ (GWeakNotify) g_hash_table_remove, style->priv->style_hash);
+ }
+
+ return ccss_style;
+
+}
+
+
+/**
+ * nbtk_style_get_property:
+ * @style: the style data store object
+ * @stylable: a stylable to retreive the data for
+ * @pspec: a #GParamSpec describing the property required
+ * @value: a #GValue to place the return value in
+ *
+ * Requests the property described in @pspec for the specified stylable
+ */
+
+void
+nbtk_style_get_property (NbtkStyle *style,
+ NbtkStylable *stylable,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ NbtkStylePrivate *priv;
+ gboolean value_set = FALSE;
+
+ g_return_if_fail (NBTK_IS_STYLE (style));
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+ g_return_if_fail (pspec != NULL);
+ g_return_if_fail (value != NULL);
+
+ priv = style->priv;
+
+ /* look up the property in the css */
+ if (priv->stylesheet)
+ {
+ ccss_style_t *ccss_style;
+
+ ccss_style = nbtk_style_get_ccss_query (style, stylable);
+ if (ccss_style)
+ {
+ nbtk_style_fetch_ccss_property (ccss_style, pspec, value);
+ value_set = TRUE;
+ }
+ }
+
+ /* no value was found in css, so copy in the default value */
+ if (!value_set)
+ {
+ g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ g_param_value_set_default (pspec, value);
+ }
+}
+
+/**
+ * nbtk_style_get_valist:
+ * @style: a #NbtkStyle
+ * @stylable: a #NbtkStylable
+ * @first_property_name: name of the first property to get
+ * @va_args: return location for the first property, followed optionally
+ * by more name/return location pairs, followed by %NULL
+ *
+ * Gets the style properties for @stylable from @style.
+ *
+ * Please refer to nbtk_style_get() for further information.
+ */
+void
+nbtk_style_get_valist (NbtkStyle *style,
+ NbtkStylable *stylable,
+ const gchar *first_property_name,
+ va_list va_args)
+{
+ NbtkStylePrivate *priv;
+ const gchar *name = first_property_name;
+ gboolean values_set = FALSE;
+
+ g_return_if_fail (NBTK_IS_STYLE (style));
+ g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+ g_return_if_fail (style->priv != NULL);
+
+ priv = style->priv;
+
+ /* look up the property in the css */
+ if (priv->stylesheet)
+ {
+ ccss_style_t *ccss_style;
+
+ ccss_style = nbtk_style_get_ccss_query (style, stylable);
+
+ if (ccss_style)
+ {
+ while (name)
+ {
+ GValue value = { 0, };
+ gchar *error = NULL;
+ GParamSpec *pspec = nbtk_stylable_find_property (stylable, name);
+ nbtk_style_fetch_ccss_property (ccss_style, pspec, &value);
+ G_VALUE_LCOPY (&value, va_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+ g_value_unset (&value);
+ break;
+ }
+ g_value_unset (&value);
+ name = va_arg (va_args, gchar*);
+ }
+ values_set = TRUE;
+ }
+ }
+
+ if (!values_set)
+ {
+ /* Set the remaining properties to their default values
+ * even if broken out of the above loop. */
+ while (name)
+ {
+ GValue value = { 0, };
+ gchar *error = NULL;
+ nbtk_stylable_get_default_value (stylable, name, &value);
+ G_VALUE_LCOPY (&value, va_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+ g_value_unset (&value);
+ break;
+ }
+ g_value_unset (&value);
+ name = va_arg (va_args, gchar*);
+ }
+ }
+}
+
+/**
+ * nbtk_style_get:
+ * @style: a #NbtkStyle
+ * @stylable: a #NbtkStylable
+ * @first_property_name: name of the first property to get
+ * @Varargs: return location for the first property, followed optionally
+ * by more name/return location pairs, followed by %NULL
+ *
+ * Gets the style properties for @stylable from @style.
+ *
+ * In general, a copy is made of the property contents and the caller
+ * is responsible for freeing the memory in the appropriate manner for
+ * the property type.
+ */
+void
+nbtk_style_get (NbtkStyle *style,
+ NbtkStylable *stylable,
+ const gchar *first_property_name,
+ ...)
+{
+ va_list va_args;
+
+ g_return_if_fail (NBTK_IS_STYLE (style));
+ g_return_if_fail (first_property_name != NULL);
+
+ va_start (va_args, first_property_name);
+ nbtk_style_get_valist (style, stylable, first_property_name, va_args);
+ va_end (va_args);
+}
diff --git a/src/nbtk/nbtk-style.h b/src/nbtk/nbtk-style.h
new file mode 100644
index 0000000..35efc12
--- /dev/null
+++ b/src/nbtk/nbtk-style.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_STYLE_H__
+#define __NBTK_STYLE_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_STYLE (nbtk_style_get_type ())
+#define NBTK_STYLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_STYLE, NbtkStyle))
+#define NBTK_IS_STYLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_STYLE))
+#define NBTK_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_STYLE, NbtkStyleClass))
+#define NBTK_IS_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_STYLE))
+#define NBTK_STYLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_STYLE, NbtkStyleClass))
+
+typedef struct _NbtkStyle NbtkStyle;
+typedef struct _NbtkStylePrivate NbtkStylePrivate;
+typedef struct _NbtkStyleClass NbtkStyleClass;
+
+/* forward declaration */
+typedef struct _NbtkStylable NbtkStylable; /* dummy typedef */
+typedef struct _NbtkStylableIface NbtkStylableIface;
+
+typedef enum { /*< prefix=NBTK_STYLE_ERROR >*/
+ NBTK_STYLE_ERROR_INVALID_FILE
+} NbtkStyleError;
+
+/**
+ * NbtkStyle:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+struct _NbtkStyle
+{
+ /*< private >*/
+ GObject parent_instance;
+
+ NbtkStylePrivate *priv;
+};
+
+struct _NbtkStyleClass
+{
+ GObjectClass parent_class;
+
+ void (* changed) (NbtkStyle *style);
+};
+
+GType nbtk_style_get_type (void) G_GNUC_CONST;
+
+NbtkStyle * nbtk_style_get_default (void);
+NbtkStyle * nbtk_style_new (void);
+
+gboolean nbtk_style_load_from_file (NbtkStyle *style,
+ const gchar *filename,
+ GError **error);
+void nbtk_style_get_property (NbtkStyle *style,
+ NbtkStylable *stylable,
+ GParamSpec *pspec,
+ GValue *value);
+void nbtk_style_get (NbtkStyle *style,
+ NbtkStylable *stylable,
+ const gchar *first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+void nbtk_style_get_valist (NbtkStyle *style,
+ NbtkStylable *stylable,
+ const gchar *first_property_name,
+ va_list va_args);
+
+G_END_DECLS
+
+#endif /* __NBTK_STYLE_H__ */
diff --git a/src/nbtk/nbtk-subtexture.c b/src/nbtk/nbtk-subtexture.c
new file mode 100644
index 0000000..4841dc8
--- /dev/null
+++ b/src/nbtk/nbtk-subtexture.c
@@ -0,0 +1,575 @@
+/*
+ * nbtk-subtexture.h: Class to wrap a texture and "subframe" it.
+ * based on
+ * nbtk-texture-frame.c: Expandible texture actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl/cogl.h>
+#include <clutter/clutter.h>
+
+#include "nbtk-subtexture.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_PARENT_TEXTURE,
+
+ PROP_TOP,
+ PROP_LEFT,
+ PROP_WIDTH,
+ PROP_HEIGHT
+};
+
+G_DEFINE_TYPE (NbtkSubtexture, nbtk_subtexture, CLUTTER_TYPE_ACTOR);
+
+#define NBTK_SUBTEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_SUBTEXTURE, NbtkSubtexturePrivate))
+
+struct _NbtkSubtexturePrivate
+{
+ ClutterTexture *parent_texture;
+
+ int left;
+ int top;
+ int width;
+ int height;
+
+ CoglHandle material;
+};
+
+static void
+nbtk_subtexture_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+ if (G_UNLIKELY (priv->parent_texture == NULL))
+ {
+ if (min_width_p)
+ *min_width_p = 0;
+
+ if (natural_width_p)
+ *natural_width_p = 0;
+ }
+ else
+ {
+ if (min_width_p)
+ *min_width_p = priv->width;
+ if (natural_width_p)
+ *natural_width_p = priv->width;
+ }
+}
+
+static void
+nbtk_subtexture_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+ if (G_UNLIKELY (priv->parent_texture == NULL))
+ {
+ if (min_height_p)
+ *min_height_p = 0;
+
+ if (natural_height_p)
+ *natural_height_p = 0;
+ }
+ else
+ {
+ if (min_height_p)
+ *min_height_p = priv->height;
+ if (natural_height_p)
+ *natural_height_p = priv->height;
+ }
+}
+
+static void
+nbtk_subtexture_realize (ClutterActor *self)
+{
+ NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+ if (priv->material != COGL_INVALID_HANDLE)
+ return;
+
+ priv->material = cogl_material_new ();
+
+ CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
+}
+
+static void
+nbtk_subtexture_unrealize (ClutterActor *self)
+{
+ NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+ if (priv->material == COGL_INVALID_HANDLE)
+ return;
+
+ cogl_material_unref (priv->material);
+ priv->material = COGL_INVALID_HANDLE;
+
+ CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
+}
+
+static void
+nbtk_subtexture_paint (ClutterActor *self)
+{
+ NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+ CoglHandle cogl_texture = COGL_INVALID_HANDLE;
+ ClutterActorBox box = { 0, 0, 0, 0 };
+ gfloat tx1, ty1, tx2, ty2, tex_width, tex_height, width, height;
+ guint8 opacity;
+
+ /* no need to paint stuff if we don't have a texture */
+ if (G_UNLIKELY (priv->parent_texture == NULL))
+ return;
+
+ /* parent texture may have been hidden, so need to make sure it gets
+ * realized
+ */
+ if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
+ clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
+
+ cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
+ if (cogl_texture == COGL_INVALID_HANDLE)
+ return;
+
+ tex_width = cogl_texture_get_width (cogl_texture);
+ tex_height = cogl_texture_get_height (cogl_texture);
+
+ clutter_actor_get_allocation_box (self, &box);
+
+ width = box.x2 - box.x1;
+ height = box.y2 - box.y1;
+
+ tx1 = 1.0 * priv->left / tex_width;
+ ty1 = 1.0 * priv->top / tex_height;
+
+ tx2 = 1.0 * (priv->left + priv->width) / tex_width;
+ ty2 = 1.0 * (priv->top + priv->height) / tex_height;
+
+
+ opacity = clutter_actor_get_paint_opacity (self);
+
+ g_assert (priv->material != COGL_INVALID_HANDLE);
+
+ /* set the source material using the parent texture's COGL handle */
+ cogl_material_set_color4ub (priv->material, 255, 255, 255, opacity);
+ cogl_material_set_layer (priv->material, 0, cogl_texture);
+ cogl_set_source (priv->material);
+
+ cogl_rectangle_with_texture_coords (0,0, (float)width, (float)height,
+ tx1, ty1, tx2, ty2);
+}
+
+static inline void
+nbtk_subtexture_set_frame_internal (NbtkSubtexture *frame,
+ int left,
+ int top,
+ int width,
+ int height)
+{
+ NbtkSubtexturePrivate *priv = frame->priv;
+ GObject *gobject = G_OBJECT (frame);
+ gboolean changed = FALSE;
+
+ g_object_freeze_notify (gobject);
+
+ if (priv->top != top)
+ {
+ priv->top = top;
+ g_object_notify (gobject, "top");
+ changed = TRUE;
+ }
+
+ if (priv->left != left)
+ {
+ priv->left = left;
+ g_object_notify (gobject, "left");
+ changed = TRUE;
+ }
+
+ if (priv->width != width)
+ {
+ priv->width = width;
+ g_object_notify (gobject, "width");
+ changed = TRUE;
+ }
+
+ if (priv->height != height)
+ {
+ priv->height = height;
+ g_object_notify (gobject, "height");
+ changed = TRUE;
+ }
+
+ if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
+
+ g_object_thaw_notify (gobject);
+}
+
+static void
+nbtk_subtexture_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkSubtexture *frame = NBTK_SUBTEXTURE (gobject);
+ NbtkSubtexturePrivate *priv = frame->priv;
+
+ switch (prop_id)
+ {
+ case PROP_PARENT_TEXTURE:
+ nbtk_subtexture_set_parent_texture (frame,
+ g_value_get_object (value));
+ break;
+
+ case PROP_TOP:
+ nbtk_subtexture_set_frame_internal (frame,
+ priv->left,
+ g_value_get_int (value),
+ priv->width,
+ priv->height);
+ break;
+
+ case PROP_LEFT:
+ nbtk_subtexture_set_frame_internal (frame,
+ g_value_get_int (value),
+ priv->top,
+ priv->width,
+ priv->height);
+ break;
+
+ case PROP_WIDTH:
+ nbtk_subtexture_set_frame_internal (frame,
+ priv->left,
+ priv->top,
+ g_value_get_int (value),
+ priv->height);
+ break;
+
+ case PROP_HEIGHT:
+ nbtk_subtexture_set_frame_internal (frame,
+ priv->left,
+ priv->top,
+ priv->width,
+ g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_subtexture_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_PARENT_TEXTURE:
+ g_value_set_object (value, priv->parent_texture);
+ break;
+
+ case PROP_LEFT:
+ g_value_set_int (value, priv->left);
+ break;
+
+ case PROP_TOP:
+ g_value_set_int (value, priv->top);
+ break;
+
+ case PROP_WIDTH:
+ g_value_set_int (value, priv->width);
+ break;
+
+ case PROP_HEIGHT:
+ g_value_set_int (value, priv->height);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_subtexture_dispose (GObject *gobject)
+{
+ NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (gobject)->priv;
+
+ if (priv->parent_texture)
+ {
+ g_object_unref (priv->parent_texture);
+ priv->parent_texture = NULL;
+ }
+
+ if (priv->material)
+ {
+ cogl_material_unref (priv->material);
+ priv->material = COGL_INVALID_HANDLE;
+ }
+
+ G_OBJECT_CLASS (nbtk_subtexture_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_subtexture_class_init (NbtkSubtextureClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (gobject_class, sizeof (NbtkSubtexturePrivate));
+
+ actor_class->get_preferred_width =
+ nbtk_subtexture_get_preferred_width;
+ actor_class->get_preferred_height =
+ nbtk_subtexture_get_preferred_height;
+ actor_class->realize = nbtk_subtexture_realize;
+ actor_class->unrealize = nbtk_subtexture_unrealize;
+ actor_class->paint = nbtk_subtexture_paint;
+
+ gobject_class->set_property = nbtk_subtexture_set_property;
+ gobject_class->get_property = nbtk_subtexture_get_property;
+ gobject_class->dispose = nbtk_subtexture_dispose;
+
+ pspec = g_param_spec_object ("parent-texture",
+ "Parent Texture",
+ "The parent ClutterTexture",
+ CLUTTER_TYPE_TEXTURE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT);
+ g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
+
+ pspec = g_param_spec_int ("left",
+ "Left",
+ "Left offset",
+ 0, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
+
+ pspec = g_param_spec_int ("top",
+ "Top",
+ "Top offset",
+ 0, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_TOP, pspec);
+
+ pspec = g_param_spec_int ("width",
+ "Width",
+ "Width",
+ 0, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
+
+ pspec = g_param_spec_int ("height",
+ "Height",
+ "Height",
+ 0, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_HEIGHT, pspec);
+}
+
+static void
+nbtk_subtexture_init (NbtkSubtexture *self)
+{
+ NbtkSubtexturePrivate *priv;
+
+ self->priv = priv = NBTK_SUBTEXTURE_GET_PRIVATE (self);
+
+ priv->material = COGL_INVALID_HANDLE;
+}
+
+/**
+ * nbtk_subtexture_new:
+ * @texture: a #ClutterTexture or %NULL
+ * @left: left
+ * @top: top
+ * @width: width
+ * @height: height
+ *
+ * A #NbtkSubtexture is a specialized texture that efficiently clones
+ * an area of the given @texture while keeping preserving portions of the
+ * same texture.
+ *
+ * A #NbtkSubtexture can be used to make a rectangular texture fit a
+ * given size without stretching its borders.
+ *
+ * Return value: the newly created #NbtkSubtexture
+ */
+ClutterActor*
+nbtk_subtexture_new (ClutterTexture *texture,
+ gint left,
+ gint top,
+ gint width,
+ gint height)
+{
+ g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
+
+ return g_object_new (NBTK_TYPE_SUBTEXTURE,
+ "parent-texture", texture,
+ "top", top,
+ "left", left,
+ "width", width,
+ "height", height,
+ NULL);
+}
+
+/**
+ * nbtk_subtexture_get_parent_texture:
+ * @frame: A #NbtkSubtexture
+ *
+ * Return the texture used by the #NbtkSubtexture
+ *
+ * Returns: a #ClutterTexture owned by the #NbtkSubtexture
+ */
+ClutterTexture *
+nbtk_subtexture_get_parent_texture (NbtkSubtexture *frame)
+{
+ g_return_val_if_fail (NBTK_IS_SUBTEXTURE (frame), NULL);
+
+ return frame->priv->parent_texture;
+}
+
+/**
+ * nbtk_subtexture_set_parent_texture:
+ * @frame: A #NbtkSubtexture
+ * @texture: A #ClutterTexture
+ *
+ * Set the #ClutterTexture used by this #NbtkSubtexture
+ *
+ */
+void
+nbtk_subtexture_set_parent_texture (NbtkSubtexture *frame,
+ ClutterTexture *texture)
+{
+ NbtkSubtexturePrivate *priv;
+ gboolean was_visible;
+
+ g_return_if_fail (NBTK_IS_SUBTEXTURE (frame));
+ g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
+
+ priv = frame->priv;
+
+ was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
+
+ if (priv->parent_texture == texture)
+ return;
+
+ if (priv->parent_texture)
+ {
+ g_object_unref (priv->parent_texture);
+ priv->parent_texture = NULL;
+
+ if (was_visible)
+ clutter_actor_hide (CLUTTER_ACTOR (frame));
+ }
+
+ if (texture)
+ {
+ priv->parent_texture = g_object_ref (texture);
+
+ if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
+ clutter_actor_show (CLUTTER_ACTOR (frame));
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
+
+ g_object_notify (G_OBJECT (frame), "parent-texture");
+}
+
+/**
+ * nbtk_subtexture_set_frame:
+ * @frame: A #NbtkSubtexture
+ * @left: left
+ * @top: top
+ * @width: width
+ * @height: height
+ *
+ * Set the frame of the subtexture
+ *
+ */
+void
+nbtk_subtexture_set_frame (NbtkSubtexture *frame,
+ gint left,
+ gint top,
+ gint width,
+ gint height)
+{
+ g_return_if_fail (NBTK_IS_SUBTEXTURE (frame));
+
+ nbtk_subtexture_set_frame_internal (frame, left, top, width, height);
+}
+
+/**
+ * nbtk_subtexture_get_frame:
+ * @frame: A #NbtkSubtexture
+ * @left: left
+ * @top: top
+ * @width: width
+ * @height: height
+ *
+ * Retrieve the current frame.
+ *
+ */
+void
+nbtk_subtexture_get_frame (NbtkSubtexture *frame,
+ gint *left,
+ gint *top,
+ gint *width,
+ gint *height)
+{
+ NbtkSubtexturePrivate *priv;
+
+ g_return_if_fail (NBTK_IS_SUBTEXTURE (frame));
+
+ priv = frame->priv;
+
+ if (top)
+ *top = priv->top;
+
+ if (left)
+ *left = priv->left;
+
+ if (width)
+ *width = priv->width;
+
+ if (height)
+ *height = priv->height;
+}
diff --git a/src/nbtk/nbtk-subtexture.h b/src/nbtk/nbtk-subtexture.h
new file mode 100644
index 0000000..33a4b72
--- /dev/null
+++ b/src/nbtk/nbtk-subtexture.h
@@ -0,0 +1,92 @@
+/*
+ * nbtk-subtexture.h: Class to wrap a texture and "subframe" it.
+ *
+ * Based on
+ * nbtk-texture-frame.h: Expandible texture actor
+ *
+ * Copyright 2007, 2008 OpenedHand Ltd
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __NBTK_SUBTEXTURE_H__
+#define __NBTK_SUBTEXTURE_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_SUBTEXTURE (nbtk_subtexture_get_type ())
+#define NBTK_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_SUBTEXTURE, NbtkSubtexture))
+#define NBTK_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_SUBTEXTURE, NbtkSubtextureClass))
+#define NBTK_IS_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_SUBTEXTURE))
+#define NBTK_IS_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_SUBTEXTURE))
+#define NBTK_SUBTEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_SUBTEXTURE, NbtkSubtextureClass))
+
+typedef struct _NbtkSubtexture NbtkSubtexture;
+typedef struct _NbtkSubtexturePrivate NbtkSubtexturePrivate;
+typedef struct _NbtkSubtextureClass NbtkSubtextureClass;
+
+/**
+ * NbtkSubtexture:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _NbtkSubtexture
+{
+ /*< private >*/
+ ClutterActor parent_instance;
+
+ NbtkSubtexturePrivate *priv;
+};
+
+struct _NbtkSubtextureClass
+{
+ ClutterActorClass parent_class;
+
+ /* padding for future expansion */
+ void (*_nbtk_box_1) (void);
+ void (*_nbtk_box_2) (void);
+ void (*_nbtk_box_3) (void);
+ void (*_nbtk_box_4) (void);
+};
+
+GType nbtk_subtexture_get_type (void) G_GNUC_CONST;
+ClutterActor * nbtk_subtexture_new (ClutterTexture *texture,
+ gint top,
+ gint left,
+ gint width,
+ gint height);
+void nbtk_subtexture_set_parent_texture (NbtkSubtexture *frame,
+ ClutterTexture *texture);
+ClutterTexture *nbtk_subtexture_get_parent_texture (NbtkSubtexture *frame);
+void nbtk_subtexture_set_frame (NbtkSubtexture *frame,
+ gint top,
+ gint left,
+ gint width,
+ gint height);
+void nbtk_subtexture_get_frame (NbtkSubtexture *frame,
+ gint *top,
+ gint *left,
+ gint *width,
+ gint *height);
+
+G_END_DECLS
+
+#endif /* __NBTK_SUBTEXTURE_H__ */
diff --git a/src/nbtk/nbtk-texture-cache.c b/src/nbtk/nbtk-texture-cache.c
new file mode 100644
index 0000000..10499bd
--- /dev/null
+++ b/src/nbtk/nbtk-texture-cache.c
@@ -0,0 +1,456 @@
+/*
+ * nbtk-widget.h: Base class for Nbtk actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/**
+ * SECTION:nbtk-texture-cache
+ * @short_description: A per-process store to cache textures
+ *
+ * #NbtkTextureCache allows an application to re-use an previously loaded
+ * textures.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <string.h>
+
+#include "nbtk-texture-cache.h"
+#include "nbtk-marshal.h"
+#include "nbtk-private.h"
+#include "nbtk-subtexture.h"
+G_DEFINE_TYPE (NbtkTextureCache, nbtk_texture_cache, G_TYPE_OBJECT)
+
+#define TEXTURE_CACHE_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCachePrivate))
+
+typedef struct _NbtkTextureCachePrivate NbtkTextureCachePrivate;
+
+struct _NbtkTextureCachePrivate
+{
+ GHashTable *cache;
+};
+
+typedef struct FinalizedClosure
+{
+ gchar *path;
+ NbtkTextureCache *cache;
+} FinalizedClosure;
+
+enum
+{
+ PROP_0,
+};
+
+static NbtkTextureCache* __cache_singleton = NULL;
+
+/*
+ * Convention: posX with a value of -1 indicates whole texture
+ */
+typedef struct NbtkTextureCacheItem {
+ char filename[256];
+ int width, height;
+ int posX, posY;
+ ClutterActor *ptr;
+} NbtkTextureCacheItem;
+
+static NbtkTextureCacheItem *
+nbtk_texture_cache_item_new (void)
+{
+ return g_slice_new0 (NbtkTextureCacheItem);
+}
+
+static void
+nbtk_texture_cache_item_free (NbtkTextureCacheItem *item)
+{
+ g_slice_free (NbtkTextureCacheItem, item);
+}
+
+static void
+nbtk_texture_cache_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_texture_cache_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_texture_cache_dispose (GObject *object)
+{
+ if (G_OBJECT_CLASS (nbtk_texture_cache_parent_class)->dispose)
+ G_OBJECT_CLASS (nbtk_texture_cache_parent_class)->dispose (object);
+}
+
+static void
+nbtk_texture_cache_finalize (GObject *object)
+{
+ NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object);
+
+ if (priv->cache)
+ {
+ g_hash_table_unref (priv->cache);
+ priv->cache = NULL;
+ }
+
+ G_OBJECT_CLASS (nbtk_texture_cache_parent_class)->finalize (object);
+}
+
+static void
+nbtk_texture_cache_class_init (NbtkTextureCacheClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NbtkTextureCachePrivate));
+
+ object_class->get_property = nbtk_texture_cache_get_property;
+ object_class->set_property = nbtk_texture_cache_set_property;
+ object_class->dispose = nbtk_texture_cache_dispose;
+ object_class->finalize = nbtk_texture_cache_finalize;
+
+}
+
+static void
+nbtk_texture_cache_init (NbtkTextureCache *self)
+{
+ NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+
+ priv->cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+}
+
+/**
+ * nbtk_texture_cache_get_default:
+ *
+ * Returns the default texture cache. This is owned by Nbtk and should not be
+ * unreferenced or freed.
+ *
+ * Returns: a NbtkTextureCache
+ */
+NbtkTextureCache*
+nbtk_texture_cache_get_default (void)
+{
+ if (G_UNLIKELY (__cache_singleton == NULL))
+ __cache_singleton = g_object_new (NBTK_TYPE_TEXTURE_CACHE, NULL);
+
+ return __cache_singleton;
+}
+
+#if 0
+static void
+on_texure_finalized (gpointer data,
+ GObject *where_the_object_was)
+{
+ FinalizedClosure *closure = (FinalizedClosure *)data;
+ NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache);
+
+ g_hash_table_remove (priv->cache, closure->path);
+
+ g_free(closure->path);
+ g_free(closure);
+}
+#endif
+
+/**
+ * nbtk_texture_cache_get_size:
+ * @self: A #NbtkTextureCache
+ *
+ * Returns the number of items in the texture cache
+ *
+ * Returns: the current size of the cache
+ */
+gint
+nbtk_texture_cache_get_size (NbtkTextureCache *self)
+{
+ NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+
+ return g_hash_table_size (priv->cache);
+}
+
+static void
+add_texture_to_cache (NbtkTextureCache *self,
+ const gchar *path,
+ NbtkTextureCacheItem *item)
+{
+ /* FinalizedClosure *closure; */
+ NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+
+ g_hash_table_insert (priv->cache, g_strdup (path), item);
+
+#if 0
+ /* Make sure we can remove from hash */
+ closure = g_new0 (FinalizedClosure, 1);
+ closure->path = g_strdup (path);
+ closure->cache = self;
+
+ g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure);
+#endif
+}
+
+/* NOTE: you should unref the returned texture when not needed */
+
+/**
+ * nbtk_texture_cache_get_texture:
+ * @self: A #NbtkTextureCache
+ * @path: A path to a image file
+ * @want_clone: ignored
+ *
+ * Create a new ClutterTexture with the specified image. Adds the image to the
+ * cache if the image had not been previously loaded. Subsequent calls with
+ * the same image path will return a new ClutterTexture with the previously
+ * loaded image.
+ *
+ * Returns: a newly created ClutterTexture
+ */
+ClutterTexture*
+nbtk_texture_cache_get_texture (NbtkTextureCache *self,
+ const gchar *path,
+ gboolean want_clone)
+{
+ ClutterActor *texture;
+ CoglHandle *handle;
+ NbtkTextureCachePrivate *priv;
+ NbtkTextureCacheItem *item;
+
+ g_return_val_if_fail (NBTK_IS_TEXTURE_CACHE (self), NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+
+ priv = TEXTURE_CACHE_PRIVATE (self);
+
+ if (want_clone)
+ g_warning ("The want_clone parameter of %s is now ignored. This function "
+ "always returns a new ClutterTexture", __FUNCTION__);
+
+ item = g_hash_table_lookup (priv->cache, path);
+
+ if (item && item->posX != -1)
+ {
+ GError *err = NULL;
+ /*
+ * We have a cache hit, but it's for a partial texture. The only
+ * sane option is to read it from disk and just don't cache it
+ * at all.
+ */
+ return CLUTTER_TEXTURE(clutter_texture_new_from_file(path, &err));
+ }
+ if (!item)
+ {
+ GError *err = NULL;
+
+ item = nbtk_texture_cache_item_new ();
+ item->posX = -1;
+ item->posY = -1;
+ item->ptr = clutter_texture_new_from_file (path, &err);
+ clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
+ &item->width, &item->height);
+
+ if (!item->ptr)
+ {
+ if (err)
+ {
+ g_warning ("Error loading image: %s", err->message);
+ g_error_free (err);
+ }
+
+ return NULL;
+ }
+
+ add_texture_to_cache (self, path, item);
+ }
+
+ texture = clutter_texture_new ();
+ handle = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (item->ptr));
+ clutter_texture_set_cogl_texture ((ClutterTexture*) texture, handle);
+
+ return (ClutterTexture*) texture;
+}
+
+
+/**
+ * nbtk_texture_cache_get_actor:
+ * @self: A #NbtkTextureCache
+ * @path: A path to a image file
+ *
+ * Create a new ClutterSubTexture with the specified image. Adds the image to the
+ * cache if the image had not been previously loaded. Subsequent calls with
+ * the same image path will return a new ClutterTexture with the previously
+ * loaded image.
+ *
+ * Use this function if all you need is an actor for drawing.
+ *
+ * Returns: a newly created ClutterTexture
+ */
+ClutterActor*
+nbtk_texture_cache_get_actor (NbtkTextureCache *self,
+ const gchar *path)
+{
+ NbtkTextureCachePrivate *priv;
+ NbtkTextureCacheItem *item;
+ GError *err = NULL;
+
+ g_return_val_if_fail (NBTK_IS_TEXTURE_CACHE (self), NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ priv = TEXTURE_CACHE_PRIVATE (self);
+
+
+ item = g_hash_table_lookup (priv->cache, path);
+
+ if (item)
+ {
+ int posX = item->posX;
+ int posY = item->posY;
+ if (posX == -1)
+ posX = 0;
+ if (posY == -1)
+ posY = 0;
+ return nbtk_subtexture_new (CLUTTER_TEXTURE (item->ptr), posX, posY,
+ item->width, item->height);
+ }
+
+ item = nbtk_texture_cache_item_new ();
+ item->posX = -1;
+ item->posY = -1;
+ item->ptr = clutter_texture_new_from_file (path, &err);
+ clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
+ &item->width, &item->height);
+
+ if (!item->ptr)
+ {
+ if (err)
+ {
+ g_warning ("Error loading image: %s", err->message);
+ g_error_free (err);
+ }
+
+ return NULL;
+ }
+
+ add_texture_to_cache (self, path, item);
+
+ return nbtk_subtexture_new (CLUTTER_TEXTURE (item->ptr), 0, 0, item->width,
+ item->height);
+}
+
+void
+nbtk_texture_cache_load_cache (NbtkTextureCache *self,
+ const gchar *filename)
+{
+ FILE *file;
+ NbtkTextureCacheItem *element, head;
+ int ret;
+ ClutterActor *actor;
+ GError *error = NULL;
+ NbtkTextureCachePrivate *priv;
+
+ g_return_if_fail (NBTK_IS_TEXTURE_CACHE (self));
+ g_return_if_fail (filename != NULL);
+
+ priv = TEXTURE_CACHE_PRIVATE (self);
+
+ file = fopen(filename, "rm");
+ if (!file)
+ return;
+
+ ret = fread (&head, sizeof(NbtkTextureCacheItem), 1, file);
+ if (ret < 0)
+ {
+ fclose (file);
+ return;
+ }
+
+ /* check if we already if this texture in the cache */
+ if (g_hash_table_lookup (priv->cache, head.filename))
+ {
+ /* skip it, we're done */
+ fclose (file);
+ return;
+ }
+
+ actor = clutter_texture_new_from_file (head.filename, &error);
+
+ if (error)
+ {
+ g_critical (G_STRLOC ": Error opening cache image file: %s",
+ error->message);
+ g_clear_error (&error);
+ fclose (file);
+ return;
+ }
+
+ element = nbtk_texture_cache_item_new ();
+ element->posX = -1;
+ element->posY = -1;
+ element->ptr = actor;
+ strncpy (element->filename, head.filename, 256);
+ clutter_texture_get_base_size (CLUTTER_TEXTURE (element->ptr),
+ &element->width, &element->height);
+ g_hash_table_insert (priv->cache, element->filename, element);
+
+ while (!feof (file))
+ {
+ element = nbtk_texture_cache_item_new ();
+ ret = fread (element, sizeof (NbtkTextureCacheItem), 1, file);
+ if (ret < 1)
+ {
+ /* end of file */
+ nbtk_texture_cache_item_free (element);
+ break;
+ }
+
+ element->ptr = actor;
+
+ if (g_hash_table_lookup (priv->cache, element->filename))
+ {
+ /* file is already in the cache.... */
+ nbtk_texture_cache_item_free (element);
+ } else {
+ g_hash_table_insert (priv->cache, element->filename, element);
+ }
+ }
+}
diff --git a/src/nbtk/nbtk-texture-cache.h b/src/nbtk/nbtk-texture-cache.h
new file mode 100644
index 0000000..1b2e17d
--- /dev/null
+++ b/src/nbtk/nbtk-texture-cache.h
@@ -0,0 +1,94 @@
+/*
+ * nbtk-texture-cache.h: Cached textures object
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef _NBTK_TEXTURE_CACHE
+#define _NBTK_TEXTURE_CACHE
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_TEXTURE_CACHE nbtk_texture_cache_get_type()
+
+#define NBTK_TEXTURE_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCache))
+
+#define NBTK_TEXTURE_CACHE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCacheClass))
+
+#define NBTK_IS_TEXTURE_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ NBTK_TYPE_TEXTURE_CACHE))
+
+#define NBTK_IS_TEXTURE_CACHE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ NBTK_TYPE_TEXTURE_CACHE))
+
+#define NBTK_TEXTURE_CACHE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCacheClass))
+
+/**
+ * NbtkTextureCache:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+typedef struct {
+ /*< private >*/
+ GObject parent;
+} NbtkTextureCache;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ void (* loaded) (NbtkTextureCache *self,
+ const gchar *path,
+ ClutterTexture *texture);
+
+ void (* error_loading) (NbtkTextureCache *self,
+ GError *error);
+} NbtkTextureCacheClass;
+
+GType nbtk_texture_cache_get_type (void);
+
+NbtkTextureCache* nbtk_texture_cache_get_default (void);
+ClutterTexture* nbtk_texture_cache_get_texture (NbtkTextureCache *self,
+ const gchar *path,
+ gboolean want_clone);
+ClutterActor* nbtk_texture_cache_get_actor (NbtkTextureCache *self,
+ const gchar *path);
+
+gint nbtk_texture_cache_get_size (NbtkTextureCache *self);
+
+void nbtk_texture_cache_load_cache(NbtkTextureCache *self, const char *filename);
+
+G_END_DECLS
+
+#endif /* _NBTK_TEXTURE_CACHE */
diff --git a/src/nbtk/nbtk-texture-frame.c b/src/nbtk/nbtk-texture-frame.c
new file mode 100644
index 0000000..6734cba
--- /dev/null
+++ b/src/nbtk/nbtk-texture-frame.c
@@ -0,0 +1,620 @@
+/*
+ * nbtk-texture-frame.h: Expandible texture actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/**
+ * SECTION:nbtk-texture-frame
+ * @short_description: Stretch a texture to fit the entire allocation
+ *
+ * #NbtkTextureFrame
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl/cogl.h>
+
+#include "nbtk-texture-frame.h"
+#include "nbtk-private.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_PARENT_TEXTURE,
+
+ PROP_TOP,
+ PROP_RIGHT,
+ PROP_BOTTOM,
+ PROP_LEFT
+};
+
+G_DEFINE_TYPE (NbtkTextureFrame, nbtk_texture_frame, CLUTTER_TYPE_ACTOR);
+
+#define NBTK_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFramePrivate))
+
+struct _NbtkTextureFramePrivate
+{
+ ClutterTexture *parent_texture;
+
+ gfloat top;
+ gfloat right;
+ gfloat bottom;
+ gfloat left;
+};
+
+static void
+nbtk_texture_frame_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (self)->priv;
+
+ if (G_UNLIKELY (priv->parent_texture == NULL))
+ {
+ if (min_width_p)
+ *min_width_p = 0;
+
+ if (natural_width_p)
+ *natural_width_p = 0;
+ }
+ else
+ {
+ ClutterActorClass *klass;
+
+ /* by directly querying the parent texture's class implementation
+ * we are going around any override mechanism the parent texture
+ * might have in place, and we ask directly for the original
+ * preferred width
+ */
+ klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
+ klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
+ for_height,
+ min_width_p,
+ natural_width_p);
+ }
+}
+
+static void
+nbtk_texture_frame_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (self)->priv;
+
+ if (G_UNLIKELY (priv->parent_texture == NULL))
+ {
+ if (min_height_p)
+ *min_height_p = 0;
+
+ if (natural_height_p)
+ *natural_height_p = 0;
+ }
+ else
+ {
+ ClutterActorClass *klass;
+
+ /* by directly querying the parent texture's class implementation
+ * we are going around any override mechanism the parent texture
+ * might have in place, and we ask directly for the original
+ * preferred height
+ */
+ klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
+ klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
+ for_width,
+ min_height_p,
+ natural_height_p);
+ }
+}
+
+static void
+nbtk_texture_frame_paint (ClutterActor *self)
+{
+ NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (self)->priv;
+ CoglHandle cogl_texture = COGL_INVALID_HANDLE;
+ CoglHandle cogl_material = COGL_INVALID_HANDLE;
+ ClutterActorBox box = { 0, };
+ gfloat width, height;
+ gfloat tex_width, tex_height;
+ gfloat ex, ey;
+ gfloat tx1, ty1, tx2, ty2;
+ guint8 opacity;
+
+ /* no need to paint stuff if we don't have a texture */
+ if (G_UNLIKELY (priv->parent_texture == NULL))
+ return;
+
+ /* parent texture may have been hidden, so need to make sure it gets
+ * realized
+ */
+ if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
+ clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
+
+ cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
+ if (cogl_texture == COGL_INVALID_HANDLE)
+ return;
+ cogl_material = clutter_texture_get_cogl_material (priv->parent_texture);
+ if (cogl_material == COGL_INVALID_HANDLE)
+ return;
+
+ tex_width = cogl_texture_get_width (cogl_texture);
+ tex_height = cogl_texture_get_height (cogl_texture);
+
+ clutter_actor_get_allocation_box (self, &box);
+ width = box.x2 - box.x1;
+ height = box.y2 - box.y1;
+
+ tx1 = priv->left / tex_width;
+ tx2 = (tex_width - priv->right) / tex_width;
+ ty1 = priv->top / tex_height;
+ ty2 = (tex_height - priv->bottom) / tex_height;
+
+ ex = width - priv->right;
+ if (ex < 0)
+ ex = priv->right; /* FIXME ? */
+
+ ey = height - priv->bottom;
+ if (ey < 0)
+ ey = priv->bottom; /* FIXME ? */
+
+ opacity = clutter_actor_get_paint_opacity (self);
+
+ /* Paint using the parent texture's material. It should already have
+ the cogl texture set as the first layer */
+ /* NB: for correct blending we need set a preumultiplied color here: */
+ cogl_material_set_color4ub (cogl_material,
+ opacity, opacity, opacity, opacity);
+ cogl_set_source (cogl_material);
+
+ {
+ GLfloat rectangles[] =
+ {
+ /* top left corner */
+ 0, 0, priv->left, priv->top,
+ 0.0, 0.0,
+ tx1, ty1,
+
+ /* top middle */
+ priv->left, 0, ex, priv->top,
+ tx1, 0.0,
+ tx2, ty1,
+
+ /* top right */
+ ex, 0, width, priv->top,
+ tx2, 0.0,
+ 1.0, ty1,
+
+ /* mid left */
+ 0, priv->top, priv->left, ey,
+ 0.0, ty1,
+ tx1, ty2,
+
+ /* center */
+ priv->left, priv->top, ex, ey,
+ tx1, ty1,
+ tx2, ty2,
+
+ /* mid right */
+ ex, priv->top, width, ey,
+ tx2, ty1,
+ 1.0, ty2,
+
+ /* bottom left */
+ 0, ey, priv->left, height,
+ 0.0, ty2,
+ tx1, 1.0,
+
+ /* bottom center */
+ priv->left, ey, ex, height,
+ tx1, ty2,
+ tx2, 1.0,
+
+ /* bottom right */
+ ex, ey, width, height,
+ tx2, ty2,
+ 1.0, 1.0
+ };
+
+ cogl_rectangles_with_texture_coords (rectangles, 9);
+ }
+}
+
+static inline void
+nbtk_texture_frame_set_frame_internal (NbtkTextureFrame *frame,
+ gfloat top,
+ gfloat right,
+ gfloat bottom,
+ gfloat left)
+{
+ NbtkTextureFramePrivate *priv = frame->priv;
+ GObject *gobject = G_OBJECT (frame);
+ gboolean changed = FALSE;
+
+ g_object_freeze_notify (gobject);
+
+ if (priv->top != top)
+ {
+ priv->top = top;
+ g_object_notify (gobject, "top");
+ changed = TRUE;
+ }
+
+ if (priv->right != right)
+ {
+ priv->right = right;
+ g_object_notify (gobject, "right");
+ changed = TRUE;
+ }
+
+ if (priv->bottom != bottom)
+ {
+ priv->bottom = bottom;
+ g_object_notify (gobject, "bottom");
+ changed = TRUE;
+ }
+
+ if (priv->left != left)
+ {
+ priv->left = left;
+ g_object_notify (gobject, "left");
+ changed = TRUE;
+ }
+
+ if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
+
+ g_object_thaw_notify (gobject);
+}
+
+static void
+nbtk_texture_frame_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkTextureFrame *frame = NBTK_TEXTURE_FRAME (gobject);
+ NbtkTextureFramePrivate *priv = frame->priv;
+
+ switch (prop_id)
+ {
+ case PROP_PARENT_TEXTURE:
+ nbtk_texture_frame_set_parent_texture (frame,
+ g_value_get_object (value));
+ break;
+
+ case PROP_TOP:
+ nbtk_texture_frame_set_frame_internal (frame,
+ g_value_get_float (value),
+ priv->right,
+ priv->bottom,
+ priv->left);
+ break;
+
+ case PROP_RIGHT:
+ nbtk_texture_frame_set_frame_internal (frame,
+ priv->top,
+ g_value_get_float (value),
+ priv->bottom,
+ priv->left);
+ break;
+
+ case PROP_BOTTOM:
+ nbtk_texture_frame_set_frame_internal (frame,
+ priv->top,
+ priv->right,
+ g_value_get_float (value),
+ priv->left);
+ break;
+
+ case PROP_LEFT:
+ nbtk_texture_frame_set_frame_internal (frame,
+ priv->top,
+ priv->right,
+ priv->bottom,
+ g_value_get_float (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_texture_frame_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_PARENT_TEXTURE:
+ g_value_set_object (value, priv->parent_texture);
+ break;
+
+ case PROP_LEFT:
+ g_value_set_float (value, priv->left);
+ break;
+
+ case PROP_TOP:
+ g_value_set_float (value, priv->top);
+ break;
+
+ case PROP_RIGHT:
+ g_value_set_float (value, priv->right);
+ break;
+
+ case PROP_BOTTOM:
+ g_value_set_float (value, priv->bottom);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_texture_frame_dispose (GObject *gobject)
+{
+ NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (gobject)->priv;
+
+ if (priv->parent_texture)
+ {
+ g_object_unref (priv->parent_texture);
+ priv->parent_texture = NULL;
+ }
+
+ G_OBJECT_CLASS (nbtk_texture_frame_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_texture_frame_class_init (NbtkTextureFrameClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (gobject_class, sizeof (NbtkTextureFramePrivate));
+
+ actor_class->get_preferred_width =
+ nbtk_texture_frame_get_preferred_width;
+ actor_class->get_preferred_height =
+ nbtk_texture_frame_get_preferred_height;
+ actor_class->paint = nbtk_texture_frame_paint;
+
+ gobject_class->set_property = nbtk_texture_frame_set_property;
+ gobject_class->get_property = nbtk_texture_frame_get_property;
+ gobject_class->dispose = nbtk_texture_frame_dispose;
+
+ pspec = g_param_spec_object ("parent-texture",
+ "Parent Texture",
+ "The parent ClutterTexture",
+ CLUTTER_TYPE_TEXTURE,
+ NBTK_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT);
+ g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
+
+ pspec = g_param_spec_float ("left",
+ "Left",
+ "Left offset",
+ 0, G_MAXFLOAT,
+ 0,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
+
+ pspec = g_param_spec_float ("top",
+ "Top",
+ "Top offset",
+ 0, G_MAXFLOAT,
+ 0,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_TOP, pspec);
+
+ pspec = g_param_spec_float ("bottom",
+ "Bottom",
+ "Bottom offset",
+ 0, G_MAXFLOAT,
+ 0,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
+
+ pspec = g_param_spec_float ("right",
+ "Right",
+ "Right offset",
+ 0, G_MAXFLOAT,
+ 0,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
+}
+
+static void
+nbtk_texture_frame_init (NbtkTextureFrame *self)
+{
+ NbtkTextureFramePrivate *priv;
+
+ self->priv = priv = NBTK_TEXTURE_FRAME_GET_PRIVATE (self);
+}
+
+/**
+ * nbtk_texture_frame_new:
+ * @texture: a #ClutterTexture or %NULL
+ * @left: left margin preserving its content
+ * @top: top margin preserving its content
+ * @right: right margin preserving its content
+ * @bottom: bottom margin preserving its content
+ *
+ * A #NbtkTextureFrame is a specialized texture that efficiently clones
+ * an area of the given @texture while keeping preserving portions of the
+ * same texture.
+ *
+ * A #NbtkTextureFrame can be used to make a rectangular texture fit a
+ * given size without stretching its borders.
+ *
+ * Return value: the newly created #NbtkTextureFrame
+ */
+ClutterActor*
+nbtk_texture_frame_new (ClutterTexture *texture,
+ gfloat top,
+ gfloat right,
+ gfloat bottom,
+ gfloat left)
+{
+ g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
+
+ return g_object_new (NBTK_TYPE_TEXTURE_FRAME,
+ "parent-texture", texture,
+ "top", top,
+ "right", right,
+ "bottom", bottom,
+ "left", left,
+ NULL);
+}
+
+/**
+ * nbtk_texture_frame_get_parent_texture:
+ * @frame: A #NbtkTextureFrame
+ *
+ * Return the texture used by the #NbtkTextureFrame
+ *
+ * Returns: a #ClutterTexture owned by the #NbtkTextureFrame
+ */
+ClutterTexture *
+nbtk_texture_frame_get_parent_texture (NbtkTextureFrame *frame)
+{
+ g_return_val_if_fail (NBTK_IS_TEXTURE_FRAME (frame), NULL);
+
+ return frame->priv->parent_texture;
+}
+
+/**
+ * nbtk_texture_frame_set_parent_texture:
+ * @frame: A #NbtkTextureFrame
+ * @texture: A #ClutterTexture
+ *
+ * Set the #ClutterTexture used by this #NbtkTextureFrame
+ *
+ */
+void
+nbtk_texture_frame_set_parent_texture (NbtkTextureFrame *frame,
+ ClutterTexture *texture)
+{
+ NbtkTextureFramePrivate *priv;
+ gboolean was_visible;
+
+ g_return_if_fail (NBTK_IS_TEXTURE_FRAME (frame));
+ g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
+
+ priv = frame->priv;
+
+ was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
+
+ if (priv->parent_texture == texture)
+ return;
+
+ if (priv->parent_texture)
+ {
+ g_object_unref (priv->parent_texture);
+ priv->parent_texture = NULL;
+
+ if (was_visible)
+ clutter_actor_hide (CLUTTER_ACTOR (frame));
+ }
+
+ if (texture)
+ {
+ priv->parent_texture = g_object_ref_sink (texture);
+
+ if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
+ clutter_actor_show (CLUTTER_ACTOR (frame));
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
+
+ g_object_notify (G_OBJECT (frame), "parent-texture");
+}
+
+/**
+ * nbtk_texture_frame_set_frame:
+ * @frame: A #NbtkTextureFrame
+ * @top: width of the top slice
+ * @right: width of the right slice
+ * @bottom: width of the bottom slice
+ * @left: width of the left slice
+ *
+ * Set the slice lines of the specified frame. The slices are calculated as
+ * widths from the edge of the frame.
+ *
+ */
+void
+nbtk_texture_frame_set_frame (NbtkTextureFrame *frame,
+ gfloat top,
+ gfloat right,
+ gfloat bottom,
+ gfloat left)
+{
+ g_return_if_fail (NBTK_IS_TEXTURE_FRAME (frame));
+
+ nbtk_texture_frame_set_frame_internal (frame, top, right, bottom, left);
+}
+
+/**
+ * nbtk_texture_frame_get_frame:
+ * @frame: A #NbtkTextureFrame
+ * @top: width of the top slice
+ * @right: width of the right slice
+ * @bottom: width of the bottom slice
+ * @left: width of the left slice
+ *
+ * Retrieve the current slice lines from the specified frame.
+ *
+ */
+void
+nbtk_texture_frame_get_frame (NbtkTextureFrame *frame,
+ gfloat *top,
+ gfloat *right,
+ gfloat *bottom,
+ gfloat *left)
+{
+ NbtkTextureFramePrivate *priv;
+
+ g_return_if_fail (NBTK_IS_TEXTURE_FRAME (frame));
+
+ priv = frame->priv;
+
+ if (top)
+ *top = priv->top;
+
+ if (right)
+ *right = priv->right;
+
+ if (bottom)
+ *bottom = priv->bottom;
+
+ if (left)
+ *left = priv->left;
+}
diff --git a/src/nbtk/nbtk-texture-frame.h b/src/nbtk/nbtk-texture-frame.h
new file mode 100644
index 0000000..710c10b
--- /dev/null
+++ b/src/nbtk/nbtk-texture-frame.h
@@ -0,0 +1,92 @@
+/*
+ * nbtk-texture-frame.h: Expandible texture actor
+ *
+ * Copyright 2007, 2008 OpenedHand Ltd
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_TEXTURE_FRAME_H__
+#define __NBTK_TEXTURE_FRAME_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_TEXTURE_FRAME (nbtk_texture_frame_get_type ())
+#define NBTK_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFrame))
+#define NBTK_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFrameClass))
+#define NBTK_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_TEXTURE_FRAME))
+#define NBTK_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_TEXTURE_FRAME))
+#define NBTK_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFrameClass))
+
+typedef struct _NbtkTextureFrame NbtkTextureFrame;
+typedef struct _NbtkTextureFramePrivate NbtkTextureFramePrivate;
+typedef struct _NbtkTextureFrameClass NbtkTextureFrameClass;
+
+/**
+ * NbtkTextureFrame:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _NbtkTextureFrame
+{
+ /*< private >*/
+ ClutterActor parent_instance;
+
+ NbtkTextureFramePrivate *priv;
+};
+
+struct _NbtkTextureFrameClass
+{
+ ClutterActorClass parent_class;
+
+ /* padding for future expansion */
+ void (*_clutter_box_1) (void);
+ void (*_clutter_box_2) (void);
+ void (*_clutter_box_3) (void);
+ void (*_clutter_box_4) (void);
+};
+
+GType nbtk_texture_frame_get_type (void) G_GNUC_CONST;
+ClutterActor * nbtk_texture_frame_new (ClutterTexture *texture,
+ gfloat top,
+ gfloat right,
+ gfloat bottom,
+ gfloat left);
+void nbtk_texture_frame_set_parent_texture (NbtkTextureFrame *frame,
+ ClutterTexture *texture);
+ClutterTexture *nbtk_texture_frame_get_parent_texture (NbtkTextureFrame *frame);
+void nbtk_texture_frame_set_frame (NbtkTextureFrame *frame,
+ gfloat top,
+ gfloat right,
+ gfloat bottom,
+ gfloat left);
+void nbtk_texture_frame_get_frame (NbtkTextureFrame *frame,
+ gfloat *top,
+ gfloat *right,
+ gfloat *bottom,
+ gfloat *left);
+
+G_END_DECLS
+
+#endif /* __NBTK_TEXTURE_FRAME_H__ */
diff --git a/src/nbtk/nbtk-tooltip.c b/src/nbtk/nbtk-tooltip.c
new file mode 100644
index 0000000..4bae527
--- /dev/null
+++ b/src/nbtk/nbtk-tooltip.c
@@ -0,0 +1,710 @@
+/*
+ * nbtk-tooltip.c: Plain tooltip actor
+ *
+ * Copyright 2008, 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Thomas Wood <thomas linux intel com>
+ *
+ */
+
+/**
+ * SECTION:nbtk-tooltip
+ * @short_description: A tooltip widget
+ *
+ * #NbtkTooltip implements a single tooltip. It should not normally be created
+ * by the application but by the widget implementing tooltip capabilities, for
+ * example, #nbtk_button_set_tooltip().
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "nbtk-tooltip.h"
+
+#include "nbtk-widget.h"
+#include "nbtk-stylable.h"
+#include "nbtk-private.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_LABEL,
+ PROP_TIP_AREA
+};
+
+#define NBTK_TOOLTIP_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_TOOLTIP, NbtkTooltipPrivate))
+
+struct _NbtkTooltipPrivate
+{
+ ClutterActor *label;
+
+ gfloat arrow_offset;
+ gboolean actor_below;
+
+ ClutterGeometry *tip_area;
+};
+
+G_DEFINE_TYPE (NbtkTooltip, nbtk_tooltip, NBTK_TYPE_WIDGET);
+
+static void
+nbtk_tooltip_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkTooltip *tooltip = NBTK_TOOLTIP (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ nbtk_tooltip_set_label (tooltip, g_value_get_string (value));
+ break;
+
+ case PROP_TIP_AREA:
+ nbtk_tooltip_set_tip_area (tooltip, g_value_get_boxed (value));
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_tooltip_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkTooltipPrivate *priv = NBTK_TOOLTIP (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
+ break;
+
+ case PROP_TIP_AREA:
+ g_value_set_boxed (value, priv->tip_area);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_tooltip_style_changed (NbtkWidget *self)
+{
+ ClutterColor *color = NULL;
+ NbtkTooltipPrivate *priv;
+ gchar *font_name;
+ gchar *font_string;
+ gint font_size;
+
+ priv = NBTK_TOOLTIP (self)->priv;
+
+ nbtk_stylable_get (NBTK_STYLABLE (self),
+ "color", &color,
+ "font-family", &font_name,
+ "font-size", &font_size,
+ NULL);
+
+ if (color)
+ {
+ clutter_text_set_color (CLUTTER_TEXT (priv->label), color);
+ clutter_color_free (color);
+ }
+
+ if (font_name || font_size)
+ {
+ if (font_name && font_size)
+ {
+ font_string = g_strdup_printf ("%s %dpx", font_name, font_size);
+ g_free (font_name);
+ }
+ else
+ if (font_size)
+ font_string = g_strdup_printf ("%dpx", font_size);
+ else
+ font_string = font_name;
+
+ clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
+
+ g_free (font_string);
+ }
+
+}
+
+static void
+nbtk_tooltip_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+ gfloat min_label_w, natural_label_w;
+ gfloat label_height, arrow_height;
+ ClutterActor *arrow_image;
+ NbtkPadding padding;
+
+ nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+ arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+ if (arrow_image)
+ {
+ clutter_actor_get_preferred_height (arrow_image,
+ -1,
+ NULL,
+ &arrow_height);
+ }
+ else
+ {
+ arrow_height = 0;
+ }
+
+ if (for_height > -1)
+ {
+ label_height = for_height - arrow_height - padding.top - padding.bottom;
+ }
+ else
+ {
+ label_height = -1;
+ }
+
+ if (priv->label)
+ {
+ clutter_actor_get_preferred_width (priv->label,
+ label_height,
+ &min_label_w,
+ &natural_label_w);
+ }
+ else
+ {
+ min_label_w = 0;
+ natural_label_w = 0;
+ }
+
+
+ if (min_width_p)
+ {
+ *min_width_p = padding.left + padding.right + min_label_w;
+ }
+
+ if (natural_width_p)
+ {
+ *natural_width_p = padding.left + padding.right + natural_label_w;
+ }
+}
+
+static void
+nbtk_tooltip_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+ gfloat arrow_height;
+ gfloat min_label_h, natural_label_h;
+ gfloat label_width;
+ ClutterActor *arrow_image;
+ NbtkPadding padding;
+
+ arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+
+ if (arrow_image && !priv->actor_below)
+ {
+ clutter_actor_get_preferred_height (arrow_image,
+ -1,
+ NULL,
+ &arrow_height);
+ }
+ else
+ {
+ arrow_height = 0;
+ }
+ nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+ if (for_width > -1)
+ {
+ label_width = for_width - padding.left - padding.right;
+ }
+ else
+ {
+ label_width = -1;
+ }
+
+ if (priv->label)
+ {
+ clutter_actor_get_preferred_height (priv->label,
+ label_width,
+ &min_label_h,
+ &natural_label_h);
+ }
+ else
+ {
+ min_label_h = 0;
+ natural_label_h = 0;
+ }
+
+ if (min_height_p)
+ {
+ *min_height_p = padding.top + padding.bottom
+ + arrow_height + min_label_h;
+ }
+
+ if (natural_height_p)
+ {
+ *natural_height_p = padding.top + padding.bottom
+ + arrow_height + natural_label_h;
+ }
+}
+
+static void
+nbtk_tooltip_allocate (ClutterActor *self,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+ ClutterActorBox child_box, arrow_box;
+ gfloat arrow_height, arrow_width;
+ ClutterActor *border_image, *arrow_image;
+ NbtkPadding padding;
+
+ CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->allocate (self,
+ box,
+ flags);
+
+ nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+ arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+
+ if (arrow_image && !priv->actor_below)
+ {
+ clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height);
+ clutter_actor_get_preferred_width (arrow_image, -1, NULL, &arrow_width);
+
+ arrow_box.x1 = (float) (priv->arrow_offset) - (int) (arrow_width / 2);
+ arrow_box.y1 = 0;
+ arrow_box.x2 = arrow_box.x1 + arrow_width;
+ arrow_box.y2 = arrow_box.y1 + arrow_height;
+
+ clutter_actor_allocate (arrow_image, &arrow_box, flags);
+ }
+ else
+ {
+ arrow_height = 0;
+ arrow_width = 0;
+ }
+
+ child_box.x1 = child_box.y1 = 0;
+ child_box.x2 = (box->x2 - box->x1);
+ child_box.y2 = (box->y2 - box->y1);
+
+ /* remove the space that is used by the arrow */
+ child_box.y1 += arrow_height;
+
+ border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+ if (border_image)
+ clutter_actor_allocate (border_image, &child_box, flags);
+
+ if (priv->label)
+ {
+ /* now remove the padding */
+ child_box.y1 += padding.top;
+ child_box.x1 += padding.left;
+ child_box.x2 -= padding.right;
+ child_box.y2 -= padding.bottom;
+
+ clutter_actor_allocate (priv->label, &child_box, flags);
+ }
+}
+
+
+static void
+nbtk_tooltip_paint (ClutterActor *self)
+{
+ ClutterActor *border_image, *arrow_image;
+ NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+
+ border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+ if (border_image)
+ clutter_actor_paint (border_image);
+
+ arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+ if (arrow_image && !priv->actor_below)
+ clutter_actor_paint (arrow_image);
+
+ clutter_actor_paint (priv->label);
+}
+
+static void
+nbtk_tooltip_map (ClutterActor *self)
+{
+ NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+ ClutterActor *border_image, *arrow_image;
+
+ CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->map (self);
+
+ border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+ if (border_image)
+ clutter_actor_map (border_image);
+
+ arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+ if (arrow_image)
+ clutter_actor_map (arrow_image);
+
+ clutter_actor_map (priv->label);
+}
+
+static void
+nbtk_tooltip_unmap (ClutterActor *self)
+{
+ NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+ ClutterActor *border_image, *arrow_image;
+
+ CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->unmap (self);
+
+ border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+ if (border_image)
+ clutter_actor_unmap (border_image);
+
+ arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+ if (arrow_image)
+ clutter_actor_unmap (arrow_image);
+
+ clutter_actor_unmap (priv->label);
+}
+
+static void
+nbtk_tooltip_class_init (NbtkTooltipClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (NbtkTooltipPrivate));
+
+ gobject_class->set_property = nbtk_tooltip_set_property;
+ gobject_class->get_property = nbtk_tooltip_get_property;
+
+ actor_class->get_preferred_width = nbtk_tooltip_get_preferred_width;
+ actor_class->get_preferred_height = nbtk_tooltip_get_preferred_height;
+ actor_class->allocate = nbtk_tooltip_allocate;
+ actor_class->paint = nbtk_tooltip_paint;
+ actor_class->map = nbtk_tooltip_map;
+ actor_class->unmap = nbtk_tooltip_unmap;
+
+ pspec = g_param_spec_string ("label",
+ "Label",
+ "Label of the tooltip",
+ NULL, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
+
+ pspec = g_param_spec_boxed ("tip-area",
+ "Tip Area",
+ "Area on the stage the tooltip applies to",
+ CLUTTER_TYPE_GEOMETRY,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_TIP_AREA, pspec);
+}
+
+static void
+nbtk_tooltip_init (NbtkTooltip *tooltip)
+{
+ tooltip->priv = NBTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+ tooltip->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
+ "line-alignment", PANGO_ALIGN_CENTER,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "use-markup", TRUE,
+ NULL);
+
+ tooltip->priv->tip_area = NULL;
+
+ clutter_actor_set_parent (CLUTTER_ACTOR (tooltip->priv->label),
+ CLUTTER_ACTOR (tooltip));
+
+ g_object_set (tooltip, "show-on-set-parent", FALSE, NULL);
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (tooltip), FALSE);
+
+ g_signal_connect (tooltip, "style-changed",
+ G_CALLBACK (nbtk_tooltip_style_changed), NULL);
+}
+
+static void
+nbtk_tooltip_update_position (NbtkTooltip *tooltip)
+{
+ NbtkTooltipPrivate *priv = tooltip->priv;
+ ClutterGeometry *tip_area = tooltip->priv->tip_area;
+ gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y;
+ gfloat stage_w, stage_h;
+ ClutterActor *stage;
+
+ /* ensure the tooltip with is not fixed size */
+ clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1);
+
+ /* if no area set, just position ourselves top left */
+ if (!priv->tip_area)
+ {
+ clutter_actor_set_position ((ClutterActor*) tooltip, 0, 0);
+ return;
+ }
+
+ /* we need to have a style in case there are padding values to take into
+ * account when calculating width/height */
+ nbtk_widget_ensure_style ((NbtkWidget *) tooltip);
+
+ /* find out the tooltip's size */
+ clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h);
+
+ /* attempt to place the tooltip */
+ tooltip_x = (int) (tip_area->x + (tip_area->width / 2) - (tooltip_w / 2));
+ tooltip_y = (int) (tip_area->y + tip_area->height);
+
+ stage = clutter_actor_get_stage ((ClutterActor *) tooltip);
+ if (!stage)
+ {
+ return;
+ }
+ clutter_actor_get_size (stage, &stage_w, &stage_h);
+
+ /* make sure the tooltip is not off screen vertically */
+ if (tooltip_w > stage_w)
+ {
+ tooltip_x = 0;
+ clutter_actor_set_width ((ClutterActor*) tooltip, stage_w);
+ }
+ else if (tooltip_x < 0)
+ {
+ tooltip_x = 0;
+ }
+ else if (tooltip_x + tooltip_w > stage_w)
+ {
+ tooltip_x = (int) (stage_w) - tooltip_w;
+ }
+
+ /* make sure the tooltip is not off screen horizontally */
+ if (tooltip_y + tooltip_h > stage_h)
+ {
+ priv->actor_below = TRUE;
+
+ /* re-query size as may have changed */
+ clutter_actor_get_preferred_height ((ClutterActor*) tooltip,
+ -1, NULL, &tooltip_h);
+ tooltip_y = tip_area->y - tooltip_h;
+ }
+ else
+ {
+ priv->actor_below = FALSE;
+ }
+
+ /* calculate the arrow offset */
+ priv->arrow_offset = tip_area->x + tip_area->width / 2 - tooltip_x;
+
+ clutter_actor_set_position ((ClutterActor*) tooltip, tooltip_x, tooltip_y);
+}
+
+/**
+ * nbtk_tooltip_get_label:
+ * @tooltip: a #NbtkTooltip
+ *
+ * Get the text displayed on the tooltip
+ *
+ * Returns: the text for the tooltip. This must not be freed by the application
+ */
+G_CONST_RETURN gchar *
+nbtk_tooltip_get_label (NbtkTooltip *tooltip)
+{
+ g_return_val_if_fail (NBTK_IS_TOOLTIP (tooltip), NULL);
+
+ return clutter_text_get_text (CLUTTER_TEXT (tooltip->priv->label));
+}
+
+/**
+ * nbtk_tooltip_set_label:
+ * @tooltip: a #NbtkTooltip
+ * @text: text to set the label to
+ *
+ * Sets the text displayed on the tooltip
+ */
+void
+nbtk_tooltip_set_label (NbtkTooltip *tooltip,
+ const gchar *text)
+{
+ NbtkTooltipPrivate *priv;
+
+ g_return_if_fail (NBTK_IS_TOOLTIP (tooltip));
+
+ priv = tooltip->priv;
+
+ clutter_text_set_text (CLUTTER_TEXT (priv->label), text);
+
+ g_object_notify (G_OBJECT (tooltip), "label");
+}
+
+/**
+ * nbtk_tooltip_show:
+ * @tooltip: a #NbtkTooltip
+ *
+ * Show the tooltip relative to the associated widget.
+ */
+void
+nbtk_tooltip_show (NbtkTooltip *tooltip)
+{
+ NbtkTooltipPrivate *priv;
+ ClutterActor *parent;
+ ClutterActor *stage;
+ ClutterActor *self = CLUTTER_ACTOR (tooltip);
+ ClutterAnimation *animation;
+
+ /* make sure we're not currently already animating (e.g. hiding) */
+ animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
+ if (animation)
+ clutter_animation_completed (animation);
+
+ priv = tooltip->priv;
+ parent = clutter_actor_get_parent (self);
+ stage = clutter_actor_get_stage (self);
+
+ if (!stage)
+ {
+ g_warning ("NbtkTooltip is not on any stage.");
+ return;
+ }
+
+ /* make sure we're parented on the stage */
+ if (G_UNLIKELY (parent != stage))
+ {
+ g_object_ref (self);
+ clutter_actor_unparent (self);
+ clutter_actor_set_parent (self, stage);
+ g_object_unref (self);
+ parent = stage;
+ }
+
+ /* raise the tooltip to the top */
+ clutter_container_raise_child (CLUTTER_CONTAINER (stage),
+ CLUTTER_ACTOR (tooltip),
+ NULL);
+
+ nbtk_tooltip_update_position (tooltip);
+
+ /* finally show the tooltip... */
+ CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->show (self);
+
+ /* and give it some bounce! */
+ g_object_set (G_OBJECT (self),
+ "scale-center-x", priv->arrow_offset,
+ "scale-center-y", (priv->actor_below) ? clutter_actor_get_height (self) : 0,
+ NULL);
+ clutter_actor_set_scale (self, 0.0, 0.0);
+ clutter_actor_animate (self, CLUTTER_EASE_OUT_ELASTIC,
+ 500,
+ "scale-x", 1.0,
+ "scale-y", 1.0,
+ NULL);
+}
+
+static void
+nbtk_tooltip_hide_complete (ClutterAnimation *animation,
+ ClutterActor *actor)
+{
+ CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->hide (actor);
+ g_signal_handlers_disconnect_by_func (actor,
+ nbtk_tooltip_hide_complete,
+ actor);
+}
+
+/**
+ * nbtk_tooltip_hide:
+ * @tooltip: a #NbtkTooltip
+ *
+ * Hide the tooltip
+ */
+void
+nbtk_tooltip_hide (NbtkTooltip *tooltip)
+{
+ ClutterAnimation *animation;
+
+ g_return_if_fail (NBTK_TOOLTIP (tooltip));
+
+ /* make sure we're not currently already animating (e.g. hiding) */
+ animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
+ if (animation)
+ clutter_animation_completed (animation);
+
+ g_object_set (G_OBJECT (tooltip),
+ "scale-center-x", tooltip->priv->arrow_offset,
+ NULL);
+ animation =
+ clutter_actor_animate (CLUTTER_ACTOR (tooltip), CLUTTER_EASE_IN_SINE,
+ 150,
+ "scale-x", 0.0,
+ "scale-y", 0.0,
+ NULL);
+ g_signal_connect (animation, "completed",
+ G_CALLBACK (nbtk_tooltip_hide_complete), tooltip);
+}
+
+/**
+ * nbtk_tooltip_set_tip_area:
+ * @tooltip: A #NbtkTooltip
+ * @area: A #ClutterGeometry
+ *
+ * Set the area on the stage that the tooltip applies to.
+ */
+void
+nbtk_tooltip_set_tip_area (NbtkTooltip *tooltip,
+ const ClutterGeometry *area)
+{
+ g_return_if_fail (NBTK_IS_TOOLTIP (tooltip));
+
+ if (tooltip->priv->tip_area)
+ g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area);
+ tooltip->priv->tip_area = g_boxed_copy (CLUTTER_TYPE_GEOMETRY, area);
+
+ nbtk_tooltip_update_position (tooltip);
+}
+
+/**
+ * nbtk_tooltip_get_tip_area:
+ * @tooltip: A #NbtkTooltip
+ *
+ * Retrieve the area on the stage that the tooltip currently applies to
+ *
+ * Returns: the #ClutterGeometry, owned by the tooltip which must not be freed
+ * by the application.
+ */
+G_CONST_RETURN ClutterGeometry*
+nbtk_tooltip_get_tip_area (NbtkTooltip *tooltip)
+{
+ g_return_val_if_fail (NBTK_IS_TOOLTIP (tooltip), NULL);
+
+ return tooltip->priv->tip_area;
+}
diff --git a/src/nbtk/nbtk-tooltip.h b/src/nbtk/nbtk-tooltip.h
new file mode 100644
index 0000000..8f6b435
--- /dev/null
+++ b/src/nbtk/nbtk-tooltip.h
@@ -0,0 +1,79 @@
+/*
+ * nbtk-tooltip.h: Plain tooltip actor
+ *
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_TOOLTIP_H__
+#define __NBTK_TOOLTIP_H__
+
+G_BEGIN_DECLS
+
+#include <nbtk/nbtk-bin.h>
+
+#define NBTK_TYPE_TOOLTIP (nbtk_tooltip_get_type ())
+#define NBTK_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_TOOLTIP, NbtkTooltip))
+#define NBTK_IS_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_TOOLTIP))
+#define NBTK_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_TOOLTIP, NbtkTooltipClass))
+#define NBTK_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_TOOLTIP))
+#define NBTK_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_TOOLTIP, NbtkTooltipClass))
+
+typedef struct _NbtkTooltip NbtkTooltip;
+typedef struct _NbtkTooltipPrivate NbtkTooltipPrivate;
+typedef struct _NbtkTooltipClass NbtkTooltipClass;
+
+/**
+ * NbtkTooltip:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+struct _NbtkTooltip
+{
+ /*< private >*/
+ NbtkBin parent_instance;
+
+ NbtkTooltipPrivate *priv;
+};
+
+struct _NbtkTooltipClass
+{
+ NbtkBinClass parent_class;
+};
+
+GType nbtk_tooltip_get_type (void) G_GNUC_CONST;
+
+G_CONST_RETURN gchar *nbtk_tooltip_get_label (NbtkTooltip *tooltip);
+void nbtk_tooltip_set_label (NbtkTooltip *tooltip,
+ const gchar *text);
+void nbtk_tooltip_show (NbtkTooltip *tooltip);
+void nbtk_tooltip_hide (NbtkTooltip *tooltip);
+
+void nbtk_tooltip_set_tip_area (NbtkTooltip *tooltip, const ClutterGeometry *area);
+G_CONST_RETURN ClutterGeometry* nbtk_tooltip_get_tip_area (NbtkTooltip *tooltip);
+
+G_END_DECLS
+
+#endif /* __NBTK_TOOLTIP_H__ */
diff --git a/src/nbtk/nbtk-types.h b/src/nbtk/nbtk-types.h
new file mode 100644
index 0000000..77acfad
--- /dev/null
+++ b/src/nbtk/nbtk-types.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_TYPES_H__
+#define __NBTK_TYPES_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_BORDER_IMAGE (nbtk_border_image_get_type ())
+#define NBTK_TYPE_PADDING (nbtk_padding_get_type ())
+
+typedef struct _NbtkPadding NbtkPadding;
+
+GType nbtk_border_image_get_type (void) G_GNUC_CONST;
+
+/**
+ * NbtkPadding:
+ * @top: padding from the top
+ * @right: padding from the right
+ * @bottom: padding from the bottom
+ * @left: padding from the left
+ *
+ * The padding from the internal border of the parent container.
+ */
+struct _NbtkPadding
+{
+ gfloat top;
+ gfloat right;
+ gfloat bottom;
+ gfloat left;
+};
+
+GType nbtk_padding_get_type (void) G_GNUC_CONST;
+
+/**
+ * NbtkAlignment:
+ * @NBTK_ALIGN_TOP: align to the top (vertically)
+ * @NBTK_ALIGN_RIGHT: align to the right (horizontally)
+ * @NBTK_ALIGN_BOTTOM: align to the bottom (vertically)
+ * @NBTK_ALIGN_LEFT: align to the left (horizontally)
+ * @NBTK_ALIGN_CENTER: align to the center (horizontally or vertically)
+ *
+ * The alignment values for a #NbtkBin.
+ */
+typedef enum {
+ NBTK_ALIGN_TOP,
+ NBTK_ALIGN_RIGHT,
+ NBTK_ALIGN_BOTTOM,
+ NBTK_ALIGN_LEFT,
+ NBTK_ALIGN_CENTER
+} NbtkAlignment;
+
+typedef enum {
+ NBTK_ALIGN_START,
+ NBTK_ALIGN_MIDDLE,
+ NBTK_ALIGN_END
+} NbtkAlign;
+
+G_END_DECLS
+
+#endif /* __NBTK_TYPES_H__ */
diff --git a/src/nbtk/nbtk-widget.c b/src/nbtk/nbtk-widget.c
new file mode 100644
index 0000000..1542cec
--- /dev/null
+++ b/src/nbtk/nbtk-widget.c
@@ -0,0 +1,1354 @@
+/*
+ * nbtk-widget.c: Base class for Nbtk actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ * Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <clutter/clutter.h>
+#include <ccss/ccss.h>
+
+#include "nbtk-widget.h"
+
+#include "nbtk-marshal.h"
+#include "nbtk-private.h"
+#include "nbtk-stylable.h"
+#include "nbtk-texture-cache.h"
+#include "nbtk-texture-frame.h"
+#include "nbtk-tooltip.h"
+
+typedef ccss_border_image_t NbtkBorderImage;
+
+/*
+ * Forward declaration for sake of NbtkWidgetChild
+ */
+struct _NbtkWidgetPrivate
+{
+ NbtkPadding border;
+ NbtkPadding padding;
+
+ NbtkStyle *style;
+ gchar *pseudo_class;
+ gchar *style_class;
+
+ ClutterActor *border_image;
+ ClutterActor *background_image;
+ ClutterColor *bg_color;
+
+ gboolean is_stylable : 1;
+ gboolean has_tooltip : 1;
+ gboolean is_style_dirty : 1;
+
+ NbtkTooltip *tooltip;
+};
+
+/**
+ * SECTION:nbtk-widget
+ * @short_description: Base class for stylable actors
+ *
+ * #NbtkWidget is a simple abstract class on top of #ClutterActor. It
+ * provides basic themeing properties.
+ *
+ * Actors in the Nbtk library should subclass #NbtkWidget if they plan
+ * to obey to a certain #NbtkStyle.
+ */
+
+enum
+{
+ PROP_0,
+
+ PROP_STYLE,
+ PROP_PSEUDO_CLASS,
+ PROP_STYLE_CLASS,
+
+ PROP_STYLABLE,
+
+ PROP_HAS_TOOLTIP,
+ PROP_TOOLTIP_TEXT
+};
+
+static void nbtk_stylable_iface_init (NbtkStylableIface *iface);
+
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NbtkWidget, nbtk_widget, CLUTTER_TYPE_ACTOR,
+ G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE,
+ nbtk_stylable_iface_init));
+
+#define NBTK_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_WIDGET, NbtkWidgetPrivate))
+
+static void
+nbtk_widget_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkWidget *actor = NBTK_WIDGET (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_STYLE:
+ nbtk_stylable_set_style (NBTK_STYLABLE (actor),
+ g_value_get_object (value));
+ break;
+
+ case PROP_PSEUDO_CLASS:
+ nbtk_widget_set_style_pseudo_class (actor, g_value_get_string (value));
+ break;
+
+ case PROP_STYLE_CLASS:
+ nbtk_widget_set_style_class_name (actor, g_value_get_string (value));
+ break;
+
+ case PROP_STYLABLE:
+ if (actor->priv->is_stylable != g_value_get_boolean (value))
+ {
+ actor->priv->is_stylable = g_value_get_boolean (value);
+ clutter_actor_queue_relayout ((ClutterActor *)gobject);
+ }
+ break;
+
+ case PROP_HAS_TOOLTIP:
+ nbtk_widget_set_has_tooltip (actor, g_value_get_boolean (value));
+ break;
+
+ case PROP_TOOLTIP_TEXT:
+ nbtk_widget_set_tooltip_text (actor, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_widget_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NbtkWidget *actor = NBTK_WIDGET (gobject);
+ NbtkWidgetPrivate *priv = actor->priv;
+
+ switch (prop_id)
+ {
+ case PROP_STYLE:
+ g_value_set_object (value, priv->style);
+ break;
+
+ case PROP_PSEUDO_CLASS:
+ g_value_set_string (value, priv->pseudo_class);
+ break;
+
+ case PROP_STYLE_CLASS:
+ g_value_set_string (value, priv->style_class);
+ break;
+
+ case PROP_STYLABLE:
+ g_value_set_boolean (value, priv->is_stylable);
+ break;
+
+ case PROP_HAS_TOOLTIP:
+ g_value_set_boolean (value, priv->has_tooltip);
+ break;
+
+ case PROP_TOOLTIP_TEXT:
+ g_value_set_string (value, nbtk_tooltip_get_label (priv->tooltip));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nbtk_widget_dispose (GObject *gobject)
+{
+ NbtkWidget *actor = NBTK_WIDGET (gobject);
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+ if (priv->style)
+ {
+ g_object_unref (priv->style);
+ priv->style = NULL;
+ }
+
+ if (priv->border_image)
+ {
+ clutter_actor_unparent (priv->border_image);
+ priv->border_image = NULL;
+ }
+
+ if (priv->bg_color)
+ {
+ clutter_color_free (priv->bg_color);
+ priv->bg_color = NULL;
+ }
+
+ if (priv->tooltip)
+ {
+ ClutterContainer *parent;
+ ClutterActor *tooltip = CLUTTER_ACTOR (priv->tooltip);
+
+ /* this is just a little bit awkward because the tooltip is parented
+ * on the stage, but we still want to "own" it */
+ parent = CLUTTER_CONTAINER (clutter_actor_get_parent (tooltip));
+
+ if (parent)
+ clutter_container_remove_actor (parent, tooltip);
+
+ priv->tooltip = NULL;
+ }
+
+ G_OBJECT_CLASS (nbtk_widget_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_widget_finalize (GObject *gobject)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (gobject)->priv;
+
+ g_free (priv->style_class);
+ g_free (priv->pseudo_class);
+
+ G_OBJECT_CLASS (nbtk_widget_parent_class)->finalize (gobject);
+}
+
+static void
+nbtk_widget_allocate (ClutterActor *actor,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+ ClutterActorClass *klass;
+ ClutterGeometry area;
+ ClutterVertex in_v, out_v;
+
+ klass = CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class);
+ klass->allocate (actor, box, flags);
+
+ /* update tooltip position */
+ if (priv->tooltip)
+ {
+ in_v.x = in_v.y = in_v.z = 0;
+ clutter_actor_apply_transform_to_point (actor, &in_v, &out_v);
+ area.x = out_v.x;
+ area.y = out_v.y;
+
+ in_v.x = box->x2 - box->x1;
+ in_v.y = box->y2 - box->y1;
+ clutter_actor_apply_transform_to_point (actor, &in_v, &out_v);
+ area.width = out_v.x - area.x;
+ area.height = out_v.y - area.y;
+
+ nbtk_tooltip_set_tip_area (priv->tooltip, &area);
+ }
+
+
+
+ if (priv->border_image)
+ {
+ ClutterActorBox frame_box = {
+ 0,
+ 0,
+ box->x2 - box->x1,
+ box->y2 - box->y1
+ };
+
+ clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
+ &frame_box,
+ flags);
+ }
+
+ if (priv->background_image)
+ {
+ ClutterActorBox frame_box = {
+ 0, 0, box->x2 - box->x1, box->y2 - box->y1
+ };
+ gfloat w, h;
+
+ clutter_actor_get_size (CLUTTER_ACTOR (priv->background_image), &w, &h);
+
+ /* scale the background into the allocated bounds */
+ if (w > frame_box.x2 || h > frame_box.y2)
+ {
+ gint new_h, new_w, offset;
+ gint box_w, box_h;
+
+ box_w = (int) frame_box.x2;
+ box_h = (int) frame_box.y2;
+
+ /* scale to fit */
+ new_h = (int) ((h / w) * ((gfloat) box_w));
+ new_w = (int) ((w / h) * ((gfloat) box_h));
+
+ if (new_h > box_h)
+ {
+ /* center for new width */
+ offset = ((box_w) - new_w) * 0.5;
+ frame_box.x1 = offset;
+ frame_box.x2 = offset + new_w;
+
+ frame_box.y2 = box_h;
+ }
+ else
+ {
+ /* center for new height */
+ offset = ((box_h) - new_h) * 0.5;
+ frame_box.y1 = offset;
+ frame_box.y2 = offset + new_h;
+
+ frame_box.x2 = box_w;
+ }
+
+ }
+ else
+ {
+ /* center the background on the widget */
+ frame_box.x1 = (int) (((box->x2 - box->x1) / 2) - (w / 2));
+ frame_box.y1 = (int) (((box->y2 - box->y1) / 2) - (h / 2));
+ frame_box.x2 = frame_box.x1 + w;
+ frame_box.y2 = frame_box.y1 + h;
+ }
+
+ clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
+ &frame_box,
+ flags);
+ }
+}
+
+static void
+nbtk_widget_real_draw_background (NbtkWidget *self,
+ ClutterActor *background,
+ const ClutterColor *color)
+{
+ /* Default implementation just draws the background
+ * colour and the image on top
+ */
+ if (color && color->alpha != 0)
+ {
+ ClutterActor *actor = CLUTTER_ACTOR (self);
+ ClutterActorBox allocation = { 0, };
+ ClutterColor bg_color = *color;
+ gfloat w, h;
+
+ bg_color.alpha = clutter_actor_get_paint_opacity (actor)
+ * bg_color.alpha
+ / 255;
+
+ clutter_actor_get_allocation_box (actor, &allocation);
+
+ w = allocation.x2 - allocation.x1;
+ h = allocation.y2 - allocation.y1;
+
+ cogl_set_source_color4ub (bg_color.red,
+ bg_color.green,
+ bg_color.blue,
+ bg_color.alpha);
+ cogl_rectangle (0, 0, w, h);
+ }
+
+ if (background)
+ clutter_actor_paint (background);
+}
+
+static void
+nbtk_widget_paint (ClutterActor *self)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (self)->priv;
+ NbtkWidgetClass *klass = NBTK_WIDGET_GET_CLASS (self);
+
+ klass->draw_background (NBTK_WIDGET (self),
+ priv->border_image,
+ priv->bg_color);
+
+ if (priv->background_image != NULL)
+ clutter_actor_paint (priv->background_image);
+}
+
+static void
+nbtk_widget_parent_set (ClutterActor *widget,
+ ClutterActor *old_parent)
+{
+ ClutterActorClass *parent_class;
+ ClutterActor *new_parent;
+
+ parent_class = CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class);
+ if (parent_class->parent_set)
+ parent_class->parent_set (widget, old_parent);
+
+ new_parent = clutter_actor_get_parent (widget);
+
+ /* don't send the style changed signal if we no longer have a parent actor */
+ if (new_parent)
+ nbtk_stylable_changed ((NbtkStylable*) widget);
+}
+
+static void
+nbtk_widget_map (ClutterActor *actor)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+ CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->map (actor);
+
+ nbtk_widget_ensure_style ((NbtkWidget*) actor);
+
+ if (priv->border_image)
+ clutter_actor_map (priv->border_image);
+
+ if (priv->background_image)
+ clutter_actor_map (priv->background_image);
+
+ if (priv->tooltip)
+ clutter_actor_map ((ClutterActor *) priv->tooltip);
+}
+
+static void
+nbtk_widget_unmap (ClutterActor *actor)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+ CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->unmap (actor);
+
+ if (priv->border_image)
+ clutter_actor_unmap (priv->border_image);
+
+ if (priv->background_image)
+ clutter_actor_unmap (priv->background_image);
+
+ if (priv->tooltip)
+ clutter_actor_unmap ((ClutterActor *) priv->tooltip);
+}
+
+static void
+nbtk_widget_style_changed (NbtkStylable *self)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (self)->priv;
+ NbtkBorderImage *border_image = NULL;
+ NbtkTextureCache *texture_cache;
+ ClutterTexture *texture;
+ gchar *bg_file = NULL;
+ NbtkPadding *padding = NULL;
+ gboolean relayout_needed = FALSE;
+ gboolean has_changed = FALSE;
+ ClutterColor *color;
+
+ /* application has request this widget is not stylable */
+ if (!priv->is_stylable)
+ return;
+
+ /* cache these values for use in the paint function */
+ nbtk_stylable_get (self,
+ "background-color", &color,
+ "background-image", &bg_file,
+ "border-image", &border_image,
+ "padding", &padding,
+ NULL);
+
+ if (color)
+ {
+ if (priv->bg_color && clutter_color_equal (color, priv->bg_color))
+ {
+ /* color is the same ... */
+ clutter_color_free (color);
+ }
+ else
+ {
+ clutter_color_free (priv->bg_color);
+ priv->bg_color = color;
+ has_changed = TRUE;
+ }
+ }
+ else
+ if (priv->bg_color)
+ {
+ clutter_color_free (priv->bg_color);
+ priv->bg_color = NULL;
+ has_changed = TRUE;
+ }
+
+
+
+ if (padding)
+ {
+ if (priv->padding.top != padding->top ||
+ priv->padding.left != padding->left ||
+ priv->padding.right != padding->right ||
+ priv->padding.bottom != padding->bottom)
+ {
+ /* Padding changed. Need to relayout. */
+ has_changed = TRUE;
+ relayout_needed = TRUE;
+ }
+
+ priv->padding = *padding;
+ g_boxed_free (NBTK_TYPE_PADDING, padding);
+ }
+
+ if (priv->border_image)
+ {
+ clutter_actor_unparent (priv->border_image);
+ priv->border_image = NULL;
+ }
+
+ if (priv->background_image)
+ {
+ clutter_actor_unparent (priv->background_image);
+ priv->background_image = NULL;
+ }
+
+ texture_cache = nbtk_texture_cache_get_default ();
+
+ /* Check if the URL is actually present, not garbage in the property */
+ if (border_image && border_image->uri)
+ {
+ gint border_left, border_right, border_top, border_bottom;
+ gint width, height;
+
+ /* `border-image' takes precedence over `background-image'.
+ * Firefox lets the background-image shine thru when border-image has
+ * alpha an channel, maybe that would be an option for the future. */
+ texture = nbtk_texture_cache_get_texture (texture_cache,
+ border_image->uri,
+ FALSE);
+
+ clutter_texture_get_base_size (CLUTTER_TEXTURE (texture),
+ &width, &height);
+
+ border_left = ccss_position_get_size (&border_image->left, width);
+ border_top = ccss_position_get_size (&border_image->top, height);
+ border_right = ccss_position_get_size (&border_image->right, width);
+ border_bottom = ccss_position_get_size (&border_image->bottom, height);
+
+ priv->border_image = nbtk_texture_frame_new (texture,
+ border_top,
+ border_right,
+ border_bottom,
+ border_left);
+ clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
+ g_boxed_free (NBTK_TYPE_BORDER_IMAGE, border_image);
+
+ has_changed = TRUE;
+ relayout_needed = TRUE;
+ }
+
+ if (bg_file != NULL &&
+ strcmp (bg_file, "none"))
+ {
+ texture = nbtk_texture_cache_get_texture (texture_cache,
+ bg_file,
+ FALSE);
+ priv->background_image = (ClutterActor*) texture;
+
+ if (priv->background_image != NULL)
+ {
+ clutter_actor_set_parent (priv->background_image,
+ CLUTTER_ACTOR (self));
+ }
+ else
+ g_warning ("Could not load %s", bg_file);
+
+ has_changed = TRUE;
+ relayout_needed = TRUE;
+ }
+ g_free (bg_file);
+
+ /* If there are any properties above that need to cause a relayout thay
+ * should set this flag.
+ */
+ if (has_changed)
+ {
+ if (relayout_needed)
+ clutter_actor_queue_relayout ((ClutterActor *) self);
+ else
+ clutter_actor_queue_redraw ((ClutterActor *) self);
+ }
+
+ priv->is_style_dirty = FALSE;
+}
+
+static void
+nbtk_widget_stylable_child_notify (ClutterActor *actor,
+ gpointer user_data)
+{
+ if (NBTK_IS_STYLABLE (actor))
+ nbtk_stylable_changed ((NbtkStylable*) actor);
+}
+
+static void
+nbtk_widget_stylable_changed (NbtkStylable *stylable)
+{
+
+ NBTK_WIDGET (stylable)->priv->is_style_dirty = TRUE;
+
+ /* update the style only if we are mapped */
+ if (!CLUTTER_ACTOR_IS_MAPPED ((ClutterActor *) stylable))
+ return;
+
+ g_signal_emit_by_name (stylable, "style-changed", 0);
+
+
+ if (CLUTTER_IS_CONTAINER (stylable))
+ {
+ /* notify our children that their parent stylable has changed */
+ clutter_container_foreach ((ClutterContainer *) stylable,
+ nbtk_widget_stylable_child_notify,
+ NULL);
+ }
+}
+
+static gboolean
+nbtk_widget_enter (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+
+ if (priv->has_tooltip)
+ nbtk_widget_show_tooltip ((NbtkWidget*) actor);
+
+ if (CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->enter_event)
+ return CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->enter_event (actor, event);
+ else
+ return FALSE;
+}
+
+static gboolean
+nbtk_widget_leave (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+ if (priv->has_tooltip)
+ nbtk_tooltip_hide (priv->tooltip);
+
+ if (CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->leave_event)
+ return CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->leave_event (actor, event);
+ else
+ return FALSE;
+}
+
+static void
+nbtk_widget_hide (ClutterActor *actor)
+{
+ NbtkWidget *widget = (NbtkWidget *) actor;
+
+ /* hide the tooltip, if there is one */
+ if (widget->priv->tooltip)
+ nbtk_tooltip_hide (NBTK_TOOLTIP (widget->priv->tooltip));
+
+ CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->hide (actor);
+}
+
+
+
+static void
+nbtk_widget_class_init (NbtkWidgetClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (NbtkWidgetPrivate));
+
+ gobject_class->set_property = nbtk_widget_set_property;
+ gobject_class->get_property = nbtk_widget_get_property;
+ gobject_class->dispose = nbtk_widget_dispose;
+ gobject_class->finalize = nbtk_widget_finalize;
+
+ actor_class->allocate = nbtk_widget_allocate;
+ actor_class->paint = nbtk_widget_paint;
+ actor_class->parent_set = nbtk_widget_parent_set;
+ actor_class->map = nbtk_widget_map;
+ actor_class->unmap = nbtk_widget_unmap;
+
+ actor_class->enter_event = nbtk_widget_enter;
+ actor_class->leave_event = nbtk_widget_leave;
+ actor_class->hide = nbtk_widget_hide;
+
+ klass->draw_background = nbtk_widget_real_draw_background;
+
+ /**
+ * NbtkWidget:pseudo-class:
+ *
+ * The pseudo-class of the actor. Typical values include "hover", "active",
+ * "focus".
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PSEUDO_CLASS,
+ g_param_spec_string ("pseudo-class",
+ "Pseudo Class",
+ "Pseudo class for styling",
+ "",
+ NBTK_PARAM_READWRITE));
+ /**
+ * NbtkWidget:style-class:
+ *
+ * The style-class of the actor for use in styling.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_STYLE_CLASS,
+ g_param_spec_string ("style-class",
+ "Style Class",
+ "Style class for styling",
+ "",
+ NBTK_PARAM_READWRITE));
+
+ g_object_class_override_property (gobject_class, PROP_STYLE, "style");
+
+ /**
+ * NbtkWidget:stylable:
+ *
+ * Enable or disable styling of the widget
+ */
+ pspec = g_param_spec_boolean ("stylable",
+ "Stylable",
+ "Whether the table should be styled",
+ TRUE,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_STYLABLE,
+ pspec);
+
+ /**
+ * NbtkWidget:has-tooltip:
+ *
+ * Determines whether the widget has a tooltip. If set to TRUE, causes the
+ * widget to monitor enter and leave events (i.e. sets the widget reactive).
+ */
+ pspec = g_param_spec_boolean ("has-tooltip",
+ "Has Tooltip",
+ "Determines whether the widget has a tooltip",
+ FALSE,
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_HAS_TOOLTIP,
+ pspec);
+
+
+ /**
+ * NbtkWidget:tooltip-text:
+ *
+ * text displayed on the tooltip
+ */
+ pspec = g_param_spec_string ("tooltip-text",
+ "Tooltip Text",
+ "Text displayed on the tooltip",
+ "",
+ NBTK_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, pspec);
+
+}
+
+static NbtkStyle *
+nbtk_widget_get_style (NbtkStylable *stylable)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (stylable)->priv;
+
+ return priv->style;
+}
+
+static void
+nbtk_style_changed_cb (NbtkStyle *style,
+ NbtkStylable *stylable)
+{
+ nbtk_stylable_changed (stylable);
+}
+
+
+static void
+nbtk_widget_set_style (NbtkStylable *stylable,
+ NbtkStyle *style)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (stylable)->priv;
+
+ if (priv->style)
+ g_object_unref (priv->style);
+
+ priv->style = g_object_ref_sink (style);
+
+ g_signal_connect (priv->style,
+ "changed",
+ G_CALLBACK (nbtk_style_changed_cb),
+ stylable);
+}
+
+static NbtkStylable*
+nbtk_widget_get_container (NbtkStylable *stylable)
+{
+ ClutterActor *parent;
+
+ g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+ parent = clutter_actor_get_parent (CLUTTER_ACTOR (stylable));
+
+ if (NBTK_IS_STYLABLE (parent))
+ return NBTK_STYLABLE (parent);
+ else
+ return NULL;
+}
+
+static NbtkStylable*
+nbtk_widget_get_base_style (NbtkStylable *stylable)
+{
+ return NULL;
+}
+
+static const gchar*
+nbtk_widget_get_style_id (NbtkStylable *stylable)
+{
+ g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+ return clutter_actor_get_name (CLUTTER_ACTOR (stylable));
+}
+
+static const gchar*
+nbtk_widget_get_style_type (NbtkStylable *stylable)
+{
+ return G_OBJECT_TYPE_NAME (stylable);
+}
+
+static const gchar*
+nbtk_widget_get_style_class (NbtkStylable *stylable)
+{
+ g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+ return NBTK_WIDGET (stylable)->priv->style_class;
+}
+
+static const gchar*
+nbtk_widget_get_pseudo_class (NbtkStylable *stylable)
+{
+ g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+ return NBTK_WIDGET (stylable)->priv->pseudo_class;
+}
+
+static gboolean
+nbtk_widget_get_viewport (NbtkStylable *stylable,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height)
+{
+ g_return_val_if_fail (NBTK_IS_WIDGET (stylable), FALSE);
+
+ *x = 0;
+ *y = 0;
+
+ *width = clutter_actor_get_width (CLUTTER_ACTOR (stylable));
+ *height = clutter_actor_get_height (CLUTTER_ACTOR (stylable));
+
+ return TRUE;
+}
+
+/**
+ * nbtk_widget_set_style_class_name:
+ * @actor: a #NbtkWidget
+ * @style_class: a new style class string
+ *
+ * Set the style class name
+ */
+void
+nbtk_widget_set_style_class_name (NbtkWidget *actor,
+ const gchar *style_class)
+{
+ NbtkWidgetPrivate *priv = actor->priv;
+
+ g_return_if_fail (NBTK_WIDGET (actor));
+
+ priv = actor->priv;
+
+ if (g_strcmp0 (style_class, priv->style_class))
+ {
+ g_free (priv->style_class);
+ priv->style_class = g_strdup (style_class);
+
+ nbtk_stylable_changed ((NbtkStylable*) actor);
+
+ g_object_notify (G_OBJECT (actor), "style-class");
+ }
+}
+
+
+/**
+ * nbtk_widget_get_style_class_name:
+ * @actor: a #NbtkWidget
+ *
+ * Get the current style class name
+ *
+ * Returns: the class name string. The string is owned by the #NbtkWidget and
+ * should not be modified or freed.
+ */
+const gchar*
+nbtk_widget_get_style_class_name (NbtkWidget *actor)
+{
+ g_return_val_if_fail (NBTK_WIDGET (actor), NULL);
+
+ return actor->priv->style_class;
+}
+
+/**
+ * nbtk_widget_get_style_pseudo_class:
+ * @actor: a #NbtkWidget
+ *
+ * Get the current style pseudo class
+ *
+ * Returns: the pseudo class string. The string is owned by the #NbtkWidget and
+ * should not be modified or freed.
+ */
+const gchar*
+nbtk_widget_get_style_pseudo_class (NbtkWidget *actor)
+{
+ g_return_val_if_fail (NBTK_WIDGET (actor), NULL);
+
+ return actor->priv->pseudo_class;
+}
+
+/**
+ * nbtk_widget_set_style_pseudo_class:
+ * @actor: a #NbtkWidget
+ * @pseudo_class: a new pseudo class string
+ *
+ * Set the style pseudo class
+ */
+void
+nbtk_widget_set_style_pseudo_class (NbtkWidget *actor,
+ const gchar *pseudo_class)
+{
+ NbtkWidgetPrivate *priv;
+
+ g_return_if_fail (NBTK_WIDGET (actor));
+
+ priv = actor->priv;
+
+ if (g_strcmp0 (pseudo_class, priv->pseudo_class))
+ {
+ g_free (priv->pseudo_class);
+ priv->pseudo_class = g_strdup (pseudo_class);
+
+ nbtk_stylable_changed ((NbtkStylable*) actor);
+
+ g_object_notify (G_OBJECT (actor), "pseudo-class");
+ }
+}
+
+
+static void
+nbtk_stylable_iface_init (NbtkStylableIface *iface)
+{
+ static gboolean is_initialized = FALSE;
+
+ if (!is_initialized)
+ {
+ GParamSpec *pspec;
+ ClutterColor color = { 0x00, 0x00, 0x00, 0xff };
+ ClutterColor bg_color = { 0xff, 0xff, 0xff, 0x00 };
+
+ is_initialized = TRUE;
+
+ pspec = clutter_param_spec_color ("background-color",
+ "Background Color",
+ "The background color of an actor",
+ &bg_color,
+ G_PARAM_READWRITE);
+ nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+ pspec = clutter_param_spec_color ("color",
+ "Text Color",
+ "The color of the text of an actor",
+ &color,
+ G_PARAM_READWRITE);
+ nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+ pspec = g_param_spec_string ("background-image",
+ "Background Image",
+ "Background image filename",
+ NULL,
+ G_PARAM_READWRITE);
+ nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+ pspec = g_param_spec_string ("font-family",
+ "Font Family",
+ "Name of the font to use",
+ "Sans",
+ G_PARAM_READWRITE);
+ nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+ pspec = g_param_spec_int ("font-size",
+ "Font Size",
+ "Size of the font to use in pixels",
+ 0, G_MAXINT, 12,
+ G_PARAM_READWRITE);
+ nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+ pspec = g_param_spec_boxed ("border-image",
+ "Border image",
+ "9-slice image to use for drawing borders and background",
+ NBTK_TYPE_BORDER_IMAGE,
+ G_PARAM_READWRITE);
+ nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+ pspec = g_param_spec_boxed ("padding",
+ "Padding",
+ "Padding between the widget's borders "
+ "and its content",
+ NBTK_TYPE_PADDING,
+ G_PARAM_READWRITE);
+ nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+ iface->style_changed = nbtk_widget_style_changed;
+ iface->stylable_changed = nbtk_widget_stylable_changed;
+
+ iface->get_style = nbtk_widget_get_style;
+ iface->set_style = nbtk_widget_set_style;
+ iface->get_base_style = nbtk_widget_get_base_style;
+ iface->get_container = nbtk_widget_get_container;
+ iface->get_style_id = nbtk_widget_get_style_id;
+ iface->get_style_type = nbtk_widget_get_style_type;
+ iface->get_style_class = nbtk_widget_get_style_class;
+ iface->get_pseudo_class = nbtk_widget_get_pseudo_class;
+ /* iface->get_attribute = nbtk_widget_get_attribute; */
+ iface->get_viewport = nbtk_widget_get_viewport;
+ }
+}
+
+
+static void
+nbtk_widget_name_notify (NbtkWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ nbtk_stylable_changed ((NbtkStylable*) widget);
+}
+
+static void
+nbtk_widget_init (NbtkWidget *actor)
+{
+ NbtkWidgetPrivate *priv;
+
+ actor->priv = priv = NBTK_WIDGET_GET_PRIVATE (actor);
+ priv->is_stylable = TRUE;
+
+ /* connect style changed */
+ g_signal_connect (actor, "notify::name", G_CALLBACK (nbtk_widget_name_notify), NULL);
+
+ /* set the default style */
+ nbtk_widget_set_style (NBTK_STYLABLE (actor), nbtk_style_get_default ());
+
+}
+
+static NbtkBorderImage *
+nbtk_border_image_copy (const NbtkBorderImage *border_image)
+{
+ NbtkBorderImage *copy;
+
+ g_return_val_if_fail (border_image != NULL, NULL);
+
+ copy = g_slice_new (NbtkBorderImage);
+ *copy = *border_image;
+
+ return copy;
+}
+
+static void
+nbtk_border_image_free (NbtkBorderImage *border_image)
+{
+ if (G_LIKELY (border_image))
+ g_slice_free (NbtkBorderImage, border_image);
+}
+
+GType
+nbtk_border_image_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (G_UNLIKELY (our_type == 0))
+ our_type =
+ g_boxed_type_register_static (I_("NbtkBorderImage"),
+ (GBoxedCopyFunc) nbtk_border_image_copy,
+ (GBoxedFreeFunc) nbtk_border_image_free);
+
+ return our_type;
+}
+
+/**
+ * nbtk_widget_ensure_style:
+ * @widget: A #NbtkWidget
+ *
+ * Ensures that @widget has read its style information.
+ *
+ */
+void
+nbtk_widget_ensure_style (NbtkWidget *widget)
+{
+ g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+ if (widget->priv->is_style_dirty)
+ {
+ g_signal_emit_by_name (widget, "style-changed", 0);
+ }
+}
+
+
+/**
+ * nbtk_widget_get_border_image:
+ * @actor: A #NbtkWidget
+ *
+ * Get the texture used as the border image. This is set using the
+ * "border-image" CSS property. This function should normally only be used
+ * by subclasses.
+ *
+ * Returns: #ClutterActor
+ */
+ClutterActor *
+nbtk_widget_get_border_image (NbtkWidget *actor)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+ return priv->border_image;
+}
+
+/**
+ * nbtk_widget_get_background_image:
+ * @actor: A #NbtkWidget
+ *
+ * Get the texture used as the background image. This is set using the
+ * "background-image" CSS property. This function should normally only be used
+ * by subclasses.
+ *
+ * Returns: a #ClutterActor
+ */
+ClutterActor *
+nbtk_widget_get_background_image (NbtkWidget *actor)
+{
+ NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+ return priv->background_image;
+}
+
+/**
+ * nbtk_widget_get_padding:
+ * @widget: A #NbtkWidget
+ * @padding: A pointer to an #NbtkPadding to fill
+ *
+ * Gets the padding of the widget, set using the "padding" CSS property. This
+ * function should normally only be used by subclasses.
+ *
+ */
+void
+nbtk_widget_get_padding (NbtkWidget *widget,
+ NbtkPadding *padding)
+{
+ g_return_if_fail (NBTK_IS_WIDGET (widget));
+ g_return_if_fail (padding != NULL);
+
+ *padding = widget->priv->padding;
+}
+
+/**
+ * nbtk_widget_set_has_tooltip:
+ * @widget: A #NbtkWidget
+ * @has_tooltip: #TRUE if the widget should display a tooltip
+ *
+ * Enables tooltip support on the #NbtkWidget.
+ *
+ * Note that setting has-tooltip to #TRUE will cause the widget to be set
+ * reactive. If you no longer need tooltip support and do not need the widget
+ * to be reactive, you need to set ClutterActor::reactive to FALSE.
+ *
+ */
+void
+nbtk_widget_set_has_tooltip (NbtkWidget *widget,
+ gboolean has_tooltip)
+{
+ NbtkWidgetPrivate *priv;
+
+ g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+ priv = widget->priv;
+
+ priv->has_tooltip = has_tooltip;
+
+ if (has_tooltip)
+ {
+ clutter_actor_set_reactive ((ClutterActor*) widget, TRUE);
+
+ if (!priv->tooltip)
+ {
+ priv->tooltip = g_object_new (NBTK_TYPE_TOOLTIP, NULL);
+ clutter_actor_set_parent ((ClutterActor *) priv->tooltip,
+ (ClutterActor *) widget);
+ }
+ }
+ else
+ {
+ if (priv->tooltip)
+ {
+ clutter_actor_unparent (CLUTTER_ACTOR (priv->tooltip));
+ priv->tooltip = NULL;
+ }
+ }
+}
+
+/**
+ * nbtk_widget_get_has_tooltip:
+ * @widget: A #NbtkWidget
+ *
+ * Returns the current value of the has-tooltip property. See
+ * nbtk_tooltip_set_has_tooltip() for more information.
+ *
+ * Returns: current value of has-tooltip on @widget
+ */
+gboolean
+nbtk_widget_get_has_tooltip (NbtkWidget *widget)
+{
+ g_return_val_if_fail (NBTK_IS_WIDGET (widget), FALSE);
+
+ return widget->priv->has_tooltip;
+}
+
+/**
+ * nbtk_widget_set_tooltip_text:
+ * @widget: A #NbtkWidget
+ * @text: text to set as the tooltip
+ *
+ * Set the tooltip text of the widget. This will set NbtkWidget::has-tooltip to
+ * #TRUE. A value of #NULL will unset the tooltip and set has-tooltip to #FALSE.
+ *
+ */
+void
+nbtk_widget_set_tooltip_text (NbtkWidget *widget,
+ const gchar *text)
+{
+ NbtkWidgetPrivate *priv;
+
+ g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+ priv = widget->priv;
+
+ if (text == NULL)
+ nbtk_widget_set_has_tooltip (widget, FALSE);
+ else
+ nbtk_widget_set_has_tooltip (widget, TRUE);
+
+ nbtk_tooltip_set_label (priv->tooltip, text);
+}
+
+/**
+ * nbtk_widget_get_tooltip_text:
+ * @widget: A #NbtkWidget
+ *
+ * Get the current tooltip string
+ *
+ * Returns: The current tooltip string, owned by the #NbtkWidget
+ */
+const gchar*
+nbtk_widget_get_tooltip_text (NbtkWidget *widget)
+{
+ g_return_val_if_fail (NBTK_IS_WIDGET (widget), NULL);
+
+ return nbtk_tooltip_get_label (widget->priv->tooltip);
+}
+
+/**
+ * nbtk_widget_show_tooltip:
+ * @widget: A #NbtkWidget
+ *
+ * Show the tooltip for @widget
+ *
+ */
+void
+nbtk_widget_show_tooltip (NbtkWidget *widget)
+{
+ gfloat x, y, width, height;
+ ClutterGeometry area;
+
+ g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+ /* XXX not necceary, but first allocate transform is wrong */
+
+ clutter_actor_get_transformed_position ((ClutterActor*) widget,
+ &x, &y);
+
+ clutter_actor_get_size ((ClutterActor*) widget, &width, &height);
+
+ area.x = x;
+ area.y = y;
+ area.width = width;
+ area.height = height;
+
+
+ if (widget->priv->tooltip)
+ {
+ nbtk_tooltip_set_tip_area (widget->priv->tooltip, &area);
+ nbtk_tooltip_show (widget->priv->tooltip);
+ }
+}
+
+/**
+ * nbtk_widget_hide_tooltip:
+ * @widget: A #NbtkWidget
+ *
+ * Hide the tooltip for @widget
+ *
+ */
+void
+nbtk_widget_hide_tooltip (NbtkWidget *widget)
+{
+ g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+ if (widget->priv->tooltip)
+ nbtk_tooltip_hide (widget->priv->tooltip);
+}
+
+/**
+ * nbtk_widget_draw_background:
+ * @widget: a #NbtkWidget
+ *
+ * Invokes #NbtkWidget::draw_background() using the default background
+ * image and/or color from the @widget style
+ *
+ * This function should be used by subclasses of #NbtkWidget that override
+ * the paint() virtual function and cannot chain up
+ */
+void
+nbtk_widget_draw_background (NbtkWidget *self)
+{
+ NbtkWidgetPrivate *priv;
+ NbtkWidgetClass *klass;
+
+ g_return_if_fail (NBTK_IS_WIDGET (self));
+
+ priv = self->priv;
+
+ klass = NBTK_WIDGET_GET_CLASS (self);
+ klass->draw_background (NBTK_WIDGET (self),
+ priv->border_image,
+ priv->bg_color);
+
+}
diff --git a/src/nbtk/nbtk-widget.h b/src/nbtk/nbtk-widget.h
new file mode 100644
index 0000000..1f5fe66
--- /dev/null
+++ b/src/nbtk/nbtk-widget.h
@@ -0,0 +1,107 @@
+/*
+ * nbtk-widget.h: Base class for Nbtk actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_WIDGET_H__
+#define __NBTK_WIDGET_H__
+
+#include <clutter/clutter.h>
+#include <nbtk/nbtk-types.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_WIDGET (nbtk_widget_get_type ())
+#define NBTK_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_WIDGET, NbtkWidget))
+#define NBTK_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_WIDGET))
+#define NBTK_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_WIDGET, NbtkWidgetClass))
+#define NBTK_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_WIDGET))
+#define NBTK_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_WIDGET, NbtkWidgetClass))
+
+typedef struct _NbtkWidget NbtkWidget;
+typedef struct _NbtkWidgetPrivate NbtkWidgetPrivate;
+typedef struct _NbtkWidgetClass NbtkWidgetClass;
+
+/**
+ * NbtkWidget:
+ *
+ * Base class for stylable actors. The contents of the #NbtkWidget
+ * structure are private and should only be accessed through the
+ * public API.
+ */
+struct _NbtkWidget
+{
+ /*< private >*/
+ ClutterActor parent_instance;
+
+ NbtkWidgetPrivate *priv;
+};
+
+/**
+ * NbtkWidgetClass:
+ *
+ * Base class for stylable actors.
+ */
+struct _NbtkWidgetClass
+{
+ /*< private >*/
+ ClutterActorClass parent_class;
+
+ /* vfuncs */
+ void (* draw_background) (NbtkWidget *self,
+ ClutterActor *background,
+ const ClutterColor *color);
+};
+
+GType nbtk_widget_get_type (void) G_GNUC_CONST;
+
+void nbtk_widget_set_style_pseudo_class (NbtkWidget *actor,
+ const gchar *pseudo_class);
+G_CONST_RETURN gchar *nbtk_widget_get_style_pseudo_class (NbtkWidget *actor);
+void nbtk_widget_set_style_class_name (NbtkWidget *actor,
+ const gchar *style_class);
+G_CONST_RETURN gchar *nbtk_widget_get_style_class_name (NbtkWidget *actor);
+
+
+void nbtk_widget_set_has_tooltip (NbtkWidget *widget, gboolean has_tooltip);
+gboolean nbtk_widget_get_has_tooltip (NbtkWidget *widget);
+void nbtk_widget_set_tooltip_text (NbtkWidget *widget, const gchar *text);
+const gchar* nbtk_widget_get_tooltip_text (NbtkWidget *widget);
+
+void nbtk_widget_show_tooltip (NbtkWidget *widget);
+void nbtk_widget_hide_tooltip (NbtkWidget *widget);
+
+void nbtk_widget_ensure_style (NbtkWidget *widget);
+
+
+/* Only to be used by sub-classes of NbtkWidget */
+ClutterActor *nbtk_widget_get_background_image (NbtkWidget *actor);
+ClutterActor *nbtk_widget_get_border_image (NbtkWidget *actor);
+void nbtk_widget_get_padding (NbtkWidget *widget,
+ NbtkPadding *padding);
+void nbtk_widget_draw_background (NbtkWidget *widget);
+
+G_END_DECLS
+
+#endif /* __NBTK_WIDGET_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]