diff --git a/configure.ac b/configure.ac index edb960394..b65b1177b 100644 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0 gobject-introspection-1.0 >= 0.6.5) PKG_CHECK_MODULES(TIDY, clutter-1.0) -PKG_CHECK_MODULES(NBTK, clutter-1.0 gtk+-2.0 libccss-1 >= 0.3.1) +PKG_CHECK_MODULES(NBTK, clutter-1.0 gtk+-2.0 libccss-1 >= 0.3.1 clutter-imcontext-0.1) PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0) PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0) PKG_CHECK_MODULES(TRAY, gtk+-2.0) diff --git a/src/Makefile-nbtk.am b/src/Makefile-nbtk.am index b9b6a7dc0..d08e0b598 100644 --- a/src/Makefile-nbtk.am +++ b/src/Makefile-nbtk.am @@ -69,6 +69,9 @@ nbtk_source_h = \ nbtk/nbtk-box-layout.h \ nbtk/nbtk-box-layout-child.h \ nbtk/nbtk-button.h \ + nbtk/nbtk-clipboard.h \ + nbtk/nbtk-entry.h \ + nbtk/nbtk-label.h \ nbtk/nbtk-private.h \ nbtk/nbtk-scrollable.h \ nbtk/nbtk-scroll-bar.h \ @@ -91,6 +94,9 @@ nbtk_source_c = \ nbtk/nbtk-box-layout.c \ nbtk/nbtk-box-layout-child.c \ nbtk/nbtk-button.c \ + nbtk/nbtk-clipboard.c \ + nbtk/nbtk-entry.c \ + nbtk/nbtk-label.c \ nbtk/nbtk-private.c \ nbtk/nbtk-scrollable.c \ nbtk/nbtk-scroll-bar.c \ diff --git a/src/nbtk/nbtk-clipboard.c b/src/nbtk/nbtk-clipboard.c new file mode 100644 index 000000000..f10337224 --- /dev/null +++ b/src/nbtk/nbtk-clipboard.c @@ -0,0 +1,367 @@ +/* + * nbtk-clipboard.c: clipboard object + * + * 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: Thomas Wood + * + */ + + +#include "nbtk-clipboard.h" +#include +#include +#include +#include + +G_DEFINE_TYPE (NbtkClipboard, nbtk_clipboard, G_TYPE_OBJECT) + +#define CLIPBOARD_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), NBTK_TYPE_CLIPBOARD, NbtkClipboardPrivate)) + +struct _NbtkClipboardPrivate +{ + Window clipboard_window; + gchar *clipboard_text; + + Atom *supported_targets; + gint n_targets; +}; + +typedef struct _EventFilterData EventFilterData; +struct _EventFilterData +{ + NbtkClipboard *clipboard; + NbtkClipboardCallbackFunc callback; + gpointer user_data; +}; + +static Atom __atom_clip = None; +static Atom __utf8_string = None; +static Atom __atom_targets = None; + +static void +nbtk_clipboard_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +nbtk_clipboard_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +nbtk_clipboard_dispose (GObject *object) +{ + G_OBJECT_CLASS (nbtk_clipboard_parent_class)->dispose (object); +} + +static void +nbtk_clipboard_finalize (GObject *object) +{ + NbtkClipboardPrivate *priv = ((NbtkClipboard *) object)->priv; + + g_free (priv->clipboard_text); + priv->clipboard_text = NULL; + + g_free (priv->supported_targets); + priv->supported_targets = NULL; + priv->n_targets = 0; + + G_OBJECT_CLASS (nbtk_clipboard_parent_class)->finalize (object); +} + +static ClutterX11FilterReturn +nbtk_clipboard_provider (XEvent *xev, + ClutterEvent *cev, + NbtkClipboard *clipboard) +{ + XSelectionEvent notify_event; + XSelectionRequestEvent *req_event; + + if (xev->type != SelectionRequest) + return CLUTTER_X11_FILTER_CONTINUE; + + req_event = &xev->xselectionrequest; + + clutter_x11_trap_x_errors (); + + if (req_event->target == __atom_targets) + { + XChangeProperty (req_event->display, + req_event->requestor, + req_event->property, + XA_ATOM, + 32, + PropModeReplace, + (guchar*) clipboard->priv->supported_targets, + clipboard->priv->n_targets); + } + else + { + XChangeProperty (req_event->display, + req_event->requestor, + req_event->property, + req_event->target, + 8, + PropModeReplace, + (guchar*) clipboard->priv->clipboard_text, + strlen (clipboard->priv->clipboard_text)); + } + + notify_event.type = SelectionNotify; + notify_event.display = req_event->display; + notify_event.requestor = req_event->requestor; + notify_event.selection = req_event->selection; + notify_event.target = req_event->target; + notify_event.time = req_event->time; + + if (req_event->property == None) + notify_event.property = req_event->target; + else + notify_event.property = req_event->property; + + /* notify the requestor that they have a copy of the selection */ + XSendEvent (req_event->display, req_event->requestor, False, 0, + (XEvent *) ¬ify_event); + /* Make it happen non async */ + XSync (clutter_x11_get_default_display(), FALSE); + + clutter_x11_untrap_x_errors (); /* FIXME: Warn here on fail ? */ + + return CLUTTER_X11_FILTER_REMOVE; +} + + +static void +nbtk_clipboard_class_init (NbtkClipboardClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NbtkClipboardPrivate)); + + object_class->get_property = nbtk_clipboard_get_property; + object_class->set_property = nbtk_clipboard_set_property; + object_class->dispose = nbtk_clipboard_dispose; + object_class->finalize = nbtk_clipboard_finalize; +} + +static void +nbtk_clipboard_init (NbtkClipboard *self) +{ + Display *dpy; + NbtkClipboardPrivate *priv; + + priv = self->priv = CLIPBOARD_PRIVATE (self); + + priv->clipboard_window = + XCreateSimpleWindow (clutter_x11_get_default_display (), + clutter_x11_get_root_window (), + -1, -1, 1, 1, 0, 0, 0); + + dpy = clutter_x11_get_default_display (); + + /* Only create once */ + if (__atom_clip == None) + __atom_clip = XInternAtom (dpy, "CLIPBOARD", 0); + + if (__utf8_string == None) + __utf8_string = XInternAtom (dpy, "UTF8_STRING", 0); + + if (__atom_targets == None) + __atom_targets = XInternAtom (dpy, "TARGETS", 0); + + priv->n_targets = 2; + priv->supported_targets = g_new (Atom, priv->n_targets); + + priv->supported_targets[0] = __utf8_string; + priv->supported_targets[1] = __atom_targets; + + clutter_x11_add_filter ((ClutterX11FilterFunc) nbtk_clipboard_provider, + self); +} + +static ClutterX11FilterReturn +nbtk_clipboard_x11_event_filter (XEvent *xev, + ClutterEvent *cev, + EventFilterData *filter_data) +{ + Atom actual_type; + int actual_format, result; + unsigned long nitems, bytes_after; + unsigned char *data = NULL; + + if(xev->type != SelectionNotify) + return CLUTTER_X11_FILTER_CONTINUE; + + if (xev->xselection.property == None) + { + /* clipboard empty */ + filter_data->callback (filter_data->clipboard, + NULL, + filter_data->user_data); + + clutter_x11_remove_filter ((ClutterX11FilterFunc) nbtk_clipboard_x11_event_filter, + filter_data); + g_free (filter_data); + return CLUTTER_X11_FILTER_REMOVE; + } + + clutter_x11_trap_x_errors (); + + result = XGetWindowProperty (xev->xselection.display, + xev->xselection.requestor, + xev->xselection.property, + 0L, G_MAXINT, + True, + AnyPropertyType, + &actual_type, + &actual_format, + &nitems, + &bytes_after, + &data); + + if (clutter_x11_untrap_x_errors () || result != Success) + { + /* FIXME: handle failure better */ + g_warning ("Clipboard: prop retrival failed"); + } + + filter_data->callback (filter_data->clipboard, (char*) data, + filter_data->user_data); + + clutter_x11_remove_filter + ((ClutterX11FilterFunc) nbtk_clipboard_x11_event_filter, + filter_data); + + g_free (filter_data); + + if (data) + XFree (data); + + return CLUTTER_X11_FILTER_REMOVE; +} + +/** + * nbtk_clipboard_get_default: + * + * Get the global #NbtkClipboard object that represents the clipboard. + * + * Returns: a #NbtkClipboard owned by Nbtk and must not be unrefferenced or + * freed. + */ +NbtkClipboard* +nbtk_clipboard_get_default () +{ + static NbtkClipboard *default_clipboard = NULL; + + if (!default_clipboard) + { + default_clipboard = g_object_new (NBTK_TYPE_CLIPBOARD, NULL); + } + + return default_clipboard; +} + +/** + * nbtk_clipboard_get_text: + * @clipboard: A #NbtkCliboard + * @callback: function to be called when the text is retreived + * @user_data: data to be passed to the callback + * + * Request the data from the clipboard in text form. @callback is executed + * when the data is retreived. + * + */ +void +nbtk_clipboard_get_text (NbtkClipboard *clipboard, + NbtkClipboardCallbackFunc callback, + gpointer user_data) +{ + EventFilterData *data; + + Display *dpy; + + g_return_if_fail (NBTK_IS_CLIPBOARD (clipboard)); + g_return_if_fail (callback != NULL); + + data = g_new0 (EventFilterData, 1); + data->clipboard = clipboard; + data->callback = callback; + data->user_data = user_data; + + clutter_x11_add_filter ((ClutterX11FilterFunc)nbtk_clipboard_x11_event_filter, + data); + + dpy = clutter_x11_get_default_display (); + + clutter_x11_trap_x_errors (); /* safety on */ + + XConvertSelection (dpy, + __atom_clip, + __utf8_string, __utf8_string, + clipboard->priv->clipboard_window, + CurrentTime); + + clutter_x11_untrap_x_errors (); +} + +/** + * nbtk_clipboard_set_text: + * @clipboard: A #NbtkClipboard + * @text: text to copy to the clipboard + * + * Sets text as the current contents of the clipboard. + * + */ +void +nbtk_clipboard_set_text (NbtkClipboard *clipboard, + const gchar *text) +{ + NbtkClipboardPrivate *priv; + Display *dpy; + + g_return_if_fail (NBTK_IS_CLIPBOARD (clipboard)); + g_return_if_fail (text != NULL); + + priv = clipboard->priv; + + /* make a copy of the text */ + g_free (priv->clipboard_text); + priv->clipboard_text = g_strdup (text); + + /* tell X we own the clipboard selection */ + dpy = clutter_x11_get_default_display (); + + clutter_x11_trap_x_errors (); + + XSetSelectionOwner (dpy, __atom_clip, priv->clipboard_window, CurrentTime); + XSync (dpy, FALSE); + + clutter_x11_untrap_x_errors (); +} diff --git a/src/nbtk/nbtk-clipboard.h b/src/nbtk/nbtk-clipboard.h new file mode 100644 index 000000000..75925484b --- /dev/null +++ b/src/nbtk/nbtk-clipboard.h @@ -0,0 +1,94 @@ +/* + * nbtk-clipboard.h: clipboard object + * + * 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: Thomas Wood + * + */ + +#ifndef _NBTK_CLIPBOARD_H +#define _NBTK_CLIPBOARD_H + +#include + +G_BEGIN_DECLS + +#define NBTK_TYPE_CLIPBOARD nbtk_clipboard_get_type() + +#define NBTK_CLIPBOARD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + NBTK_TYPE_CLIPBOARD, NbtkClipboard)) + +#define NBTK_CLIPBOARD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + NBTK_TYPE_CLIPBOARD, NbtkClipboardClass)) + +#define NBTK_IS_CLIPBOARD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + NBTK_TYPE_CLIPBOARD)) + +#define NBTK_IS_CLIPBOARD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + NBTK_TYPE_CLIPBOARD)) + +#define NBTK_CLIPBOARD_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + NBTK_TYPE_CLIPBOARD, NbtkClipboardClass)) + +typedef struct _NbtkClipboard NbtkClipboard; +typedef struct _NbtkClipboardClass NbtkClipboardClass; +typedef struct _NbtkClipboardPrivate NbtkClipboardPrivate; + +/** + * NbtkClipboard: + * + * The contents of this structure is private and should only be accessed using + * the provided API. + */ +struct _NbtkClipboard +{ + /*< private >*/ + GObject parent; + NbtkClipboardPrivate *priv; +}; + +struct _NbtkClipboardClass +{ + GObjectClass parent_class; +}; + +/** + * NbtkClipboardCallbackFunc: + * @clipboard: A #NbtkClipboard + * @text: text from the clipboard + * @user_data: user data + * + * Callback function called when text is retrieved from the clipboard. + */ +typedef void (*NbtkClipboardCallbackFunc) (NbtkClipboard *clipboard, + const gchar *text, + gpointer user_data); + +GType nbtk_clipboard_get_type (void); + +NbtkClipboard* nbtk_clipboard_get_default (); +void nbtk_clipboard_get_text (NbtkClipboard *clipboard, NbtkClipboardCallbackFunc callback, gpointer user_data); +void nbtk_clipboard_set_text (NbtkClipboard *clipboard, const gchar *text); + +G_END_DECLS + +#endif /* _NBTK_CLIPBOARD_H */ diff --git a/src/nbtk/nbtk-entry.c b/src/nbtk/nbtk-entry.c new file mode 100644 index 000000000..9cc762a63 --- /dev/null +++ b/src/nbtk/nbtk-entry.c @@ -0,0 +1,960 @@ +/* + * nbtk-entry.c: Plain entry 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. + * + * Written by: Thomas Wood + * + */ + +/** + * SECTION:nbtk-entry + * @short_description: Widget for displaying text + * + * #NbtkEntry is a simple widget for displaying text. It derives from + * #NbtkWidget to add extra style and placement functionality over + * #ClutterText. The internal #ClutterText is publicly accessibly to allow + * applications to set further properties. + * + * #NbtkEntry supports the following pseudo style states: + * + * + * focus: the widget has focus + * + * + * indeterminate: the widget is showing the hint text + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +#include + +#include +#include + +#include "nbtk-entry.h" + +#include "nbtk-widget.h" +#include "nbtk-stylable.h" +#include "nbtk-texture-cache.h" +#include "nbtk-marshal.h" +#include "nbtk-clipboard.h" + +#define HAS_FOCUS(actor) (clutter_actor_get_stage (actor) && clutter_stage_get_key_focus ((ClutterStage *) clutter_actor_get_stage (actor)) == actor) + + +/* properties */ +enum +{ + PROP_0, + + PROP_ENTRY, + PROP_HINT +}; + +/* signals */ +enum +{ + PRIMARY_ICON_CLICKED, + SECONDARY_ICON_CLICKED, + + LAST_SIGNAL +}; + +#define NBTK_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_ENTRY, NbtkEntryPrivate)) +#define NBTK_ENTRY_PRIV(x) ((NbtkEntry *) x)->priv + + +struct _NbtkEntryPrivate +{ + ClutterActor *entry; + gchar *hint; + + ClutterActor *primary_icon; + ClutterActor *secondary_icon; + + gfloat spacing; +}; + +static guint entry_signals[LAST_SIGNAL] = { 0, }; + +static void nbtk_stylable_iface_init (NbtkStylableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (NbtkEntry, nbtk_entry, NBTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE, + nbtk_stylable_iface_init)); + +static void +nbtk_entry_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NbtkEntry *entry = NBTK_ENTRY (gobject); + + switch (prop_id) + { + case PROP_ENTRY: + nbtk_entry_set_text (entry, g_value_get_string (value)); + break; + + case PROP_HINT: + nbtk_entry_set_hint_text (entry, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +nbtk_entry_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (gobject); + + switch (prop_id) + { + case PROP_ENTRY: + g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->entry))); + break; + + case PROP_HINT: + g_value_set_string (value, priv->hint); + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +nbtk_entry_dispose (GObject *object) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (object); + + if (priv->entry) + { + clutter_actor_unparent (priv->entry); + priv->entry = NULL; + } +} + +static void +nbtk_entry_finalize (GObject *object) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (object); + + g_free (priv->hint); + priv->hint = NULL; +} + +static void +nbtk_stylable_iface_init (NbtkStylableIface *iface) +{ + static gboolean is_initialized = FALSE; + + if (!is_initialized) + { + GParamSpec *pspec; + static const ClutterColor default_color + = { 0x0, 0x9c, 0xcf, 0xff }; + + is_initialized = TRUE; + + pspec = clutter_param_spec_color ("caret-color", + "Caret Color", + "Color of the entry's caret", + &default_color, + G_PARAM_READWRITE); + nbtk_stylable_iface_install_property (iface, NBTK_TYPE_ENTRY, pspec); + + pspec = clutter_param_spec_color ("selection-background-color", + "Selection Background Color", + "Color of the entry's selection", + &default_color, + G_PARAM_READWRITE); + nbtk_stylable_iface_install_property (iface, NBTK_TYPE_ENTRY, pspec); + } +} + +static void +nbtk_entry_style_changed (NbtkWidget *self) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (self); + ClutterColor *color = NULL; + ClutterColor *caret_color = NULL; + ClutterColor *selection_background_color = NULL; + gchar *font_name; + gchar *font_string; + gint font_size; + + nbtk_stylable_get (NBTK_STYLABLE (self), + "color", &color, + "caret-color", &caret_color, + "selection-background-color", &selection_background_color, + "font-family", &font_name, + "font-size", &font_size, + NULL); + + if (color) + { + clutter_text_set_color (CLUTTER_TEXT (priv->entry), color); + clutter_color_free (color); + } + + if (caret_color) + { + clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), caret_color); + clutter_color_free (caret_color); + } + + if (selection_background_color) + { + clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), + selection_background_color); + clutter_color_free (selection_background_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->entry), font_string); + g_free (font_string); + } +} + +static void +nbtk_entry_get_preferred_width (ClutterActor *actor, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + NbtkPadding padding; + gfloat icon_w; + + nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding); + + for_height -= padding.top + padding.bottom; + + clutter_actor_get_preferred_width (priv->entry, for_height, + min_width_p, + natural_width_p); + + if (priv->primary_icon) + { + clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w); + + if (min_width_p) + *min_width_p += icon_w + priv->spacing; + + if (natural_width_p) + *natural_width_p += icon_w + priv->spacing; + } + + if (priv->secondary_icon) + { + clutter_actor_get_preferred_width (priv->secondary_icon, + -1, NULL, &icon_w); + + if (min_width_p) + *min_width_p += icon_w + priv->spacing; + + if (natural_width_p) + *natural_width_p += icon_w + priv->spacing; + } + + if (min_width_p) + *min_width_p += padding.left + padding.right; + + if (natural_width_p) + *natural_width_p += padding.left + padding.right; +} + +static void +nbtk_entry_get_preferred_height (ClutterActor *actor, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + NbtkPadding padding; + gfloat icon_h; + + nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding); + + for_width -= padding.left + padding.right; + + clutter_actor_get_preferred_height (priv->entry, for_width, + min_height_p, + natural_height_p); + + if (priv->primary_icon) + { + clutter_actor_get_preferred_height (priv->primary_icon, + -1, NULL, &icon_h); + + if (min_height_p && icon_h > *min_height_p) + *min_height_p = icon_h; + + if (natural_height_p && icon_h > *natural_height_p) + *natural_height_p = icon_h; + } + + if (priv->secondary_icon) + { + clutter_actor_get_preferred_height (priv->secondary_icon, + -1, NULL, &icon_h); + + if (min_height_p && icon_h > *min_height_p) + *min_height_p = icon_h; + + if (natural_height_p && icon_h > *natural_height_p) + *natural_height_p = icon_h; + } + + if (min_height_p) + *min_height_p += padding.top + padding.bottom; + + if (natural_height_p) + *natural_height_p += padding.top + padding.bottom; +} + +static void +nbtk_entry_allocate (ClutterActor *actor, + const ClutterActorBox *box, + ClutterAllocationFlags flags) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + ClutterActorClass *parent_class; + ClutterActorBox child_box, icon_box; + NbtkPadding padding; + gfloat icon_w, icon_h; + gfloat entry_h, min_h, pref_h, avail_h; + + nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding); + + parent_class = CLUTTER_ACTOR_CLASS (nbtk_entry_parent_class); + parent_class->allocate (actor, box, flags); + + avail_h = (box->y2 - box->y1) - padding.top - padding.bottom; + + child_box.x1 = padding.left; + child_box.x2 = box->x2 - box->x1 - padding.right; + + if (priv->primary_icon) + { + clutter_actor_get_preferred_width (priv->primary_icon, + -1, NULL, &icon_w); + clutter_actor_get_preferred_height (priv->primary_icon, + -1, NULL, &icon_h); + + icon_box.x1 = padding.left; + icon_box.x2 = icon_box.x1 + icon_w; + + icon_box.y1 = (int) (padding.top + avail_h / 2 - icon_h / 2); + icon_box.y2 = icon_box.y1 + icon_h; + + clutter_actor_allocate (priv->primary_icon, + &icon_box, + flags); + + /* reduce the size for the entry */ + child_box.x1 += icon_w + priv->spacing; + } + + if (priv->secondary_icon) + { + clutter_actor_get_preferred_width (priv->secondary_icon, + -1, NULL, &icon_w); + clutter_actor_get_preferred_height (priv->secondary_icon, + -1, NULL, &icon_h); + + icon_box.x2 = (box->x2 - box->x1) - padding.right; + icon_box.x1 = icon_box.x2 - icon_w; + + icon_box.y1 = (int) (padding.top + avail_h / 2 - icon_h / 2); + icon_box.y2 = icon_box.y1 + icon_h; + + clutter_actor_allocate (priv->secondary_icon, + &icon_box, + flags); + + /* reduce the size for the entry */ + child_box.x2 -= icon_w - priv->spacing; + } + + clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1, + &min_h, &pref_h); + + entry_h = CLAMP (pref_h, min_h, avail_h); + + child_box.y1 = (int) (padding.top + avail_h / 2 - entry_h / 2); + child_box.y2 = child_box.y1 + entry_h; + + clutter_actor_allocate (priv->entry, &child_box, flags); +} + +static void +clutter_text_focus_in_cb (ClutterText *text, + ClutterActor *actor) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + + /* remove the hint if visible */ + if (priv->hint + && !strcmp (clutter_text_get_text (text), priv->hint)) + { + clutter_text_set_text (text, ""); + } + nbtk_widget_set_style_pseudo_class (NBTK_WIDGET (actor), "focus"); + clutter_text_set_cursor_visible (text, TRUE); +} + +static void +clutter_text_focus_out_cb (ClutterText *text, + ClutterActor *actor) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + + /* add a hint if the entry is empty */ + if (priv->hint && !strcmp (clutter_text_get_text (text), "")) + { + clutter_text_set_text (text, priv->hint); + nbtk_widget_set_style_pseudo_class (NBTK_WIDGET (actor), "indeterminate"); + } + else + { + nbtk_widget_set_style_pseudo_class (NBTK_WIDGET (actor), NULL); + } + clutter_text_set_cursor_visible (text, FALSE); +} + +static void +nbtk_entry_paint (ClutterActor *actor) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + ClutterActorClass *parent_class; + + parent_class = CLUTTER_ACTOR_CLASS (nbtk_entry_parent_class); + parent_class->paint (actor); + + clutter_actor_paint (priv->entry); + + if (priv->primary_icon) + clutter_actor_paint (priv->primary_icon); + + if (priv->secondary_icon) + clutter_actor_paint (priv->secondary_icon); +} + +static void +nbtk_entry_pick (ClutterActor *actor, + const ClutterColor *c) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + + CLUTTER_ACTOR_CLASS (nbtk_entry_parent_class)->pick (actor, c); + + clutter_actor_paint (priv->entry); + + if (priv->primary_icon) + clutter_actor_paint (priv->primary_icon); + + if (priv->secondary_icon) + clutter_actor_paint (priv->secondary_icon); +} + +static void +nbtk_entry_map (ClutterActor *actor) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY (actor)->priv; + + CLUTTER_ACTOR_CLASS (nbtk_entry_parent_class)->map (actor); + + clutter_actor_map (priv->entry); + + if (priv->primary_icon) + clutter_actor_map (priv->primary_icon); + + if (priv->secondary_icon) + clutter_actor_map (priv->secondary_icon); +} + +static void +nbtk_entry_unmap (ClutterActor *actor) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY (actor)->priv; + + CLUTTER_ACTOR_CLASS (nbtk_entry_parent_class)->unmap (actor); + + clutter_actor_unmap (priv->entry); + + if (priv->primary_icon) + clutter_actor_unmap (priv->primary_icon); + + if (priv->secondary_icon) + clutter_actor_unmap (priv->secondary_icon); +} + +static void +nbtk_entry_clipboard_callback (NbtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + ClutterText *ctext = (ClutterText*) ((NbtkEntry *) data)->priv->entry; + gint cursor_pos; + + if (!text) + return; + + /* delete the current selection before pasting */ + clutter_text_delete_selection (ctext); + + /* "paste" the clipboard text into the entry */ + cursor_pos = clutter_text_get_cursor_position (ctext); + clutter_text_insert_text (ctext, text, cursor_pos); +} + +static gboolean +nbtk_entry_key_press_event (ClutterActor *actor, + ClutterKeyEvent *event) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + + /* This is expected to handle events that were emitted for the inner + ClutterText. They only reach this function if the ClutterText + didn't handle them */ + + /* paste */ + if ((event->modifier_state & CLUTTER_CONTROL_MASK) + && event->keyval == CLUTTER_v) + { + NbtkClipboard *clipboard; + + clipboard = nbtk_clipboard_get_default (); + + nbtk_clipboard_get_text (clipboard, nbtk_entry_clipboard_callback, actor); + + return TRUE; + } + + /* copy */ + if ((event->modifier_state & CLUTTER_CONTROL_MASK) + && event->keyval == CLUTTER_c) + { + NbtkClipboard *clipboard; + gchar *text; + + clipboard = nbtk_clipboard_get_default (); + + text = clutter_text_get_selection ((ClutterText*) priv->entry); + + if (text && strlen (text)) + nbtk_clipboard_set_text (clipboard, text); + + return TRUE; + } + + + /* cut */ + if ((event->modifier_state & CLUTTER_CONTROL_MASK) + && event->keyval == CLUTTER_x) + { + NbtkClipboard *clipboard; + gchar *text; + + clipboard = nbtk_clipboard_get_default (); + + text = clutter_text_get_selection ((ClutterText*) priv->entry); + + if (text && strlen (text)) + { + nbtk_clipboard_set_text (clipboard, text); + + /* now delete the text */ + clutter_text_delete_selection ((ClutterText *) priv->entry); + } + + return TRUE; + } + + return FALSE; +} + +static void +nbtk_entry_key_focus_in (ClutterActor *actor) +{ + NbtkEntryPrivate *priv = NBTK_ENTRY_PRIV (actor); + + /* We never want key focus. The ClutterText should be given first + pass for all key events */ + clutter_actor_grab_key_focus (priv->entry); +} + +static void +nbtk_entry_class_init (NbtkEntryClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (NbtkEntryPrivate)); + + gobject_class->set_property = nbtk_entry_set_property; + gobject_class->get_property = nbtk_entry_get_property; + gobject_class->finalize = nbtk_entry_finalize; + gobject_class->dispose = nbtk_entry_dispose; + + actor_class->get_preferred_width = nbtk_entry_get_preferred_width; + actor_class->get_preferred_height = nbtk_entry_get_preferred_height; + actor_class->allocate = nbtk_entry_allocate; + actor_class->paint = nbtk_entry_paint; + actor_class->pick = nbtk_entry_pick; + actor_class->map = nbtk_entry_map; + actor_class->unmap = nbtk_entry_unmap; + + actor_class->key_press_event = nbtk_entry_key_press_event; + actor_class->key_focus_in = nbtk_entry_key_focus_in; + + pspec = g_param_spec_string ("text", + "Text", + "Text of the entry", + NULL, G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ENTRY, pspec); + + pspec = g_param_spec_string ("hint-text", + "Hint Text", + "Text to display when the entry is not focused " + "and the text property is empty", + NULL, G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ENTRY, pspec); + + /* signals */ + /** + * NbtkEntry::primary-icon-clicked: + * + * Emitted when the primary icon is clicked + */ + entry_signals[PRIMARY_ICON_CLICKED] = + g_signal_new ("primary-icon-clicked", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NbtkEntryClass, primary_icon_clicked), + NULL, NULL, + _nbtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** + * NbtkEntry::secondary-icon-clicked: + * + * Emitted when the secondary icon is clicked + */ + entry_signals[SECONDARY_ICON_CLICKED] = + g_signal_new ("secondary-icon-clicked", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NbtkEntryClass, secondary_icon_clicked), + NULL, NULL, + _nbtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +nbtk_entry_init (NbtkEntry *entry) +{ + NbtkEntryPrivate *priv; + + priv = entry->priv = NBTK_ENTRY_GET_PRIVATE (entry); + + priv->entry = g_object_new (CLUTTER_TYPE_IMTEXT, + "line-alignment", PANGO_ALIGN_LEFT, + "editable", TRUE, + "reactive", TRUE, + "single-line-mode", TRUE, + NULL); + + g_signal_connect (priv->entry, "key-focus-in", + G_CALLBACK (clutter_text_focus_in_cb), entry); + + g_signal_connect (priv->entry, "key-focus-out", + G_CALLBACK (clutter_text_focus_out_cb), entry); + + priv->spacing = 6.0f; + + clutter_actor_set_parent (priv->entry, CLUTTER_ACTOR (entry)); + clutter_actor_set_reactive ((ClutterActor *) entry, TRUE); + + /* set cursor hidden until we receive focus */ + clutter_text_set_cursor_visible ((ClutterText *) priv->entry, FALSE); + + g_signal_connect (entry, "style-changed", + G_CALLBACK (nbtk_entry_style_changed), NULL); +} + +/** + * nbtk_entry_new: + * @text: text to set the entry to + * + * Create a new #NbtkEntry with the specified entry + * + * Returns: a new #NbtkEntry + */ +NbtkWidget * +nbtk_entry_new (const gchar *text) +{ + NbtkWidget *entry; + + /* add the entry to the stage, but don't allow it to be visible */ + entry = g_object_new (NBTK_TYPE_ENTRY, + "text", text, + NULL); + + return entry; +} + +/** + * nbtk_entry_get_text: + * @entry: a #NbtkEntry + * + * Get the text displayed on the entry + * + * Returns: the text for the entry. This must not be freed by the application + */ +G_CONST_RETURN gchar * +nbtk_entry_get_text (NbtkEntry *entry) +{ + g_return_val_if_fail (NBTK_IS_ENTRY (entry), NULL); + + return clutter_text_get_text (CLUTTER_TEXT (entry->priv->entry)); +} + +/** + * nbtk_entry_set_text: + * @entry: a #NbtkEntry + * @text: text to set the entry to + * + * Sets the text displayed on the entry + */ +void +nbtk_entry_set_text (NbtkEntry *entry, + const gchar *text) +{ + NbtkEntryPrivate *priv; + + g_return_if_fail (NBTK_IS_ENTRY (entry)); + + priv = entry->priv; + + /* set a hint if we are blanking the entry */ + if (priv->hint + && text && !strcmp ("", text) + && !HAS_FOCUS (priv->entry)) + { + text = priv->hint; + nbtk_widget_set_style_pseudo_class (NBTK_WIDGET (entry), "indeterminate"); + } + else + { + if (HAS_FOCUS (priv->entry)) + nbtk_widget_set_style_pseudo_class (NBTK_WIDGET (entry), "focus"); + else + nbtk_widget_set_style_pseudo_class (NBTK_WIDGET (entry), NULL); + } + + clutter_text_set_text (CLUTTER_TEXT (priv->entry), text); + + g_object_notify (G_OBJECT (entry), "text"); +} + +/** + * nbtk_entry_get_clutter_text: + * @entry: a #NbtkEntry + * + * Retrieve the internal #ClutterText so that extra parameters can be set + * + * Returns: the #ClutterText used by #NbtkEntry. The entry is owned by the + * #NbtkEntry and should not be unref'ed by the application. + */ +ClutterActor* +nbtk_entry_get_clutter_text (NbtkEntry *entry) +{ + g_return_val_if_fail (NBTK_ENTRY (entry), NULL); + + return entry->priv->entry; +} + +/** + * nbtk_entry_set_hint_text: + * @entry: a #NbtkEntry + * @text: text to set as the entry hint + * + * Sets the text to display when the entry is empty and unfocused. When the + * entry is displaying the hint, it has a pseudo class of "indeterminate". + * A value of NULL unsets the hint. + */ +void +nbtk_entry_set_hint_text (NbtkEntry *entry, + const gchar *text) +{ + NbtkEntryPrivate *priv; + + g_return_if_fail (NBTK_IS_ENTRY (entry)); + + priv = entry->priv; + + g_free (priv->hint); + + priv->hint = g_strdup (text); + + if (!strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), "")) + { + clutter_text_set_text (CLUTTER_TEXT (priv->entry), priv->hint); + nbtk_widget_set_style_pseudo_class (NBTK_WIDGET (entry), "indeterminate"); + } +} + +/** + * nbtk_entry_get_hint_text: + * @entry: a #NbtkEntry + * + * Gets the text that is displayed when the entry is empty and unfocused + * + * Returns: the current value of the hint property. This string is owned by the + * #NbtkEntry and should not be freed or modified. + */ +G_CONST_RETURN +gchar * +nbtk_entry_get_hint_text (NbtkEntry *entry) +{ + g_return_val_if_fail (NBTK_IS_ENTRY (entry), NULL); + + return entry->priv->hint; +} + +static gboolean +_nbtk_entry_icon_press_cb (ClutterActor *actor, + ClutterButtonEvent *event, + NbtkEntry *entry) +{ + NbtkEntryPrivate *priv = entry->priv; + + if (actor == priv->primary_icon) + g_signal_emit (entry, entry_signals[PRIMARY_ICON_CLICKED], 0); + else + g_signal_emit (entry, entry_signals[SECONDARY_ICON_CLICKED], 0); + + return FALSE; +} + +static void +_nbtk_entry_set_icon_from_file (NbtkEntry *entry, + ClutterActor **icon, + const gchar *filename) +{ + if (*icon) + { + g_signal_handlers_disconnect_by_func (*icon, + _nbtk_entry_icon_press_cb, + entry); + clutter_actor_unparent (*icon); + *icon = NULL; + } + + if (filename) + { + NbtkTextureCache *cache; + + cache = nbtk_texture_cache_get_default (); + + + + *icon = (ClutterActor*) nbtk_texture_cache_get_texture (cache, filename, FALSE); + + clutter_actor_set_reactive (*icon, TRUE); + clutter_actor_set_parent (*icon, CLUTTER_ACTOR (entry)); + g_signal_connect (*icon, "button-release-event", + G_CALLBACK (_nbtk_entry_icon_press_cb), entry); + } + + clutter_actor_queue_relayout (CLUTTER_ACTOR (entry)); +} + +/** + * nbtk_entry_set_primary_icon_from_file: + * @entry: a #NbtkEntry + * @filename: filename of an icon + * + * Set the primary icon of the entry to the given filename + */ +void +nbtk_entry_set_primary_icon_from_file (NbtkEntry *entry, + const gchar *filename) +{ + NbtkEntryPrivate *priv; + + g_return_if_fail (NBTK_IS_ENTRY (entry)); + + priv = entry->priv; + + _nbtk_entry_set_icon_from_file (entry, &priv->primary_icon, filename); + +} + +/** + * nbtk_entry_set_secondary_icon_from_file: + * @entry: a #NbtkEntry + * @filename: filename of an icon + * + * Set the primary icon of the entry to the given filename + */ +void +nbtk_entry_set_secondary_icon_from_file (NbtkEntry *entry, + const gchar *filename) +{ + NbtkEntryPrivate *priv; + + g_return_if_fail (NBTK_IS_ENTRY (entry)); + + priv = entry->priv; + + _nbtk_entry_set_icon_from_file (entry, &priv->secondary_icon, filename); + +} diff --git a/src/nbtk/nbtk-entry.h b/src/nbtk/nbtk-entry.h new file mode 100644 index 000000000..aca4c8c45 --- /dev/null +++ b/src/nbtk/nbtk-entry.h @@ -0,0 +1,88 @@ +/* + * nbtk-entry.h: Plain entry 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 + * + */ + +#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION) +#error "Only can be included directly.h" +#endif + +#ifndef __NBTK_ENTRY_H__ +#define __NBTK_ENTRY_H__ + +G_BEGIN_DECLS + +#include + +#define NBTK_TYPE_ENTRY (nbtk_entry_get_type ()) +#define NBTK_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_ENTRY, NbtkEntry)) +#define NBTK_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_ENTRY)) +#define NBTK_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_ENTRY, NbtkEntryClass)) +#define NBTK_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_ENTRY)) +#define NBTK_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_ENTRY, NbtkEntryClass)) + +typedef struct _NbtkEntry NbtkEntry; +typedef struct _NbtkEntryPrivate NbtkEntryPrivate; +typedef struct _NbtkEntryClass NbtkEntryClass; + +/** + * NbtkEntry: + * + * The contents of this structure is private and should only be accessed using + * the provided API. + */ +struct _NbtkEntry +{ + /*< private >*/ + NbtkWidget parent_instance; + + NbtkEntryPrivate *priv; +}; + +struct _NbtkEntryClass +{ + NbtkWidgetClass parent_class; + + /* signals */ + void (*primary_icon_clicked) (NbtkEntry *entry); + void (*secondary_icon_clicked) (NbtkEntry *entry); +}; + +GType nbtk_entry_get_type (void) G_GNUC_CONST; + +NbtkWidget * nbtk_entry_new (const gchar *text); +G_CONST_RETURN gchar *nbtk_entry_get_text (NbtkEntry *entry); +void nbtk_entry_set_text (NbtkEntry *entry, + const gchar *text); +ClutterActor* nbtk_entry_get_clutter_text (NbtkEntry *entry); + +void nbtk_entry_set_hint_text (NbtkEntry *entry, + const gchar *text); +G_CONST_RETURN gchar *nbtk_entry_get_hint_text (NbtkEntry *entry); + +void nbtk_entry_set_primary_icon_from_file (NbtkEntry *entry, + const gchar *filename); +void nbtk_entry_set_secondary_icon_from_file (NbtkEntry *entry, + const gchar *filename); + +G_END_DECLS + +#endif /* __NBTK_ENTRY_H__ */ diff --git a/src/nbtk/nbtk-label.c b/src/nbtk/nbtk-label.c new file mode 100644 index 000000000..3763724af --- /dev/null +++ b/src/nbtk/nbtk-label.c @@ -0,0 +1,363 @@ +/* + * nbtk-label.c: Plain label 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. + * + * Written by: Thomas Wood + * + */ + +/** + * SECTION:nbtk-label + * @short_description: Widget for displaying text + * + * #NbtkLabel is a simple widget for displaying text. It derives from + * #NbtkWidget to add extra style and placement functionality over + * #ClutterText. The internal #ClutterText is publicly accessibly to allow + * applications to set further properties. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include + +#include "nbtk-label.h" + +#include "nbtk-widget.h" +#include "nbtk-stylable.h" + +enum +{ + PROP_0, + + PROP_LABEL +}; + +#define NBTK_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_LABEL, NbtkLabelPrivate)) + +struct _NbtkLabelPrivate +{ + ClutterActor *label; +}; + +G_DEFINE_TYPE (NbtkLabel, nbtk_label, NBTK_TYPE_WIDGET); + +static void +nbtk_label_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NbtkLabel *label = NBTK_LABEL (gobject); + + switch (prop_id) + { + case PROP_LABEL: + nbtk_label_set_text (label, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +nbtk_label_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (gobject)->priv; + + switch (prop_id) + { + case PROP_LABEL: + g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +nbtk_label_style_changed (NbtkWidget *self) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (self)->priv; + ClutterColor *color = NULL; + gchar *font_name; + gchar *font_string; + gint font_size; + + nbtk_stylable_get (NBTK_STYLABLE (self), + "color", &color, + "font-family", &font_name, + "font-size", &font_size, + NULL); + + if (color) + { + clutter_text_set_color (CLUTTER_TEXT (priv->label), color); + clutter_color_free (color); + } + + if (font_name || font_size) + { + if (font_name && font_size) + { + font_string = g_strdup_printf ("%s %dpx", font_name, font_size); + g_free (font_name); + } + else + { + if (font_size) + font_string = g_strdup_printf ("%dpx", font_size); + else + font_string = font_name; + } + + clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string); + g_free (font_string); + } + +} + +static void +nbtk_label_get_preferred_width (ClutterActor *actor, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (actor)->priv; + NbtkPadding padding = { 0, }; + + nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding); + + clutter_actor_get_preferred_width (priv->label, for_height, + min_width_p, + natural_width_p); + + if (min_width_p) + *min_width_p += padding.left + padding.right; + + if (natural_width_p) + *natural_width_p += padding.left + padding.right; +} + +static void +nbtk_label_get_preferred_height (ClutterActor *actor, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (actor)->priv; + NbtkPadding padding = { 0, }; + + nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding); + + clutter_actor_get_preferred_height (priv->label, for_width, + min_height_p, + natural_height_p); + + if (min_height_p) + *min_height_p += padding.top + padding.bottom; + + if (natural_height_p) + *natural_height_p += padding.top + padding.bottom; +} + +static void +nbtk_label_allocate (ClutterActor *actor, + const ClutterActorBox *box, + ClutterAllocationFlags flags) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (actor)->priv; + ClutterActorClass *parent_class; + ClutterActorBox child_box; + NbtkPadding padding = { 0, }; + + nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding); + + parent_class = CLUTTER_ACTOR_CLASS (nbtk_label_parent_class); + parent_class->allocate (actor, box, flags); + + child_box.x1 = padding.left; + child_box.y1 = padding.top; + child_box.x2 = box->x2 - box->x1 - padding.right; + child_box.y2 = box->y2 - box->y1 - padding.bottom; + + clutter_actor_allocate (priv->label, &child_box, flags); +} + +static void +nbtk_label_paint (ClutterActor *actor) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (actor)->priv; + ClutterActorClass *parent_class; + + parent_class = CLUTTER_ACTOR_CLASS (nbtk_label_parent_class); + parent_class->paint (actor); + + clutter_actor_paint (priv->label); +} + +static void +nbtk_label_map (ClutterActor *actor) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (actor)->priv; + + CLUTTER_ACTOR_CLASS (nbtk_label_parent_class)->map (actor); + + clutter_actor_map (priv->label); +} + +static void +nbtk_label_unmap (ClutterActor *actor) +{ + NbtkLabelPrivate *priv = NBTK_LABEL (actor)->priv; + + CLUTTER_ACTOR_CLASS (nbtk_label_parent_class)->unmap (actor); + + clutter_actor_unmap (priv->label); +} + +static void +nbtk_label_class_init (NbtkLabelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (NbtkLabelPrivate)); + + gobject_class->set_property = nbtk_label_set_property; + gobject_class->get_property = nbtk_label_get_property; + + actor_class->paint = nbtk_label_paint; + actor_class->allocate = nbtk_label_allocate; + actor_class->get_preferred_width = nbtk_label_get_preferred_width; + actor_class->get_preferred_height = nbtk_label_get_preferred_height; + actor_class->map = nbtk_label_map; + actor_class->unmap = nbtk_label_unmap; + + pspec = g_param_spec_string ("text", + "Text", + "Text of the label", + NULL, G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LABEL, pspec); + +} + +static void +nbtk_label_init (NbtkLabel *label) +{ + NbtkLabelPrivate *priv; + + label->priv = priv = NBTK_LABEL_GET_PRIVATE (label); + + label->priv->label = g_object_new (CLUTTER_TYPE_TEXT, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + + clutter_actor_set_parent (priv->label, CLUTTER_ACTOR (label)); + + g_signal_connect (label, "style-changed", + G_CALLBACK (nbtk_label_style_changed), NULL); +} + +/** + * nbtk_label_new: + * @text: text to set the label to + * + * Create a new #NbtkLabel with the specified label + * + * Returns: a new #NbtkLabel + */ +NbtkWidget * +nbtk_label_new (const gchar *text) +{ + if (text == NULL || *text == '\0') + return g_object_new (NBTK_TYPE_LABEL, NULL); + else + return g_object_new (NBTK_TYPE_LABEL, + "text", text, + NULL); +} + +/** + * nbtk_label_get_text: + * @label: a #NbtkLabel + * + * Get the text displayed on the label + * + * Returns: the text for the label. This must not be freed by the application + */ +G_CONST_RETURN gchar * +nbtk_label_get_text (NbtkLabel *label) +{ + g_return_val_if_fail (NBTK_IS_LABEL (label), NULL); + + return clutter_text_get_text (CLUTTER_TEXT (label->priv->label)); +} + +/** + * nbtk_label_set_text: + * @label: a #NbtkLabel + * @text: text to set the label to + * + * Sets the text displayed on the label + */ +void +nbtk_label_set_text (NbtkLabel *label, + const gchar *text) +{ + NbtkLabelPrivate *priv; + + g_return_if_fail (NBTK_IS_LABEL (label)); + g_return_if_fail (text != NULL); + + priv = label->priv; + + clutter_text_set_text (CLUTTER_TEXT (priv->label), text); + + g_object_notify (G_OBJECT (label), "text"); +} + +/** + * nbtk_label_get_clutter_text: + * @label: a #NbtkLabel + * + * Retrieve the internal #ClutterText so that extra parameters can be set + * + * Returns: the #ClutterText used by #NbtkLabel. The label is owned by the + * #NbtkLabel and should not be unref'ed by the application. + */ +ClutterActor* +nbtk_label_get_clutter_text (NbtkLabel *label) +{ + g_return_val_if_fail (NBTK_LABEL (label), NULL); + + return label->priv->label; +} diff --git a/src/nbtk/nbtk-label.h b/src/nbtk/nbtk-label.h new file mode 100644 index 000000000..613e0fd02 --- /dev/null +++ b/src/nbtk/nbtk-label.h @@ -0,0 +1,75 @@ +/* + * nbtk-label.h: Plain label 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 + * + */ + +#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION) +#error "Only can be included directly.h" +#endif + +#ifndef __NBTK_LABEL_H__ +#define __NBTK_LABEL_H__ + +G_BEGIN_DECLS + +#include + +#define NBTK_TYPE_LABEL (nbtk_label_get_type ()) +#define NBTK_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_LABEL, NbtkLabel)) +#define NBTK_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_LABEL)) +#define NBTK_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_LABEL, NbtkLabelClass)) +#define NBTK_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_LABEL)) +#define NBTK_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_LABEL, NbtkLabelClass)) + +typedef struct _NbtkLabel NbtkLabel; +typedef struct _NbtkLabelPrivate NbtkLabelPrivate; +typedef struct _NbtkLabelClass NbtkLabelClass; + +/** + * NbtkLabel: + * + * The contents of this structure is private and should only be accessed using + * the provided API. + */ +struct _NbtkLabel +{ + /*< private >*/ + NbtkWidget parent_instance; + + NbtkLabelPrivate *priv; +}; + +struct _NbtkLabelClass +{ + NbtkWidgetClass parent_class; +}; + +GType nbtk_label_get_type (void) G_GNUC_CONST; + +NbtkWidget * nbtk_label_new (const gchar *text); +G_CONST_RETURN gchar *nbtk_label_get_text (NbtkLabel *label); +void nbtk_label_set_text (NbtkLabel *label, + const gchar *text); +ClutterActor * nbtk_label_get_clutter_text (NbtkLabel *label); + +G_END_DECLS + +#endif /* __NBTK_LABEL_H__ */ diff --git a/tools/build/gnome-shell.modules b/tools/build/gnome-shell.modules index 095bc9d06..6395eb516 100644 --- a/tools/build/gnome-shell.modules +++ b/tools/build/gnome-shell.modules @@ -7,6 +7,8 @@ href="git://git.clutter-project.org/"/> + @@ -36,6 +38,13 @@ + + + + + + +