[gimp] app: add mnemonic support for GimpSpinScale's label
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: add mnemonic support for GimpSpinScale's label
- Date: Tue, 28 May 2013 20:48:29 +0000 (UTC)
commit 4c0169aaaf3eb4d3441b684c69170226e891950c
Author: Michael Natterer <mitch gimp org>
Date: Tue May 28 22:46:22 2013 +0200
app: add mnemonic support for GimpSpinScale's label
app/widgets/gimpspinscale.c | 388 +++++++++++++++++++++++++++++++++++++++----
1 files changed, 355 insertions(+), 33 deletions(-)
---
diff --git a/app/widgets/gimpspinscale.c b/app/widgets/gimpspinscale.c
index 9adb717..f886794 100644
--- a/app/widgets/gimpspinscale.c
+++ b/app/widgets/gimpspinscale.c
@@ -22,6 +22,7 @@
#include "config.h"
#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
#include "libgimpwidgets/gimpwidgets.h"
#include "libgimpmath/gimpmath.h"
@@ -52,6 +53,12 @@ typedef struct _GimpSpinScalePrivate GimpSpinScalePrivate;
struct _GimpSpinScalePrivate
{
gchar *label;
+ gchar *label_text;
+ gchar *label_pattern;
+
+ GtkWindow *mnemonic_window;
+ guint mnemonic_keyval;
+ gboolean mnemonics_visible;
gboolean scale_limits_set;
gdouble scale_lower;
@@ -70,33 +77,46 @@ struct _GimpSpinScalePrivate
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_size_request (GtkWidget *widget,
- GtkRequisition *requisition);
-static void gimp_spin_scale_style_set (GtkWidget *widget,
- GtkStyle *prev_style);
-static gboolean gimp_spin_scale_expose (GtkWidget *widget,
- GdkEventExpose *event);
-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 void gimp_spin_scale_value_changed (GtkSpinButton *spin_button);
+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_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gimp_spin_scale_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static gboolean gimp_spin_scale_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+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 void gimp_spin_scale_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *old_toplevel);
+static void gimp_spin_scale_screen_changed (GtkWidget *widget,
+ GdkScreen *old_screen);
+
+static void gimp_spin_scale_value_changed (GtkSpinButton *spin_button);
+
+static void gimp_spin_scale_settings_notify (GtkSettings *settings,
+ const GParamSpec *pspec,
+ GimpSpinScale *scale);
+static void gimp_spin_scale_mnemonics_notify (GtkWindow *window,
+ const GParamSpec *pspec,
+ GimpSpinScale *scale);
+static void gimp_spin_scale_setup_mnemonic (GimpSpinScale *scale,
+ guint previous_keyval);
G_DEFINE_TYPE (GimpSpinScale, gimp_spin_scale, GTK_TYPE_SPIN_BUTTON);
@@ -123,6 +143,8 @@ gimp_spin_scale_class_init (GimpSpinScaleClass *klass)
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->hierarchy_changed = gimp_spin_scale_hierarchy_changed;
+ widget_class->screen_changed = gimp_spin_scale_screen_changed;
spin_button_class->value_changed = gimp_spin_scale_value_changed;
@@ -157,6 +179,12 @@ static void
gimp_spin_scale_dispose (GObject *object)
{
GimpSpinScalePrivate *private = GET_PRIVATE (object);
+ guint keyval;
+
+ keyval = private->mnemonic_keyval;
+ private->mnemonic_keyval = 0;
+
+ gimp_spin_scale_setup_mnemonic (GIMP_SPIN_SCALE (object), keyval);
if (private->layout)
{
@@ -178,6 +206,18 @@ gimp_spin_scale_finalize (GObject *object)
private->label = NULL;
}
+ if (private->label_text)
+ {
+ g_free (private->label_text);
+ private->label_text = NULL;
+ }
+
+ if (private->label_pattern)
+ {
+ g_free (private->label_pattern);
+ private->label_pattern = NULL;
+ }
+
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -273,6 +313,45 @@ gimp_spin_scale_style_set (GtkWidget *widget,
}
}
+static PangoAttrList *
+pattern_to_attrs (const gchar *text,
+ const gchar *pattern)
+{
+ PangoAttrList *attrs = pango_attr_list_new ();
+ const char *p = text;
+ const char *q = pattern;
+ const char *start;
+
+ while (TRUE)
+ {
+ while (*p && *q && *q != '_')
+ {
+ p = g_utf8_next_char (p);
+ q++;
+ }
+ start = p;
+ while (*p && *q && *q == '_')
+ {
+ p = g_utf8_next_char (p);
+ q++;
+ }
+
+ if (p > start)
+ {
+ PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
+
+ attr->start_index = start - text;
+ attr->end_index = p - text;
+
+ pango_attr_list_insert (attrs, attr);
+ }
+ else
+ break;
+ }
+
+ return attrs;
+}
+
static gboolean
gimp_spin_scale_expose (GtkWidget *widget,
GdkEventExpose *event)
@@ -356,8 +435,21 @@ gimp_spin_scale_expose (GtkWidget *widget,
if (! private->layout)
{
private->layout = gtk_widget_create_pango_layout (widget,
- private->label);
+ private->label_text);
pango_layout_set_ellipsize (private->layout, PANGO_ELLIPSIZE_END);
+
+ if (private->mnemonics_visible)
+ {
+ PangoAttrList *attrs;
+
+ attrs = pattern_to_attrs (private->label_text,
+ private->label_pattern);
+ if (attrs)
+ {
+ pango_layout_set_attributes (private->layout, attrs);
+ pango_attr_list_unref (attrs);
+ }
+ }
}
pango_layout_set_width (private->layout,
@@ -644,13 +736,61 @@ gimp_spin_scale_leave_notify (GtkWidget *widget,
}
static void
+gimp_spin_scale_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *old_toplevel)
+{
+ GimpSpinScalePrivate *private = GET_PRIVATE (widget);
+
+ gimp_spin_scale_setup_mnemonic (GIMP_SPIN_SCALE (widget),
+ private->mnemonic_keyval);
+}
+
+static void
+gimp_spin_scale_screen_changed (GtkWidget *widget,
+ GdkScreen *old_screen)
+{
+ GimpSpinScale *scale = GIMP_SPIN_SCALE (widget);
+ GimpSpinScalePrivate *private = GET_PRIVATE (scale);
+ GtkSettings *settings;
+
+ if (private->layout)
+ {
+ g_object_unref (private->layout);
+ private->layout = NULL;
+ }
+
+ if (old_screen)
+ {
+ settings = gtk_settings_get_for_screen (old_screen);
+
+ g_signal_handlers_disconnect_by_func (settings,
+ gimp_spin_scale_settings_notify,
+ scale);
+ }
+
+ if (! gtk_widget_has_screen (widget))
+ return;
+
+ settings = gtk_widget_get_settings (widget);
+
+ g_signal_connect (settings, "notify::gtk-enable-mnemonics",
+ G_CALLBACK (gimp_spin_scale_settings_notify),
+ scale);
+ g_signal_connect (settings, "notify::gtk-enable-accels",
+ G_CALLBACK (gimp_spin_scale_settings_notify),
+ scale);
+
+ gimp_spin_scale_settings_notify (settings, NULL, scale);
+}
+
+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;
+ GimpSpinScalePrivate *private = GET_PRIVATE (spin_button);
+ GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
+ gdouble lower;
+ gdouble upper;
+ gdouble value;
gimp_spin_scale_get_limits (GIMP_SPIN_SCALE (spin_button), &lower, &upper);
@@ -661,6 +801,91 @@ gimp_spin_scale_value_changed (GtkSpinButton *spin_button)
1.0 / private->gamma));
}
+static void
+gimp_spin_scale_settings_notify (GtkSettings *settings,
+ const GParamSpec *pspec,
+ GimpSpinScale *scale)
+{
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (scale));
+
+ if (GTK_IS_WINDOW (toplevel))
+ gimp_spin_scale_mnemonics_notify (GTK_WINDOW (toplevel), NULL, scale);
+}
+
+static void
+gimp_spin_scale_mnemonics_notify (GtkWindow *window,
+ const GParamSpec *pspec,
+ GimpSpinScale *scale)
+{
+ GimpSpinScalePrivate *private = GET_PRIVATE (scale);
+ gboolean mnemonics_visible = FALSE;
+ gboolean enable_mnemonics;
+ gboolean auto_mnemonics;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (scale)),
+ "gtk-enable-mnemonics", &enable_mnemonics,
+ "gtk-auto-mnemonics", &auto_mnemonics,
+ NULL);
+
+ if (enable_mnemonics &&
+ (! auto_mnemonics ||
+ gtk_widget_is_sensitive (GTK_WIDGET (scale))))
+ {
+ g_object_get (window,
+ "mnemonics-visible", &mnemonics_visible,
+ NULL);
+ }
+
+ if (private->mnemonics_visible != mnemonics_visible)
+ {
+ private->mnemonics_visible = mnemonics_visible;
+
+ if (private->layout)
+ {
+ g_object_unref (private->layout);
+ private->layout = NULL;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (scale));
+ }
+}
+
+static void
+gimp_spin_scale_setup_mnemonic (GimpSpinScale *scale,
+ guint previous_keyval)
+{
+ GimpSpinScalePrivate *private = GET_PRIVATE (scale);
+ GtkWidget *widget = GTK_WIDGET (scale);
+ GtkWidget *toplevel;
+
+ if (private->mnemonic_window)
+ {
+ g_signal_handlers_disconnect_by_func (private->mnemonic_window,
+ gimp_spin_scale_mnemonics_notify,
+ scale);
+
+ gtk_window_remove_mnemonic (private->mnemonic_window,
+ previous_keyval,
+ widget);
+ private->mnemonic_window = NULL;
+ }
+
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (gtk_widget_is_toplevel (toplevel) &&
+ private->mnemonic_keyval != GDK_KEY_VoidSymbol)
+ {
+ gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
+ private->mnemonic_keyval,
+ widget);
+ private->mnemonic_window = GTK_WINDOW (toplevel);
+
+ g_signal_connect (toplevel, "notify::mnemonics-visible",
+ G_CALLBACK (gimp_spin_scale_mnemonics_notify),
+ scale);
+ }
+}
+
/* public functions */
@@ -678,11 +903,90 @@ gimp_spin_scale_new (GtkAdjustment *adjustment,
NULL);
}
+static gboolean
+separate_uline_pattern (const gchar *str,
+ guint *accel_key,
+ gchar **new_str,
+ gchar **pattern)
+{
+ gboolean underscore;
+ const gchar *src;
+ gchar *dest;
+ gchar *pattern_dest;
+
+ *accel_key = GDK_KEY_VoidSymbol;
+ *new_str = g_new (gchar, strlen (str) + 1);
+ *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
+
+ underscore = FALSE;
+
+ src = str;
+ dest = *new_str;
+ pattern_dest = *pattern;
+
+ while (*src)
+ {
+ gunichar c;
+ const gchar *next_src;
+
+ c = g_utf8_get_char (src);
+ if (c == (gunichar)-1)
+ {
+ g_warning ("Invalid input string");
+ g_free (*new_str);
+ g_free (*pattern);
+
+ return FALSE;
+ }
+ next_src = g_utf8_next_char (src);
+
+ if (underscore)
+ {
+ if (c == '_')
+ *pattern_dest++ = ' ';
+ else
+ {
+ *pattern_dest++ = '_';
+ if (*accel_key == GDK_KEY_VoidSymbol)
+ *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
+ }
+
+ while (src < next_src)
+ *dest++ = *src++;
+
+ underscore = FALSE;
+ }
+ else
+ {
+ if (c == '_')
+ {
+ underscore = TRUE;
+ src = next_src;
+ }
+ else
+ {
+ while (src < next_src)
+ *dest++ = *src++;
+
+ *pattern_dest++ = ' ';
+ }
+ }
+ }
+
+ *dest = 0;
+ *pattern_dest = 0;
+
+ return TRUE;
+}
+
void
gimp_spin_scale_set_label (GimpSpinScale *scale,
const gchar *label)
{
GimpSpinScalePrivate *private;
+ guint accel_key = GDK_KEY_VoidSymbol;
+ gchar *text = NULL;
+ gchar *pattern = NULL;
g_return_if_fail (GIMP_IS_SPIN_SCALE (scale));
@@ -691,9 +995,27 @@ gimp_spin_scale_set_label (GimpSpinScale *scale,
if (label == private->label)
return;
+ if (label && ! separate_uline_pattern (label, &accel_key, &text, &pattern))
+ return;
+
g_free (private->label);
private->label = g_strdup (label);
+ g_free (private->label_text);
+ private->label_text = g_strdup (text);
+
+ g_free (private->label_pattern);
+ private->label_pattern = g_strdup (pattern);
+
+ if (private->mnemonic_keyval != accel_key)
+ {
+ guint previous = private->mnemonic_keyval;
+
+ private->mnemonic_keyval = accel_key;
+
+ gimp_spin_scale_setup_mnemonic (scale, previous);
+ }
+
if (private->layout)
{
g_object_unref (private->layout);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]