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