diff options
| author | John Smith <john.smith7545@yahoo.com> | 2012-09-22 01:43:42 +0000 |
|---|---|---|
| committer | John Smith <john.smith7545@yahoo.com> | 2012-09-22 01:43:42 +0000 |
| commit | 5ab9fc74c53a9e59dd4f46a1f8ff027f878e7836 (patch) | |
| tree | 0f09c4e06b1cc8b33da603f8e114b9c7bd54867b /src | |
| parent | Fix for bug #1053024: set current layer from template in new blank document a... (diff) | |
| download | inkscape-5ab9fc74c53a9e59dd4f46a1f8ff027f878e7836.tar.gz inkscape-5ab9fc74c53a9e59dd4f46a1f8ff027f878e7836.zip | |
Fix for 1014988 : Add gimp spin scale widget
(bzr r11690)
Diffstat (limited to 'src')
| -rw-r--r-- | src/ui/widget/Makefile_insert | 4 | ||||
| -rw-r--r-- | src/ui/widget/gimpspinscale.c | 1089 | ||||
| -rw-r--r-- | src/ui/widget/gimpspinscale.h | 76 | ||||
| -rw-r--r-- | src/ui/widget/spin-scale.cpp | 248 | ||||
| -rw-r--r-- | src/ui/widget/spin-scale.h | 117 |
5 files changed, 1534 insertions, 0 deletions
diff --git a/src/ui/widget/Makefile_insert b/src/ui/widget/Makefile_insert index 8589f16b0..bbda64648 100644 --- a/src/ui/widget/Makefile_insert +++ b/src/ui/widget/Makefile_insert @@ -19,6 +19,8 @@ ink_common_sources += \ ui/widget/entry.h \ ui/widget/filter-effect-chooser.h \ ui/widget/filter-effect-chooser.cpp \ + ui/widget/gimpspinscale.c \ + ui/widget/gimpspinscale.h \ ui/widget/frame.cpp \ ui/widget/frame.h \ ui/widget/imageicon.cpp \ @@ -62,6 +64,8 @@ ink_common_sources += \ ui/widget/selected-style.cpp \ ui/widget/spinbutton.h \ ui/widget/spinbutton.cpp \ + ui/widget/spin-scale.h \ + ui/widget/spin-scale.cpp \ ui/widget/spin-slider.h \ ui/widget/spin-slider.cpp \ ui/widget/style-subject.h \ diff --git a/src/ui/widget/gimpspinscale.c b/src/ui/widget/gimpspinscale.c new file mode 100644 index 000000000..1b7ac7bfe --- /dev/null +++ b/src/ui/widget/gimpspinscale.c @@ -0,0 +1,1089 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpspinscale.c + * Copyright (C) 2010 Michael Natterer <mitch@gimp.org> + * 2012 Øyvind Kolås <pippin@gimp.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <math.h> +#include <string.h> +#include <gdk/gdkkeysyms.h> + +#include "gimpspinscale.h" + + +enum +{ + PROP_0, + PROP_LABEL, + PROP_FOCUS_WIDGET +}; + +typedef enum +{ + TARGET_NUMBER, + TARGET_UPPER, + TARGET_LOWER +#if WITH_GTKMM_3_0 + ,TARGET_NONE +#endif +} SpinScaleTarget; + +typedef enum +{ + APPEARANCE_FULL = 1, /* Full size suitable for tablets */ + APPEARANCE_COMPACT, /* Compact, suitable for desktops with mouse control */ +} SpinScaleAppearance; + +typedef struct _GimpSpinScalePrivate GimpSpinScalePrivate; + +struct _GimpSpinScalePrivate +{ + gchar *label; + + gboolean scale_limits_set; + gdouble scale_lower; + gdouble scale_upper; + gdouble gamma; + + PangoLayout *layout; + gboolean changing_value; + gboolean relative_change; + gdouble start_x; + gdouble start_value; + + GtkWidget* focusWidget; + gboolean transferFocus; + SpinScaleAppearance appearanceMode; +}; + +#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + GIMP_TYPE_SPIN_SCALE, \ + GimpSpinScalePrivate)) + + +static void gimp_spin_scale_dispose (GObject *object); +static void gimp_spin_scale_finalize (GObject *object); +static void gimp_spin_scale_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_spin_scale_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_spin_scale_style_set (GtkWidget *widget, + GtkStyle *prev_style); + +#if WITH_GTKMM_3_0 +static void gimp_spin_scale_get_preferred_width (GtkWidget *widget, + gint *minimum_width, + gint *natural_width); +static void gimp_spin_scale_get_preferred_height (GtkWidget *widget, + gint *minimum_width, + gint *natural_width); +static gboolean gimp_spin_scale_draw (GtkWidget *widget, + cairo_t *cr); +#else +static void gimp_spin_scale_size_request (GtkWidget *widget, + GtkRequisition *requisition); + +static gboolean gimp_spin_scale_expose (GtkWidget *widget, + GdkEventExpose *event); +#endif + +static gboolean gimp_spin_scale_button_press (GtkWidget *widget, + GdkEventButton *event); +static gboolean gimp_spin_scale_button_release (GtkWidget *widget, + GdkEventButton *event); +static gboolean gimp_spin_scale_motion_notify (GtkWidget *widget, + GdkEventMotion *event); +static gboolean gimp_spin_scale_leave_notify (GtkWidget *widget, + GdkEventCrossing *event); +static gboolean gimp_spin_scale_keypress( GtkWidget *widget, + GdkEventKey *event); + +static void gimp_spin_scale_defocus( GtkSpinButton *spin_button ); + +static void gimp_spin_scale_value_changed (GtkSpinButton *spin_button); + + +G_DEFINE_TYPE (GimpSpinScale, gimp_spin_scale, GTK_TYPE_SPIN_BUTTON); + +#define parent_class gimp_spin_scale_parent_class + + +static void +gimp_spin_scale_class_init (GimpSpinScaleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkSpinButtonClass *spin_button_class = GTK_SPIN_BUTTON_CLASS (klass); + + object_class->dispose = gimp_spin_scale_dispose; + object_class->finalize = gimp_spin_scale_finalize; + object_class->set_property = gimp_spin_scale_set_property; + object_class->get_property = gimp_spin_scale_get_property; + + widget_class->style_set = gimp_spin_scale_style_set; +#if WITH_GTKMM_3_0 + widget_class->get_preferred_width = gimp_spin_scale_get_preferred_width; + widget_class->get_preferred_height = gimp_spin_scale_get_preferred_height; + widget_class->draw = gimp_spin_scale_draw; +#else + widget_class->size_request = gimp_spin_scale_size_request; + widget_class->expose_event = gimp_spin_scale_expose; +#endif + widget_class->button_press_event = gimp_spin_scale_button_press; + widget_class->button_release_event = gimp_spin_scale_button_release; + widget_class->motion_notify_event = gimp_spin_scale_motion_notify; + widget_class->leave_notify_event = gimp_spin_scale_leave_notify; + widget_class->key_press_event = gimp_spin_scale_keypress; + + spin_button_class->value_changed = gimp_spin_scale_value_changed; + + g_object_class_install_property (object_class, PROP_LABEL, + g_param_spec_string ("label", NULL, NULL, + NULL, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (GimpSpinScalePrivate)); +} + +static void +gimp_spin_scale_init (GimpSpinScale *scale) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (scale); + + gtk_entry_set_alignment (GTK_ENTRY (scale), 1.0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (scale), TRUE); + + private->gamma = 1.0; + private->focusWidget = NULL; + private->transferFocus = FALSE; + private->appearanceMode = APPEARANCE_COMPACT; +} + +static void +gimp_spin_scale_dispose (GObject *object) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (object); + + if (private->layout) + { + g_object_unref (private->layout); + private->layout = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_spin_scale_finalize (GObject *object) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (object); + + if (private->label) + { + g_free (private->label); + private->label = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_spin_scale_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_LABEL: + g_free (private->label); + private->label = g_value_dup_string (value); + if (private->layout) + { + g_object_unref (private->layout); + private->layout = NULL; + } + gtk_widget_queue_resize (GTK_WIDGET (object)); + break; + + case PROP_FOCUS_WIDGET: + { + /* TODO unhook prior */ + private->focusWidget = (GtkWidget*)g_value_get_pointer( value ); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_spin_scale_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_LABEL: + g_value_set_string (value, private->label); + break; + + case PROP_FOCUS_WIDGET: + g_value_set_pointer( value, private->focusWidget ); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +void +gimp_spin_scale_set_focuswidget( GtkWidget *scale, GtkWidget* widget ) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (scale); + + /* TODO unhook prior */ + + private->focusWidget = widget; +} + +void +gimp_spin_scale_set_appearance( GtkWidget *widget, const gchar *appearance) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); + + if ( strcmp("full", appearance) == 0 ) { + private->appearanceMode = APPEARANCE_FULL; + } else if ( strcmp("compact", appearance) == 0 ) { + private->appearanceMode = APPEARANCE_COMPACT; + } +} + +static void +#if WITH_GTKMM_3_0 +gimp_spin_scale_get_preferred_width (GtkWidget *widget, + gint *minimum_width, + gint *natural_width) +#else +gimp_spin_scale_size_request (GtkWidget *widget, + GtkRequisition *requisition) +#endif +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); + GtkStyle *style = gtk_widget_get_style (widget); + PangoContext *context = gtk_widget_get_pango_context (widget); + PangoFontMetrics *metrics; + +#if WITH_GTKMM_3_0 + GTK_WIDGET_CLASS (parent_class)->get_preferred_width (widget, + minimum_width, + natural_width); +#else + gint height; + GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition); +#endif + + metrics = pango_context_get_metrics (context, style->font_desc, + pango_context_get_language (context)); + +#if WITH_GTKMM_3_0 +#else + height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + + pango_font_metrics_get_descent (metrics)); + + if (private->appearanceMode == APPEARANCE_COMPACT) { + requisition->height += 1; + } else { + requisition->height += height; + } + +#endif + + + if (private->label) + { + gint char_width; + gint digit_width; + gint char_pixels; + + char_width = pango_font_metrics_get_approximate_char_width (metrics); + digit_width = pango_font_metrics_get_approximate_digit_width (metrics); + char_pixels = PANGO_PIXELS (MAX (char_width, digit_width)); + +#if WITH_GTKMM_3_0 + *minimum_width += char_pixels * 3; + *natural_width += char_pixels * 3; +#else + /* ~3 chars for the ellipses */ + requisition->width += char_pixels * 3; +#endif + + } + + pango_font_metrics_unref (metrics); +} + +#if WITH_GTKMM_3_0 +static void +gimp_spin_scale_get_preferred_height (GtkWidget *widget, + gint *minimum_height, + gint *natural_height) +{ + GtkStyle *style = gtk_widget_get_style (widget); + PangoContext *context = gtk_widget_get_pango_context (widget); + PangoFontMetrics *metrics; + //gint height; + + GTK_WIDGET_CLASS (parent_class)->get_preferred_height (widget, + minimum_height, + natural_height); + + metrics = pango_context_get_metrics (context, style->font_desc, + pango_context_get_language (context)); + + //height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + + // pango_font_metrics_get_descent (metrics)); + + *minimum_height += 1; + *natural_height += 1; + + pango_font_metrics_unref (metrics); +} +#endif + +static void +gimp_spin_scale_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); + + GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style); + + if (private->layout) + { + g_object_unref (private->layout); + private->layout = NULL; + } +} + + +static gboolean + +#if WITH_GTKMM_3_0 + gimp_spin_scale_draw (GtkWidget *widget, cairo_t *cr) +#else + gimp_spin_scale_expose (GtkWidget *widget, GdkEventExpose *event) +#endif +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); + GtkStyle *style = gtk_widget_get_style (widget); + + +#if WITH_GTKMM_3_0 + GtkAllocation allocation; + + cairo_save (cr); + GTK_WIDGET_CLASS (parent_class)->draw (widget, cr); + cairo_restore (cr); + + gtk_widget_get_allocation (widget, &allocation); + + +#else + cairo_t *cr; + gint w; + + GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + + cr = gdk_cairo_create (event->window); + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + w = gdk_window_get_width (event->window); + +#endif + + cairo_set_line_width (cr, 1.0); + + +#if WITH_GTKMM_3_0 + if (private->label) + { + GdkRectangle text_area; + gint minimum_width; + gint natural_width; +#else + if (private->label && + gtk_widget_is_drawable (widget) && + event->window == gtk_entry_get_text_window (GTK_ENTRY (widget))) + { + GtkRequisition requisition; + GtkAllocation allocation; +#endif + + PangoRectangle logical; + gint layout_offset_x; + gint layout_offset_y; + +#if WITH_GTKMM_3_0 + gtk_entry_get_text_area (GTK_ENTRY (widget), &text_area); + + GTK_WIDGET_CLASS (parent_class)->get_preferred_width (widget, + &minimum_width, + &natural_width); +#else + GTK_WIDGET_CLASS (parent_class)->size_request (widget, &requisition); + gtk_widget_get_allocation (widget, &allocation); +#endif + + if (! private->layout) + { + private->layout = gtk_widget_create_pango_layout (widget, + private->label); + pango_layout_set_ellipsize (private->layout, PANGO_ELLIPSIZE_END); + } + + pango_layout_set_width (private->layout, + PANGO_SCALE * +#if WITH_GTKMM_3_0 + (allocation.width - minimum_width + 10)); +#else + (allocation.width - requisition.width + 10)); +#endif + pango_layout_get_pixel_extents (private->layout, NULL, &logical); + + gtk_entry_get_layout_offsets (GTK_ENTRY (widget), NULL, &layout_offset_y); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) +#if WITH_GTKMM_3_0 + layout_offset_x = text_area.x + text_area.width - logical.width - 4; +#else + layout_offset_x = w - logical.width - 4; +#endif + else + layout_offset_x = 4; + + layout_offset_x -= logical.x; + +#if WITH_GTKMM_3_0 + cairo_move_to (cr, layout_offset_x, text_area.y + layout_offset_y-3); +#else + cairo_move_to (cr, layout_offset_x, layout_offset_y-3); +#endif + + gdk_cairo_set_source_color (cr, + &style->text[gtk_widget_get_state (widget)]); + + pango_cairo_show_layout (cr, private->layout); + } + +#if WITH_GTKMM_3_0 +#else + cairo_destroy (cr); +#endif + + return FALSE; +} + +#if WITH_GTKMM_3_0 +/* Returns TRUE if a translation should be done */ +static gboolean +gtk_widget_get_translation_to_window (GtkWidget *widget, + GdkWindow *window, + int *x, + int *y) +{ + GdkWindow *w, *widget_window; + + if (!gtk_widget_get_has_window (widget)) + { + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + + *x = -allocation.x; + *y = -allocation.y; + } + else + { + *x = 0; + *y = 0; + } + + widget_window = gtk_widget_get_window (widget); + + for (w = window; w && w != widget_window; w = gdk_window_get_parent (w)) + { + int wx, wy; + gdk_window_get_position (w, &wx, &wy); + *x += wx; + *y += wy; + } + + if (w == NULL) + { + *x = 0; + *y = 0; + return FALSE; + } + + return TRUE; +} + +static void +gimp_spin_scale_event_to_widget_coords (GtkWidget *widget, + GdkWindow *window, + gdouble event_x, + gdouble event_y, + gint *widget_x, + gint *widget_y) +{ + gint tx, ty; + + if (gtk_widget_get_translation_to_window (widget, window, &tx, &ty)) + { + event_x += tx; + event_y += ty; + } + + *widget_x = event_x; + *widget_y = event_y; +} +#endif + +static SpinScaleTarget +gimp_spin_scale_get_target (GtkWidget *widget, + gdouble x, + gdouble y) +{ + GtkAllocation allocation; + PangoRectangle logical; + gint layout_x; + gint layout_y; + + gtk_widget_get_allocation (widget, &allocation); + gtk_entry_get_layout_offsets (GTK_ENTRY (widget), &layout_x, &layout_y); + pango_layout_get_pixel_extents (gtk_entry_get_layout (GTK_ENTRY (widget)), + NULL, &logical); + +#if WITH_GTKMM_3_0 + GdkRectangle text_area; + gtk_entry_get_text_area (GTK_ENTRY (widget), &text_area); + + if (x >= text_area.x && x < text_area.width && + y >= text_area.y && y < text_area.height) + { + x -= text_area.x; + y -= text_area.y; + + if (x > layout_x && x < layout_x + logical.width && + y > layout_y && y < layout_y + logical.height) + { + return TARGET_NUMBER; + } + else if (y > text_area.height / 2) + { + return TARGET_LOWER; + } + + return TARGET_UPPER; + } + + return TARGET_NONE; +#else + if (x > layout_x && x < layout_x + logical.width && + y > layout_y && y < layout_y + logical.height) + { + return TARGET_NUMBER; + } + + else if (y > allocation.height / 2) + { + return TARGET_LOWER; + } + return TARGET_UPPER; +#endif +} + +static void +gimp_spin_scale_get_limits (GimpSpinScale *scale, + gdouble *lower, + gdouble *upper) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (scale); + + if (private->scale_limits_set) + { + *lower = private->scale_lower; + *upper = private->scale_upper; + } + else + { + GtkSpinButton *spin_button = GTK_SPIN_BUTTON (scale); + GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button); + + *lower = gtk_adjustment_get_lower (adjustment); + *upper = gtk_adjustment_get_upper (adjustment); + } +} + +static void +gimp_spin_scale_change_value (GtkWidget *widget, + gdouble x) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); + GtkSpinButton *spin_button = GTK_SPIN_BUTTON (widget); + GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button); + gdouble lower; + gdouble upper; + gdouble value; +#if WITH_GTKMM_3_0 +#else +#endif +#if WITH_GTKMM_3_0 + GdkRectangle text_area; + gtk_entry_get_text_area (GTK_ENTRY (widget), &text_area); + gimp_spin_scale_get_limits (GIMP_SPIN_SCALE (widget), &lower, &upper); +#else + GdkWindow *text_window = gtk_entry_get_text_window (GTK_ENTRY (widget)); + gint width; + + gimp_spin_scale_get_limits (GIMP_SPIN_SCALE (widget), &lower, &upper); + width = gdk_window_get_width (text_window); + +#endif + + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) +#if WITH_GTKMM_3_0 + x = text_area.width - x; +#else + x = width - x; +#endif + + + if (private->relative_change) + { + gdouble diff; + gdouble step; + + +#if WITH_GTKMM_3_0 + step = (upper - lower) / text_area.width / 10.0; +#else + step = (upper - lower) / width / 10.0; +#endif + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + +#if WITH_GTKMM_3_0 + diff = x - (text_area.width - private->start_x); +#else + diff = x - (width - private->start_x); +#endif + else + diff = x - private->start_x; + + value = (private->start_value + diff * step); + } + else + { + gdouble fraction; + + +#if WITH_GTKMM_3_0 + fraction = x / (gdouble) text_area.width; +#else + fraction = x / (gdouble) width; +#endif + if (fraction > 0.0) + fraction = pow (fraction, private->gamma); + + value = fraction * (upper - lower) + lower; + } + + gtk_adjustment_set_value (adjustment, value); +} + +static gboolean +gimp_spin_scale_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); + + private->changing_value = FALSE; + private->relative_change = FALSE; + +#if WITH_GTKMM_3_0 + gint x, y; + gimp_spin_scale_event_to_widget_coords (widget, event->window, + event->x, event->y, + &x, &y); + switch (gimp_spin_scale_get_target (widget, x, y)) + { + case TARGET_UPPER: + private->changing_value = TRUE; + + gtk_widget_grab_focus (widget); + + gimp_spin_scale_change_value (widget, x); + + return TRUE; + + case TARGET_LOWER: + private->changing_value = TRUE; + + gtk_widget_grab_focus (widget); + + private->relative_change = TRUE; + private->start_x = x; + private->start_value = gtk_adjustment_get_value (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget))); + + return TRUE; + + default: + break; + } +#else + if (event->window == gtk_entry_get_text_window (GTK_ENTRY (widget))) + { + switch (gimp_spin_scale_get_target (widget, event->x, event->y)) + { + case TARGET_UPPER: + private->changing_value = TRUE; + + gtk_widget_grab_focus (widget); + + gimp_spin_scale_change_value (widget, event->x); + + return TRUE; + + case TARGET_LOWER: + private->changing_value = TRUE; + + gtk_widget_grab_focus (widget); + + private->relative_change = TRUE; + private->start_x = event->x; + private->start_value = gtk_adjustment_get_value (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget))); + + return TRUE; + + default: + break; + } + } +#endif + + return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event); +} + +static gboolean +gimp_spin_scale_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); +#if WITH_GTKMM_3_0 + gint x, y; + + gimp_spin_scale_event_to_widget_coords (widget, event->window, + event->x, event->y, + &x, &y); +#endif + + if (private->changing_value) + { + private->changing_value = FALSE; +#if WITH_GTKMM_3_0 + gimp_spin_scale_change_value (widget, x); +#else + gimp_spin_scale_change_value (widget, event->x); +#endif + return TRUE; + } + + return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event); +} + +static gboolean +gimp_spin_scale_motion_notify (GtkWidget *widget, + GdkEventMotion *event) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); +#if WITH_GTKMM_3_0 + gint x, y; + + gimp_spin_scale_event_to_widget_coords (widget, event->window, + event->x, event->y, + &x, &y); +#endif + + if (private->changing_value) + { +#if WITH_GTKMM_3_0 + gimp_spin_scale_change_value (widget, x); +#else + gimp_spin_scale_change_value (widget, event->x); +#endif + + return TRUE; + } + + GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event); + + if (! (event->state & + (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) +#if WITH_GTKMM_3_0 +#else + && event->window == gtk_entry_get_text_window (GTK_ENTRY (widget)) +#endif + ) + { + GdkDisplay *display = gtk_widget_get_display (widget); + GdkCursor *cursor = NULL; + +#if WITH_GTKMM_3_0 + switch (gimp_spin_scale_get_target (widget, x, y)) +#else + switch (gimp_spin_scale_get_target (widget, event->x, event->y)) +#endif + { + case TARGET_NUMBER: + cursor = gdk_cursor_new_for_display (display, GDK_XTERM); + break; + + case TARGET_UPPER: + cursor = gdk_cursor_new_for_display (display, GDK_SB_UP_ARROW); + break; + + case TARGET_LOWER: + cursor = gdk_cursor_new_for_display (display, GDK_SB_H_DOUBLE_ARROW); + break; + + default: + break; + } + + +#if WITH_GTKMM_3_0 + if (cursor) + { + gdk_window_set_cursor (event->window, cursor); + g_object_unref (cursor); + } +#else + gdk_window_set_cursor (event->window, cursor); + gdk_cursor_unref (cursor); +#endif + } + + return FALSE; +} + +static gboolean +gimp_spin_scale_leave_notify (GtkWidget *widget, + GdkEventCrossing *event) +{ + gdk_window_set_cursor (event->window, NULL); + + return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event); +} + +gboolean gimp_spin_scale_keypress( GtkWidget *widget, GdkEventKey *event) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (widget); + guint key = 0; + gdk_keymap_translate_keyboard_state( gdk_keymap_get_for_display( gdk_display_get_default() ), + event->hardware_keycode, (GdkModifierType)event->state, + 0, &key, 0, 0, 0 ); + + switch ( key ) { + + case GDK_KEY_Escape: + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + { + private->transferFocus = TRUE; + gimp_spin_scale_defocus( GTK_SPIN_BUTTON(widget) ); + } + break; + + } + + return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event); +} + +static void +gimp_spin_scale_defocus( GtkSpinButton *spin_button ) +{ + GimpSpinScalePrivate *private = GET_PRIVATE (spin_button); + + if ( private->transferFocus ) { + if ( private->focusWidget ) { + gtk_widget_grab_focus( private->focusWidget ); + } + } +} + +static void +gimp_spin_scale_value_changed (GtkSpinButton *spin_button) +{ + GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button); + GimpSpinScalePrivate *private = GET_PRIVATE (spin_button); + gdouble lower; + gdouble upper; + gdouble value; + + gimp_spin_scale_get_limits (GIMP_SPIN_SCALE (spin_button), &lower, &upper); + + value = CLAMP (gtk_adjustment_get_value (adjustment), lower, upper); + + + gtk_entry_set_progress_fraction (GTK_ENTRY (spin_button), + pow ((value - lower) / (upper - lower), + 1.0 / private->gamma)); + + // TODO - Allow scrollwheel to change value then return focus, + // but clicks/keypress should keep focus in the control + //if ( gtk_widget_has_focus( GTK_WIDGET(spin_button) ) ) { + // gimp_spin_scale_defocus( spin_button ); + //} +} + + +/* public functions */ + +GtkWidget * +gimp_spin_scale_new (GtkAdjustment *adjustment, + const gchar *label, + gint digits) +{ + g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL); + + return g_object_new (GIMP_TYPE_SPIN_SCALE, + "adjustment", adjustment, + "label", label, + "digits", digits, + NULL); +} + +void +gimp_spin_scale_set_scale_limits (GimpSpinScale *scale, + gdouble lower, + gdouble upper) +{ + GimpSpinScalePrivate *private; + GtkSpinButton *spin_button; + GtkAdjustment *adjustment; + + g_return_if_fail (GIMP_IS_SPIN_SCALE (scale)); + + private = GET_PRIVATE (scale); + spin_button = GTK_SPIN_BUTTON (scale); + adjustment = gtk_spin_button_get_adjustment (spin_button); + + g_return_if_fail (lower >= gtk_adjustment_get_lower (adjustment)); + g_return_if_fail (upper <= gtk_adjustment_get_upper (adjustment)); + + private->scale_limits_set = TRUE; + private->scale_lower = lower; + private->scale_upper = upper; + private->gamma = 1.0; + + gimp_spin_scale_value_changed (spin_button); +} + +void +gimp_spin_scale_set_gamma (GimpSpinScale *scale, + gdouble gamma) +{ + GimpSpinScalePrivate *private; + + g_return_if_fail (GIMP_IS_SPIN_SCALE (scale)); + + private = GET_PRIVATE (scale); + + private->gamma = gamma; + + gimp_spin_scale_value_changed (GTK_SPIN_BUTTON (scale)); +} + +gdouble +gimp_spin_scale_get_gamma (GimpSpinScale *scale) +{ + GimpSpinScalePrivate *private; + + g_return_val_if_fail (GIMP_IS_SPIN_SCALE (scale), 1.0); + + private = GET_PRIVATE (scale); + + return private->gamma; +} + +void +gimp_spin_scale_unset_scale_limits (GimpSpinScale *scale) +{ + GimpSpinScalePrivate *private; + + g_return_if_fail (GIMP_IS_SPIN_SCALE (scale)); + + private = GET_PRIVATE (scale); + + private->scale_limits_set = FALSE; + private->scale_lower = 0.0; + private->scale_upper = 0.0; + + gimp_spin_scale_value_changed (GTK_SPIN_BUTTON (scale)); +} + +gboolean +gimp_spin_scale_get_scale_limits (GimpSpinScale *scale, + gdouble *lower, + gdouble *upper) +{ + GimpSpinScalePrivate *private; + + g_return_val_if_fail (GIMP_IS_SPIN_SCALE (scale), FALSE); + + private = GET_PRIVATE (scale); + + if (lower) + *lower = private->scale_lower; + + if (upper) + *upper = private->scale_upper; + + return private->scale_limits_set; +} diff --git a/src/ui/widget/gimpspinscale.h b/src/ui/widget/gimpspinscale.h new file mode 100644 index 000000000..ad63625ac --- /dev/null +++ b/src/ui/widget/gimpspinscale.h @@ -0,0 +1,76 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpspinscale.h + * Copyright (C) 2010 Michael Natterer <mitch@gimp.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GIMP_SPIN_SCALE_H__ +#define __GIMP_SPIN_SCALE_H__ + +#ifndef WITH_GIMP +#include <gtk/gtk.h> +#endif + +G_BEGIN_DECLS + +#define GIMP_TYPE_SPIN_SCALE (gimp_spin_scale_get_type ()) +#define GIMP_SPIN_SCALE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SPIN_SCALE, GimpSpinScale)) +#define GIMP_SPIN_SCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SPIN_SCALE, GimpSpinScaleClass)) +#define GIMP_IS_SPIN_SCALE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SPIN_SCALE)) +#define GIMP_IS_SPIN_SCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SPIN_SCALE)) +#define GIMP_SPIN_SCALE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SPIN_SCALE, GimpSpinScaleClass)) + + +typedef struct _GimpSpinScale GimpSpinScale; +typedef struct _GimpSpinScaleClass GimpSpinScaleClass; + +struct _GimpSpinScale +{ + GtkSpinButton parent_instance; +}; + +struct _GimpSpinScaleClass +{ + GtkSpinButtonClass parent_class; +}; + + +GType gimp_spin_scale_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_spin_scale_new (GtkAdjustment *adjustment, + const gchar *label, + gint digits); + +void gimp_spin_scale_set_scale_limits (GimpSpinScale *scale, + gdouble lower, + gdouble upper); +void gimp_spin_scale_unset_scale_limits (GimpSpinScale *scale); +gboolean gimp_spin_scale_get_scale_limits (GimpSpinScale *scale, + gdouble *lower, + gdouble *upper); + +void gimp_spin_scale_set_gamma (GimpSpinScale *scale, + gdouble gamma); +gdouble gimp_spin_scale_get_gamma (GimpSpinScale *scale); + +void gimp_spin_scale_set_focuswidget( GtkWidget *scale, GtkWidget* widget ); + +void gimp_spin_scale_set_appearance( GtkWidget *scale, const gchar *appearance); + +G_END_DECLS + +#endif /* __GIMP_SPIN_SCALE_H__ */ diff --git a/src/ui/widget/spin-scale.cpp b/src/ui/widget/spin-scale.cpp new file mode 100644 index 000000000..00c575568 --- /dev/null +++ b/src/ui/widget/spin-scale.cpp @@ -0,0 +1,248 @@ +/* + * Author: + * + * Copyright (C) 2012 Author + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + +#include <glib.h> +#include <glibmm/i18n.h> +#include <glibmm/stringutils.h> +#include <gtkmm/adjustment.h> + +#include "spin-scale.h" +#include "ui/widget/gimpspinscale.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +SpinScale::SpinScale(const char* label, double value, double lower, double upper, double step_inc, + double climb_rate, int digits, const SPAttributeEnum a, const char* tip_text) + : AttrWidget(a, value) +{ + +#if WITH_GTKMM_3_0 + _adjustment = Gtk::Adjustment::create(value, lower, upper, step_inc); + _spinscale = gimp_spin_scale_new (_adjustment->gobj(), label, digits); +#else + _adjustment = new Gtk::Adjustment(value, lower, upper, step_inc); + _spinscale = gimp_spin_scale_new (_adjustment->gobj(), label, digits); +#endif + + signal_value_changed().connect(signal_attr_changed().make_slot()); + + pack_start(*Gtk::manage(Glib::wrap(_spinscale))); + + if (tip_text){ + gtk_widget_set_tooltip_text( _spinscale, tip_text ); + } + + show_all_children(); +} + +SpinScale::SpinScale(const char* label, +#if WITH_GTKMM_3_0 + Glib::RefPtr<Gtk::Adjustment> adj, +#else + Gtk::Adjustment *adj, +#endif + int digits, const SPAttributeEnum a, const char* tip_text) + : AttrWidget(a, 0.0), + _adjustment(adj) + +{ + + _spinscale = gimp_spin_scale_new (_adjustment->gobj(), label, digits); + + signal_value_changed().connect(signal_attr_changed().make_slot()); + + pack_start(*Gtk::manage(Glib::wrap(_spinscale))); + + if (tip_text){ + gtk_widget_set_tooltip_text( _spinscale, tip_text ); + } + + show_all_children(); +} + +Glib::ustring SpinScale::get_as_attribute() const +{ + const double val = _adjustment->get_value(); + + //if(_spin.get_digits() == 0) + // return Glib::Ascii::dtostr((int)val); + //else + return Glib::Ascii::dtostr(val); +} + +void SpinScale::set_from_attribute(SPObject* o) +{ + const gchar* val = attribute_value(o); + if(val) + _adjustment->set_value(Glib::Ascii::strtod(val)); + else + _adjustment->set_value(get_default()->as_double()); +} + +Glib::SignalProxy0<void> SpinScale::signal_value_changed() +{ + return _adjustment->signal_value_changed(); +} + +double SpinScale::get_value() const +{ + return _adjustment->get_value(); +} + +void SpinScale::set_value(const double val) +{ + _adjustment->set_value(val); +} + +void SpinScale::set_focuswidget(GtkWidget *widget) +{ + gimp_spin_scale_set_focuswidget(_spinscale, widget); +} + + +void SpinScale::set_appearance(const gchar* appearance) +{ + gimp_spin_scale_set_appearance(_spinscale, appearance); +} + +#if WITH_GTKMM_3_0 +const Glib::RefPtr<Gtk::Adjustment> SpinScale::get_adjustment() const +#else +const Gtk::Adjustment *SpinScale::get_adjustment() const +#endif +{ + return _adjustment; +} +#if WITH_GTKMM_3_0 +Glib::RefPtr<Gtk::Adjustment> SpinScale::get_adjustment() +#else +Gtk::Adjustment *SpinScale::get_adjustment() +#endif +{ + return _adjustment; +} + + +DualSpinScale::DualSpinScale(const char* label1, const char* label2, double value, double lower, double upper, double step_inc, + double climb_rate, int digits, const SPAttributeEnum a, char* tip_text1, char* tip_text2) + : AttrWidget(a), + _s1(label1, value, lower, upper, step_inc, climb_rate, digits, SP_ATTR_INVALID, tip_text1), + _s2(label2, value, lower, upper, step_inc, climb_rate, digits, SP_ATTR_INVALID, tip_text2), + //TRANSLATORS: "Link" means to _link_ two sliders together + _link(C_("Sliders", "Link")) +{ + signal_value_changed().connect(signal_attr_changed().make_slot()); + + _s1.get_adjustment()->signal_value_changed().connect(_signal_value_changed.make_slot()); + _s2.get_adjustment()->signal_value_changed().connect(_signal_value_changed.make_slot()); + _s1.get_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &DualSpinScale::update_linked)); + + _link.signal_toggled().connect(sigc::mem_fun(*this, &DualSpinScale::link_toggled)); + + Gtk::VBox* vb = Gtk::manage(new Gtk::VBox); + vb->add(_s1); + vb->add(_s2); + pack_start(*vb); + pack_start(_link, false, false); + _link.set_active(true); + + show_all(); +} + +Glib::ustring DualSpinScale::get_as_attribute() const +{ + if(_link.get_active()) + return _s1.get_as_attribute(); + else + return _s1.get_as_attribute() + " " + _s2.get_as_attribute(); +} + +void DualSpinScale::set_from_attribute(SPObject* o) +{ + const gchar* val = attribute_value(o); + if(val) { + // Split val into parts + gchar** toks = g_strsplit(val, " ", 2); + + if(toks) { + double v1 = 0.0, v2 = 0.0; + if(toks[0]) + v1 = v2 = Glib::Ascii::strtod(toks[0]); + if(toks[1]) + v2 = Glib::Ascii::strtod(toks[1]); + + _link.set_active(toks[1] == 0); + + _s1.get_adjustment()->set_value(v1); + _s2.get_adjustment()->set_value(v2); + + g_strfreev(toks); + } + } +} + +sigc::signal<void>& DualSpinScale::signal_value_changed() +{ + return _signal_value_changed; +} + +const SpinScale& DualSpinScale::get_SpinScale1() const +{ + return _s1; +} + +SpinScale& DualSpinScale::get_SpinScale1() +{ + return _s1; +} + +const SpinScale& DualSpinScale::get_SpinScale2() const +{ + return _s2; +} + +SpinScale& DualSpinScale::get_SpinScale2() +{ + return _s2; +} + +/*void DualSpinScale::remove_scale() +{ + _s1.remove_scale(); + _s2.remove_scale(); +}*/ + +void DualSpinScale::link_toggled() +{ + _s2.set_sensitive(!_link.get_active()); + update_linked(); +} + +void DualSpinScale::update_linked() +{ + if(_link.get_active()) + _s2.set_value(_s1.get_value()); +} + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/widget/spin-scale.h b/src/ui/widget/spin-scale.h new file mode 100644 index 000000000..a8403307f --- /dev/null +++ b/src/ui/widget/spin-scale.h @@ -0,0 +1,117 @@ +/* + * Author: + * + * Copyright (C) 2012 Author + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + +#ifndef INKSCAPE_UI_WIDGET_SPIN_SCALE_H +#define INKSCAPE_UI_WIDGET_SPIN_SCALE_H + +#include <gtkmm/adjustment.h> +#include <gtkmm/box.h> +#include <gtkmm/scale.h> +#include <gtkmm/togglebutton.h> +#include "spinbutton.h" +#include "attr-widget.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +/** + * Wrap the gimpspinscale class + * A combo widget with label, scale slider, spinbutton and adjustment + */ +class SpinScale : public Gtk::HBox, public AttrWidget +{ + +public: + SpinScale(const char* label, double value, double lower, double upper, double step_inc, double climb_rate, + int digits, const SPAttributeEnum a = SP_ATTR_INVALID, const char* tip_text = NULL); + + SpinScale(const char* label, +#if WITH_GTKMM_3_0 + Glib::RefPtr<Gtk::Adjustment> adj, +#else + Gtk::Adjustment *adj, +#endif + int digits, const SPAttributeEnum a = SP_ATTR_INVALID, const char* tip_text = NULL); + + virtual Glib::ustring get_as_attribute() const; + virtual void set_from_attribute(SPObject*); + + // Shortcuts to _adjustment + Glib::SignalProxy0<void> signal_value_changed(); + double get_value() const; + void set_value(const double); + void set_focuswidget(GtkWidget *widget); + void set_appearance(const gchar* appearance); + +#if WITH_GTKMM_3_0 + const Glib::RefPtr<Gtk::Adjustment> get_adjustment() const; + Glib::RefPtr<Gtk::Adjustment> get_adjustment(); +#else + const Gtk::Adjustment *get_adjustment() const; + Gtk::Adjustment *get_adjustment(); +#endif + +private: +#if WITH_GTKMM_3_0 + Glib::RefPtr<Gtk::Adjustment> _adjustment; +#else + Gtk::Adjustment *_adjustment; +#endif + + GtkWidget *_spinscale; +}; + + +/** + * Contains two SpinScales for controlling number-opt-number attributes. + * + * @see SpinScale + */ +class DualSpinScale : public Gtk::HBox, public AttrWidget +{ +public: + DualSpinScale(const char* label1, const char* label2, double value, double lower, double upper, double step_inc, + double climb_rate, int digits, const SPAttributeEnum, char* tip_text1, char* tip_text2); + + virtual Glib::ustring get_as_attribute() const; + virtual void set_from_attribute(SPObject*); + + sigc::signal<void>& signal_value_changed(); + + const SpinScale& get_SpinScale1() const; + SpinScale& get_SpinScale1(); + + const SpinScale& get_SpinScale2() const; + SpinScale& get_SpinScale2(); + + //void remove_scale(); +private: + void link_toggled(); + void update_linked(); + sigc::signal<void> _signal_value_changed; + SpinScale _s1, _s2; + Gtk::ToggleButton _link; +}; + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +#endif // INKSCAPE_UI_WIDGET_SPIN_SCALE_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
