[gimp] libgimp: mnemonic duplicate verification in GimpProcedureDialog.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] libgimp: mnemonic duplicate verification in GimpProcedureDialog.
- Date: Fri, 20 Nov 2020 08:39:54 +0000 (UTC)
commit c58b7ad80bcfbc6ed14dfc167ce9bd3fe65931f3
Author: Jehan <jehan girinstud io>
Date: Fri Nov 20 01:29:08 2020 +0100
libgimp: mnemonic duplicate verification in GimpProcedureDialog.
A very common issue we have with dialog creation is good mnemonics. In
particular, we want to:
* Keep consistent mnemonics for common features (basically the core
buttons) common to all plug-in dialogs.
* Have mnemonics for all options.
* Avoid duplicate mnemonics if possible.
Mnemonics are a usability/accessibility feature which can be important
for people using the keyboard a lot (not necessarily only because they
prefer keyboard, but also possibly because of various disorders).
This code will check at runtime that there are no missing or duplicate
mnemonics and simply print to stderr. We don't want to bother overly the
users about these, but we want developers and translators to be aware
about these so that they can easily spot and fix them.
libgimp/gimpproceduredialog.c | 103 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 96 insertions(+), 7 deletions(-)
---
diff --git a/libgimp/gimpproceduredialog.c b/libgimp/gimpproceduredialog.c
index 19f04c9dca..c91f4a139e 100644
--- a/libgimp/gimpproceduredialog.c
+++ b/libgimp/gimpproceduredialog.c
@@ -54,6 +54,8 @@ struct _GimpProcedureDialogPrivate
GtkWidget *reset_popover;
GHashTable *widgets;
+ GHashTable *mnemonics;
+ GHashTable *core_mnemonics;
GtkSizeGroup *label_group;
};
@@ -82,6 +84,10 @@ static void gimp_procedure_dialog_estimate_increments (gdouble lower,
gdouble *step,
gdouble *page);
+static gboolean gimp_procedure_dialog_check_mnemonic (GimpProcedureDialog *dialog,
+ GtkWidget *widget,
+ const gchar *id,
+ const gchar *core_id);
G_DEFINE_TYPE_WITH_PRIVATE (GimpProcedureDialog, gimp_procedure_dialog,
GIMP_TYPE_DIALOG)
@@ -124,6 +130,8 @@ gimp_procedure_dialog_init (GimpProcedureDialog *dialog)
dialog->priv = gimp_procedure_dialog_get_instance_private (dialog);
dialog->priv->widgets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ dialog->priv->mnemonics = g_hash_table_new_full (g_direct_hash, NULL, NULL, g_free);
+ dialog->priv->core_mnemonics = g_hash_table_new_full (g_direct_hash, NULL, NULL, g_free);
dialog->priv->label_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
}
@@ -137,7 +145,10 @@ gimp_procedure_dialog_dispose (GObject *object)
g_clear_object (&dialog->priv->initial_config);
g_clear_pointer (&dialog->priv->reset_popover, gtk_widget_destroy);
- g_clear_pointer (&dialog->priv->widgets, g_hash_table_unref);
+
+ g_clear_pointer (&dialog->priv->widgets, g_hash_table_destroy);
+ g_clear_pointer (&dialog->priv->mnemonics, g_hash_table_destroy);
+ g_clear_pointer (&dialog->priv->core_mnemonics, g_hash_table_destroy);
g_clear_object (&dialog->priv->label_group);
@@ -243,11 +254,15 @@ gimp_procedure_dialog_new (GimpProcedure *procedure,
else
ok_label = _("_OK");
- gimp_dialog_add_buttons (GIMP_DIALOG (dialog),
- _("_Reset"), RESPONSE_RESET,
- _("_Cancel"), GTK_RESPONSE_CANCEL,
- ok_label, GTK_RESPONSE_OK,
- NULL);
+ button = gimp_dialog_add_button (GIMP_DIALOG (dialog),
+ _("_Reset"), RESPONSE_RESET);
+ gimp_procedure_dialog_check_mnemonic (GIMP_PROCEDURE_DIALOG (dialog), button, NULL, "reset");
+ button = gimp_dialog_add_button (GIMP_DIALOG (dialog),
+ _("_Cancel"), GTK_RESPONSE_CANCEL);
+ gimp_procedure_dialog_check_mnemonic (GIMP_PROCEDURE_DIALOG (dialog), button, NULL, "cancel");
+ button = gimp_dialog_add_button (GIMP_DIALOG (dialog),
+ ok_label, GTK_RESPONSE_OK);
+ gimp_procedure_dialog_check_mnemonic (GIMP_PROCEDURE_DIALOG (dialog), button, NULL, "ok");
gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
@@ -272,6 +287,7 @@ gimp_procedure_dialog_new (GimpProcedure *procedure,
gtk_widget_show (hbox);
button = gtk_button_new_with_mnemonic (_("_Load Defaults"));
+ gimp_procedure_dialog_check_mnemonic (GIMP_PROCEDURE_DIALOG (dialog), button, NULL, "load-defaults");
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
@@ -280,6 +296,7 @@ gimp_procedure_dialog_new (GimpProcedure *procedure,
dialog);
button = gtk_button_new_with_mnemonic (_("_Save Defaults"));
+ gimp_procedure_dialog_check_mnemonic (GIMP_PROCEDURE_DIALOG (dialog), button, NULL, "save-defaults");
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
@@ -422,7 +439,7 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog,
if (! widget)
{
g_warning ("%s: widget type %s not supported for parameter '%s' of type %s",
- G_STRFUNC, G_OBJECT_TYPE_NAME (widget_type),
+ G_STRFUNC, g_type_name (widget_type),
property, G_PARAM_SPEC_TYPE_NAME (pspec));
return NULL;
}
@@ -434,6 +451,7 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog,
gtk_size_group_add_widget (dialog->priv->label_group, label);
}
+ gimp_procedure_dialog_check_mnemonic (dialog, widget, property, NULL);
g_hash_table_insert (dialog->priv->widgets, g_strdup (property), widget);
return widget;
@@ -508,6 +526,7 @@ gimp_procedure_dialog_get_int_combo (GimpProcedureDialog *dialog,
gtk_size_group_add_widget (dialog->priv->label_group, label);
}
+ gimp_procedure_dialog_check_mnemonic (dialog, widget, property, NULL);
g_hash_table_insert (dialog->priv->widgets, g_strdup (property), widget);
return widget;
@@ -1160,3 +1179,73 @@ gimp_procedure_dialog_estimate_increments (gdouble lower,
*page = 10.0;
}
}
+
+static gboolean
+gimp_procedure_dialog_check_mnemonic (GimpProcedureDialog *dialog,
+ GtkWidget *widget,
+ const gchar *id,
+ const gchar *core_id)
+{
+ GtkWidget *label = NULL;
+ gchar *duplicate;
+ gboolean success = TRUE;
+ guint mnemonic = GDK_KEY_VoidSymbol;
+
+ g_return_val_if_fail ((id && ! core_id) || (core_id && ! id), FALSE);
+
+ if (GIMP_IS_LABELED (widget))
+ {
+ label = gimp_labeled_get_label (GIMP_LABELED (widget));
+ }
+ else if (g_type_is_a (G_OBJECT_TYPE (widget), GTK_TYPE_LABEL))
+ {
+ label = widget;
+ }
+ else if (g_type_is_a (G_OBJECT_TYPE (widget), GTK_TYPE_BUTTON))
+ {
+ label = gtk_bin_get_child (GTK_BIN (widget));
+ if (! label || ! g_type_is_a (G_OBJECT_TYPE (label), GTK_TYPE_LABEL))
+ label = NULL;
+ }
+
+ if (label &&
+ (mnemonic = gtk_label_get_mnemonic_keyval (GTK_LABEL (label))) &&
+ mnemonic != GDK_KEY_VoidSymbol)
+ {
+ duplicate = g_hash_table_lookup (dialog->priv->core_mnemonics, GINT_TO_POINTER (mnemonic));
+ if (duplicate && g_strcmp0 (duplicate, id ? id : core_id) != 0)
+ {
+ g_printerr ("Procedure '%s': duplicate mnemonic %s for label of property %s and dialog button
%s\n",
+ gimp_procedure_get_name (dialog->priv->procedure),
+ gdk_keyval_name (mnemonic), id, duplicate);
+ success = FALSE;
+ }
+
+ if (success)
+ {
+ duplicate = g_hash_table_lookup (dialog->priv->mnemonics, GINT_TO_POINTER (mnemonic));
+ if (duplicate && g_strcmp0 (duplicate, id ? id : core_id) != 0)
+ {
+ g_printerr ("Procedure '%s': duplicate mnemonic %s for label of properties %s and %s\n",
+ gimp_procedure_get_name (dialog->priv->procedure),
+ gdk_keyval_name (mnemonic), id, duplicate);
+ success = FALSE;
+ }
+ else if (! duplicate)
+ {
+ if (id)
+ g_hash_table_insert (dialog->priv->mnemonics, GINT_TO_POINTER (mnemonic), g_strdup (id));
+ else
+ g_hash_table_insert (dialog->priv->core_mnemonics, GINT_TO_POINTER (mnemonic), g_strdup
(core_id));
+ }
+ }
+ }
+ else
+ {
+ g_printerr ("Procedure '%s': no mnemonic for property %s\n",
+ gimp_procedure_get_name (dialog->priv->procedure), id);
+ success = FALSE;
+ }
+
+ return success;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]