From 560a131fd676a3d5926ebbf12e4aa7a91ad38e90 Mon Sep 17 00:00:00 2001
From: Jasper van de Gronde
Date: Mon, 6 Sep 2010 19:32:05 +0200
Subject: [PATCH] Automatically add shortcuts to tooltips
---
src/interface.cpp | 90 +++++------------------------------------
src/interface.h | 2 -
src/shortcuts.cpp | 28 ++++++++++++-
src/shortcuts.h | 4 +-
src/verbs.cpp | 20 ++++++++-
src/verbs.h | 4 +-
src/widgets/button.cpp | 8 ++--
src/widgets/toolbox.cpp | 6 +--
8 files changed, 67 insertions(+), 95 deletions(-)
diff --git a/src/interface.cpp b/src/interface.cpp
index 6dc29288f..d66a14dbf 100644
--- a/src/interface.cpp
+++ b/src/interface.cpp
@@ -456,85 +456,12 @@ sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
} // end of sp_ui_menu_append_item()
-/**
-\brief a wrapper around gdk_keyval_name producing (when possible) characters, not names
- */
-static gchar const *
-sp_key_name(guint keyval)
-{
- /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
- simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
- gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
-
- if (!strcmp(n, "asciicircum")) return "^";
- else if (!strcmp(n, "parenleft" )) return "(";
- else if (!strcmp(n, "parenright" )) return ")";
- else if (!strcmp(n, "plus" )) return "+";
- else if (!strcmp(n, "minus" )) return "-";
- else if (!strcmp(n, "asterisk" )) return "*";
- else if (!strcmp(n, "KP_Multiply")) return "*";
- else if (!strcmp(n, "Delete" )) return "Del";
- else if (!strcmp(n, "Page_Up" )) return "PgUp";
- else if (!strcmp(n, "Page_Down" )) return "PgDn";
- else if (!strcmp(n, "grave" )) return "`";
- else if (!strcmp(n, "numbersign" )) return "#";
- else if (!strcmp(n, "bar" )) return "|";
- else if (!strcmp(n, "slash" )) return "/";
- else if (!strcmp(n, "exclam" )) return "!";
- else if (!strcmp(n, "percent" )) return "%";
- else return n;
-}
-
-
-/**
- * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
- * \param c Points to a buffer at least 256 bytes long.
- */
-void
-sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
-{
- /* TODO: This function shouldn't exist. Our callers should use GtkAccelLabel instead of
- * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
- * Will probably need to change sp_shortcut_invoke callers.
- *
- * The existing gtk_label_new_with_mnemonic call can be replaced with
- * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
- * gtk_label_set_text_with_mnemonic(lbl, str).
- */
- static GtkAccelLabelClass const &accel_lbl_cls
- = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
-
- struct { unsigned test; char const *name; } const modifier_tbl[] = {
- { SP_SHORTCUT_SHIFT_MASK, accel_lbl_cls.mod_name_shift },
- { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
- { SP_SHORTCUT_ALT_MASK, accel_lbl_cls.mod_name_alt }
- };
-
- gchar *p = c;
- gchar *end = p + 256;
-
- for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
- if ((shortcut & modifier_tbl[i].test)
- && (p < end))
- {
- p += g_snprintf(p, end - p, "%s%s",
- modifier_tbl[i].name,
- accel_lbl_cls.mod_separator);
- }
- }
- if (p < end) {
- p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
- }
- end[-1] = '\0'; // snprintf doesn't guarantee to nul-terminate the string.
-}
-
void
sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
{
SPAction *action;
unsigned int shortcut;
gchar *s;
- gchar key[256];
gchar *atitle;
action = verb->get_action(NULL);
@@ -548,11 +475,12 @@ sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
g_free(atitle);
shortcut = sp_shortcut_get_primary(verb);
- if (shortcut) {
+ if (shortcut!=GDK_VoidSymbol) {
+ gchar* key = sp_shortcut_get_label(shortcut);
s = g_stpcpy(s, " (");
- sp_ui_shortcut_string(shortcut, key);
s = g_stpcpy(s, key);
s = g_stpcpy(s, ")");
+ g_free(key);
}
}
@@ -582,9 +510,8 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::
if (!action) return NULL;
shortcut = sp_shortcut_get_primary(verb);
- if (shortcut) {
- gchar c[256];
- sp_ui_shortcut_string(shortcut, c);
+ if (shortcut!=GDK_VoidSymbol) {
+ gchar* c = sp_shortcut_get_label(shortcut);
GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
GtkWidget *const name_lbl = gtk_label_new("");
gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
@@ -600,6 +527,7 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::
item = gtk_image_menu_item_new();
}
gtk_container_add((GtkContainer *) item, hb);
+ g_free(c);
} else {
if (radio) {
item = gtk_radio_menu_item_new (group);
@@ -754,9 +682,8 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *
SPAction *action = (verb) ? verb->get_action(view) : 0;
GtkWidget *item = gtk_check_menu_item_new();
- if (verb && shortcut) {
- gchar c[256];
- sp_ui_shortcut_string(shortcut, c);
+ if (verb && shortcut!=GDK_VoidSymbol) {
+ gchar* c = sp_shortcut_get_label(shortcut);
GtkWidget *hb = gtk_hbox_new(FALSE, 16);
@@ -775,6 +702,7 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *
gtk_widget_show_all(hb);
gtk_container_add((GtkContainer *) item, hb);
+ g_free(c);
} else {
GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
diff --git a/src/interface.h b/src/interface.h
index 099fdd277..3900350bd 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -69,8 +69,6 @@ void sp_ui_dialog_title_string (Inkscape::Verb * verb, gchar* c);
void sp_ui_error_dialog (const gchar * message);
bool sp_ui_overwrite_file (const gchar * filename);
-void sp_ui_shortcut_string (unsigned int shortcut, gchar* c);
-
#endif
/*
diff --git a/src/shortcuts.cpp b/src/shortcuts.cpp
index 7d0f3747d..d02ef6e48 100644
--- a/src/shortcuts.cpp
+++ b/src/shortcuts.cpp
@@ -28,6 +28,7 @@
#include
#include
+#include
#include "helper/action.h"
#include "io/sys.h"
@@ -202,10 +203,33 @@ unsigned int
sp_shortcut_get_primary(Inkscape::Verb *verb)
{
if (!primary_shortcuts) sp_shortcut_init();
- return (unsigned int)GPOINTER_TO_INT(g_hash_table_lookup(primary_shortcuts,
- (gpointer)(verb)));
+ gpointer value;
+ if (g_hash_table_lookup_extended(primary_shortcuts, (gpointer)(verb), NULL, &value)) {
+ return (unsigned int)GPOINTER_TO_INT(value);
+ } else {
+ return GDK_VoidSymbol;
+ }
}
+gchar* sp_shortcut_get_label (unsigned int shortcut)
+{
+ // The comment below was copied from the function sp_ui_shortcut_string in interface.cpp (which was subsequently removed)
+ /* TODO: This function shouldn't exist. Our callers should use GtkAccelLabel instead of
+ * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
+ * Will probably need to change sp_shortcut_invoke callers.
+ *
+ * The existing gtk_label_new_with_mnemonic call can be replaced with
+ * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
+ * gtk_label_set_text_with_mnemonic(lbl, str).
+ */
+ if (shortcut==GDK_VoidSymbol) return 0;
+ return gtk_accelerator_get_label(
+ shortcut&(~SP_SHORTCUT_MODIFIER_MASK),static_cast(
+ ((shortcut&SP_SHORTCUT_SHIFT_MASK)?GDK_SHIFT_MASK:0) |
+ ((shortcut&SP_SHORTCUT_CONTROL_MASK)?GDK_CONTROL_MASK:0) |
+ ((shortcut&SP_SHORTCUT_ALT_MASK)?GDK_MOD1_MASK:0)
+ ));
+}
/*
Local Variables:
diff --git a/src/shortcuts.h b/src/shortcuts.h
index 5119851c9..3fa092713 100644
--- a/src/shortcuts.h
+++ b/src/shortcuts.h
@@ -24,12 +24,14 @@ namespace Inkscape {
#define SP_SHORTCUT_SHIFT_MASK (1 << 24)
#define SP_SHORTCUT_CONTROL_MASK (1 << 25)
#define SP_SHORTCUT_ALT_MASK (1 << 26)
+#define SP_SHORTCUT_MODIFIER_MASK (SP_SHORTCUT_SHIFT_MASK|SP_SHORTCUT_CONTROL_MASK|SP_SHORTCUT_ALT_MASK)
/* Returns true if action was performed */
bool sp_shortcut_invoke (unsigned int shortcut, Inkscape::UI::View::View *view);
Inkscape::Verb * sp_shortcut_get_verb (unsigned int shortcut);
-unsigned int sp_shortcut_get_primary (Inkscape::Verb * verb);
+unsigned int sp_shortcut_get_primary (Inkscape::Verb * verb); // Returns GDK_VoidSymbol if no shortcut is found.
+char* sp_shortcut_get_label (unsigned int shortcut); // Returns the human readable form of the shortcut (or NULL), for example Shift+Ctrl+F. Free the returned string with g_free.
#endif
diff --git a/src/verbs.cpp b/src/verbs.cpp
index c2af399c5..6ba0aa562 100644
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
@@ -63,6 +63,7 @@
#include "selection-chemistry.h"
#include "seltrans.h"
#include "shape-editor.h"
+#include "shortcuts.h"
#include "sp-flowtext.h"
#include "sp-guide.h"
#include "splivarot.h"
@@ -334,7 +335,7 @@ Verb::VerbIDTable Verb::_verb_ids;
in the \c _verbs hashtable which is indexed by the \c code.
*/
Verb::Verb(gchar const *id, gchar const *name, gchar const *tip, gchar const *image) :
- _actions(NULL), _id(id), _name(name), _tip(tip), _image(image)
+ _actions(NULL), _id(id), _name(name), _tip(tip), _full_tip(0), _image(image)
{
static int count = SP_VERB_LAST;
@@ -358,6 +359,8 @@ Verb::~Verb(void)
delete _actions;
}
+ if (_full_tip) g_free(_full_tip);
+
return;
}
@@ -631,7 +634,20 @@ Verb::sensitive(SPDocument *in_doc, bool in_sensitive)
gchar const *
Verb::get_tip (void)
{
- return _(_tip);
+ if (!_tip) return 0;
+ unsigned int shortcut = sp_shortcut_get_primary(this);
+ if (shortcut!=_shortcut || !_full_tip) {
+ if (_full_tip) g_free(_full_tip);
+ _shortcut = shortcut;
+ gchar* shortcutString = sp_shortcut_get_label(shortcut);
+ if (shortcutString) {
+ _full_tip = g_strdup_printf("%s (%s)", _(_tip), shortcutString);
+ g_free(shortcutString);
+ } else {
+ _full_tip = g_strdup(_(_tip));
+ }
+ }
+ return _full_tip;
}
void
diff --git a/src/verbs.h b/src/verbs.h
index 7c04ba3f6..16d4d795a 100644
--- a/src/verbs.h
+++ b/src/verbs.h
@@ -339,6 +339,8 @@ private:
gchar const * _name;
/** \brief Tooltip for the verb. */
gchar const * _tip;
+ gchar * _full_tip; // includes shortcut
+ unsigned int _shortcut;
/** \brief Name of the image that represents the verb. */
gchar const * _image;
/** \brief Unique numerical representation of the verb. In most cases
@@ -409,7 +411,7 @@ public:
gchar const * name,
gchar const * tip,
gchar const * image) :
- _actions(NULL), _id(id), _name(name), _tip(tip), _image(image), _code(code), _default_sensitive(true) {
+ _actions(NULL), _id(id), _name(name), _tip(tip), _full_tip(0), _image(image), _code(code), _default_sensitive(true) {
_verbs.insert(VerbTable::value_type(_code, this));
_verb_ids.insert(VerbIDTable::value_type(_id, this));
}
diff --git a/src/widgets/button.cpp b/src/widgets/button.cpp
index 6769fa389..aa32a9d91 100644
--- a/src/widgets/button.cpp
+++ b/src/widgets/button.cpp
@@ -27,6 +27,8 @@
#include "shortcuts.h"
#include "interface.h"
+#include
+
#include "icon.h"
#include "button.h"
@@ -291,15 +293,15 @@ sp_button_set_composed_tooltip (GtkTooltips *tooltips, GtkWidget *widget, SPActi
{
if (action) {
unsigned int shortcut = sp_shortcut_get_primary (action->verb);
- if (shortcut) {
+ if (shortcut!=GDK_VoidSymbol) {
// there's both action and shortcut
- gchar key[256];
- sp_ui_shortcut_string (shortcut, key);
+ gchar* key = sp_shortcut_get_label(shortcut);
gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
gtk_tooltips_set_tip (tooltips, widget, tip, NULL);
g_free (tip);
+ g_free (key);
} else {
// action has no shortcut
diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp
index bdf1e6ff2..f6a6735e9 100644
--- a/src/widgets/toolbox.cpp
+++ b/src/widgets/toolbox.cpp
@@ -827,14 +827,14 @@ GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inksc
unsigned int shortcut = sp_shortcut_get_primary(verb);
- if (shortcut) {
- gchar key[256];
- sp_ui_shortcut_string(shortcut, key);
+ if (shortcut!=GDK_VoidSymbol) {
+ gchar* key = sp_shortcut_get_label(shortcut);
gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
if ( t ) {
gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
}
g_free(tip);
+ g_free(key);
} else {
if ( t ) {
gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
--
2.30.2