Import Mx core as ST

Import the core MxWidget/MxBin and their dependencies; we use the
namespace "St" (Shell Toolkit) because it is the same length as Mx
so enabling easy sharing of code, but makes it clear that this is
a friendly fork and not a literal import.

Based on a patch by Colin Walters <walters@verbum.org>

https://bugzilla.gnome.org/show_bug.cgi?id=591245
This commit is contained in:
Owen W. Taylor 2009-09-08 15:47:30 -04:00
parent 2b78d5bd5d
commit d291e568fd
25 changed files with 7360 additions and 6 deletions

View File

@ -57,6 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
gobject-introspection-1.0 >= 0.6.5)
PKG_CHECK_MODULES(TIDY, clutter-1.0)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 libccss-1 >= 0.3.1)
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)

102
src/Makefile-st.am Normal file
View File

@ -0,0 +1,102 @@
st_cflags = \
-I$(top_srcdir)/src \
-DPREFIX=\""$(prefix)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DG_DISABLE_DEPRECATED \
-DG_LOG_DOMAIN=\"St\" \
-DST_COMPILATION \
-DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" \
$(ST_CFLAGS) \
$(NULL)
st_built_sources = \
st-enum-types.h \
st-enum-types.c \
st-marshal.h \
st-marshal.c
BUILT_SOURCES += $(st_built_sources)
EXTRA_DIST += \
st/st-marshal.list \
st/st-enum-types.h.in \
st/st-enum-types.c.in
CLEANFILES += stamp-st-marshal.h stamp-st-enum-types.h
st-marshal.h: stamp-st-marshal.h
@true
stamp-st-marshal.h: Makefile st/st-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
--prefix=_st_marshal \
--header \
$(srcdir)/st/st-marshal.list > $@.tmp && \
(cmp -s $@.tmp st-marshal.h || cp -f $@.tmp st-marshal.h) && \
rm -f $@.tmp && \
echo timestamp > $(@F)
st-marshal.c: Makefile st/st-marshal.list
$(AM_V_GEN) (echo "#include \"st-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_st_marshal \
--body \
$(srcdir)/st/st-marshal.list ) > $@.tmp && \
cp -f $@.tmp st-marshal.c && \
rm -f $@.tmp
st-enum-types.h: stamp-st-enum-types.h Makefile
@true
stamp-st-enum-types.h: $(source_h) st/st-enum-types.h.in
$(AM_V_GEN) ( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template st/st-enum-types.h.in \
$(st_source_h) ) >> $@.tmp && \
(cmp -s $@.tmp st-enum-types.h || cp $@.tmp st-enum-types.h) && \
rm -f $@.tmp && \
echo timestamp > $(@F)
st-enum-types.c: stamp-st-enum-types.h st/st-enum-types.c.in
$(AM_V_GEN) ( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template st/st-enum-types.c.in \
$(st_source_h) ) >> $@.tmp && \
cp $@.tmp $@ && \
rm -f $@.tmp
# please, keep this sorted alphabetically
st_source_h = \
st/st-bin.h \
st/st-private.h \
st/st-stylable.h \
st/st-style.h \
st/st-subtexture.h \
st/st-texture-cache.h \
st/st-texture-frame.h \
st/st-tooltip.h \
st/st-types.h \
st/st-widget.h \
$(NULL)
# please, keep this sorted alphabetically
st_source_c = \
st/st-bin.c \
st/st-private.c \
st/st-stylable.c \
st/st-style.c \
st/st-subtexture.c \
st/st-texture-cache.c \
st/st-texture-frame.c \
st/st-tooltip.c \
st/st-widget.c \
$(NULL)
noinst_LTLIBRARIES += libst-1.0.la
libst_1_0_la_LIBADD = $(ST_LIBS)
libst_1_0_la_SOURCES = \
$(st_source_c) \
$(st_source_h) \
$(st_built_sources) \
$(NULL)
libst_1_0_la_CPPFLAGS = $(st_cflags)
libst_1_0_la_LDFLAGS = $(LDADD)

View File

@ -24,6 +24,7 @@ EXTRA_DIST += gnome-shell.in
include Makefile-big.am
include Makefile-gdmuser.am
include Makefile-st.am
include Makefile-tray.am
gnome_shell_cflags = \
@ -150,14 +151,15 @@ libgnome_shell_la_LIBADD = \
$(MUTTER_PLUGIN_LIBS) \
$(LIBGNOMEUI_LIBS) \
libbig-1.0.la \
libst-1.0.la \
libgdmuser-1.0.la \
libtray.la
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
typelibdir = $(pkglibdir)
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Shell \
--nsversion=0.1 \
@ -167,6 +169,7 @@ Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
--libtool="$(LIBTOOL)" \
--add-include-path=$(builddir) \
--include=Big-1.0 \
--include=St-1.0 \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
$(addprefix $(srcdir)/,$(libgnome_shell_la_gir_sources)) \
@ -177,14 +180,14 @@ CLEANFILES += Shell-0.1.gir
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
# (not the fake library, since we've already done the rewriting)
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
$(AM_V_GEN) \
$(G_IR_COMPILER) \
--includedir=. \
--includedir=$(MUTTER_LIB_DIR)/mutter/ \
Shell-0.1.gir -o $@
CLEANFILES += Shell-0.1.typelib
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)/big-enum-types.h Makefile
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Big \
--nsversion=1.0 \
@ -201,6 +204,28 @@ Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)
CLEANFILES += Big-1.0.gir
Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
$(G_IR_COMPILER) Big-1.0.gir -o $@
$(AM_V_GEN) $(G_IR_COMPILER) Big-1.0.gir -o $@
CLEANFILES += Big-1.0.typelib
St-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libst-1.0.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=St \
--nsversion=1.0 \
--include=Clutter-1.0 \
--add-include-path=$(builddir) \
--libtool="$(LIBTOOL)" \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
-DST_COMPILATION \
$(addprefix $(srcdir)/,$(st_source_h)) \
$(addprefix $(srcdir)/,$(st_source_c)) \
$(srcdir)/st-enum-types.h \
$(ST_CFLAGS) \
-o $@
CLEANFILES += St-1.0.gir
St-1.0.typelib: St-1.0.gir
$(AM_V_GEN) $(G_IR_COMPILER) \
$< -o $@
CLEANFILES += St-1.0.typelib

791
src/st/st-bin.c Normal file
View File

