keymap editor
[blender.git] / source / blender / windowmanager / intern / wm_keymap.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2007 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30
31 #include "DNA_screen_types.h"
32 #include "DNA_userdef_types.h"
33 #include "DNA_windowmanager_types.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38
39 #include "BKE_blender.h"
40 #include "BKE_context.h"
41 #include "BKE_idprop.h"
42 #include "BKE_library.h"
43 #include "BKE_main.h"
44 #include "BKE_utildefines.h"
45
46 #include "RNA_access.h"
47 #include "RNA_types.h"
48 #include "RNA_enum_types.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52 #include "wm_window.h"
53 #include "wm_event_system.h"
54 #include "wm_event_types.h"
55
56 /* ********************* key config ***********************/
57
58 void WM_keymap_properties_reset(wmKeyMapItem *kmi)
59 {
60         WM_operator_properties_free(kmi->ptr);
61         MEM_freeN(kmi->ptr);
62
63         kmi->ptr = NULL;
64         kmi->properties = NULL;
65
66         WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
67 }
68
69 static void keymap_properties_set(wmKeyMapItem *kmi)
70 {
71         WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
72 }
73
74 wmKeyConfig *WM_keyconfig_add(wmWindowManager *wm, char *idname)
75 {
76         wmKeyConfig *keyconf;
77         
78         keyconf= MEM_callocN(sizeof(wmKeyConfig), "wmKeyConfig");
79         BLI_strncpy(keyconf->idname, idname, sizeof(keyconf->idname));
80         BLI_addtail(&wm->keyconfigs, keyconf);
81
82         return keyconf;
83 }
84
85 void WM_keyconfig_free(wmKeyConfig *keyconf)
86 {
87         wmKeyMap *km;
88
89         while((km= keyconf->keymaps.first)) {
90                 WM_keymap_free(km);
91                 BLI_freelinkN(&keyconf->keymaps, km);
92         }
93
94         MEM_freeN(keyconf);
95 }
96
97 void WM_keyconfig_userdef(wmWindowManager *wm)
98 {
99         wmKeyMap *km;
100         wmKeyMapItem *kmi;
101
102         for(km=U.keymaps.first; km; km=km->next) {
103                 /* modal keymaps don't have operator properties */
104                 if ((km->flag & KEYMAP_MODAL) == 0) {
105                         for(kmi=km->items.first; kmi; kmi=kmi->next) {
106                                 keymap_properties_set(kmi);
107                         }
108                 }
109         }
110 }
111
112 static wmKeyConfig *wm_keyconfig_list_find(ListBase *lb, char *idname)
113 {
114         wmKeyConfig *kc;
115
116         for(kc= lb->first; kc; kc= kc->next)
117                 if(0==strncmp(idname, kc->idname, KMAP_MAX_NAME))
118                         return kc;
119         
120         return NULL;
121 }
122
123 /* ************************ free ************************* */
124
125 void WM_keymap_free(wmKeyMap *keymap)
126 {
127         wmKeyMapItem *kmi;
128
129         for(kmi=keymap->items.first; kmi; kmi=kmi->next) {
130                 if(kmi->ptr) {
131                         WM_operator_properties_free(kmi->ptr);
132                         MEM_freeN(kmi->ptr);
133                 }
134         }
135
136         BLI_freelistN(&keymap->items);
137 }
138
139 /* ***************** generic call, exported **************** */
140
141 static void keymap_event_set(wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier)
142 {
143         kmi->type= type;
144         kmi->val= val;
145         kmi->keymodifier= keymodifier;
146         
147         if(modifier == KM_ANY) {
148                 kmi->shift= kmi->ctrl= kmi->alt= kmi->oskey= KM_ANY;
149         }
150         else {
151                 
152                 kmi->shift= kmi->ctrl= kmi->alt= kmi->oskey= 0;
153                 
154                 /* defines? */
155                 if(modifier & KM_SHIFT)
156                         kmi->shift= 1;
157                 else if(modifier & KM_SHIFT2)
158                         kmi->shift= 2;
159                 if(modifier & KM_CTRL)
160                         kmi->ctrl= 1;
161                 else if(modifier & KM_CTRL2)
162                         kmi->ctrl= 2;
163                 if(modifier & KM_ALT)
164                         kmi->alt= 1;
165                 else if(modifier & KM_ALT2)
166                         kmi->alt= 2;
167                 if(modifier & KM_OSKEY)
168                         kmi->oskey= 1;
169                 else if(modifier & KM_OSKEY2)
170                         kmi->oskey= 2;  
171         }
172 }
173
174 /* if item was added, then bail out */
175 wmKeyMapItem *WM_keymap_verify_item(wmKeyMap *keymap, char *idname, int type, int val, int modifier, int keymodifier)
176 {
177         wmKeyMapItem *kmi;
178         
179         for(kmi= keymap->items.first; kmi; kmi= kmi->next)
180                 if(strncmp(kmi->idname, idname, OP_MAX_TYPENAME)==0)
181                         break;
182         if(kmi==NULL) {
183                 kmi= MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
184                 
185                 BLI_addtail(&keymap->items, kmi);
186                 BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME);
187                 
188                 keymap_event_set(kmi, type, val, modifier, keymodifier);
189                 keymap_properties_set(kmi);
190         }
191         return kmi;
192 }
193
194 /* always add item */
195 wmKeyMapItem *WM_keymap_add_item(wmKeyMap *keymap, char *idname, int type, int val, int modifier, int keymodifier)
196 {
197         wmKeyMapItem *kmi= MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
198         
199         BLI_addtail(&keymap->items, kmi);
200         BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME);
201
202         keymap_event_set(kmi, type, val, modifier, keymodifier);
203         keymap_properties_set(kmi);
204
205         if ((keymap->flag & KEYMAP_USER) == 0) {
206                 keymap->kmi_id++;
207                 kmi->id = keymap->kmi_id;
208         }
209
210         return kmi;
211 }
212
213 /* menu wrapper for WM_keymap_add_item */
214 wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap, char *idname, int type, int val, int modifier, int keymodifier)
215 {
216         wmKeyMapItem *kmi= WM_keymap_add_item(keymap, "WM_OT_call_menu", type, val, modifier, keymodifier);
217         RNA_string_set(kmi->ptr, "name", idname);
218         return kmi;
219 }
220
221 void WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi)
222 {
223         if(BLI_findindex(&keymap->items, kmi) != -1) {
224                 if(kmi->ptr) {
225                         WM_operator_properties_free(kmi->ptr);
226                         MEM_freeN(kmi->ptr);
227                 }
228                 BLI_freelinkN(&keymap->items, kmi);
229         }
230 }
231
232 /* ****************** storage in WM ************ */
233
234 /* name id's are for storing general or multiple keymaps, 
235    space/region ids are same as DNA_space_types.h */
236 /* gets free'd in wm.c */
237
238 wmKeyMap *WM_keymap_list_find(ListBase *lb, char *idname, int spaceid, int regionid)
239 {
240         wmKeyMap *km;
241
242         for(km= lb->first; km; km= km->next)
243                 if(km->spaceid==spaceid && km->regionid==regionid)
244                         if(0==strncmp(idname, km->idname, KMAP_MAX_NAME))
245                                 return km;
246         
247         return NULL;
248 }
249
250 wmKeyMap *WM_keymap_find(wmKeyConfig *keyconf, char *idname, int spaceid, int regionid)
251 {
252         wmKeyMap *km= WM_keymap_list_find(&keyconf->keymaps, idname, spaceid, regionid);
253         
254         if(km==NULL) {
255                 km= MEM_callocN(sizeof(struct wmKeyMap), "keymap list");
256                 BLI_strncpy(km->idname, idname, KMAP_MAX_NAME);
257                 km->spaceid= spaceid;
258                 km->regionid= regionid;
259                 BLI_addtail(&keyconf->keymaps, km);
260         }
261         
262         return km;
263 }
264
265 /* ****************** modal keymaps ************ */
266
267 /* modal maps get linked to a running operator, and filter the keys before sending to modal() callback */
268
269 wmKeyMap *WM_modalkeymap_add(wmKeyConfig *keyconf, char *idname, EnumPropertyItem *items)
270 {
271         wmKeyMap *km= WM_keymap_find(keyconf, idname, 0, 0);
272         km->flag |= KEYMAP_MODAL;
273         km->modal_items= items;
274         
275         return km;
276 }
277
278 wmKeyMap *WM_modalkeymap_get(wmKeyConfig *keyconf, char *idname)
279 {
280         wmKeyMap *km;
281         
282         for(km= keyconf->keymaps.first; km; km= km->next)
283                 if(km->flag & KEYMAP_MODAL)
284                         if(0==strncmp(idname, km->idname, KMAP_MAX_NAME))
285                                 break;
286         
287         return km;
288 }
289
290
291 wmKeyMapItem *WM_modalkeymap_add_item(wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value)
292 {
293         wmKeyMapItem *kmi= MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
294         
295         BLI_addtail(&km->items, kmi);
296         kmi->propvalue= value;
297         
298         keymap_event_set(kmi, type, val, modifier, keymodifier);
299
300         if ((km->flag & KEYMAP_USER) == 0) {
301                 km->kmi_id++;
302                 kmi->id = km->kmi_id;
303         }
304
305         return kmi;
306 }
307
308 void WM_modalkeymap_assign(wmKeyMap *km, char *opname)
309 {
310         wmOperatorType *ot= WM_operatortype_find(opname, 0);
311         
312         if(ot)
313                 ot->modalkeymap= km;
314         else
315                 printf("error: modalkeymap_assign, unknown operator %s\n", opname);
316 }
317
318 /* ***************** get string from key events **************** */
319
320 const char *WM_key_event_string(short type)
321 {
322         const char *name= NULL;
323         if(RNA_enum_name(event_type_items, (int)type, &name))
324                 return name;
325         
326         return "";
327 }
328
329 char *WM_keymap_item_to_string(wmKeyMapItem *kmi, char *str, int len)
330 {
331         char buf[128];
332
333         buf[0]= 0;
334
335         if (kmi->shift == KM_ANY &&
336                 kmi->ctrl == KM_ANY &&
337                 kmi->alt == KM_ANY &&
338                 kmi->oskey == KM_ANY) {
339
340                 strcat(buf, "Any ");
341         } else {
342                 if(kmi->shift)
343                         strcat(buf, "Shift ");
344
345                 if(kmi->ctrl)
346                         strcat(buf, "Ctrl ");
347
348                 if(kmi->alt)
349                         strcat(buf, "Alt ");
350
351                 if(kmi->oskey)
352                         strcat(buf, "Cmd ");
353         }
354                 
355         if(kmi->keymodifier) {
356                 strcat(buf, WM_key_event_string(kmi->keymodifier));
357                 strcat(buf, " ");
358         }
359
360         strcat(buf, WM_key_event_string(kmi->type));
361         BLI_strncpy(str, buf, len);
362
363         return str;
364 }
365
366 static wmKeyMapItem *wm_keymap_item_find_handlers(const bContext *C, ListBase *handlers, const char *opname, int opcontext, IDProperty *properties, int compare_props, wmKeyMap **keymap_r)
367 {
368         wmWindowManager *wm= CTX_wm_manager(C);
369         wmEventHandler *handler;
370         wmKeyMap *keymap;
371         wmKeyMapItem *kmi;
372
373         /* find keymap item in handlers */
374         for(handler=handlers->first; handler; handler=handler->next) {
375                 keymap= WM_keymap_active(wm, handler->keymap);
376
377                 if(keymap && (!keymap->poll || keymap->poll((bContext*)C))) {
378                         for(kmi=keymap->items.first; kmi; kmi=kmi->next) {
379                                 if(strcmp(kmi->idname, opname) == 0 && WM_key_event_string(kmi->type)[0]) {
380                                         if(compare_props) {
381                                                 if(kmi->ptr && IDP_EqualsProperties(properties, kmi->ptr->data)) {
382                                                         if(keymap_r) *keymap_r= keymap;
383                                                         return kmi;
384                                                 }
385                                         }
386                                         else {
387                                                 if(keymap_r) *keymap_r= keymap;
388                                                 return kmi;
389                                         }
390                                 }
391                         }
392                 }
393         }
394         
395         return NULL;
396 }
397
398 static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, const char *opname, int opcontext, IDProperty *properties, int compare_props, wmKeyMap **keymap_r)
399 {
400         wmWindow *win= CTX_wm_window(C);
401         ScrArea *sa= CTX_wm_area(C);
402         ARegion *ar= CTX_wm_region(C);
403         wmKeyMapItem *found= NULL;
404
405         /* look into multiple handler lists to find the item */
406         if(win)
407                 found= wm_keymap_item_find_handlers(C, &win->handlers, opname, opcontext, properties, compare_props, keymap_r);
408         
409
410         if(sa && found==NULL)
411                 found= wm_keymap_item_find_handlers(C, &sa->handlers, opname, opcontext, properties, compare_props, keymap_r);
412
413         if(found==NULL) {
414                 if(ELEM(opcontext, WM_OP_EXEC_REGION_WIN, WM_OP_INVOKE_REGION_WIN)) {
415                         if(sa) {
416                                 ARegion *ar= sa->regionbase.first;
417                                 for(; ar; ar= ar->next)
418                                         if(ar->regiontype==RGN_TYPE_WINDOW)
419                                                 break;
420
421                                 if(ar)
422                                         found= wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, compare_props, keymap_r);
423                         }
424                 }
425                 else {
426                         if(ar)
427                                 found= wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, compare_props, keymap_r);
428                 }
429         }
430         
431         return found;
432 }
433
434 static wmKeyMapItem *wm_keymap_item_find(const bContext *C, const char *opname, int opcontext, IDProperty *properties, wmKeyMap **keymap_r)
435 {
436         wmKeyMapItem *found= wm_keymap_item_find_props(C, opname, opcontext, properties, 1, keymap_r);
437
438         if(!found)
439                 found= wm_keymap_item_find_props(C, opname, opcontext, properties, 0, keymap_r);
440
441         return found;
442 }
443
444 char *WM_key_event_operator_string(const bContext *C, const char *opname, int opcontext, IDProperty *properties, char *str, int len)
445 {
446         wmKeyMapItem *kmi= wm_keymap_item_find(C, opname, opcontext, properties, NULL);
447         
448         if(kmi) {
449                 WM_keymap_item_to_string(kmi, str, len);
450                 return str;
451         }
452
453         return NULL;
454 }
455
456 /* ***************** user preferences ******************* */
457
458 int WM_keymap_user_init(wmWindowManager *wm, wmKeyMap *keymap)
459 {
460         wmKeyConfig *keyconf;
461         wmKeyMap *km;
462
463         if(!keymap)
464                 return 0;
465
466         /* init from user key config */
467         keyconf= wm_keyconfig_list_find(&wm->keyconfigs, U.keyconfigstr);
468         if(keyconf) {
469                 km= WM_keymap_list_find(&keyconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
470                 if(km) {
471                         keymap->poll= km->poll; /* lazy init */
472                         keymap->modal_items= km->modal_items;
473                         return 1;
474                 }
475         }
476
477         /* or from default */
478         km= WM_keymap_list_find(&wm->defaultconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
479         if(km) {
480                 keymap->poll= km->poll; /* lazy init */
481                 keymap->modal_items= km->modal_items;
482                 return 1;
483         }
484
485         return 0;
486 }
487
488 wmKeyMap *WM_keymap_active(wmWindowManager *wm, wmKeyMap *keymap)
489 {
490         wmKeyConfig *keyconf;
491         wmKeyMap *km;
492
493         if(!keymap)
494                 return NULL;
495         
496         /* first user defined keymaps */
497         km= WM_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
498         if(km) {
499                 km->poll= keymap->poll; /* lazy init */
500                 km->modal_items= keymap->modal_items;
501                 return km;
502         }
503         
504         /* then user key config */
505         keyconf= wm_keyconfig_list_find(&wm->keyconfigs, U.keyconfigstr);
506         if(keyconf) {
507                 km= WM_keymap_list_find(&keyconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
508                 if(km) {
509                         km->poll= keymap->poll; /* lazy init */
510                         km->modal_items= keymap->modal_items;
511                         return km;
512                 }
513         }
514
515         /* then use default */
516         km= WM_keymap_list_find(&wm->defaultconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
517         return km;
518 }
519
520 wmKeyMap *WM_keymap_copy_to_user(wmKeyMap *keymap)
521 {
522         wmKeyMap *usermap;
523         wmKeyMapItem *kmi;
524
525         usermap= WM_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
526
527         if(!usermap) {
528                 /* not saved yet, duplicate existing */
529                 usermap= MEM_dupallocN(keymap);
530                 usermap->modal_items= NULL;
531                 usermap->poll= NULL;
532                 usermap->flag |= KEYMAP_USER;
533
534                 BLI_addtail(&U.keymaps, usermap);
535         }
536         else {
537                 /* already saved, free items for re-copy */
538                 WM_keymap_free(usermap);
539         }
540
541         BLI_duplicatelist(&usermap->items, &keymap->items);
542
543         for(kmi=usermap->items.first; kmi; kmi=kmi->next) {
544                 if(kmi->properties) {
545                         kmi->ptr= MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
546                         WM_operator_properties_create(kmi->ptr, kmi->idname);
547
548                         kmi->properties= IDP_CopyProperty(kmi->properties);
549                         kmi->ptr->data= kmi->properties;
550                 }
551         }
552
553         for(kmi=keymap->items.first; kmi; kmi=kmi->next)
554                 kmi->flag &= ~KMI_EXPANDED;
555
556         return usermap;
557 }
558
559 void WM_keymap_restore_item_to_default(bContext *C, wmKeyMap *keymap, wmKeyMapItem *kmi)
560 {
561         wmWindowManager *wm = CTX_wm_manager(C);
562         wmKeyConfig *keyconf;
563         wmKeyMap *km = NULL;
564
565         /* look in user key config */
566         keyconf= wm_keyconfig_list_find(&wm->keyconfigs, U.keyconfigstr);
567         if(keyconf) {
568                 km= WM_keymap_list_find(&keyconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
569         }
570
571         if (!km) {
572                 /* or from default */
573                 km= WM_keymap_list_find(&wm->defaultconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
574         }
575
576         if (km) {
577                 wmKeyMapItem *orig;
578
579                 for (orig = km->items.first; orig; orig = orig->next) {
580                         if (orig->id == kmi->id)
581                                 break;
582                 }
583
584                 if (orig) {
585                         if(strcmp(orig->idname, kmi->idname) != 0) {
586                                 BLI_strncpy(kmi->idname, orig->idname, sizeof(kmi->idname));
587
588                                 WM_keymap_properties_reset(kmi);
589                         }
590                         kmi->properties= IDP_CopyProperty(orig->properties);
591                         kmi->ptr->data= kmi->properties;
592
593                         kmi->propvalue = orig->propvalue;
594                         kmi->type = orig->type;
595                         kmi->val = orig->val;
596                         kmi->shift = orig->shift;
597                         kmi->ctrl = orig->ctrl;
598                         kmi->alt = orig->alt;
599                         kmi->oskey = orig->oskey;
600                         kmi->keymodifier = orig->keymodifier;
601                         kmi->maptype = orig->maptype;
602
603                 }
604
605         }
606 }
607
608 void WM_keymap_restore_to_default(wmKeyMap *keymap)
609 {
610         wmKeyMap *usermap;
611
612         usermap= WM_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
613
614         if(usermap) {
615                 WM_keymap_free(usermap);
616                 BLI_freelinkN(&U.keymaps, usermap);
617         }
618 }
619
620 /* searches context and changes keymap item, if found */
621 void WM_key_event_operator_change(const bContext *C, const char *opname, int opcontext, IDProperty *properties, short key, short modifier)
622 {
623         wmWindowManager *wm= CTX_wm_manager(C);
624         wmKeyMap *keymap;
625         wmKeyMapItem *kmi;
626         
627         kmi= wm_keymap_item_find(C, opname, opcontext, properties, &keymap);
628
629         if(kmi) {
630                 /* if the existing one is in a default keymap, copy it
631                    to user preferences, and lookup again so we get a
632                    key map item from the user preferences we can modify */
633                 if(BLI_findindex(&wm->defaultconf->keymaps, keymap) >= 0) {
634                         WM_keymap_copy_to_user(keymap);
635                         kmi= wm_keymap_item_find(C, opname, opcontext, properties, NULL);
636                 }
637
638                 keymap_event_set(kmi, key, KM_PRESS, modifier, 0);
639         }
640 }
641