diff options
Diffstat (limited to 'src/idoscalemenuitem.c')
-rw-r--r-- | src/idoscalemenuitem.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/src/idoscalemenuitem.c b/src/idoscalemenuitem.c new file mode 100644 index 0000000..dd13b53 --- /dev/null +++ b/src/idoscalemenuitem.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2010 Canonical, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Cody Russell <crussell@canonical.com> + */ + +#include <gtk/gtk.h> +#include "idoscalemenuitem.h" + +static void ido_scale_menu_item_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void ido_scale_menu_item_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static GObject* ido_scale_menu_item_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params); +static void ido_scale_menu_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void ido_scale_menu_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void ido_scale_menu_item_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer data); +static void ido_scale_menu_item_construct_child (IdoScaleMenuItem *item); +static gboolean ido_scale_menu_item_button_press_event (GtkWidget *menuitem, + GdkEventButton *event); +static gboolean ido_scale_menu_item_button_release_event (GtkWidget *menuitem, + GdkEventButton *event); +static gboolean ido_scale_menu_item_motion_notify_event (GtkWidget *menuitem, + GdkEventMotion *event); + +struct _IdoScaleMenuItemPrivate { + GtkWidget *scale; + GtkAdjustment *adjustment; +}; + +enum { + PROP_0, + PROP_MIN, + PROP_MAX, + PROP_STEP, + PROP_VALUE, + PROP_ADJUSTMENT +}; + +G_DEFINE_TYPE (IdoScaleMenuItem, ido_scale_menu_item, GTK_TYPE_MENU_ITEM) + +#define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDO_TYPE_SCALE_MENU_ITEM, IdoScaleMenuItemPrivate)) + +static void +ido_scale_menu_item_state_changed (GtkWidget *widget, + GtkStateType previous_state) +{ + gtk_widget_set_state (widget, GTK_STATE_NORMAL); +} + +static gboolean +ido_scale_menu_item_scroll_event (GtkWidget *menuitem, + GdkEventScroll *event) +{ + GtkWidget *scale = GET_PRIVATE (IDO_SCALE_MENU_ITEM (menuitem))->scale; + + gtk_widget_event (scale, (GdkEvent *) event); + + return TRUE; +} + +static void +ido_scale_menu_item_class_init (IdoScaleMenuItemClass *item_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (item_class); + GtkObjectClass *object_class = GTK_OBJECT_CLASS (item_class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (item_class); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (item_class); + + container_class->forall = ido_scale_menu_item_forall; + + widget_class->size_request = ido_scale_menu_item_size_request; + widget_class->size_allocate = ido_scale_menu_item_size_allocate; + + widget_class->button_press_event = ido_scale_menu_item_button_press_event; + widget_class->button_release_event = ido_scale_menu_item_button_release_event; + widget_class->motion_notify_event = ido_scale_menu_item_motion_notify_event; + widget_class->scroll_event = ido_scale_menu_item_scroll_event; + widget_class->state_changed = ido_scale_menu_item_state_changed; + + gobject_class->constructor = ido_scale_menu_item_constructor; + gobject_class->set_property = ido_scale_menu_item_set_property; + gobject_class->get_property = ido_scale_menu_item_get_property; + + g_object_class_install_property (gobject_class, + PROP_ADJUSTMENT, + g_param_spec_object ("adjustment", + "Adjustment", + "The adjustment containing the scale value", + GTK_TYPE_ADJUSTMENT, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (IdoScaleMenuItemPrivate)); +} + +static void +ido_scale_menu_item_init (IdoScaleMenuItem *self) +{ + IdoScaleMenuItemPrivate *priv = GET_PRIVATE (self); + + priv->scale = NULL; + priv->adjustment = NULL; +} + +static void +ido_scale_menu_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + IdoScaleMenuItem *menu_item = IDO_SCALE_MENU_ITEM (object); + IdoScaleMenuItemPrivate *priv = GET_PRIVATE (menu_item); + + switch (prop_id) + { + case PROP_ADJUSTMENT: + if (!priv->scale) + { + priv->adjustment = g_value_get_object (value); + priv->scale = gtk_hscale_new (priv->adjustment); + gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE); + } + + ido_scale_menu_item_construct_child (menu_item); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ido_scale_menu_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + IdoScaleMenuItem *menu_item = IDO_SCALE_MENU_ITEM (object); + IdoScaleMenuItemPrivate *priv = GET_PRIVATE (menu_item); + GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->scale)); + + switch (prop_id) + { + case PROP_ADJUSTMENT: + g_value_set_object (value, adjustment); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject* +ido_scale_menu_item_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + IdoScaleMenuItemPrivate *priv; + + object = G_OBJECT_CLASS (ido_scale_menu_item_parent_class)->constructor (type, + n_construct_properties, + construct_params); + + priv = GET_PRIVATE (object); + + if (priv->scale != NULL) + { + ido_scale_menu_item_construct_child (IDO_SCALE_MENU_ITEM (object)); + } + + return object; +} + +static void +ido_scale_menu_item_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer data) +{ + IdoScaleMenuItem *item = IDO_SCALE_MENU_ITEM (container); + IdoScaleMenuItemPrivate *priv = GET_PRIVATE (item); + + GTK_CONTAINER_CLASS (ido_scale_menu_item_parent_class)->forall (container, + include_internals, + callback, + data); + + if (include_internals && priv->scale) + (* callback) (priv->scale, data); +} + +static void +ido_scale_menu_item_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + IdoScaleMenuItem *item; + IdoScaleMenuItemPrivate *priv; + GtkWidget *parent; + GtkPackDirection dir; + gint child_width = 0; + gint child_height = 0; + + parent = gtk_widget_get_parent (widget); + item = IDO_SCALE_MENU_ITEM (widget); + priv = GET_PRIVATE (item); + + if (GTK_IS_MENU_BAR (parent)) + dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); + else + dir = GTK_PACK_DIRECTION_LTR; + + if (priv->scale && GTK_WIDGET_VISIBLE (priv->scale)) + { + GtkRequisition child_requisition; + + gtk_widget_size_request (priv->scale, + &child_requisition); + + child_width = child_requisition.width; + child_height = child_requisition.height; + } + + GTK_WIDGET_CLASS (ido_scale_menu_item_parent_class)->size_request (widget, requisition); + + if (dir == GTK_PACK_DIRECTION_LTR || dir == GTK_PACK_DIRECTION_RTL) + requisition->height = MAX (requisition->height, child_height); + else + requisition->width = MAX (requisition->width, child_width); +} + +static void +ido_scale_menu_item_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + IdoScaleMenuItem *item; + IdoScaleMenuItemPrivate *priv; + GtkWidget *parent; + GtkPackDirection dir; + + item = IDO_SCALE_MENU_ITEM (widget); + priv = GET_PRIVATE (item); + parent = gtk_widget_get_parent (widget); + + if (GTK_IS_MENU_BAR (parent)) + dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); + else + dir = GTK_PACK_DIRECTION_LTR; + + GTK_WIDGET_CLASS (ido_scale_menu_item_parent_class)->size_allocate (widget, allocation); + + if (priv->scale && GTK_WIDGET_VISIBLE (priv->scale)) + { + GtkRequisition child_requisition; + GtkAllocation child_allocation; + gint x, y, offset; + + gtk_widget_get_child_requisition (priv->scale, + &child_requisition); + + if (dir == GTK_PACK_DIRECTION_LTR || dir == GTK_PACK_DIRECTION_RTL) + { + offset = GTK_CONTAINER (item)->border_width + widget->style->xthickness; + + if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) == + (dir == GTK_PACK_DIRECTION_LTR)) + { + x = offset + (GTK_MENU_ITEM (item)->toggle_size - child_requisition.width) / 2; + } + else + { + // TODO rtl + } + } + else + { + offset = GTK_CONTAINER (item)->border_width + widget->style->ythickness; + + // TODO vertical + } + + y = widget->allocation.height / 2 - child_requisition.height / 2; + + child_allocation.width = allocation->width; + child_allocation.height = child_requisition.height; + child_allocation.x = widget->allocation.x + MAX (x, 0); + child_allocation.y = widget->allocation.y + MAX (y, 0); + + gtk_widget_size_allocate (priv->scale, &child_allocation); + } +} + +static gboolean +ido_scale_menu_item_button_press_event (GtkWidget *menuitem, + GdkEventButton *event) +{ + GtkWidget *scale = GET_PRIVATE (IDO_SCALE_MENU_ITEM (menuitem))->scale; + + event->x = 0; + event->y = 0; + + //gtk_grab_add (scale); + //gtk_grab_remove (menuitem->parent); + + gtk_widget_event (scale, (GdkEvent *)event); + + //g_signal_stop_emission_by_name (menuitem->parent, "hide"); + + return FALSE; +} + +static gboolean +ido_scale_menu_item_button_release_event (GtkWidget *menuitem, + GdkEventButton *event) +{ + GtkWidget *scale = GET_PRIVATE (IDO_SCALE_MENU_ITEM (menuitem))->scale; + + //gtk_grab_remove (scale); + + gtk_widget_event (scale, (GdkEvent *) event); + + return TRUE; +} + +static gboolean +ido_scale_menu_item_motion_notify_event (GtkWidget *menuitem, + GdkEventMotion *event) +{ + GtkWidget *scale = GET_PRIVATE (IDO_SCALE_MENU_ITEM (menuitem))->scale; + gtk_widget_event (scale, (GdkEvent *) event); + + return TRUE; +} + +static void +ido_scale_menu_item_construct_child (IdoScaleMenuItem *item) +{ + IdoScaleMenuItemPrivate *priv = GET_PRIVATE (item); + GtkWidget *scale = NULL; + + if (priv->scale) + { + scale = g_object_ref (priv->scale); + if (scale->parent) + { + gtk_container_remove (GTK_CONTAINER (scale->parent), scale); + } + } + + priv->scale = NULL; + + priv->scale = gtk_hscale_new (priv->adjustment); + gtk_widget_set_can_focus (priv->scale, TRUE); + gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE); + + if (GTK_BIN (item)->child) + gtk_container_remove (GTK_CONTAINER (item), + GTK_BIN (item)->child); + + if (scale) + { + priv->scale = scale; + g_object_set (priv->scale, + "visible", TRUE, + "no-show-all", TRUE, + NULL); + + gtk_container_add (GTK_CONTAINER (item), scale); + gtk_widget_show_all (scale); + + g_object_unref (scale); + + return; + } +} + + +/** + * ido_scale_menu_item_new: + * @label: the text of the new menu item. + * @adjustment: A #GtkAdjustment describing the slider value. + * @returns: a new #IdoScaleMenuItem. + * + * Creates a new #IdoScaleMenuItem with an empty label. + **/ +GtkWidget* +ido_scale_menu_item_new (const gchar *label, + GtkAdjustment *adjustment) +{ + return g_object_new (IDO_TYPE_SCALE_MENU_ITEM, + "adjustment", adjustment, + NULL); +} + +/** + * ido_scale_menu_item_new_with_label: + * @label: the text of the menu item. + * @min: The minimum value of the slider. + * @max: The maximum value of the slider. + * @step: The step increment of the slider. + * @returns: a new #IdoScaleMenuItem. + * + * Creates a new #IdoScaleMenuItem containing a label. + **/ +GtkWidget* +ido_scale_menu_item_new_with_range (const gchar *label, + gdouble min, + gdouble max, + gdouble step) +{ + GtkObject *adjustment = gtk_adjustment_new (min, min, max, step, 10 * step, 0); + + return g_object_new (IDO_TYPE_SCALE_MENU_ITEM, + "label", label, + "adjustment", adjustment, + NULL); +} + +#define __IDO_SCALE_MENU_ITEM_C__ |