app: add mnemonic support for GimpSpinScale's label

This commit is contained in:
Michael Natterer 2013-05-28 22:46:22 +02:00
parent d5fb5922b7
commit 4c0169aaaf
1 changed files with 353 additions and 31 deletions

View File

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libgimpwidgets/gimpwidgets.h" #include "libgimpwidgets/gimpwidgets.h"
#include "libgimpmath/gimpmath.h" #include "libgimpmath/gimpmath.h"
@ -52,6 +53,12 @@ typedef struct _GimpSpinScalePrivate GimpSpinScalePrivate;
struct _GimpSpinScalePrivate struct _GimpSpinScalePrivate
{ {
gchar *label; gchar *label;
gchar *label_text;
gchar *label_pattern;
GtkWindow *mnemonic_window;
guint mnemonic_keyval;
gboolean mnemonics_visible;
gboolean scale_limits_set; gboolean scale_limits_set;
gdouble scale_lower; gdouble scale_lower;
@ -95,9 +102,22 @@ static gboolean gimp_spin_scale_motion_notify (GtkWidget *widget,
GdkEventMotion *event); GdkEventMotion *event);
static gboolean gimp_spin_scale_leave_notify (GtkWidget *widget, static gboolean gimp_spin_scale_leave_notify (GtkWidget *widget,
GdkEventCrossing *event); 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_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); 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->button_release_event = gimp_spin_scale_button_release;
widget_class->motion_notify_event = gimp_spin_scale_motion_notify; widget_class->motion_notify_event = gimp_spin_scale_motion_notify;
widget_class->leave_notify_event = gimp_spin_scale_leave_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; spin_button_class->value_changed = gimp_spin_scale_value_changed;
@ -157,6 +179,12 @@ static void
gimp_spin_scale_dispose (GObject *object) gimp_spin_scale_dispose (GObject *object)
{ {
GimpSpinScalePrivate *private = GET_PRIVATE (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) if (private->layout)
{ {
@ -178,6 +206,18 @@ gimp_spin_scale_finalize (GObject *object)
private->label = NULL; 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); 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 static gboolean
gimp_spin_scale_expose (GtkWidget *widget, gimp_spin_scale_expose (GtkWidget *widget,
GdkEventExpose *event) GdkEventExpose *event)
@ -356,8 +435,21 @@ gimp_spin_scale_expose (GtkWidget *widget,
if (! private->layout) if (! private->layout)
{ {
private->layout = gtk_widget_create_pango_layout (widget, private->layout = gtk_widget_create_pango_layout (widget,
private->label); private->label_text);
pango_layout_set_ellipsize (private->layout, PANGO_ELLIPSIZE_END); 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, pango_layout_set_width (private->layout,
@ -643,11 +735,59 @@ gimp_spin_scale_leave_notify (GtkWidget *widget,
return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event); return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event);
} }
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 static void
gimp_spin_scale_value_changed (GtkSpinButton *spin_button) gimp_spin_scale_value_changed (GtkSpinButton *spin_button)
{ {
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
GimpSpinScalePrivate *private = GET_PRIVATE (spin_button); GimpSpinScalePrivate *private = GET_PRIVATE (spin_button);
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble lower; gdouble lower;
gdouble upper; gdouble upper;
gdouble value; gdouble value;
@ -661,6 +801,91 @@ gimp_spin_scale_value_changed (GtkSpinButton *spin_button)
1.0 / private->gamma)); 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 */ /* public functions */
@ -678,11 +903,90 @@ gimp_spin_scale_new (GtkAdjustment *adjustment,
NULL); 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 void
gimp_spin_scale_set_label (GimpSpinScale *scale, gimp_spin_scale_set_label (GimpSpinScale *scale,
const gchar *label) const gchar *label)
{ {
GimpSpinScalePrivate *private; GimpSpinScalePrivate *private;
guint accel_key = GDK_KEY_VoidSymbol;
gchar *text = NULL;
gchar *pattern = NULL;
g_return_if_fail (GIMP_IS_SPIN_SCALE (scale)); g_return_if_fail (GIMP_IS_SPIN_SCALE (scale));
@ -691,9 +995,27 @@ gimp_spin_scale_set_label (GimpSpinScale *scale,
if (label == private->label) if (label == private->label)
return; return;
if (label && ! separate_uline_pattern (label, &accel_key, &text, &pattern))
return;
g_free (private->label); g_free (private->label);
private->label = g_strdup (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) if (private->layout)
{ {
g_object_unref (private->layout); g_object_unref (private->layout);