From 706b0ad396bd569224e28ed45090a3457e0634f8 Mon Sep 17 00:00:00 2001 From: Danielle Madeley Date: Mon, 14 Mar 2011 20:26:23 +1100 Subject: Import MxGtkLightSwitch from Mx Available under the terms of the LGPL 2.1. --- libempathy-gtk/Makefile.am | 8 +- libempathy-gtk/mx-gtk-light-switch.c | 539 +++++++++++++++++++++++++++++++++++ libempathy-gtk/mx-gtk-light-switch.h | 71 +++++ po/POTFILES.in | 1 + 4 files changed, 617 insertions(+), 2 deletions(-) create mode 100644 libempathy-gtk/mx-gtk-light-switch.c create mode 100644 libempathy-gtk/mx-gtk-light-switch.h diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am index 390d37cbb..1b71e03a3 100644 --- a/libempathy-gtk/Makefile.am +++ b/libempathy-gtk/Makefile.am @@ -90,7 +90,9 @@ libempathy_gtk_handwritten_source = \ empathy-theme-irc.c \ empathy-theme-manager.c \ empathy-tls-dialog.c \ - empathy-ui-utils.c + empathy-ui-utils.c \ + mx-gtk-light-switch.c \ + $(NULL) libempathy_gtk_headers = \ empathy-account-chooser.h \ @@ -152,7 +154,9 @@ libempathy_gtk_headers = \ empathy-theme-irc.h \ empathy-theme-manager.h \ empathy-tls-dialog.h \ - empathy-ui-utils.h + empathy-ui-utils.h \ + mx-gtk-light-switch.h \ + $(NULL) libempathy_gtk_la_SOURCES = \ $(libempathy_gtk_handwritten_source) \ diff --git a/libempathy-gtk/mx-gtk-light-switch.c b/libempathy-gtk/mx-gtk-light-switch.c new file mode 100644 index 000000000..a1427a318 --- /dev/null +++ b/libempathy-gtk/mx-gtk-light-switch.c @@ -0,0 +1,539 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * 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:mx-gtk-light-switch + * @short_description: a toggle switch between two states + * + * A visual representation of a toggle switch that can move between two states. + */ + +#include +#include "mx-gtk-light-switch.h" + +#include + +#if 0 +/* We use the special gcc constructor attribute so we can avoid + * requiring an init function to get translations to work! This + * function is also in mx-utils but we also need it here + * because that is a separate library */ +static void __attribute__ ((constructor)) +_start (void) +{ + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +} +#endif + +G_DEFINE_TYPE (MxGtkLightSwitch, mx_gtk_light_switch, GTK_TYPE_DRAWING_AREA) + +#define MX_GTK_LIGHT_SWITCH_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitchPrivate)) + +static gboolean mx_gtk_light_switch_configure (GtkWidget *lightswitch, + GdkEventConfigure *event); +static gboolean mx_gtk_light_switch_expose (GtkWidget *lightswitch, + GdkEventExpose *event); +static gboolean mx_gtk_light_switch_button_release (GtkWidget *lightswitch, + GdkEventButton *event); +static gboolean mx_gtk_light_switch_button_press (GtkWidget *lightswitch, + GdkEventButton *event); +static gboolean mx_gtk_light_switch_motion_notify (GtkWidget *lightswitch, + GdkEventMotion *event); +static void mx_gtk_light_switch_size_request (GtkWidget *lightswitch, + GtkRequisition *req); + +static void mx_gtk_light_switch_style_set (GtkWidget *lightswitch, + GtkStyle *previous_style); + +enum { + SWITCH_FLIPPED, + LAST_SIGNAL +}; + +static guint mx_gtk_light_switch_signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_ACTIVE, +}; + +typedef struct _MxGtkLightSwitchPrivate MxGtkLightSwitchPrivate; + +struct _MxGtkLightSwitchPrivate { + gboolean active; /* boolean state of switch */ + gboolean dragging; /* true if dragging switch */ + gint x; /* the x position of the switch */ + gint drag_start; /* position dragging started at */ + gint drag_threshold; + gint switch_width; + gint switch_height; + gint trough_width; + gint offset; /* offset of the mouse to slider when dragging */ +}; + +/* MEDIUM VERTICAL BAR U+2759 */ +#define ON_STRING "\342\235\231" + /* WHITE CIRCLE U+25CB */ +#define OFF_STRING "\342\227\213" + +#define UNAVAILABLE_STRING _("Unavailable") + +static void +mx_gtk_light_switch_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MxGtkLightSwitch *ls; + + ls = MX_GTK_LIGHT_SWITCH (object); + + switch (prop_id) + { + case PROP_ACTIVE: + mx_gtk_light_switch_set_active (ls, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +mx_gtk_light_switch_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MxGtkLightSwitchPrivate *priv; + + priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (object); + + switch (prop_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +mx_gtk_light_switch_class_init (MxGtkLightSwitchClass *klass) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + GParamSpec *spec; + + object_class = G_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + object_class->set_property = mx_gtk_light_switch_set_property; + object_class->get_property = mx_gtk_light_switch_get_property; + + widget_class->configure_event = mx_gtk_light_switch_configure; + widget_class->expose_event = mx_gtk_light_switch_expose; + widget_class->button_release_event = mx_gtk_light_switch_button_release; + widget_class->button_press_event = mx_gtk_light_switch_button_press; + widget_class->motion_notify_event = mx_gtk_light_switch_motion_notify; + widget_class->size_request = mx_gtk_light_switch_size_request; + widget_class->style_set = mx_gtk_light_switch_style_set; + + spec = g_param_spec_boolean ("active", + "Active", + "Is the light switch on or not", + FALSE, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_ACTIVE, spec); + + /* MxGtkLightSwitch signals */ + mx_gtk_light_switch_signals[SWITCH_FLIPPED] = + g_signal_new ("switch-flipped", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MxGtkLightSwitchClass, switch_flipped), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + + g_type_class_add_private (klass, + sizeof (MxGtkLightSwitchPrivate)); +} + +static void +mx_gtk_light_switch_init (MxGtkLightSwitch *self) +{ + MxGtkLightSwitchPrivate *priv; + + priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (self); + priv->active = FALSE; + priv->x = 0; + + /* add events, do initial draw/update, etc */ + gtk_widget_add_events (GTK_WIDGET (self), + GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK); +} + +static void +draw (GtkWidget *lightswitch, + cairo_t *cr) +{ + MxGtkLightSwitchPrivate *priv; + + gint on_label_x; + gint off_label_x; +/* + gint label_width; + gint label_height; +*/ + GtkStyle *style; +// PangoLayout *layout; +// PangoContext *context; + GtkStateType state_type; + + priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + style = lightswitch->style; +#if GTK_CHECK_VERSION (2,19,5) + state_type = gtk_widget_get_state (lightswitch); +#else + state_type = GTK_WIDGET_STATE (lightswitch); +#endif + + on_label_x = (priv->trough_width / 5) * 0.75; + off_label_x = (priv->trough_width / 8) * 5; + + /* draw the trough */ + gtk_paint_box (style, + lightswitch->window, + (state_type != GTK_STATE_INSENSITIVE && priv->active) + ? GTK_STATE_SELECTED : state_type, + GTK_SHADOW_IN, + NULL, + NULL, + "light-switch-trough", + 0, + 0, + (priv->trough_width), + priv->switch_height); + + if (state_type == GTK_STATE_INSENSITIVE) + return; +#if 0 + if (state_type == GTK_STATE_INSENSITIVE) + { + context = gdk_pango_context_get (); + layout = pango_layout_new (context); + g_object_unref (context); + + pango_layout_set_font_description (layout, style->font_desc); + pango_layout_set_text (layout, UNAVAILABLE_STRING, -1); + pango_layout_get_size (layout, &label_width, &label_height); + gtk_paint_layout (style, lightswitch->window, state_type, FALSE, + NULL, lightswitch, "lighswitch-label", + (priv->trough_width - (label_width / PANGO_SCALE)) / 2, + (priv->switch_height - (label_height / PANGO_SCALE)) / 2, + layout); + g_object_unref (layout); + return; + } + + /* Draw the first label; "On" */ + context = gdk_pango_context_get (); + layout = pango_layout_new (context); + g_object_unref (context); + pango_layout_set_font_description (layout, + style->font_desc); + pango_layout_set_text (layout, ON_STRING, -1); + pango_layout_get_size (layout, + &label_width, + &label_height); + gtk_paint_layout (style, + lightswitch->window, + (priv->active) ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, + FALSE, + NULL, + (GtkWidget*) lightswitch, + "lightswitch-label", + on_label_x, + (priv->switch_height + - (label_height / PANGO_SCALE)) / 2, + layout); + + pango_layout_set_text (layout, OFF_STRING, -1); + pango_layout_get_size (layout, + &label_width, + &label_height); + gtk_paint_layout (style, + lightswitch->window, + (priv->active) ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, + FALSE, + NULL, + (GtkWidget*) lightswitch, + "lightswitch-label", + off_label_x, + (priv->switch_height + - (label_height / PANGO_SCALE)) / 2, + layout); + g_object_unref (layout); +#endif + /* draw the switch itself */ + gtk_paint_box (style, + lightswitch->window, +#if GTK_CHECK_VERSION (2,19,5) + gtk_widget_get_state (lightswitch), +#else + GTK_WIDGET_STATE (lightswitch), +#endif + GTK_SHADOW_OUT, + NULL, + NULL, + "light-switch-handle", + priv->x + style->xthickness, + style->ythickness, + priv->switch_width - style->xthickness * 2, + priv->switch_height - style->ythickness * 2); + +} + +static void +mx_gtk_light_switch_size_request (GtkWidget *lightswitch, + GtkRequisition *req) +{ + MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + + req->height = priv->switch_height; + req->width = priv->trough_width; +} + +static void +mx_gtk_light_switch_style_set (GtkWidget *lightswitch, + GtkStyle *previous_style) +{ + MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + PangoLayout *layout; + gint label_width, label_height; + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (lightswitch), NULL); + pango_layout_set_text (layout, UNAVAILABLE_STRING, -1); + pango_layout_get_pixel_size (layout, &label_width, &label_height); + g_object_unref (layout); + + /* MxToggle is 105x39, so make sure light-switch is at least this size */ + priv->trough_width = MAX (103, label_width); + priv->switch_width = (priv->trough_width / 2) * 1.1; + //priv->switch_height = MAX (39, label_height); + priv->switch_height = 24; + priv->switch_width = 50; + priv->trough_width = 98; +} + +static gboolean +mx_gtk_light_switch_configure (GtkWidget *lightswitch, + GdkEventConfigure *event) +{ + MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + + if (priv->active) + priv->x = priv->trough_width - priv->switch_width; + else + priv->x = 0; + + return FALSE; +} + +static gboolean +mx_gtk_light_switch_expose (GtkWidget *lightswitch, + GdkEventExpose *event) +{ + cairo_t *cr; + cr = gdk_cairo_create (lightswitch->window); + + cairo_rectangle (cr, + event->area.x, + event->area.y, + event->area.width, + event->area.height); + + cairo_clip (cr); + + draw (lightswitch, cr); + + cairo_destroy (cr); + + return FALSE; +} + +static gboolean +mx_gtk_light_switch_motion_notify (GtkWidget *lightswitch, + GdkEventMotion *event) +{ + MxGtkLightSwitchPrivate *priv; + + priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + + if (ABS (event->x - priv->drag_start) < priv->drag_threshold) + return TRUE; + + if (event->state & GDK_BUTTON1_MASK) + { + gint position = event->x - priv->offset; + + if (position > (priv->trough_width - priv->switch_width)) + priv->x = (priv->trough_width - priv->switch_width); + else if (position < 0) + priv->x = 0; + else + priv->x = position; + + priv->dragging = TRUE; + gtk_widget_queue_draw ((GtkWidget *) lightswitch); + } + + return TRUE; +} + +/** + * mx_gtk_light_switch_get_active: + * @lightswitch: A #MxGtkLightSwitch + * + * Get the value of the "active" property + * + * Returns: #TRUE if the switch is "on" + */ +gboolean +mx_gtk_light_switch_get_active (MxGtkLightSwitch *lightswitch) +{ + MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + + return priv->active; +} + +/** + * mx_gtk_light_switch_set_active: + * @lightswitch: A #MxGtkLightSwitch + * @active: #TRUE to set the switch to its ON state + * + * Set the value of the "active" property + * + */ +void +mx_gtk_light_switch_set_active (MxGtkLightSwitch *lightswitch, + gboolean active) +{ + MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + + if (priv->active == active) + { + return; + } + else + { + priv->active = active; + if (active == TRUE) + { + priv->x = priv->trough_width - priv->switch_width; + } + else + { + priv->x = 0; + } + + gtk_widget_queue_draw ((GtkWidget *) lightswitch); + + g_object_notify (G_OBJECT (lightswitch), "active"); + g_signal_emit (lightswitch, + mx_gtk_light_switch_signals[SWITCH_FLIPPED], + 0, + priv->active); + } +} + +static gboolean +mx_gtk_light_switch_button_press (GtkWidget *lightswitch, + GdkEventButton *event) +{ + MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + + if (priv->active) + priv->offset = event->x - (priv->trough_width - priv->switch_width); + else + priv->offset = event->x; + + priv->drag_start = event->x; + + g_object_get (gtk_widget_get_settings (lightswitch), + "gtk-dnd-drag-threshold", &priv->drag_threshold, + NULL); + + return FALSE; +} + +static gboolean +mx_gtk_light_switch_button_release (GtkWidget *lightswitch, + GdkEventButton *event) +{ + MxGtkLightSwitchPrivate *priv; + + priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); + + /* detect whereabouts we are and "drop" into a state */ + if (priv->dragging) + { + priv->dragging = FALSE; + if (priv->x + (priv->switch_width / 2) > priv->trough_width / 2) + { + mx_gtk_light_switch_set_active ((MxGtkLightSwitch *) lightswitch, TRUE); + priv->x = priv->trough_width - priv->switch_width; + } + else + { + mx_gtk_light_switch_set_active ((MxGtkLightSwitch *) lightswitch, FALSE); + priv->x = 0; + } + /* we always need to queue a redraw after dragging to put the slider back + * in the correct place */ + gtk_widget_queue_draw ((GtkWidget *) lightswitch); + } + else + { + mx_gtk_light_switch_set_active ((MxGtkLightSwitch *) lightswitch, !priv->active); + } + + return FALSE; +} + +/** + * mx_gtk_light_switch_new: + * + * Create a #MxGtkLightSwitch + * + * Returns: a newly allocated #MxGtkLightSwitch + */ +GtkWidget* +mx_gtk_light_switch_new (void) +{ + return g_object_new (MX_GTK_TYPE_LIGHT_SWITCH, NULL); +} + diff --git a/libempathy-gtk/mx-gtk-light-switch.h b/libempathy-gtk/mx-gtk-light-switch.h new file mode 100644 index 000000000..88fdf69e6 --- /dev/null +++ b/libempathy-gtk/mx-gtk-light-switch.h @@ -0,0 +1,71 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * 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 _MX_GTK_LIGHT_SWITCH +#define _MX_GTK_LIGHT_SWITCH + +#include + +G_BEGIN_DECLS + +#define MX_GTK_TYPE_LIGHT_SWITCH mx_gtk_light_switch_get_type () + +#define MX_GTK_LIGHT_SWITCH(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitch)) + +#define MX_GTK_LIGHT_SWITCH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitchClass)) + +#define MX_GTK_IS_LIGHT_SWITCH(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_GTK_TYPE_LIGHT_SWITCH)) + +#define MX_GTK_IS_LIGHT_SWITCH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_GTK_TYPE_LIGHT_SWITCH)) + +#define MX_GTK_LIGHT_SWITCH_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitchClass)) + +/** + * MxGtkLightSwitch: + * + * The contents of this structure are private and should only be accessed + * through the public API. + */ +typedef struct { + /*< private >*/ + GtkDrawingArea parent; +} MxGtkLightSwitch; + +typedef struct { + GtkDrawingAreaClass parent_class; + + void (*switch_flipped) (MxGtkLightSwitch *lightswitch, gboolean state); +} MxGtkLightSwitchClass; + +GType mx_gtk_light_switch_get_type (void); + +void mx_gtk_light_switch_set_active (MxGtkLightSwitch *lightswitch, + gboolean active); +gboolean mx_gtk_light_switch_get_active (MxGtkLightSwitch *lightswitch); + +GtkWidget* mx_gtk_light_switch_new (void); + +G_END_DECLS + +#endif /* _MX_GTK_LIGHT_SWITCH */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 2311bb096..4b5af18b9 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -72,6 +72,7 @@ libempathy-gtk/empathy-theme-manager.c libempathy-gtk/empathy-tls-dialog.c libempathy-gtk/empathy-ui-utils.c libempathy-gtk/totem-subtitle-encoding.c +libempathy-gtk/mx-gtk-light-switch.c nautilus-sendto-plugin/empathy-nautilus-sendto.c -- cgit v1.2.3