summaryrefslogtreecommitdiffstats
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
parentFix for 172236 : Dropper pixmaps and onetime fix (diff)
downloadinkscape-60141f6bdf22a0efad3baff5a944d15a2b28f728.tar.gz
inkscape-60141f6bdf22a0efad3baff5a944d15a2b28f728.zip
Fix for 1036059 : Keyboard shortcut editor
(bzr r11895)
-rw-r--r--src/extension/effect.h2
-rw-r--r--src/shortcuts.cpp524
-rw-r--r--src/shortcuts.h16
-rw-r--r--src/ui/dialog/filedialog.cpp3
-rw-r--r--src/ui/dialog/filedialog.h2
-rw-r--r--src/ui/dialog/filedialogimpl-gtkmm.cpp21
-rw-r--r--src/ui/dialog/filedialogimpl-gtkmm.h1
-rw-r--r--src/ui/dialog/filedialogimpl-win32.cpp72
-rw-r--r--src/ui/dialog/filedialogimpl-win32.h2
-rw-r--r--src/ui/dialog/inkscape-preferences.cpp319
-rw-r--r--src/ui/dialog/inkscape-preferences.h62
-rw-r--r--src/verbs.cpp59
-rw-r--r--src/verbs.h19
13 files changed, 1060 insertions, 42 deletions
diff --git a/src/extension/effect.h b/src/extension/effect.h
index bb36b9238..6616a23ec 100644
--- a/src/extension/effect.h
+++ b/src/extension/effect.h
@@ -67,7 +67,7 @@ class Effect : public Extension {
gchar const * image,
Effect * effect,
bool showPrefs) :
- Verb(id, _(name), _(tip), image),
+ Verb(id, _(name), _(tip), image, _("Extensions")),
_effect(effect),
_showPrefs(showPrefs),
_elip_name(NULL) {
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)
diff --git a/src/shortcuts.h b/src/shortcuts.h
index 9d84aa6d1..118909bd3 100644
--- a/src/shortcuts.h
+++ b/src/shortcuts.h
@@ -1,6 +1,8 @@
#ifndef __SP_SHORTCUTS_H__
#define __SP_SHORTCUTS_H__
+#include <gtkmm/cellrendereraccel.h>
+
/*
* Keyboard shortcut processing
*
@@ -29,9 +31,23 @@ namespace Inkscape {
/* Returns true if action was performed */
bool sp_shortcut_invoke (unsigned int shortcut, Inkscape::UI::View::View *view);
+void sp_shortcut_init();
Inkscape::Verb * sp_shortcut_get_verb (unsigned int shortcut);
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.
+void sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary, bool const is_user_set=false);
+void sp_shortcut_unset(unsigned int const shortcut);
+void sp_shortcut_add_to_file(char const *action, unsigned int const shortcut);
+void sp_shortcut_delete_from_file(char const *action, unsigned int const shortcut);
+void sp_shortcuts_delete_all_from_file();
+Glib::ustring sp_shortcut_to_label(unsigned int const shortcut);
+unsigned int sp_gdkmodifier_to_shortcut(guint accel_key, Gdk::ModifierType gdkmodifier, guint hardware_keycode);
+void sp_shortcut_get_file_names(std::vector<Glib::ustring> *names, std::vector<Glib::ustring> *paths);
+bool sp_shortcut_is_user_set(Inkscape::Verb *verb);
+void sp_shortcut_file_export();
+bool sp_shortcut_file_import();
+void sp_shortcut_file_import_do(char const *importname);
+void sp_shortcut_file_export_do(char const *exportname);
#endif
diff --git a/src/ui/dialog/filedialog.cpp b/src/ui/dialog/filedialog.cpp
index 47ba6c748..b3af6fc00 100644
--- a/src/ui/dialog/filedialog.cpp
+++ b/src/ui/dialog/filedialog.cpp
@@ -152,6 +152,9 @@ Glib::ustring FileSaveDialog::getDocTitle()
void FileSaveDialog::appendExtension(Glib::ustring& path, Inkscape::Extension::Output* outputExtension)
{
+ if (!outputExtension)
+ return;
+
try {
bool appendExtension = true;
Glib::ustring utf8Name = Glib::filename_to_utf8( path );
diff --git a/src/ui/dialog/filedialog.h b/src/ui/dialog/filedialog.h
index 6a3436aea..63cee6bdc 100644
--- a/src/ui/dialog/filedialog.h
+++ b/src/ui/dialog/filedialog.h
@@ -208,6 +208,8 @@ public:
virtual Glib::ustring getCurrentDirectory() = 0;
+ virtual void addFileType(Glib::ustring name, Glib::ustring pattern) = 0;
+
protected:
/**
diff --git a/src/ui/dialog/filedialogimpl-gtkmm.cpp b/src/ui/dialog/filedialogimpl-gtkmm.cpp
index 8c2a7e056..90d9855ec 100644
--- a/src/ui/dialog/filedialogimpl-gtkmm.cpp
+++ b/src/ui/dialog/filedialogimpl-gtkmm.cpp
@@ -1047,7 +1047,9 @@ FileSaveDialogImplGtk::FileSaveDialogImplGtk( Gtk::Window &parentWindow,
fileTypeCheckbox.set_active(prefs->getBool("/dialogs/save_as/append_extension", true));
}
- createFileTypeMenu();
+ if (_dialogType != CUSTOM_TYPE)
+ createFileTypeMenu();
+
fileTypeComboBox.set_size_request(200,40);
fileTypeComboBox.signal_changed().connect(
sigc::mem_fun(*this, &FileSaveDialogImplGtk::fileTypeChangedCallback) );
@@ -1174,7 +1176,24 @@ void FileSaveDialogImplGtk::fileTypeChangedCallback()
updateNameAndExtension();
}
+void FileSaveDialogImplGtk::addFileType(Glib::ustring name, Glib::ustring pattern)
+{
+ //#Let user choose
+ FileType guessType;
+ guessType.name = name;
+ guessType.pattern = pattern;
+ guessType.extension = NULL;
+ #if WITH_GTKMM_2_24
+ fileTypeComboBox.append(guessType.name);
+ #else
+ fileTypeComboBox.append_text(guessType.name);
+ #endif
+ fileTypes.push_back(guessType);
+
+ fileTypeComboBox.set_active(0);
+ fileTypeChangedCallback(); //call at least once to set the filter
+}
void FileSaveDialogImplGtk::createFileTypeMenu()
{
diff --git a/src/ui/dialog/filedialogimpl-gtkmm.h b/src/ui/dialog/filedialogimpl-gtkmm.h
index 02841a082..7501b5e14 100644
--- a/src/ui/dialog/filedialogimpl-gtkmm.h
+++ b/src/ui/dialog/filedialogimpl-gtkmm.h
@@ -291,6 +291,7 @@ public:
virtual void setSelectionType( Inkscape::Extension::Extension * key );
Glib::ustring getCurrentDirectory();
+ void addFileType(Glib::ustring name, Glib::ustring pattern);
private:
//void change_title(const Glib::ustring& title);
diff --git a/src/ui/dialog/filedialogimpl-win32.cpp b/src/ui/dialog/filedialogimpl-win32.cpp
index 5891f25ac..6425d9fee 100644
--- a/src/ui/dialog/filedialogimpl-win32.cpp
+++ b/src/ui/dialog/filedialogimpl-win32.cpp
@@ -188,7 +188,8 @@ FileOpenDialogImplWin32::FileOpenDialogImplWin32(Gtk::Window &parent,
_mutex = NULL;
- createFilterMenu();
+ if (dialogType != CUSTOM_TYPE)
+ createFilterMenu();
}
@@ -1748,6 +1749,72 @@ void FileSaveDialogImplWin32::createFilterMenu()
_filter_index = 1; // A value of 1 selects the 1st filter - NOT the 2nd
}
+
+void FileSaveDialogImplWin32::addFileType(Glib::ustring name, Glib::ustring pattern)
+{
+ list<Filter> filter_list;
+
+ knownExtensions.clear();
+
+ int extension_index = 0;
+ int filter_length = 1;
+
+ ustring all_exe_files_filter = pattern;
+ Filter all_exe_files;
+
+ const gchar *all_exe_files_filter_name = name.data();
+
+ // Calculate the amount of memory required
+ int filter_count = 1;
+
+
+ // Filter Executable Files
+ all_exe_files.name = g_utf8_to_utf16(all_exe_files_filter_name,
+ -1, NULL, &all_exe_files.name_length, NULL);
+ all_exe_files.filter = g_utf8_to_utf16(all_exe_files_filter.data(),
+ -1, NULL, &all_exe_files.filter_length, NULL);
+ all_exe_files.mod = NULL;
+ filter_list.push_front(all_exe_files);
+
+ knownExtensions.insert( Glib::ustring(all_exe_files_filter).casefold() );
+
+ _extension_map = new Inkscape::Extension::Extension*[filter_count];
+
+ _filter = new wchar_t[filter_length];
+ wchar_t *filterptr = _filter;
+
+ for(list<Filter>::iterator filter_iterator = filter_list.begin();
+ filter_iterator != filter_list.end(); ++filter_iterator)
+ {
+ const Filter &filter = *filter_iterator;
+
+ wcsncpy(filterptr, (wchar_t*)filter.name, filter.name_length);
+ filterptr += filter.name_length;
+ g_free(filter.name);
+
+ *(filterptr++) = L'\0';
+ *(filterptr++) = L'*';
+
+ if(filter.filter != NULL)
+ {
+ wcsncpy(filterptr, (wchar_t*)filter.filter, filter.filter_length);
+ filterptr += filter.filter_length;
+ g_free(filter.filter);
+ }
+
+ *(filterptr++) = L'\0';
+
+ // Associate this input extension with the file type name
+ _extension_map[extension_index++] = filter.mod;
+ }
+ *(filterptr++) = L'\0';
+
+ _filter_count = extension_index;
+ _filter_index = 1; // Select the 1st filter in the list
+
+
+}
+
void FileSaveDialogImplWin32::GetSaveFileName_thread()
{
OPENFILENAMEEXW ofn;
@@ -1814,7 +1881,7 @@ FileSaveDialogImplWin32::show()
if(Glib::Thread::create(sigc::mem_fun(*this, &FileSaveDialogImplWin32::GetSaveFileName_thread), true))
g_main_loop_run(_main_loop);
- if(_result)
+ if(_result && _extension)
appendExtension(myFilename, (Inkscape::Extension::Output*)_extension);
}
@@ -1827,6 +1894,7 @@ void FileSaveDialogImplWin32::setSelectionType( Inkscape::Extension::Extension *
}
+
UINT_PTR CALLBACK FileSaveDialogImplWin32::GetSaveFileName_hookproc(
HWND hdlg, UINT uiMsg, WPARAM, LPARAM lParam)
{
diff --git a/src/ui/dialog/filedialogimpl-win32.h b/src/ui/dialog/filedialogimpl-win32.h
index d016b0a24..15953f9d8 100644
--- a/src/ui/dialog/filedialogimpl-win32.h
+++ b/src/ui/dialog/filedialogimpl-win32.h
@@ -343,6 +343,8 @@ public:
virtual void setSelectionType( Inkscape::Extension::Extension *key );
+ virtual void addFileType(Glib::ustring name, Glib::ustring pattern);
+
private:
/// A handle to the title label and edit box
HWND _title_label;
diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp
index 2731b6174..03366a0c3 100644
--- a/src/ui/dialog/inkscape-preferences.cpp
+++ b/src/ui/dialog/inkscape-preferences.cpp
@@ -47,7 +47,11 @@
#include "display/canvas-grid.h"
#include "path-prefix.h"
#include "io/resource.h"
+#include "io/sys.h"
#include "inkscape.h"
+#include "shortcuts.h"
+#include "document.h"
+
#ifdef HAVE_ASPELL
# include <aspell.h>
@@ -137,6 +141,7 @@ InkscapePreferences::InkscapePreferences()
initPageRendering();
initPageSpellcheck();
+
signalPresent().connect(sigc::mem_fun(*this, &InkscapePreferences::_presentPages));
//calculate the size request for this dialog
@@ -741,6 +746,8 @@ void InkscapePreferences::initPageUI()
_grids_axonom.add_line( false, _("Major grid line every:"), _grids_axonom_empspacing, "", "", false);
this->AddPage(_page_grids, _("Grids"), iter_ui, PREFS_PAGE_UI_GRIDS);
+
+ initKeyboardShortcuts(iter_ui);
}
#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
@@ -1405,6 +1412,316 @@ void InkscapePreferences::initPageBitmaps()
this->AddPage(_page_bitmaps, _("Bitmaps"), PREFS_PAGE_BITMAPS);
}
+void InkscapePreferences::initKeyboardShortcuts(Gtk::TreeModel::iterator iter_ui)
+{
+ std::vector<Glib::ustring> fileNames;
+ std::vector<Glib::ustring> fileLabels;
+
+ sp_shortcut_get_file_names(&fileLabels, &fileNames);
+
+ _kb_filelist.init( "/options/kbshortcuts/shortcutfile", &fileLabels[0], &fileNames[0], fileLabels.size(), fileNames[0]);
+
+ Glib::ustring tooltip(_("Select a file of predefined shortcuts to use. Any customized shortcuts you create will be added seperately to "));
+ tooltip += Glib::ustring(IO::Resource::get_path(IO::Resource::USER, IO::Resource::KEYS, "default.xml"));
+
+ _page_keyshortcuts.add_line( false, _("Shortcut file:"), _kb_filelist, "", tooltip.c_str(), false);
+
+ _kb_search.init("/options/kbshortcuts/value", true);
+ _page_keyshortcuts.add_line( false, _("Search:"), _kb_search, "", "", true);
+
+ _kb_store = Gtk::TreeStore::create( _kb_columns );
+ _kb_store->set_sort_column (_kb_columns.id, Gtk::SORT_ASCENDING );
+
+ _kb_filter = Gtk::TreeModelFilter::create(_kb_store);
+ _kb_filter->set_visible_func (sigc::mem_fun(*this, &InkscapePreferences::onKBSearchFilter));
+
+ _kb_shortcut_renderer.property_editable() = true;
+
+ _kb_tree.set_model(_kb_filter);
+ _kb_tree.append_column(_("Name"), _kb_columns.name);
+ _kb_tree.append_column(_("Shortcut"), _kb_shortcut_renderer);
+ _kb_tree.append_column(_("Description"), _kb_columns.description);
+ _kb_tree.append_column(_("ID"), _kb_columns.id);
+
+ _kb_tree.set_expander_column(*_kb_tree.get_column(0));
+
+ _kb_tree.get_column(0)->set_resizable(true);
+ _kb_tree.get_column(0)->set_clickable(true);
+ _kb_tree.get_column(0)->set_fixed_width (200);
+
+ _kb_tree.get_column(1)->set_resizable(true);
+ _kb_tree.get_column(1)->set_clickable(true);
+ _kb_tree.get_column(1)->set_fixed_width (150);
+ //_kb_tree.get_column(1)->add_attribute(_kb_shortcut_renderer.property_text(), _kb_columns.shortcut);
+ _kb_tree.get_column(1)->set_cell_data_func(_kb_shortcut_renderer, sigc::ptr_fun(InkscapePreferences::onKBShortcutRenderer));
+
+ _kb_tree.get_column(2)->set_resizable(true);
+ _kb_tree.get_column(2)->set_clickable(true);
+
+ _kb_tree.get_column(3)->set_resizable(true);
+ _kb_tree.get_column(3)->set_clickable(true);
+
+ _kb_shortcut_renderer.signal_accel_edited().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBTreeEdited) );
+ _kb_shortcut_renderer.signal_accel_cleared().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBTreeCleared) );
+
+ Gtk::ScrolledWindow* scroller = new Gtk::ScrolledWindow();
+ scroller->add(_kb_tree);
+
+ int row = 3;
+ _page_keyshortcuts.attach(*scroller, 0, 2, row, row+1, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL);
+ row++;
+
+ Gtk::HButtonBox *box_buttons = manage (new Gtk::HButtonBox);
+ box_buttons->set_layout(Gtk::BUTTONBOX_END);
+ box_buttons->set_spacing(4);
+ _page_keyshortcuts.attach(*box_buttons, 0, 3, row, row+1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK);
+
+ UI::Widget::Button *kb_reset = manage(new UI::Widget::Button(_("Reset"), _("Remove all your customized keyboard shortcuts, and revert to the shortcuts in the shortcut file listed above")));
+ box_buttons->pack_start(*kb_reset, true, true, 6);
+ box_buttons->set_child_secondary(*kb_reset);
+
+ UI::Widget::Button *kb_import = manage(new UI::Widget::Button(_("Import ..."), _("Import custom keyboard shortcuts from a file")));
+ box_buttons->pack_end(*kb_import, true, true, 6);
+
+ UI::Widget::Button *kb_export = manage(new UI::Widget::Button(_("Export ..."), _("Export custom keyboard shortcuts to a file")));
+ box_buttons->pack_end(*kb_export, true, true, 6);
+
+ kb_reset->signal_clicked().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBReset) );
+ kb_import->signal_clicked().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBImport) );
+ kb_export->signal_clicked().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBExport) );
+ _kb_search.signal_key_release_event().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBSearchKeyEvent) );
+ _kb_filelist.signal_changed().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBList) );
+ _page_keyshortcuts.signal_realize().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBRealize) );
+
+ this->AddPage(_page_keyshortcuts, _("Keyboard Shortcuts"), iter_ui, PREFS_PAGE_UI_KEYBOARD_SHORTCUTS);
+
+ _kb_shortcuts_loaded = false;
+ Gtk::TreeStore::iterator iter_group = _kb_store->append();
+ (*iter_group)[_kb_columns.name] = "Loading ...";
+ (*iter_group)[_kb_columns.shortcut] = "";
+ (*iter_group)[_kb_columns.id] = "";
+ (*iter_group)[_kb_columns.description] = "";
+ (*iter_group)[_kb_columns.shortcutid] = 0;
+ (*iter_group)[_kb_columns.user_set] = 0;
+
+}
+
+void InkscapePreferences::onKBList()
+{
+ sp_shortcut_init();
+ onKBListKeyboardShortcuts();
+}
+
+void InkscapePreferences::onKBReset()
+{
+ sp_shortcuts_delete_all_from_file();
+ sp_shortcut_init();
+ onKBListKeyboardShortcuts();
+}
+
+void InkscapePreferences::onKBImport()
+{
+ if (sp_shortcut_file_import()) {
+ onKBListKeyboardShortcuts();
+ }
+}
+
+void InkscapePreferences::onKBExport()
+{
+ sp_shortcut_file_export();
+}
+
+bool InkscapePreferences::onKBSearchKeyEvent(GdkEventKey *event)
+{
+ _kb_filter->refilter();
+ return FALSE;
+}
+
+void InkscapePreferences::onKBTreeCleared(const Glib::ustring& path)
+{
+ Gtk::TreeModel::iterator iter = _kb_filter->get_iter(path);
+ Glib::ustring id = (*iter)[_kb_columns.id];
+ unsigned int const current_shortcut_id = (*iter)[_kb_columns.shortcutid];
+
+ // Remove current shortcut from file
+ sp_shortcut_delete_from_file(id.c_str(), current_shortcut_id);
+
+ sp_shortcut_init();
+ onKBListKeyboardShortcuts();
+
+}
+
+void InkscapePreferences::onKBTreeEdited (const Glib::ustring& path, guint accel_key, Gdk::ModifierType accel_mods, guint hardware_keycode)
+{
+ Gtk::TreeModel::iterator iter = _kb_filter->get_iter(path);
+
+ Glib::ustring id = (*iter)[_kb_columns.id];
+ Glib::ustring current_shortcut = (*iter)[_kb_columns.shortcut];
+ unsigned int const current_shortcut_id = (*iter)[_kb_columns.shortcutid];
+
+ Inkscape::Verb *const verb = Inkscape::Verb::getbyid(id.c_str());
+ if (!verb) {
+ return;
+ }
+
+ unsigned int const new_shortcut_id = sp_gdkmodifier_to_shortcut(accel_key, accel_mods, hardware_keycode);
+ if (new_shortcut_id) {
+
+ // Delete current shortcut if it existed
+ sp_shortcut_delete_from_file(id.c_str(), current_shortcut_id);
+ // Delete any references to the new shortcut
+ sp_shortcut_delete_from_file(id.c_str(), new_shortcut_id);
+ // Add the new shortcut
+ sp_shortcut_add_to_file(id.c_str(), new_shortcut_id);
+
+ sp_shortcut_init();
+ onKBListKeyboardShortcuts();
+ }
+}
+
+bool InkscapePreferences::onKBSearchFilter(const Gtk::TreeModel::const_iterator& iter)
+{
+ Glib::ustring search = _kb_search.get_text().lowercase();
+ if (search.empty()) {
+ return TRUE;
+ }
+
+ Glib::ustring name = (*iter)[_kb_columns.name];
+ Glib::ustring desc = (*iter)[_kb_columns.description];
+ Glib::ustring shortcut = (*iter)[_kb_columns.shortcut];
+ Glib::ustring id = (*iter)[_kb_columns.id];
+
+ if (id.empty()) {
+ return TRUE; // Keep all group nodes visible
+ }
+
+ return (name.lowercase().find(search) != name.npos
+ || shortcut.lowercase().find(search) != name.npos
+ || desc.lowercase().find(search) != name.npos
+ || id.lowercase().find(search) != name.npos);
+}
+
+void InkscapePreferences::onKBRealize()
+{
+ if (!_kb_shortcuts_loaded /*&& _current_page == &_page_keyshortcuts*/) {
+ _kb_shortcuts_loaded = true;
+ onKBListKeyboardShortcuts();
+ }
+}
+
+InkscapePreferences::ModelColumns &InkscapePreferences::onKBGetCols()
+{
+ static InkscapePreferences::ModelColumns cols;
+ return cols;
+}
+
+void InkscapePreferences::onKBShortcutRenderer(Gtk::CellRenderer *renderer, Gtk::TreeIter const &iter) {
+
+ Glib::ustring shortcut = (*iter)[onKBGetCols().shortcut];
+ unsigned int user_set = (*iter)[onKBGetCols().user_set];
+ Gtk::CellRendererAccel *accel = dynamic_cast<Gtk::CellRendererAccel *>(renderer);
+ if (user_set) {
+ accel->property_markup() = Glib::ustring("<span foreground=\"blue\"> " + shortcut + " </span>").c_str();
+ } else {
+ accel->property_markup() = Glib::ustring("<span> " + shortcut + " </span>").c_str();
+ }
+}
+
+void InkscapePreferences::onKBListKeyboardShortcuts()
+{
+ // Save the current selection
+ Gtk::TreeStore::iterator iter = _kb_tree.get_selection()->get_selected();
+ Glib::ustring selected_id = "";
+ if (iter) {
+ selected_id = (*iter)[_kb_columns.id];
+ }
+
+ _kb_store->clear();
+
+ std::vector<Verb *>verbs = Inkscape::Verb::getList();
+
+ for (unsigned int i = 0; i < verbs.size(); i++) {
+
+ Inkscape::Verb* verb = verbs[i];
+ if (!verb) {
+ continue;
+ }
+ if (!verb->get_name()){
+ continue;
+ }
+
+ Gtk::TreeStore::Path path;
+ if (_kb_store->iter_is_valid(_kb_store->get_iter("0"))) {
+ path = _kb_store->get_path(_kb_store->get_iter("0"));
+ }
+
+ // Find this group in the tree
+ Glib::ustring group = verb->get_group() ? verb->get_group() : "Misc";
+ Gtk::TreeStore::iterator iter_group;
+ bool found = false;
+ while (path) {
+ iter_group = _kb_store->get_iter(path);
+ if (!_kb_store->iter_is_valid(iter_group)) {
+ break;
+ }
+ Glib::ustring name = (*iter_group)[_kb_columns.name];
+ if ((*iter_group)[_kb_columns.name] == group) {
+ found = true;
+ break;
+ }
+ path.next();
+ }
+
+ if (!found) {
+ // Add the group if not there
+ iter_group = _kb_store->append();
+ (*iter_group)[_kb_columns.name] = group;
+ (*iter_group)[_kb_columns.shortcut] = "";
+ (*iter_group)[_kb_columns.id] = "";
+ (*iter_group)[_kb_columns.description] = "";
+ (*iter_group)[_kb_columns.shortcutid] = 0;
+ (*iter_group)[_kb_columns.user_set] = 0;
+ }
+
+ // Remove the key accelerators from the verb name
+ Glib::ustring name = verb->get_name();
+ std::string::size_type k = 0;
+ while((k=name.find('_',k))!=name.npos) {
+ name.erase(k, 1);
+ }
+
+ // Get the shortcut label
+ unsigned int shortcut_id = sp_shortcut_get_primary(verb);
+ Glib::ustring shortcut_label = "";
+ if (shortcut_id != GDK_KEY_VoidSymbol) {
+ gchar* str = sp_shortcut_get_label(shortcut_id);
+ if (str) {
+ shortcut_label = str;
+ g_free(str);
+ str = 0;
+ }
+ }
+ // Add the verb to the group
+ Gtk::TreeStore::iterator row = _kb_store->append(iter_group->children());
+ (*row)[_kb_columns.name] = name;
+ (*row)[_kb_columns.shortcut] = shortcut_label;
+ (*row)[_kb_columns.description] = verb->get_short_tip() ? verb->get_short_tip() : "";
+ (*row)[_kb_columns.shortcutid] = shortcut_id;
+ (*row)[_kb_columns.id] = verb->get_id();
+ (*row)[_kb_columns.user_set] = sp_shortcut_is_user_set(verb);
+
+ if (selected_id == verb->get_id()) {
+ Gtk::TreeStore::Path sel_path = _kb_filter->convert_child_path_to_path(_kb_store->get_path(row));
+ _kb_tree.expand_to_path(sel_path);
+ _kb_tree.get_selection()->select(sel_path);
+ }
+ }
+
+ if (selected_id.empty()) {
+ _kb_tree.expand_to_path(_kb_store->get_path(_kb_store->get_iter("0:1")));
+ }
+
+}
void InkscapePreferences::initPageSpellcheck()
{
@@ -1609,7 +1926,7 @@ bool InkscapePreferences::PresentPage(const Gtk::TreeModel::iterator& iter)
_page_list.expand_row(_path_tools, false);
if (desired_page >= PREFS_PAGE_TOOLS_SHAPES && desired_page <= PREFS_PAGE_TOOLS_SHAPES_SPIRAL)
_page_list.expand_row(_path_shapes, false);
- if (desired_page >= PREFS_PAGE_UI && desired_page <= PREFS_PAGE_UI_GRIDS)
+ if (desired_page >= PREFS_PAGE_UI && desired_page <= PREFS_PAGE_UI_KEYBOARD_SHORTCUTS)
_page_list.expand_row(_path_ui, false);
if (desired_page >= PREFS_PAGE_BEHAVIOR && desired_page <= PREFS_PAGE_BEHAVIOR_MASKS)
_page_list.expand_row(_path_behavior, false);
diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h
index d60035515..690016556 100644
--- a/src/ui/dialog/inkscape-preferences.h
+++ b/src/ui/dialog/inkscape-preferences.h
@@ -18,15 +18,19 @@
#include <iostream>
#include <vector>
#include "ui/widget/preferences-widget.h"
+#include "ui/widget/button.h"
+#include <stddef.h>
#include <gtkmm/colorbutton.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/treestore.h>
#include <gtkmm/treeview.h>
#include <gtkmm/frame.h>
#include <gtkmm/notebook.h>
-#include <stddef.h>
#include <gtkmm/textview.h>
#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/liststore.h>
+#include <gtkmm/treemodel.h>
+#include <gtkmm/treemodelfilter.h>
#include "ui/widget/panel.h"
@@ -60,6 +64,7 @@ enum {
PREFS_PAGE_UI,
PREFS_PAGE_UI_WINDOWS,
PREFS_PAGE_UI_GRIDS,
+ PREFS_PAGE_UI_KEYBOARD_SHORTCUTS,
PREFS_PAGE_BEHAVIOR,
PREFS_PAGE_BEHAVIOR_SELECTING,
PREFS_PAGE_BEHAVIOR_TRANSFORMS,
@@ -79,6 +84,7 @@ enum {
PREFS_PAGE_BITMAPS,
PREFS_PAGE_RENDERING,
PREFS_PAGE_SPELLCHECK
+
};
namespace Inkscape {
@@ -166,6 +172,8 @@ protected:
UI::Widget::DialogPage _page_bitmaps;
UI::Widget::DialogPage _page_spellcheck;
+ UI::Widget::DialogPage _page_keyshortcuts;
+
UI::Widget::PrefSpinButton _mouse_sens;
UI::Widget::PrefSpinButton _mouse_thres;
UI::Widget::PrefSlider _mouse_grabsize;
@@ -337,12 +345,16 @@ protected:
UI::Widget::PrefCheckButton _spell_ignorenumbers;
UI::Widget::PrefCheckButton _spell_ignoreallcaps;
+
UI::Widget::PrefCombo _misc_overs_bitmap;
UI::Widget::PrefEntryFileButtonHBox _misc_bitmap_editor;
UI::Widget::PrefCheckButton _misc_bitmap_autoreload;
UI::Widget::PrefSpinButton _bitmap_copy_res;
UI::Widget::PrefCombo _bitmap_import;
+ UI::Widget::PrefEntry _kb_search;
+ UI::Widget::PrefCombo _kb_filelist;
+
UI::Widget::PrefCheckButton _save_use_current_dir;
UI::Widget::PrefCheckButton _save_autosave_enable;
UI::Widget::PrefSpinButton _save_autosave_interval;
@@ -411,6 +423,37 @@ protected:
UI::Widget::PrefEntry _importexport_ocal_username;
UI::Widget::PrefEntry _importexport_ocal_password;
+ /*
+ * Keyboard shortcut members
+ */
+ class ModelColumns: public Gtk::TreeModel::ColumnRecord {
+ public:
+ ModelColumns() {
+ add(name);
+ add(id);
+ add(shortcut);
+ add(description);
+ add(shortcutid);
+ add(user_set);
+ }
+ virtual ~ModelColumns() {
+ }
+
+ Gtk::TreeModelColumn<Glib::ustring> name;
+ Gtk::TreeModelColumn<Glib::ustring> id;
+ Gtk::TreeModelColumn<Glib::ustring> shortcut;
+ Gtk::TreeModelColumn<Glib::ustring> description;
+ Gtk::TreeModelColumn<unsigned int> shortcutid;
+ Gtk::TreeModelColumn<unsigned int> user_set;
+ };
+ ModelColumns _kb_columns;
+ static ModelColumns &onKBGetCols();
+ Glib::RefPtr<Gtk::TreeStore> _kb_store;
+ Gtk::TreeView _kb_tree;
+ Gtk::CellRendererAccel _kb_shortcut_renderer;
+ Glib::RefPtr<Gtk::TreeModelFilter> _kb_filter;
+ gboolean _kb_shortcuts_loaded;
+
int _max_dialog_width;
int _max_dialog_height;
int _sb_width;
@@ -441,9 +484,26 @@ protected:
void initPageBitmaps();
void initPageSystem();
void initPageI18n(); // Do we still need it?
+ void initKeyboardShortcuts(Gtk::TreeModel::iterator iter_ui);
void _presentPages();
+ /*
+ * Functions for the Keyboard shortcut editor panel
+ */
+ void onKBReset();
+ void onKBImport();
+ void onKBExport();
+ void onKBList();
+ void onKBRealize();
+ void onKBListKeyboardShortcuts();
+ void onKBTreeEdited (const Glib::ustring& path, guint accel_key, Gdk::ModifierType accel_mods, guint hardware_keycode);
+ void onKBTreeCleared(const Glib::ustring& path_string);
+ bool onKBSearchKeyEvent(GdkEventKey *event);
+ bool onKBSearchFilter(const Gtk::TreeModel::const_iterator& iter);
+ static void onKBShortcutRenderer(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter);
+
+
private:
InkscapePreferences();
InkscapePreferences(InkscapePreferences const &d);
diff --git a/src/verbs.cpp b/src/verbs.cpp
index 8c45ce665..bbadb1a25 100644
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
@@ -133,7 +133,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("File"))
{ }
}; // FileVerb class
@@ -152,7 +152,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Edit"))
{ }
}; // EditVerb class
@@ -171,7 +171,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Selection"))
{ }
}; // SelectionVerb class
@@ -190,7 +190,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Layer"))
{ }
}; // LayerVerb class
@@ -209,7 +209,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Object"))
{ }
}; // ObjectVerb class
@@ -228,7 +228,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Context"))
{ }
}; // ContextVerb class
@@ -247,7 +247,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("View"))
{ }
}; // ZoomVerb class
@@ -267,7 +267,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Dialog"))
{ }
}; // DialogVerb class
@@ -286,7 +286,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Help"))
{ }
}; // HelpVerb class
@@ -305,7 +305,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Help"))
{ }
}; // TutorialVerb class
@@ -324,7 +324,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Text"))
{ }
}; //TextVerb : public Verb
@@ -341,7 +341,7 @@ Verb::VerbIDTable Verb::_verb_ids;
* each call it is incremented. The list of allocated verbs is kept
* 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) :
+Verb::Verb(gchar const *id, gchar const *name, gchar const *tip, gchar const *image, gchar const *group) :
_actions(0),
_id(id),
_name(name),
@@ -350,6 +350,7 @@ Verb::Verb(gchar const *id, gchar const *name, gchar const *tip, gchar const *im
_shortcut(0),
_image(image),
_code(0),
+ _group(group),
_default_sensitive(false)
{
static int count = SP_VERB_LAST;
@@ -2002,6 +2003,7 @@ void DialogVerb::perform(SPAction *action, void *data)
case SP_VERB_DIALOG_PRINT_COLORS_PREVIEW:
dt->_dlg_mgr->showDialog("PrintColorsPreviewDialog");
break;
+
default:
break;
}
@@ -2104,7 +2106,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Extensions"))
{
set_default_sensitive(false);
}
@@ -2169,7 +2171,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("View"))
{
set_default_sensitive(false);
}
@@ -2235,7 +2237,7 @@ public:
gchar const *name,
gchar const *tip,
gchar const *image) :
- Verb(code, id, name, tip, image)
+ Verb(code, id, name, tip, image, _("Layer"))
{
set_default_sensitive(true);
}
@@ -2294,8 +2296,8 @@ void LockAndHideVerb::perform(SPAction *action, void *data)
// these must be in the same order as the SP_VERB_* enum in "verbs.h"
Verb *Verb::_base_verbs[] = {
// Header
- new Verb(SP_VERB_INVALID, NULL, NULL, NULL, NULL),
- new Verb(SP_VERB_NONE, "None", N_("None"), N_("Does nothing"), NULL),
+ new Verb(SP_VERB_INVALID, NULL, NULL, NULL, NULL, NULL),
+ new Verb(SP_VERB_NONE, "None", N_("None"), N_("Does nothing"), NULL, NULL),
// File
new FileVerb(SP_VERB_FILE_NEW, "FileNew", N_("Default"), N_("Create new document from the default template"),
@@ -2707,7 +2709,7 @@ Verb *Verb::_base_verbs[] = {
#ifdef HAVE_GTK_WINDOW_FULLSCREEN
new ZoomVerb(SP_VERB_FULLSCREEN, "FullScreen", N_("_Fullscreen"), N_("Stretch this document window to full screen"),
INKSCAPE_ICON("view-fullscreen")),
- new ZoomVerb(SP_VERB_FULLSCREENFOCUS, "FullScreenFocus", N_("Fullscreen & Focus Mode"), Glib::ustring::format(N_("Stretch this document window to full screen"), N_(" and "), N_("Remove excess toolbars to focus on drawing")).c_str(),
+ new ZoomVerb(SP_VERB_FULLSCREENFOCUS, "FullScreenFocus", N_("Fullscreen & Focus Mode"), N_("Stretch this document window to full screen"),
INKSCAPE_ICON("view-fullscreen")),
#endif // HAVE_GTK_WINDOW_FULLSCREEN
new ZoomVerb(SP_VERB_FOCUSTOGGLE, "FocusToggle", N_("Toggle _Focus Mode"), N_("Remove excess toolbars to focus on drawing"),
@@ -2814,7 +2816,6 @@ Verb *Verb::_base_verbs[] = {
N_("Select which color separations to render in Print Colors Preview rendermode"), NULL),
new DialogVerb(SP_VERB_DIALOG_EXPORT, "DialogExport", N_("_Export PNG Image..."),
N_("Export this document or a selection as a PNG image"), INKSCAPE_ICON("document-export")),
-
// Help
new HelpVerb(SP_VERB_HELP_ABOUT_EXTENSIONS, "HelpAboutExtensions", N_("About E_xtensions"),
N_("Information on Inkscape extensions"), NULL),
@@ -2898,9 +2899,27 @@ Verb *Verb::_base_verbs[] = {
// Footer
- new Verb(SP_VERB_LAST, " '\"invalid id", NULL, NULL, NULL)
+ new Verb(SP_VERB_LAST, " '\"invalid id", NULL, NULL, NULL, NULL)
};
+std::vector<Inkscape::Verb *>
+Verb::getList (void) {
+
+ std::vector<Verb *> verbs;
+ // Go through the dynamic verb table
+ for (VerbTable::iterator iter = _verbs.begin(); iter != _verbs.end(); ++iter) {
+ Verb * verb = iter->second;
+ if (verb->get_code() == SP_VERB_INVALID ||
+ verb->get_code() == SP_VERB_NONE ||
+ verb->get_code() == SP_VERB_LAST) {
+ continue;
+ }
+
+ verbs.push_back(verb);
+ }
+
+ return verbs;
+};
void
Verb::list (void) {
diff --git a/src/verbs.h b/src/verbs.h
index c47d3ae16..c4a7b67e0 100644
--- a/src/verbs.h
+++ b/src/verbs.h
@@ -348,6 +348,7 @@ enum {
gchar *sp_action_get_title (const SPAction *action);
#include <map>
+#include <vector>
namespace Inkscape {
@@ -418,6 +419,9 @@ private:
*/
unsigned int _code;
+ /** Name of the group the verb belongs to. */
+ gchar const * _group;
+
/**
* Whether this verb is set to default to sensitive or
* insensitive when new actions are created.
@@ -452,17 +456,24 @@ public:
gchar const * get_name (void) { return _name; }
/** Accessor to get the internal variable. */
+ gchar const * get_short_tip (void) { return _tip; };
+
+ /** Accessor to get the internal variable. */
gchar const * get_tip (void) ;
/** Accessor to get the internal variable. */
gchar const * get_image (void) { return _image; }
+ /** Get the verbs group */
+ gchar const * get_group (void) { return _group; }
+
/** Set the name after initialization. */
gchar const * set_name (gchar const * name) { _name = name; return _name; }
/** Set the tooltip after initialization. */
gchar const * set_tip (gchar const * tip) { _tip = tip; return _tip; }
+
protected:
SPAction *make_action_helper (Inkscape::UI::View::View *view, void (*perform_fun)(SPAction *, void *), void *in_pntr = NULL);
virtual SPAction *make_action (Inkscape::UI::View::View *view);
@@ -494,7 +505,8 @@ public:
gchar const * id,
gchar const * name,
gchar const * tip,
- gchar const * image) :
+ gchar const * image,
+ gchar const * group) :
_actions(0),
_id(id),
_name(name),
@@ -503,12 +515,13 @@ public:
_shortcut(0),
_image(image),
_code(code),
+ _group(group),
_default_sensitive(true)
{
_verbs.insert(VerbTable::value_type(_code, this));
_verb_ids.insert(VerbIDTable::value_type(_id, this));
}
- Verb (gchar const * id, gchar const * name, gchar const * tip, gchar const * image);
+ Verb (gchar const * id, gchar const * name, gchar const * tip, gchar const * image, gchar const * group);
virtual ~Verb (void);
SPAction * get_action(Inkscape::UI::View::View * view);
@@ -560,6 +573,8 @@ protected:
public:
static void list (void);
+ static std::vector<Inkscape::Verb *>getList (void);
+
}; /* Verb class */