gnome-shell r103 - in trunk: . src src/big
- From: walters svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-shell r103 - in trunk: . src src/big
- Date: Mon, 1 Dec 2008 23:01:52 +0000 (UTC)
Author: walters
Date: Mon Dec 1 23:01:52 2008
New Revision: 103
URL: http://svn.gnome.org/viewvc/gnome-shell?rev=103&view=rev
Log:
Import Big
We want this for the BigBox primarily; rounded corners, etc.
http://bugzilla.gnome.org/show_bug.cgi?id=562923
Added:
trunk/src/Makefile-big.am
trunk/src/big/
trunk/src/big/big-enum-types.c.in
trunk/src/big/big-enum-types.h.in
trunk/src/big/box.c
trunk/src/big/box.h
trunk/src/big/rectangle.c
trunk/src/big/rectangle.h
trunk/src/big/theme-image.c
trunk/src/big/theme-image.h
Modified:
trunk/configure.ac
trunk/src/Makefile-tidy.am
trunk/src/Makefile.am
Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Mon Dec 1 23:01:52 2008
@@ -18,6 +18,7 @@
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 metacity-plugins gjs-gi-1.0)
PKG_CHECK_MODULES(TIDY, clutter-0.8)
+PKG_CHECK_MODULES(BIG, clutter-cairo-0.8 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
Added: trunk/src/Makefile-big.am
==============================================================================
--- (empty file)
+++ trunk/src/Makefile-big.am Mon Dec 1 23:01:52 2008
@@ -0,0 +1,67 @@
+big_cflags = \
+ -I$(top_srcdir)/src \
+ -DPREFIX=\""$(prefix)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DG_DISABLE_DEPRECATED \
+ -DG_LOG_DOMAIN=\"Big\" \
+ $(BIG_CFLAGS) \
+ $(NULL)
+
+big_built_sources = \
+ big-enum-types.h \
+ big-enum-types.c \
+ $(NULL)
+
+BUILT_SOURCES += $(big_built_sources)
+
+BIG_STAMP_FILES = stamp-big-marshal.h stamp-big-enum-types.h
+
+# please, keep this sorted alphabetically
+big_source_h = \
+ big/box.h \
+ big/rectangle.h \
+ big/theme-image.h \
+ $(NULL)
+
+# please, keep this sorted alphabetically
+big_source_c = \
+ big/box.c \
+ big/rectangle.c \
+ big/theme-image.c \
+ $(NULL)
+
+big-enum-types.h: stamp-big-enum-types.h Makefile
+ @true
+stamp-big-enum-types.h: $(big_source_h) big/big-enum-types.h.in
+ ( cd $(srcdir) && \
+ $(GLIB_MKENUMS) \
+ --template $(srcdir)/big/big-enum-types.h.in \
+ $(big_source_h) ) >> xgen-teth && \
+ (cmp xgen-teth big-enum-types.h || cp xgen-teth big-enum-types.h) && \
+ rm -f xgen-teth && \
+ echo timestamp > $(@F)
+
+big-enum-types.c: stamp-big-enum-types.h big/big-enum-types.c.in
+ ( cd $(srcdir) && \
+ $(GLIB_MKENUMS) \
+ --template $(srcdir)/big/big-enum-types.c.in \
+ $(big_source_h) ) >> xgen-tetc && \
+ cp xgen-tetc big-enum-types.c && \
+ rm -f xgen-tetc
+
+noinst_LTLIBRARIES += libbig-1.0.la
+
+libbig_1_0_la_LIBADD = $(BIG_LIBS)
+libbig_1_0_la_SOURCES = \
+ $(big_source_c) \
+ $(big_source_h) \
+ $(big_built_sources) \
+ $(NULL)
+libbig_1_0_la_CPPFLAGS = $(big_cflags)
+libbig_1_0_la_LDFLAGS = $(LDADD)
+
+CLEANFILES += $(BIG_STAMP_FILES) $(BUILT_SOURCES)
+
+EXTRA_DIST += \
+ big/big-enum-types.h.in \
+ big/big-enum-types.c.in
Modified: trunk/src/Makefile-tidy.am
==============================================================================
--- trunk/src/Makefile-tidy.am (original)
+++ trunk/src/Makefile-tidy.am Mon Dec 1 23:01:52 2008
@@ -1,5 +1,3 @@
-NULL =
-
tidy_cflags = \
-I$(top_srcdir)/src \
-DPREFIX=\""$(prefix)"\" \
@@ -85,7 +83,7 @@
cp xgen-tetc tidy-enum-types.c && \
rm -f xgen-tetc
-noinst_LTLIBRARIES = libtidy-1.0.la
+noinst_LTLIBRARIES += libtidy-1.0.la
libtidy_1_0_la_LIBADD = $(TIDY_LIBS)
libtidy_1_0_la_SOURCES = \
@@ -99,7 +97,7 @@
CLEANFILES += $(TIDY_STAMP_FILES) $(BUILT_SOURCES)
-EXTRA_DIST = \
+EXTRA_DIST += \
tidy/tidy-enum-types.h.in \
tidy/tidy-enum-types.c.in \
tidy/tidy-private.h \
Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am (original)
+++ trunk/src/Makefile.am Mon Dec 1 23:01:52 2008
@@ -1,7 +1,11 @@
+NULL =
BUILT_SOURCES =
CLEANFILES =
+EXTRA_DIST =
+noinst_LTLIBRARIES =
include Makefile-tidy.am
+include Makefile-big.am
include Makefile-tray.am
gnome_shell_cflags = \
@@ -60,12 +64,13 @@
libgnome_shell_la_LDFLAGS = -avoid-version -module
libgnome_shell_la_LIBADD = \
$(MUTTER_PLUGIN_LIBS) \
+ libbig-1.0.la \
libtidy-1.0.la \
libtray.la
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
typelibdir = $(pkglibdir)
-typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib
+typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib Big-1.0.typelib
Shell-0.1.gir: $(metacity) $(G_IR_SCANNER) libgnome-shell.la Makefile
$(G_IR_SCANNER) \
@@ -103,3 +108,20 @@
Tidy-1.0.typelib: libtidy-1.0.la Tidy-1.0.gir
LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Tidy-1.0.gir -o $@
CLEANFILES += Tidy-1.0.typelib
+
+Big-1.0.gir: $(metacity) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
+ $(G_IR_SCANNER) \
+ --namespace=Big \
+ --nsversion=1.0 \
+ --include=ClutterCairo-0.8 \
+ --program=metacity \
+ --program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
+ $(big_source_h) \
+ $(big_source_c) \
+ $(big_cflags) \
+ -o $@
+CLEANFILES += Big-1.0.gir
+
+Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
+ LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Big-1.0.gir -o $@
+CLEANFILES += Big-1.0.typelib
Added: trunk/src/big/big-enum-types.c.in
==============================================================================
--- (empty file)
+++ trunk/src/big/big-enum-types.c.in Mon Dec 1 23:01:52 2008
@@ -0,0 +1,30 @@
+/*** BEGIN file-header ***/
+#include "big-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("@EnumName@", values);
+ }
+ return enum_type_id;
+}
+/*** END value-tail ***/
Added: trunk/src/big/big-enum-types.h.in
==============================================================================
--- (empty file)
+++ trunk/src/big/big-enum-types.h.in Mon Dec 1 23:01:52 2008
@@ -0,0 +1,25 @@
+/*** BEGIN file-header ***/
+#ifndef __BIG_ENUM_TYPES_H__
+#define __BIG_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 /* !__BIG_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define BIG_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+
+/*** END value-header ***/
Added: trunk/src/big/box.c
==============================================================================
--- (empty file)
+++ trunk/src/big/box.c Mon Dec 1 23:01:52 2008
@@ -0,0 +1,3050 @@
+/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* big-box.c: Box container.
+
+ Copyright (C) 2006-2008 Red Hat, Inc.
+ Copyright (C) 2008 litl, LLC.
+
+ The libbigwidgets-lgpl is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+ If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+ Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <glib.h>
+
+#include <clutter/clutter-units.h>
+#include <clutter/clutter-actor.h>
+#include <clutter/clutter-container.h>
+#include <cogl/cogl.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "big-enum-types.h"
+
+#include "box.h"
+#include "theme-image.h"
+#include "rectangle.h"
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (BigBox,
+ big_box,
+ CLUTTER_TYPE_ACTOR,
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init));
+
+#define BIG_BOX_GET_PRIVATE(obj) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((obj), BIG_TYPE_BOX, BigBoxPrivate))
+
+#define BOX_CHILD_IS_VISIBLE(c) \
+(CLUTTER_ACTOR_IS_VISIBLE (c->actor))
+
+#define BOX_CHILD_IN_LAYOUT(c) \
+(!c->fixed && (BOX_CHILD_IS_VISIBLE (c) || c->if_hidden))
+
+enum
+{
+ PROP_0,
+
+ PROP_ORIENTATION,
+ PROP_SPACING,
+ PROP_X_ALIGN,
+ PROP_Y_ALIGN,
+ PROP_PADDING,
+ PROP_PADDING_TOP,
+ PROP_PADDING_BOTTOM,
+ PROP_PADDING_LEFT,
+ PROP_PADDING_RIGHT,
+ PROP_BORDER,
+ PROP_BORDER_TOP,
+ PROP_BORDER_BOTTOM,
+ PROP_BORDER_LEFT,
+ PROP_BORDER_RIGHT,
+ PROP_CORNER_RADIUS,
+ PROP_BACKGROUND_BORDER_TOP,
+ PROP_BACKGROUND_BORDER_BOTTOM,
+ PROP_BACKGROUND_BORDER_LEFT,
+ PROP_BACKGROUND_BORDER_RIGHT,
+ PROP_BACKGROUND_COLOR,
+ PROP_BACKGROUND_FILENAME,
+ PROP_BACKGROUND_PIXBUF,
+ PROP_BACKGROUND_TEXTURE,
+ PROP_BACKGROUND_RECTANGLE,
+ PROP_BACKGROUND_REPEAT,
+ PROP_BACKGROUND_X_ALIGN,
+ PROP_BACKGROUND_Y_ALIGN,
+ PROP_BORDER_COLOR,
+ PROP_DEBUG
+};
+
+struct _BigBoxPrivate
+{
+ GList *children;
+ BigBoxOrientation orientation;
+ BigBoxAlignment x_align;
+ BigBoxAlignment y_align;
+ ClutterUnit spacing;
+ ClutterUnit padding_top;
+ ClutterUnit padding_bottom;
+ ClutterUnit padding_left;
+ ClutterUnit padding_right;
+ ClutterUnit border_top;
+ ClutterUnit border_bottom;
+ ClutterUnit border_left;
+ ClutterUnit border_right;
+ ClutterUnit corner_radius;
+ ClutterColor border_color;
+ guint background_border_top;
+ guint background_border_bottom;
+ guint background_border_left;
+ guint background_border_right;
+ ClutterColor background_color;
+ ClutterActor *background_texture;
+ BigBoxBackgroundRepeat background_repeat;
+ BigBoxAlignment background_x_align;
+ BigBoxAlignment background_y_align;
+ ClutterActor *background_rectangle;
+
+ guint draw_rounded_corner : 1;
+ guint debug : 1;
+};
+
+typedef struct
+{
+ ClutterActor *actor;
+
+ guint expand : 1;
+ guint end : 1;
+ guint if_fits : 1;
+ guint fixed : 1;
+ guint if_hidden : 1;
+
+ /* BigBoxAlignment applies if fixed=true */
+ guint fixed_x_align : 3;
+ guint fixed_y_align : 3;
+} BigBoxChild;
+
+typedef struct
+{
+ ClutterUnit minimum;
+ ClutterUnit natural;
+ ClutterUnit adjustment;
+ guint does_not_fit : 1;
+} BigBoxAdjustInfo;
+
+static gboolean
+box_child_set_flags (BigBoxChild *c,
+ BigBoxPackFlags flags)
+{
+ BigBoxPackFlags old;
+
+ old = 0;
+
+ if (c->end)
+ old |= BIG_BOX_PACK_END;
+ if (c->expand)
+ old |= BIG_BOX_PACK_EXPAND;
+ if (c->if_fits)
+ old |= BIG_BOX_PACK_IF_FITS;
+ if (c->fixed)
+ old |= BIG_BOX_PACK_FIXED;
+ if (c->if_hidden)
+ old |= BIG_BOX_PACK_ALLOCATE_WHEN_HIDDEN;
+
+ if (old == flags)
+ return FALSE; /* no change */
+
+ c->expand = (flags & BIG_BOX_PACK_EXPAND) != 0;
+ c->end = (flags & BIG_BOX_PACK_END) != 0;
+ c->if_fits = (flags & BIG_BOX_PACK_IF_FITS) != 0;
+ c->fixed = (flags & BIG_BOX_PACK_FIXED) != 0;
+ c->if_hidden = (flags & BIG_BOX_PACK_ALLOCATE_WHEN_HIDDEN) != 0;
+
+ return TRUE;
+}
+
+static gboolean
+box_child_set_align (BigBoxChild *c,
+ BigBoxAlignment fixed_x_align,
+ BigBoxAlignment fixed_y_align)
+{
+ if (fixed_x_align == c->fixed_x_align &&
+ fixed_y_align == c->fixed_y_align)
+ return FALSE;
+
+ c->fixed_x_align = fixed_x_align;
+ c->fixed_y_align = fixed_y_align;
+
+ return TRUE;
+}
+
+static BigBoxChild *
+box_child_find (BigBox *box,
+ ClutterActor *actor)
+{
+ GList *c;
+
+ for (c = box->priv->children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = c->data;
+
+ if (child->actor == actor)
+ return (BigBoxChild *) child;
+ }
+
+ return NULL;
+}
+
+static BigBoxChild *
+box_child_new_from_actor (ClutterActor *child,
+ BigBoxPackFlags flags)
+{
+ BigBoxChild *c;
+
+ c = g_new0 (BigBoxChild, 1);
+
+ g_object_ref (child);
+ c->actor = child;
+
+ box_child_set_flags (c, flags);
+
+ return c;
+}
+
+static void
+box_child_free (BigBoxChild *c)
+{
+ g_object_unref (c->actor);
+
+ g_free (c);
+}
+
+static void
+box_child_remove (BigBox *box, BigBoxChild *child)
+{
+ BigBoxPrivate *priv = box->priv;
+
+ priv->children = g_list_remove (priv->children, child);
+
+ clutter_actor_unparent (child->actor);
+
+ /* at this point, the actor passed to the "actor-removed" signal
+ * handlers is not parented anymore to the container but since we
+ * are holding a reference on it, it's still valid
+ */
+ g_signal_emit_by_name (box, "actor-removed", child->actor);
+
+ box_child_free (child);
+}
+
+static void
+big_box_real_add (ClutterContainer *container,
+ ClutterActor *child)
+{
+ big_box_append (BIG_BOX (container), child, BIG_BOX_PACK_FIXED);
+}
+
+static void
+big_box_real_remove (ClutterContainer *container,
+ ClutterActor *child)
+{
+ BigBox *box = BIG_BOX (container);
+ BigBoxChild *c;
+
+ g_object_ref (child);
+
+ c = box_child_find (box, child);
+
+ if (c != NULL)
+ {
+ box_child_remove (box, c);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+ }
+
+ g_object_unref (child);
+}
+
+static void
+big_box_real_foreach (ClutterContainer *container,
+ ClutterCallback callback,
+ gpointer user_data)
+{
+ BigBox *group = BIG_BOX (container);
+ BigBoxPrivate *priv = group->priv;
+ GList *l;
+
+ for (l = priv->children; l; l = l->next)
+ {
+ BigBoxChild *c = (BigBoxChild *) l->data;
+
+ (* callback) (c->actor, user_data);
+ }
+}
+
+static void
+big_box_real_raise (ClutterContainer *container,
+ ClutterActor *child,
+ ClutterActor *sibling)
+{
+ BigBox *box = BIG_BOX (container);
+ BigBoxPrivate *priv = box->priv;
+ BigBoxChild *c;
+
+ c = box_child_find (box, child);
+
+ /* Child not found */
+ if (c == NULL)
+ return;
+
+ /* We only do raise for children not in the layout */
+ if (!c->fixed)
+ return;
+
+ priv->children = g_list_remove (priv->children, c);
+
+ /* Raise at the top */
+ if (!sibling)
+ {
+ GList *last_item;
+
+ last_item = g_list_last (priv->children);
+
+ if (last_item)
+ {
+ BigBoxChild *sibling_child = last_item->data;
+
+ sibling = sibling_child->actor;
+ }
+
+ priv->children = g_list_append (priv->children, c);
+ }
+ else
+ {
+ BigBoxChild *sibling_child;
+ gint pos;
+
+ sibling_child = box_child_find (box, sibling);
+
+ pos = g_list_index (priv->children, sibling_child) + 1;
+
+ priv->children = g_list_insert (priv->children, c, pos);
+ }
+
+ if (sibling &&
+ clutter_actor_get_depth (sibling) != clutter_actor_get_depth (child))
+ {
+ clutter_actor_set_depth (child, clutter_actor_get_depth (sibling));
+ }
+}
+
+static void
+big_box_real_lower (ClutterContainer *container,
+ ClutterActor *child,
+ ClutterActor *sibling)
+{
+ BigBox *box = BIG_BOX (container);
+ BigBoxPrivate *priv = box->priv;
+ BigBoxChild *c;
+
+ c = box_child_find (box, child);
+
+ /* Child not found */
+ if (c == NULL)
+ return;
+
+ /* We only do lower for children not in the layout */
+ if (!c->fixed)
+ return;
+
+ priv->children = g_list_remove (priv->children, c);
+
+ /* Push to bottom */
+ if (!sibling)
+ {
+ GList *first_item;
+
+ first_item = g_list_first (priv->children);
+
+ if (first_item)
+ {
+ BigBoxChild *sibling_child = first_item->data;
+
+ sibling = sibling_child->actor;
+ }
+
+ priv->children = g_list_prepend (priv->children, c);
+ }
+ else
+ {
+ BigBoxChild *sibling_child;
+ gint pos;
+
+ sibling_child = box_child_find (box, sibling);
+
+ pos = g_list_index (priv->children, sibling_child);
+
+ priv->children = g_list_insert (priv->children, c, pos);
+ }
+
+ if (sibling &&
+ clutter_actor_get_depth (sibling) != clutter_actor_get_depth (child))
+ {
+ clutter_actor_set_depth (child, clutter_actor_get_depth (sibling));
+ }
+}
+
+static int
+sort_z_order (gconstpointer a,
+ gconstpointer b)
+{
+ const BigBoxChild *child_a = a;
+ const BigBoxChild *child_b = b;
+ int depth_a, depth_b;
+
+ /* Depth of non-fixed children is ignored in stacking/painting order and they
+ * are considered to be at depth=0. Though when the children are painting
+ * themselves the depth translation is applied.
+ */
+
+ depth_a = child_a->fixed ? clutter_actor_get_depth (child_a->actor) : 0;
+ depth_b = child_b->fixed ? clutter_actor_get_depth (child_b->actor) : 0;
+
+ return depth_a - depth_b;
+}
+
+static void
+big_box_real_sort_depth_order (ClutterContainer *container)
+{
+ BigBox *box = BIG_BOX (container);
+ BigBoxPrivate *priv = box->priv;
+
+ priv->children = g_list_sort (priv->children, sort_z_order);
+
+ if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (box)))
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ iface->add = big_box_real_add;
+ iface->remove = big_box_real_remove;
+ iface->foreach = big_box_real_foreach;
+ iface->raise = big_box_real_raise;
+ iface->lower = big_box_real_lower;
+ iface->sort_depth_order = big_box_real_sort_depth_order;
+}
+
+static void
+big_box_get_bg_texture_allocation (ClutterActor *self,
+ ClutterUnit allocated_width,
+ ClutterUnit allocated_height,
+ ClutterActorBox *bg_box)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit bg_width, bg_height;
+ ClutterUnit min_x1, min_y1, max_x2, max_y2;
+
+ g_return_if_fail (BIG_IS_BOX (self));
+
+ priv = BIG_BOX (self)->priv;
+
+ clutter_actor_get_preferred_width (priv->background_texture,
+ -1,
+ NULL,
+ &bg_width);
+ clutter_actor_get_preferred_height (priv->background_texture,
+ bg_width,
+ NULL,
+ &bg_height);
+
+ min_x1 = priv->border_left;
+ max_x2 = allocated_width - priv->border_right;
+
+ switch (priv->background_x_align)
+ {
+ case BIG_BOX_ALIGNMENT_FIXED:
+ g_warning("Must specify a real alignment for background, not FIXED");
+ break;
+ case BIG_BOX_ALIGNMENT_FILL:
+ bg_box->x1 = min_x1;
+ bg_box->x2 = max_x2;
+ break;
+
+ case BIG_BOX_ALIGNMENT_START:
+ bg_box->x1 = min_x1;
+ bg_box->x2 = MIN (bg_box->x1 + bg_width, max_x2);
+ break;
+
+ case BIG_BOX_ALIGNMENT_END:
+ bg_box->x1 = MAX (min_x1, max_x2 - bg_width);
+ bg_box->x2 = max_x2;
+ break;
+
+ case BIG_BOX_ALIGNMENT_CENTER:
+ bg_box->x1 = MAX (min_x1, priv->border_left + (allocated_width - bg_width) / 2);
+ bg_box->x2 = MIN (bg_box->x1 + bg_width, max_x2);
+ break;
+ }
+
+ min_y1 = priv->border_top;
+ max_y2 = allocated_height - priv->border_bottom;
+
+ switch (priv->background_y_align)
+ {
+ case BIG_BOX_ALIGNMENT_FIXED:
+ g_warning("Must specify a real alignment for background, not FIXED");
+ break;
+ case BIG_BOX_ALIGNMENT_FILL:
+ bg_box->y1 = min_y1;
+ bg_box->y2 = max_y2;
+ break;
+
+ case BIG_BOX_ALIGNMENT_START:
+ bg_box->y1 = min_y1;
+ bg_box->y2 = MIN (bg_box->y1 + bg_height, max_y2);
+ break;
+
+ case BIG_BOX_ALIGNMENT_END:
+ bg_box->y1 = MAX (min_y1, max_y2 - bg_height);
+ bg_box->y2 = max_y2;
+ break;
+
+ case BIG_BOX_ALIGNMENT_CENTER:
+ bg_box->y1 = MAX (min_y1, priv->border_top + (allocated_height - bg_height) / 2);
+ bg_box->y2 = MIN (bg_box->y1 + bg_height, max_y2);
+ break;
+ }
+}
+
+static void
+big_box_update_background_border (BigBox *box)
+{
+ BigBoxPrivate *priv;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+
+ priv = box->priv;
+
+ if (priv->background_texture)
+ {
+ guint border_top = 0, border_bottom = 0, border_left = 0, border_right = 0;
+
+ if ((priv->background_x_align == BIG_BOX_ALIGNMENT_FILL) &&
+ /* No repeat on the horizontal axis */
+ ((priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_NONE) ||
+ (priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_Y)))
+ {
+ border_left = priv->background_border_left;
+ border_right = priv->background_border_right;
+ }
+
+ if ((priv->background_y_align == BIG_BOX_ALIGNMENT_FILL) &&
+ /* No repeat on the vertical axis */
+ ((priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_NONE) ||
+ (priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_X)))
+ {
+ border_top = priv->background_border_top;
+ border_bottom = priv->background_border_bottom;
+ }
+
+ g_object_set (priv->background_texture,
+ "border-left", border_left,
+ "border-right", border_right,
+ "border-top", border_top,
+ "border-bottom", border_bottom,
+ NULL);
+ }
+}
+
+/* Update whether or not we draw rounded corners.
+ * If different sizes are set for different border segments,
+ * we ignore the radius.
+ */
+static void
+big_box_update_draw_rounded_corner (BigBox *box)
+{
+ BigBoxPrivate *priv;
+
+ priv = box->priv;
+
+ priv->draw_rounded_corner = (priv->border_top == priv->border_left) &&
+ (priv->border_top == priv->border_right) &&
+ (priv->border_top == priv->border_bottom) &&
+ (priv->corner_radius != 0);
+
+ if (!priv->draw_rounded_corner &&
+ priv->background_rectangle)
+ {
+ clutter_actor_unparent(priv->background_rectangle);
+ priv->background_rectangle = NULL;
+ }
+
+ if (priv->draw_rounded_corner &&
+ !priv->background_rectangle)
+ {
+ priv->background_rectangle = g_object_new (BIG_TYPE_RECTANGLE, NULL);
+ clutter_actor_set_parent (priv->background_rectangle, CLUTTER_ACTOR (box));
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+ }
+
+ if (priv->draw_rounded_corner)
+ {
+ g_object_set (priv->background_rectangle,
+ "color", &priv->background_color,
+ "border-color", &priv->border_color,
+ "border-width", CLUTTER_UNITS_TO_DEVICE (priv->border_top),
+ "corner-radius", CLUTTER_UNITS_TO_DEVICE (priv->corner_radius),
+ NULL);
+ }
+}
+
+static void
+big_box_set_background_repeat (BigBox *box,
+ BigBoxBackgroundRepeat background_repeat)
+{
+ BigBoxPrivate *priv;
+
+ priv = box->priv;
+
+ priv->background_repeat = background_repeat;
+
+ if (priv->background_texture)
+ {
+ gboolean repeat_x = FALSE, repeat_y = FALSE;
+
+ switch (priv->background_repeat)
+ {
+ case BIG_BOX_BACKGROUND_REPEAT_NONE:
+ break;
+
+ case BIG_BOX_BACKGROUND_REPEAT_X:
+ repeat_x = TRUE;
+ break;
+
+ case BIG_BOX_BACKGROUND_REPEAT_Y:
+ repeat_y = TRUE;
+ break;
+
+ case BIG_BOX_BACKGROUND_REPEAT_BOTH:
+ repeat_x = TRUE;
+ repeat_y = TRUE;
+ break;
+ }
+
+ g_object_set (G_OBJECT (priv->background_texture),
+ "repeat-x", repeat_x,
+ "repeat-y", repeat_y,
+ NULL);
+ }
+}
+
+static void
+big_box_set_background_pixbuf (BigBox *box, GdkPixbuf *pixbuf)
+{
+ BigBoxPrivate *priv;
+ ClutterActor *background_texture;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+
+ priv = box->priv;
+
+ if (priv->background_texture)
+ {
+ clutter_actor_unparent (priv->background_texture);
+ priv->background_texture = NULL;
+ }
+
+ /* This means we just want to remove current background texture */
+ if (pixbuf == NULL)
+ return;
+
+ /* The border values are set depending on the background-repeat
+ * and background alignment settings in
+ * big_box_update_background_border */
+ background_texture = big_theme_image_new_from_pixbuf (pixbuf,
+ 0, 0, 0, 0);
+
+ if (background_texture)
+ {
+ clutter_actor_set_parent (background_texture, CLUTTER_ACTOR (box));
+ priv->background_texture = background_texture;
+ big_box_set_background_repeat (box, priv->background_repeat);
+ }
+}
+
+static void
+big_box_set_background_filename (BigBox *box, const char *filename)
+{
+ BigBoxPrivate *priv;
+ ClutterActor *background_texture;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (filename != NULL);
+
+ priv = box->priv;
+
+ if (priv->background_texture)
+ {
+ clutter_actor_unparent (priv->background_texture);
+ priv->background_texture = NULL;
+ }
+
+ /* This means we just want to remove current background texture */
+ if (filename == NULL)
+ return;
+
+ /* The border values are set depending on the background-repeat
+ * and background alignment settings in
+ * big_box_update_background_border */
+ background_texture = big_theme_image_new_from_file (filename,
+ 0, 0, 0, 0);
+
+ if (background_texture)
+ {
+ clutter_actor_set_parent (background_texture, CLUTTER_ACTOR (box));
+ priv->background_texture = background_texture;
+ big_box_set_background_repeat (box, priv->background_repeat);
+ }
+}
+
+static void
+big_box_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BigBoxPrivate *priv = BIG_BOX (gobject)->priv;
+ gboolean need_repaint = TRUE;
+ gboolean need_resize = TRUE;
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ priv->orientation = g_value_get_enum (value);
+ break;
+
+ case PROP_SPACING:
+ priv->spacing = CLUTTER_UNITS_FROM_DEVICE (g_value_get_int (value));
+ break;
+
+ case PROP_X_ALIGN:
+ priv->x_align = g_value_get_enum (value);
+ break;
+
+ case PROP_Y_ALIGN:
+ priv->y_align = g_value_get_enum (value);
+ break;
+
+ case PROP_PADDING:
+ big_box_set_padding(BIG_BOX(gobject),
+ g_value_get_int(value));
+ break;
+
+ case PROP_PADDING_TOP:
+ priv->padding_top = CLUTTER_UNITS_FROM_DEVICE (g_value_get_int (value));
+ break;
+
+ case PROP_PADDING_BOTTOM:
+ priv->padding_bottom = CLUTTER_UNITS_FROM_DEVICE (g_value_get_int (value));
+ break;
+
+ case PROP_PADDING_LEFT:
+ priv->padding_left = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+ break;
+
+ case PROP_PADDING_RIGHT:
+ priv->padding_right = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+ break;
+
+ case PROP_BORDER:
+ big_box_set_border_width(BIG_BOX(gobject),
+ g_value_get_int(value));
+ break;
+
+ case PROP_BORDER_TOP:
+ priv->border_top = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+ big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+ break;
+
+ case PROP_BORDER_BOTTOM:
+ priv->border_bottom = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+ big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+ break;
+
+ case PROP_BORDER_LEFT:
+ priv->border_left = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+ big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+ break;
+
+ case PROP_BORDER_RIGHT:
+ priv->border_right = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+ big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+ break;
+
+ case PROP_CORNER_RADIUS:
+ priv->corner_radius = CLUTTER_UNITS_FROM_DEVICE(g_value_get_uint (value));
+ big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_BORDER_TOP:
+ priv->background_border_top = g_value_get_uint (value);
+ need_resize = FALSE;
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_BORDER_BOTTOM:
+ priv->background_border_bottom = g_value_get_uint (value);
+ need_resize = FALSE;
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_BORDER_LEFT:
+ priv->background_border_left = g_value_get_uint (value);
+ need_resize = FALSE;
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_BORDER_RIGHT:
+ priv->background_border_right = g_value_get_uint (value);
+ need_resize = FALSE;
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_COLOR:
+ {
+ ClutterColor *color;
+ color = g_value_get_boxed (value);
+ if (color) {
+ priv->background_color = *color;
+ } else {
+ /* null = default (black and transparent) */
+ priv->background_color.red = 0;
+ priv->background_color.green = 0;
+ priv->background_color.blue = 0;
+ priv->background_color.alpha = 0;
+ }
+
+ if (priv->background_rectangle)
+ {
+ g_object_set (priv->background_rectangle,
+ "color", &priv->background_color,
+ NULL);
+ }
+
+ need_resize = FALSE;
+ }
+ break;
+
+ case PROP_BACKGROUND_FILENAME:
+ big_box_set_background_filename (BIG_BOX (gobject),
+ g_value_get_string (value));
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_PIXBUF:
+ big_box_set_background_pixbuf (BIG_BOX (gobject),
+ g_value_get_object (value));
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_REPEAT:
+ big_box_set_background_repeat (BIG_BOX (gobject),
+ g_value_get_enum (value));
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_X_ALIGN:
+ priv->background_x_align = g_value_get_enum (value);
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BACKGROUND_Y_ALIGN:
+ priv->background_y_align = g_value_get_enum (value);
+ big_box_update_background_border (BIG_BOX (gobject));
+ break;
+
+ case PROP_BORDER_COLOR:
+ {
+ ClutterColor *color;
+ color = g_value_get_boxed (value);
+ if (color) {
+ priv->border_color = *color;
+ } else {
+ /* null = default (black and transparent) */
+ priv->border_color.red = 0;
+ priv->border_color.green = 0;
+ priv->border_color.blue = 0;
+ priv->border_color.alpha = 0;
+ }
+
+ if (priv->background_rectangle)
+ {
+ g_object_set (priv->background_rectangle,
+ "border-color", &priv->border_color,
+ NULL);
+ }
+
+ need_resize = FALSE;
+ }
+ break;
+
+ case PROP_DEBUG:
+ priv->debug = g_value_get_boolean (value);
+
+ need_repaint = FALSE;
+ need_resize = FALSE;
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+
+ if (need_resize)
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (gobject));
+ else if (need_repaint)
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (gobject));
+}
+
+static void
+big_box_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BigBoxPrivate *priv = BIG_BOX (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, priv->orientation);
+ break;
+
+ case PROP_SPACING:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->spacing));
+ break;
+
+ case PROP_X_ALIGN:
+ g_value_set_enum (value, priv->x_align);
+ break;
+
+ case PROP_Y_ALIGN:
+ g_value_set_enum (value, priv->y_align);
+ break;
+
+ case PROP_PADDING_TOP:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_top));
+ break;
+
+ case PROP_PADDING_BOTTOM:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_bottom));
+ break;
+
+ case PROP_PADDING_LEFT:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_left));
+ break;
+
+ case PROP_PADDING_RIGHT:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_right));
+ break;
+
+ case PROP_BORDER_TOP:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->border_top));
+ break;
+
+ case PROP_BORDER_BOTTOM:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->border_bottom));
+ break;
+
+ case PROP_BORDER_LEFT:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->border_left));
+ break;
+
+ case PROP_BORDER_RIGHT:
+ g_value_set_int (value,CLUTTER_UNITS_TO_DEVICE(priv->border_right));
+ break;
+
+ case PROP_CORNER_RADIUS:
+ g_value_set_uint (value,CLUTTER_UNITS_TO_DEVICE (priv->corner_radius));
+ break;
+
+ case PROP_BACKGROUND_TEXTURE:
+ g_value_set_object (value, priv->background_texture);
+ break;
+
+ case PROP_BACKGROUND_RECTANGLE:
+ g_value_set_object (value, priv->background_rectangle);
+ break;
+
+ case PROP_BACKGROUND_BORDER_TOP:
+ g_value_set_uint (value, priv->background_border_top);
+ break;
+
+ case PROP_BACKGROUND_BORDER_BOTTOM:
+ g_value_set_uint (value, priv->background_border_bottom);
+ break;
+
+ case PROP_BACKGROUND_BORDER_LEFT:
+ g_value_set_uint (value, priv->background_border_left);
+ break;
+
+ case PROP_BACKGROUND_BORDER_RIGHT:
+ g_value_set_uint (value, priv->background_border_right);
+ break;
+
+ case PROP_BACKGROUND_COLOR:
+ g_value_set_boxed (value, &priv->background_color);
+ break;
+
+ case PROP_BACKGROUND_REPEAT:
+ g_value_set_enum (value, priv->background_repeat);
+ break;
+
+ case PROP_BACKGROUND_X_ALIGN:
+ g_value_set_enum (value, priv->background_x_align);
+ break;
+
+ case PROP_BACKGROUND_Y_ALIGN:
+ g_value_set_enum (value, priv->background_y_align);
+ break;
+
+ case PROP_BORDER_COLOR:
+ g_value_set_boxed (value, &priv->border_color);
+ break;
+
+ case PROP_DEBUG:
+ g_value_set_boolean (value, priv->debug);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+big_box_finalize (GObject *gobject)
+{
+ G_OBJECT_CLASS (big_box_parent_class)->finalize (gobject);
+}
+
+static void
+big_box_dispose (GObject *gobject)
+{
+ BigBox *self = BIG_BOX (gobject);
+ BigBoxPrivate *priv = self->priv;
+
+ if (priv->background_texture)
+ {
+ clutter_actor_unparent (priv->background_texture);
+ priv->background_texture = NULL;
+ }
+
+ if (priv->background_rectangle)
+ {
+ clutter_actor_unparent (priv->background_rectangle);
+ priv->background_rectangle = NULL;
+ }
+
+ while (priv->children)
+ {
+ clutter_actor_destroy(((BigBoxChild*)priv->children->data)->actor);
+ }
+
+ G_OBJECT_CLASS (big_box_parent_class)->dispose (gobject);
+}
+
+static void
+big_box_get_content_width_request (ClutterActor *self,
+ ClutterUnit *min_width_p,
+ ClutterUnit *natural_width_p)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit total_min;
+ ClutterUnit total_natural;
+ gint n_children_in_min;
+ gint n_children_in_natural;
+ GList *c;
+
+ priv = BIG_BOX (self)->priv;
+
+ total_min = 0;
+ total_natural = 0;
+ n_children_in_min = 0;
+ n_children_in_natural = 0;
+
+ for (c = priv->children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+ ClutterUnit min_width;
+ ClutterUnit natural_width;
+
+ if (!BOX_CHILD_IN_LAYOUT (child))
+ continue;
+
+ /* PACK_IF_FITS children are do not contribute to the min size
+ * of the whole box, but do contribute to natural size, and
+ * will be hidden entirely if their width request does not
+ * fit.
+ */
+ clutter_actor_get_preferred_width (child->actor,
+ -1,
+ &min_width,
+ &natural_width);
+
+ if (priv->debug)
+ g_debug ("Child %p min width %d natural %d",
+ child->actor,
+ CLUTTER_UNITS_TO_DEVICE (min_width),
+ CLUTTER_UNITS_TO_DEVICE (natural_width));
+
+ n_children_in_natural += 1;
+
+ /* children with if fits flag won't appear at our min width if
+ * we are horizontal. If we're vertical, always request enough
+ * width for all if_fits children. Children with 0 min size won't
+ * themselves appear but they will get spacing around them, so
+ * they count in n_children_in_min.
+ */
+ if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+ {
+ total_min = MAX (total_min, min_width);
+ n_children_in_min += 1;
+
+ total_natural = MAX (total_natural, natural_width);
+ }
+ else
+ {
+ if (!child->if_fits)
+ {
+ total_min += min_width;
+ n_children_in_min += 1;
+ }
+
+ total_natural += natural_width;
+ }
+ }
+
+ if (priv->orientation == BIG_BOX_ORIENTATION_HORIZONTAL &&
+ n_children_in_min > 1)
+ {
+ total_min += priv->spacing * (n_children_in_min - 1);
+ }
+
+ if (priv->orientation == BIG_BOX_ORIENTATION_HORIZONTAL &&
+ n_children_in_natural > 1)
+ {
+ total_natural += priv->spacing * (n_children_in_natural - 1);
+ }
+
+ if (min_width_p)
+ *min_width_p = total_min;
+ if (natural_width_p)
+ *natural_width_p = total_natural;
+}
+
+static void
+big_box_get_preferred_width (ClutterActor *self,
+ ClutterUnit for_height,
+ ClutterUnit *min_width_p,
+ ClutterUnit *natural_width_p)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit content_min_width;
+ ClutterUnit content_natural_width;
+ ClutterUnit outside;
+
+ priv = BIG_BOX (self)->priv;
+
+ big_box_get_content_width_request (self,
+ &content_min_width,
+ &content_natural_width);
+
+ outside = priv->padding_left + priv->padding_right +
+ priv->border_left + priv->border_right;
+
+ if (min_width_p)
+ *min_width_p = content_min_width + outside;
+ if (natural_width_p)
+ *natural_width_p = content_natural_width + outside;
+
+ if (priv->debug)
+ {
+ if (min_width_p)
+ g_debug ("Computed minimum width as %d", CLUTTER_UNITS_TO_DEVICE (*min_width_p));
+ if (natural_width_p)
+ g_debug ("Computed natural width as %d", CLUTTER_UNITS_TO_DEVICE (*natural_width_p));
+ }
+}
+
+static void
+big_box_get_content_area_horizontal (ClutterActor *self,
+ ClutterUnit requested_content_width,
+ ClutterUnit natural_content_width,
+ ClutterUnit allocated_box_width,
+ ClutterUnit *x_p,
+ ClutterUnit *width_p)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit left;
+ ClutterUnit right;
+ ClutterUnit unpadded_box_width;
+ ClutterUnit content_width;
+
+ priv = BIG_BOX (self)->priv;
+
+ left = priv->border_left + priv->padding_left;
+ right = priv->border_right + priv->padding_right;
+
+ g_return_if_fail (requested_content_width >= 0);
+
+ if (natural_content_width < allocated_box_width)
+ content_width = natural_content_width;
+ else
+ content_width = MAX (requested_content_width, allocated_box_width);
+
+ unpadded_box_width = allocated_box_width - left - right;
+
+ switch (priv->x_align)
+ {
+ case BIG_BOX_ALIGNMENT_FIXED:
+ g_warning("Must specify a real alignment for content, not FIXED");
+ break;
+ case BIG_BOX_ALIGNMENT_FILL:
+ if (x_p)
+ *x_p = left;
+ if (width_p)
+ *width_p = unpadded_box_width;
+ break;
+ case BIG_BOX_ALIGNMENT_START:
+ if (x_p)
+ *x_p = left;
+ if (width_p)
+ *width_p = content_width;
+ break;
+ case BIG_BOX_ALIGNMENT_END:
+ if (x_p)
+ *x_p = allocated_box_width - right - content_width;
+ if (width_p)
+ *width_p = content_width;
+ break;
+ case BIG_BOX_ALIGNMENT_CENTER:
+ if (x_p)
+ *x_p = left + (unpadded_box_width - content_width) / 2;
+ if (width_p)
+ *width_p = content_width;
+ break;
+ }
+}
+
+static void
+big_box_get_content_area_vertical (ClutterActor *self,
+ ClutterUnit requested_content_height,
+ ClutterUnit natural_content_height,
+ ClutterUnit allocated_box_height,
+ ClutterUnit *y_p,
+ ClutterUnit *height_p)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit top;
+ ClutterUnit bottom;
+ ClutterUnit unpadded_box_height;
+ ClutterUnit content_height;
+
+ priv = BIG_BOX (self)->priv;
+
+ top = priv->border_top + priv->padding_top;
+ bottom = priv->border_bottom + priv->padding_bottom;
+
+ g_return_if_fail (requested_content_height >= 0);
+
+ if (natural_content_height < allocated_box_height)
+ content_height = natural_content_height;
+ else
+ content_height = MAX (requested_content_height, allocated_box_height);
+
+ unpadded_box_height = allocated_box_height - top - bottom;
+
+ switch (priv->y_align)
+ {
+ case BIG_BOX_ALIGNMENT_FIXED:
+ g_warning("Must specify a real alignment for content, not FIXED");
+ break;
+ case BIG_BOX_ALIGNMENT_FILL:
+ if (y_p)
+ *y_p = top;
+ if (height_p)
+ *height_p = unpadded_box_height;
+ break;
+ case BIG_BOX_ALIGNMENT_START:
+ if (y_p)
+ *y_p = top;
+ if (height_p)
+ *height_p = content_height;
+ break;
+ case BIG_BOX_ALIGNMENT_END:
+ if (y_p)
+ *y_p = allocated_box_height - bottom - content_height;
+ if (height_p)
+ *height_p = content_height;
+ break;
+ case BIG_BOX_ALIGNMENT_CENTER:
+ if (y_p)
+ *y_p = top + (unpadded_box_height - content_height) / 2;
+ if (height_p)
+ *height_p = content_height;
+ break;
+ }
+}
+static BigBoxAdjustInfo *
+big_box_adjust_infos_new (BigBox *box,
+ ClutterUnit for_content_width)
+{
+ BigBoxPrivate *priv = box->priv;
+ BigBoxAdjustInfo *adjusts = g_new0 (BigBoxAdjustInfo, g_list_length (priv->children));
+ GList *c;
+ gint i = 0;
+
+ for (c = priv->children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ if (!BOX_CHILD_IN_LAYOUT (child))
+ {
+ adjusts[i].minimum = adjusts[i].natural = 0;
+ }
+ else if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+ {
+ clutter_actor_get_preferred_height (child->actor, for_content_width,
+ &adjusts[i].minimum, &adjusts[i].natural);
+ }
+ else
+ {
+ clutter_actor_get_preferred_width (child->actor, -1, &adjusts[i].minimum,
+ &adjusts[i].natural);
+ }
+
+ i++;
+ }
+
+ return adjusts;
+}
+
+static void
+big_box_adjust_if_fits_as_not_fitting (GList *children,
+ BigBoxAdjustInfo *adjusts)
+{
+ GList *c;
+ gint i;
+
+ i = 0;
+
+ for (c = children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ if (child->if_fits)
+ {
+ adjusts[i].adjustment -= adjusts[i].minimum;
+ adjusts[i].does_not_fit = TRUE;
+ }
+
+ ++i;
+ }
+}
+
+static gboolean
+big_box_adjust_up_to_natural_size (GList *children,
+ ClutterUnit *remaining_extra_space_p,
+ BigBoxAdjustInfo *adjusts,
+ gboolean if_fits)
+{
+ ClutterUnit smallest_increase;
+ ClutterUnit space_to_distribute;
+ GList *c;
+ gint n_needing_increase;
+ gint i;
+
+ g_assert (*remaining_extra_space_p >= 0);
+
+ if (*remaining_extra_space_p == 0)
+ return FALSE;
+
+ smallest_increase = CLUTTER_MAXUNIT;
+ n_needing_increase = 0;
+
+ i = 0;
+
+ for (c = children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ if (BOX_CHILD_IN_LAYOUT (child) &&
+ ((!child->if_fits && !if_fits) ||
+ (child->if_fits && if_fits && !adjusts[i].does_not_fit)))
+ {
+ ClutterUnit needed_increase;
+
+ g_assert (adjusts[i].adjustment >= 0);
+
+ /* Guaranteed to be >= 0 */
+ needed_increase = adjusts[i].natural - adjusts[i].minimum;
+
+ g_assert (needed_increase >= 0);
+
+ needed_increase -= adjusts[i].adjustment; /* see how much we've already increased */
+
+ if (needed_increase > 0)
+ {
+ n_needing_increase += 1;
+ smallest_increase = MIN (smallest_increase, needed_increase);
+ }
+ }
+
+ ++i;
+ }
+
+ if (n_needing_increase == 0)
+ return FALSE;
+
+ g_assert (smallest_increase < G_MAXINT);
+
+ space_to_distribute = MIN (*remaining_extra_space_p,
+ smallest_increase * n_needing_increase);
+
+ g_assert(space_to_distribute >= 0);
+ g_assert(space_to_distribute <= *remaining_extra_space_p);
+
+ *remaining_extra_space_p -= space_to_distribute;
+
+ i = 0;
+
+ for (c = children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ if (BOX_CHILD_IN_LAYOUT (child) &&
+ ((!child->if_fits && !if_fits) ||
+ (child->if_fits && if_fits && !adjusts[i].does_not_fit)))
+ {
+ ClutterUnit needed_increase;
+
+ g_assert (adjusts[i].adjustment >= 0);
+
+ /* Guaranteed to be >= 0 */
+ needed_increase = adjusts[i].natural - adjusts[i].minimum;
+
+ g_assert(needed_increase >= 0);
+
+ needed_increase -= adjusts[i].adjustment; /* see how much we've already increased */
+
+ if (needed_increase > 0)
+ {
+ ClutterUnit extra;
+
+ extra = (space_to_distribute / n_needing_increase);
+
+ n_needing_increase -= 1;
+ space_to_distribute -= extra;
+ adjusts[i].adjustment += extra;
+ }
+ }
+
+ ++i;
+ }
+
+ g_assert (n_needing_increase == 0);
+ g_assert (space_to_distribute == 0);
+
+ return TRUE;
+}
+
+static gboolean
+big_box_adjust_one_if_fits (GList *children,
+ ClutterUnit spacing,
+ ClutterUnit *remaining_extra_space_p,
+ BigBoxAdjustInfo *adjusts)
+{
+ GList *c;
+ ClutterUnit spacing_delta;
+ gint i;
+ gboolean visible_children = FALSE;
+
+ if (*remaining_extra_space_p == 0)
+ return FALSE;
+
+ /* if there are no currently visible children, then adding a child won't
+ * add another spacing
+ */
+ i = 0;
+
+ for (c = children; c != NULL; c = c->next) {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ if (BOX_CHILD_IN_LAYOUT (child) &&
+ (!child->if_fits || !adjusts[i].does_not_fit))
+ {
+ visible_children = TRUE;
+ break;
+ }
+
+ i++;
+ }
+
+ spacing_delta = visible_children ? spacing : 0;
+
+ i = 0;
+
+ for (c = children; c != NULL; c = c->next)
+ {
+ if (adjusts[i].does_not_fit)
+ {
+ /* This child was adjusted downward, see if we can pop it visible
+ * (picking the smallest instead of first if-fits child on each pass
+ * might be nice, but for now it's the first that fits)
+ */
+ if ((adjusts[i].minimum + spacing_delta) <= *remaining_extra_space_p)
+ {
+ adjusts[i].adjustment += adjusts[i].minimum;
+
+ g_assert (adjusts[i].adjustment >= 0);
+
+ adjusts[i].does_not_fit = FALSE;
+ *remaining_extra_space_p -= (adjusts[i].minimum + spacing_delta);
+
+ g_assert (*remaining_extra_space_p >= 0);
+
+ return TRUE;
+ }
+ }
+
+ ++i;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+box_child_is_expandable (BigBoxChild *child, BigBoxAdjustInfo *adjust)
+{
+ return (BOX_CHILD_IS_VISIBLE (child) || child->if_hidden) && child->expand &&
+ (!child->if_fits || (adjust && !(adjust->does_not_fit)));
+}
+
+static int
+big_box_count_expandable_children (GList *children,
+ BigBoxAdjustInfo *adjusts)
+{
+ GList *c;
+ gint count;
+ gint i;
+
+ count = 0;
+ i = 0;
+
+ for (c = children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ /* We assume here that we've prevented via g_warning
+ * any floats/fixed from having expand=TRUE
+ */
+ if (box_child_is_expandable (child, adjusts ? &(adjusts[i]) : NULL))
+ ++count;
+
+ ++i;
+ }
+
+ return count;
+}
+
+static void
+big_box_adjust_for_expandable (GList *children,
+ ClutterUnit *remaining_extra_space_p,
+ BigBoxAdjustInfo *adjusts)
+{
+ GList *c;
+ ClutterUnit expand_space;
+ gint expand_count;
+ gint i;
+
+ if (*remaining_extra_space_p == 0)
+ return;
+
+ expand_space = *remaining_extra_space_p;
+ expand_count = big_box_count_expandable_children (children, adjusts);
+
+ if (expand_count == 0)
+ return;
+
+ i = 0;
+
+ for (c = children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ if (box_child_is_expandable (child, &(adjusts[i])) &&
+ !adjusts[i].does_not_fit)
+ {
+ ClutterUnit extra;
+
+ extra = (expand_space / expand_count);
+
+ expand_count -= 1;
+ expand_space -= extra;
+ adjusts[i].adjustment += extra;
+ }
+
+ ++i;
+ }
+
+ /* if we had anything to expand, then we will have used up all space */
+ g_assert (expand_space == 0);
+ g_assert (expand_count == 0);
+
+ *remaining_extra_space_p = 0;
+}
+static void
+big_box_compute_adjusts (GList *children,
+ BigBoxAdjustInfo *adjusts,
+ ClutterUnit spacing,
+ ClutterUnit alloc_request_delta)
+{
+ ClutterUnit remaining_extra_space;
+
+ if (children == NULL)
+ return;
+
+ /* Go ahead and cram all PACK_IF_FITS children to zero width,
+ * we'll expand them again if we can.
+ */
+ big_box_adjust_if_fits_as_not_fitting (children, adjusts);
+
+ /* Make no adjustments if we got too little or just right space.
+ * (FIXME handle too little space better)
+ */
+ if (alloc_request_delta <= 0) {
+ return;
+ }
+
+ remaining_extra_space = alloc_request_delta;
+
+ /* Adjust non-PACK_IF_FITS up to natural size */
+ while (big_box_adjust_up_to_natural_size (children,
+ &remaining_extra_space, adjusts,
+ FALSE))
+ ;
+
+ /* See if any PACK_IF_FITS can get their minimum size */
+ while (big_box_adjust_one_if_fits (children,
+ spacing, &remaining_extra_space, adjusts))
+ ;
+
+ /* If so, then see if they can also get a natural size */
+ while (big_box_adjust_up_to_natural_size(children,
+ &remaining_extra_space, adjusts,
+ TRUE))
+ ;
+
+ /* And finally we can expand to fill empty space */
+ big_box_adjust_for_expandable (children, &remaining_extra_space, adjusts);
+
+ /* remaining_extra_space need not be 0, if we had no expandable children */
+}
+
+static ClutterUnit
+big_box_get_adjusted_size (BigBoxAdjustInfo *adjust)
+{
+ return adjust->minimum + adjust->adjustment;
+}
+
+static void
+big_box_get_hbox_height_request (ClutterActor *self,
+ ClutterUnit for_width,
+ ClutterUnit *min_height_p,
+ ClutterUnit *natural_height_p)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit total_min;
+ ClutterUnit total_natural;
+ ClutterUnit requested_content_width;
+ ClutterUnit natural_content_width;
+ ClutterUnit allocated_content_width;
+ BigBoxAdjustInfo *width_adjusts;
+ GList *c;
+ gint i;
+
+ priv = BIG_BOX (self)->priv;
+
+ total_min = 0;
+ total_natural = 0;
+
+ big_box_get_content_width_request (self, &requested_content_width,
+ &natural_content_width);
+
+ big_box_get_content_area_horizontal (self,
+ requested_content_width,
+ natural_content_width,
+ for_width, NULL,
+ &allocated_content_width);
+
+ width_adjusts = big_box_adjust_infos_new (BIG_BOX (self), for_width);
+
+ big_box_compute_adjusts (priv->children,
+ width_adjusts,
+ priv->spacing,
+ allocated_content_width - requested_content_width);
+
+ i = 0;
+
+ for (c = priv->children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = c->data;
+ ClutterUnit min_height, natural_height;
+ ClutterUnit req = 0;
+
+ if (!BOX_CHILD_IN_LAYOUT (child))
+ {
+ ++i;
+ continue;
+ }
+
+ req = big_box_get_adjusted_size (&width_adjusts[i]);
+
+ clutter_actor_get_preferred_height (child->actor, req,
+ &min_height, &natural_height);
+
+ if (priv->debug)
+ g_debug ("H - Child %p min height %d natural %d",
+ child->actor,
+ CLUTTER_UNITS_TO_DEVICE (min_height),
+ CLUTTER_UNITS_TO_DEVICE (natural_height));
+
+ total_min = MAX (total_min, min_height);
+ total_natural = MAX (total_natural, natural_height);
+
+ ++i;
+ }
+
+ g_free (width_adjusts);
+
+ if (min_height_p)
+ *min_height_p = total_min;
+ if (natural_height_p)
+ *natural_height_p = total_natural;
+}
+
+static void
+big_box_get_vbox_height_request (ClutterActor *self,
+ ClutterUnit for_width,
+ ClutterUnit *min_height_p,
+ ClutterUnit *natural_height_p)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit total_min;
+ ClutterUnit total_natural;
+ gint n_children_in_min;
+ gint n_children_in_natural;
+ GList *c;
+
+ priv = BIG_BOX (self)->priv;
+
+ total_min = 0;
+ total_natural = 0;
+ n_children_in_min = 0;
+ n_children_in_natural = 0;
+
+ for (c = priv->children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+ ClutterUnit min_height;
+ ClutterUnit natural_height;
+
+ if (!BOX_CHILD_IN_LAYOUT (child))
+ continue;
+
+ clutter_actor_get_preferred_height (child->actor, for_width,
+ &min_height, &natural_height);
+
+ if (priv->debug)
+ g_debug ("V - Child %p min height %d natural %d",
+ child->actor,
+ CLUTTER_UNITS_TO_DEVICE (min_height),
+ CLUTTER_UNITS_TO_DEVICE (natural_height));
+
+ n_children_in_natural += 1;
+ total_natural += natural_height;
+
+ if (!child->if_fits)
+ {
+ n_children_in_min += 1;
+ total_min += min_height;
+ }
+ }
+
+ if (n_children_in_min > 1)
+ total_min += priv->spacing * (n_children_in_min - 1);
+ if (n_children_in_natural > 1)
+ total_natural += priv->spacing * (n_children_in_natural - 1);
+
+ if (min_height_p)
+ *min_height_p = total_min;
+ if (natural_height_p)
+ *natural_height_p = total_natural;
+}
+
+static void
+big_box_get_content_height_request (ClutterActor *self,
+ ClutterUnit for_width,
+ ClutterUnit *min_height_p,
+ ClutterUnit *natural_height_p)
+{
+ BigBoxPrivate *priv;
+
+ priv = BIG_BOX (self)->priv;
+
+ if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+ big_box_get_vbox_height_request (self, for_width,
+ min_height_p, natural_height_p);
+ else
+ big_box_get_hbox_height_request (self, for_width,
+ min_height_p, natural_height_p);
+}
+
+static void
+big_box_get_preferred_height (ClutterActor *self,
+ ClutterUnit for_width,
+ ClutterUnit *min_height_p,
+ ClutterUnit *natural_height_p)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit content_min_height, content_natural_height;
+ ClutterUnit content_for_width;
+ ClutterUnit outside;
+
+ priv = BIG_BOX (self)->priv;
+
+ content_for_width = for_width
+ - priv->padding_left - priv->padding_right
+ - priv->border_left - priv->border_right;
+
+ /* We need to call this even if just returning the box-height prop,
+ * so that children can rely on getting the full request, allocate
+ * cycle in order every time, and so we compute the cached requests.
+ */
+ big_box_get_content_height_request (self,
+ content_for_width,
+ &content_min_height,
+ &content_natural_height);
+
+ outside = priv->padding_top + priv->padding_bottom +
+ priv->border_top + priv->border_bottom;
+
+ if (min_height_p)
+ *min_height_p = content_min_height + outside;
+ if (natural_height_p)
+ *natural_height_p = content_natural_height + outside;
+
+
+ if (priv->debug)
+ {
+ if (min_height_p)
+ g_debug ("Computed minimum height for width=%d as %d",
+ CLUTTER_UNITS_TO_DEVICE (for_width), CLUTTER_UNITS_TO_DEVICE (*min_height_p));
+ if (natural_height_p)
+ g_debug ("Computed natural height for width=%d as %d",
+ CLUTTER_UNITS_TO_DEVICE (for_width), CLUTTER_UNITS_TO_DEVICE (*natural_height_p));
+ }
+}
+
+static void
+big_box_layout (ClutterActor *self,
+ ClutterUnit content_x,
+ ClutterUnit content_y,
+ ClutterUnit allocated_content_width,
+ ClutterUnit allocated_content_height,
+ ClutterUnit requested_content_width,
+ ClutterUnit requested_content_height,
+ gboolean absolute_origin_changed)
+{
+ BigBoxPrivate *priv;
+ BigBoxAdjustInfo *adjusts;
+ ClutterActorBox child_box;
+ ClutterUnit allocated_size, requested_size;
+ ClutterUnit start;
+ ClutterUnit end;
+ GList *c;
+ gint i;
+
+ priv = BIG_BOX (self)->priv;
+
+ if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+ {
+ allocated_size = allocated_content_height;
+ requested_size = requested_content_height;
+ start = content_y;
+ }
+ else
+ {
+ allocated_size = allocated_content_width;
+ requested_size = requested_content_width;
+ start = content_x;
+ }
+
+ end = start + allocated_size;
+
+ adjusts = big_box_adjust_infos_new (BIG_BOX (self), allocated_content_width);
+
+ big_box_compute_adjusts (priv->children,
+ adjusts,
+ priv->spacing,
+ allocated_size - requested_size);
+
+ i = 0;
+
+ for (c = priv->children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+ ClutterUnit req;
+
+ if (!BOX_CHILD_IN_LAYOUT (child))
+ {
+ ++i;
+ continue;
+ }
+
+ if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+ {
+ req = big_box_get_adjusted_size (&adjusts[i]);
+
+ child_box.x1 = content_x;
+ child_box.y1 = child->end ? end - req : start;
+ child_box.x2 = child_box.x1 + allocated_content_width;
+ child_box.y2 = child_box.y1 + req;
+
+ if (priv->debug)
+ g_debug ("V - Child %p %s being allocated: %d, %d, %d, %d w: %d h: %d",
+ child->actor,
+ g_type_name_from_instance((GTypeInstance*) child->actor),
+ CLUTTER_UNITS_TO_DEVICE (child_box.x1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.x2),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y2),
+ CLUTTER_UNITS_TO_DEVICE (child_box.x2 - child_box.x1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y2 - child_box.y1));
+
+ clutter_actor_allocate (child->actor, &child_box,
+ absolute_origin_changed);
+ }
+ else
+ {
+ req = big_box_get_adjusted_size (&adjusts[i]);
+
+ child_box.x1 = child->end ? end - req : start;
+ child_box.y1 = content_y;
+ child_box.x2 = child_box.x1 + req;
+ child_box.y2 = child_box.y1 + allocated_content_height;
+
+ if (priv->debug)
+ g_debug ("H - Child %p %s being allocated: %d, %d, %d, %d w: %d h: %d",
+ child->actor,
+ g_type_name_from_instance((GTypeInstance*) child->actor),
+ CLUTTER_UNITS_TO_DEVICE (child_box.x1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.x2),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y2),
+ CLUTTER_UNITS_TO_DEVICE (child_box.x2 - child_box.x1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y2 - child_box.y1));
+
+ clutter_actor_allocate (child->actor, &child_box,
+ absolute_origin_changed);
+ }
+
+ if (req <= 0)
+ {
+ /* Child was adjusted out of existence, act like it's
+ * !visible
+ */
+ child_box.x1 = 0;
+ child_box.y1 = 0;
+ child_box.x2 = 0;
+ child_box.y2 = 0;
+
+ clutter_actor_allocate (child->actor, &child_box,
+ absolute_origin_changed);
+ }
+
+ /* Children with req == 0 still get spacing unless they are IF_FITS.
+ * The handling of spacing could use improvement (spaces should probably
+ * act like items with min width 0 and natural width of spacing) but
+ * it's pretty hard to get right without rearranging the code a lot.
+ */
+ if (!adjusts[i].does_not_fit)
+ {
+ if (child->end)
+ end -= (req + priv->spacing);
+ else
+ start += (req + priv->spacing);
+ }
+
+ ++i;
+ }
+
+ g_free (adjusts);
+}
+
+static void
+big_box_allocate (ClutterActor *self,
+ const ClutterActorBox *box,
+ gboolean absolute_origin_changed)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit requested_content_width;
+ ClutterUnit requested_content_height;
+ ClutterUnit natural_content_width;
+ ClutterUnit natural_content_height;
+ ClutterUnit allocated_content_width;
+ ClutterUnit allocated_content_height = 0;
+ ClutterUnit content_x, content_y = 0;
+ GList *c;
+
+ priv = BIG_BOX (self)->priv;
+
+ if (priv->debug)
+ g_debug ("Entire box %p being allocated: %d, %d, %d, %d",
+ self,
+ CLUTTER_UNITS_TO_DEVICE (box->x1),
+ CLUTTER_UNITS_TO_DEVICE (box->y1),
+ CLUTTER_UNITS_TO_DEVICE (box->x2),
+ CLUTTER_UNITS_TO_DEVICE (box->y2));
+
+ CLUTTER_ACTOR_CLASS (big_box_parent_class)->allocate (self, box, absolute_origin_changed);
+
+ big_box_get_content_width_request (self,
+ &requested_content_width,
+ &natural_content_width);
+
+ big_box_get_content_area_horizontal (self, requested_content_width,
+ natural_content_width, box->x2 - box->x1,
+ &content_x, &allocated_content_width);
+
+ big_box_get_content_height_request (self,
+ allocated_content_width,
+ &requested_content_height,
+ &natural_content_height);
+
+ big_box_get_content_area_vertical (self, requested_content_height,
+ natural_content_height, box->y2 - box->y1,
+ &content_y, &allocated_content_height);
+
+ if (priv->debug)
+ {
+ if (allocated_content_height < requested_content_height)
+ g_debug ("Box %p allocated height %d but requested %d",
+ self,
+ CLUTTER_UNITS_TO_DEVICE (allocated_content_height),
+ CLUTTER_UNITS_TO_DEVICE (requested_content_height));
+ if (allocated_content_width < requested_content_width)
+ g_debug ("Box %p allocated width %d but requested %d",
+ self,
+ CLUTTER_UNITS_TO_DEVICE (allocated_content_width),
+ CLUTTER_UNITS_TO_DEVICE (requested_content_width));
+ }
+
+ if (priv->background_texture)
+ {
+ ClutterActorBox bg_box;
+
+ big_box_get_bg_texture_allocation (self,
+ box->x2 - box->x1,
+ box->y2 - box->y1,
+ &bg_box);
+
+ if (priv->debug)
+ {
+ g_debug ("Box %p texture allocated width %d and height %d",
+ self,
+ CLUTTER_UNITS_TO_DEVICE (bg_box.x2 - bg_box.x1),
+ CLUTTER_UNITS_TO_DEVICE (bg_box.y2 - bg_box.y1));
+ }
+
+ clutter_actor_allocate (priv->background_texture, &bg_box,
+ absolute_origin_changed);
+ }
+
+ if (priv->background_rectangle)
+ {
+ ClutterActorBox rectangle_box;
+
+ rectangle_box.x1 = 0;
+ rectangle_box.y1 = 0;
+ rectangle_box.x2 = box->x2 - box->x1;
+ rectangle_box.y2 = box->y2 - box->y1;
+
+ clutter_actor_allocate (priv->background_rectangle,
+ &rectangle_box,
+ absolute_origin_changed);
+ }
+
+ for (c = priv->children; c != NULL; c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+ ClutterActorBox child_box;
+
+ if (!(BOX_CHILD_IS_VISIBLE (child) || child->if_hidden))
+ {
+ child_box.x1 = 0;
+ child_box.y1 = 0;
+ child_box.x2 = 0;
+ child_box.y2 = 0;
+
+ clutter_actor_allocate (child->actor, &child_box, FALSE);
+ }
+ else if (child->fixed)
+ {
+ ClutterUnit x, y, width, height;
+
+ clutter_actor_get_positionu (child->actor, &x, &y);
+ clutter_actor_get_preferred_width(child->actor, -1, NULL, &width);
+ clutter_actor_get_preferred_height(child->actor, width, NULL, &height);
+
+ switch (child->fixed_x_align)
+ {
+ case BIG_BOX_ALIGNMENT_FIXED:
+ /* honor child forced x,y instead of aligning automatically */
+ child_box.x1 = x;
+ child_box.x2 = x + width;
+ break;
+ case BIG_BOX_ALIGNMENT_START:
+ child_box.x1 = content_x;
+ child_box.x2 = child_box.x1 + width;
+ break;
+ case BIG_BOX_ALIGNMENT_END:
+ child_box.x2 = content_x + allocated_content_width;
+ child_box.x1 = child_box.x2 - width;
+ break;
+ case BIG_BOX_ALIGNMENT_CENTER:
+ child_box.x1 = content_x + (allocated_content_width - width) / 2;
+ child_box.x2 = child_box.x1 + width;
+ break;
+ case BIG_BOX_ALIGNMENT_FILL:
+ child_box.x1 = content_x;
+ child_box.x2 = content_x + allocated_content_width;
+ break;
+ }
+
+ switch (child->fixed_y_align)
+ {
+ case BIG_BOX_ALIGNMENT_FIXED:
+ /* honor child forced x,y instead of aligning automatically */
+ child_box.y1 = y;
+ child_box.y2 = y + height;
+ break;
+ case BIG_BOX_ALIGNMENT_START:
+ child_box.y1 = content_y;
+ child_box.y2 = child_box.y1 + height;
+ break;
+ case BIG_BOX_ALIGNMENT_END:
+ child_box.y2 = content_y + allocated_content_height;
+ child_box.y1 = child_box.y2 - height;
+ break;
+ case BIG_BOX_ALIGNMENT_CENTER:
+ child_box.y1 = content_y + (allocated_content_height - height) / 2;
+ child_box.y2 = child_box.y1 + height;
+ break;
+ case BIG_BOX_ALIGNMENT_FILL:
+ child_box.y1 = content_y;
+ child_box.y2 = content_y + allocated_content_height;
+ break;
+ }
+
+ if (priv->debug)
+ g_debug ("Fixed Child being allocated: %d, %d, %d, %d",
+ CLUTTER_UNITS_TO_DEVICE (child_box.x1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y1),
+ CLUTTER_UNITS_TO_DEVICE (child_box.x2),
+ CLUTTER_UNITS_TO_DEVICE (child_box.y2));
+
+ clutter_actor_allocate(child->actor, &child_box,
+ absolute_origin_changed);
+ }
+ }
+
+ big_box_layout (self, content_x, content_y,
+ allocated_content_width, allocated_content_height,
+ requested_content_width, requested_content_height,
+ absolute_origin_changed);
+}
+
+static void
+big_box_paint (ClutterActor *actor)
+{
+ BigBox *self = BIG_BOX (actor);
+ GList *c;
+ ClutterColor color;
+ guint8 actor_opacity;
+ ClutterGeometry allocation;
+ ClutterGeometry bg_rect;
+ int border_top, border_bottom, border_left, border_right;
+ int padding_top, padding_bottom, padding_left, padding_right;
+
+ /* First, convert everything to pixels */
+ border_top = CLUTTER_UNITS_TO_DEVICE (self->priv->border_top);
+ border_bottom = CLUTTER_UNITS_TO_DEVICE (self->priv->border_bottom);
+ border_left = CLUTTER_UNITS_TO_DEVICE (self->priv->border_left);
+ border_right = CLUTTER_UNITS_TO_DEVICE (self->priv->border_right);
+ padding_top = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_top);
+ padding_bottom = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_bottom);
+ padding_left = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_left);
+ padding_right = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_right);
+
+ actor_opacity = clutter_actor_get_paint_opacity (actor);
+
+ /* Remember allocation.x and .y don't matter since we are
+ * theoretically already translated to 0,0
+ */
+ clutter_actor_get_allocation_geometry (actor, &allocation);
+
+ cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+
+ cogl_push_matrix ();
+
+ /* Background */
+
+ color = self->priv->background_color;
+ color.alpha = (color.alpha * actor_opacity) / 0xff;
+
+ bg_rect.x = border_left;
+ bg_rect.y = border_top;
+ bg_rect.width = allocation.width - border_left - border_right;
+ bg_rect.height = allocation.height - border_top - border_bottom;
+
+ /* Border */
+
+ if (self->priv->draw_rounded_corner)
+ {
+ /* we delegate drawing of the background to BigRectangle.
+ * Ideally we would always do that but currently BigRectangle
+ * does not support border segments with different size
+ */
+ clutter_actor_paint (self->priv->background_rectangle);
+ }
+ else
+ {
+ if (color.alpha != 0)
+ {
+ cogl_color (&color);
+ cogl_rectangle (bg_rect.x, bg_rect.y, bg_rect.width, bg_rect.height);
+ }
+
+ color = self->priv->border_color;
+ color.alpha = (color.alpha * actor_opacity) / 0xff;
+
+ if (color.alpha != 0)
+ {
+ cogl_color (&color);
+
+ /* top */
+ cogl_rectangle (0, 0,
+ allocation.width,
+ border_top);
+
+ /* left */
+ cogl_rectangle (0, border_top,
+ border_left,
+ allocation.height - border_top - border_bottom);
+
+ /* right */
+ cogl_rectangle (allocation.width - border_right,
+ border_top,
+ border_right,
+ allocation.height - border_top - border_bottom);
+
+ /* bottom */
+ cogl_rectangle (0, allocation.height - border_bottom,
+ allocation.width,
+ border_bottom);
+ }
+ }
+
+ cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+
+ if (self->priv->background_texture &&
+ CLUTTER_ACTOR_IS_VISIBLE(self->priv->background_texture)) {
+ clutter_actor_paint (CLUTTER_ACTOR (self->priv->background_texture));
+ cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+ }
+
+ /* Children */
+
+ for (c = self->priv->children;
+ c != NULL;
+ c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ g_assert (child != NULL);
+
+ if (BOX_CHILD_IS_VISIBLE (child)) {
+ clutter_actor_paint (child->actor);
+ cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+ }
+ }
+
+ cogl_pop_matrix();
+}
+
+static void
+big_box_pick (ClutterActor *actor,
+ const ClutterColor *color)
+{
+ BigBox *self = BIG_BOX (actor);
+ GList *c;
+
+ CLUTTER_ACTOR_CLASS (big_box_parent_class)->pick (actor, color);
+
+ for (c = self->priv->children;
+ c != NULL;
+ c = c->next)
+ {
+ BigBoxChild *child = (BigBoxChild *) c->data;
+
+ g_assert (child != NULL);
+
+ if (BOX_CHILD_IS_VISIBLE (child))
+ clutter_actor_paint (child->actor);
+ }
+}
+
+static void
+big_box_class_init (BigBoxClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (BigBoxPrivate));
+
+ gobject_class->set_property = big_box_set_property;
+ gobject_class->get_property = big_box_get_property;
+ gobject_class->dispose = big_box_dispose;
+ gobject_class->finalize = big_box_finalize;
+
+ actor_class->get_preferred_width = big_box_get_preferred_width;
+ actor_class->get_preferred_height = big_box_get_preferred_height;
+ actor_class->allocate = big_box_allocate;
+ actor_class->paint = big_box_paint;
+ actor_class->pick = big_box_pick;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ORIENTATION,
+ g_param_spec_enum ("orientation",
+ "Orientation",
+ "Orientation of the box",
+ BIG_TYPE_BOX_ORIENTATION,
+ BIG_BOX_ORIENTATION_VERTICAL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SPACING,
+ g_param_spec_int ("spacing",
+ "Spacing",
+ "Spacing between items of the box",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X_ALIGN,
+ g_param_spec_enum ("x-align",
+ "X alignment",
+ "X alignment",
+ BIG_TYPE_BOX_ALIGNMENT,
+ BIG_BOX_ALIGNMENT_START,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y_ALIGN,
+ g_param_spec_enum ("y-align",
+ "Y alignment",
+ "Y alignment",
+ BIG_TYPE_BOX_ALIGNMENT,
+ BIG_BOX_ALIGNMENT_START,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PADDING,
+ g_param_spec_int ("padding",
+ "Padding",
+ "Padding (set all paddings at once)",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PADDING_TOP,
+ g_param_spec_int ("padding-top",
+ "Padding on the top",
+ "Padding on the top",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PADDING_BOTTOM,
+ g_param_spec_int ("padding-bottom",
+ "Padding on the bottom",
+ "Padding on the bottom",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PADDING_LEFT,
+ g_param_spec_int ("padding-left",
+ "Padding on the left",
+ "Padding on the left",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PADDING_RIGHT,
+ g_param_spec_int ("padding-right",
+ "Padding on the right",
+ "Padding on the right",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER,
+ g_param_spec_int ("border",
+ "Border",
+ "Border (set all borders at once)",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_TOP,
+ g_param_spec_int ("border-top",
+ "Border on the top",
+ "Border on the top",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_BOTTOM,
+ g_param_spec_int ("border-bottom",
+ "Border on the bottom",
+ "Border on the bottom",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_LEFT,
+ g_param_spec_int ("border-left",
+ "Border on the left",
+ "Border on the left",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_RIGHT,
+ g_param_spec_int ("border-right",
+ "Border on the right",
+ "Border on the right",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CORNER_RADIUS,
+ g_param_spec_uint ("corner-radius",
+ "Corner radius",
+ "Radius of the rounded corner "
+ "(ignored with border segments of "
+ "different sizes)",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ /**
+ * BigBox:background-border-top:
+ *
+ * Specifies a border on the top of the image which should not be
+ * stretched when the image is set to fill the box vertically
+ * (background-y-align set to FILL, background-repeat set to
+ * not repeat vertically. Useful for images with rounded corners.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_BORDER_TOP,
+ g_param_spec_uint ("background-border-top",
+ "Background border on the top",
+ "Background border on the top",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ /**
+ * BigBox:background-border-bottom:
+ *
+ * Specifies a border on the bottom of the image which should not be
+ * stretched when the image is set to fill the box vertically
+ * (background-y-align set to FILL, background-repeat set to
+ * not repeat vertically. Useful for images with rounded corners.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_BORDER_BOTTOM,
+ g_param_spec_uint ("background-border-bottom",
+ "Background border on the bottom",
+ "Background border on the bottom",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ /**
+ * BigBox:background-border-left:
+ *
+ * Specifies a border on the left of the image which should not be
+ * stretched when the image is set to fill the box horizontally
+ * (background-x-align set to FILL, background-repeat set to
+ * not repeat horizontally. Useful for images with rounded corners.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_BORDER_LEFT,
+ g_param_spec_uint ("background-border-left",
+ "Background border on the left",
+ "Background border on the left",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ /**
+ * BigBox:background-border-right:
+ *
+ * Specifies a border on the right of the image which should not be
+ * stretched when the image is set to fill the box horizontally
+ * (background-x-align set to FILL, background-repeat set to
+ * not repeat horizontally. Useful for images with rounded corners.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_BORDER_RIGHT,
+ g_param_spec_uint ("background-border-right",
+ "Background border on the right",
+ "Background border on the right",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ /**
+ * BigBox:background-color:
+ *
+ * Background color, covers padding but not border.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_COLOR,
+ g_param_spec_boxed ("background-color",
+ "Background Color",
+ "The color of the background",
+ CLUTTER_TYPE_COLOR,
+ G_PARAM_READWRITE));
+ /**
+ * BigBox:background-filename:
+ *
+ * Background filename, covers padding but not border.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_FILENAME,
+ g_param_spec_string ("background-filename",
+ "Background Filename",
+ "The image filename of the background",
+ NULL,
+ G_PARAM_WRITABLE));
+ /**
+ * BigBox:background-pixbuf:
+ *
+ * Background pixbuf, covers padding but not border.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_PIXBUF,
+ g_param_spec_object ("background-pixbuf",
+ "Background Pixbuf",
+ "The image pixbuf of the background",
+ GDK_TYPE_PIXBUF,
+ G_PARAM_WRITABLE));
+ /**
+ * BigBox:background-texture:
+ *
+ * Background texture, covers padding but not border.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_TEXTURE,
+ g_param_spec_object ("background-texture",
+ "Background Texture",
+ "The texture of the background",
+ CLUTTER_TYPE_TEXTURE,
+ G_PARAM_READABLE));
+
+ /**
+ * BigBox:background-rectangle:
+ *
+ * Background rectangle, covers the complete box allocation.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_RECTANGLE,
+ g_param_spec_object ("background-rectangle",
+ "Background Rectangle",
+ "The rectangle forming the box border and "
+ "background color",
+ BIG_TYPE_RECTANGLE,
+ G_PARAM_READABLE));
+ /**
+ * BigBox:background-repeat:
+ *
+ * Sets if/how a background image will be repeated.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_REPEAT,
+ g_param_spec_enum ("background-repeat",
+ "Background Repeat",
+ "Background Repeat",
+ BIG_TYPE_BOX_BACKGROUND_REPEAT,
+ BIG_BOX_BACKGROUND_REPEAT_NONE,
+ G_PARAM_READWRITE));
+ /**
+ * BigBox:background-x-align:
+ *
+ * Sets the horizontal alignment of the background texture.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_X_ALIGN,
+ g_param_spec_enum ("background-x-align",
+ "Background X alignment",
+ "Background X alignment",
+ BIG_TYPE_BOX_ALIGNMENT,
+ BIG_BOX_ALIGNMENT_FILL,
+ G_PARAM_READWRITE));
+ /**
+ * BigBox:background-y-align:
+ *
+ * Sets the vertical alignment of the background texture.
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BACKGROUND_Y_ALIGN,
+ g_param_spec_enum ("background-y-align",
+ "Background Y alignment",
+ "Background Y alignment",
+ BIG_TYPE_BOX_ALIGNMENT,
+ BIG_BOX_ALIGNMENT_FILL,
+ G_PARAM_READWRITE));
+ /**
+ * BigBox:border-color:
+ *
+ * Border color, color of border if any. Make the border transparent
+ * to use the border purely for spacing with no visible border.
+ *
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_COLOR,
+ g_param_spec_boxed ("border-color",
+ "Border Color",
+ "The color of the border of the rectangle",
+ CLUTTER_TYPE_COLOR,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_DEBUG,
+ g_param_spec_boolean ("debug",
+ "Debug",
+ "Whether debug is activated or not",
+ FALSE,
+ G_PARAM_READWRITE));
+}
+
+static void
+big_box_init (BigBox *box)
+{
+ box->priv = BIG_BOX_GET_PRIVATE (box);
+
+ box->priv->orientation = BIG_BOX_ORIENTATION_VERTICAL;
+ box->priv->x_align = BIG_BOX_ALIGNMENT_FILL;
+ box->priv->y_align = BIG_BOX_ALIGNMENT_FILL;
+ box->priv->spacing = 0;
+ box->priv->padding_top = 0;
+ box->priv->padding_bottom = 0;
+ box->priv->padding_left = 0;
+ box->priv->padding_right = 0;
+ box->priv->border_top = 0;
+ box->priv->border_bottom = 0;
+ box->priv->border_left = 0;
+ box->priv->border_right = 0;
+
+ box->priv->background_texture = NULL;
+ box->priv->background_repeat = BIG_BOX_BACKGROUND_REPEAT_NONE;
+ box->priv->background_x_align = BIG_BOX_ALIGNMENT_FILL;
+ box->priv->background_y_align = BIG_BOX_ALIGNMENT_FILL;
+
+ box->priv->background_rectangle = NULL;
+ box->priv->draw_rounded_corner = FALSE;
+
+ /* both bg and border colors default to black and transparent (all bits 0) */
+}
+
+ClutterActor *
+big_box_new (BigBoxOrientation orientation)
+{
+ return g_object_new (BIG_TYPE_BOX,
+ "orientation", orientation,
+ NULL);
+}
+
+void
+big_box_prepend (BigBox *box,
+ ClutterActor *child,
+ BigBoxPackFlags flags)
+{
+ BigBoxPrivate *priv;
+ BigBoxChild *c;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+ priv = box->priv;
+
+ g_object_ref (child);
+
+ c = box_child_new_from_actor (child, flags);
+
+ priv->children = g_list_prepend (priv->children, c);
+
+ clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+ g_signal_emit_by_name (box, "actor-added", child);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+ g_object_unref (child);
+}
+
+void
+big_box_append (BigBox *box,
+ ClutterActor *child,
+ BigBoxPackFlags flags)
+{
+ BigBoxPrivate *priv;
+ BigBoxChild *c;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+ priv = box->priv;
+
+ g_object_ref (child);
+
+ c = box_child_new_from_actor (child, flags);
+
+ priv->children = g_list_append (priv->children, c);
+
+ clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+ g_signal_emit_by_name (box, "actor-added", child);
+
+ big_box_real_sort_depth_order (CLUTTER_CONTAINER (box));
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+ g_object_unref (child);
+}
+
+gboolean
+big_box_is_empty (BigBox *box)
+{
+ g_return_val_if_fail (BIG_IS_BOX (box), TRUE);
+
+ return (box->priv->children == NULL);
+}
+
+void
+big_box_remove_all (BigBox *box)
+{
+ BigBoxPrivate *priv;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+
+ priv = box->priv;
+
+ while (priv->children != NULL) {
+ BigBoxChild *child = priv->children->data;
+
+ box_child_remove (box, child);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+}
+
+void
+big_box_insert_after (BigBox *box,
+ ClutterActor *child,
+ ClutterActor *ref_child,
+ BigBoxPackFlags flags)
+{
+ BigBoxPrivate *priv;
+ BigBoxChild *c, *ref_c;
+ gint position;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (child));
+ g_return_if_fail (CLUTTER_IS_ACTOR (ref_child));
+
+ priv = box->priv;
+
+ g_object_ref (child);
+
+ ref_c = box_child_find (box, ref_child);
+
+ if (ref_c != NULL)
+ {
+ c = box_child_new_from_actor (child, flags);
+
+ position = g_list_index (priv->children, ref_c);
+ priv->children = g_list_insert (priv->children, c, ++position);
+
+ clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+ g_signal_emit_by_name (box, "actor-added", child);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+ }
+
+ g_object_unref (child);
+}
+
+void
+big_box_insert_before (BigBox *box,
+ ClutterActor *child,
+ ClutterActor *ref_child,
+ BigBoxPackFlags flags)
+{
+ BigBoxPrivate *priv;
+ BigBoxChild *c, *ref_c;
+ gint position;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+ priv = box->priv;
+
+ g_object_ref (child);
+
+ ref_c = box_child_find (box, ref_child);
+
+ if (ref_c != NULL)
+ {
+ c = box_child_new_from_actor (child, flags);
+
+ position = g_list_index (priv->children, ref_c);
+ priv->children = g_list_insert (priv->children, c, position);
+
+ clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+ g_signal_emit_by_name (box, "actor-added", child);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+ }
+
+ g_object_unref (child);
+}
+
+void
+big_box_set_child_packing (BigBox *box,
+ ClutterActor *child,
+ BigBoxPackFlags flags)
+{
+ BigBoxChild *c;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+ g_object_ref (child);
+
+ c = box_child_find (box, child);
+
+ if (c != NULL && box_child_set_flags (c, flags))
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+ g_object_unref (child);
+}
+
+void
+big_box_set_child_align(BigBox *box,
+ ClutterActor *child,
+ BigBoxAlignment fixed_x_align,
+ BigBoxAlignment fixed_y_align)
+{
+ BigBoxChild *c;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+ g_object_ref (child);
+
+ c = box_child_find (box, child);
+
+ if (c != NULL && box_child_set_align (c, fixed_x_align, fixed_y_align))
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+ g_object_unref (child);
+}
+
+void
+big_box_set_padding (BigBox *box, int padding)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit padding_in_units;
+ gboolean padding_changed;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (padding >= 0);
+
+ priv = box->priv;
+
+ padding_in_units = CLUTTER_UNITS_FROM_DEVICE (padding);
+
+ padding_changed = (priv->padding_top != padding_in_units ||
+ priv->padding_bottom != padding_in_units ||
+ priv->padding_left != padding_in_units ||
+ priv->padding_right != padding_in_units);
+
+ if (padding_changed)
+ {
+ g_object_freeze_notify (G_OBJECT (box));
+
+ if (box->priv->padding_top != padding_in_units)
+ g_object_notify (G_OBJECT (box), "padding-top");
+ box->priv->padding_top = padding_in_units;
+
+ if (box->priv->padding_bottom != padding_in_units)
+ g_object_notify (G_OBJECT (box), "padding-bottom");
+ box->priv->padding_bottom = padding_in_units;
+
+ if (box->priv->padding_left != padding_in_units)
+ g_object_notify (G_OBJECT (box), "padding-left");
+ box->priv->padding_left = padding_in_units;
+
+ if (box->priv->padding_right != padding_in_units)
+ g_object_notify (G_OBJECT (box), "padding-right");
+ box->priv->padding_right = padding_in_units;
+
+ g_object_thaw_notify (G_OBJECT (box));
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+ }
+}
+
+void
+big_box_set_border_width (BigBox *box, int border_width)
+{
+ BigBoxPrivate *priv;
+ ClutterUnit border_in_units;
+ gboolean border_changed;
+
+ g_return_if_fail (BIG_IS_BOX (box));
+ g_return_if_fail (border_width >= 0);
+
+ priv = box->priv;
+
+ border_in_units = CLUTTER_UNITS_FROM_DEVICE (border_width);
+
+ border_changed = (priv->border_top != border_in_units ||
+ priv->border_bottom != border_in_units ||
+ priv->border_left != border_in_units ||
+ priv->border_right != border_in_units);
+
+ if (border_changed)
+ {
+ g_object_freeze_notify (G_OBJECT (box));
+
+ if (box->priv->border_top != border_in_units)
+ g_object_notify (G_OBJECT (box), "border-top");
+ box->priv->border_top = border_in_units;
+
+ if (box->priv->border_bottom != border_in_units)
+ g_object_notify (G_OBJECT (box), "border-bottom");
+ box->priv->border_bottom = border_in_units;
+
+ if (box->priv->border_left != border_in_units)
+ g_object_notify (G_OBJECT (box), "border-left");
+ box->priv->border_left = border_in_units;
+
+ if (box->priv->border_right != border_in_units)
+ g_object_notify (G_OBJECT (box), "border-right");
+ box->priv->border_right = border_in_units;
+
+ g_object_thaw_notify (G_OBJECT (box));
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+ big_box_update_draw_rounded_corner (box);
+ }
+}
Added: trunk/src/big/box.h
==============================================================================
--- (empty file)
+++ trunk/src/big/box.h Mon Dec 1 23:01:52 2008
@@ -0,0 +1,129 @@
+/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* big-box.h: Box container.
+
+ Copyright (C) 2006-2008 Red Hat, Inc.
+ Copyright (C) 2008 litl, LLC.
+
+ The libbigwidgets-lgpl is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+ If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+ Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __BIG_BOX_H__
+#define __BIG_BOX_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define BIG_TYPE_BOX (big_box_get_type ())
+#define BIG_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIG_TYPE_BOX, BigBox))
+#define BIG_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIG_TYPE_BOX))
+#define BIG_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BIG_TYPE_BOX, BigBoxClass))
+#define BIG_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BIG_TYPE_BOX))
+#define BIG_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BIG_TYPE_BOX, BigBoxClass))
+
+typedef struct _BigBox BigBox;
+typedef struct _BigBoxPrivate BigBoxPrivate;
+typedef struct _BigBoxClass BigBoxClass;
+
+typedef enum
+{
+ BIG_BOX_PACK_NONE = 0,
+ BIG_BOX_PACK_EXPAND = 1 << 0,
+ BIG_BOX_PACK_END = 1 << 1,
+ BIG_BOX_PACK_IF_FITS = 1 << 2,
+ BIG_BOX_PACK_FIXED = 1 << 3,
+ BIG_BOX_PACK_ALLOCATE_WHEN_HIDDEN = 1 << 4
+} BigBoxPackFlags;
+
+typedef enum
+{
+ BIG_BOX_ALIGNMENT_FIXED = 0,
+ BIG_BOX_ALIGNMENT_FILL = 1,
+ BIG_BOX_ALIGNMENT_START = 2,
+ BIG_BOX_ALIGNMENT_END = 3,
+ BIG_BOX_ALIGNMENT_CENTER = 4
+} BigBoxAlignment;
+
+typedef enum
+{
+ BIG_BOX_ORIENTATION_VERTICAL = 1,
+ BIG_BOX_ORIENTATION_HORIZONTAL = 2
+} BigBoxOrientation;
+
+typedef enum
+{
+ BIG_BOX_BACKGROUND_REPEAT_NONE = 0,
+ BIG_BOX_BACKGROUND_REPEAT_X = 1,
+ BIG_BOX_BACKGROUND_REPEAT_Y = 2,
+ BIG_BOX_BACKGROUND_REPEAT_BOTH = 3,
+} BigBoxBackgroundRepeat;
+
+struct _BigBox
+{
+ ClutterActor parent_instance;
+
+ BigBoxPrivate *priv;
+};
+
+struct _BigBoxClass
+{
+ ClutterActorClass parent_class;
+};
+
+GType big_box_get_type (void) G_GNUC_CONST;
+
+ClutterActor *big_box_new (BigBoxOrientation orientation);
+
+void big_box_prepend (BigBox *box,
+ ClutterActor *child,
+ BigBoxPackFlags flags);
+
+void big_box_append (BigBox *box,
+ ClutterActor *child,
+ BigBoxPackFlags flags);
+
+gboolean big_box_is_empty (BigBox *box);
+
+void big_box_remove_all (BigBox *box);
+
+void big_box_insert_after (BigBox *box,
+ ClutterActor *child,
+ ClutterActor *ref_child,
+ BigBoxPackFlags flags);
+
+void big_box_insert_before (BigBox *box,
+ ClutterActor *child,
+ ClutterActor *ref_child,
+ BigBoxPackFlags flags);
+
+void big_box_set_child_packing (BigBox *box,
+ ClutterActor *child,
+ BigBoxPackFlags flags);
+
+void big_box_set_child_align (BigBox *box,
+ ClutterActor *child,
+ BigBoxAlignment fixed_x_align,
+ BigBoxAlignment fixed_y_align);
+
+void big_box_set_padding (BigBox *box,
+ int padding);
+
+void big_box_set_border_width (BigBox *box,
+ int border_width);
+
+G_END_DECLS
+
+#endif /* __BIG_BOX_H__ */
Added: trunk/src/big/rectangle.c
==============================================================================
--- (empty file)
+++ trunk/src/big/rectangle.c Mon Dec 1 23:01:52 2008
@@ -0,0 +1,537 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* rectangle.c: Rounded rectangle.
+
+ Copyright (C) 2008 litl, LLC.
+
+ The libbigwidgets-lgpl is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+ If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+ Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <cairo/cairo.h>
+
+#include "rectangle.h"
+
+typedef struct {
+ gint ref_count;
+
+ ClutterColor color;
+ ClutterColor border_color;
+ int radius;
+ int border_width;
+
+ CoglHandle texture;
+ guint8 *data;
+} Corner;
+
+struct BigRectangle {
+ ClutterRectangle parent_instance;
+ ClutterUnit radius;
+ Corner *corner;
+ gboolean corners_dirty;
+};
+
+/* map of { radius, border_width, border_color, color } to Corner textures */
+static GHashTable *all_corners = NULL;
+
+struct BigRectangleClass {
+ ClutterRectangleClass parent_class;
+};
+
+G_DEFINE_TYPE(BigRectangle, big_rectangle, CLUTTER_TYPE_RECTANGLE)
+
+enum
+{
+ PROP_0,
+
+ PROP_CORNER_RADIUS
+};
+
+static gboolean
+corner_equal(gconstpointer a,
+ gconstpointer b)
+{
+ const Corner *corner_a;
+ const Corner *corner_b;
+
+ corner_a = a;
+ corner_b = b;
+
+ return *((guint32 *)&corner_a->color) == *((guint32 *)&corner_b->color) &&
+ *((guint32 *)&corner_a->border_color) == *((guint32 *)&corner_b->border_color) &&
+ corner_a->border_width == corner_b->border_width &&
+ corner_a->radius == corner_b->radius;
+}
+
+static guint
+corner_hash(gconstpointer key)
+{
+ const Corner *corner;
+ guint hashed[4];
+
+ corner = key;
+
+ hashed[0] = *((guint *)&(corner->color));
+ hashed[1] = *((guint *)&(corner->border_color));
+ hashed[2] = *((guint *)&(corner->border_width));
+ hashed[3] = *((guint *)&(corner->radius));
+
+ return hashed[0] ^ hashed[1] ^ hashed[2] ^ hashed[3];
+}
+
+static Corner *
+create_corner_texture(Corner *src)
+{
+ Corner *corner;
+ CoglHandle texture;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ guint x, y;
+ guint rowstride;
+ guint8 *data;
+ guint32 *src_p;
+ guint8 *dst_p;
+ guint size;
+
+ corner = g_memdup(src, sizeof(Corner));
+
+ size = 2 * MAX(corner->border_width, corner->radius);
+ rowstride = size * 4;
+ data = g_new0(guint8, size * rowstride);
+
+ surface = cairo_image_surface_create_for_data(data,
+ CAIRO_FORMAT_ARGB32,
+ size, size,
+ rowstride);
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_scale(cr, size, size);
+
+ if (corner->border_width < corner->radius) {
+ double internal_radius = 0.5 * (1.0 - (double) corner->border_width / corner->radius);
+
+ if (corner->border_width != 0) {
+ cairo_set_source_rgba(cr,
+ (double)corner->border_color.red / G_MAXUINT8,
+ (double)corner->border_color.green / G_MAXUINT8,
+ (double)corner->border_color.blue / G_MAXUINT8,
+ (double)corner->border_color.alpha / G_MAXUINT8);
+
+ cairo_arc(cr, 0.5, 0.5, 0.5, 0, 2 * M_PI);
+ cairo_fill(cr);
+ }
+
+ cairo_set_source_rgba(cr,
+ (double)corner->color.red / G_MAXUINT8,
+ (double)corner->color.green / G_MAXUINT8,
+ (double)corner->color.blue / G_MAXUINT8,
+ (double)corner->color.alpha / G_MAXUINT8);
+ cairo_arc(cr, 0.5, 0.5, internal_radius, 0, 2 * M_PI);
+ cairo_fill(cr);
+
+ } else {
+ double radius;
+
+ radius = (gdouble)corner->radius / corner->border_width;
+
+ cairo_set_source_rgba(cr,
+ (double)corner->border_color.red / G_MAXUINT8,
+ (double)corner->border_color.green / G_MAXUINT8,
+ (double)corner->border_color.blue / G_MAXUINT8,
+ (double)corner->border_color.alpha / G_MAXUINT8);
+
+ cairo_arc(cr, radius, radius, radius, M_PI, 3 * M_PI / 2);
+ cairo_line_to(cr, 1.0 - radius, 0.0);
+ cairo_arc(cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2*M_PI);
+ cairo_line_to(cr, 1.0, 1.0 - radius);
+ cairo_arc(cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2);
+ cairo_line_to(cr, radius, 1.0);
+ cairo_arc(cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI);
+ cairo_fill(cr);
+ }
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+
+ corner->data = g_new0(guint8, size * rowstride);
+
+ /* cogl doesn't seem to support the conversion, do it manually */
+ /* borrowed from clutter-cairo, conversion from ARGB pre-multiplied
+ * to RGBA */
+ for (y = 0; y < size; y++) {
+ src_p = (guint32 *) (data + y * rowstride);
+ dst_p = corner->data + y * rowstride;
+
+ for (x = 0; x < size; x++) {
+ guint8 alpha = (*src_p >> 24) & 0xff;
+
+ if (alpha == 0) {
+ dst_p[0] = dst_p[1] = dst_p[2] = dst_p[3] = alpha;
+ } else {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ dst_p[0] = (((*src_p >> 16) & 0xff) * 255 ) / alpha;
+ dst_p[1] = (((*src_p >> 8) & 0xff) * 255 ) / alpha;
+ dst_p[2] = (((*src_p >> 0) & 0xff) * 255 ) / alpha;
+ dst_p[3] = alpha;
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+ dst_p[0] = alpha;
+ dst_p[1] = (((*src_p >> 0) & 0xff) * 255 ) / alpha;
+ dst_p[2] = (((*src_p >> 8) & 0xff) * 255 ) / alpha;
+ dst_p[3] = (((*src_p >> 16) & 0xff) * 255 ) / alpha;
+#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+#error unknown ENDIAN type
+#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+ }
+ dst_p += 4;
+ src_p++;
+ }
+ }
+
+ g_free(data);
+
+ texture = cogl_texture_new_from_data(size, size,
+ 0,
+ FALSE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_ANY,
+ rowstride,
+ corner->data);
+ g_assert(texture != COGL_INVALID_HANDLE);
+
+ corner->ref_count = 1;
+ corner->texture = texture;
+
+ g_hash_table_insert(all_corners, corner, corner);
+
+ return corner;
+}
+
+static void
+corner_unref(Corner *corner)
+{
+ corner->ref_count --;
+
+ if (corner->ref_count == 0) {
+ g_hash_table_remove(all_corners, corner);
+
+ cogl_texture_unref(corner->texture);
+ g_free(corner->data);
+ g_free(corner);
+ }
+}
+
+static Corner *
+corner_get(guint radius,
+ ClutterColor *color,
+ guint border_width,
+ ClutterColor *border_color)
+{
+ Corner key;
+ Corner *corner;
+
+ if (all_corners == NULL) {
+ all_corners = g_hash_table_new(corner_hash, corner_equal);
+ }
+
+ key.radius = radius;
+ key.color = *color;
+ key.border_color = *border_color;
+ key.border_width = border_width;
+
+ corner = g_hash_table_lookup(all_corners, &key);
+
+ if (!corner) {
+ corner = create_corner_texture(&key);
+ } else {
+ corner->ref_count ++;
+ }
+
+ return corner;
+}
+
+static void
+big_rectangle_update_corners(BigRectangle *rectangle)
+{
+ Corner *corner;
+
+ corner = NULL;
+
+ if (rectangle->radius != 0) {
+ ClutterColor *color;
+ ClutterColor *border_color;
+ guint border_width;
+
+ g_object_get(rectangle,
+ "border-color", &border_color,
+ "border-width", &border_width,
+ "color", &color,
+ NULL);
+
+ corner = corner_get(CLUTTER_UNITS_TO_DEVICE(rectangle->radius),
+ color,
+ border_width,
+ border_color);
+
+ clutter_color_free(border_color);
+ clutter_color_free(color);
+ }
+
+ if (rectangle->corner) {
+ corner_unref(rectangle->corner);
+ }
+
+ rectangle->corner = corner;
+
+ rectangle->corners_dirty = FALSE;
+}
+
+static void
+big_rectangle_paint(ClutterActor *actor)
+{
+ BigRectangle *rectangle;
+ ClutterColor *color;
+ ClutterColor *border_color;
+ guint border_width;
+ ClutterActorBox box;
+ ClutterFixed radiusx;
+ ClutterFixed widthx;
+ ClutterFixed heightx;
+ ClutterFixed border_widthx;
+ ClutterFixed max;
+ ClutterColor tmp_col;
+
+ rectangle = BIG_RECTANGLE(actor);
+
+ if (rectangle->radius == 0) {
+ /* In that case we are no different than our parent class,
+ * so don't bother */
+ CLUTTER_ACTOR_CLASS(big_rectangle_parent_class)->paint(actor);
+ return;
+ }
+
+ if (rectangle->corners_dirty)
+ big_rectangle_update_corners(rectangle);
+
+ g_object_get(rectangle,
+ "border-color", &border_color,
+ "border-width", &border_width,
+ "color", &color,
+ NULL);
+
+ clutter_actor_get_allocation_box(actor, &box);
+
+ /* translation was already done */
+ box.x2 -= box.x1;
+ box.y2 -= box.y1;
+
+ widthx = CLUTTER_UNITS_TO_FIXED(box.x2);
+ heightx = CLUTTER_UNITS_TO_FIXED(box.y2);
+
+ radiusx = CLUTTER_UNITS_TO_FIXED(rectangle->radius);
+
+ border_widthx = CLUTTER_INT_TO_FIXED(border_width);
+ max = MAX(border_widthx, radiusx);
+
+ if (radiusx != 0) {
+ tmp_col.red = 0xFF;
+ tmp_col.blue = 0xFF;
+ tmp_col.green = 0xFF;
+ tmp_col.alpha = clutter_actor_get_paint_opacity(actor);
+
+ cogl_color(&tmp_col);
+
+ /* NW */
+ cogl_texture_rectangle(rectangle->corner->texture,
+ 0, 0,
+ max, max,
+ 0, 0,
+ CFX_HALF, CFX_HALF);
+
+ /* NE */
+ cogl_texture_rectangle(rectangle->corner->texture,
+ widthx - max, 0,
+ widthx, max,
+ CFX_HALF, 0,
+ CFX_ONE, CFX_HALF);
+
+ /* SW */
+ cogl_texture_rectangle(rectangle->corner->texture,
+ 0, heightx - max,
+ max, heightx,
+ 0, CFX_HALF,
+ CFX_HALF, CFX_ONE);
+
+ /* SE */
+ cogl_texture_rectangle(rectangle->corner->texture,
+ widthx - max, heightx - max,
+ widthx, heightx,
+ CFX_HALF, CFX_HALF,
+ CFX_ONE, CFX_ONE);
+
+ }
+
+ if (border_width != 0) {
+ tmp_col = *border_color;
+
+ tmp_col.alpha = clutter_actor_get_paint_opacity(actor) * tmp_col.alpha / 255;
+
+ cogl_color(&tmp_col);
+
+ /* NORTH */
+ cogl_rectanglex(max, 0,
+ widthx - 2 * max, border_widthx);
+
+ /* EAST */
+ cogl_rectanglex(widthx - border_widthx, max,
+ border_widthx, heightx - 2 * max);
+
+ /* SOUTH */
+ cogl_rectanglex(max, heightx - border_widthx,
+ widthx - 2 * max, border_widthx);
+
+ /* WEST */
+ cogl_rectanglex(0, max,
+ border_widthx, heightx - 2 * max);
+
+ }
+
+ tmp_col = *color;
+ tmp_col.alpha = clutter_actor_get_paint_opacity(actor) * tmp_col.alpha / 255;
+
+ cogl_color(&tmp_col);
+
+ if (radiusx > border_widthx) {
+ cogl_rectanglex(radiusx, border_widthx,
+ widthx - radiusx - radiusx, radiusx - border_widthx);
+ cogl_rectanglex(radiusx, heightx - radiusx,
+ widthx - 2 * radiusx, radiusx - border_widthx);
+ }
+
+ cogl_rectanglex(border_widthx, MAX(radiusx, border_widthx),
+ widthx - 2 * border_widthx, heightx - 2 * MAX(radiusx, border_widthx));
+
+ clutter_color_free(border_color);
+ clutter_color_free(color);
+}
+
+static void
+big_rectangle_notify(GObject *object,
+ GParamSpec *pspec)
+{
+ BigRectangle *rectangle;
+
+ rectangle = BIG_RECTANGLE(object);
+
+ if (g_str_equal(pspec->name, "border-width") ||
+ g_str_equal(pspec->name, "color") ||
+ g_str_equal(pspec->name, "border-color")) {
+ rectangle->corners_dirty = TRUE;
+ }
+}
+
+static void
+big_rectangle_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BigRectangle *rectangle;
+
+ rectangle = BIG_RECTANGLE(object);
+
+ switch (prop_id) {
+ case PROP_CORNER_RADIUS:
+ rectangle->radius = CLUTTER_UNITS_FROM_DEVICE(g_value_get_uint(value));
+ rectangle->corners_dirty = TRUE;
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+
+ }
+}
+
+static void
+big_rectangle_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BigRectangle *rectangle;
+
+ rectangle = BIG_RECTANGLE(object);
+
+ switch (prop_id) {
+ case PROP_CORNER_RADIUS:
+ g_value_set_uint(value, CLUTTER_UNITS_TO_DEVICE(rectangle->radius));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+big_rectangle_dispose(GObject *object)
+{
+ BigRectangle *rectangle;
+
+ rectangle = BIG_RECTANGLE(object);
+
+ rectangle->radius = 0;
+ big_rectangle_update_corners(rectangle);
+
+ if (G_OBJECT_CLASS(big_rectangle_parent_class)->dispose)
+ G_OBJECT_CLASS(big_rectangle_parent_class)->dispose(object);
+
+}
+
+
+static void
+big_rectangle_class_init(BigRectangleClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ gobject_class->dispose = big_rectangle_dispose;
+ gobject_class->set_property = big_rectangle_set_property;
+ gobject_class->get_property = big_rectangle_get_property;
+ gobject_class->notify = big_rectangle_notify;
+
+ actor_class->paint = big_rectangle_paint;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CORNER_RADIUS,
+ g_param_spec_uint("corner-radius",
+ "Corner radius",
+ "Radius of the rectangle rounded corner",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+}
+
+static void
+big_rectangle_init(BigRectangle *rectangle)
+{
+}
Added: trunk/src/big/rectangle.h
==============================================================================
--- (empty file)
+++ trunk/src/big/rectangle.h Mon Dec 1 23:01:52 2008
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ Copyright (C) 2008 litl, LLC.
+
+ The libbigwidgets-lgpl is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+ If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+ Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef __BIG_RECTANGLE_H__
+#define __BIG_RECTANGLE_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define BIG_TYPE_RECTANGLE (big_rectangle_get_type ())
+#define BIG_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIG_TYPE_RECTANGLE, BigRectangle))
+#define BIG_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BIG_TYPE_RECTANGLE, BigRectangleClass))
+#define BIG_IS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIG_TYPE_RECTANGLE))
+#define BIG_IS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BIG_TYPE_RECTANGLE))
+#define BIG_RECTANGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BIG_TYPE_RECTANGLE, BigRectangleClass))
+
+typedef struct BigRectangle BigRectangle;
+typedef struct BigRectangleClass BigRectangleClass;
+
+GType big_rectangle_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __BIG_RECTANGLE_H__ */
Added: trunk/src/big/theme-image.c
==============================================================================
--- (empty file)
+++ trunk/src/big/theme-image.c Mon Dec 1 23:01:52 2008
@@ -0,0 +1,795 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* theme-image.c: Stretched image.
+
+ Copyright (C) 2005-2008 Red Hat, Inc.
+ Copyright (C) 2008 litl, LLC.
+
+ The libbigwidgets-lgpl is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+ If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+ Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <glib.h>
+
+#include <clutter/clutter-units.h>
+#include <clutter/clutter-actor.h>
+#include <clutter-cairo/clutter-cairo.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+
+#include "theme-image.h"
+
+typedef enum {
+ BIG_THEME_IMAGE_UNSET,
+ BIG_THEME_IMAGE_SVG,
+ BIG_THEME_IMAGE_SURFACE
+
+} BigThemeImageType;
+
+struct BigThemeImage {
+ ClutterCairo parent_instance;
+
+ guint border_top;
+ guint border_bottom;
+ guint border_left;
+ guint border_right;
+
+ BigThemeImageType type;
+
+ union {
+ RsvgHandle *svg_handle;
+ cairo_surface_t *surface;
+ } u;
+
+ guint render_idle;
+ guint needs_render : 1;
+};
+
+struct BigThemeImageClass {
+ ClutterCairoClass parent_class;
+};
+
+G_DEFINE_TYPE(BigThemeImage, big_theme_image, CLUTTER_TYPE_CAIRO)
+
+enum
+{
+ PROP_0,
+
+ PROP_BORDER_TOP,
+ PROP_BORDER_BOTTOM,
+ PROP_BORDER_LEFT,
+ PROP_BORDER_RIGHT,
+
+ PROP_FILENAME,
+ PROP_PIXBUF
+};
+
+static void
+big_theme_image_render(BigThemeImage *image)
+{
+ int source_width = 0;
+ int source_height = 0;
+ int source_x1 = 0, source_x2 = 0, source_y1 = 0, source_y2 = 0;
+ int dest_x1 = 0, dest_x2 = 0, dest_y1 = 0, dest_y2 = 0;
+ int i, j;
+ int dest_width;
+ int dest_height;
+ ClutterGeometry geometry;
+ cairo_t *cr;
+
+ image->needs_render = FALSE;
+
+ if (image->render_idle) {
+ g_source_remove(image->render_idle);
+ image->render_idle = 0;
+ }
+
+
+ /* To draw a theme image, we divide the source and destination into 9
+ * pieces and draw each piece separately. (Some pieces may not exist
+ * if we have 0-width borders, in which case they'll be skipped)
+ *
+ * i=0 i=1 i=2
+ * border_left border_right
+ * +------------+--------------------+--------------+
+ * j=0: border_top | | | |
+ * +------------+--------------------+--------------+
+ * j=1 | | | |
+ * +------------+--------------------+--------------+
+ * j=2: border_bottom | | | |
+ * +------------+--------------------+--------------+
+ *
+ */
+
+ switch (image->type) {
+ case BIG_THEME_IMAGE_SURFACE:
+ if (!image->u.surface)
+ return;
+
+ source_width = cairo_image_surface_get_width(image->u.surface);
+ source_height = cairo_image_surface_get_height(image->u.surface);
+ break;
+ case BIG_THEME_IMAGE_SVG:
+ {
+ RsvgDimensionData dimensions;
+
+ if (!image->u.svg_handle)
+ return;
+
+ rsvg_handle_get_dimensions(image->u.svg_handle, &dimensions);
+ source_width = dimensions.width;
+ source_height = dimensions.height;
+ break;
+ }
+ default:
+ return;
+ }
+
+ clutter_actor_get_allocation_geometry(CLUTTER_ACTOR(image), &geometry);
+
+ dest_width = geometry.width;
+ dest_height = geometry.height;
+
+ cr = clutter_cairo_create(CLUTTER_CAIRO(image));
+
+ for (j = 0; j < 3; j++) {
+ switch (j) {
+ case 0:
+ source_y1 = 0;
+ source_y2 = image->border_top;
+ dest_y1 = 0;
+ dest_y2 = image->border_top;
+ break;
+ case 1:
+ source_y1 = image->border_top;
+ source_y2 = source_height - image->border_bottom;
+ dest_y1 = image->border_top;
+ dest_y2 = dest_height - image->border_bottom;
+ break;
+ case 2:
+ source_y1 = source_height - image->border_bottom;
+ source_y2 = source_height;
+ dest_y1 = dest_height - image->border_bottom;
+ dest_y2 = dest_height;
+ break;
+ }
+
+ if (dest_y2 <= dest_y1)
+ continue;
+
+ /* pixbuf-theme-engine has a nice interpretation of source_y2 == source_y1,
+ * dest_y2 != dest_y1, which is to linearly interpolate between the surrounding
+ * areas. We could do that for the surface case by setting
+ *
+ * source_y1 == y - 0.5
+ * source_y2 == y + 0.5
+ *
+ * but it's hard for the SVG case. source_y2 < source_y1 is pathological ... someone
+ * specified borders that sum up larger than the image.
+ */
+ if (source_y2 <= source_y1)
+ continue;
+
+ for (i = 0; i < 3; i++) {
+ switch (i) {
+ case 0:
+ source_x1 = 0;
+ source_x2 = image->border_left;
+ dest_x1 = 0;
+ dest_x2 = image->border_left;
+ break;
+ case 1:
+ source_x1 = image->border_left;
+ source_x2 = source_width - image->border_right;
+ dest_x1 = image->border_left;
+ dest_x2 = dest_width - image->border_right;
+ break;
+ case 2:
+ source_x1 = source_width - image->border_right;
+ source_x2 = source_width;
+ dest_x1 = dest_width - image->border_right;
+ dest_x2 = dest_width;
+ break;
+ }
+
+ if (dest_x2 <= dest_x1)
+ continue;
+
+ if (source_x2 <= source_x1)
+ continue;
+
+ cairo_save(cr);
+
+ cairo_rectangle(cr, dest_x1, dest_y1, dest_x2 - dest_x1, dest_y2 - dest_y1);
+ cairo_clip(cr);
+
+ cairo_translate(cr, dest_x1, dest_y1);
+ cairo_scale(cr,
+ (double)(dest_x2 - dest_x1) / (source_x2 - source_x1),
+ (double)(dest_y2 - dest_y1) / (source_y2 - source_y1));
+
+ switch (image->type) {
+ case BIG_THEME_IMAGE_SURFACE:
+ cairo_set_source_surface(cr, image->u.surface, - source_x1, - source_y1);
+ cairo_paint(cr);
+ break;
+ case BIG_THEME_IMAGE_SVG:
+ cairo_translate(cr, - source_x1, - source_y1);
+ rsvg_handle_render_cairo(image->u.svg_handle, cr);
+ break;
+ default:
+ break;
+ }
+
+ cairo_restore(cr);
+ }
+ }
+
+ /* This will cause the surface content to be uploaded as
+ * new texture content */
+ cairo_destroy(cr);
+}
+
+static gboolean
+big_theme_image_render_idle(gpointer data)
+{
+ BigThemeImage *image;
+
+ image = data;
+ big_theme_image_render(image);
+
+ return FALSE;
+}
+
+static void
+big_theme_image_queue_render(BigThemeImage *image)
+{
+ image->needs_render = TRUE;
+ if (!image->render_idle)
+ image->render_idle = g_idle_add(big_theme_image_render_idle,
+ image);
+}
+
+static void
+big_theme_image_paint(ClutterActor *actor)
+{
+ BigThemeImage *image;
+
+ image = BIG_THEME_IMAGE(actor);
+
+ if (image->needs_render)
+ big_theme_image_render(image);
+
+ if (CLUTTER_ACTOR_CLASS(big_theme_image_parent_class)->paint)
+ CLUTTER_ACTOR_CLASS(big_theme_image_parent_class)->paint(actor);
+}
+
+static void
+big_theme_image_allocate(ClutterActor *actor,
+ const ClutterActorBox *box,
+ gboolean absolute_origin_changed)
+{
+ BigThemeImage *image;
+ guint old_width;
+ guint old_height;
+ guint width;
+ guint height;
+
+ image = BIG_THEME_IMAGE(actor);
+
+ width = ABS(CLUTTER_UNITS_TO_DEVICE(box->x2 - box->x1));
+ height = ABS(CLUTTER_UNITS_TO_DEVICE(box->y2 - box->y1));
+
+ g_object_get(actor,
+ "surface-width", &old_width,
+ "surface-height", &old_height,
+ NULL);
+
+ if (width != old_width || height != old_height) {
+
+ clutter_cairo_surface_resize(CLUTTER_CAIRO(actor), width, height);
+
+ big_theme_image_queue_render(image);
+ }
+
+ if (CLUTTER_ACTOR_CLASS(big_theme_image_parent_class))
+ CLUTTER_ACTOR_CLASS(big_theme_image_parent_class)->allocate(actor,
+ box,
+ absolute_origin_changed);
+}
+
+static void
+big_theme_image_get_preferred_height(ClutterActor *actor,
+ ClutterUnit for_width,
+ ClutterUnit *min_height_p,
+ ClutterUnit *natural_height_p)
+{
+ BigThemeImage *image;
+
+ image = BIG_THEME_IMAGE(actor);
+
+ if (min_height_p)
+ *min_height_p = 0;
+
+ if (!natural_height_p)
+ return;
+
+ *natural_height_p = 0;
+
+ switch (image->type) {
+ case BIG_THEME_IMAGE_SURFACE:
+ if (!image->u.surface)
+ break;
+
+ *natural_height_p = CLUTTER_UNITS_FROM_DEVICE(
+ cairo_image_surface_get_height(image->u.surface));
+ break;
+ case BIG_THEME_IMAGE_SVG:
+ {
+ RsvgDimensionData dimensions;
+
+ if (!image->u.svg_handle)
+ return;
+
+ rsvg_handle_get_dimensions(image->u.svg_handle, &dimensions);
+ *natural_height_p =
+ CLUTTER_UNITS_FROM_DEVICE(dimensions.height);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void
+big_theme_image_get_preferred_width(ClutterActor *actor,
+ ClutterUnit for_height,
+ ClutterUnit *min_width_p,
+ ClutterUnit *natural_width_p)
+{
+ BigThemeImage *image;
+
+ image = BIG_THEME_IMAGE(actor);
+
+ if (min_width_p)
+ *min_width_p = 0;
+
+ if (!natural_width_p)
+ return;
+
+ *natural_width_p = 0;
+
+ switch (image->type) {
+ case BIG_THEME_IMAGE_SURFACE:
+ if (!image->u.surface)
+ break;
+
+ *natural_width_p = CLUTTER_UNITS_FROM_DEVICE(cairo_image_surface_get_width(image->u.surface));
+ break;
+ case BIG_THEME_IMAGE_SVG:
+ {
+ RsvgDimensionData dimensions;
+
+ if (!image->u.svg_handle)
+ return;
+
+ rsvg_handle_get_dimensions(image->u.svg_handle, &dimensions);
+ *natural_width_p = CLUTTER_UNITS_FROM_DEVICE(dimensions.width);
+ break;
+ }
+ default:
+ break;
+ }
+
+}
+
+static void
+big_theme_image_set_border_value(BigThemeImage *image, guint *old_value, const GValue *new_value)
+{
+ guint border_value;
+
+ border_value = g_value_get_uint(new_value);
+
+ if (*old_value != border_value) {
+ *old_value = border_value;
+
+ big_theme_image_queue_render(image);
+ }
+}
+
+static void
+big_theme_image_set_filename(BigThemeImage *image, const char *filename)
+{
+ if (!filename)
+ return;
+
+ if (g_str_has_suffix(filename, ".png") ||
+ g_str_has_suffix(filename, ".PNG")) {
+
+ image->type = BIG_THEME_IMAGE_SURFACE;
+
+ image->u.surface = cairo_image_surface_create_from_png(filename);
+
+ if (image->u.surface == NULL) {
+ g_warning("Error when loading PNG from file %s", filename);
+ }
+ } else if (g_str_has_suffix(filename, ".svg") ||
+ g_str_has_suffix(filename, ".SVG")) {
+
+ GError *error;
+
+ error = NULL;
+ image->u.svg_handle = rsvg_handle_new_from_file(filename, &error);
+
+ if (image->u.svg_handle == NULL) {
+ g_warning("Error when loading SVG from file %s: %s", filename,
+ error?error->message:"Error not set by RSVG");
+ if (error)
+ g_error_free(error);
+
+ return;
+ }
+
+ image->type = BIG_THEME_IMAGE_SVG;
+ } else {
+ g_warning("%s: Unsupported file type", filename);
+ return;
+ }
+
+ big_theme_image_queue_render(image);
+}
+
+static cairo_surface_t *
+create_surface_from_pixbuf(const GdkPixbuf *pixbuf)
+{
+ gint width = gdk_pixbuf_get_width (pixbuf);
+ gint height = gdk_pixbuf_get_height (pixbuf);
+ guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
+ int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+ guchar *cairo_pixels;
+ cairo_format_t format;
+ cairo_surface_t *surface;
+ static const cairo_user_data_key_t key;
+
+ int j;
+
+ if (n_channels == 3)
+ format = CAIRO_FORMAT_RGB24;
+ else
+ format = CAIRO_FORMAT_ARGB32;
+
+ cairo_pixels = g_malloc (4 * width * height);
+
+ surface = cairo_image_surface_create_for_data((unsigned char *)cairo_pixels,
+ format,
+ width, height, 4 * width);
+
+ cairo_surface_set_user_data(surface, &key,
+ cairo_pixels, (cairo_destroy_func_t)g_free);
+
+ for (j = height; j; j--) {
+ guchar *p = gdk_pixels;
+ guchar *q = cairo_pixels;
+
+ if (n_channels == 3) {
+ guchar *end = p + 3 * width;
+
+ while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ q[0] = p[2];
+ q[1] = p[1];
+ q[2] = p[0];
+#else
+ q[1] = p[0];
+ q[2] = p[1];
+ q[3] = p[2];
+#endif
+ p += 3;
+ q += 4;
+ }
+ } else {
+ guchar *end = p + 4 * width;
+ guint t1,t2,t3;
+
+#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
+
+ while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ MULT(q[0], p[2], p[3], t1);
+ MULT(q[1], p[1], p[3], t2);
+ MULT(q[2], p[0], p[3], t3);
+ q[3] = p[3];
+#else
+ q[0] = p[3];
+ MULT(q[1], p[0], p[3], t1);
+ MULT(q[2], p[1], p[3], t2);
+ MULT(q[3], p[2], p[3], t3);
+#endif
+
+ p += 4;
+ q += 4;
+ }
+#undef MULT
+ }
+
+ gdk_pixels += gdk_rowstride;
+ cairo_pixels += 4 * width;
+ }
+
+ return surface;
+}
+
+static void
+big_theme_image_set_pixbuf(BigThemeImage *image, GdkPixbuf *pixbuf)
+{
+ if (!pixbuf)
+ return;
+
+ image->type = BIG_THEME_IMAGE_SURFACE;
+
+ image->u.surface = create_surface_from_pixbuf(pixbuf);
+
+ g_assert(image->u.surface != NULL);
+
+ big_theme_image_queue_render(image);
+}
+
+static void
+big_theme_image_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BigThemeImage *image;
+
+ image = BIG_THEME_IMAGE(object);
+
+ switch (prop_id) {
+ case PROP_BORDER_TOP:
+ big_theme_image_set_border_value(image, &image->border_top, value);
+ break;
+
+ case PROP_BORDER_BOTTOM:
+ big_theme_image_set_border_value(image, &image->border_bottom, value);
+ break;
+
+ case PROP_BORDER_LEFT:
+ big_theme_image_set_border_value(image, &image->border_left, value);
+ break;
+
+ case PROP_BORDER_RIGHT:
+ big_theme_image_set_border_value(image, &image->border_right, value);
+ break;
+
+ case PROP_FILENAME:
+ big_theme_image_set_filename(image, g_value_get_string(value));
+ break;
+
+ case PROP_PIXBUF:
+ big_theme_image_set_pixbuf(image, g_value_get_object(value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+
+ }
+}
+
+static void
+big_theme_image_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BigThemeImage *image;
+
+ image = BIG_THEME_IMAGE(object);
+
+ switch (prop_id) {
+ case PROP_BORDER_TOP:
+ g_value_set_uint(value, image->border_top);
+ break;
+
+ case PROP_BORDER_BOTTOM:
+ g_value_set_uint(value, image->border_bottom);
+ break;
+
+ case PROP_BORDER_LEFT:
+ g_value_set_uint(value, image->border_left);
+ break;
+
+ case PROP_BORDER_RIGHT:
+ g_value_set_uint(value, image->border_right);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+big_theme_image_dispose(GObject *object)
+{
+ BigThemeImage *image;
+
+ image = BIG_THEME_IMAGE(object);
+
+ if (image->render_idle) {
+ g_source_remove(image->render_idle);
+ image->render_idle = 0;
+ }
+
+ switch (image->type) {
+ case BIG_THEME_IMAGE_SVG:
+ if (image->u.svg_handle) {
+ g_object_unref(image->u.svg_handle);
+ image->u.svg_handle = NULL;
+ }
+ break;
+ case BIG_THEME_IMAGE_SURFACE:
+ if (image->u.surface) {
+ cairo_surface_destroy(image->u.surface);
+ image->u.surface = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (G_OBJECT_CLASS(big_theme_image_parent_class)->dispose)
+ G_OBJECT_CLASS(big_theme_image_parent_class)->dispose(object);
+
+}
+
+
+static void
+big_theme_image_class_init(BigThemeImageClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ gobject_class->dispose = big_theme_image_dispose;
+ gobject_class->set_property = big_theme_image_set_property;
+ gobject_class->get_property = big_theme_image_get_property;
+
+ actor_class->allocate = big_theme_image_allocate;
+ actor_class->get_preferred_width = big_theme_image_get_preferred_width;
+ actor_class->get_preferred_height = big_theme_image_get_preferred_height;
+ actor_class->paint = big_theme_image_paint;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_TOP,
+ g_param_spec_uint("border-top",
+ "Border top",
+ "Top dimension of the image border "
+ "(none-scaled part)",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_BOTTOM,
+ g_param_spec_uint("border-bottom",
+ "Border bottom",
+ "Bottom dimension of the image border "
+ "(none-scaled part)",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_LEFT,
+ g_param_spec_uint("border-left",
+ "Border left",
+ "Left dimension of the image border "
+ "(none-scaled part)",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_BORDER_RIGHT,
+ g_param_spec_uint("border-right",
+ "Border right",
+ "Right dimension of the image border "
+ "(none-scaled part)",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILENAME,
+ g_param_spec_string("filename",
+ "Filename",
+ "Name of the file",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PIXBUF,
+ g_param_spec_object("pixbuf",
+ "Pixbuf",
+ "Pixbuf of the image",
+ GDK_TYPE_PIXBUF,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+}
+
+static void
+big_theme_image_init(BigThemeImage *image)
+{
+}
+
+ClutterActor *
+big_theme_image_new_from_file(const char *filename,
+ guint border_top,
+ guint border_bottom,
+ guint border_left,
+ guint border_right)
+{
+ ClutterActor *actor;
+
+ actor = g_object_new(BIG_TYPE_THEME_IMAGE,
+ /* FIXME ClutterCairo requires creating a bogus
+ * surface with nonzero size
+ */
+ "surface-width", 1,
+ "surface-height", 1,
+ "filename", filename,
+ "border-top", border_top,
+ "border-bottom", border_bottom,
+ "border-left", border_left,
+ "border-right", border_right,
+ NULL);
+
+ return actor;
+}
+
+ClutterActor *
+big_theme_image_new_from_pixbuf(GdkPixbuf *pixbuf,
+ guint border_top,
+ guint border_bottom,
+ guint border_left,
+ guint border_right)
+{
+ ClutterActor *actor;
+
+ actor = g_object_new(BIG_TYPE_THEME_IMAGE,
+ /* FIXME ClutterCairo requires creating a bogus
+ * surface with nonzero size
+ */
+ "surface-width", 1,
+ "surface-height", 1,
+ "pixbuf", pixbuf,
+ "border-top", border_top,
+ "border-bottom", border_bottom,
+ "border-left", border_left,
+ "border-right", border_right,
+ NULL);
+
+ return actor;
+}
+
Added: trunk/src/big/theme-image.h
==============================================================================
--- (empty file)
+++ trunk/src/big/theme-image.h Mon Dec 1 23:01:52 2008
@@ -0,0 +1,39 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#ifndef __BIG_THEME_IMAGE_H__
+#define __BIG_THEME_IMAGE_H__
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define BIG_TYPE_THEME_IMAGE (big_theme_image_get_type ())
+#define BIG_THEME_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIG_TYPE_THEME_IMAGE, BigThemeImage))
+#define BIG_THEME_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BIG_TYPE_THEME_IMAGE, BigThemeImageClass))
+#define BIG_IS_THEME_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIG_TYPE_THEME_IMAGE))
+#define BIG_IS_THEME_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BIG_TYPE_THEME_IMAGE))
+#define BIG_THEME_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BIG_TYPE_THEME_IMAGE, BigThemeImageClass))
+
+typedef struct BigThemeImage BigThemeImage;
+typedef struct BigThemeImageClass BigThemeImageClass;
+
+GType big_theme_image_get_type (void) G_GNUC_CONST;
+
+ClutterActor * big_theme_image_new_from_file (const gchar *filename,
+ guint border_top,
+ guint border_bottom,
+ guint border_left,
+ guint border_right);
+
+ClutterActor * big_theme_image_new_from_pixbuf (GdkPixbuf *pixbuf,
+ guint border_top,
+ guint border_bottom,
+ guint border_left,
+ guint border_right);
+
+G_END_DECLS
+
+#endif /* __BIG_THEME_IMAGE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]