@ -0,0 +1,791 @@
/*
* st-bin.c: Basic container actor
*
* Copyright (c) 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Emmanuele Bassi <ebassi@linux.intel.com>
*
*/
/**
* SECTION:st-bin
* @short_description: a simple container with one actor
*
* #StBin is a simple container capable of having only one
* #ClutterActor as a child.
*
* #StBin inherits from #StWidget, so it is fully themable.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <clutter/clutter.h>
#include "st-bin.h"
#include "st-enum-types.h"
#include "st-private.h"
#include "st-stylable.h"
#define ST_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BIN, StBinPrivate))
struct _StBinPrivate
{
ClutterActor *child;
StAlign x_align;
StAlign y_align;
guint x_fill : 1;
guint y_fill : 1;
};
enum
{
PROP_0,
PROP_CHILD,
PROP_X_ALIGN,
PROP_Y_ALIGN,
PROP_X_FILL,
PROP_Y_FILL
};
static void clutter_container_iface_init (ClutterContainerIface *iface);
G_DEFINE_TYPE_WITH_CODE (StBin, st_bin, ST_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
clutter_container_iface_init));
void
_st_bin_get_align_factors (StBin *bin,
gdouble *x_align,
gdouble *y_align)
{
StBinPrivate *priv = bin->priv;
gdouble factor;
switch (priv->x_align)
{
case ST_ALIGN_START:
factor = 0.0;
break;
case ST_ALIGN_MIDDLE:
factor = 0.5;
break;
case ST_ALIGN_END:
factor = 1.0;
break;
default:
factor = 0.0;
break;
}
if (x_align)
*x_align = factor;
switch (priv->y_align)
{
case ST_ALIGN_START:
factor = 0.0;
break;
case ST_ALIGN_MIDDLE:
factor = 0.5;
break;
case ST_ALIGN_END:
factor = 1.0;
break;
default:
factor = 0.0;
break;
}
if (y_align)
*y_align = factor;
}
static void
st_bin_add (ClutterContainer *container,
ClutterActor *actor)
{
st_bin_set_child (ST_BIN (container), actor);
}
static void
st_bin_remove (ClutterContainer *container,
ClutterActor *actor)
{
StBinPrivate *priv = ST_BIN (container)->priv;
if (priv->child == actor)
st_bin_set_child (ST_BIN (container), NULL);
}
static void
st_bin_foreach (ClutterContainer *container,
ClutterCallback callback,
gpointer user_data)
{
StBinPrivate *priv = ST_BIN (container)->priv;
if (priv->child)
callback (priv->child, user_data);
}
static void
clutter_container_iface_init (ClutterContainerIface *iface)
{
iface->add = st_bin_add;
iface->remove = st_bin_remove;
iface->foreach = st_bin_foreach;
}
static void
st_bin_paint (ClutterActor *self)
{
StBinPrivate *priv = ST_BIN (self)->priv;
/* allow StWidget to paint the background */
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->paint (self);
/* the pain our child */
if (priv->child)
clutter_actor_paint (priv->child);
}
static void
st_bin_pick (ClutterActor *self,
const ClutterColor *pick_color)
{
StBinPrivate *priv = ST_BIN (self)->priv;
/* get the default pick implementation */
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->pick (self, pick_color);
if (priv->child)
clutter_actor_paint (priv->child);
}
static void
st_bin_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StBinPrivate *priv = ST_BIN (self)->priv;
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->allocate (self, box,
flags);
if (priv->child)
{
gfloat natural_width, natural_height;
gfloat min_width, min_height;
gfloat child_width, child_height;
gfloat available_width, available_height;
ClutterRequestMode request;
ClutterActorBox allocation = { 0, };
StPadding padding = { 0, };
gdouble x_align, y_align;
_st_bin_get_align_factors (ST_BIN (self), &x_align, &y_align);
st_widget_get_padding (ST_WIDGET (self), &padding);
available_width = box->x2 - box->x1
- padding.left - padding.right;
available_height = box->y2 - box->y1
- padding.top - padding.bottom;
if (available_width < 0)
available_width = 0;
if (available_height < 0)
available_height = 0;
if (priv->x_fill)
{
allocation.x1 = (int) padding.left;
allocation.x2 = (int)(allocation.x1 + available_width);
}
if (priv->y_fill)
{
allocation.y1 = (int) padding.top;
allocation.y2 = (int)(allocation.y1 + available_height);
}
/* if we are filling horizontally and vertically then we're done */
if (priv->x_fill && priv->y_fill)
{
clutter_actor_allocate (priv->child, &allocation, flags);
return;
}
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
g_object_get (G_OBJECT (priv->child), "request-mode", &request, NULL);
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
{
clutter_actor_get_preferred_width (priv->child, available_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
clutter_actor_get_preferred_height (priv->child, child_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
}
else
{
clutter_actor_get_preferred_height (priv->child, available_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
clutter_actor_get_preferred_width (priv->child, child_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
}
if (!priv->x_fill)
{
allocation.x1 = (int)((available_width - child_width) * x_align
+ padding.left);
allocation.x2 = allocation.x1 + child_width;
}
if (!priv->y_fill)
{
allocation.y1 = (int)((available_height - child_height) * y_align
+ padding.top);
allocation.y2 = allocation.y1 + child_height;
}
clutter_actor_allocate (priv->child, &allocation, flags);
}
}
static void
st_bin_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StBinPrivate *priv = ST_BIN (self)->priv;
gfloat min_width, natural_width;
StPadding padding = { 0, };
st_widget_get_padding (ST_WIDGET (self), &padding);
min_width = natural_width = padding.left + padding.right;
if (priv->child == NULL)
{
if (min_width_p)
*min_width_p = min_width;
if (natural_width_p)
*natural_width_p = natural_width;
}
else
{
clutter_actor_get_preferred_width (priv->child, for_height,
min_width_p,
natural_width_p);
if (min_width_p)
*min_width_p += min_width;
if (natural_width_p)
*natural_width_p += natural_width;
}
}
static void
st_bin_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StBinPrivate *priv = ST_BIN (self)->priv;
gfloat min_height, natural_height;
StPadding padding = { 0, };
st_widget_get_padding (ST_WIDGET (self), &padding);
min_height = natural_height = padding.top + padding.bottom;
if (priv->child == NULL)
{
if (min_height_p)
*min_height_p = min_height;
if (natural_height_p)
*natural_height_p = natural_height;
}
else
{
clutter_actor_get_preferred_height (priv->child, for_width,
min_height_p,
natural_height_p);
if (min_height_p)
*min_height_p += min_height;
if (natural_height_p)
*natural_height_p += natural_height;
}
}
static void
st_bin_dispose (GObject *gobject)
{
StBinPrivate *priv = ST_BIN (gobject)->priv;
if (priv->child)
{
clutter_actor_unparent (priv->child);
priv->child = NULL;
}
G_OBJECT_CLASS (st_bin_parent_class)->dispose (gobject);
}
static void
st_bin_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StBin *bin = ST_BIN (gobject);
switch (prop_id)
{
case PROP_CHILD:
st_bin_set_child (bin, g_value_get_object (value));
break;
case PROP_X_ALIGN:
st_bin_set_alignment (bin,
g_value_get_enum (value),
bin->priv->y_align);
break;
case PROP_Y_ALIGN:
st_bin_set_alignment (bin,
bin->priv->x_align,
g_value_get_enum (value));
break;
case PROP_X_FILL:
st_bin_set_fill (bin,
g_value_get_boolean (value),
bin->priv->y_fill);
break;
case PROP_Y_FILL:
st_bin_set_fill (bin,
bin->priv->y_fill,
g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
st_bin_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StBinPrivate *priv = ST_BIN (gobject)->priv;
switch (prop_id)
{
case PROP_CHILD:
g_value_set_object (value, priv->child);
break;
case PROP_X_FILL:
g_value_set_boolean (value, priv->x_fill);
break;
case PROP_Y_FILL:
g_value_set_boolean (value, priv->y_fill);
break;
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;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
st_bin_class_init (StBinClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StBinPrivate));
gobject_class->set_property = st_bin_set_property;
gobject_class->get_property = st_bin_get_property;
gobject_class->dispose = st_bin_dispose;
actor_class->get_preferred_width = st_bin_get_preferred_width;
actor_class->get_preferred_height = st_bin_get_preferred_height;
actor_class->allocate = st_bin_allocate;
actor_class->paint = st_bin_paint;
actor_class->pick = st_bin_pick;
/**
* StBin:child:
*
* The child #ClutterActor of the #StBin container.
*/
pspec = g_param_spec_object ("child",
"Child",
"The child of the Bin",
CLUTTER_TYPE_ACTOR,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
/**
* StBin:x-align:
*
* The horizontal alignment of the #StBin child.
*/
pspec = g_param_spec_enum ("x-align",
"X Align",
"The horizontal alignment",
ST_TYPE_ALIGN,
ST_ALIGN_MIDDLE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
/**
* StBin:y-align:
*
* The vertical alignment of the #StBin child.
*/
pspec = g_param_spec_enum ("y-align",
"Y Align",
"The vertical alignment",
ST_TYPE_ALIGN,
ST_ALIGN_MIDDLE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
/**
* StBin:x-fill:
*
* Whether the child should fill the horizontal allocation
*/
pspec = g_param_spec_boolean ("x-fill",
"X Fill",
"Whether the child should fill the "
"horizontal allocation",
FALSE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_X_FILL, pspec);
/**
* StBin:y-fill:
*
* Whether the child should fill the vertical allocation
*/
pspec = g_param_spec_boolean ("y-fill",
"Y Fill",
"Whether the child should fill the "
"vertical allocation",
FALSE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec);
}
static void
st_bin_init (StBin *bin)
{
bin->priv = ST_BIN_GET_PRIVATE (bin);
bin->priv->x_align = ST_ALIGN_MIDDLE;
bin->priv->y_align = ST_ALIGN_MIDDLE;
}
/**
* st_bin_new:
*
* Creates a new #StBin, a simple container for one child.
*
* Return value: the newly created #StBin actor
*/
StWidget *
st_bin_new (void)
{
return g_object_new (ST_TYPE_BIN, NULL);
}
/**
* st_bin_set_child:
* @bin: a #StBin
* @child: a #ClutterActor, or %NULL
*
* Sets @child as the child of @bin.
*
* If @bin already has a child, the previous child is removed.
*/
void
st_bin_set_child (StBin *bin,
ClutterActor *child)
{
StBinPrivate *priv;
g_return_if_fail (ST_IS_BIN (bin));
g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
priv = bin->priv;
if (priv->child == child)
return;
if (priv->child)
{
ClutterActor *old_child = priv->child;
g_object_ref (old_child);
priv->child = NULL;
clutter_actor_unparent (old_child);
g_signal_emit_by_name (bin, "actor-removed", old_child);
g_object_unref (old_child);
}
if (child)
{
priv->child = child;
clutter_actor_set_parent (child, CLUTTER_ACTOR (bin));
g_signal_emit_by_name (bin, "actor-added", priv->child);
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
g_object_notify (G_OBJECT (bin), "child");
}
/**
* st_bin_get_child:
* @bin: a #StBin
*
* Retrieves a pointer to the child of @bin.
*
* Return value: a #ClutterActor, or %NULL
*/
ClutterActor *
st_bin_get_child (StBin *bin)
{
g_return_val_if_fail (ST_IS_BIN (bin), NULL);
return bin->priv->child;
}
/**
* st_bin_set_alignment:
* @bin: a #StBin
* @x_align: horizontal alignment
* @y_align: vertical alignment
*
* Sets the horizontal and vertical alignment of the child
* inside a #StBin.
*/
void
st_bin_set_alignment (StBin *bin,
StAlign x_align,
StAlign y_align)
{
StBinPrivate *priv;
gboolean changed = FALSE;
g_return_if_fail (ST_IS_BIN (bin));
priv = bin->priv;
g_object_freeze_notify (G_OBJECT (bin));
if (priv->x_align != x_align)
{
priv->x_align = x_align;
g_object_notify (G_OBJECT (bin), "x-align");
changed = TRUE;
}
if (priv->y_align != y_align)
{
priv->y_align = y_align;
g_object_notify (G_OBJECT (bin), "y-align");
changed = TRUE;
}
if (changed)
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
g_object_thaw_notify (G_OBJECT (bin));
}
/**
* st_bin_get_alignment:
* @bin: a #StBin
* @x_align: return location for the horizontal alignment, or %NULL
* @y_align: return location for the vertical alignment, or %NULL
*
* Retrieves the horizontal and vertical alignment of the child
* inside a #StBin, as set by st_bin_set_alignment().
*/
void
st_bin_get_alignment (StBin *bin,
StAlign *x_align,
StAlign *y_align)
{
StBinPrivate *priv;
g_return_if_fail (ST_IS_BIN (bin));
priv = bin->priv;
if (x_align)
*x_align = priv->x_align;
if (y_align)
*y_align = priv->y_align;
}
/**
* st_bin_set_fill:
* @bin: a #StBin
* @x_fill: %TRUE if the child should fill horizontally the @bin
* @y_fill: %TRUE if the child should fill vertically the @bin
*
* Sets whether the child of @bin should fill out the horizontal
* and/or vertical allocation of the parent
*/
void
st_bin_set_fill (StBin *bin,
gboolean x_fill,
gboolean y_fill)
{
StBinPrivate *priv;
gboolean changed = FALSE;
g_return_if_fail (ST_IS_BIN (bin));
priv = bin->priv;
g_object_freeze_notify (G_OBJECT (bin));
if (priv->x_fill != x_fill)
{
priv->x_fill = x_fill;
changed = TRUE;
g_object_notify (G_OBJECT (bin), "x-fill");
}
if (priv->y_fill != y_fill)
{
priv->y_fill = y_fill;
changed = TRUE;
g_object_notify (G_OBJECT (bin), "y-fill");
}
if (changed)
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
g_object_thaw_notify (G_OBJECT (bin));
}
/**
* st_bin_get_fill:
* @bin: a #StBin
* @x_fill: (out): return location for the horizontal fill, or %NULL
* @y_fill: (out): return location for the vertical fill, or %NULL
*
* Retrieves the horizontal and vertical fill settings
*/
void
st_bin_get_fill (StBin *bin,
gboolean *x_fill,
gboolean *y_fill)
{
g_return_if_fail (ST_IS_BIN (bin));
if (x_fill)
*x_fill = bin->priv->x_fill;
if (y_fill)
*y_fill = bin->priv->y_fill;
}
static gpointer
st_padding_copy (gpointer data)
{
return g_slice_dup (StPadding, data);
}
static void
st_padding_free (gpointer data)
{
if (G_LIKELY (data))
g_slice_free (StPadding, data);
}
GType
st_padding_get_type (void)
{
static GType our_type = 0;
if (G_UNLIKELY (our_type == 0))
our_type = g_boxed_type_register_static (I_("StPadding"),
st_padding_copy,
st_padding_free);
return our_type;
}

92
src/st/st-bin.h Normal file
View File

@ -0,0 +1,92 @@
/*
* st-bin.h: Basic container actor
*
* Copyright 2009, 2008 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Emmanuele Bassi <ebassi@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_BIN_H__
#define __ST_BIN_H__
#include <st/st-types.h>
#include <st/st-widget.h>
G_BEGIN_DECLS
#define ST_TYPE_BIN (st_bin_get_type ())
#define ST_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BIN, StBin))
#define ST_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BIN))
#define ST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BIN, StBinClass))
#define ST_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BIN))
#define ST_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BIN, StBinClass))
typedef struct _StBin StBin;
typedef struct _StBinPrivate StBinPrivate;
typedef struct _StBinClass StBinClass;
/**
* StBin:
*
* The #StBin struct contains only private data
*/
struct _StBin
{
/*< private >*/
StWidget parent_instance;
StBinPrivate *priv;
};
/**
* StBinClass:
*
* The #StBinClass struct contains only private data
*/
struct _StBinClass
{
/*< private >*/
StWidgetClass parent_class;
};
GType st_bin_get_type (void) G_GNUC_CONST;
StWidget * st_bin_new (void);
void st_bin_set_child (StBin *bin,
ClutterActor *child);
ClutterActor *st_bin_get_child (StBin *bin);
void st_bin_set_alignment (StBin *bin,
StAlign x_align,
StAlign y_align);
void st_bin_get_alignment (StBin *bin,
StAlign *x_align,
StAlign *y_align);
void st_bin_set_fill (StBin *bin,
gboolean x_fill,
gboolean y_fill);
void st_bin_get_fill (StBin *bin,
gboolean *x_fill,
gboolean *y_fill);
G_END_DECLS
#endif /* __ST_BIN_H__ */

30
src/st/st-enum-types.c.in Normal file
View File

@ -0,0 +1,30 @@
/*** BEGIN file-header ***/
#include "st-enum-types.h"
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
#include "@filename@"
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type(void) {
static GType enum_type_id = 0;
if (G_UNLIKELY (!enum_type_id))
{
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
enum_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
}
return enum_type_id;
}
/*** END value-tail ***/

29
src/st/st-enum-types.h.in Normal file
View File

@ -0,0 +1,29 @@
/*** BEGIN file-header ***/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_ENUM_TYPES_H__
#define __ST_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 /* !__ST_ENUM_TYPES_H__ */
/*** END file-tail ***/
/*** BEGIN value-header ***/
GType @enum_name@_get_type (void) G_GNUC_CONST;
#define ST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
/*** END value-header ***/

12
src/st/st-marshal.list Normal file
View File

@ -0,0 +1,12 @@
VOID:OBJECT
VOID:VOID
VOID:PARAM
VOID:POINTER
VOID:UINT
VOID:UINT,UINT
VOID:OBJECT,OBJECT
VOID:STRING,OBJECT
VOID:OBJECT,OBJECT,INT,INT
VOID:OBJECT,FLOAT,FLOAT,INT,ENUM
VOID:FLOAT,FLOAT,INT,ENUM
VOID:FLOAT,FLOAT

111
src/st/st-private.c Normal file
View File

@ -0,0 +1,111 @@
#include "st-private.h"
/* Utility function to modify a child allocation box with respect to the
* x/y-fill child properties. Expects childbox to contain the available
* allocation space.
*/
void
_st_allocate_fill (ClutterActor *child,
ClutterActorBox *childbox,
StAlign x_alignment,
StAlign y_alignment,
gboolean x_fill,
gboolean y_fill)
{
gfloat natural_width, natural_height;
gfloat min_width, min_height;
gfloat child_width, child_height;
gfloat available_width, available_height;
ClutterRequestMode request;
ClutterActorBox allocation = { 0, };
gdouble x_align, y_align;
if (x_alignment == ST_ALIGN_START)
x_align = 0.0;
else if (x_alignment == ST_ALIGN_MIDDLE)
x_align = 0.5;
else
x_align = 1.0;
if (y_alignment == ST_ALIGN_START)
y_align = 0.0;
else if (y_alignment == ST_ALIGN_MIDDLE)
y_align = 0.5;
else
y_align = 1.0;
available_width = childbox->x2 - childbox->x1;
available_height = childbox->y2 - childbox->y1;
if (available_width < 0)
available_width = 0;
if (available_height < 0)
available_height = 0;
if (x_fill)
{
allocation.x1 = childbox->x1;
allocation.x2 = (int)(allocation.x1 + available_width);
}
if (y_fill)
{
allocation.y1 = childbox->y1;
allocation.y2 = (int)(allocation.y1 + available_height);
}
/* if we are filling horizontally and vertically then we're done */
if (x_fill && y_fill)
{
*childbox = allocation;
return;
}
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
g_object_get (G_OBJECT (child), "request-mode", &request, NULL);
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
{
clutter_actor_get_preferred_width (child, available_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
clutter_actor_get_preferred_height (child, child_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
}
else
{
clutter_actor_get_preferred_height (child, available_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
clutter_actor_get_preferred_width (child, child_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
}
if (!x_fill)
{
allocation.x1 = childbox->x1 + (int)((available_width - child_width) * x_align);
allocation.x2 = allocation.x1 + (int) child_width;
}
if (!y_fill)
{
allocation.y1 = childbox->y1 + (int)((available_height - child_height) * y_align);
allocation.y2 = allocation.y1 + (int) child_height;
}
*childbox = allocation;
}

57
src/st/st-private.h Normal file
View File

@ -0,0 +1,57 @@
/*
* st-private.h: Private declarations
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#ifndef __ST_PRIVATE_H__
#define __ST_PRIVATE_H__
#include <glib.h>
#include "st-widget.h"
#include "st-bin.h"
G_BEGIN_DECLS
#define I_(str) (g_intern_static_string ((str)))
#define ST_PARAM_READABLE \
(G_PARAM_READABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
#define ST_PARAM_READWRITE \
(G_PARAM_READABLE | G_PARAM_WRITABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
G_END_DECLS
ClutterActor *_st_widget_get_dnd_clone (StWidget *widget);
void _st_bin_get_align_factors (StBin *bin,
gdouble *x_align,
gdouble *y_align);
void _st_allocate_fill (ClutterActor *child,
ClutterActorBox *childbox,
StAlign x_align,
StAlign y_align,
gboolean x_fill,
gboolean y_fill);
#endif /* __ST_PRIVATE_H__ */

849
src/st/st-stylable.c Normal file
View File

@ -0,0 +1,849 @@
/*
* st-stylable.c: Interface for stylable objects
*
* Copyright 2008 Intel Corporation
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Emmanuele Bassi <ebassi@openedhand.com>
* Thomas Wood <thomas@linux.intel.com>
*
*/
/**
* SECTION:st-stylable
* @short_description: Interface for stylable objects
*
* Stylable objects are classes that can have "style properties", that is
* properties that can be changed by attaching a #StStyle to them.
*
* Objects can choose to subclass #StWidget, and thus inherit all the
* #StWidget style properties; or they can subclass #StWidget and
* reimplement the #StStylable interface to add new style properties
* specific for them (and their subclasses); or, finally, they can simply
* subclass #GObject and implement #StStylable to install new properties.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <glib-object.h>
#include <gobject/gvaluecollector.h>
#include <gobject/gobjectnotifyqueue.c>
#include "st-marshal.h"
#include "st-private.h"
#include "st-stylable.h"
enum
{
STYLE_CHANGED,
STYLE_NOTIFY,
CHANGED,
LAST_SIGNAL
};
static GObjectNotifyContext property_notify_context = { 0, };
static GParamSpecPool *style_property_spec_pool = NULL;
static GQuark quark_real_owner = 0;
static GQuark quark_style = 0;
static guint stylable_signals[LAST_SIGNAL] = { 0, };
static void
st_stylable_notify_dispatcher (GObject *gobject,
guint n_pspecs,
GParamSpec **pspecs)
{
guint i;
for (i = 0; i < n_pspecs; i++)
g_signal_emit (gobject, stylable_signals[STYLE_NOTIFY],
g_quark_from_string (pspecs[i]->name),
pspecs[i]);
}
static void
st_stylable_base_finalize (gpointer g_iface)
{
GList *list, *node;
list = g_param_spec_pool_list_owned (style_property_spec_pool,
G_TYPE_FROM_INTERFACE (g_iface));
for (node = list; node; node = node->next)
{
GParamSpec *pspec = node->data;
g_param_spec_pool_remove (style_property_spec_pool, pspec);
g_param_spec_unref (pspec);
}
g_list_free (list);
}
static void
st_stylable_base_init (gpointer g_iface)
{
static gboolean initialised = FALSE;
if (G_UNLIKELY (!initialised))
{
GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
initialised = TRUE;
quark_real_owner = g_quark_from_static_string ("st-stylable-real-owner-quark");
quark_style = g_quark_from_static_string ("st-stylable-style-quark");
style_property_spec_pool = g_param_spec_pool_new (FALSE);
property_notify_context.quark_notify_queue = g_quark_from_static_string ("StStylable-style-property-notify-queue");
property_notify_context.dispatcher = st_stylable_notify_dispatcher;
/**
* StStylable:style:
*
* The #StStyle attached to a stylable object.
*/
g_object_interface_install_property (g_iface,
g_param_spec_object ("style",
"Style",
"A style object",
ST_TYPE_STYLE,
ST_PARAM_READWRITE));
/**
* StStylable::style-changed:
* @stylable: the #StStylable that received the signal
* @old_style: the previously set #StStyle for @stylable
*
* The ::style-changed signal is emitted each time one of the style
* properties have changed.
*/
stylable_signals[STYLE_CHANGED] =
g_signal_new (I_("style-changed"),
iface_type,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (StStylableIface, style_changed),
NULL, NULL,
_st_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* StStylable::stylable-changed:
* @actor: the actor that received the signal
*
* The ::changed signal is emitted each time any of the properties of the
* stylable has changed.
*/
stylable_signals[CHANGED] =
g_signal_new (I_("stylable-changed"),
iface_type,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StStylableIface, stylable_changed),
NULL, NULL,
_st_marshal_VOID__VOID,
G_TYPE_NONE, 0);
stylable_signals[STYLE_NOTIFY] =
g_signal_new (I_("style-notify"),
iface_type,
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (StStylableIface, style_notify),
NULL, NULL,
_st_marshal_VOID__PARAM,
G_TYPE_NONE, 1,
G_TYPE_PARAM);
}
}
GType
st_stylable_get_type (void)
{
static GType our_type = 0;
if (G_UNLIKELY (our_type == 0))
{
GTypeInfo stylable_info = {
sizeof (StStylableIface),
st_stylable_base_init,
st_stylable_base_finalize
};
our_type = g_type_register_static (G_TYPE_INTERFACE,
I_("StStylable"),
&stylable_info, 0);
}
return our_type;
}
void
st_stylable_freeze_notify (StStylable *stylable)
{
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_object_ref (stylable);
g_object_notify_queue_freeze (G_OBJECT (stylable), &property_notify_context);
g_object_unref (stylable);
}
void
st_stylable_thaw_notify (StStylable *stylable)
{
GObjectNotifyQueue *nqueue;
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_object_ref (stylable);
nqueue = g_object_notify_queue_from_object (G_OBJECT (stylable),
&property_notify_context);
if (!nqueue || !nqueue->freeze_count)
g_warning ("%s: property-changed notification for %s(%p) is not frozen",
G_STRFUNC, G_OBJECT_TYPE_NAME (stylable), stylable);
else
g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
g_object_unref (stylable);
}
void
st_stylable_notify (StStylable *stylable,
const gchar *property_name)
{
GParamSpec *pspec;
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_return_if_fail (property_name != NULL);
g_object_ref (stylable);
pspec = g_param_spec_pool_lookup (style_property_spec_pool,
property_name,
G_OBJECT_TYPE (stylable),
TRUE);
if (!pspec)
g_warning ("%s: object class `%s' has no style property named `%s'",
G_STRFUNC,
G_OBJECT_TYPE_NAME (stylable),
property_name);
else
{
GObjectNotifyQueue *nqueue;
nqueue = g_object_notify_queue_freeze (G_OBJECT (stylable),
&property_notify_context);
g_object_notify_queue_add (G_OBJECT (stylable), nqueue, pspec);
g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
}
g_object_unref (stylable);
}
/**
* st_stylable_iface_install_property:
* @iface: a #StStylableIface
* @owner_type: #GType of the style property owner
* @pspec: a #GParamSpec
*
* Installs a property for @owner_type using @pspec as the property
* description.
*
* This function should be used inside the #StStylableIface initialization
* function of a class, for instance:
*
* <informalexample><programlisting>
* G_DEFINE_TYPE_WITH_CODE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR,
* G_IMPLEMENT_INTERFACE (ST_TYPE_STYLABLE,
* st_stylable_init));
* ...
* static void
* st_stylable_init (StStylableIface *iface)
* {
* static gboolean is_initialized = FALSE;
*
* if (!is_initialized)
* {
* ...
* st_stylable_iface_install_property (stylable,
* FOO_TYPE_ACTOR,
* g_param_spec_int ("x-spacing",
* "X Spacing",
* "Horizontal spacing",
* -1, G_MAXINT,
* 2,
* G_PARAM_READWRITE));
* ...
* }
* }
* </programlisting></informalexample>
*/
void
st_stylable_iface_install_property (StStylableIface *iface,
GType owner_type,
GParamSpec *pspec)
{
g_return_if_fail (ST_IS_STYLABLE_IFACE (iface));
g_return_if_fail (owner_type != G_TYPE_INVALID);
g_return_if_fail (G_IS_PARAM_SPEC (pspec));
g_return_if_fail (pspec->flags & G_PARAM_READABLE);
g_return_if_fail (!(pspec->flags & (G_PARAM_CONSTRUCT_ONLY | G_PARAM_CONSTRUCT
)));
if (g_param_spec_pool_lookup (style_property_spec_pool, pspec->name,
owner_type,
FALSE))
{
g_warning ("%s: class `%s' already contains a style property named `%s'",
G_STRLOC,
g_type_name (owner_type),
pspec->name);
return;
}
g_param_spec_ref_sink (pspec);
g_param_spec_set_qdata_full (pspec, quark_real_owner,
g_strdup (g_type_name (owner_type)),
g_free);
g_param_spec_pool_insert (style_property_spec_pool,
pspec,
owner_type);
}
/**
* st_stylable_list_properties:
* @stylable: a #StStylable
* @n_props: return location for the number of properties, or %NULL
*
* Retrieves all the #GParamSpec<!-- -->s installed by @stylable.
*
* Return value: an array of #GParamSpec<!-- -->s. Free it with
* g_free() when done.
*/
GParamSpec **
st_stylable_list_properties (StStylable *stylable,
guint *n_props)
{
GParamSpec **pspecs = NULL;
guint n;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
pspecs = g_param_spec_pool_list (style_property_spec_pool,
G_OBJECT_TYPE (stylable),
&n);
if (n_props)
*n_props = n;
return pspecs;
}
/**
* st_stylable_find_property:
* @stylable: a #StStylable
* @property_name: the name of the property to find
*
* Finds the #GParamSpec installed by @stylable for the property
* with @property_name.
*
* Return value: a #GParamSpec for the given property, or %NULL if
* no property with that name was found
*/
GParamSpec *
st_stylable_find_property (StStylable *stylable,
const gchar *property_name)
{
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
g_return_val_if_fail (property_name != NULL, NULL);
return g_param_spec_pool_lookup (style_property_spec_pool,
property_name,
G_OBJECT_TYPE (stylable),
TRUE);
}
static inline void
st_stylable_get_property_internal (StStylable *stylable,
GParamSpec *pspec,
GValue *value)
{
StStyle *style;
GValue real_value = { 0, };
style = st_stylable_get_style (stylable);
if (!style)
{
g_value_reset (value);
return;
}
st_style_get_property (style, stylable, pspec, &real_value);
g_value_copy (&real_value, value);
g_value_unset (&real_value);
}
/**
* st_stylable_get_property:
* @stylable: a #StStylable
* @property_name: the name of the property
* @value: return location for an empty #GValue
*
* Retrieves the value of @property_name for @stylable, and puts it
* into @value.
*/
void
st_stylable_get_property (StStylable *stylable,
const gchar *property_name,
GValue *value)
{
GParamSpec *pspec;
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_return_if_fail (property_name != NULL);
g_return_if_fail (value != NULL);
pspec = st_stylable_find_property (stylable, property_name);
if (!pspec)
{
g_warning ("Stylable class `%s' doesn't have a property named `%s'",
g_type_name (G_OBJECT_TYPE (stylable)),
property_name);
return;
}
if (!(pspec->flags & G_PARAM_READABLE))
{
g_warning ("Style property `%s' of class `%s' is not readable",
pspec->name,
g_type_name (G_OBJECT_TYPE (stylable)));
return;
}
if (G_VALUE_TYPE (value) != G_PARAM_SPEC_VALUE_TYPE (pspec))
{
g_warning ("Passed value is not of the requested type `%s' for "
"the style property `%s' of class `%s'",
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
pspec->name,
g_type_name (G_OBJECT_TYPE (stylable)));
return;
}
st_stylable_get_property_internal (stylable, pspec, value);
}
/**
* st_stylable_get:
* @stylable: a #StStylable
* @first_property_name: name of the first property to get
* @Varargs: return location for the first property, followed optionally
* by more name/return location pairs, followed by %NULL
*
* Gets the style properties for @stylable.
*
* In general, a copy is made of the property contents and the called
* is responsible for freeing the memory in the appropriate manner for
* the property type.
*
* <example>
* <title>Using st_stylable_get(<!-- -->)</title>
* <para>An example of using st_stylable_get() to get the contents of
* two style properties - one of type #G_TYPE_INT and one of type
* #CLUTTER_TYPE_COLOR:</para>
* <programlisting>
* gint x_spacing;
* ClutterColor *bg_color;
*
* st_stylable_get (stylable,
* "x-spacing", &amp;x_spacing,
* "bg-color", &amp;bg_color,
* NULL);
*
* /<!-- -->* do something with x_spacing and bg_color *<!-- -->/
*
* clutter_color_free (bg_color);
* </programlisting>
* </example>
*/
void
st_stylable_get (StStylable *stylable,
const gchar *first_property_name,
...)
{
StStyle *style;
va_list args;
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_return_if_fail (first_property_name != NULL);
style = st_stylable_get_style (stylable);
va_start (args, first_property_name);
st_style_get_valist (style, stylable, first_property_name, args);
va_end (args);
}
/**
* st_stylable_get_default_value:
* @stylable: a #StStylable
* @property_name: name of the property to query
* @value_out: return location for the default value
*
* Query @stylable for the default value of property @property_name and
* fill @value_out with the result.
*
* Returns: %TRUE if property @property_name exists and the default value has
* been returned.
*/
gboolean
st_stylable_get_default_value (StStylable *stylable,
const gchar *property_name,
GValue *value_out)
{
GParamSpec *pspec;
pspec = st_stylable_find_property (stylable, property_name);
if (!pspec)
{
g_warning ("%s: no style property named `%s' found for class `%s'",
G_STRLOC,
property_name,
g_type_name (G_OBJECT_TYPE (stylable)));
return FALSE;
}
if (!(pspec->flags & G_PARAM_READABLE))
{
g_warning ("Style property `%s' of class `%s' is not readable",
pspec->name,
g_type_name (G_OBJECT_TYPE (stylable)));
return FALSE;
}
g_value_init (value_out, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_param_value_set_default (pspec, value_out);
return TRUE;
}
/**
* st_stylable_get_style:
* @stylable: a #StStylable
*
* Retrieves the #StStyle used by @stylable. This function does not
* alter the reference count of the returned object.
*
* Return value: a #StStyle
*/
StStyle *
st_stylable_get_style (StStylable *stylable)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_style)
return iface->get_style (stylable);
return g_object_get_data (G_OBJECT (stylable), "st-stylable-style");
}
/**
* st_stylable_set_style:
* @stylable: a #StStylable
* @style: a #StStyle
*
* Sets @style as the new #StStyle to be used by @stylable.
*
* The #StStylable will take ownership of the passed #StStyle.
*
* After the #StStle has been set, the StStylable::style-set signal
* will be emitted.
*/
void
st_stylable_set_style (StStylable *stylable,
StStyle *style)
{
StStylableIface *iface;
StStyle *old_style;
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_return_if_fail (ST_IS_STYLE (style));
iface = ST_STYLABLE_GET_IFACE (stylable);
old_style = st_stylable_get_style (stylable);
g_object_ref (old_style);
if (iface->set_style)
iface->set_style (stylable, style);
else
{
g_object_set_qdata_full (G_OBJECT (stylable),
quark_style,
g_object_ref_sink (style),
g_object_unref);
}
g_signal_emit (stylable, stylable_signals[STYLE_CHANGED], 0, old_style);
g_object_unref (old_style);
g_object_notify (G_OBJECT (stylable), "style");
}
/**
* st_stylable_get_container:
* @stylable: a #StStylable
*
* Obtain the parent #StStylable that contains @stylable.
*
* Return value: The parent #StStylable
*/
StStylable*
st_stylable_get_container (StStylable *stylable)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_container)
return iface->get_container (stylable);
else
return NULL;
}
/**
* st_stylable_get_base_style:
* @stylable: a #StStylable
*
* Get the parent ancestor #StStylable of @stylable.
*
* Return value: the parent #StStylable
*/
StStylable*
st_stylable_get_base_style (StStylable *stylable)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_base_style)
return iface->get_base_style (stylable);
else
return NULL;
}
/**
* st_stylable_get_style_id:
* @stylable: a #StStylable
*
* Get the ID value of @stylable
*
* Return value: the id of @stylable
*/
const gchar*
st_stylable_get_style_id (StStylable *stylable)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_style_id)
return iface->get_style_id (stylable);
else
return NULL;
}
/**
* st_stylable_get_style_type:
* @stylable: a #StStylable
*
* Get the type name of @stylable
*
* Return value: the type name of @stylable
*/
const gchar*
st_stylable_get_style_type (StStylable *stylable)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_style_type)
return iface->get_style_type (stylable);
else
return G_OBJECT_TYPE_NAME (stylable);
}
/**
* st_stylable_get_style_class:
* @stylable: a #StStylable
*
* Get the style class name of @stylable
*
* Return value: the type name of @stylable
*/
const gchar*
st_stylable_get_style_class (StStylable *stylable)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_style_class)
return iface->get_style_class (stylable);
else
return NULL;
}
/**
* st_stylable_get_pseudo_class:
* @stylable: a #StStylable
*
* Get the pseudo class name of @stylable
*
* Return value: the pseudo class name of @stylable
*/
const gchar*
st_stylable_get_pseudo_class (StStylable *stylable)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_pseudo_class)
return iface->get_pseudo_class (stylable);
else
return NULL;
}
/**
* st_stylable_get_attribute:
* @stylable: a #StStylable
* @name: attribute name
*
* Get the named attribute from @stylable
*
* Return value: the value of the attribute
*/
gchar*
st_stylable_get_attribute (StStylable *stylable,
const gchar *name)
{
StStylableIface *iface;
GValue value = { 0, };
GValue string_value = { 0, };
gchar *ret;
GParamSpec *pspec;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), NULL);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_attribute)
return iface->get_attribute (stylable, name);
/* look up a generic gobject property */
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (stylable), name);
/* if no such property exists, return NULL */
if (pspec == NULL)
return NULL;
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_object_get_property (G_OBJECT (stylable), name, &value);
g_value_init (&string_value, G_TYPE_STRING);
if (g_value_transform (&value, &string_value))
ret = g_strdup (g_value_get_string (&string_value));
else
ret = NULL;
g_value_unset (&value);
g_value_unset (&string_value);
return ret;
}
/**
* st_stylable_get_viewport:
* @stylable: a #StStylable
* @x: location to store X coordinate
* @y: location to store Y coordinate
* @width: location to store width
* @height: location to store height
*
* Obtain the position and dimensions of @stylable.
*
* Return value: true if the function succeeded
*/
gboolean
st_stylable_get_viewport (StStylable *stylable,
gint *x,
gint *y,
gint *width,
gint *height)
{
StStylableIface *iface;
g_return_val_if_fail (ST_IS_STYLABLE (stylable), FALSE);
iface = ST_STYLABLE_GET_IFACE (stylable);
if (iface->get_viewport)
return iface->get_viewport (stylable, x, y, width, height);
else
return FALSE;
}
/**
* st_stylable_changed:
* @stylable: A #StStylable
*
* Emit the "stylable-changed" signal on @stylable
*/
void
st_stylable_changed (StStylable *stylable)
{
g_signal_emit (stylable, stylable_signals[CHANGED], 0, NULL);
}

