1 #define __SP_SHORTCUTS_C__
3 /** \file
4 * Keyboard shortcut processing.
5 */
6 /*
7 * Authors:
8 * Lauris Kaplinski <lauris@kaplinski.com>
9 * MenTaLguY <mental@rydia.net>
10 * bulia byak <buliabyak@users.sf.net>
11 * Peter Moulder <pmoulder@mail.csse.monash.edu.au>
12 *
13 * Copyright (C) 2005 Monash University
14 * Copyright (C) 2005 MenTaLguY <mental@rydia.net>
15 *
16 * You may redistribute and/or modify this file under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2 of the License, or (at your
18 * option) any later version.
19 */
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vector>
27 #include <gdk/gdkkeys.h>
28 #include <gdk/gdkkeysyms.h>
30 #include "helper/action.h"
31 #include "io/sys.h"
32 #include "io/resource.h"
33 #include "shortcuts.h"
34 #include "verbs.h"
35 #include "xml/node-iterators.h"
36 #include "xml/repr.h"
38 using namespace Inkscape;
40 static void sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary);
41 static void try_shortcuts_file(char const *filename);
42 static void read_shortcuts_file(char const *filename);
44 /* Returns true if action was performed */
46 bool
47 sp_shortcut_invoke(unsigned int shortcut, Inkscape::UI::View::View *view)
48 {
49 Inkscape::Verb *verb = sp_shortcut_get_verb(shortcut);
50 if (verb) {
51 SPAction *action = verb->get_action(view);
52 if (action) {
53 sp_action_perform(action, NULL);
54 return true;
55 }
56 }
57 return false;
58 }
60 static GHashTable *verbs = NULL;
61 static GHashTable *primary_shortcuts = NULL;
63 static void
64 sp_shortcut_init()
65 {
66 using Inkscape::IO::Resource::get_path;
67 using Inkscape::IO::Resource::SYSTEM;
68 using Inkscape::IO::Resource::USER;
69 using Inkscape::IO::Resource::KEYS;
71 verbs = g_hash_table_new(NULL, NULL);
72 primary_shortcuts = g_hash_table_new(NULL, NULL);
74 read_shortcuts_file(get_path(SYSTEM, KEYS, "default.xml"));
75 try_shortcuts_file(get_path(USER, KEYS, "default.xml"));
76 }
78 static void try_shortcuts_file(char const *filename) {
79 using Inkscape::IO::file_test;
81 /* ah, if only we had an exception to catch... (permission, forgiveness) */
82 if (file_test(filename, G_FILE_TEST_EXISTS)) {
83 read_shortcuts_file(filename);
84 }
85 }
87 static void read_shortcuts_file(char const *filename) {
88 XML::Document *doc=sp_repr_read_file(filename, NULL);
89 if (!doc) {
90 g_warning("Unable to read keys file %s", filename);
91 return;
92 }
94 XML::Node const *root=doc->root();
95 g_return_if_fail(!strcmp(root->name(), "keybindings"));
96 XML::NodeConstSiblingIterator iter=root->firstChild();
97 for ( ; iter ; ++iter ) {
98 bool is_primary;
100 if (!strcmp(iter->name(), "primary")) {
101 is_primary = true;
102 } else if (!strcmp(iter->name(), "secondary")) {
103 is_primary = false;
104 } else {
105 g_warning("Unknown key binding type %s", iter->name());
106 continue;
107 }
109 gchar const *verb_name=iter->attribute("verb");
110 if (!verb_name) {
111 g_warning("Missing verb name for shortcut");
112 continue;
113 }
115 gchar const *keyval_name=iter->attribute("keyval");
116 if (!keyval_name) {
117 g_warning("Missing keyval for %s", verb_name);
118 continue;
119 }
120 guint keyval=gdk_keyval_from_name(keyval_name);
121 if (keyval == GDK_VoidSymbol) {
122 g_warning("Unknown keyval %s for %s", keyval_name, verb_name);
123 continue;
124 }
126 guint modifiers=0;
128 gchar const *modifiers_string=iter->attribute("modifiers");
129 if (modifiers_string) {
130 gchar const *iter=modifiers_string;
131 while (*iter) {
132 size_t length=strcspn(iter, ",");
133 gchar *mod=g_strndup(iter, length);
134 if (!strcmp(mod, "control")) {
135 modifiers |= SP_SHORTCUT_CONTROL_MASK;
136 } else if (!strcmp(mod, "shift")) {
137 modifiers |= SP_SHORTCUT_SHIFT_MASK;
138 } else if (!strcmp(mod, "alt")) {
139 modifiers |= SP_SHORTCUT_ALT_MASK;
140 } else {
141 g_warning("Unknown modifier %s for %s", mod, verb_name);
142 }
143 g_free(mod);
144 iter += length;
145 if (*iter) iter++;
146 }
147 }
149 sp_shortcut_set(keyval | modifiers,
150 Inkscape::Verb::getbyid(verb_name),
151 is_primary);
152 }
154 GC::release(doc);
155 }
157 /**
158 * Adds a keyboard shortcut for the given verb.
159 * (Removes any existing binding for the given shortcut, including appropriately
160 * adjusting sp_shortcut_get_primary if necessary.)
161 *
162 * \param is_primary True iff this is the shortcut to be written in menu items or buttons.
163 *
164 * \post sp_shortcut_get_verb(shortcut) == verb.
165 * \post !is_primary or sp_shortcut_get_primary(verb) == shortcut.
166 */
167 static void
168 sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary)
169 {
170 if (!verbs) sp_shortcut_init();
172 Inkscape::Verb *old_verb = (Inkscape::Verb *)(g_hash_table_lookup(verbs, GINT_TO_POINTER(shortcut)));
173 g_hash_table_insert(verbs, GINT_TO_POINTER(shortcut), (gpointer)(verb));
175 /* Maintain the invariant that sp_shortcut_get_primary(v) returns either 0 or a valid shortcut for v. */
176 if (old_verb && old_verb != verb) {
177 unsigned int const old_primary = (unsigned int)GPOINTER_TO_INT(g_hash_table_lookup(primary_shortcuts, (gpointer)old_verb));
179 if (old_primary == shortcut) {
180 g_hash_table_insert(primary_shortcuts, (gpointer)old_verb, GINT_TO_POINTER(0));
181 }
182 }
184 if (is_primary) {
185 g_hash_table_insert(primary_shortcuts, (gpointer)(verb), GINT_TO_POINTER(shortcut));
186 }
187 }
189 Inkscape::Verb *
190 sp_shortcut_get_verb(unsigned int shortcut)
191 {
192 if (!verbs) sp_shortcut_init();
193 return (Inkscape::Verb *)(g_hash_table_lookup(verbs, GINT_TO_POINTER(shortcut)));
194 }
196 unsigned int
197 sp_shortcut_get_primary(Inkscape::Verb *verb)
198 {
199 if (!primary_shortcuts) sp_shortcut_init();
200 return (unsigned int)GPOINTER_TO_INT(g_hash_table_lookup(primary_shortcuts,
201 (gpointer)(verb)));
202 }
205 /*
206 Local Variables:
207 mode:c++
208 c-file-style:"stroustrup"
209 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
210 indent-tabs-mode:nil
211 fill-column:99
212 End:
213 */
214 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :