Line data Source code
1 : /* Copyright (C) 2012 by Ben Martin */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 :
28 : #include "gdraw.h"
29 : #include "gfile.h"
30 : #include "hotkeys.h"
31 : #include <locale.h>
32 : #include <string.h>
33 : #include <ustring.h>
34 : #include <errno.h>
35 : #include <unistd.h>
36 : #include "intl.h"
37 :
38 : #ifdef __MINGW32__
39 : #define fsync _commit
40 : #endif
41 :
42 : static int hotkeySystemCanUseMacCommand = 0;
43 :
44 : struct dlistnode* hotkeys = 0;
45 :
46 0 : static char* hotkeyGetWindowTypeString( Hotkey* hk )
47 : {
48 0 : char* pt = strchr(hk->action,'.');
49 0 : if( !pt )
50 0 : return 0;
51 0 : int len = pt - hk->action;
52 : static char buffer[HOTKEY_ACTION_MAX_SIZE+1];
53 0 : strncpy( buffer, hk->action, len );
54 0 : buffer[len] = '\0';
55 0 : return buffer;
56 : }
57 :
58 : /**
59 : * Does the hotkey 'hk' have the right window_type to trigger its
60 : * action on the window 'w'.
61 : */
62 0 : static int hotkeyHasMatchingWindowTypeString( char* windowType, Hotkey* hk ) {
63 0 : if( !windowType )
64 0 : return 0;
65 0 : char* pt = strchr(hk->action,'.');
66 0 : if( !pt )
67 0 : return 0;
68 :
69 0 : int len = pt - hk->action;
70 0 : if( strlen(windowType) < len )
71 0 : return 0;
72 0 : int rc = strncmp( windowType, hk->action, len );
73 0 : if( !rc )
74 0 : return 1;
75 0 : return 0;
76 : }
77 :
78 : /**
79 : * Does the hotkey 'hk' have the right window_type to trigger its
80 : * action on the window 'w'.
81 : */
82 : /*
83 : * Unused
84 : static int hotkeyHasMatchingWindowType( GWindow w, Hotkey* hk ) {
85 : char* windowType = GDrawGetWindowTypeName( w );
86 : return hotkeyHasMatchingWindowTypeString( windowType, hk );
87 : }
88 : */
89 :
90 : static struct dlistnodeExternal*
91 0 : hotkeyFindAllByStateAndKeysym( char* windowType, uint16 state, uint16 keysym ) {
92 :
93 0 : struct dlistnodeExternal* ret = 0;
94 0 : struct dlistnode* node = hotkeys;
95 0 : for( ; node; node=node->next ) {
96 0 : Hotkey* hk = (Hotkey*)node;
97 0 : if( hk->keysym ) {
98 0 : if( keysym == hk->keysym ) {
99 0 : if( state == hk->state ) {
100 0 : if( hotkeyHasMatchingWindowTypeString( windowType, hk ) ) {
101 0 : dlist_pushfront_external( (struct dlistnode **)&ret, hk );
102 : }
103 : }
104 : }
105 : }
106 : }
107 0 : return ret;
108 : }
109 :
110 :
111 0 : static Hotkey* hotkeyFindByStateAndKeysym( char* windowType, uint16 state, uint16 keysym ) {
112 :
113 0 : struct dlistnode* node = hotkeys;
114 0 : for( ; node; node=node->next ) {
115 0 : Hotkey* hk = (Hotkey*)node;
116 0 : if( hk->keysym ) {
117 0 : if( keysym == hk->keysym ) {
118 0 : if( state == hk->state ) {
119 0 : if( hotkeyHasMatchingWindowTypeString( windowType, hk ) ) {
120 0 : return hk;
121 : }
122 : }
123 : }
124 : }
125 : }
126 0 : return 0;
127 : }
128 :
129 :
130 0 : struct dlistnodeExternal* hotkeyFindAllByEvent( GWindow w, GEvent *event ) {
131 0 : if( event->u.chr.autorepeat )
132 0 : return 0;
133 0 : char* windowType = GDrawGetWindowTypeName( w );
134 0 : return hotkeyFindAllByStateAndKeysym( windowType,
135 0 : event->u.chr.state,
136 0 : event->u.chr.keysym );
137 : }
138 :
139 :
140 0 : Hotkey* hotkeyFindByEvent( GWindow w, GEvent *event ) {
141 :
142 0 : if( event->u.chr.autorepeat )
143 0 : return 0;
144 :
145 0 : char* windowType = GDrawGetWindowTypeName( w );
146 0 : return hotkeyFindByStateAndKeysym( windowType, event->u.chr.state, event->u.chr.keysym );
147 : }
148 :
149 : /**
150 : * Return the file name of the user defined hotkeys.
151 : * The return value must be freed by the caller.
152 : *
153 : * If extension is not null it will be postpended to the returned path.
154 : * This way you get to avoid dealing with appending to a string in c.
155 : */
156 40 : static char* getHotkeyFilename(char* extension) {
157 40 : char *ret = NULL;
158 : char buffer[1025];
159 40 : char *ffdir = getFontForgeUserDir(Config);
160 :
161 40 : if ( ffdir==NULL ) {
162 0 : fprintf(stderr,_("Cannot find your hotkey definition file!\n"));
163 0 : return NULL;
164 : }
165 40 : if( !extension )
166 40 : extension = "";
167 :
168 40 : sprintf(buffer,"%s/hotkeys%s", ffdir, extension);
169 40 : ret = copy(buffer);
170 40 : free(ffdir);
171 40 : return ret;
172 : }
173 :
174 : /**
175 : * Remove leading and trailing " " characters. N memory allocations
176 : * are performed, null is injected at the end of string and if there are leading
177 : * spaces the return value will be past them.
178 : */
179 0 : static char* trimspaces( char* line ) {
180 0 : while ( line[strlen(line)-1]==' ' )
181 0 : line[strlen(line)-1] = '\0';
182 0 : while( *line == ' ' )
183 0 : ++line;
184 0 : return line;
185 : }
186 :
187 0 : static Hotkey* hotkeySetFull( char* action, char* keydefinition, int append, int isUserDefined )
188 : {
189 0 : Hotkey* hk = calloc(1,sizeof(Hotkey));
190 0 : if ( hk==NULL ) return 0;
191 0 : strncpy( hk->action, action, HOTKEY_ACTION_MAX_SIZE );
192 0 : HotkeyParse( hk, keydefinition );
193 :
194 : // If we didn't get a hotkey (No Shortcut)
195 : // then we move along
196 0 : if( !hk->state && !hk->keysym ) {
197 0 : free(hk);
198 0 : return 0;
199 : }
200 :
201 : // If we already have a binding for that hotkey combination
202 : // for this window, forget the old one. One combo = One action.
203 0 : if( !append ) {
204 0 : Hotkey* oldkey = hotkeyFindByStateAndKeysym( hotkeyGetWindowTypeString(hk),
205 0 : hk->state, hk->keysym );
206 0 : if( oldkey ) {
207 0 : dlist_erase( &hotkeys, (struct dlistnode *)oldkey );
208 0 : free(oldkey);
209 : }
210 : }
211 :
212 0 : hk->isUserDefined = isUserDefined;
213 0 : dlist_pushfront( &hotkeys, (struct dlistnode *)hk );
214 0 : return hk;
215 : }
216 0 : Hotkey* hotkeySet( char* action, char* keydefinition, int append )
217 : {
218 0 : int isUserDefined = 1;
219 0 : return hotkeySetFull( action, keydefinition, append, isUserDefined );
220 : }
221 :
222 :
223 :
224 : /**
225 : * Load all the hotkeys from the file at filename, marking them as userdefined
226 : * if isUserDefined is set.
227 : */
228 120 : static void loadHotkeysFromFile( const char* filename, int isUserDefined, int warnIfNotFound )
229 : {
230 : char line[1100];
231 120 : FILE* f = fopen(filename,"r");
232 120 : if( !f ) {
233 120 : if( warnIfNotFound ) {
234 40 : fprintf(stderr,_("Failed to open hotkey definition file: %s\n"), filename );
235 : }
236 240 : return;
237 : }
238 :
239 0 : while ( fgets(line,sizeof(line),f)!=NULL ) {
240 0 : int append = 0;
241 :
242 0 : if ( *line=='#' )
243 0 : continue;
244 0 : char* pt = strchr(line,':');
245 0 : if ( pt==NULL )
246 0 : continue;
247 0 : *pt = '\0';
248 0 : char* keydefinition = pt+1;
249 0 : chomp( keydefinition );
250 0 : keydefinition = trimspaces( keydefinition );
251 0 : char* action = line;
252 0 : if( line[0] == '+' ) {
253 0 : append = 1;
254 0 : action++;
255 : }
256 :
257 0 : hotkeySetFull( action, keydefinition, append, isUserDefined );
258 : }
259 0 : fclose(f);
260 : }
261 :
262 : /**
263 : * Load all the default hotkeys for this locale and then the users
264 : * ~/.Fontforge/hotkeys.
265 : */
266 40 : void hotkeysLoad()
267 : {
268 : char localefn[PATH_MAX+1];
269 40 : char* p = 0;
270 40 : char* sharedir = getShareDir();
271 :
272 40 : snprintf(localefn,PATH_MAX,"%s/hotkeys/default", sharedir );
273 40 : loadHotkeysFromFile( localefn, false, true );
274 :
275 : // FUTURE: perhaps find out how to convert en_AU.UTF-8 that setlocale()
276 : // gives to its fallback of en_GB. There are likely to be a bunch of other
277 : // languages which are similar but have specific locales
278 40 : char* currentlocale = copy(setlocale(LC_MESSAGES, 0));
279 40 : snprintf(localefn,PATH_MAX,"%s/hotkeys/%s", sharedir, currentlocale);
280 40 : loadHotkeysFromFile( localefn, false, false );
281 80 : while((p = strrchr( currentlocale, '.' ))) {
282 0 : *p = '\0';
283 0 : snprintf(localefn,PATH_MAX,"%s/hotkeys/%s", sharedir, currentlocale);
284 0 : loadHotkeysFromFile( localefn, false, false );
285 : }
286 80 : while((p = strrchr( currentlocale, '_' ))) {
287 0 : *p = '\0';
288 0 : snprintf(localefn,PATH_MAX,"%s/hotkeys/%s", sharedir, currentlocale);
289 0 : loadHotkeysFromFile( localefn, false, false );
290 : }
291 40 : free(currentlocale);
292 :
293 40 : char* fn = getHotkeyFilename(0);
294 40 : if( !fn ) {
295 40 : return;
296 : }
297 40 : loadHotkeysFromFile( fn, true, false );
298 40 : free(fn);
299 : }
300 :
301 0 : static void hotkeysSaveCallback(Hotkey* hk,FILE* f) {
302 0 : if( hk->isUserDefined ) {
303 0 : fprintf( f, "%s:%s\n", hk->action, hk->text );
304 : }
305 0 : }
306 :
307 : /**
308 : * Save all the user defined hotkeys back to the users
309 : * ~/.Fontforge/hotkeys file.
310 : */
311 0 : void hotkeysSave() {
312 0 : char* fn = getHotkeyFilename(".new");
313 0 : if( !fn ) {
314 0 : return;
315 : }
316 0 : FILE* f = fopen(fn,"w");
317 0 : if( !f ) {
318 0 : free(fn);
319 0 : fprintf(stderr,_("Failed to open your hotkey definition file for updates.\n"));
320 0 : return;
321 : }
322 :
323 0 : dlist_foreach_reverse_udata( &hotkeys,
324 : (dlist_foreach_udata_func_type)hotkeysSaveCallback, f );
325 0 : fsync(fileno(f));
326 0 : fclose(f);
327 :
328 : //
329 : // Atomic rename of new over the old.
330 : //
331 0 : char* newpath = getHotkeyFilename(0);
332 : #ifdef __MINGW32__
333 : //Atomic rename doesn't exist on Windows.
334 : unlink(newpath);
335 : #endif
336 0 : int rc = rename( fn, newpath );
337 0 : int e = errno;
338 0 : free(fn);
339 0 : free(newpath);
340 0 : if( rc == -1 ) {
341 0 : fprintf(stderr,_("Failed to rename the new hotkeys file over your old one!\n"));
342 0 : fprintf(stderr,_("Reason:%s\n"), strerror(e));
343 : }
344 : }
345 :
346 :
347 0 : char* hotkeysGetKeyDescriptionFromAction( char* action ) {
348 0 : struct dlistnode* node = hotkeys;
349 0 : for( ; node; node=node->next ) {
350 0 : Hotkey* hk = (Hotkey*)node;
351 0 : if(!strcmp(hk->action,action)) {
352 0 : return hk->text;
353 : }
354 : }
355 0 : return 0;
356 : }
357 :
358 :
359 : /**
360 : * Find a hotkey by the action. This is useful for menus to find out
361 : * what hotkey is currently bound to them. So if the user changes
362 : * file/open to be alt+j then the menu can adjust the hotkey is is
363 : * displaying to show the user what key they have assigned.
364 : */
365 0 : static Hotkey* hotkeyFindByAction( char* action ) {
366 0 : struct dlistnode* node = hotkeys;
367 0 : for( ; node; node=node->next ) {
368 0 : Hotkey* hk = (Hotkey*)node;
369 0 : if(!strcmp(hk->action,action)) {
370 0 : return hk;
371 : }
372 : }
373 0 : return 0;
374 : }
375 :
376 0 : Hotkey* isImmediateKey( GWindow w, char* path, GEvent *event )
377 : {
378 0 : char* wt = GDrawGetWindowTypeName(w);
379 0 : if(!wt)
380 0 : return 0;
381 :
382 0 : char* subMenuName = "_ImmediateKeys";
383 : char line[PATH_MAX+1];
384 0 : snprintf(line,PATH_MAX,"%s.%s.%s",wt, subMenuName, path );
385 : // printf("line:%s\n",line);
386 0 : Hotkey* hk = hotkeyFindByAction( line );
387 0 : if( !hk )
388 0 : return 0;
389 0 : if( !hk->action )
390 0 : return 0;
391 :
392 0 : if( event->u.chr.keysym == hk->keysym )
393 0 : return hk;
394 :
395 0 : return 0;
396 : }
397 :
398 0 : Hotkey* hotkeyFindByMenuPathInSubMenu( GWindow w, char* subMenuName, char* path ) {
399 :
400 0 : char* wt = GDrawGetWindowTypeName(w);
401 0 : if(!wt)
402 0 : return 0;
403 :
404 : char line[PATH_MAX+1];
405 0 : snprintf(line,PATH_MAX,"%s.%s%s%s",wt, subMenuName, ".Menu.", path );
406 : // printf("line:%s\n",line);
407 0 : return(hotkeyFindByAction(line));
408 : }
409 :
410 0 : Hotkey* hotkeyFindByMenuPath( GWindow w, char* path ) {
411 :
412 0 : char* wt = GDrawGetWindowTypeName(w);
413 0 : if(!wt)
414 0 : return 0;
415 :
416 : char line[PATH_MAX+1];
417 0 : snprintf(line,PATH_MAX,"%s%s%s",wt, ".Menu.", path );
418 0 : return(hotkeyFindByAction(line));
419 : }
420 :
421 :
422 0 : char* hotkeyTextToMacModifiers( char* keydesc )
423 : {
424 0 : keydesc = copy( keydesc );
425 0 : keydesc = str_replace_all( keydesc, "Ctl", "⌘", 1 );
426 0 : keydesc = str_replace_all( keydesc, "Command", "⌘", 1 );
427 0 : keydesc = str_replace_all( keydesc, "Cmd", "⌘", 1 );
428 0 : keydesc = str_replace_all( keydesc, "Shft", "⇧", 1 );
429 0 : keydesc = str_replace_all( keydesc, "Alt", "⎇", 1 );
430 0 : keydesc = str_replace_all( keydesc, "+", "", 1 );
431 0 : return keydesc;
432 : }
433 :
434 :
435 0 : char* hotkeyTextWithoutModifiers( char* hktext ) {
436 0 : if( !strcmp( hktext, "no shortcut" )
437 0 : || !strcmp( hktext, "No shortcut" )
438 0 : || !strcmp( hktext, "No Shortcut" ))
439 0 : return "";
440 :
441 0 : char* p = strrchr( hktext, '+' );
442 0 : if( !p )
443 0 : return hktext;
444 :
445 : //
446 : // Handle Control++ by moving back over the last plus
447 : //
448 0 : if( p > hktext )
449 : {
450 0 : char* pp = p - 1;
451 0 : if( *pp == '+' )
452 0 : --p;
453 : }
454 0 : return p+1;
455 : }
456 :
457 :
458 0 : void hotkeySystemSetCanUseMacCommand( int v )
459 : {
460 0 : hotkeySystemCanUseMacCommand = v;
461 0 : }
462 :
463 0 : int hotkeySystemGetCanUseMacCommand()
464 : {
465 0 : return hotkeySystemCanUseMacCommand;
466 : }
|