124
src/st/st-stylable.h Normal file
View File

@ -0,0 +1,124 @@
/*
* st-stylable.h: Interface for stylable objects
*
* Copyright 2008, 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Emmanuele Bassi <ebassi@openedhand.com>
* Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_STYLABLE_H__
#define __ST_STYLABLE_H__
#include <glib-object.h>
#include <st/st-style.h>
G_BEGIN_DECLS
#define ST_TYPE_STYLABLE (st_stylable_get_type ())
#define ST_STYLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_STYLABLE, StStylable))
#define ST_IS_STYLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_STYLABLE))
#define ST_STYLABLE_IFACE(iface) (G_TYPE_CHECK_CLASS_CAST ((iface), ST_TYPE_STYLABLE, StStylableIface))
#define ST_IS_STYLABLE_IFACE(iface) (G_TYPE_CHECK_CLASS_TYPE ((iface), ST_TYPE_STYLABLE))
#define ST_STYLABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), ST_TYPE_STYLABLE, StStylableIface))
/* StStylableIface is defined in st-style.h */
struct _StStylableIface
{
GTypeInterface g_iface;
/* virtual functions */
StStyle * (* get_style) (StStylable *stylable);
void (* set_style) (StStylable *stylable,
StStyle *style);
/* context virtual functions */
StStylable *(*get_container) (StStylable *stylable);
StStylable *(*get_base_style) (StStylable *stylable);
const gchar *(*get_style_id) (StStylable *stylable);
const gchar *(*get_style_type) (StStylable *stylable);
const gchar *(*get_style_class) (StStylable *stylable);
const gchar *(*get_pseudo_class) (StStylable *stylable);
gchar *(*get_attribute) (StStylable *stylable,
const gchar *name);
gboolean (*get_viewport) (StStylable *stylable,
gint *x,
gint *y,
gint *width,
gint *height);
/* signals, not vfuncs */
void (* style_notify) (StStylable *stylable,
GParamSpec *pspec);
void (* style_changed) (StStylable *stylable);
void (* stylable_changed) (StStylable *stylable);
};
GType st_stylable_get_type (void) G_GNUC_CONST;
void st_stylable_iface_install_property (StStylableIface *iface,
GType owner_type,
GParamSpec *pspec);
void st_stylable_freeze_notify (StStylable *stylable);
void st_stylable_notify (StStylable *stylable,
const gchar *property_name);
void st_stylable_thaw_notify (StStylable *stylable);
GParamSpec **st_stylable_list_properties (StStylable *stylable,
guint *n_props);
GParamSpec * st_stylable_find_property (StStylable *stylable,
const gchar *property_name);
void st_stylable_set_style (StStylable *stylable,
StStyle *style);
StStyle * st_stylable_get_style (StStylable *stylable);
void st_stylable_get (StStylable *stylable,
const gchar *first_property_name,
...) G_GNUC_NULL_TERMINATED;
void st_stylable_get_property (StStylable *stylable,
const gchar *property_name,
GValue *value);
gboolean st_stylable_get_default_value (StStylable *stylable,
const gchar *property_name,
GValue *value_out);
StStylable* st_stylable_get_container (StStylable *stylable);
StStylable* st_stylable_get_base_style (StStylable *stylable);
const gchar* st_stylable_get_style_id (StStylable *stylable);
const gchar* st_stylable_get_style_type (StStylable *stylable);
const gchar* st_stylable_get_style_class (StStylable *stylable);
const gchar* st_stylable_get_pseudo_class (StStylable *stylable);
gchar* st_stylable_get_attribute (StStylable *stylable,
const gchar *name);
gboolean st_stylable_get_viewport (StStylable *stylable,
gint *x,
gint *y,
gint *width,
gint *height);
void st_stylable_changed (StStylable *stylable);
G_END_DECLS
#endif /* __ST_STYLABLE_H__ */

