fe5b42a1841721ce1f46bd0b57e7d8d5687e009a
[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         return kmi;
205 }
206
207 /* menu wrapper for WM_keymap_add_item */
208 wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap, char *idname, int type, int val, int modifier, int keymodifier)
209 {
210         wmKeyMapItem *kmi= WM_keymap_add_item(keymap, "WM_OT_call_menu", type, val, modifier, keymodifier);
211         RNA_string_set(kmi->ptr, "name", idname);
212         return kmi;
213 }
214
215 void WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi)
216 {
217         if(BLI_findindex(&keymap->items, kmi) != -1) {
218                 if(kmi->ptr) {
219                         WM_operator_properties_free(kmi->ptr);
220                         MEM_freeN(kmi->ptr);
221                 }
222                 BLI_freelinkN(&keymap->items, kmi);
223         }
224 }
225
226 /* ****************** storage in WM ************ */
227
228 /* name id's are for storing general or multiple keymaps, 
229    space/region ids are same as DNA_space_types.h */
230 /* gets free'd in wm.c */
231
232 wmKeyMap *WM_keymap_list_find(ListBase *lb, char *idname, int spaceid, int regionid)
233 {
234         wmKeyMap *km;
235
236         for(km= lb->first; km; km= km->next)
237                 if(km->spaceid==spaceid && km->regionid==regionid)
238                         if(0==strncmp(idname, km->idname, KMAP_MAX_NAME))
239                                 return km;
240         
241         return NULL;
242 }
243
244 wmKeyMap *WM_keymap_find(wmKeyConfig *keyconf, char *idname, int spaceid, int regionid)
245 {
246         wmKeyMap *km= WM_keymap_list_find(&keyconf->keymaps, idname, spaceid, regionid);
247         
248         if(km==NULL) {
249                 km= MEM_callocN(sizeof(struct wmKeyMap), "keymap list");
250                 BLI_strncpy(km->idname, idname, KMAP_MAX_NAME);
251                 km->spaceid= spaceid;
252                 km->regionid= regionid;
253                 BLI_addtail(&keyconf->keymaps, km);
254         }
255         
256         return km;
257 }
258
259 /* ****************** modal keymaps ************ */
260
261 /* modal maps get linked to a running operator, and filter the keys before sending to modal() callback */
262
263 wmKeyMap *WM_modalkeymap_add(wmKeyConfig *keyconf, char *idname, EnumPropertyItem *items)
264 {
265         wmKeyMap *km= WM_keymap_find(keyconf, idname, 0, 0);
266         km->flag |= KEYMAP_MODAL;
267         km->modal_items= items;
268         
269         return km;
270 }
271
272 wmKeyMap *WM_modalkeymap_get(wmKeyConfig *keyconf, char *idname)
273 {
274         wmKeyMap *km;
275         
276         for(km= keyconf->keymaps.first; km; km= km->next)
277                 if(km->flag & KEYMAP_MODAL)
278                         if(0==strncmp(idname, km->idname, KMAP_MAX_NAME))
279                                 break;
280         
281         return km;
282 }
283
284
285 wmKeyMapItem *WM_modalkeymap_add_item(wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value)
286 {
287         wmKeyMapItem *kmi= MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
288         
289         BLI_addtail(&km->items, kmi);
290         kmi->propvalue= value;
291         
292         keymap_event_set(kmi, type, val, modifier, keymodifier);
293
294         return kmi;
295 }
296
297 void WM_modalkeymap_assign(wmKeyMap *km, char *opname)
298 {
299         wmOperatorType *ot= WM_operatortype_find(opname, 0);
300         
301         if(ot)
302                 ot->modalkeymap= km;
303         else
304                 printf("error: modalkeymap_assign, unknown operator %s\n", opname);
305 }
306
307 /* ***************** get string from key events **************** */
308
309 const char *WM_key_event_string(short type)
310 {
311         const char *name= NULL;
312         if(RNA_enum_name(event_type_items, (int)type, &name))
313                 return name;
314         
315         return "";
316 }
317
318 char *WM_keymap_item_to_string(wmKeyMapItem *kmi, char *str, int len)
319 {
320         char buf[128];
321
322         buf[0]= 0;
323
324         if (kmi->shift == KM_ANY &&
325                 kmi->ctrl == KM_ANY &&
326                 kmi->alt == KM_ANY &&
327                 kmi->oskey == KM_ANY) {
328
329                 strcat(buf, "Any ");
330         } else {
331                 if(kmi->shift)
332                         strcat(buf, "Shift ");
333
334                 if(kmi->ctrl)
335                         strcat(buf, "Ctrl ");
336
337                 if(kmi->alt)
338                         strcat(buf, "Alt ");
339
340                 if(kmi->oskey)
341                         strcat(buf, "Cmd ");
342         }
343                 
344         if(kmi->keymodifier) {
345                 strcat(buf, WM_key_event_string(kmi->keymodifier));
346                 strcat(buf, " ");
347         }
348
349         strcat(buf, WM_key_event_string(kmi->type));
350         BLI_strncpy(str, buf, len);
351
352         return str;
353 }
354
355 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)
356 {
357         wmWindowManager *wm= CTX_wm_manager(C);
358         wmEventHandler *handler;
359         wmKeyMap *keymap;
360         wmKeyMapItem *kmi;
361
362         /* find keymap item in handlers */
363         for(handler=handlers->first; handler; handler=handler->next) {
364                 keymap= WM_keymap_active(wm, handler->keymap);
365
366                 if(keymap && (!keymap->poll || keymap->poll((bContext*)C))) {
367                         for(kmi=keymap->items.first; kmi; kmi=kmi->next) {
368                                 if(strcmp(kmi->idname, opname) == 0 && WM_key_event_string(kmi->type)[0]) {
369                                         if(compare_props) {
370                                                 if(kmi->ptr && IDP_EqualsProperties(properties, kmi->ptr->data)) {
371                                                         if(keymap_r) *keymap_r= keymap;
372                                                         return kmi;
373                                                 }
374                                         }
375                                         else {
376                                                 if(keymap_r) *keymap_r= keymap;
377                                                 return kmi;
378                                         }
379                                 }
380                         }
381                 }
382         }
383         
384         return NULL;
385 }
386
387 static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, const char *opname, int opcontext, IDProperty *properties, int compare_props, wmKeyMap **keymap_r)
388 {
389         wmWindow *win= CTX_wm_window(C);
390         ScrArea *sa= CTX_wm_area(C);
391         ARegion *ar= CTX_wm_region(C);
392         wmKeyMapItem *found= NULL;
393
394         /* look into multiple handler lists to find the item */
395         if(win)
396                 found= wm_keymap_item_find_handlers(C, &win->handlers, opname, opcontext, properties, compare_props, keymap_r);
397         
398
399         if(sa && found==NULL)
400                 found= wm_keymap_item_find_handlers(C, &sa->handlers, opname, opcontext, properties, compare_props, keymap_r);
401
402         if(found==NULL) {
403                 if(ELEM(opcontext, WM_OP_EXEC_REGION_WIN, WM_OP_INVOKE_REGION_WIN)) {
404                         if(sa) {
405                                 ARegion *ar= sa->regionbase.first;
406                                 for(; ar; ar= ar->next)
407                                         if(ar->regiontype==RGN_TYPE_WINDOW)
408                                                 break;
409
410                                 if(ar)
411                                         found= wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, compare_props, keymap_r);
412                         }
413                 }
414                 else {
415                         if(ar)
416                                 found= wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, compare_props, keymap_r);
417                 }
418         }
419         
420         return found;
421 }
422
423 static wmKeyMapItem *wm_keymap_item_find(const bContext *C, const char *opname, int opcontext, IDProperty *properties, wmKeyMap **keymap_r)
424 {
425         wmKeyMapItem *found= wm_keymap_item_find_props(C, opname, opcontext, properties, 1, keymap_r);
426
427         if(!found)
428                 found= wm_keymap_item_find_props(C, opname, opcontext, properties, 0, keymap_r);
429
430         return found;
431 }
432
433 char *WM_key_event_operator_string(const bContext *C, const char *opname, int opcontext, IDProperty *properties, char *str, int len)
434 {
435         wmKeyMapItem *kmi= wm_keymap_item_find(C, opname, opcontext, properties, NULL);
436         
437         if(kmi) {
438                 WM_keymap_item_to_string(kmi, str, len);
439                 return str;
440         }
441
442         return NULL;
443 }
444
445 /* ***************** user preferences ******************* */
446
447 int WM_keymap_user_init(wmWindowManager *wm, wmKeyMap *keymap)
448 {
449         wmKeyConfig *keyconf;
450         wmKeyMap *km;
451
452         if(!keymap)
453                 return 0;
454
455         /* init from user key config */
456         keyconf= wm_keyconfig_list_find(&wm->keyconfigs, U.keyconfigstr);
457         if(keyconf) {
458                 km= WM_keymap_list_find(&keyconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
459                 if(km) {
460                         keymap->poll= km->poll; /* lazy init */
461                         keymap->modal_items= km->modal_items;
462                         return 1;
463                 }
464         }
465
466         /* or from default */
467         km= WM_keymap_list_find(&wm->defaultconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
468         if(km) {
469                 keymap->poll= km->poll; /* lazy init */
470                 keymap->modal_items= km->modal_items;
471                 return 1;
472         }
473
474         return 0;
475 }
476
477 wmKeyMap *WM_keymap_active(wmWindowManager *wm, wmKeyMap *keymap)
478 {
479         wmKeyConfig *keyconf;
480         wmKeyMap *km;
481
482         if(!keymap)
483                 return NULL;
484         
485         /* first user defined keymaps */
486         km= WM_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
487         if(km) {
488                 km->poll= keymap->poll; /* lazy init */
489                 km->modal_items= keymap->modal_items;
490                 return km;
491         }
492         
493         /* then user key config */
494         keyconf= wm_keyconfig_list_find(&wm->keyconfigs, U.keyconfigstr);
495         if(keyconf) {
496                 km= WM_keymap_list_find(&keyconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
497                 if(km) {
498                         km->poll= keymap->poll; /* lazy init */
499                         km->modal_items= keymap->modal_items;
500                         return km;
501                 }
502         }
503
504         /* then use default */
505         km= WM_keymap_list_find(&wm->defaultconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
506         return km;
507 }
508
509 wmKeyMap *WM_keymap_copy_to_user(wmKeyMap *keymap)
510 {
511         wmKeyMap *usermap;
512         wmKeyMapItem *kmi;
513
514         usermap= WM_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
515
516         if(!usermap) {
517                 /* not saved yet, duplicate existing */
518                 usermap= MEM_dupallocN(keymap);
519                 usermap->modal_items= NULL;
520                 usermap->poll= NULL;
521                 usermap->flag |= KEYMAP_USER;
522
523                 BLI_addtail(&U.keymaps, usermap);
524         }
525         else {
526                 /* already saved, free items for re-copy */
527                 WM_keymap_free(usermap);
528         }
529
530         BLI_duplicatelist(&usermap->items, &keymap->items);
531
532         for(kmi=usermap->items.first; kmi; kmi=kmi->next) {
533                 if(kmi->properties) {
534                         kmi->ptr= MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
535                         WM_operator_properties_create(kmi->ptr, kmi->idname);
536
537                         kmi->properties= IDP_CopyProperty(kmi->properties);
538                         kmi->ptr->data= kmi->properties;
539                 }
540         }
541
542         for(kmi=keymap->items.first; kmi; kmi=kmi->next)
543                 kmi->flag &= ~KMI_EXPANDED;
544
545         return usermap;
546 }
547
548 void WM_keymap_restore_to_default(wmKeyMap *keymap)
549 {
550         wmKeyMap *usermap;
551
552         usermap= WM_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
553
554         if(usermap) {
555                 WM_keymap_free(usermap);
556                 BLI_freelinkN(&U.keymaps, usermap);
557         }
558 }
559
560 /* searches context and changes keymap item, if found */
561 void WM_key_event_operator_change(const bContext *C, const char *opname, int opcontext, IDProperty *properties, short key, short modifier)
562 {
563         wmWindowManager *wm= CTX_wm_manager(C);
564         wmKeyMap *keymap;
565         wmKeyMapItem *kmi;
566         
567         kmi= wm_keymap_item_find(C, opname, opcontext, properties, &keymap);
568
569         if(kmi) {
570                 /* if the existing one is in a default keymap, copy it
571                    to user preferences, and lookup again so we get a
572                    key map item from the user preferences we can modify */
573                 if(BLI_findindex(&wm->defaultconf->keymaps, keymap) >= 0) {
574                         WM_keymap_copy_to_user(keymap);
575                         kmi= wm_keymap_item_find(C, opname, opcontext, properties, NULL);
576                 }
577
578                 keymap_event_set(kmi, key, KM_PRESS, modifier, 0);
579         }
580 }
581