Fixing keymap export:
[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 static void keymap_properties_set(wmKeyMapItem *kmi)
59 {
60         WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
61 }
62
63 wmKeyConfig *WM_keyconfig_add(wmWindowManager *wm, char *idname)
64 {
65         wmKeyConfig *keyconf;
66         
67         keyconf= MEM_callocN(sizeof(wmKeyConfig), "wmKeyConfig");
68         BLI_strncpy(keyconf->idname, idname, sizeof(keyconf->idname));
69         BLI_addtail(&wm->keyconfigs, keyconf);
70
71         return keyconf;
72 }
73
74 void WM_keyconfig_free(wmKeyConfig *keyconf)
75 {
76         wmKeyMap *km;
77
78         while((km= keyconf->keymaps.first)) {
79                 WM_keymap_free(km);
80                 BLI_freelinkN(&keyconf->keymaps, km);
81         }
82
83         MEM_freeN(keyconf);
84 }
85
86 void WM_keyconfig_userdef(wmWindowManager *wm)
87 {
88         wmKeyMap *km;
89         wmKeyMapItem *kmi;
90
91         for(km=U.keymaps.first; km; km=km->next) {
92                 /* modal keymaps don't have operator properties */
93                 if ((km->flag & KEYMAP_MODAL) == 0) {
94                         for(kmi=km->items.first; kmi; kmi=kmi->next) {
95                                 keymap_properties_set(kmi);
96                         }
97                 }
98         }
99 }
100
101 static wmKeyConfig *wm_keyconfig_list_find(ListBase *lb, char *idname)
102 {
103         wmKeyConfig *kc;
104
105         for(kc= lb->first; kc; kc= kc->next)
106                 if(0==strncmp(idname, kc->idname, KMAP_MAX_NAME))
107                         return kc;
108         
109         return NULL;
110 }
111
112 /* ************************ free ************************* */
113
114 void WM_keymap_free(wmKeyMap *keymap)
115 {
116         wmKeyMapItem *kmi;
117
118         for(kmi=keymap->items.first; kmi; kmi=kmi->next) {
119                 if(kmi->ptr) {
120                         WM_operator_properties_free(kmi->ptr);
121                         MEM_freeN(kmi->ptr);
122                 }
123         }
124
125         BLI_freelistN(&keymap->items);
126 }
127
128 /* ***************** generic call, exported **************** */
129
130 static void keymap_event_set(wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier)
131 {
132         kmi->type= type;
133         kmi->val= val;
134         kmi->keymodifier= keymodifier;
135         
136         if(modifier == KM_ANY) {
137                 kmi->shift= kmi->ctrl= kmi->alt= kmi->oskey= KM_ANY;
138         }
139         else {
140                 
141                 kmi->shift= kmi->ctrl= kmi->alt= kmi->oskey= 0;
142                 
143                 /* defines? */
144                 if(modifier & KM_SHIFT)
145                         kmi->shift= 1;
146                 else if(modifier & KM_SHIFT2)
147                         kmi->shift= 2;
148                 if(modifier & KM_CTRL)
149                         kmi->ctrl= 1;
150                 else if(modifier & KM_CTRL2)
151                         kmi->ctrl= 2;
152                 if(modifier & KM_ALT)
153                         kmi->alt= 1;
154                 else if(modifier & KM_ALT2)
155                         kmi->alt= 2;
156                 if(modifier & KM_OSKEY)
157                         kmi->oskey= 1;
158                 else if(modifier & KM_OSKEY2)
159                         kmi->oskey= 2;  
160         }
161 }
162
163 /* if item was added, then bail out */
164 wmKeyMapItem *WM_keymap_verify_item(wmKeyMap *keymap, char *idname, int type, int val, int modifier, int keymodifier)
165 {
166         wmKeyMapItem *kmi;
167         
168         for(kmi= keymap->items.first; kmi; kmi= kmi->next)
169                 if(strncmp(kmi->idname, idname, OP_MAX_TYPENAME)==0)
170                         break;
171         if(kmi==NULL) {
172                 kmi= MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
173                 
174                 BLI_addtail(&keymap->items, kmi);
175                 BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME);
176                 
177                 keymap_event_set(kmi, type, val, modifier, keymodifier);
178                 keymap_properties_set(kmi);
179         }
180         return kmi;
181 }
182
183 /* always add item */
184 wmKeyMapItem *WM_keymap_add_item(wmKeyMap *keymap, char *idname, int type, int val, int modifier, int keymodifier)
185 {
186         wmKeyMapItem *kmi= MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
187         
188         BLI_addtail(&keymap->items, kmi);
189         BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME);
190
191         keymap_event_set(kmi, type, val, modifier, keymodifier);
192         keymap_properties_set(kmi);
193         return kmi;
194 }
195
196 void WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi)
197 {
198         if(BLI_findindex(&keymap->items, kmi) != -1) {
199                 if(kmi->ptr) {
200                         WM_operator_properties_free(kmi->ptr);
201                         MEM_freeN(kmi->ptr);
202                 }
203                 BLI_freelinkN(&keymap->items, kmi);
204         }
205 }
206
207 /* ****************** storage in WM ************ */
208
209 /* name id's are for storing general or multiple keymaps, 
210    space/region ids are same as DNA_space_types.h */
211 /* gets free'd in wm.c */
212
213 static wmKeyMap *wm_keymap_list_find(ListBase *lb, char *idname, int spaceid, int regionid)
214 {
215         wmKeyMap *km;
216
217         for(km= lb->first; km; km= km->next)
218                 if(km->spaceid==spaceid && km->regionid==regionid)
219                         if(0==strncmp(idname, km->idname, KMAP_MAX_NAME))
220                                 return km;
221         
222         return NULL;
223 }
224
225 wmKeyMap *WM_keymap_find(wmKeyConfig *keyconf, char *idname, int spaceid, int regionid)
226 {
227         wmKeyMap *km= wm_keymap_list_find(&keyconf->keymaps, idname, spaceid, regionid);
228         
229         if(km==NULL) {
230                 km= MEM_callocN(sizeof(struct wmKeyMap), "keymap list");
231                 BLI_strncpy(km->idname, idname, KMAP_MAX_NAME);
232                 km->spaceid= spaceid;
233                 km->regionid= regionid;
234                 BLI_addtail(&keyconf->keymaps, km);
235         }
236         
237         return km;
238 }
239
240 /* ****************** modal keymaps ************ */
241
242 /* modal maps get linked to a running operator, and filter the keys before sending to modal() callback */
243
244 wmKeyMap *WM_modalkeymap_add(wmKeyConfig *keyconf, char *idname, EnumPropertyItem *items)
245 {
246         wmKeyMap *km= WM_keymap_find(keyconf, idname, 0, 0);
247         km->flag |= KEYMAP_MODAL;
248         km->modal_items= items;
249         
250         return km;
251 }
252
253 wmKeyMap *WM_modalkeymap_get(wmKeyConfig *keyconf, char *idname)
254 {
255         wmKeyMap *km;
256         
257         for(km= keyconf->keymaps.first; km; km= km->next)
258                 if(km->flag & KEYMAP_MODAL)
259                         if(0==strncmp(idname, km->idname, KMAP_MAX_NAME))
260                                 break;
261         
262         return km;
263 }
264
265
266 wmKeyMapItem *WM_modalkeymap_add_item(wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value)
267 {
268         wmKeyMapItem *kmi= MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
269         
270         BLI_addtail(&km->items, kmi);
271         kmi->propvalue= value;
272         
273         keymap_event_set(kmi, type, val, modifier, keymodifier);
274
275         return kmi;
276 }
277
278 void WM_modalkeymap_assign(wmKeyMap *km, char *opname)
279 {
280         wmOperatorType *ot= WM_operatortype_find(opname, 0);
281         
282         if(ot)
283                 ot->modalkeymap= km;
284         else
285                 printf("error: modalkeymap_assign, unknown operator %s\n", opname);
286 }
287
288 /* ***************** get string from key events **************** */
289
290 const char *WM_key_event_string(short type)
291 {
292         const char *name= NULL;
293         if(RNA_enum_name(event_type_items, (int)type, &name))
294                 return name;
295         
296         return "";
297 }
298
299 char *WM_keymap_item_to_string(wmKeyMapItem *kmi, char *str, int len)
300 {
301         char buf[128];
302
303         buf[0]= 0;
304
305         if (kmi->shift == KM_ANY &&
306                 kmi->ctrl == KM_ANY &&
307                 kmi->alt == KM_ANY &&
308                 kmi->oskey == KM_ANY) {
309
310                 strcat(buf, "Any ");
311         } else {
312                 if(kmi->shift)
313                         strcat(buf, "Shift ");
314
315                 if(kmi->ctrl)
316                         strcat(buf, "Ctrl ");
317
318                 if(kmi->alt)
319                         strcat(buf, "Alt ");
320
321                 if(kmi->oskey)
322                         strcat(buf, "Cmd ");
323         }
324                 
325         if(kmi->keymodifier) {
326                 strcat(buf, WM_key_event_string(kmi->keymodifier));
327                 strcat(buf, " ");
328         }
329
330         strcat(buf, WM_key_event_string(kmi->type));
331         BLI_strncpy(str, buf, len);
332
333         return str;
334 }
335
336 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)
337 {
338         wmWindowManager *wm= CTX_wm_manager(C);
339         wmEventHandler *handler;
340         wmKeyMap *keymap;
341         wmKeyMapItem *kmi;
342
343         /* find keymap item in handlers */
344         for(handler=handlers->first; handler; handler=handler->next) {
345                 keymap= WM_keymap_active(wm, handler->keymap);
346
347                 if(keymap && (!keymap->poll || keymap->poll((bContext*)C))) {
348                         for(kmi=keymap->items.first; kmi; kmi=kmi->next) {
349                                 if(strcmp(kmi->idname, opname) == 0 && WM_key_event_string(kmi->type)[0]) {
350                                         if(compare_props) {
351                                                 if(kmi->ptr && IDP_EqualsProperties(properties, kmi->ptr->data)) {
352                                                         if(keymap_r) *keymap_r= keymap;
353                                                         return kmi;
354                                                 }
355                                         }
356                                         else {
357                                                 if(keymap_r) *keymap_r= keymap;
358                                                 return kmi;
359                                         }
360                                 }
361                         }
362                 }
363         }
364         
365         return NULL;
366 }
367
368 static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, const char *opname, int opcontext, IDProperty *properties, int compare_props, wmKeyMap **keymap_r)
369 {
370         wmWindow *win= CTX_wm_window(C);
371         ScrArea *sa= CTX_wm_area(C);
372         ARegion *ar= CTX_wm_region(C);
373         wmKeyMapItem *found= NULL;
374
375         /* look into multiple handler lists to find the item */
376         if(win)
377                 found= wm_keymap_item_find_handlers(C, &win->handlers, opname, opcontext, properties, compare_props, keymap_r);
378         
379
380         if(sa && found==NULL)
381                 found= wm_keymap_item_find_handlers(C, &sa->handlers, opname, opcontext, properties, compare_props, keymap_r);
382
383         if(found==NULL) {
384                 if(ELEM(opcontext, WM_OP_EXEC_REGION_WIN, WM_OP_INVOKE_REGION_WIN)) {
385                         if(sa) {
386                                 ARegion *ar= sa->regionbase.first;
387                                 for(; ar; ar= ar->next)
388                                         if(ar->regiontype==RGN_TYPE_WINDOW)
389                                                 break;
390
391                                 if(ar)
392                                         found= wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, compare_props, keymap_r);
393                         }
394                 }
395                 else {
396                         if(ar)
397                                 found= wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, compare_props, keymap_r);
398                 }
399         }
400         
401         return found;
402 }
403
404 static wmKeyMapItem *wm_keymap_item_find(const bContext *C, const char *opname, int opcontext, IDProperty *properties, wmKeyMap **keymap_r)
405 {
406         wmKeyMapItem *found= wm_keymap_item_find_props(C, opname, opcontext, properties, 1, keymap_r);
407
408         if(!found)
409                 found= wm_keymap_item_find_props(C, opname, opcontext, properties, 0, keymap_r);
410
411         return found;
412 }
413
414 char *WM_key_event_operator_string(const bContext *C, const char *opname, int opcontext, IDProperty *properties, char *str, int len)
415 {
416         wmKeyMapItem *kmi= wm_keymap_item_find(C, opname, opcontext, properties, NULL);
417         
418         if(kmi) {
419                 WM_keymap_item_to_string(kmi, str, len);
420                 return str;
421         }
422
423         return NULL;
424 }
425
426 /* ***************** user preferences ******************* */
427
428 int WM_keymap_user_init(wmWindowManager *wm, wmKeyMap *keymap)
429 {
430         wmKeyConfig *keyconf;
431         wmKeyMap *km;
432
433         if(!keymap)
434                 return 0;
435
436         /* init from user key config */
437         keyconf= wm_keyconfig_list_find(&wm->keyconfigs, U.keyconfigstr);
438         if(keyconf) {
439                 km= wm_keymap_list_find(&keyconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
440                 if(km) {
441                         keymap->poll= km->poll; /* lazy init */
442                         keymap->modal_items= km->modal_items;
443                         return 1;
444                 }
445         }
446
447         /* or from default */
448         km= wm_keymap_list_find(&wm->defaultconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
449         if(km) {
450                 keymap->poll= km->poll; /* lazy init */
451                 keymap->modal_items= km->modal_items;
452                 return 1;
453         }
454
455         return 0;
456 }
457
458 wmKeyMap *WM_keymap_active(wmWindowManager *wm, wmKeyMap *keymap)
459 {
460         wmKeyConfig *keyconf;
461         wmKeyMap *km;
462
463         if(!keymap)
464                 return NULL;
465         
466         /* first user defined keymaps */
467         km= wm_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
468         if(km) {
469                 km->poll= keymap->poll; /* lazy init */
470                 km->modal_items= keymap->modal_items;
471                 return km;
472         }
473         
474         /* then user key config */
475         keyconf= wm_keyconfig_list_find(&wm->keyconfigs, U.keyconfigstr);
476         if(keyconf) {
477                 km= wm_keymap_list_find(&keyconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
478                 if(km) {
479                         km->poll= keymap->poll; /* lazy init */
480                         km->modal_items= keymap->modal_items;
481                         return km;
482                 }
483         }
484
485         /* then use default */
486         km= wm_keymap_list_find(&wm->defaultconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
487         return km;
488 }
489
490 wmKeyMap *WM_keymap_copy_to_user(wmKeyMap *keymap)
491 {
492         wmKeyMap *usermap;
493         wmKeyMapItem *kmi;
494
495         usermap= wm_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
496
497         if(!usermap) {
498                 /* not saved yet, duplicate existing */
499                 usermap= MEM_dupallocN(keymap);
500                 usermap->modal_items= NULL;
501                 usermap->poll= NULL;
502                 usermap->flag |= KEYMAP_USER;
503
504                 BLI_addtail(&U.keymaps, usermap);
505         }
506         else {
507                 /* already saved, free items for re-copy */
508                 WM_keymap_free(usermap);
509         }
510
511         BLI_duplicatelist(&usermap->items, &keymap->items);
512
513         for(kmi=usermap->items.first; kmi; kmi=kmi->next) {
514                 if(kmi->properties) {
515                         kmi->ptr= MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
516                         WM_operator_properties_create(kmi->ptr, kmi->idname);
517
518                         kmi->properties= IDP_CopyProperty(kmi->properties);
519                         kmi->ptr->data= kmi->properties;
520                 }
521         }
522
523         for(kmi=keymap->items.first; kmi; kmi=kmi->next)
524                 kmi->flag &= ~KMI_EXPANDED;
525
526         return usermap;
527 }
528
529 void WM_keymap_restore_to_default(wmKeyMap *keymap)
530 {
531         wmKeyMap *usermap;
532
533         usermap= wm_keymap_list_find(&U.keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
534
535         if(usermap) {
536                 WM_keymap_free(usermap);
537                 BLI_freelinkN(&U.keymaps, usermap);
538         }
539 }
540
541 /* searches context and changes keymap item, if found */
542 void WM_key_event_operator_change(const bContext *C, const char *opname, int opcontext, IDProperty *properties, short key, short modifier)
543 {
544         wmWindowManager *wm= CTX_wm_manager(C);
545         wmKeyMap *keymap;
546         wmKeyMapItem *kmi;
547         
548         kmi= wm_keymap_item_find(C, opname, opcontext, properties, &keymap);
549
550         if(kmi) {
551                 /* if the existing one is in a default keymap, copy it
552                    to user preferences, and lookup again so we get a
553                    key map item from the user preferences we can modify */
554                 if(BLI_findindex(&wm->defaultconf->keymaps, keymap) >= 0) {
555                         WM_keymap_copy_to_user(keymap);
556                         kmi= wm_keymap_item_find(C, opname, opcontext, properties, NULL);
557                 }
558
559                 keymap_event_set(kmi, key, KM_PRESS, modifier, 0);
560         }
561 }
562