780
src/st/st-style.c Normal file
View File

@ -0,0 +1,780 @@
/*
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/**
* SECTION:st-style
* @short_description: a data store for style properties
*
* #StStyle is a property data store that can read properties from a style
* sheet. It is queried with objects that implement the StStylable
* interface.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <glib-object.h>
#include <gobject/gvaluecollector.h>
#include <glib/gi18n-lib.h>
#include <clutter/clutter.h>
#include <ccss/ccss.h>
#include "st-stylable.h"
#include "st-style.h"
#include "st-types.h"
#include "st-marshal.h"
#include "st-widget.h"
enum
{
CHANGED,
LAST_SIGNAL
};
#define ST_STYLE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_STYLE, StStylePrivate))
#define ST_STYLE_ERROR g_style_error_quark ()
typedef struct {
GType value_type;
gchar *value_name;
GValue value;
} StyleProperty;
struct _StStylePrivate
{
ccss_stylesheet_t *stylesheet;
GList *image_paths;
GHashTable *style_hash;
GHashTable *node_hash;
};
typedef struct {
ccss_node_t parent;
StStylable *stylable;
StStylableIface *iface;
} st_style_node_t;
static ccss_function_t const * peek_css_functions (void);
static ccss_node_class_t * peek_node_class (void);
static guint style_signals[LAST_SIGNAL] = { 0, };
static StStyle *default_style = NULL;
G_DEFINE_TYPE (StStyle, st_style, G_TYPE_OBJECT);
static GQuark
g_style_error_quark (void)
{
return g_quark_from_static_string ("st-style-error-quark");
}
static gboolean
st_style_real_load_from_file (StStyle *style,
const gchar *filename,
GError **error,
gint priority)
{
StStylePrivate *priv;
ccss_grammar_t *grammar;
GError *internal_error;
gchar *path;
GList *l;
g_return_val_if_fail (ST_IS_STYLE (style), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
priv = ST_STYLE (style)->priv;
if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
{
internal_error = g_error_new (ST_STYLE_ERROR,
ST_STYLE_ERROR_INVALID_FILE,
_("Invalid theme file '%s'"), filename);
g_propagate_error (error, internal_error);
return FALSE;
}
/* add the path of the stylesheet to the search path */
path = g_path_get_dirname (filename);
/* make sure path is valid */
if (!path)
return TRUE;
for (l = priv->image_paths; l; l = l->next)
{
if (g_str_equal ((gchar *) l->data, path))
{
/* we have this path already */
g_free (path);
path = NULL;
}
}
/* Add the new path */
if (path)
priv->image_paths = g_list_append (priv->image_paths, path);
/* now load the stylesheet */
if (!priv->stylesheet)
{
grammar = ccss_grammar_create_css ();
ccss_grammar_add_functions (grammar, peek_css_functions ());
priv->stylesheet = ccss_grammar_create_stylesheet_from_file (grammar,
filename,
path);
ccss_grammar_destroy (grammar);
}
else
{
ccss_stylesheet_add_from_file (priv->stylesheet, filename, priority, path);
}
g_signal_emit (style, style_signals[CHANGED], 0, NULL);
return TRUE;
}
/**
* st_style_load_from_file:
* @style: a #StStyle
* @filename: filename of the style sheet to load
* @error: a #GError or #NULL
*
* Load style information from the specified file.
*
* returns: TRUE if the style information was loaded successfully. Returns
* FALSE on error.
*/
gboolean
st_style_load_from_file (StStyle *style,
const gchar *filename,
GError **error)
{
return st_style_real_load_from_file (style, filename, error,
CCSS_STYLESHEET_AUTHOR);
}
static void
st_style_load (StStyle *style)
{
const gchar *env_var;
gchar *rc_file = NULL;
GError *error;
env_var = g_getenv ("ST_RC_FILE");
if (env_var && *env_var)
rc_file = g_strdup (env_var);
if (!rc_file)
rc_file = g_build_filename (PACKAGE_DATA_DIR,
"st",
"style",
"default.css",
NULL);
error = NULL;
if (g_file_test (rc_file, G_FILE_TEST_EXISTS))
{
/* load the default theme with lowest priority */
if (!st_style_real_load_from_file (style, rc_file, &error, CCSS_STYLESHEET_USER_AGENT))
{
g_critical ("Unable to load resource file '%s': %s",
rc_file,
error->message);
g_error_free (error);
}
}
g_free (rc_file);
}
static void
st_style_finalize (GObject *gobject)
{
StStylePrivate *priv = ((StStyle *) gobject)->priv;
GList *l;
for (l = priv->image_paths; l; l = g_list_delete_link (l, l))
{
g_free (l->data);
}
G_OBJECT_CLASS (st_style_parent_class)->finalize (gobject);
}
static void
st_style_class_init (StStyleClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (StStylePrivate));
gobject_class->finalize = st_style_finalize;
/**
* StStyle::changed:
*
* Indicates that the style data has changed in some way. For example, a new
* stylesheet may have been loaded.
*/
style_signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StStyleClass, changed),
NULL, NULL,
_st_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
/* url loader for libccss */
static char *
ccss_url (GSList const *args,
void *user_data)
{
const gchar *given_path, *filename;
gchar *test_path;
g_return_val_if_fail (args, NULL);
given_path = (char const *) args->data;
/* we can only deal with local paths */
if (!g_str_has_prefix (given_path, "file://"))
return NULL;
filename = &given_path[7];
/*
* Handle absolute paths correctly
*/
if (*filename == '/')
return strdup (filename);
/* first try looking in the theme dir */
test_path = g_build_filename (g_get_user_config_dir (),
"st",
filename,
NULL);
if (g_file_test (test_path, G_FILE_TEST_IS_REGULAR))
return test_path;
g_free (test_path);
if (user_data)
{
test_path = g_build_filename ((gchar *) user_data, filename, NULL);
if (g_file_test (test_path, G_FILE_TEST_IS_REGULAR))
return test_path;
g_free (test_path);
}
else
{
g_warning ("No path available css url resolver!");
}
/* couldn't find the image anywhere, so just return the filename */
return strdup (given_path);
}
static ccss_function_t const *
peek_css_functions (void)
{
static ccss_function_t const ccss_functions[] =
{
{ "url", ccss_url },
{ NULL }
};
return ccss_functions;
}
static void
st_style_init (StStyle *style)
{
StStylePrivate *priv;
style->priv = priv = ST_STYLE_GET_PRIVATE (style);
/* create a hash table to look up pointer keys and values */
style->priv->node_hash = g_hash_table_new_full (NULL, NULL,
NULL, g_free);
style->priv->style_hash = g_hash_table_new_full (NULL, NULL,
NULL, (GDestroyNotify) ccss_style_destroy);
st_style_load (style);
}
/**
* st_style_new:
*
* Creates a new #StStyle object. This must be freed using #g_object_unref
* when no longer required.
*
* Returns: a newly allocated #StStyle
*/
StStyle *
st_style_new (void)
{
return g_object_new (ST_TYPE_STYLE, NULL);
}
/**
* st_style_get_default:
*
* Return the default StStyle object. This includes the current theme (if
* any).
*
* Returns: a #StStyle object. This must not be freed or unref'd by
* applications
*/
StStyle *
st_style_get_default (void)
{
if (G_LIKELY (default_style))
return default_style;
default_style = g_object_new (ST_TYPE_STYLE, NULL);
return default_style;
}
/* functions for ccss */
static st_style_node_t *
get_container (st_style_node_t *node)
{
st_style_node_t *container;
ClutterActor *parent;
g_return_val_if_fail (node, NULL);
g_return_val_if_fail (node->iface, NULL);
g_return_val_if_fail (node->stylable, NULL);
parent = clutter_actor_get_parent (CLUTTER_ACTOR (node->stylable));
while (parent && !ST_IS_WIDGET (parent))
parent = clutter_actor_get_parent (CLUTTER_ACTOR (parent));
if (!parent)
return NULL;
container = g_new0 (st_style_node_t, 1);
ccss_node_init ((ccss_node_t*) container, peek_node_class ());
container->iface = node->iface;
container->stylable = ST_STYLABLE (parent);
return container;
}
static const gchar*
get_style_id (st_style_node_t *node)
{
return st_stylable_get_style_id (node->stylable);
}
static const gchar*
get_style_type (st_style_node_t *node)
{
return st_stylable_get_style_type (node->stylable);
}
static const gchar*
get_style_class (st_style_node_t *node)
{
return st_stylable_get_style_class (node->stylable);
}
static const gchar*
get_pseudo_class (st_style_node_t *node)
{
return st_stylable_get_pseudo_class (node->stylable);
}
static const gchar*
get_attribute (st_style_node_t *node,
const char *name)
{
return st_stylable_get_attribute (node->stylable, name);
}
static void
release (st_style_node_t *node)
{
g_return_if_fail (node);
g_free (node);
}
static ccss_node_class_t *
peek_node_class (void)
{
static ccss_node_class_t _node_class = {
.is_a = NULL,
.get_container = (ccss_node_get_container_f) get_container,
.get_id = (ccss_node_get_id_f) get_style_id,
.get_type = (ccss_node_get_type_f) get_style_type,
.get_class = (ccss_node_get_class_f) get_style_class,
.get_pseudo_class = (ccss_node_get_pseudo_class_f) get_pseudo_class,
.get_viewport = NULL, // (ccss_node_get_viewport_f) get_viewport,
.get_attribute = (ccss_node_get_attribute_f) get_attribute,
.release = (ccss_node_release_f) release
};
return &_node_class;
}
static void
st_style_fetch_ccss_property (ccss_style_t *ccss_style,
GParamSpec *pspec,
GValue *value)
{
gboolean value_set = FALSE;
g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
if (G_PARAM_SPEC_VALUE_TYPE (pspec))
{
double number;
if (G_IS_PARAM_SPEC_INT (pspec))
{
if (ccss_style_get_double (ccss_style, pspec->name, &number))
{
g_value_set_int (value, (gint) number);
value_set = TRUE;
}
}
else if (G_IS_PARAM_SPEC_UINT (pspec))
{
if (ccss_style_get_double (ccss_style, pspec->name, &number))
{
g_value_set_uint (value, (guint) number);
value_set = TRUE;
}
}
else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == ST_TYPE_BORDER_IMAGE &&
!g_strcmp0 ("border-image", pspec->name))
{
ccss_border_image_t const *border_image;
if (ccss_style_get_property (ccss_style,
"border-image",
(ccss_property_base_t const **) &border_image))
{
if (border_image &&
border_image->base.state == CCSS_PROPERTY_STATE_SET)
{
g_value_set_boxed (value, border_image);
value_set = TRUE;
}
}
}
else if (ST_TYPE_PADDING == G_PARAM_SPEC_VALUE_TYPE (pspec) &&
0 == g_strcmp0 ("padding", pspec->name))
{
StPadding padding = { 0, };
gboolean padding_set = 0;
if (ccss_style_get_double (ccss_style, "padding-top", &number))
{
padding.top = number;
padding_set = TRUE;
}
if (ccss_style_get_double (ccss_style, "padding-right", &number))
{
padding.right = number;
padding_set = TRUE;
}
if (ccss_style_get_double (ccss_style, "padding-bottom", &number))
{
padding.bottom = number;
padding_set = TRUE;
}
if (ccss_style_get_double (ccss_style, "padding-left", &number))
{
padding.left = number;
padding_set = TRUE;
}
if (padding_set)
{
g_value_set_boxed (value, &padding);
value_set = TRUE;
}
}
else
{
gchar *string = NULL;
ccss_style_get_string (ccss_style, pspec->name, &string);
if (string)
{
if (CLUTTER_IS_PARAM_SPEC_COLOR (pspec))
{
ClutterColor color = { 0, };
clutter_color_from_string (&color, string);
clutter_value_set_color (value, &color);
value_set = TRUE;
}
else
if (G_IS_PARAM_SPEC_STRING (pspec))
{
g_value_set_string (value, string);
value_set = TRUE;
}
g_free (string);
}
}
}
/* no value was found in css, so copy in the default value */
if (!value_set)
g_param_value_set_default (pspec, value);
}
static ccss_style_t*
st_style_get_ccss_query (StStyle *style,
StStylable *stylable)
{
StStylableIface *iface = ST_STYLABLE_GET_IFACE (stylable);
ccss_style_t *ccss_style;
st_style_node_t *ccss_node;
ccss_node = g_hash_table_lookup (style->priv->node_hash, stylable);
if (!ccss_node)
{
ccss_node = g_new0 (st_style_node_t, 1);
ccss_node_init ((ccss_node_t*) ccss_node, peek_node_class ());
ccss_node->iface = iface;
ccss_node->stylable = stylable;
g_hash_table_insert (style->priv->node_hash, stylable, ccss_node);
g_signal_connect_swapped (stylable, "stylable-changed",
G_CALLBACK (g_hash_table_remove),
style->priv->node_hash);
g_object_weak_ref ((GObject*) stylable,
(GWeakNotify) g_hash_table_remove, style->priv->node_hash);
}
ccss_style = g_hash_table_lookup (style->priv->style_hash, stylable);
if (!ccss_style)
{
ccss_style = ccss_stylesheet_query (style->priv->stylesheet,
(ccss_node_t *) ccss_node);
g_hash_table_insert (style->priv->style_hash, stylable, ccss_style);
/* remove the cache if the stylable changes */
g_signal_connect_swapped (stylable, "stylable-changed",
G_CALLBACK (g_hash_table_remove),
style->priv->style_hash);
g_object_weak_ref ((GObject*) stylable,
(GWeakNotify) g_hash_table_remove, style->priv->style_hash);
}
return ccss_style;
}
/**
* st_style_get_property:
* @style: the style data store object
* @stylable: a stylable to retreive the data for
* @pspec: a #GParamSpec describing the property required
* @value: a #GValue to place the return value in
*
* Requests the property described in @pspec for the specified stylable
*/
void
st_style_get_property (StStyle *style,
StStylable *stylable,
GParamSpec *pspec,
GValue *value)
{
StStylePrivate *priv;
gboolean value_set = FALSE;
g_return_if_fail (ST_IS_STYLE (style));
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_return_if_fail (pspec != NULL);
g_return_if_fail (value != NULL);
priv = style->priv;
/* look up the property in the css */
if (priv->stylesheet)
{
ccss_style_t *ccss_style;
ccss_style = st_style_get_ccss_query (style, stylable);
if (ccss_style)
{
st_style_fetch_ccss_property (ccss_style, pspec, value);
value_set = TRUE;
}
}
/* no value was found in css, so copy in the default value */
if (!value_set)
{
g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_param_value_set_default (pspec, value);
}
}
/**
* st_style_get_valist:
* @style: a #StStyle
* @stylable: a #StStylable
* @first_property_name: name of the first property to get
* @va_args: return location for the first property, followed optionally
* by more name/return location pairs, followed by %NULL
*
* Gets the style properties for @stylable from @style.
*
* Please refer to st_style_get() for further information.
*/
void
st_style_get_valist (StStyle *style,
StStylable *stylable,
const gchar *first_property_name,
va_list va_args)
{
StStylePrivate *priv;
const gchar *name = first_property_name;
gboolean values_set = FALSE;
g_return_if_fail (ST_IS_STYLE (style));
g_return_if_fail (ST_IS_STYLABLE (stylable));
g_return_if_fail (style->priv != NULL);
priv = style->priv;
/* look up the property in the css */
if (priv->stylesheet)
{
ccss_style_t *ccss_style;
ccss_style = st_style_get_ccss_query (style, stylable);
if (ccss_style)
{
while (name)
{
GValue value = { 0, };
gchar *error = NULL;
GParamSpec *pspec = st_stylable_find_property (stylable, name);
st_style_fetch_ccss_property (ccss_style, pspec, &value);
G_VALUE_LCOPY (&value, va_args, 0, &error);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error);
g_free (error);
g_value_unset (&value);
break;
}
g_value_unset (&value);
name = va_arg (va_args, gchar*);
}
values_set = TRUE;
}
}
if (!values_set)
{
/* Set the remaining properties to their default values
* even if broken out of the above loop. */
while (name)
{
GValue value = { 0, };
gchar *error = NULL;
st_stylable_get_default_value (stylable, name, &value);
G_VALUE_LCOPY (&value, va_args, 0, &error);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error);
g_free (error);
g_value_unset (&value);
break;
}
g_value_unset (&value);
name = va_arg (va_args, gchar*);
}
}
}
/**
* st_style_get:
* @style: a #StStyle
* @stylable: a #StStylable
* @first_property_name: name of the first property to get
* @Varargs: return location for the first property, followed optionally
* by more name/return location pairs, followed by %NULL
*
* Gets the style properties for @stylable from @style.
*
* In general, a copy is made of the property contents and the caller
* is responsible for freeing the memory in the appropriate manner for
* the property type.
*/
void
st_style_get (StStyle *style,
StStylable *stylable,
const gchar *first_property_name,
...)
{
va_list va_args;
g_return_if_fail (ST_IS_STYLE (style));
g_return_if_fail (first_property_name != NULL);
va_start (va_args, first_property_name);
st_style_get_valist (style, stylable, first_property_name, va_args);
va_end (va_args);
}

