Code

fix parameter ranges, copyedit
[inkscape.git] / src / shortcuts.cpp
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>
26 #include <cstring>
27 #include <string>
29 #include <gdk/gdkkeys.h>
30 #include <gdk/gdkkeysyms.h>
32 #include "helper/action.h"
33 #include "io/sys.h"
34 #include "io/resource.h"
35 #include "shortcuts.h"
36 #include "verbs.h"
37 #include "xml/node-iterators.h"
38 #include "xml/repr.h"
40 using namespace Inkscape;
42 static void sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary);
43 static void try_shortcuts_file(char const *filename);
44 static void read_shortcuts_file(char const *filename);
46 /* Returns true if action was performed */
48 bool
49 sp_shortcut_invoke(unsigned int shortcut, Inkscape::UI::View::View *view)
50 {
51     Inkscape::Verb *verb = sp_shortcut_get_verb(shortcut);
52     if (verb) {
53         SPAction *action = verb->get_action(view);
54         if (action) {
55             sp_action_perform(action, NULL);
56             return true;
57         }
58     }
59     return false;
60 }
62 static GHashTable *verbs = NULL;
63 static GHashTable *primary_shortcuts = NULL;
65 static void
66 sp_shortcut_init()
67 {
68     using Inkscape::IO::Resource::get_path;
69     using Inkscape::IO::Resource::SYSTEM;
70     using Inkscape::IO::Resource::USER;
71     using Inkscape::IO::Resource::KEYS;
73     verbs = g_hash_table_new(NULL, NULL);
74     primary_shortcuts = g_hash_table_new(NULL, NULL);
76     read_shortcuts_file(get_path(SYSTEM, KEYS, "default.xml"));
77     try_shortcuts_file(get_path(USER, KEYS, "default.xml"));
78 }
80 static void try_shortcuts_file(char const *filename) {
81     using Inkscape::IO::file_test;
83     /* ah, if only we had an exception to catch... (permission, forgiveness) */
84     if (file_test(filename, G_FILE_TEST_EXISTS)) {
85         read_shortcuts_file(filename);
86     }
87 }
89 static void read_shortcuts_file(char const *filename) {
90     XML::Document *doc=sp_repr_read_file(filename, NULL);
91     if (!doc) {
92         g_warning("Unable to read keys file %s", filename);
93         return;
94     }
96     XML::Node const *root=doc->root();
97     g_return_if_fail(!strcmp(root->name(), "keys"));
98     XML::NodeConstSiblingIterator iter=root->firstChild();
99     for ( ; iter ; ++iter ) {
100         bool is_primary;
102         if (!strcmp(iter->name(), "bind")) {
103             is_primary = iter->attribute("display") && strcmp(iter->attribute("display"), "false") && strcmp(iter->attribute("display"), "0");
104         } else {
105             // some unknown element, do not complain
106             continue;
107         }
109         gchar const *verb_name=iter->attribute("action");
110         if (!verb_name) {
111             g_warning("Missing verb name (action= attribute) for shortcut");
112             continue;
113         }
115         Inkscape::Verb *verb=Inkscape::Verb::getbyid(verb_name);
116         if (!verb) {
117             g_warning("Unknown verb name: %s", verb_name);
118             continue;
119         }
121         gchar const *keyval_name=iter->attribute("key");
122         if (!keyval_name || !*keyval_name) {
123             // that's ok, it's just listed for reference without assignment, skip it
124             continue;
125         }
127         guint keyval=gdk_keyval_from_name(keyval_name);
128         if (keyval == GDK_VoidSymbol || keyval == 0) {
129             g_warning("Unknown keyval %s for %s", keyval_name, verb_name);
130             continue;
131         }
133         guint modifiers=0;
135         gchar const *modifiers_string=iter->attribute("modifiers");
136         if (modifiers_string) {
137             gchar const *iter=modifiers_string;
138             while (*iter) {
139                 size_t length=strcspn(iter, ",");
140                 gchar *mod=g_strndup(iter, length);
141                 if (!strcmp(mod, "Control") || !strcmp(mod, "Ctrl")) {
142                     modifiers |= SP_SHORTCUT_CONTROL_MASK;
143                 } else if (!strcmp(mod, "Shift")) {
144                     modifiers |= SP_SHORTCUT_SHIFT_MASK;
145                 } else if (!strcmp(mod, "Alt")) {
146                     modifiers |= SP_SHORTCUT_ALT_MASK;
147                 } else {
148                     g_warning("Unknown modifier %s for %s", mod, verb_name);
149                 }
150                 g_free(mod);
151                 iter += length;
152                 if (*iter) iter++;
153             }
154         }
156         sp_shortcut_set(keyval | modifiers, verb, is_primary);
157     }
159     GC::release(doc);
162 /**
163  * Adds a keyboard shortcut for the given verb.
164  * (Removes any existing binding for the given shortcut, including appropriately
165  * adjusting sp_shortcut_get_primary if necessary.)
166  *
167  * \param is_primary True iff this is the shortcut to be written in menu items or buttons.
168  *
169  * \post sp_shortcut_get_verb(shortcut) == verb.
170  * \post !is_primary or sp_shortcut_get_primary(verb) == shortcut.
171  */
172 static void
173 sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary)
175     if (!verbs) sp_shortcut_init();
177     Inkscape::Verb *old_verb = (Inkscape::Verb *)(g_hash_table_lookup(verbs, GINT_TO_POINTER(shortcut)));
178     g_hash_table_insert(verbs, GINT_TO_POINTER(shortcut), (gpointer)(verb));
180     /* Maintain the invariant that sp_shortcut_get_primary(v) returns either 0 or a valid shortcut for v. */
181     if (old_verb && old_verb != verb) {
182         unsigned int const old_primary = (unsigned int)GPOINTER_TO_INT(g_hash_table_lookup(primary_shortcuts, (gpointer)old_verb));
184         if (old_primary == shortcut) {
185             g_hash_table_insert(primary_shortcuts, (gpointer)old_verb, GINT_TO_POINTER(0));
186         }
187     }
189     if (is_primary) {
190         g_hash_table_insert(primary_shortcuts, (gpointer)(verb), GINT_TO_POINTER(shortcut));
191     }
194 Inkscape::Verb *
195 sp_shortcut_get_verb(unsigned int shortcut)
197     if (!verbs) sp_shortcut_init();
198     return (Inkscape::Verb *)(g_hash_table_lookup(verbs, GINT_TO_POINTER(shortcut)));
201 unsigned int
202 sp_shortcut_get_primary(Inkscape::Verb *verb)
204     if (!primary_shortcuts) sp_shortcut_init();
205     return (unsigned int)GPOINTER_TO_INT(g_hash_table_lookup(primary_shortcuts,
206                                                              (gpointer)(verb)));
210 /*
211   Local Variables:
212   mode:c++
213   c-file-style:"stroustrup"
214   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
215   indent-tabs-mode:nil
216   fill-column:99
217   End:
218 */
219 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :