summaryrefslogtreecommitdiffstats
path: root/src/shortcuts.cpp
diff options
context:
space:
mode:
authorJohn Smith <john.smith7545@yahoo.com>2012-11-25 04:53:29 +0000
committerJohn Smith <john.smith7545@yahoo.com>2012-11-25 04:53:29 +0000
commit60141f6bdf22a0efad3baff5a944d15a2b28f728 (patch)
tree229adab307e65e6adcc94a235b2e44ca14bea225 /src/shortcuts.cpp
parentFix for 172236 : Dropper pixmaps and onetime fix (diff)
downloadinkscape-60141f6bdf22a0efad3baff5a944d15a2b28f728.tar.gz
inkscape-60141f6bdf22a0efad3baff5a944d15a2b28f728.zip
Fix for 1036059 : Keyboard shortcut editor
(bzr r11895)
Diffstat (limited to 'src/shortcuts.cpp')
-rw-r--r--src/shortcuts.cpp524
1 files changed, 510 insertions, 14 deletions
diff --git a/src/shortcuts.cpp b/src/shortcuts.cpp
index 796962b11..9ececbb3b 100644
--- a/src/shortcuts.cpp
+++ b/src/shortcuts.cpp
@@ -28,6 +28,7 @@
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
+#include <glibmm/i18n.h>
#include "helper/action.h"
#include "io/sys.h"
@@ -36,12 +37,24 @@
#include "verbs.h"
#include "xml/node-iterators.h"
#include "xml/repr.h"
+#include "document.h"
+#include "preferences.h"
+#include "event-context.h"
+#include "inkscape.h"
+#include "desktop.h"
+#include "path-prefix.h"
+#include "ui/dialog/filedialog.h"
using namespace Inkscape;
-static void sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary);
+using Inkscape::IO::Resource::get_path;
+using Inkscape::IO::Resource::SYSTEM;
+using Inkscape::IO::Resource::USER;
+using Inkscape::IO::Resource::KEYS;
+
+
static void try_shortcuts_file(char const *filename);
-static void read_shortcuts_file(char const *filename);
+static void read_shortcuts_file(char const *filename, bool const is_user_set=false);
/* Returns true if action was performed */
@@ -61,19 +74,22 @@ sp_shortcut_invoke(unsigned int shortcut, Inkscape::UI::View::View *view)
static std::map<unsigned int, Inkscape::Verb * > *verbs = NULL;
static std::map<Inkscape::Verb *, unsigned int> *primary_shortcuts = NULL;
+static std::map<Inkscape::Verb *, unsigned int> *user_shortcuts = NULL;
-static void
-sp_shortcut_init()
+void sp_shortcut_init()
{
- using Inkscape::IO::Resource::get_path;
- using Inkscape::IO::Resource::SYSTEM;
- using Inkscape::IO::Resource::USER;
- using Inkscape::IO::Resource::KEYS;
verbs = new std::map<unsigned int, Inkscape::Verb * >();
primary_shortcuts = new std::map<Inkscape::Verb *, unsigned int>();
+ user_shortcuts = new std::map<Inkscape::Verb *, unsigned int>();
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring shortcutfile = prefs->getString("/options/kbshortcuts/shortcutfile");
+ if (shortcutfile.empty()) {
+ shortcutfile = Glib::ustring(get_path(SYSTEM, KEYS, "default.xml"));
+ }
- read_shortcuts_file(get_path(SYSTEM, KEYS, "default.xml"));
+ read_shortcuts_file(shortcutfile.c_str());
try_shortcuts_file(get_path(USER, KEYS, "default.xml"));
}
@@ -82,11 +98,451 @@ static void try_shortcuts_file(char const *filename) {
/* ah, if only we had an exception to catch... (permission, forgiveness) */
if (file_test(filename, G_FILE_TEST_EXISTS)) {
- read_shortcuts_file(filename);
+ read_shortcuts_file(filename, true);
}
}
-static void read_shortcuts_file(char const *filename) {
+/*
+ * Inkscape expects to add the Shift modifier to any accel_keys create with Shift
+ * For exmaple on en_US keyboard <Shift>+6 = "&" - in this case return <Shift>+&
+ * See get_group0_keyval() for explanation on why
+ */
+unsigned int sp_gdkmodifier_to_shortcut(guint accel_key, Gdk::ModifierType gdkmodifier, guint hardware_keycode) {
+
+
+ unsigned int shortcut = 0;
+ GdkEventKey event;
+ event.state = gdkmodifier;
+ event.keyval = accel_key;
+ event.hardware_keycode = hardware_keycode;
+ guint keyval = get_group0_keyval (&event);
+
+ shortcut = accel_key |
+ ( (gdkmodifier & GDK_SHIFT_MASK) || ( accel_key != keyval) ?
+ SP_SHORTCUT_SHIFT_MASK : 0 ) |
+ ( gdkmodifier & GDK_CONTROL_MASK ?
+ SP_SHORTCUT_CONTROL_MASK : 0 ) |
+ ( gdkmodifier & GDK_MOD1_MASK ?
+ SP_SHORTCUT_ALT_MASK : 0 );
+
+ return shortcut;
+}
+
+Glib::ustring sp_shortcut_to_label(unsigned int const shortcut) {
+
+ Glib::ustring modifiers = "";
+
+ if (shortcut & SP_SHORTCUT_CONTROL_MASK)
+ modifiers += "Ctrl,";
+ if (shortcut & SP_SHORTCUT_SHIFT_MASK)
+ modifiers += "Shift,";
+ if (shortcut & SP_SHORTCUT_ALT_MASK)
+ modifiers += "Alt,";
+
+ if(modifiers.length() > 0 &&
+ modifiers.find(',',modifiers.length()-1)!=modifiers.npos) {
+ modifiers.erase(modifiers.length()-1, 1);
+ }
+
+ return modifiers;
+}
+
+/*
+ * REmove all shortucts from the users file
+ */
+
+void sp_shortcuts_delete_all_from_file() {
+
+
+ char const *filename = get_path(USER, KEYS, "default.xml");
+
+ XML::Document *doc=sp_repr_read_file(filename, NULL);
+ if (!doc) {
+ g_warning("Unable to read keys file %s", filename);
+ return;
+ }
+
+ XML::Node *root=doc->root();
+ g_return_if_fail(!strcmp(root->name(), "keys"));
+
+ XML::Node *iter=root->firstChild();
+ while (iter) {
+
+ if (strcmp(iter->name(), "bind")) {
+ // some unknown element, do not complain
+ iter = iter->next();
+ continue;
+ }
+
+ // Delete node
+ sp_repr_unparent(iter);
+ iter=root->firstChild();
+ }
+
+
+ sp_repr_save_file(doc, filename, NULL);
+
+ GC::release(doc);
+}
+
+Inkscape::XML::Document *sp_shortcut_create_template_file(char const *filename) {
+
+ gchar const *buffer =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> "
+ "<keys name=\"My custom shortcuts\">"
+ "</keys>";
+
+ Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), NULL);
+ sp_repr_save_file(doc, filename, NULL);
+
+ return sp_repr_read_file(filename, NULL);
+}
+
+/*
+ * Get a list of keyboard shortcut files names and paths from the system and users paths
+ * Dont add the users custom keyboards file
+ */
+void sp_shortcut_get_file_names(std::vector<Glib::ustring> *names, std::vector<Glib::ustring> *paths) {
+
+ std::list<gchar *> sources;
+ sources.push_back( profile_path("keys") );
+ sources.push_back( g_strdup(INKSCAPE_KEYSDIR) );
+
+ // loop through possible keyboard shortcut file locations.
+ while (!sources.empty()) {
+ gchar *dirname = sources.front();
+ if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
+ && Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
+ GError *err = 0;
+ GDir *directory = g_dir_open(dirname, 0, &err);
+ if (!directory) {
+ gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
+ g_warning(_("Keyboard directory (%s) is unavailable."), safeDir);
+ g_free(safeDir);
+ } else {
+ gchar *filename = 0;
+ while ((filename = (gchar *) g_dir_read_name(directory)) != NULL) {
+ gchar* lower = g_ascii_strdown(filename, -1);
+ if (!strcmp(dirname, profile_path("keys")) &&
+ !strcmp(lower, "default.xml")) {
+ // Dont add the users custom keys file
+ continue;
+ }
+ if (!strcmp(dirname, INKSCAPE_KEYSDIR) &&
+ !strcmp(lower, "inkscape.xml")) {
+ // Dont add system inkscape.xml (since its a duplicate? of dfefault.xml)
+ continue;
+ }
+ if (g_str_has_suffix(lower, ".xml")) {
+ gchar* full = g_build_filename(dirname, filename, NULL);
+ if (!Inkscape::IO::file_test(full, G_FILE_TEST_IS_DIR)) {
+
+ // Get the "key name" from the root element of each file
+ XML::Document *doc=sp_repr_read_file(full, NULL);
+ if (!doc) {
+ g_warning("Unable to read keyboard shortcut file %s", full);
+ continue;
+ }
+ XML::Node *root=doc->root();
+ if (strcmp(root->name(), "keys")) {
+ g_warning("Not a shortcut keys file %s", full);
+ Inkscape::GC::release(doc);
+ continue;
+ }
+
+ gchar const *name=root->attribute("name");
+ Glib::ustring label(filename);
+ if (name) {
+ label = Glib::ustring(name)+ " (" + filename + ")";
+ }
+
+ if (!strcmp(filename, "default.xml")) {
+ paths->insert(paths->begin(), full);
+ names->insert(names->begin(), label.c_str());
+ } else {
+ paths->push_back(full);
+ names->push_back(label.c_str());
+ }
+
+ Inkscape::GC::release(doc);
+ }
+ g_free(full);
+ }
+ g_free(lower);
+ }
+ g_dir_close(directory);
+ }
+ }
+
+ g_free(dirname);
+ sources.pop_front();
+ }
+
+}
+
+Glib::ustring sp_shortcut_get_file_path()
+{
+ //# Get the current directory for finding files
+ Glib::ustring open_path;
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ Glib::ustring attr = prefs->getString("/dialogs/save_export/path");
+ if (!attr.empty()) open_path = attr;
+
+ //# Test if the open_path directory exists
+ if (!Inkscape::IO::file_test(open_path.c_str(),
+ (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
+ open_path = "";
+
+ if (open_path.empty()) {
+ /* Grab document directory */
+ const gchar* docURI = SP_ACTIVE_DOCUMENT->getURI();
+ if (docURI) {
+ open_path = Glib::path_get_dirname(docURI);
+ open_path.append(G_DIR_SEPARATOR_S);
+ }
+ }
+
+ //# If no open path, default to our home directory
+ if (open_path.empty())
+ {
+ open_path = g_get_home_dir();
+ open_path.append(G_DIR_SEPARATOR_S);
+ }
+
+ return open_path;
+}
+
+//static Inkscape::UI::Dialog::FileSaveDialog * saveDialog = NULL;
+
+void sp_shortcut_file_export()
+{
+ Glib::ustring open_path = sp_shortcut_get_file_path();
+ open_path.append("shortcut_keys.xml");
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ Glib::ustring filename;
+
+ Inkscape::UI::Dialog::FileSaveDialog *saveDialog =
+ Inkscape::UI::Dialog::FileSaveDialog::create(
+ *(desktop->getToplevel()),
+ open_path,
+ Inkscape::UI::Dialog::CUSTOM_TYPE,
+ _("Select a filename for exporting"),
+ "",
+ "",
+ Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS
+ );
+ saveDialog->addFileType("All Files", "*");
+
+ bool success = saveDialog->show();
+ if (!success) {
+ delete saveDialog;
+ return;
+ }
+
+ Glib::ustring fileName = saveDialog->getFilename();
+ if (fileName.size() > 0) {
+ Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
+ sp_shortcut_file_export_do(newFileName.c_str());
+ }
+
+ delete saveDialog;
+}
+
+bool sp_shortcut_file_import() {
+
+ Glib::ustring open_path = sp_shortcut_get_file_path();
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+
+ Inkscape::UI::Dialog::FileOpenDialog *importFileDialog =
+ Inkscape::UI::Dialog::FileOpenDialog::create(
+ *desktop->getToplevel(),
+ open_path,
+ Inkscape::UI::Dialog::CUSTOM_TYPE,
+ _("Select a file to import"));
+ importFileDialog->addFilterMenu("All Files", "*");
+
+ //# Show the dialog
+ bool const success = importFileDialog->show();
+
+ if (!success) {
+ delete importFileDialog;
+ return false;
+ }
+
+ Glib::ustring fileName = importFileDialog->getFilename();
+ sp_shortcut_file_import_do(fileName.c_str());
+
+ delete importFileDialog;
+
+ return true;
+}
+
+void sp_shortcut_file_import_do(char const *importname) {
+
+ XML::Document *doc=sp_repr_read_file(importname, NULL);
+ if (!doc) {
+ g_warning("Unable to read keyboard shortcut file %s", importname);
+ return;
+ }
+
+ char const *filename = get_path(USER, KEYS, "default.xml");
+ sp_repr_save_file(doc, filename, NULL);
+
+ GC::release(doc);
+
+ sp_shortcut_init();
+}
+
+void sp_shortcut_file_export_do(char const *exportname) {
+
+ char const *filename = get_path(USER, KEYS, "default.xml");
+
+ XML::Document *doc=sp_repr_read_file(filename, NULL);
+ if (!doc) {
+ g_warning("Unable to read keyboard shortcut file %s", filename);
+ return;
+ }
+
+ sp_repr_save_file(doc, exportname, NULL);
+
+ GC::release(doc);
+}
+/*
+ * Add or delete a shortcut to the users default.xml keys file
+ * @param addremove - when true add/override a shortcut, when false remove shortcut
+ * @param addshift - when true addthe Shifg modifier to the non-display element
+ *
+ * Shortcut file consists of pairs of bind elements :
+ * Element (a) is used for shortcut display in menus (display="True") and contains the gdk_keyval_name of the shortcut key
+ * Element (b) is used in shortcut lookup and contains an uppercase version of the gdk_keyval_name,
+ * or a gdk_keyval_name name and the "Shift" modifier for Shift altered hardware code keys (see get_group0_keyval() for explanation)
+ */
+void sp_shortcut_delete_from_file(char const *action, unsigned int const shortcut) {
+
+ char const *filename = get_path(USER, KEYS, "default.xml");
+
+ XML::Document *doc=sp_repr_read_file(filename, NULL);
+ if (!doc) {
+ g_warning("Unable to read keyboard shortcut file %s", filename);
+ return;
+ }
+
+ gchar *key = gdk_keyval_name (shortcut & (~SP_SHORTCUT_MODIFIER_MASK));
+ std::string modifiers = sp_shortcut_to_label(shortcut & (SP_SHORTCUT_MODIFIER_MASK));
+
+ if (!key) {
+ g_warning("Unknown key for shortcut %u", shortcut);
+ return;
+ }
+
+ //g_message("Removing key %s, mods %s action %s", key, modifiers.c_str(), action);
+
+ XML::Node *root=doc->root();
+ g_return_if_fail(!strcmp(root->name(), "keys"));
+ XML::Node *iter=root->firstChild();
+ while (iter) {
+
+ if (strcmp(iter->name(), "bind")) {
+ // some unknown element, do not complain
+ iter = iter->next();
+ continue;
+ }
+
+ gchar const *verb_name=iter->attribute("action");
+ if (!verb_name) {
+ iter = iter->next();
+ continue;
+ }
+
+ gchar const *keyval_name = iter->attribute("key");
+ if (!keyval_name || !*keyval_name) {
+ // that's ok, it's just listed for reference without assignment, skip it
+ iter = iter->next();
+ continue;
+ }
+
+ if (Glib::ustring(key).lowercase() != Glib::ustring(keyval_name).lowercase()) {
+ // If deleting, then delete both the upper and lower case versions
+ iter = iter->next();
+ continue;
+ }
+
+ gchar const *modifiers_string = iter->attribute("modifiers");
+ if ((modifiers_string && !strcmp(modifiers.c_str(), modifiers_string)) ||
+ (!modifiers_string && modifiers.empty())) {
+ //Looks like a match
+ // Delete node
+ sp_repr_unparent(iter);
+ iter = root->firstChild();
+ continue;
+ }
+ iter = iter->next();
+ }
+
+ sp_repr_save_file(doc, filename, NULL);
+
+ GC::release(doc);
+
+}
+
+void sp_shortcut_add_to_file(char const *action, unsigned int const shortcut) {
+
+ char const *filename = get_path(USER, KEYS, "default.xml");
+
+ XML::Document *doc=sp_repr_read_file(filename, NULL);
+ if (!doc) {
+ g_warning("Unable to read keyboard shortcut file %s, creating ....", filename);
+ doc = sp_shortcut_create_template_file(filename);
+ if (!doc) {
+ g_warning("Unable to create keyboard shortcut file %s", filename);
+ return;
+ }
+ }
+
+ gchar *key = gdk_keyval_name (shortcut & (~SP_SHORTCUT_MODIFIER_MASK));
+ std::string modifiers = sp_shortcut_to_label(shortcut & (SP_SHORTCUT_MODIFIER_MASK));
+
+ if (!key) {
+ g_warning("Unknown key for shortcut %u", shortcut);
+ return;
+ }
+
+ //g_message("Adding key %s, mods %s action %s", key, modifiers.c_str(), action);
+
+ // Add node
+ Inkscape::XML::Node *newnode;
+ newnode = doc->createElement("bind");
+ newnode->setAttribute("key", key);
+ if (!modifiers.empty()) {
+ newnode->setAttribute("modifiers", modifiers.c_str());
+ }
+ newnode->setAttribute("action", action);
+ newnode->setAttribute("display", "true");
+
+ doc->root()->appendChild(newnode);
+
+ if (strlen(key) == 1) {
+ // Add another uppercase version if a character
+ Inkscape::XML::Node *newnode;
+ newnode = doc->createElement("bind");
+ newnode->setAttribute("key", Glib::ustring(key).uppercase().c_str());
+ if (!modifiers.empty()) {
+ newnode->setAttribute("modifiers", modifiers.c_str());
+ }
+
+ newnode->setAttribute("action", action);
+ doc->root()->appendChild(newnode);
+ }
+
+ sp_repr_save_file(doc, filename, NULL);
+
+ GC::release(doc);
+
+}
+static void read_shortcuts_file(char const *filename, bool const is_user_set) {
XML::Document *doc=sp_repr_read_file(filename, NULL);
if (!doc) {
g_warning("Unable to read keys file %s", filename);
@@ -153,13 +609,37 @@ static void read_shortcuts_file(char const *filename) {
}
}
- sp_shortcut_set(keyval | modifiers, verb, is_primary);
+ sp_shortcut_set(keyval | modifiers, verb, is_primary, is_user_set);
}
GC::release(doc);
}
/**
+ * Removes a keyboard shortcut for the given verb.
+ * (Removes any existing binding for the given shortcut, including appropriately
+ * adjusting sp_shortcut_get_primary if necessary.)*
+ */
+void
+sp_shortcut_unset(unsigned int const shortcut)
+{
+ if (!verbs) sp_shortcut_init();
+
+ Inkscape::Verb *verb = (*verbs)[shortcut];
+
+ /* Maintain the invariant that sp_shortcut_get_primary(v) returns either 0 or a valid shortcut for v. */
+ if (verb) {
+
+ (*verbs)[shortcut] = 0;
+
+ unsigned int const old_primary = (*primary_shortcuts)[verb];
+ if (old_primary == shortcut) {
+ (*primary_shortcuts)[verb] = 0;
+ }
+
+ }
+}
+/**
* Adds a keyboard shortcut for the given verb.
* (Removes any existing binding for the given shortcut, including appropriately
* adjusting sp_shortcut_get_primary if necessary.)
@@ -169,8 +649,8 @@ static void read_shortcuts_file(char const *filename) {
* \post sp_shortcut_get_verb(shortcut) == verb.
* \post !is_primary or sp_shortcut_get_primary(verb) == shortcut.
*/
-static void
-sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary)
+void
+sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary, bool const is_user_set)
{
if (!verbs) sp_shortcut_init();
@@ -183,11 +663,13 @@ sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool co
if (old_primary == shortcut) {
(*primary_shortcuts)[old_verb] = 0;
+ (*user_shortcuts)[old_verb] = 0;
}
}
if (is_primary) {
(*primary_shortcuts)[verb] = shortcut;
+ (*user_shortcuts)[verb] = is_user_set;
}
}
@@ -211,6 +693,20 @@ unsigned int sp_shortcut_get_primary(Inkscape::Verb *verb)
return result;
}
+bool sp_shortcut_is_user_set(Inkscape::Verb *verb)
+{
+ unsigned int result = false;
+ if (!primary_shortcuts) {
+ sp_shortcut_init();
+ }
+
+ if (primary_shortcuts->count(verb)) {
+ result = (*user_shortcuts)[verb];
+ }
+ return result;
+}
+
+
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)