94
src/st/st-style.h Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_STYLE_H__
#define __ST_STYLE_H__
#include <glib-object.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_STYLE (st_style_get_type ())
#define ST_STYLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_STYLE, StStyle))
#define ST_IS_STYLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_STYLE))
#define ST_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_STYLE, StStyleClass))
#define ST_IS_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_STYLE))
#define ST_STYLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_STYLE, StStyleClass))
typedef struct _StStyle StStyle;
typedef struct _StStylePrivate StStylePrivate;
typedef struct _StStyleClass StStyleClass;
/* forward declaration */
typedef struct _StStylable StStylable; /* dummy typedef */
typedef struct _StStylableIface StStylableIface;
typedef enum { /*< prefix=ST_STYLE_ERROR >*/
ST_STYLE_ERROR_INVALID_FILE
} StStyleError;
/**
* StStyle:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StStyle
{
/*< private >*/
GObject parent_instance;
StStylePrivate *priv;
};
struct _StStyleClass
{
GObjectClass parent_class;
void (* changed) (StStyle *style);
};
GType st_style_get_type (void) G_GNUC_CONST;
StStyle *st_style_get_default (void);
StStyle *st_style_new (void);
gboolean st_style_load_from_file (StStyle *style,
const gchar *filename,
GError **error);
void st_style_get_property (StStyle *style,
StStylable *stylable,
GParamSpec *pspec,
GValue *value);
void st_style_get (StStyle *style,
StStylable *stylable,
const gchar *first_property_name,
...) G_GNUC_NULL_TERMINATED;
void st_style_get_valist (StStyle *style,
StStylable *stylable,
const gchar *first_property_name,
va_list va_args);
G_END_DECLS
#endif /* __ST_STYLE_H__ */

575
src/st/st-subtexture.c Normal file
View File

@ -0,0 +1,575 @@
/*
* st-subtexture.h: Class to wrap a texture and "subframe" it.
* based on
* st-texture-frame.c: Expandible texture actor
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <cogl/cogl.h>
#include <clutter/clutter.h>
#include "st-subtexture.h"
enum
{
PROP_0,
PROP_PARENT_TEXTURE,
PROP_TOP,
PROP_LEFT,
PROP_WIDTH,
PROP_HEIGHT
};
G_DEFINE_TYPE (StSubtexture, st_subtexture, CLUTTER_TYPE_ACTOR);
#define ST_SUBTEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_SUBTEXTURE, StSubtexturePrivate))
struct _StSubtexturePrivate
{
ClutterTexture *parent_texture;
int left;
int top;
int width;
int height;
CoglHandle material;
};
static void
st_subtexture_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_width_p)
*min_width_p = 0;
if (natural_width_p)
*natural_width_p = 0;
}
else
{
if (min_width_p)
*min_width_p = priv->width;
if (natural_width_p)
*natural_width_p = priv->width;
}
}
static void
st_subtexture_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_height_p)
*min_height_p = 0;
if (natural_height_p)
*natural_height_p = 0;
}
else
{
if (min_height_p)
*min_height_p = priv->height;
if (natural_height_p)
*natural_height_p = priv->height;
}
}
static void
st_subtexture_realize (ClutterActor *self)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (priv->material != COGL_INVALID_HANDLE)
return;
priv->material = cogl_material_new ();
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
static void
st_subtexture_unrealize (ClutterActor *self)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (priv->material == COGL_INVALID_HANDLE)
return;
cogl_material_unref (priv->material);
priv->material = COGL_INVALID_HANDLE;
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
static void
st_subtexture_paint (ClutterActor *self)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
ClutterActorBox box = { 0, 0, 0, 0 };
gfloat tx1, ty1, tx2, ty2, tex_width, tex_height, width, height;
guint8 opacity;
/* no need to paint stuff if we don't have a texture */
if (G_UNLIKELY (priv->parent_texture == NULL))
return;
/* parent texture may have been hidden, so need to make sure it gets
* realized
*/
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
if (cogl_texture == COGL_INVALID_HANDLE)
return;
tex_width = cogl_texture_get_width (cogl_texture);
tex_height = cogl_texture_get_height (cogl_texture);
clutter_actor_get_allocation_box (self, &box);
width = box.x2 - box.x1;
height = box.y2 - box.y1;
tx1 = 1.0 * priv->left / tex_width;
ty1 = 1.0 * priv->top / tex_height;
tx2 = 1.0 * (priv->left + priv->width) / tex_width;
ty2 = 1.0 * (priv->top + priv->height) / tex_height;
opacity = clutter_actor_get_paint_opacity (self);
g_assert (priv->material != COGL_INVALID_HANDLE);
/* set the source material using the parent texture's COGL handle */
cogl_material_set_color4ub (priv->material, 255, 255, 255, opacity);
cogl_material_set_layer (priv->material, 0, cogl_texture);
cogl_set_source (priv->material);
cogl_rectangle_with_texture_coords (0,0, (float) width, (float) height,
tx1, ty1, tx2, ty2);
}
static inline void
st_subtexture_set_frame_internal (StSubtexture *frame,
int left,
int top,
int width,
int height)
{
StSubtexturePrivate *priv = frame->priv;
GObject *gobject = G_OBJECT (frame);
gboolean changed = FALSE;
g_object_freeze_notify (gobject);
if (priv->top != top)
{
priv->top = top;
g_object_notify (gobject, "top");
changed = TRUE;
}
if (priv->left != left)
{
priv->left = left;
g_object_notify (gobject, "left");
changed = TRUE;
}
if (priv->width != width)
{
priv->width = width;
g_object_notify (gobject, "width");
changed = TRUE;
}
if (priv->height != height)
{
priv->height = height;
g_object_notify (gobject, "height");
changed = TRUE;
}
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
g_object_thaw_notify (gobject);
}
static void
st_subtexture_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StSubtexture *frame = ST_SUBTEXTURE (gobject);
StSubtexturePrivate *priv = frame->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
st_subtexture_set_parent_texture (frame,
g_value_get_object (value));
break;
case PROP_TOP:
st_subtexture_set_frame_internal (frame,
priv->left,
g_value_get_int (value),
priv->width,
priv->height);
break;
case PROP_LEFT:
st_subtexture_set_frame_internal (frame,
g_value_get_int (value),
priv->top,
priv->width,
priv->height);
break;
case PROP_WIDTH:
st_subtexture_set_frame_internal (frame,
priv->left,
priv->top,
g_value_get_int (value),
priv->height);
break;
case PROP_HEIGHT:
st_subtexture_set_frame_internal (frame,
priv->left,
priv->top,
priv->width,
g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_subtexture_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (gobject)->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
g_value_set_object (value, priv->parent_texture);
break;
case PROP_LEFT:
g_value_set_int (value, priv->left);
break;
case PROP_TOP:
g_value_set_int (value, priv->top);
break;
case PROP_WIDTH:
g_value_set_int (value, priv->width);
break;
case PROP_HEIGHT:
g_value_set_int (value, priv->height);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_subtexture_dispose (GObject *gobject)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (gobject)->priv;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
}
if (priv->material)
{
cogl_material_unref (priv->material);
priv->material = COGL_INVALID_HANDLE;
}
G_OBJECT_CLASS (st_subtexture_parent_class)->dispose (gobject);
}
static void
st_subtexture_class_init (StSubtextureClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (gobject_class, sizeof (StSubtexturePrivate));
actor_class->get_preferred_width =
st_subtexture_get_preferred_width;
actor_class->get_preferred_height =
st_subtexture_get_preferred_height;
actor_class->realize = st_subtexture_realize;
actor_class->unrealize = st_subtexture_unrealize;
actor_class->paint = st_subtexture_paint;
gobject_class->set_property = st_subtexture_set_property;
gobject_class->get_property = st_subtexture_get_property;
gobject_class->dispose = st_subtexture_dispose;
pspec = g_param_spec_object ("parent-texture",
"Parent Texture",
"The parent ClutterTexture",
CLUTTER_TYPE_TEXTURE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT);
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
pspec = g_param_spec_int ("left",
"Left",
"Left offset",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
pspec = g_param_spec_int ("top",
"Top",
"Top offset",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
pspec = g_param_spec_int ("width",
"Width",
"Width",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
pspec = g_param_spec_int ("height",
"Height",
"Height",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_HEIGHT, pspec);
}
static void
st_subtexture_init (StSubtexture *self)
{
StSubtexturePrivate *priv;
self->priv = priv = ST_SUBTEXTURE_GET_PRIVATE (self);
priv->material = COGL_INVALID_HANDLE;
}
/**
* st_subtexture_new:
* @texture: a #ClutterTexture or %NULL
* @left: left
* @top: top
* @width: width
* @height: height
*
* A #StSubtexture is a specialized texture that efficiently clones
* an area of the given @texture while keeping preserving portions of the
* same texture.
*
* A #StSubtexture can be used to make a rectangular texture fit a
* given size without stretching its borders.
*
* Return value: the newly created #StSubtexture
*/
ClutterActor*
st_subtexture_new (ClutterTexture *texture,
gint left,
gint top,
gint width,
gint height)
{
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
return g_object_new (ST_TYPE_SUBTEXTURE,
"parent-texture", texture,
"top", top,
"left", left,
"width", width,
"height", height,
NULL);
}
/**
* st_subtexture_get_parent_texture:
* @frame: A #StSubtexture
*
* Return the texture used by the #StSubtexture
*
* Returns: a #ClutterTexture owned by the #StSubtexture
*/
ClutterTexture *
st_subtexture_get_parent_texture (StSubtexture *frame)
{
g_return_val_if_fail (ST_IS_SUBTEXTURE (frame), NULL);
return frame->priv->parent_texture;
}
/**
* st_subtexture_set_parent_texture:
* @frame: A #StSubtexture
* @texture: A #ClutterTexture
*
* Set the #ClutterTexture used by this #StSubtexture
*
*/
void
st_subtexture_set_parent_texture (StSubtexture *frame,
ClutterTexture *texture)
{
StSubtexturePrivate *priv;
gboolean was_visible;
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
priv = frame->priv;
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
if (priv->parent_texture == texture)
return;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
if (was_visible)
clutter_actor_hide (CLUTTER_ACTOR (frame));
}
if (texture)
{
priv->parent_texture = g_object_ref (texture);
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
clutter_actor_show (CLUTTER_ACTOR (frame));
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
g_object_notify (G_OBJECT (frame), "parent-texture");
}
/**
* st_subtexture_set_frame:
* @frame: A #StSubtexture
* @left: left
* @top: top
* @width: width
* @height: height
*
* Set the frame of the subtexture
*
*/
void
st_subtexture_set_frame (StSubtexture *frame,
gint left,
gint top,
gint width,
gint height)
{
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
st_subtexture_set_frame_internal (frame, left, top, width, height);
}
/**
* st_subtexture_get_frame:
* @frame: A #StSubtexture
* @left: left
* @top: top
* @width: width
* @height: height
*
* Retrieve the current frame.
*
*/
void
st_subtexture_get_frame (StSubtexture *frame,
gint *left,
gint *top,
gint *width,
gint *height)
{
StSubtexturePrivate *priv;
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
priv = frame->priv;
if (top)
*top = priv->top;
if (left)
*left = priv->left;
if (width)
*width = priv->width;
if (height)
*height = priv->height;
}

96
src/st/st-subtexture.h Normal file
View File

@ -0,0 +1,96 @@
/*
* st-subtexture.h: Class to wrap a texture and "subframe" it.
*
* Based on
* st-texture-frame.h: Expandible texture actor
*
* Copyright 2007, 2008 OpenedHand Ltd
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_SUBTEXTURE_H__
#define __ST_SUBTEXTURE_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_SUBTEXTURE (st_subtexture_get_type ())
#define ST_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SUBTEXTURE, StSubtexture))
#define ST_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SUBTEXTURE, StSubtextureClass))
#define ST_IS_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SUBTEXTURE))
#define ST_IS_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SUBTEXTURE))
#define ST_SUBTEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SUBTEXTURE, StSubtextureClass))
typedef struct _StSubtexture StSubtexture;
typedef struct _StSubtexturePrivate StSubtexturePrivate;
typedef struct _StSubtextureClass StSubtextureClass;
/**
* StSubtexture:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StSubtexture
{
/*< private >*/
ClutterActor parent_instance;
StSubtexturePrivate *priv;
};
struct _StSubtextureClass
{
ClutterActorClass parent_class;
/* padding for future expansion */
void (*_st_box_1) (void);
void (*_st_box_2) (void);
void (*_st_box_3) (void);
void (*_st_box_4) (void);
};
GType st_subtexture_get_type (void) G_GNUC_CONST;
ClutterActor * st_subtexture_new (ClutterTexture *texture,
gint top,
gint left,
gint width,
gint height);
void st_subtexture_set_parent_texture (StSubtexture *frame,
ClutterTexture *texture);
ClutterTexture *st_subtexture_get_parent_texture (StSubtexture *frame);
void st_subtexture_set_frame (StSubtexture *frame,
gint top,
gint left,
gint width,
gint height);
void st_subtexture_get_frame (StSubtexture *frame,
gint *top,
gint *left,
gint *width,
gint *height);
G_END_DECLS
#endif /* __ST_SUBTEXTURE_H__ */

450
src/st/st-texture-cache.c Normal file
View File

@ -0,0 +1,450 @@
/*
* st-widget.h: Base class for St actors
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
/**
* SECTION:st-texture-cache
* @short_description: A per-process store to cache textures
*
* #StTextureCache allows an application to re-use an previously loaded
* textures.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <glib-object.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <string.h>
#include "st-texture-cache.h"
#include "st-marshal.h"
#include "st-private.h"
#include "st-subtexture.h"
G_DEFINE_TYPE (StTextureCache, st_texture_cache, G_TYPE_OBJECT)
#define TEXTURE_CACHE_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_TEXTURE_CACHE, StTextureCachePrivate))
typedef struct _StTextureCachePrivate StTextureCachePrivate;
struct _StTextureCachePrivate
{
GHashTable *cache;
};
typedef struct FinalizedClosure
{
gchar *path;
StTextureCache *cache;
} FinalizedClosure;
enum
{
PROP_0,
};
static StTextureCache* __cache_singleton = NULL;
/*
* Convention: posX with a value of -1 indicates whole texture
*/
typedef struct StTextureCacheItem {
char filename[256];
int width, height;
int posX, posY;
ClutterActor *ptr;
} StTextureCacheItem;
static StTextureCacheItem *
st_texture_cache_item_new (void)
{
return g_slice_new0 (StTextureCacheItem);
}
static void
st_texture_cache_item_free (StTextureCacheItem *item)
{
g_slice_free (StTextureCacheItem, item);
}
static void
st_texture_cache_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
st_texture_cache_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
st_texture_cache_dispose (GObject *object)
{
if (G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose)
G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
}
static void
st_texture_cache_finalize (GObject *object)
{
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object);
if (priv->cache)
{
g_hash_table_unref (priv->cache);
priv->cache = NULL;
}
G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
}
static void
st_texture_cache_class_init (StTextureCacheClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (StTextureCachePrivate));
object_class->get_property = st_texture_cache_get_property;
object_class->set_property = st_texture_cache_set_property;
object_class->dispose = st_texture_cache_dispose;
object_class->finalize = st_texture_cache_finalize;
}
static void
st_texture_cache_init (StTextureCache *self)
{
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
priv->cache = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
NULL);
}
/**
* st_texture_cache_get_default:
*
* Returns the default texture cache. This is owned by St and should not be
* unreferenced or freed.
*
* Returns: a StTextureCache
*/
StTextureCache*
st_texture_cache_get_default (void)
{
if (G_UNLIKELY (__cache_singleton == NULL))
__cache_singleton = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
return __cache_singleton;
}
#if 0
static void
on_texure_finalized (gpointer data,
GObject *where_the_object_was)
{
FinalizedClosure *closure = (FinalizedClosure *) data;
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache);
g_hash_table_remove (priv->cache, closure->path);
g_free(closure->path);
g_free(closure);
}
#endif
/**
* st_texture_cache_get_size:
* @self: A #StTextureCache
*
* Returns the number of items in the texture cache
*
* Returns: the current size of the cache
*/
gint
st_texture_cache_get_size (StTextureCache *self)
{
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
return g_hash_table_size (priv->cache);
}
static void
add_texture_to_cache (StTextureCache *self,
const gchar *path,
StTextureCacheItem *item)
{
/* FinalizedClosure *closure; */
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
g_hash_table_insert (priv->cache, g_strdup (path), item);
#if 0
/* Make sure we can remove from hash */
closure = g_new0 (FinalizedClosure, 1);
closure->path = g_strdup (path);
closure->cache = self;
g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure);
#endif
}
/* NOTE: you should unref the returned texture when not needed */
/**
* st_texture_cache_get_texture:
* @self: A #StTextureCache
* @path: A path to a image file
*
* Create a new ClutterTexture with the specified image. Adds the image to the
* cache if the image had not been previously loaded. Subsequent calls with
* the same image path will return a new ClutterTexture with the previously
* loaded image.
*
* Returns: a newly created ClutterTexture
*/
ClutterTexture*
st_texture_cache_get_texture (StTextureCache *self,
const gchar *path)
{
ClutterActor *texture;
CoglHandle *handle;
StTextureCachePrivate *priv;
StTextureCacheItem *item;
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
g_return_val_if_fail (path != NULL, NULL);
priv = TEXTURE_CACHE_PRIVATE (self);
item = g_hash_table_lookup (priv->cache, path);
if (item && item->posX != -1)
{
GError *err = NULL;
/*
* We have a cache hit, but it's for a partial texture. The only
* sane option is to read it from disk and just don't cache it
* at all.
*/
return CLUTTER_TEXTURE(clutter_texture_new_from_file(path, &err));
}
if (!item)
{
GError *err = NULL;
item = st_texture_cache_item_new ();
item->posX = -1;
item->posY = -1;
item->ptr = clutter_texture_new_from_file (path, &err);
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
&item->width, &item->height);
if (!item->ptr)
{
if (err)
{
g_warning ("Error loading image: %s", err->message);
g_error_free (err);
}
return NULL;
}
add_texture_to_cache (self, path, item);
}
texture = clutter_texture_new ();
handle = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (item->ptr));
clutter_texture_set_cogl_texture ((ClutterTexture*) texture, handle);
return (ClutterTexture*) texture;
}
/**
* st_texture_cache_get_actor:
* @self: A #StTextureCache
* @path: A path to a image file
*
* Create a new ClutterSubTexture with the specified image. Adds the image to the
* cache if the image had not been previously loaded. Subsequent calls with
* the same image path will return a new ClutterTexture with the previously
* loaded image.
*
* Use this function if all you need is an actor for drawing.
*
* Returns: a newly created ClutterTexture
*/
ClutterActor*
st_texture_cache_get_actor (StTextureCache *self,
const gchar *path)
{
StTextureCachePrivate *priv;
StTextureCacheItem *item;
GError *err = NULL;
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
g_return_val_if_fail (path != NULL, NULL);
priv = TEXTURE_CACHE_PRIVATE (self);
item = g_hash_table_lookup (priv->cache, path);
if (item)
{
int posX = item->posX;
int posY = item->posY;
if (posX == -1)
posX = 0;
if (posY == -1)
posY = 0;
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), posX, posY,
item->width, item->height);
}
item = st_texture_cache_item_new ();
item->posX = -1;
item->posY = -1;
item->ptr = clutter_texture_new_from_file (path, &err);
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
&item->width, &item->height);
if (!item->ptr)
{
if (err)
{
g_warning ("Error loading image: %s", err->message);
g_error_free (err);
}
return NULL;
}
add_texture_to_cache (self, path, item);
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), 0, 0, item->width,
item->height);
}
void
st_texture_cache_load_cache (StTextureCache *self,
const gchar *filename)
{
FILE *file;
StTextureCacheItem *element, head;
int ret;
ClutterActor *actor;
GError *error = NULL;
StTextureCachePrivate *priv;
g_return_if_fail (ST_IS_TEXTURE_CACHE (self));
g_return_if_fail (filename != NULL);
priv = TEXTURE_CACHE_PRIVATE (self);
file = fopen(filename, "rm");
if (!file)
return;
ret = fread (&head, sizeof(StTextureCacheItem), 1, file);
if (ret < 0)
{
fclose (file);
return;
}
/* check if we already if this texture in the cache */
if (g_hash_table_lookup (priv->cache, head.filename))
{
/* skip it, we're done */
fclose (file);
return;
}
actor = clutter_texture_new_from_file (head.filename, &error);
if (error)
{
g_critical (G_STRLOC ": Error opening cache image file: %s",
error->message);
g_clear_error (&error);
fclose (file);
return;
}
element = st_texture_cache_item_new ();
element->posX = -1;
element->posY = -1;
element->ptr = actor;
strncpy (element->filename, head.filename, 256);
clutter_texture_get_base_size (CLUTTER_TEXTURE (element->ptr),
&element->width, &element->height);
g_hash_table_insert (priv->cache, element->filename, element);
while (!feof (file))
{
element = st_texture_cache_item_new ();
ret = fread (element, sizeof (StTextureCacheItem), 1, file);
if (ret < 1)
{
/* end of file */
st_texture_cache_item_free (element);
break;
}
element->ptr = actor;
if (g_hash_table_lookup (priv->cache, element->filename))
{
/* file is already in the cache.... */
st_texture_cache_item_free (element);
} else {
g_hash_table_insert (priv->cache, element->filename, element);
}
}
}

95
src/st/st-texture-cache.h Normal file
View File

@ -0,0 +1,95 @@
/*
* st-texture-cache.h: Cached textures object
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef _ST_TEXTURE_CACHE
#define _ST_TEXTURE_CACHE
#include <glib-object.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_TEXTURE_CACHE st_texture_cache_get_type()
#define ST_TEXTURE_CACHE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
ST_TYPE_TEXTURE_CACHE, StTextureCache))
#define ST_TEXTURE_CACHE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
#define ST_IS_TEXTURE_CACHE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
ST_TYPE_TEXTURE_CACHE))
#define ST_IS_TEXTURE_CACHE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
ST_TYPE_TEXTURE_CACHE))
#define ST_TEXTURE_CACHE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
/**
* StTextureCache:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
typedef struct {
/*< private >*/
GObject parent;
} StTextureCache;
typedef struct {
GObjectClass parent_class;
void (* loaded) (StTextureCache *self,
const gchar *path,
ClutterTexture *texture);
void (* error_loading) (StTextureCache *self,
GError *error);
} StTextureCacheClass;
GType st_texture_cache_get_type (void);
StTextureCache* st_texture_cache_get_default (void);
ClutterTexture* st_texture_cache_get_texture (StTextureCache *self,
const gchar *path);
ClutterActor* st_texture_cache_get_actor (StTextureCache *self,
const gchar *path);
gint st_texture_cache_get_size (StTextureCache *self);
void st_texture_cache_load_cache (StTextureCache *self,
const char *filename);
G_END_DECLS
#endif /* _ST_TEXTURE_CACHE */

620
src/st/st-texture-frame.c Normal file
View File

@ -0,0 +1,620 @@
/*
* st-texture-frame.h: Expandible texture actor
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
/**
* SECTION:st-texture-frame
* @short_description: Stretch a texture to fit the entire allocation
*
* #StTextureFrame
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <cogl/cogl.h>
#include "st-texture-frame.h"
#include "st-private.h"
enum
{
PROP_0,
PROP_PARENT_TEXTURE,
PROP_TOP,
PROP_RIGHT,
PROP_BOTTOM,
PROP_LEFT
};
G_DEFINE_TYPE (StTextureFrame, st_texture_frame, CLUTTER_TYPE_ACTOR);
#define ST_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFramePrivate))
struct _StTextureFramePrivate
{
ClutterTexture *parent_texture;
gfloat top;
gfloat right;
gfloat bottom;
gfloat left;
};
static void
st_texture_frame_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_width_p)
*min_width_p = 0;
if (natural_width_p)
*natural_width_p = 0;
}
else
{
ClutterActorClass *klass;
/* by directly querying the parent texture's class implementation
* we are going around any override mechanism the parent texture
* might have in place, and we ask directly for the original
* preferred width
*/
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
for_height,
min_width_p,
natural_width_p);
}
}
static void
st_texture_frame_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_height_p)
*min_height_p = 0;
if (natural_height_p)
*natural_height_p = 0;
}
else
{
ClutterActorClass *klass;
/* by directly querying the parent texture's class implementation
* we are going around any override mechanism the parent texture
* might have in place, and we ask directly for the original
* preferred height
*/
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
for_width,
min_height_p,
natural_height_p);
}
}
static void
st_texture_frame_paint (ClutterActor *self)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
CoglHandle cogl_material = COGL_INVALID_HANDLE;
ClutterActorBox box = { 0, };
gfloat width, height;
gfloat tex_width, tex_height;
gfloat ex, ey;
gfloat tx1, ty1, tx2, ty2;
guint8 opacity;
/* no need to paint stuff if we don't have a texture */
if (G_UNLIKELY (priv->parent_texture == NULL))
return;
/* parent texture may have been hidden, so need to make sure it gets
* realized
*/
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
if (cogl_texture == COGL_INVALID_HANDLE)
return;
cogl_material = clutter_texture_get_cogl_material (priv->parent_texture);
if (cogl_material == COGL_INVALID_HANDLE)
return;
tex_width = cogl_texture_get_width (cogl_texture);
tex_height = cogl_texture_get_height (cogl_texture);
clutter_actor_get_allocation_box (self, &box);
width = box.x2 - box.x1;
height = box.y2 - box.y1;
tx1 = priv->left / tex_width;
tx2 = (tex_width - priv->right) / tex_width;
ty1 = priv->top / tex_height;
ty2 = (tex_height - priv->bottom) / tex_height;
ex = width - priv->right;
if (ex < 0)
ex = priv->right; /* FIXME ? */
ey = height - priv->bottom;
if (ey < 0)
ey = priv->bottom; /* FIXME ? */
opacity = clutter_actor_get_paint_opacity (self);
/* Paint using the parent texture's material. It should already have
the cogl texture set as the first layer */
/* NB: for correct blending we need set a preumultiplied color here: */
cogl_material_set_color4ub (cogl_material,
opacity, opacity, opacity, opacity);
cogl_set_source (cogl_material);
{
GLfloat rectangles[] =
{
/* top left corner */
0, 0, priv->left, priv->top,
0.0, 0.0,
tx1, ty1,
/* top middle */
priv->left, 0, ex, priv->top,
tx1, 0.0,
tx2, ty1,
/* top right */
ex, 0, width, priv->top,
tx2, 0.0,
1.0, ty1,
/* mid left */
0, priv->top, priv->left, ey,
0.0, ty1,
tx1, ty2,
/* center */
priv->left, priv->top, ex, ey,
tx1, ty1,
tx2, ty2,
/* mid right */
ex, priv->top, width, ey,
tx2, ty1,
1.0, ty2,
/* bottom left */
0, ey, priv->left, height,
0.0, ty2,
tx1, 1.0,
/* bottom center */
priv->left, ey, ex, height,
tx1, ty2,
tx2, 1.0,
/* bottom right */
ex, ey, width, height,
tx2, ty2,
1.0, 1.0
};
cogl_rectangles_with_texture_coords (rectangles, 9);
}
}
static inline void
st_texture_frame_set_frame_internal (StTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left)
{
StTextureFramePrivate *priv = frame->priv;
GObject *gobject = G_OBJECT (frame);
gboolean changed = FALSE;
g_object_freeze_notify (gobject);
if (priv->top != top)
{
priv->top = top;
g_object_notify (gobject, "top");
changed = TRUE;
}
if (priv->right != right)
{
priv->right = right;
g_object_notify (gobject, "right");
changed = TRUE;
}
if (priv->bottom != bottom)
{
priv->bottom = bottom;
g_object_notify (gobject, "bottom");
changed = TRUE;
}
if (priv->left != left)
{
priv->left = left;
g_object_notify (gobject, "left");
changed = TRUE;
}
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
g_object_thaw_notify (gobject);
}
static void
st_texture_frame_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StTextureFrame *frame = ST_TEXTURE_FRAME (gobject);
StTextureFramePrivate *priv = frame->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
st_texture_frame_set_parent_texture (frame,
g_value_get_object (value));
break;
case PROP_TOP:
st_texture_frame_set_frame_internal (frame,
g_value_get_float (value),
priv->right,
priv->bottom,
priv->left);
break;
case PROP_RIGHT:
st_texture_frame_set_frame_internal (frame,
priv->top,
g_value_get_float (value),
priv->bottom,
priv->left);
break;
case PROP_BOTTOM:
st_texture_frame_set_frame_internal (frame,
priv->top,
priv->right,
g_value_get_float (value),
priv->left);
break;
case PROP_LEFT:
st_texture_frame_set_frame_internal (frame,
priv->top,
priv->right,
priv->bottom,
g_value_get_float (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_texture_frame_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
g_value_set_object (value, priv->parent_texture);
break;
case PROP_LEFT:
g_value_set_float (value, priv->left);
break;
case PROP_TOP:
g_value_set_float (value, priv->top);
break;
case PROP_RIGHT:
g_value_set_float (value, priv->right);
break;
case PROP_BOTTOM:
g_value_set_float (value, priv->bottom);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_texture_frame_dispose (GObject *gobject)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
}
G_OBJECT_CLASS (st_texture_frame_parent_class)->dispose (gobject);
}
static void
st_texture_frame_class_init (StTextureFrameClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (gobject_class, sizeof (StTextureFramePrivate));
actor_class->get_preferred_width =
st_texture_frame_get_preferred_width;
actor_class->get_preferred_height =
st_texture_frame_get_preferred_height;
actor_class->paint = st_texture_frame_paint;
gobject_class->set_property = st_texture_frame_set_property;
gobject_class->get_property = st_texture_frame_get_property;
gobject_class->dispose = st_texture_frame_dispose;
pspec = g_param_spec_object ("parent-texture",
"Parent Texture",
"The parent ClutterTexture",
CLUTTER_TYPE_TEXTURE,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT);
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
pspec = g_param_spec_float ("left",
"Left",
"Left offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
pspec = g_param_spec_float ("top",
"Top",
"Top offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
pspec = g_param_spec_float ("bottom",
"Bottom",
"Bottom offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
pspec = g_param_spec_float ("right",
"Right",
"Right offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
}
static void
st_texture_frame_init (StTextureFrame *self)
{
StTextureFramePrivate *priv;
self->priv = priv = ST_TEXTURE_FRAME_GET_PRIVATE (self);
}
/**
* st_texture_frame_new:
* @texture: a #ClutterTexture or %NULL
* @left: left margin preserving its content
* @top: top margin preserving its content
* @right: right margin preserving its content
* @bottom: bottom margin preserving its content
*
* A #StTextureFrame is a specialized texture that efficiently clones
* an area of the given @texture while keeping preserving portions of the
* same texture.
*
* A #StTextureFrame can be used to make a rectangular texture fit a
* given size without stretching its borders.
*
* Return value: the newly created #StTextureFrame
*/
ClutterActor*
st_texture_frame_new (ClutterTexture *texture,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left)
{
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
return g_object_new (ST_TYPE_TEXTURE_FRAME,
"parent-texture", texture,
"top", top,
"right", right,
"bottom", bottom,
"left", left,
NULL);
}
/**
* st_texture_frame_get_parent_texture:
* @frame: A #StTextureFrame
*
* Return the texture used by the #StTextureFrame
*
* Returns: a #ClutterTexture owned by the #StTextureFrame
*/
ClutterTexture *
st_texture_frame_get_parent_texture (StTextureFrame *frame)
{
g_return_val_if_fail (ST_IS_TEXTURE_FRAME (frame), NULL);
return frame->priv->parent_texture;
}
/**
* st_texture_frame_set_parent_texture:
* @frame: A #StTextureFrame
* @texture: A #ClutterTexture
*
* Set the #ClutterTexture used by this #StTextureFrame
*
*/
void
st_texture_frame_set_parent_texture (StTextureFrame *frame,
ClutterTexture *texture)
{
StTextureFramePrivate *priv;
gboolean was_visible;
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
priv = frame->priv;
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
if (priv->parent_texture == texture)
return;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
if (was_visible)
clutter_actor_hide (CLUTTER_ACTOR (frame));
}
if (texture)
{
priv->parent_texture = g_object_ref_sink (texture);
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
clutter_actor_show (CLUTTER_ACTOR (frame));
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
g_object_notify (G_OBJECT (frame), "parent-texture");
}
/**
* st_texture_frame_set_frame:
* @frame: A #StTextureFrame
* @top: width of the top slice
* @right: width of the right slice
* @bottom: width of the bottom slice
* @left: width of the left slice
*
* Set the slice lines of the specified frame. The slices are calculated as
* widths from the edge of the frame.
*
*/
void
st_texture_frame_set_frame (StTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left)
{
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
st_texture_frame_set_frame_internal (frame, top, right, bottom, left);
}
/**
* st_texture_frame_get_frame:
* @frame: A #StTextureFrame
* @top: width of the top slice
* @right: width of the right slice
* @bottom: width of the bottom slice
* @left: width of the left slice
*
* Retrieve the current slice lines from the specified frame.
*
*/
void
st_texture_frame_get_frame (StTextureFrame *frame,
gfloat *top,
gfloat *right,
gfloat *bottom,
gfloat *left)
{
StTextureFramePrivate *priv;
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
priv = frame->priv;
if (top)
*top = priv->top;
if (right)
*right = priv->right;
if (bottom)
*bottom = priv->bottom;
if (left)
*left = priv->left;
}

93
src/st/st-texture-frame.h Normal file
View File

@ -0,0 +1,93 @@
/*
* st-texture-frame.h: Expandible texture actor
*
* Copyright 2007, 2008 OpenedHand Ltd
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TEXTURE_FRAME_H__
#define __ST_TEXTURE_FRAME_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_TEXTURE_FRAME (st_texture_frame_get_type ())
#define ST_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrame))
#define ST_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
#define ST_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TEXTURE_FRAME))
#define ST_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TEXTURE_FRAME))
#define ST_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
typedef struct _StTextureFrame StTextureFrame;
typedef struct _StTextureFramePrivate StTextureFramePrivate;
typedef struct _StTextureFrameClass StTextureFrameClass;
/**
* StTextureFrame:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StTextureFrame
{
/*< private >*/
ClutterActor parent_instance;
StTextureFramePrivate *priv;
};
struct _StTextureFrameClass
{
ClutterActorClass parent_class;
/* padding for future expansion */
void (*_clutter_box_1) (void);
void (*_clutter_box_2) (void);
void (*_clutter_box_3) (void);
void (*_clutter_box_4) (void);
};
GType st_texture_frame_get_type (void) G_GNUC_CONST;
ClutterActor * st_texture_frame_new (ClutterTexture *texture,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left);
void st_texture_frame_set_parent_texture (StTextureFrame *frame,
ClutterTexture *texture);
ClutterTexture *st_texture_frame_get_parent_texture (StTextureFrame *frame);
void st_texture_frame_set_frame (StTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left);
void st_texture_frame_get_frame (StTextureFrame *frame,
gfloat *top,
gfloat *right,
gfloat *bottom,
gfloat *left);
G_END_DECLS
#endif /* __ST_TEXTURE_FRAME_H__ */

710
src/st/st-tooltip.c Normal file
View File

@ -0,0 +1,710 @@
/*
* st-tooltip.c: Plain tooltip actor
*
* Copyright 2008, 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
/**
* SECTION:st-tooltip
* @short_description: A tooltip widget
*
* #StTooltip implements a single tooltip. It should not normally be created
* by the application but by the widget implementing tooltip capabilities, for
* example, #st_button_set_tooltip().
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "st-tooltip.h"
#include "st-widget.h"
#include "st-stylable.h"
#include "st-private.h"
enum
{
PROP_0,
PROP_LABEL,
PROP_TIP_AREA
};
#define ST_TOOLTIP_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TOOLTIP, StTooltipPrivate))
struct _StTooltipPrivate
{
ClutterActor *label;
gfloat arrow_offset;
gboolean actor_below;
ClutterGeometry *tip_area;
};
G_DEFINE_TYPE (StTooltip, st_tooltip, ST_TYPE_WIDGET);
static void
st_tooltip_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StTooltip *tooltip = ST_TOOLTIP (gobject);
switch (prop_id)
{
case PROP_LABEL:
st_tooltip_set_label (tooltip, g_value_get_string (value));
break;
case PROP_TIP_AREA:
st_tooltip_set_tip_area (tooltip, g_value_get_boxed (value));
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_tooltip_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StTooltipPrivate *priv = ST_TOOLTIP (gobject)->priv;
switch (prop_id)
{
case PROP_LABEL:
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
break;
case PROP_TIP_AREA:
g_value_set_boxed (value, priv->tip_area);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_tooltip_style_changed (StWidget *self)
{
ClutterColor *color = NULL;
StTooltipPrivate *priv;
gchar *font_name;
gchar *font_string;
gint font_size;
priv = ST_TOOLTIP (self)->priv;
st_stylable_get (ST_STYLABLE (self),
"color", &color,
"font-family", &font_name,
"font-size", &font_size,
NULL);
if (color)
{
clutter_text_set_color (CLUTTER_TEXT (priv->label), color);
clutter_color_free (color);
}
if (font_name || font_size)
{
if (font_name && font_size)
{
font_string = g_strdup_printf ("%s %dpx", font_name, font_size);
g_free (font_name);
}
else
if (font_size)
font_string = g_strdup_printf ("%dpx", font_size);
else
font_string = font_name;
clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
g_free (font_string);
}
}
static void
st_tooltip_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
gfloat min_label_w, natural_label_w;
gfloat label_height, arrow_height;
ClutterActor *arrow_image;
StPadding padding;
st_widget_get_padding (ST_WIDGET (self), &padding);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image)
{
clutter_actor_get_preferred_height (arrow_image,
-1,
NULL,
&arrow_height);
}
else
{
arrow_height = 0;
}
if (for_height > -1)
{
label_height = for_height - arrow_height - padding.top - padding.bottom;
}
else
{
label_height = -1;
}
if (priv->label)
{
clutter_actor_get_preferred_width (priv->label,
label_height,
&min_label_w,
&natural_label_w);
}
else
{
min_label_w = 0;
natural_label_w = 0;
}
if (min_width_p)
{
*min_width_p = padding.left + padding.right + min_label_w;
}
if (natural_width_p)
{
*natural_width_p = padding.left + padding.right + natural_label_w;
}
}
static void
st_tooltip_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
gfloat arrow_height;
gfloat min_label_h, natural_label_h;
gfloat label_width;
ClutterActor *arrow_image;
StPadding padding;
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image && !priv->actor_below)
{
clutter_actor_get_preferred_height (arrow_image,
-1,
NULL,
&arrow_height);
}
else
{
arrow_height = 0;
}
st_widget_get_padding (ST_WIDGET (self), &padding);
if (for_width > -1)
{
label_width = for_width - padding.left - padding.right;
}
else
{
label_width = -1;
}
if (priv->label)
{
clutter_actor_get_preferred_height (priv->label,
label_width,
&min_label_h,
&natural_label_h);
}
else
{
min_label_h = 0;
natural_label_h = 0;
}
if (min_height_p)
{
*min_height_p = padding.top + padding.bottom
+ arrow_height + min_label_h;
}
if (natural_height_p)
{
*natural_height_p = padding.top + padding.bottom
+ arrow_height + natural_label_h;
}
}
static void
st_tooltip_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
ClutterActorBox child_box, arrow_box;
gfloat arrow_height, arrow_width;
ClutterActor *border_image, *arrow_image;
StPadding padding;
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->allocate (self,
box,
flags);
st_widget_get_padding (ST_WIDGET (self), &padding);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image && !priv->actor_below)
{
clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height);
clutter_actor_get_preferred_width (arrow_image, -1, NULL, &arrow_width);
arrow_box.x1 = (float)(priv->arrow_offset) - (int)(arrow_width / 2);
arrow_box.y1 = 0;
arrow_box.x2 = arrow_box.x1 + arrow_width;
arrow_box.y2 = arrow_box.y1 + arrow_height;
clutter_actor_allocate (arrow_image, &arrow_box, flags);
}
else
{
arrow_height = 0;
arrow_width = 0;
}
child_box.x1 = child_box.y1 = 0;
child_box.x2 = (box->x2 - box->x1);
child_box.y2 = (box->y2 - box->y1);
/* remove the space that is used by the arrow */
child_box.y1 += arrow_height;
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_allocate (border_image, &child_box, flags);
if (priv->label)
{
/* now remove the padding */
child_box.y1 += padding.top;
child_box.x1 += padding.left;
child_box.x2 -= padding.right;
child_box.y2 -= padding.bottom;
clutter_actor_allocate (priv->label, &child_box, flags);
}
}
static void
st_tooltip_paint (ClutterActor *self)
{
ClutterActor *border_image, *arrow_image;
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_paint (border_image);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image && !priv->actor_below)
clutter_actor_paint (arrow_image);
clutter_actor_paint (priv->label);
}
static void
st_tooltip_map (ClutterActor *self)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
ClutterActor *border_image, *arrow_image;
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->map (self);
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_map (border_image);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image)
clutter_actor_map (arrow_image);
clutter_actor_map (priv->label);
}
static void
st_tooltip_unmap (ClutterActor *self)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
ClutterActor *border_image, *arrow_image;
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->unmap (self);
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_unmap (border_image);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image)
clutter_actor_unmap (arrow_image);
clutter_actor_unmap (priv->label);
}
static void
st_tooltip_class_init (StTooltipClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StTooltipPrivate));
gobject_class->set_property = st_tooltip_set_property;
gobject_class->get_property = st_tooltip_get_property;
actor_class->get_preferred_width = st_tooltip_get_preferred_width;
actor_class->get_preferred_height = st_tooltip_get_preferred_height;
actor_class->allocate = st_tooltip_allocate;
actor_class->paint = st_tooltip_paint;
actor_class->map = st_tooltip_map;
actor_class->unmap = st_tooltip_unmap;
pspec = g_param_spec_string ("label",
"Label",
"Label of the tooltip",
NULL, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
pspec = g_param_spec_boxed ("tip-area",
"Tip Area",
"Area on the stage the tooltip applies to",
CLUTTER_TYPE_GEOMETRY,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TIP_AREA, pspec);
}
static void
st_tooltip_init (StTooltip *tooltip)
{
tooltip->priv = ST_TOOLTIP_GET_PRIVATE (tooltip);
tooltip->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
"line-alignment", PANGO_ALIGN_CENTER,
"ellipsize", PANGO_ELLIPSIZE_END,
"use-markup", TRUE,
NULL);
tooltip->priv->tip_area = NULL;
clutter_actor_set_parent (CLUTTER_ACTOR (tooltip->priv->label),
CLUTTER_ACTOR (tooltip));
g_object_set (tooltip, "show-on-set-parent", FALSE, NULL);
clutter_actor_set_reactive (CLUTTER_ACTOR (tooltip), FALSE);
g_signal_connect (tooltip, "style-changed",
G_CALLBACK (st_tooltip_style_changed), NULL);
}
static void
st_tooltip_update_position (StTooltip *tooltip)
{
StTooltipPrivate *priv = tooltip->priv;
ClutterGeometry *tip_area = tooltip->priv->tip_area;
gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y;
gfloat stage_w, stage_h;
ClutterActor *stage;
/* ensure the tooltip with is not fixed size */
clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1);
/* if no area set, just position ourselves top left */
if (!priv->tip_area)
{
clutter_actor_set_position ((ClutterActor*) tooltip, 0, 0);
return;
}
/* we need to have a style in case there are padding values to take into
* account when calculating width/height */
st_widget_ensure_style ((StWidget *) tooltip);
/* find out the tooltip's size */
clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h);
/* attempt to place the tooltip */
tooltip_x = (int)(tip_area->x + (tip_area->width / 2) - (tooltip_w / 2));
tooltip_y = (int)(tip_area->y + tip_area->height);
stage = clutter_actor_get_stage ((ClutterActor *) tooltip);
if (!stage)
{
return;
}
clutter_actor_get_size (stage, &stage_w, &stage_h);
/* make sure the tooltip is not off screen vertically */
if (tooltip_w > stage_w)
{
tooltip_x = 0;
clutter_actor_set_width ((ClutterActor*) tooltip, stage_w);
}
else if (tooltip_x < 0)
{
tooltip_x = 0;
}
else if (tooltip_x + tooltip_w > stage_w)
{
tooltip_x = (int)(stage_w) - tooltip_w;
}
/* make sure the tooltip is not off screen horizontally */
if (tooltip_y + tooltip_h > stage_h)
{
priv->actor_below = TRUE;
/* re-query size as may have changed */
clutter_actor_get_preferred_height ((ClutterActor*) tooltip,
-1, NULL, &tooltip_h);
tooltip_y = tip_area->y - tooltip_h;
}
else
{
priv->actor_below = FALSE;
}
/* calculate the arrow offset */
priv->arrow_offset = tip_area->x + tip_area->width / 2 - tooltip_x;
clutter_actor_set_position ((ClutterActor*) tooltip, tooltip_x, tooltip_y);
}
/**
* st_tooltip_get_label:
* @tooltip: a #StTooltip
*
* Get the text displayed on the tooltip
*
* Returns: the text for the tooltip. This must not be freed by the application
*/
G_CONST_RETURN gchar *
st_tooltip_get_label (StTooltip *tooltip)
{
g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL);
return clutter_text_get_text (CLUTTER_TEXT (tooltip->priv->label));
}
/**
* st_tooltip_set_label:
* @tooltip: a #StTooltip
* @text: text to set the label to
*
* Sets the text displayed on the tooltip
*/
void
st_tooltip_set_label (StTooltip *tooltip,
const gchar *text)
{
StTooltipPrivate *priv;
g_return_if_fail (ST_IS_TOOLTIP (tooltip));
priv = tooltip->priv;
clutter_text_set_text (CLUTTER_TEXT (priv->label), text);
g_object_notify (G_OBJECT (tooltip), "label");
}
/**
* st_tooltip_show:
* @tooltip: a #StTooltip
*
* Show the tooltip relative to the associated widget.
*/
void
st_tooltip_show (StTooltip *tooltip)
{
StTooltipPrivate *priv;
ClutterActor *parent;
ClutterActor *stage;
ClutterActor *self = CLUTTER_ACTOR (tooltip);
ClutterAnimation *animation;
/* make sure we're not currently already animating (e.g. hiding) */
animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
if (animation)
clutter_animation_completed (animation);
priv = tooltip->priv;
parent = clutter_actor_get_parent (self);
stage = clutter_actor_get_stage (self);
if (!stage)
{
g_warning ("StTooltip is not on any stage.");
return;
}
/* make sure we're parented on the stage */
if (G_UNLIKELY (parent != stage))
{
g_object_ref (self);
clutter_actor_unparent (self);
clutter_actor_set_parent (self, stage);
g_object_unref (self);
parent = stage;
}
/* raise the tooltip to the top */
clutter_container_raise_child (CLUTTER_CONTAINER (stage),
CLUTTER_ACTOR (tooltip),
NULL);
st_tooltip_update_position (tooltip);
/* finally show the tooltip... */
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->show (self);
/* and give it some bounce! */
g_object_set (G_OBJECT (self),
"scale-center-x", priv->arrow_offset,
"scale-center-y", (priv->actor_below) ? clutter_actor_get_height (self) : 0,
NULL);
clutter_actor_set_scale (self, 0.0, 0.0);
clutter_actor_animate (self, CLUTTER_EASE_OUT_ELASTIC,
500,
"scale-x", 1.0,
"scale-y", 1.0,
NULL);
}
static void
st_tooltip_hide_complete (ClutterAnimation *animation,
ClutterActor *actor)
{
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->hide (actor);
g_signal_handlers_disconnect_by_func (actor,
st_tooltip_hide_complete,
actor);
}
/**
* st_tooltip_hide:
* @tooltip: a #StTooltip
*
* Hide the tooltip
*/
void
st_tooltip_hide (StTooltip *tooltip)
{
ClutterAnimation *animation;
g_return_if_fail (ST_TOOLTIP (tooltip));
/* make sure we're not currently already animating (e.g. hiding) */
animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
if (animation)
clutter_animation_completed (animation);
g_object_set (G_OBJECT (tooltip),
"scale-center-x", tooltip->priv->arrow_offset,
NULL);
animation =
clutter_actor_animate (CLUTTER_ACTOR (tooltip), CLUTTER_EASE_IN_SINE,
150,
"scale-x", 0.0,
"scale-y", 0.0,
NULL);
g_signal_connect (animation, "completed",
G_CALLBACK (st_tooltip_hide_complete), tooltip);
}
/**
* st_tooltip_set_tip_area:
* @tooltip: A #StTooltip
* @area: A #ClutterGeometry
*
* Set the area on the stage that the tooltip applies to.
*/
void
st_tooltip_set_tip_area (StTooltip *tooltip,
const ClutterGeometry *area)
{
g_return_if_fail (ST_IS_TOOLTIP (tooltip));
if (tooltip->priv->tip_area)
g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area);
tooltip->priv->tip_area = g_boxed_copy (CLUTTER_TYPE_GEOMETRY, area);
st_tooltip_update_position (tooltip);
}
/**
* st_tooltip_get_tip_area:
* @tooltip: A #StTooltip
*
* Retrieve the area on the stage that the tooltip currently applies to
*
* Returns: the #ClutterGeometry, owned by the tooltip which must not be freed
* by the application.
*/
G_CONST_RETURN ClutterGeometry*
st_tooltip_get_tip_area (StTooltip *tooltip)
{
g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL);
return tooltip->priv->tip_area;
}

80
src/st/st-tooltip.h Normal file
View File

@ -0,0 +1,80 @@
/*
* st-tooltip.h: Plain tooltip actor
*
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TOOLTIP_H__
#define __ST_TOOLTIP_H__
G_BEGIN_DECLS
#include <st/st-bin.h>
#define ST_TYPE_TOOLTIP (st_tooltip_get_type ())
#define ST_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TOOLTIP, StTooltip))
#define ST_IS_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TOOLTIP))
#define ST_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TOOLTIP, StTooltipClass))
#define ST_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TOOLTIP))
#define ST_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TOOLTIP, StTooltipClass))
typedef struct _StTooltip StTooltip;
typedef struct _StTooltipPrivate StTooltipPrivate;
typedef struct _StTooltipClass StTooltipClass;
/**
* StTooltip:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StTooltip
{
/*< private >*/
StBin parent_instance;
StTooltipPrivate *priv;
};
struct _StTooltipClass
{
StBinClass parent_class;
};
GType st_tooltip_get_type (void) G_GNUC_CONST;
G_CONST_RETURN gchar *st_tooltip_get_label (StTooltip *tooltip);
void st_tooltip_set_label (StTooltip *tooltip,
const gchar *text);
void st_tooltip_show (StTooltip *tooltip);
void st_tooltip_hide (StTooltip *tooltip);
void st_tooltip_set_tip_area (StTooltip *tooltip,
const ClutterGeometry *area);
G_CONST_RETURN ClutterGeometry* st_tooltip_get_tip_area (StTooltip *tooltip);
G_END_DECLS
#endif /* __ST_TOOLTIP_H__ */

73
src/st/st-types.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/**
* SECTION:st-types
* @short_description: type definitions used throughout St
*
* Common types for StWidgets.
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TYPES_H__
#define __ST_TYPES_H__
#include <glib-object.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_BORDER_IMAGE (st_border_image_get_type ())
#define ST_TYPE_PADDING (st_padding_get_type ())
typedef struct _StPadding StPadding;
GType st_border_image_get_type (void) G_GNUC_CONST;
/**
* StPadding:
* @top: padding from the top
* @right: padding from the right
* @bottom: padding from the bottom
* @left: padding from the left
*
* The padding from the internal border of the parent container.
*/
struct _StPadding
{
gfloat top;
gfloat right;
gfloat bottom;
gfloat left;
};
GType st_padding_get_type (void) G_GNUC_CONST;
typedef enum {
ST_ALIGN_START,
ST_ALIGN_MIDDLE,
ST_ALIGN_END
} StAlign;
G_END_DECLS
#endif /* __ST_TYPES_H__ */

1357
src/st/st-widget.c Normal file

File diff suppressed because it is too large Load Diff

108
src/st/st-widget.h Normal file
View File

@ -0,0 +1,108 @@
/*
* st-widget.h: Base class for St actors
*
* Copyright 2007 OpenedHand
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_WIDGET_H__
#define __ST_WIDGET_H__
#include <clutter/clutter.h>
#include <st/st-types.h>
G_BEGIN_DECLS
#define ST_TYPE_WIDGET (st_widget_get_type ())
#define ST_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_WIDGET, StWidget))
#define ST_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_WIDGET))
#define ST_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_WIDGET, StWidgetClass))
#define ST_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_WIDGET))
#define ST_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_WIDGET, StWidgetClass))
typedef struct _StWidget StWidget;
typedef struct _StWidgetPrivate StWidgetPrivate;
typedef struct _StWidgetClass StWidgetClass;
/**
* StWidget:
*
* Base class for stylable actors. The contents of the #StWidget
* structure are private and should only be accessed through the
* public API.
*/
struct _StWidget
{
/*< private >*/
ClutterActor parent_instance;
StWidgetPrivate *priv;
};
/**
* StWidgetClass:
*
* Base class for stylable actors.
*/
struct _StWidgetClass
{
/*< private >*/
ClutterActorClass parent_class;
/* vfuncs */
void (* draw_background) (StWidget *self,
ClutterActor *background,
const ClutterColor *color);
};
GType st_widget_get_type (void) G_GNUC_CONST;
void st_widget_set_style_pseudo_class (StWidget *actor,
const gchar *pseudo_class);
G_CONST_RETURN gchar *st_widget_get_style_pseudo_class (StWidget *actor);
void st_widget_set_style_class_name (StWidget *actor,
const gchar *style_class);
G_CONST_RETURN gchar *st_widget_get_style_class_name (StWidget *actor);
void st_widget_set_has_tooltip (StWidget *widget,
gboolean has_tooltip);
gboolean st_widget_get_has_tooltip (StWidget *widget);
void st_widget_set_tooltip_text (StWidget *widget,
const gchar *text);
const gchar* st_widget_get_tooltip_text (StWidget *widget);
void st_widget_show_tooltip (StWidget *widget);
void st_widget_hide_tooltip (StWidget *widget);
void st_widget_ensure_style (StWidget *widget);
/* Only to be used by sub-classes of StWidget */
ClutterActor *st_widget_get_background_image (StWidget *actor);
ClutterActor *st_widget_get_border_image (StWidget *actor);
void st_widget_get_padding (StWidget *widget,
StPadding *padding);
void st_widget_draw_background (StWidget *widget);
G_END_DECLS
#endif /* __ST_WIDGET_H__ */