1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2007 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup wm
22 *
23 * Configurable key-maps - add/remove/find/compare/patch...
24 */
25
26 #include <string.h>
27
28 #include "DNA_object_types.h"
29 #include "DNA_screen_types.h"
30 #include "DNA_space_types.h"
31 #include "DNA_userdef_types.h"
32 #include "DNA_windowmanager_types.h"
33 #include "DNA_workspace_types.h"
34
35 #include "CLG_log.h"
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_math.h"
40 #include "BLI_utildefines.h"
41
42 #include "BLF_api.h"
43
44 #include "BKE_context.h"
45 #include "BKE_global.h"
46 #include "BKE_idprop.h"
47 #include "BKE_main.h"
48 #include "BKE_screen.h"
49 #include "BKE_workspace.h"
50
51 #include "BLT_translation.h"
52
53 #include "RNA_access.h"
54 #include "RNA_enum_types.h"
55
56 #include "WM_api.h"
57 #include "WM_types.h"
58 #include "wm_event_system.h"
59 #include "wm_event_types.h"
60
61 struct wmKeyMapItemFind_Params {
62 bool (*filter_fn)(const wmKeyMap *km, const wmKeyMapItem *kmi, void *user_data);
63 void *user_data;
64 };
65
66 /* -------------------------------------------------------------------- */
67 /** \name Keymap Item
68 *
69 * Item in a keymap, that maps from an event to an operator or modal map item.
70 * \{ */
71
wm_keymap_item_copy(wmKeyMapItem * kmi)72 static wmKeyMapItem *wm_keymap_item_copy(wmKeyMapItem *kmi)
73 {
74 wmKeyMapItem *kmin = MEM_dupallocN(kmi);
75
76 kmin->prev = kmin->next = NULL;
77 kmin->flag &= ~KMI_UPDATE;
78
79 if (kmin->properties) {
80 kmin->ptr = MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
81 WM_operator_properties_create(kmin->ptr, kmin->idname);
82
83 kmin->properties = IDP_CopyProperty(kmin->properties);
84 kmin->ptr->data = kmin->properties;
85 }
86 else {
87 kmin->properties = NULL;
88 kmin->ptr = NULL;
89 }
90
91 return kmin;
92 }
93
wm_keymap_item_free(wmKeyMapItem * kmi)94 static void wm_keymap_item_free(wmKeyMapItem *kmi)
95 {
96 /* not kmi itself */
97 if (kmi->ptr) {
98 WM_operator_properties_free(kmi->ptr);
99 MEM_freeN(kmi->ptr);
100 kmi->ptr = NULL;
101 kmi->properties = NULL;
102 }
103 }
104
wm_keymap_item_properties_set(wmKeyMapItem * kmi)105 static void wm_keymap_item_properties_set(wmKeyMapItem *kmi)
106 {
107 WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
108 WM_operator_properties_sanitize(kmi->ptr, 1);
109 }
110
111 /**
112 * Similar to #wm_keymap_item_properties_set
113 * but checks for the #wmOperatorType having changed, see T38042.
114 */
wm_keymap_item_properties_update_ot(wmKeyMapItem * kmi)115 static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi)
116 {
117 if (kmi->idname[0] == 0) {
118 BLI_assert(kmi->ptr == NULL);
119 return;
120 }
121
122 if (kmi->ptr == NULL) {
123 wm_keymap_item_properties_set(kmi);
124 }
125 else {
126 wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
127 if (ot) {
128 if (ot->srna != kmi->ptr->type) {
129 /* matches wm_keymap_item_properties_set but doesn't alloc new ptr */
130 WM_operator_properties_create_ptr(kmi->ptr, ot);
131 /* 'kmi->ptr->data' NULL'd above, keep using existing properties.
132 * Note: the operators property types may have changed,
133 * we will need a more comprehensive sanitize function to support this properly.
134 */
135 if (kmi->properties) {
136 kmi->ptr->data = kmi->properties;
137 }
138 WM_operator_properties_sanitize(kmi->ptr, 1);
139 }
140 }
141 else {
142 /* zombie keymap item */
143 wm_keymap_item_free(kmi);
144 }
145 }
146 }
147
wm_keymap_item_properties_update_ot_from_list(ListBase * km_lb)148 static void wm_keymap_item_properties_update_ot_from_list(ListBase *km_lb)
149 {
150 LISTBASE_FOREACH (wmKeyMap *, km, km_lb) {
151 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &km->items) {
152 wm_keymap_item_properties_update_ot(kmi);
153 }
154
155 LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &km->diff_items) {
156 if (kmdi->add_item) {
157 wm_keymap_item_properties_update_ot(kmdi->add_item);
158 }
159 if (kmdi->remove_item) {
160 wm_keymap_item_properties_update_ot(kmdi->remove_item);
161 }
162 }
163 }
164 }
165
wm_keymap_item_equals_result(wmKeyMapItem * a,wmKeyMapItem * b)166 static bool wm_keymap_item_equals_result(wmKeyMapItem *a, wmKeyMapItem *b)
167 {
168 return (STREQ(a->idname, b->idname) &&
169 /* We do not really care about which Main we pass here, tbh. */
170 RNA_struct_equals(G_MAIN, a->ptr, b->ptr, RNA_EQ_UNSET_MATCH_NONE) &&
171 (a->flag & KMI_INACTIVE) == (b->flag & KMI_INACTIVE) && a->propvalue == b->propvalue);
172 }
173
wm_keymap_item_equals(wmKeyMapItem * a,wmKeyMapItem * b)174 static bool wm_keymap_item_equals(wmKeyMapItem *a, wmKeyMapItem *b)
175 {
176 return (wm_keymap_item_equals_result(a, b) && a->type == b->type && a->val == b->val &&
177 a->shift == b->shift && a->ctrl == b->ctrl && a->alt == b->alt && a->oskey == b->oskey &&
178 a->keymodifier == b->keymodifier && a->maptype == b->maptype &&
179 ((ISKEYBOARD(a->type) == 0) ||
180 (a->flag & KMI_REPEAT_IGNORE) == (b->flag & KMI_REPEAT_IGNORE)));
181 }
182
183 /* properties can be NULL, otherwise the arg passed is used and ownership is given to the kmi */
WM_keymap_item_properties_reset(wmKeyMapItem * kmi,struct IDProperty * properties)184 void WM_keymap_item_properties_reset(wmKeyMapItem *kmi, struct IDProperty *properties)
185 {
186 if (LIKELY(kmi->ptr)) {
187 WM_operator_properties_free(kmi->ptr);
188 MEM_freeN(kmi->ptr);
189
190 kmi->ptr = NULL;
191 }
192
193 kmi->properties = properties;
194
195 wm_keymap_item_properties_set(kmi);
196 }
197
WM_keymap_item_map_type_get(const wmKeyMapItem * kmi)198 int WM_keymap_item_map_type_get(const wmKeyMapItem *kmi)
199 {
200 if (ISTIMER(kmi->type)) {
201 return KMI_TYPE_TIMER;
202 }
203 if (ISKEYBOARD(kmi->type)) {
204 return KMI_TYPE_KEYBOARD;
205 }
206 if (ISTWEAK(kmi->type)) {
207 return KMI_TYPE_TWEAK;
208 }
209 if (ISMOUSE(kmi->type)) {
210 return KMI_TYPE_MOUSE;
211 }
212 if (ISNDOF(kmi->type)) {
213 return KMI_TYPE_NDOF;
214 }
215 if (kmi->type == KM_TEXTINPUT) {
216 return KMI_TYPE_TEXTINPUT;
217 }
218 if (ELEM(kmi->type, TABLET_STYLUS, TABLET_ERASER)) {
219 return KMI_TYPE_MOUSE;
220 }
221 return KMI_TYPE_KEYBOARD;
222 }
223
224 /** \} */
225
226 /* -------------------------------------------------------------------- */
227 /** \name Keymap Diff Item
228 *
229 * Item in a diff keymap, used for saving diff of keymaps in user preferences.
230 * \{ */
231
wm_keymap_diff_item_copy(wmKeyMapDiffItem * kmdi)232 static wmKeyMapDiffItem *wm_keymap_diff_item_copy(wmKeyMapDiffItem *kmdi)
233 {
234 wmKeyMapDiffItem *kmdin = MEM_dupallocN(kmdi);
235
236 kmdin->next = kmdin->prev = NULL;
237 if (kmdi->add_item) {
238 kmdin->add_item = wm_keymap_item_copy(kmdi->add_item);
239 }
240 if (kmdi->remove_item) {
241 kmdin->remove_item = wm_keymap_item_copy(kmdi->remove_item);
242 }
243
244 return kmdin;
245 }
246
wm_keymap_diff_item_free(wmKeyMapDiffItem * kmdi)247 static void wm_keymap_diff_item_free(wmKeyMapDiffItem *kmdi)
248 {
249 if (kmdi->remove_item) {
250 wm_keymap_item_free(kmdi->remove_item);
251 MEM_freeN(kmdi->remove_item);
252 }
253 if (kmdi->add_item) {
254 wm_keymap_item_free(kmdi->add_item);
255 MEM_freeN(kmdi->add_item);
256 }
257 }
258
259 /** \} */
260
261 /* -------------------------------------------------------------------- */
262 /** \name Key Configuration
263 *
264 * List of keymaps for all editors, modes, etc.
265 * There is a builtin default key configuration,
266 * a user key configuration, and other preset configurations.
267 * \{ */
268
WM_keyconfig_new(wmWindowManager * wm,const char * idname,bool user_defined)269 wmKeyConfig *WM_keyconfig_new(wmWindowManager *wm, const char *idname, bool user_defined)
270 {
271 wmKeyConfig *keyconf = BLI_findstring(&wm->keyconfigs, idname, offsetof(wmKeyConfig, idname));
272 if (keyconf) {
273 if (keyconf == wm->defaultconf) {
274 /* For default configuration, we need to keep keymap
275 * modal items and poll functions intact. */
276 LISTBASE_FOREACH (wmKeyMap *, km, &keyconf->keymaps) {
277 WM_keymap_clear(km);
278 }
279 }
280 else {
281 /* For user defined key configuration, clear all keymaps. */
282 WM_keyconfig_clear(keyconf);
283 }
284
285 return keyconf;
286 }
287
288 /* Create new configuration. */
289 keyconf = MEM_callocN(sizeof(wmKeyConfig), "wmKeyConfig");
290 BLI_strncpy(keyconf->idname, idname, sizeof(keyconf->idname));
291 BLI_addtail(&wm->keyconfigs, keyconf);
292
293 if (user_defined) {
294 keyconf->flag |= KEYCONF_USER;
295 }
296
297 return keyconf;
298 }
299
WM_keyconfig_new_user(wmWindowManager * wm,const char * idname)300 wmKeyConfig *WM_keyconfig_new_user(wmWindowManager *wm, const char *idname)
301 {
302 return WM_keyconfig_new(wm, idname, true);
303 }
304
WM_keyconfig_remove(wmWindowManager * wm,wmKeyConfig * keyconf)305 bool WM_keyconfig_remove(wmWindowManager *wm, wmKeyConfig *keyconf)
306 {
307 if (BLI_findindex(&wm->keyconfigs, keyconf) != -1) {
308 if (STREQLEN(U.keyconfigstr, keyconf->idname, sizeof(U.keyconfigstr))) {
309 BLI_strncpy(U.keyconfigstr, wm->defaultconf->idname, sizeof(U.keyconfigstr));
310 U.runtime.is_dirty = true;
311 WM_keyconfig_update_tag(NULL, NULL);
312 }
313
314 BLI_remlink(&wm->keyconfigs, keyconf);
315 WM_keyconfig_free(keyconf);
316
317 return true;
318 }
319 return false;
320 }
321
WM_keyconfig_clear(wmKeyConfig * keyconf)322 void WM_keyconfig_clear(wmKeyConfig *keyconf)
323 {
324 LISTBASE_FOREACH (wmKeyMap *, km, &keyconf->keymaps) {
325 WM_keymap_clear(km);
326 }
327
328 BLI_freelistN(&keyconf->keymaps);
329 }
330
WM_keyconfig_free(wmKeyConfig * keyconf)331 void WM_keyconfig_free(wmKeyConfig *keyconf)
332 {
333 WM_keyconfig_clear(keyconf);
334 MEM_freeN(keyconf);
335 }
336
WM_keyconfig_active(wmWindowManager * wm)337 static wmKeyConfig *WM_keyconfig_active(wmWindowManager *wm)
338 {
339 wmKeyConfig *keyconf;
340
341 /* first try from preset */
342 keyconf = BLI_findstring(&wm->keyconfigs, U.keyconfigstr, offsetof(wmKeyConfig, idname));
343 if (keyconf) {
344 return keyconf;
345 }
346
347 /* otherwise use default */
348 return wm->defaultconf;
349 }
350
WM_keyconfig_set_active(wmWindowManager * wm,const char * idname)351 void WM_keyconfig_set_active(wmWindowManager *wm, const char *idname)
352 {
353 /* setting a different key configuration as active: we ensure all is
354 * updated properly before and after making the change */
355
356 WM_keyconfig_update(wm);
357
358 BLI_strncpy(U.keyconfigstr, idname, sizeof(U.keyconfigstr));
359 if (wm->initialized & WM_KEYCONFIG_IS_INIT) {
360 U.runtime.is_dirty = true;
361 }
362
363 WM_keyconfig_update_tag(NULL, NULL);
364 WM_keyconfig_update(wm);
365 }
366
367 /** \} */
368
369 /* -------------------------------------------------------------------- */
370 /** \name Keymap
371 *
372 * List of keymap items for one editor, mode, modal operator.
373 * \{ */
374
wm_keymap_new(const char * idname,int spaceid,int regionid)375 static wmKeyMap *wm_keymap_new(const char *idname, int spaceid, int regionid)
376 {
377 wmKeyMap *km = MEM_callocN(sizeof(struct wmKeyMap), "keymap list");
378
379 BLI_strncpy(km->idname, idname, KMAP_MAX_NAME);
380 km->spaceid = spaceid;
381 km->regionid = regionid;
382
383 {
384 const char *owner_id = RNA_struct_state_owner_get();
385 if (owner_id) {
386 BLI_strncpy(km->owner_id, owner_id, sizeof(km->owner_id));
387 }
388 }
389 return km;
390 }
391
wm_keymap_copy(wmKeyMap * keymap)392 static wmKeyMap *wm_keymap_copy(wmKeyMap *keymap)
393 {
394 wmKeyMap *keymapn = MEM_dupallocN(keymap);
395 wmKeyMapItem *kmi, *kmin;
396 wmKeyMapDiffItem *kmdi, *kmdin;
397
398 keymapn->modal_items = keymap->modal_items;
399 keymapn->poll = keymap->poll;
400 keymapn->poll_modal_item = keymap->poll_modal_item;
401 BLI_listbase_clear(&keymapn->items);
402 keymapn->flag &= ~(KEYMAP_UPDATE | KEYMAP_EXPANDED);
403
404 for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) {
405 kmdin = wm_keymap_diff_item_copy(kmdi);
406 BLI_addtail(&keymapn->items, kmdin);
407 }
408
409 for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
410 kmin = wm_keymap_item_copy(kmi);
411 BLI_addtail(&keymapn->items, kmin);
412 }
413
414 return keymapn;
415 }
416
WM_keymap_clear(wmKeyMap * keymap)417 void WM_keymap_clear(wmKeyMap *keymap)
418 {
419 wmKeyMapItem *kmi;
420 wmKeyMapDiffItem *kmdi;
421
422 for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) {
423 wm_keymap_diff_item_free(kmdi);
424 }
425
426 for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
427 wm_keymap_item_free(kmi);
428 }
429
430 BLI_freelistN(&keymap->diff_items);
431 BLI_freelistN(&keymap->items);
432 }
433
WM_keymap_remove(wmKeyConfig * keyconf,wmKeyMap * keymap)434 bool WM_keymap_remove(wmKeyConfig *keyconf, wmKeyMap *keymap)
435 {
436 if (BLI_findindex(&keyconf->keymaps, keymap) != -1) {
437
438 WM_keymap_clear(keymap);
439 BLI_remlink(&keyconf->keymaps, keymap);
440 MEM_freeN(keymap);
441
442 return true;
443 }
444 return false;
445 }
446
WM_keymap_poll(bContext * C,wmKeyMap * keymap)447 bool WM_keymap_poll(bContext *C, wmKeyMap *keymap)
448 {
449 /* If we're tagged, only use compatible. */
450 if (keymap->owner_id[0] != '\0') {
451 const WorkSpace *workspace = CTX_wm_workspace(C);
452 if (BKE_workspace_owner_id_check(workspace, keymap->owner_id) == false) {
453 return false;
454 }
455 }
456
457 if (UNLIKELY(BLI_listbase_is_empty(&keymap->items))) {
458 /* Empty key-maps may be missing more there may be a typo in the name.
459 * Warn early to avoid losing time investigating each case. */
460 CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname);
461 }
462
463 if (keymap->poll != NULL) {
464 return keymap->poll(C);
465 }
466 return true;
467 }
468
keymap_event_set(wmKeyMapItem * kmi,short type,short val,int modifier,short keymodifier)469 static void keymap_event_set(
470 wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier)
471 {
472 kmi->type = type;
473 kmi->val = val;
474 kmi->keymodifier = keymodifier;
475
476 if (modifier == KM_ANY) {
477 kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_ANY;
478 }
479 else {
480 kmi->shift = (modifier & KM_SHIFT) ? KM_MOD_FIRST :
481 ((modifier & KM_SHIFT2) ? KM_MOD_SECOND : false);
482 kmi->ctrl = (modifier & KM_CTRL) ? KM_MOD_FIRST :
483 ((modifier & KM_CTRL2) ? KM_MOD_SECOND : false);
484 kmi->alt = (modifier & KM_ALT) ? KM_MOD_FIRST : ((modifier & KM_ALT2) ? KM_MOD_SECOND : false);
485 kmi->oskey = (modifier & KM_OSKEY) ? KM_MOD_FIRST :
486 ((modifier & KM_OSKEY2) ? KM_MOD_SECOND : false);
487 }
488 }
489
keymap_item_set_id(wmKeyMap * keymap,wmKeyMapItem * kmi)490 static void keymap_item_set_id(wmKeyMap *keymap, wmKeyMapItem *kmi)
491 {
492 keymap->kmi_id++;
493 if ((keymap->flag & KEYMAP_USER) == 0) {
494 kmi->id = keymap->kmi_id;
495 }
496 else {
497 kmi->id = -keymap->kmi_id; /* User defined keymap entries have negative ids */
498 }
499 }
500
501 /* always add item */
WM_keymap_add_item(wmKeyMap * keymap,const char * idname,int type,int val,int modifier,int keymodifier)502 wmKeyMapItem *WM_keymap_add_item(
503 wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
504 {
505 wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
506
507 BLI_addtail(&keymap->items, kmi);
508 BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME);
509
510 keymap_event_set(kmi, type, val, modifier, keymodifier);
511 wm_keymap_item_properties_set(kmi);
512
513 keymap_item_set_id(keymap, kmi);
514
515 WM_keyconfig_update_tag(keymap, kmi);
516
517 return kmi;
518 }
519
WM_keymap_add_item_copy(struct wmKeyMap * keymap,wmKeyMapItem * kmi_src)520 wmKeyMapItem *WM_keymap_add_item_copy(struct wmKeyMap *keymap, wmKeyMapItem *kmi_src)
521 {
522 wmKeyMapItem *kmi_dst = wm_keymap_item_copy(kmi_src);
523
524 BLI_addtail(&keymap->items, kmi_dst);
525
526 keymap_item_set_id(keymap, kmi_dst);
527
528 WM_keyconfig_update_tag(keymap, kmi_dst);
529
530 return kmi_dst;
531 }
532
WM_keymap_remove_item(wmKeyMap * keymap,wmKeyMapItem * kmi)533 bool WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi)
534 {
535 if (BLI_findindex(&keymap->items, kmi) != -1) {
536 if (kmi->ptr) {
537 WM_operator_properties_free(kmi->ptr);
538 MEM_freeN(kmi->ptr);
539 }
540 BLI_freelinkN(&keymap->items, kmi);
541
542 WM_keyconfig_update_tag(keymap, NULL);
543 return true;
544 }
545 return false;
546 }
547
548 /** \} */
549
550 /* -------------------------------------------------------------------- */
551 /** \name Keymap Diff and Patch
552 *
553 * Rather than saving the entire keymap for user preferences, we only save a
554 * diff so that changes in the defaults get synced. This system is not perfect
555 * but works better than overriding the keymap entirely when only few items
556 * are changed.
557 * \{ */
558
wm_keymap_addon_add(wmKeyMap * keymap,wmKeyMap * addonmap)559 static void wm_keymap_addon_add(wmKeyMap *keymap, wmKeyMap *addonmap)
560 {
561 wmKeyMapItem *kmi, *kmin;
562
563 for (kmi = addonmap->items.first; kmi; kmi = kmi->next) {
564 kmin = wm_keymap_item_copy(kmi);
565 keymap_item_set_id(keymap, kmin);
566 BLI_addhead(&keymap->items, kmin);
567 }
568 }
569
wm_keymap_find_item_equals(wmKeyMap * km,wmKeyMapItem * needle)570 static wmKeyMapItem *wm_keymap_find_item_equals(wmKeyMap *km, wmKeyMapItem *needle)
571 {
572 wmKeyMapItem *kmi;
573
574 for (kmi = km->items.first; kmi; kmi = kmi->next) {
575 if (wm_keymap_item_equals(kmi, needle)) {
576 return kmi;
577 }
578 }
579
580 return NULL;
581 }
582
wm_keymap_find_item_equals_result(wmKeyMap * km,wmKeyMapItem * needle)583 static wmKeyMapItem *wm_keymap_find_item_equals_result(wmKeyMap *km, wmKeyMapItem *needle)
584 {
585 wmKeyMapItem *kmi;
586
587 for (kmi = km->items.first; kmi; kmi = kmi->next) {
588 if (wm_keymap_item_equals_result(kmi, needle)) {
589 return kmi;
590 }
591 }
592
593 return NULL;
594 }
595
wm_keymap_diff(wmKeyMap * diff_km,wmKeyMap * from_km,wmKeyMap * to_km,wmKeyMap * orig_km,wmKeyMap * addon_km)596 static void wm_keymap_diff(
597 wmKeyMap *diff_km, wmKeyMap *from_km, wmKeyMap *to_km, wmKeyMap *orig_km, wmKeyMap *addon_km)
598 {
599 wmKeyMapItem *kmi, *to_kmi, *orig_kmi;
600 wmKeyMapDiffItem *kmdi;
601
602 for (kmi = from_km->items.first; kmi; kmi = kmi->next) {
603 to_kmi = WM_keymap_item_find_id(to_km, kmi->id);
604
605 if (!to_kmi) {
606 /* remove item */
607 kmdi = MEM_callocN(sizeof(wmKeyMapDiffItem), "wmKeyMapDiffItem");
608 kmdi->remove_item = wm_keymap_item_copy(kmi);
609 BLI_addtail(&diff_km->diff_items, kmdi);
610 }
611 else if (to_kmi && !wm_keymap_item_equals(kmi, to_kmi)) {
612 /* replace item */
613 kmdi = MEM_callocN(sizeof(wmKeyMapDiffItem), "wmKeyMapDiffItem");
614 kmdi->remove_item = wm_keymap_item_copy(kmi);
615 kmdi->add_item = wm_keymap_item_copy(to_kmi);
616 BLI_addtail(&diff_km->diff_items, kmdi);
617 }
618
619 /* sync expanded flag back to original so we don't lose it on repatch */
620 if (to_kmi) {
621 orig_kmi = WM_keymap_item_find_id(orig_km, kmi->id);
622
623 if (!orig_kmi && addon_km) {
624 orig_kmi = wm_keymap_find_item_equals(addon_km, kmi);
625 }
626
627 if (orig_kmi) {
628 orig_kmi->flag &= ~KMI_EXPANDED;
629 orig_kmi->flag |= (to_kmi->flag & KMI_EXPANDED);
630 }
631 }
632 }
633
634 for (kmi = to_km->items.first; kmi; kmi = kmi->next) {
635 if (kmi->id < 0) {
636 /* add item */
637 kmdi = MEM_callocN(sizeof(wmKeyMapDiffItem), "wmKeyMapDiffItem");
638 kmdi->add_item = wm_keymap_item_copy(kmi);
639 BLI_addtail(&diff_km->diff_items, kmdi);
640 }
641 }
642 }
643
wm_keymap_patch(wmKeyMap * km,wmKeyMap * diff_km)644 static void wm_keymap_patch(wmKeyMap *km, wmKeyMap *diff_km)
645 {
646 wmKeyMapDiffItem *kmdi;
647 wmKeyMapItem *kmi_remove, *kmi_add;
648
649 for (kmdi = diff_km->diff_items.first; kmdi; kmdi = kmdi->next) {
650 /* find item to remove */
651 kmi_remove = NULL;
652 if (kmdi->remove_item) {
653 kmi_remove = wm_keymap_find_item_equals(km, kmdi->remove_item);
654 if (!kmi_remove) {
655 kmi_remove = wm_keymap_find_item_equals_result(km, kmdi->remove_item);
656 }
657 }
658
659 /* add item */
660 if (kmdi->add_item) {
661 /* Do not re-add an already existing keymap item! See T42088. */
662 /* We seek only for exact copy here! See T42137. */
663 kmi_add = wm_keymap_find_item_equals(km, kmdi->add_item);
664
665 /** If kmi_add is same as kmi_remove (can happen in some cases,
666 * typically when we got kmi_remove from #wm_keymap_find_item_equals_result()),
667 * no need to add or remove anything, see T45579. */
668
669 /**
670 * \note This typically happens when we apply user-defined keymap diff to a base one that
671 * was exported with that customized keymap already. In that case:
672 *
673 * - wm_keymap_find_item_equals(km, kmdi->remove_item) finds nothing
674 * (because actual shortcut of current base does not match kmdi->remove_item any more).
675 * - wm_keymap_find_item_equals_result(km, kmdi->remove_item) finds the current kmi from
676 * base keymap (because it does exactly the same thing).
677 * - wm_keymap_find_item_equals(km, kmdi->add_item) finds the same kmi,
678 * since base keymap was exported with that user-defined shortcut already!
679 *
680 * Maybe we should rather keep user-defined keymaps specific to a given base one? */
681 if (kmi_add != NULL && kmi_add == kmi_remove) {
682 kmi_remove = NULL;
683 }
684 /* only if nothing to remove or item to remove found */
685 else if (!kmi_add && (!kmdi->remove_item || kmi_remove)) {
686 kmi_add = wm_keymap_item_copy(kmdi->add_item);
687 kmi_add->flag |= KMI_USER_MODIFIED;
688
689 if (kmi_remove) {
690 kmi_add->flag &= ~KMI_EXPANDED;
691 kmi_add->flag |= (kmi_remove->flag & KMI_EXPANDED);
692 kmi_add->id = kmi_remove->id;
693 BLI_insertlinkbefore(&km->items, kmi_remove, kmi_add);
694 }
695 else {
696 keymap_item_set_id(km, kmi_add);
697 BLI_addtail(&km->items, kmi_add);
698 }
699 }
700 }
701
702 /* remove item */
703 if (kmi_remove) {
704 wm_keymap_item_free(kmi_remove);
705 BLI_freelinkN(&km->items, kmi_remove);
706 }
707 }
708 }
709
wm_keymap_patch_update(ListBase * lb,wmKeyMap * defaultmap,wmKeyMap * addonmap,wmKeyMap * usermap)710 static wmKeyMap *wm_keymap_patch_update(ListBase *lb,
711 wmKeyMap *defaultmap,
712 wmKeyMap *addonmap,
713 wmKeyMap *usermap)
714 {
715 wmKeyMap *km;
716 int expanded = 0;
717
718 /* remove previous keymap in list, we will replace it */
719 km = WM_keymap_list_find(lb, defaultmap->idname, defaultmap->spaceid, defaultmap->regionid);
720 if (km) {
721 expanded = (km->flag & (KEYMAP_EXPANDED | KEYMAP_CHILDREN_EXPANDED));
722 WM_keymap_clear(km);
723 BLI_freelinkN(lb, km);
724 }
725
726 /* copy new keymap from an existing one */
727 if (usermap && !(usermap->flag & KEYMAP_DIFF)) {
728 /* for compatibility with old user preferences with non-diff
729 * keymaps we override the original entirely */
730 wmKeyMapItem *kmi, *orig_kmi;
731
732 km = wm_keymap_copy(usermap);
733
734 /* try to find corresponding id's for items */
735 for (kmi = km->items.first; kmi; kmi = kmi->next) {
736 orig_kmi = wm_keymap_find_item_equals(defaultmap, kmi);
737 if (!orig_kmi) {
738 orig_kmi = wm_keymap_find_item_equals_result(defaultmap, kmi);
739 }
740
741 if (orig_kmi) {
742 kmi->id = orig_kmi->id;
743 }
744 else {
745 kmi->id = -(km->kmi_id++);
746 }
747 }
748
749 km->flag |= KEYMAP_UPDATE; /* update again to create diff */
750 }
751 else {
752 km = wm_keymap_copy(defaultmap);
753 }
754
755 /* add addon keymap items */
756 if (addonmap) {
757 wm_keymap_addon_add(km, addonmap);
758 }
759
760 /* tag as being user edited */
761 if (usermap) {
762 km->flag |= KEYMAP_USER_MODIFIED;
763 }
764 km->flag |= KEYMAP_USER | expanded;
765
766 /* apply user changes of diff keymap */
767 if (usermap && (usermap->flag & KEYMAP_DIFF)) {
768 wm_keymap_patch(km, usermap);
769 }
770
771 /* add to list */
772 BLI_addtail(lb, km);
773
774 return km;
775 }
776
wm_keymap_diff_update(ListBase * lb,wmKeyMap * defaultmap,wmKeyMap * addonmap,wmKeyMap * km)777 static void wm_keymap_diff_update(ListBase *lb,
778 wmKeyMap *defaultmap,
779 wmKeyMap *addonmap,
780 wmKeyMap *km)
781 {
782 wmKeyMap *diffmap, *prevmap, *origmap;
783
784 /* create temporary default + addon keymap for diff */
785 origmap = defaultmap;
786
787 if (addonmap) {
788 defaultmap = wm_keymap_copy(defaultmap);
789 wm_keymap_addon_add(defaultmap, addonmap);
790 }
791
792 /* remove previous diff keymap in list, we will replace it */
793 prevmap = WM_keymap_list_find(lb, km->idname, km->spaceid, km->regionid);
794 if (prevmap) {
795 WM_keymap_clear(prevmap);
796 BLI_freelinkN(lb, prevmap);
797 }
798
799 /* create diff keymap */
800 diffmap = wm_keymap_new(km->idname, km->spaceid, km->regionid);
801 diffmap->flag |= KEYMAP_DIFF;
802 if (defaultmap->flag & KEYMAP_MODAL) {
803 diffmap->flag |= KEYMAP_MODAL;
804 }
805 wm_keymap_diff(diffmap, defaultmap, km, origmap, addonmap);
806
807 /* add to list if not empty */
808 if (diffmap->diff_items.first) {
809 BLI_addtail(lb, diffmap);
810 }
811 else {
812 WM_keymap_clear(diffmap);
813 MEM_freeN(diffmap);
814 }
815
816 /* free temporary default map */
817 if (addonmap) {
818 WM_keymap_clear(defaultmap);
819 MEM_freeN(defaultmap);
820 }
821 }
822
823 /** \} */
824
825 /* -------------------------------------------------------------------- */
826 /** \name Storage in WM
827 *
828 * Name id's are for storing general or multiple keymaps.
829 *
830 * - Space/region ids are same as DNA_space_types.h
831 * - Gets freed in wm.c
832 * \{ */
833
WM_keymap_list_find(ListBase * lb,const char * idname,int spaceid,int regionid)834 wmKeyMap *WM_keymap_list_find(ListBase *lb, const char *idname, int spaceid, int regionid)
835 {
836 wmKeyMap *km;
837
838 for (km = lb->first; km; km = km->next) {
839 if (km->spaceid == spaceid && km->regionid == regionid) {
840 if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) {
841 return km;
842 }
843 }
844 }
845
846 return NULL;
847 }
848
WM_keymap_list_find_spaceid_or_empty(ListBase * lb,const char * idname,int spaceid,int regionid)849 wmKeyMap *WM_keymap_list_find_spaceid_or_empty(ListBase *lb,
850 const char *idname,
851 int spaceid,
852 int regionid)
853 {
854 wmKeyMap *km;
855
856 for (km = lb->first; km; km = km->next) {
857 if (ELEM(km->spaceid, spaceid, SPACE_EMPTY) && km->regionid == regionid) {
858 if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) {
859 return km;
860 }
861 }
862 }
863
864 return NULL;
865 }
866
WM_keymap_ensure(wmKeyConfig * keyconf,const char * idname,int spaceid,int regionid)867 wmKeyMap *WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
868 {
869 wmKeyMap *km = WM_keymap_list_find(&keyconf->keymaps, idname, spaceid, regionid);
870
871 if (km == NULL) {
872 km = wm_keymap_new(idname, spaceid, regionid);
873 BLI_addtail(&keyconf->keymaps, km);
874
875 WM_keyconfig_update_tag(km, NULL);
876 }
877
878 return km;
879 }
880
WM_keymap_find_all(wmWindowManager * wm,const char * idname,int spaceid,int regionid)881 wmKeyMap *WM_keymap_find_all(wmWindowManager *wm, const char *idname, int spaceid, int regionid)
882 {
883 return WM_keymap_list_find(&wm->userconf->keymaps, idname, spaceid, regionid);
884 }
885
WM_keymap_find_all_spaceid_or_empty(wmWindowManager * wm,const char * idname,int spaceid,int regionid)886 wmKeyMap *WM_keymap_find_all_spaceid_or_empty(wmWindowManager *wm,
887 const char *idname,
888 int spaceid,
889 int regionid)
890 {
891 return WM_keymap_list_find_spaceid_or_empty(&wm->userconf->keymaps, idname, spaceid, regionid);
892 }
893
894 /** \} */
895
896 /* -------------------------------------------------------------------- */
897 /** \name Modal Keymaps
898 *
899 * Modal key-maps get linked to a running operator,
900 * and filter the keys before sending to #wmOperatorType.modal callback.
901 * \{ */
902
WM_modalkeymap_ensure(wmKeyConfig * keyconf,const char * idname,const EnumPropertyItem * items)903 wmKeyMap *WM_modalkeymap_ensure(wmKeyConfig *keyconf,
904 const char *idname,
905 const EnumPropertyItem *items)
906 {
907 wmKeyMap *km = WM_keymap_ensure(keyconf, idname, 0, 0);
908 km->flag |= KEYMAP_MODAL;
909
910 /* init modal items from default config */
911 wmWindowManager *wm = G_MAIN->wm.first;
912 if (wm->defaultconf && wm->defaultconf != keyconf) {
913 wmKeyMap *defaultkm = WM_keymap_list_find(&wm->defaultconf->keymaps, km->idname, 0, 0);
914
915 if (defaultkm) {
916 km->modal_items = defaultkm->modal_items;
917 km->poll = defaultkm->poll;
918 km->poll_modal_item = defaultkm->poll_modal_item;
919 }
920 }
921
922 if (items) {
923 km->modal_items = items;
924 }
925
926 return km;
927 }
928
WM_modalkeymap_find(wmKeyConfig * keyconf,const char * idname)929 wmKeyMap *WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
930 {
931 wmKeyMap *km;
932
933 for (km = keyconf->keymaps.first; km; km = km->next) {
934 if (km->flag & KEYMAP_MODAL) {
935 if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) {
936 break;
937 }
938 }
939 }
940
941 return km;
942 }
943
WM_modalkeymap_add_item(wmKeyMap * km,int type,int val,int modifier,int keymodifier,int value)944 wmKeyMapItem *WM_modalkeymap_add_item(
945 wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value)
946 {
947 wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
948
949 BLI_addtail(&km->items, kmi);
950 kmi->propvalue = value;
951
952 keymap_event_set(kmi, type, val, modifier, keymodifier);
953
954 keymap_item_set_id(km, kmi);
955
956 WM_keyconfig_update_tag(km, kmi);
957
958 return kmi;
959 }
960
WM_modalkeymap_add_item_str(wmKeyMap * km,int type,int val,int modifier,int keymodifier,const char * value)961 wmKeyMapItem *WM_modalkeymap_add_item_str(
962 wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value)
963 {
964 wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
965
966 BLI_addtail(&km->items, kmi);
967 BLI_strncpy(kmi->propvalue_str, value, sizeof(kmi->propvalue_str));
968
969 keymap_event_set(kmi, type, val, modifier, keymodifier);
970
971 keymap_item_set_id(km, kmi);
972
973 WM_keyconfig_update_tag(km, kmi);
974
975 return kmi;
976 }
977
wm_modalkeymap_find_propvalue_iter(wmKeyMap * km,wmKeyMapItem * kmi,const int propvalue)978 static wmKeyMapItem *wm_modalkeymap_find_propvalue_iter(wmKeyMap *km,
979 wmKeyMapItem *kmi,
980 const int propvalue)
981 {
982 if (km->flag & KEYMAP_MODAL) {
983 kmi = kmi ? kmi->next : km->items.first;
984 for (; kmi; kmi = kmi->next) {
985 if (kmi->propvalue == propvalue) {
986 return kmi;
987 }
988 }
989 }
990 else {
991 BLI_assert(!"called with non modal keymap");
992 }
993
994 return NULL;
995 }
996
WM_modalkeymap_find_propvalue(wmKeyMap * km,const int propvalue)997 wmKeyMapItem *WM_modalkeymap_find_propvalue(wmKeyMap *km, const int propvalue)
998 {
999 return wm_modalkeymap_find_propvalue_iter(km, NULL, propvalue);
1000 }
1001
WM_modalkeymap_assign(wmKeyMap * km,const char * opname)1002 void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
1003 {
1004 wmOperatorType *ot = WM_operatortype_find(opname, 0);
1005
1006 if (ot) {
1007 ot->modalkeymap = km;
1008 }
1009 else {
1010 CLOG_ERROR(WM_LOG_KEYMAPS, "unknown operator '%s'", opname);
1011 }
1012 }
1013
wm_user_modal_keymap_set_items(wmWindowManager * wm,wmKeyMap * km)1014 static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km)
1015 {
1016 /* here we convert propvalue string values delayed, due to python keymaps
1017 * being created before the actual modal keymaps, so no modal_items */
1018 wmKeyMap *defaultkm;
1019 wmKeyMapItem *kmi;
1020 int propvalue;
1021
1022 if (km && (km->flag & KEYMAP_MODAL) && !km->modal_items) {
1023 if (wm->defaultconf == NULL) {
1024 return;
1025 }
1026
1027 defaultkm = WM_keymap_list_find(&wm->defaultconf->keymaps, km->idname, 0, 0);
1028
1029 if (!defaultkm) {
1030 return;
1031 }
1032
1033 km->modal_items = defaultkm->modal_items;
1034 km->poll = defaultkm->poll;
1035 km->poll_modal_item = defaultkm->poll_modal_item;
1036
1037 if (km->modal_items) {
1038 for (kmi = km->items.first; kmi; kmi = kmi->next) {
1039 if (kmi->propvalue_str[0]) {
1040 if (RNA_enum_value_from_id(km->modal_items, kmi->propvalue_str, &propvalue)) {
1041 kmi->propvalue = propvalue;
1042 }
1043 kmi->propvalue_str[0] = '\0';
1044 }
1045 }
1046 }
1047 }
1048 }
1049
1050 /** \} */
1051
1052 /* -------------------------------------------------------------------- */
1053 /** \name Text from Key Events
1054 * \{ */
1055
key_event_glyph_or_text(const int font_id,const char * text,const char * single_glyph)1056 static const char *key_event_glyph_or_text(const int font_id,
1057 const char *text,
1058 const char *single_glyph)
1059 {
1060 BLI_assert(single_glyph == NULL || (BLI_strlen_utf8(single_glyph) == 1));
1061 return (single_glyph && BLF_has_glyph(font_id, BLI_str_utf8_as_unicode(single_glyph))) ?
1062 single_glyph :
1063 text;
1064 }
1065
WM_key_event_string(const short type,const bool compact)1066 const char *WM_key_event_string(const short type, const bool compact)
1067 {
1068 if (compact) {
1069 /* String storing a single unicode character or NULL. */
1070 const char *single_glyph = NULL;
1071 int font_id = BLF_default();
1072 const enum {
1073 UNIX,
1074 MACOS,
1075 MSWIN,
1076 } platform =
1077
1078 #if defined(__APPLE__)
1079 MACOS
1080 #elif defined(_WIN32)
1081 MSWIN
1082 #else
1083 UNIX
1084 #endif
1085 ;
1086
1087 switch (type) {
1088 case EVT_LEFTSHIFTKEY:
1089 case EVT_RIGHTSHIFTKEY: {
1090 if (platform == MACOS) {
1091 single_glyph = "\xe2\x87\xa7";
1092 }
1093 return key_event_glyph_or_text(
1094 font_id, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Shift"), single_glyph);
1095 }
1096 case EVT_LEFTCTRLKEY:
1097 case EVT_RIGHTCTRLKEY:
1098 if (platform == MACOS) {
1099 return key_event_glyph_or_text(font_id, "^", "\xe2\x8c\x83");
1100 }
1101 return IFACE_("Ctrl");
1102 case EVT_LEFTALTKEY:
1103 case EVT_RIGHTALTKEY: {
1104 if (platform == MACOS) {
1105 /* Option symbol on Mac keyboard. */
1106 single_glyph = "\xe2\x8c\xa5";
1107 }
1108 return key_event_glyph_or_text(font_id, IFACE_("Alt"), single_glyph);
1109 }
1110 case EVT_OSKEY: {
1111 if (platform == MACOS) {
1112 return key_event_glyph_or_text(font_id, IFACE_("Cmd"), "\xe2\x8c\x98");
1113 }
1114 if (platform == MSWIN) {
1115 return key_event_glyph_or_text(font_id, IFACE_("Win"), "\xe2\x9d\x96");
1116 }
1117 return IFACE_("OS");
1118 } break;
1119 case EVT_TABKEY:
1120 return key_event_glyph_or_text(font_id, IFACE_("Tab"), "\xe2\xad\xbe");
1121 case EVT_BACKSPACEKEY:
1122 return key_event_glyph_or_text(font_id, IFACE_("Bksp"), "\xe2\x8c\xab");
1123 case EVT_ESCKEY:
1124 if (platform == MACOS) {
1125 single_glyph = "\xe2\x8e\x8b";
1126 }
1127 return key_event_glyph_or_text(font_id, IFACE_("Esc"), single_glyph);
1128 case EVT_RETKEY:
1129 return key_event_glyph_or_text(font_id, IFACE_("Enter"), "\xe2\x86\xb5");
1130 case EVT_SPACEKEY:
1131 return key_event_glyph_or_text(font_id, IFACE_("Space"), "\xe2\x90\xa3");
1132 case EVT_LEFTARROWKEY:
1133 return key_event_glyph_or_text(font_id, IFACE_("Left"), "\xe2\x86\x90");
1134 case EVT_UPARROWKEY:
1135 return key_event_glyph_or_text(font_id, IFACE_("Up"), "\xe2\x86\x91");
1136 case EVT_RIGHTARROWKEY:
1137 return key_event_glyph_or_text(font_id, IFACE_("Right"), "\xe2\x86\x92");
1138 case EVT_DOWNARROWKEY:
1139 return key_event_glyph_or_text(font_id, IFACE_("Down"), "\xe2\x86\x93");
1140 }
1141 }
1142
1143 const EnumPropertyItem *it;
1144 const int i = RNA_enum_from_value(rna_enum_event_type_items, (int)type);
1145
1146 if (i == -1) {
1147 return "";
1148 }
1149 it = &rna_enum_event_type_items[i];
1150
1151 /* We first try enum items' description (abused as shortname here),
1152 * and fall back to usual name if empty. */
1153 if (compact && it->description[0]) {
1154 /* XXX No context for enum descriptions... In practice shall not be an issue though. */
1155 return IFACE_(it->description);
1156 }
1157
1158 return CTX_IFACE_(BLT_I18NCONTEXT_UI_EVENTS, it->name);
1159 }
1160
1161 /* TODO: also support (some) value, like e.g. double-click? */
WM_keymap_item_raw_to_string(const short shift,const short ctrl,const short alt,const short oskey,const short keymodifier,const short val,const short type,const bool compact,char * result,const int result_len)1162 int WM_keymap_item_raw_to_string(const short shift,
1163 const short ctrl,
1164 const short alt,
1165 const short oskey,
1166 const short keymodifier,
1167 const short val,
1168 const short type,
1169 const bool compact,
1170 char *result,
1171 const int result_len)
1172 {
1173 #define ADD_SEP \
1174 if (p != buf) { \
1175 *p++ = ' '; \
1176 } \
1177 (void)0
1178
1179 char buf[128];
1180 char *p = buf;
1181
1182 buf[0] = '\0';
1183
1184 /* TODO: support order (KM_SHIFT vs. KM_SHIFT2) ? */
1185 if (shift == KM_ANY && ctrl == KM_ANY && alt == KM_ANY && oskey == KM_ANY) {
1186 /* Don't show anything for any mapping. */
1187 }
1188 else {
1189 if (shift) {
1190 ADD_SEP;
1191 p += BLI_strcpy_rlen(p, WM_key_event_string(EVT_LEFTSHIFTKEY, true));
1192 }
1193
1194 if (ctrl) {
1195 ADD_SEP;
1196 p += BLI_strcpy_rlen(p, WM_key_event_string(EVT_LEFTCTRLKEY, true));
1197 }
1198
1199 if (alt) {
1200 ADD_SEP;
1201 p += BLI_strcpy_rlen(p, WM_key_event_string(EVT_LEFTALTKEY, true));
1202 }
1203
1204 if (oskey) {
1205 ADD_SEP;
1206 p += BLI_strcpy_rlen(p, WM_key_event_string(EVT_OSKEY, true));
1207 }
1208 }
1209
1210 if (keymodifier) {
1211 ADD_SEP;
1212 p += BLI_strcpy_rlen(p, WM_key_event_string(keymodifier, compact));
1213 }
1214
1215 if (type) {
1216 ADD_SEP;
1217 if (val == KM_DBL_CLICK) {
1218 p += BLI_strcpy_rlen(p, IFACE_("dbl-"));
1219 }
1220 p += BLI_strcpy_rlen(p, WM_key_event_string(type, compact));
1221 }
1222
1223 /* We assume size of buf is enough to always store any possible shortcut,
1224 * but let's add a debug check about it! */
1225 BLI_assert(p - buf < sizeof(buf));
1226
1227 /* We need utf8 here, otherwise we may 'cut' some unicode chars like arrows... */
1228 return BLI_strncpy_utf8_rlen(result, buf, result_len);
1229
1230 #undef ADD_SEP
1231 }
1232
WM_keymap_item_to_string(wmKeyMapItem * kmi,const bool compact,char * result,const int result_len)1233 int WM_keymap_item_to_string(wmKeyMapItem *kmi,
1234 const bool compact,
1235 char *result,
1236 const int result_len)
1237 {
1238 return WM_keymap_item_raw_to_string(kmi->shift,
1239 kmi->ctrl,
1240 kmi->alt,
1241 kmi->oskey,
1242 kmi->keymodifier,
1243 kmi->val,
1244 kmi->type,
1245 compact,
1246 result,
1247 result_len);
1248 }
1249
WM_modalkeymap_items_to_string(wmKeyMap * km,const int propvalue,const bool compact,char * result,const int result_len)1250 int WM_modalkeymap_items_to_string(
1251 wmKeyMap *km, const int propvalue, const bool compact, char *result, const int result_len)
1252 {
1253 int totlen = 0;
1254 bool add_sep = false;
1255
1256 if (km) {
1257 wmKeyMapItem *kmi;
1258
1259 /* Find all shortcuts related to that propvalue! */
1260 for (kmi = WM_modalkeymap_find_propvalue(km, propvalue); kmi && totlen < (result_len - 2);
1261 kmi = wm_modalkeymap_find_propvalue_iter(km, kmi, propvalue)) {
1262 if (add_sep) {
1263 result[totlen++] = '/';
1264 result[totlen] = '\0';
1265 }
1266 else {
1267 add_sep = true;
1268 }
1269 totlen += WM_keymap_item_to_string(kmi, compact, &result[totlen], result_len - totlen);
1270 }
1271 }
1272
1273 return totlen;
1274 }
1275
WM_modalkeymap_operator_items_to_string(wmOperatorType * ot,const int propvalue,const bool compact,char * result,const int result_len)1276 int WM_modalkeymap_operator_items_to_string(wmOperatorType *ot,
1277 const int propvalue,
1278 const bool compact,
1279 char *result,
1280 const int result_len)
1281 {
1282 wmWindowManager *wm = G_MAIN->wm.first;
1283 wmKeyMap *keymap = WM_keymap_active(wm, ot->modalkeymap);
1284 return WM_modalkeymap_items_to_string(keymap, propvalue, compact, result, result_len);
1285 }
1286
WM_modalkeymap_operator_items_to_string_buf(wmOperatorType * ot,const int propvalue,const bool compact,const int max_len,int * r_available_len,char ** r_result)1287 char *WM_modalkeymap_operator_items_to_string_buf(wmOperatorType *ot,
1288 const int propvalue,
1289 const bool compact,
1290 const int max_len,
1291 int *r_available_len,
1292 char **r_result)
1293 {
1294 char *ret = *r_result;
1295
1296 if (*r_available_len > 1) {
1297 int used_len = WM_modalkeymap_operator_items_to_string(
1298 ot, propvalue, compact, ret, min_ii(*r_available_len, max_len)) +
1299 1;
1300
1301 *r_available_len -= used_len;
1302 *r_result += used_len;
1303 if (*r_available_len == 0) {
1304 (*r_result)--; /* So that *result keeps pointing on a valid char, we'll stay on it anyway. */
1305 }
1306 }
1307 else {
1308 *ret = '\0';
1309 }
1310
1311 return ret;
1312 }
1313
1314 /** \} */
1315
wm_keymap_item_find_in_keymap(wmKeyMap * keymap,const char * opname,IDProperty * properties,const bool is_strict,const struct wmKeyMapItemFind_Params * params)1316 static wmKeyMapItem *wm_keymap_item_find_in_keymap(wmKeyMap *keymap,
1317 const char *opname,
1318 IDProperty *properties,
1319 const bool is_strict,
1320 const struct wmKeyMapItemFind_Params *params)
1321 {
1322 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
1323 /* skip disabled keymap items [T38447] */
1324 if (kmi->flag & KMI_INACTIVE) {
1325 continue;
1326 }
1327
1328 bool kmi_match = false;
1329
1330 if (STREQ(kmi->idname, opname)) {
1331 if (properties) {
1332 /* example of debugging keymaps */
1333 #if 0
1334 if (kmi->ptr) {
1335 if (STREQ("MESH_OT_rip_move", opname)) {
1336 printf("OPERATOR\n");
1337 IDP_print(properties);
1338 printf("KEYMAP\n");
1339 IDP_print(kmi->ptr->data);
1340 }
1341 }
1342 #endif
1343
1344 if (kmi->ptr && IDP_EqualsProperties_ex(properties, kmi->ptr->data, is_strict)) {
1345 kmi_match = true;
1346 }
1347 /* Debug only, helps spotting mismatches between menu entries and shortcuts! */
1348 else if (G.debug & G_DEBUG_WM) {
1349 if (is_strict && kmi->ptr) {
1350 wmOperatorType *ot = WM_operatortype_find(opname, true);
1351 if (ot) {
1352 /* make a copy of the properties and set unset ones to their default values. */
1353 PointerRNA opptr;
1354 IDProperty *properties_default = IDP_CopyProperty(kmi->ptr->data);
1355
1356 RNA_pointer_create(NULL, ot->srna, properties_default, &opptr);
1357 WM_operator_properties_default(&opptr, true);
1358
1359 if (IDP_EqualsProperties_ex(properties, properties_default, is_strict)) {
1360 char kmi_str[128];
1361 WM_keymap_item_to_string(kmi, false, kmi_str, sizeof(kmi_str));
1362 /* Note gievn properties could come from other things than menu entry... */
1363 printf(
1364 "%s: Some set values in menu entry match default op values, "
1365 "this might not be desired!\n",
1366 opname);
1367 printf("\tkm: '%s', kmi: '%s'\n", keymap->idname, kmi_str);
1368 #ifndef NDEBUG
1369 # ifdef WITH_PYTHON
1370 printf("OPERATOR\n");
1371 IDP_print(properties);
1372 printf("KEYMAP\n");
1373 IDP_print(kmi->ptr->data);
1374 # endif
1375 #endif
1376 printf("\n");
1377 }
1378
1379 IDP_FreeProperty(properties_default);
1380 }
1381 }
1382 }
1383 }
1384 else {
1385 kmi_match = true;
1386 }
1387
1388 if (kmi_match) {
1389 if ((params == NULL) || params->filter_fn(keymap, kmi, params->user_data)) {
1390 return kmi;
1391 }
1392 }
1393 }
1394 }
1395 return NULL;
1396 }
1397
wm_keymap_item_find_handlers(const bContext * C,ListBase * handlers,const char * opname,int UNUSED (opcontext),IDProperty * properties,const bool is_strict,const struct wmKeyMapItemFind_Params * params,wmKeyMap ** r_keymap)1398 static wmKeyMapItem *wm_keymap_item_find_handlers(const bContext *C,
1399 ListBase *handlers,
1400 const char *opname,
1401 int UNUSED(opcontext),
1402 IDProperty *properties,
1403 const bool is_strict,
1404 const struct wmKeyMapItemFind_Params *params,
1405 wmKeyMap **r_keymap)
1406 {
1407 wmWindowManager *wm = CTX_wm_manager(C);
1408
1409 /* find keymap item in handlers */
1410 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
1411 if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
1412 wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
1413 wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler);
1414 if (keymap && WM_keymap_poll((bContext *)C, keymap)) {
1415 wmKeyMapItem *kmi = wm_keymap_item_find_in_keymap(
1416 keymap, opname, properties, is_strict, params);
1417 if (kmi != NULL) {
1418 if (r_keymap) {
1419 *r_keymap = keymap;
1420 }
1421 return kmi;
1422 }
1423 }
1424 }
1425 }
1426 /* ensure un-initialized keymap is never used */
1427 if (r_keymap) {
1428 *r_keymap = NULL;
1429 }
1430 return NULL;
1431 }
1432
wm_keymap_item_find_props(const bContext * C,const char * opname,int opcontext,IDProperty * properties,const bool is_strict,const struct wmKeyMapItemFind_Params * params,wmKeyMap ** r_keymap)1433 static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C,
1434 const char *opname,
1435 int opcontext,
1436 IDProperty *properties,
1437 const bool is_strict,
1438 const struct wmKeyMapItemFind_Params *params,
1439 wmKeyMap **r_keymap)
1440 {
1441 wmWindow *win = CTX_wm_window(C);
1442 ScrArea *area = CTX_wm_area(C);
1443 ARegion *region = CTX_wm_region(C);
1444 wmKeyMapItem *found = NULL;
1445
1446 /* look into multiple handler lists to find the item */
1447 if (win) {
1448 found = wm_keymap_item_find_handlers(
1449 C, &win->modalhandlers, opname, opcontext, properties, is_strict, params, r_keymap);
1450 if (found == NULL) {
1451 found = wm_keymap_item_find_handlers(
1452 C, &win->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
1453 }
1454 }
1455
1456 if (area && found == NULL) {
1457 found = wm_keymap_item_find_handlers(
1458 C, &area->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
1459 }
1460
1461 if (found == NULL) {
1462 if (ELEM(opcontext, WM_OP_EXEC_REGION_WIN, WM_OP_INVOKE_REGION_WIN)) {
1463 if (area) {
1464 if (!(region && region->regiontype == RGN_TYPE_WINDOW)) {
1465 region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
1466 }
1467
1468 if (region) {
1469 found = wm_keymap_item_find_handlers(
1470 C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
1471 }
1472 }
1473 }
1474 else if (ELEM(opcontext, WM_OP_EXEC_REGION_CHANNELS, WM_OP_INVOKE_REGION_CHANNELS)) {
1475 if (!(region && region->regiontype == RGN_TYPE_CHANNELS)) {
1476 region = BKE_area_find_region_type(area, RGN_TYPE_CHANNELS);
1477 }
1478
1479 if (region) {
1480 found = wm_keymap_item_find_handlers(
1481 C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
1482 }
1483 }
1484 else if (ELEM(opcontext, WM_OP_EXEC_REGION_PREVIEW, WM_OP_INVOKE_REGION_PREVIEW)) {
1485 if (!(region && region->regiontype == RGN_TYPE_PREVIEW)) {
1486 region = BKE_area_find_region_type(area, RGN_TYPE_PREVIEW);
1487 }
1488
1489 if (region) {
1490 found = wm_keymap_item_find_handlers(
1491 C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
1492 }
1493 }
1494 else {
1495 if (region) {
1496 found = wm_keymap_item_find_handlers(
1497 C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
1498 }
1499 }
1500 }
1501
1502 return found;
1503 }
1504
wm_keymap_item_find(const bContext * C,const char * opname,int opcontext,IDProperty * properties,bool is_strict,const struct wmKeyMapItemFind_Params * params,wmKeyMap ** r_keymap)1505 static wmKeyMapItem *wm_keymap_item_find(const bContext *C,
1506 const char *opname,
1507 int opcontext,
1508 IDProperty *properties,
1509 bool is_strict,
1510 const struct wmKeyMapItemFind_Params *params,
1511 wmKeyMap **r_keymap)
1512 {
1513 wmKeyMapItem *found;
1514
1515 /* XXX Hack! Macro operators in menu entry have their whole props defined,
1516 * which is not the case for relevant keymap entries.
1517 * Could be good to check and harmonize this,
1518 * but for now always compare non-strict in this case. */
1519 wmOperatorType *ot = WM_operatortype_find(opname, true);
1520 if (ot) {
1521 is_strict = is_strict && ((ot->flag & OPTYPE_MACRO) == 0);
1522 }
1523
1524 found = wm_keymap_item_find_props(C, opname, opcontext, properties, is_strict, params, r_keymap);
1525
1526 /* This block is *only* useful in one case: when op uses an enum menu in its prop member
1527 * (then, we want to rerun a comparison with that 'prop' unset). Note this remains brittle,
1528 * since now any enum prop may be used in UI (specified by name), ot->prop is not so much used...
1529 * Otherwise:
1530 * * If non-strict, unset properties always match set ones in IDP_EqualsProperties_ex.
1531 * * If strict, unset properties never match set ones in IDP_EqualsProperties_ex,
1532 * and we do not want that to change (else we get things like T41757)!
1533 * ...so in either case, re-running a comparison with unset props set to default is useless.
1534 */
1535 if (!found && properties) {
1536 if (ot && ot->prop) { /* XXX Shall we also check ot->prop is actually an enum? */
1537 /* make a copy of the properties and unset the 'ot->prop' one if set. */
1538 PointerRNA opptr;
1539 IDProperty *properties_temp = IDP_CopyProperty(properties);
1540
1541 RNA_pointer_create(NULL, ot->srna, properties_temp, &opptr);
1542
1543 if (RNA_property_is_set(&opptr, ot->prop)) {
1544 /* For operator that has enum menu,
1545 * unset it so its value does not affect comparison result. */
1546 RNA_property_unset(&opptr, ot->prop);
1547
1548 found = wm_keymap_item_find_props(
1549 C, opname, opcontext, properties_temp, is_strict, params, r_keymap);
1550 }
1551
1552 IDP_FreeProperty(properties_temp);
1553 }
1554 }
1555
1556 /* Debug only, helps spotting mismatches between menu entries and shortcuts! */
1557 if (G.debug & G_DEBUG_WM) {
1558 if (!found && is_strict && properties) {
1559 wmKeyMap *km;
1560 wmKeyMapItem *kmi;
1561 if (ot) {
1562 /* make a copy of the properties and set unset ones to their default values. */
1563 PointerRNA opptr;
1564 IDProperty *properties_default = IDP_CopyProperty(properties);
1565
1566 RNA_pointer_create(NULL, ot->srna, properties_default, &opptr);
1567 WM_operator_properties_default(&opptr, true);
1568
1569 kmi = wm_keymap_item_find_props(
1570 C, opname, opcontext, properties_default, is_strict, params, &km);
1571 if (kmi) {
1572 char kmi_str[128];
1573 WM_keymap_item_to_string(kmi, false, kmi_str, sizeof(kmi_str));
1574 printf(
1575 "%s: Some set values in keymap entry match default op values, "
1576 "this might not be desired!\n",
1577 opname);
1578 printf("\tkm: '%s', kmi: '%s'\n", km->idname, kmi_str);
1579 #ifndef NDEBUG
1580 # ifdef WITH_PYTHON
1581 printf("OPERATOR\n");
1582 IDP_print(properties);
1583 printf("KEYMAP\n");
1584 IDP_print(kmi->ptr->data);
1585 # endif
1586 #endif
1587 printf("\n");
1588 }
1589
1590 IDP_FreeProperty(properties_default);
1591 }
1592 }
1593 }
1594
1595 return found;
1596 }
1597
kmi_filter_is_visible(const wmKeyMap * UNUSED (km),const wmKeyMapItem * kmi,void * UNUSED (user_data))1598 static bool kmi_filter_is_visible(const wmKeyMap *UNUSED(km),
1599 const wmKeyMapItem *kmi,
1600 void *UNUSED(user_data))
1601 {
1602 return ((WM_key_event_string(kmi->type, false)[0] != '\0') &&
1603 (IS_EVENT_ACTIONZONE(kmi->type) == false));
1604 }
1605
WM_key_event_operator_string(const bContext * C,const char * opname,int opcontext,IDProperty * properties,const bool is_strict,char * result,const int result_len)1606 char *WM_key_event_operator_string(const bContext *C,
1607 const char *opname,
1608 int opcontext,
1609 IDProperty *properties,
1610 const bool is_strict,
1611 char *result,
1612 const int result_len)
1613 {
1614 wmKeyMapItem *kmi = wm_keymap_item_find(C,
1615 opname,
1616 opcontext,
1617 properties,
1618 is_strict,
1619 &(struct wmKeyMapItemFind_Params){
1620 .filter_fn = kmi_filter_is_visible,
1621 .user_data = NULL,
1622 },
1623 NULL);
1624 if (kmi) {
1625 WM_keymap_item_to_string(kmi, false, result, result_len);
1626 return result;
1627 }
1628
1629 return NULL;
1630 }
1631
kmi_filter_is_visible_type_mask(const wmKeyMap * km,const wmKeyMapItem * kmi,void * user_data)1632 static bool kmi_filter_is_visible_type_mask(const wmKeyMap *km,
1633 const wmKeyMapItem *kmi,
1634 void *user_data)
1635 {
1636 short *mask_pair = user_data;
1637 return ((WM_event_type_mask_test(kmi->type, mask_pair[0]) == true) &&
1638 (WM_event_type_mask_test(kmi->type, mask_pair[1]) == false) &&
1639 kmi_filter_is_visible(km, kmi, user_data));
1640 }
1641
1642 /**
1643 * \param include_mask, exclude_mask:
1644 * Event types to include/exclude when looking up keys (#eEventType_Mask).
1645 */
WM_key_event_operator(const bContext * C,const char * opname,int opcontext,IDProperty * properties,const short include_mask,const short exclude_mask,wmKeyMap ** r_keymap)1646 wmKeyMapItem *WM_key_event_operator(const bContext *C,
1647 const char *opname,
1648 int opcontext,
1649 IDProperty *properties,
1650 const short include_mask,
1651 const short exclude_mask,
1652 wmKeyMap **r_keymap)
1653 {
1654 short user_data_mask[2] = {include_mask, exclude_mask};
1655 bool use_mask = (include_mask != EVT_TYPE_MASK_ALL) || (exclude_mask != 0);
1656 return wm_keymap_item_find(
1657 C,
1658 opname,
1659 opcontext,
1660 properties,
1661 true,
1662 &(struct wmKeyMapItemFind_Params){
1663 .filter_fn = use_mask ? kmi_filter_is_visible_type_mask : kmi_filter_is_visible,
1664 .user_data = use_mask ? user_data_mask : NULL,
1665 },
1666 r_keymap);
1667 }
1668
WM_key_event_operator_from_keymap(wmKeyMap * keymap,const char * opname,IDProperty * properties,const short include_mask,const short exclude_mask)1669 wmKeyMapItem *WM_key_event_operator_from_keymap(wmKeyMap *keymap,
1670 const char *opname,
1671 IDProperty *properties,
1672 const short include_mask,
1673 const short exclude_mask)
1674 {
1675 short user_data_mask[2] = {include_mask, exclude_mask};
1676 bool use_mask = (include_mask != EVT_TYPE_MASK_ALL) || (exclude_mask != 0);
1677 return wm_keymap_item_find_in_keymap(
1678 keymap,
1679 opname,
1680 properties,
1681 true,
1682 &(struct wmKeyMapItemFind_Params){
1683 .filter_fn = use_mask ? kmi_filter_is_visible_type_mask : kmi_filter_is_visible,
1684 .user_data = use_mask ? user_data_mask : NULL,
1685 });
1686 }
1687
WM_keymap_item_compare(wmKeyMapItem * k1,wmKeyMapItem * k2)1688 bool WM_keymap_item_compare(wmKeyMapItem *k1, wmKeyMapItem *k2)
1689 {
1690 int k1type, k2type;
1691
1692 if (k1->flag & KMI_INACTIVE || k2->flag & KMI_INACTIVE) {
1693 return 0;
1694 }
1695
1696 /* take event mapping into account */
1697 k1type = WM_userdef_event_map(k1->type);
1698 k2type = WM_userdef_event_map(k2->type);
1699
1700 if (k1type != KM_ANY && k2type != KM_ANY && k1type != k2type) {
1701 return 0;
1702 }
1703
1704 if (k1->val != KM_ANY && k2->val != KM_ANY) {
1705 /* take click, press, release conflict into account */
1706 if (k1->val == KM_CLICK && ELEM(k2->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0) {
1707 return 0;
1708 }
1709 if (k2->val == KM_CLICK && ELEM(k1->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0) {
1710 return 0;
1711 }
1712 if (k1->val != k2->val) {
1713 return 0;
1714 }
1715 }
1716
1717 if (k1->shift != KM_ANY && k2->shift != KM_ANY && k1->shift != k2->shift) {
1718 return 0;
1719 }
1720
1721 if (k1->ctrl != KM_ANY && k2->ctrl != KM_ANY && k1->ctrl != k2->ctrl) {
1722 return 0;
1723 }
1724
1725 if (k1->alt != KM_ANY && k2->alt != KM_ANY && k1->alt != k2->alt) {
1726 return 0;
1727 }
1728
1729 if (k1->oskey != KM_ANY && k2->oskey != KM_ANY && k1->oskey != k2->oskey) {
1730 return 0;
1731 }
1732
1733 if (k1->keymodifier != k2->keymodifier) {
1734 return 0;
1735 }
1736
1737 return 1;
1738 }
1739
1740 /** \} */
1741
1742 /* -------------------------------------------------------------------- */
1743 /** \name Update Final Configuration
1744 *
1745 * On load or other changes, the final user key configuration is rebuilt from the preset,
1746 * add-on and user preferences keymaps. We also test if the final configuration changed and write
1747 * the changes to the user preferences.
1748 * \{ */
1749
1750 /* so operator removal can trigger update */
1751 enum {
1752 WM_KEYMAP_UPDATE_RECONFIGURE = (1 << 0),
1753
1754 /* ensure all wmKeyMap have their operator types validated after removing an operator */
1755 WM_KEYMAP_UPDATE_OPERATORTYPE = (1 << 1),
1756 };
1757
1758 static char wm_keymap_update_flag = 0;
1759
WM_keyconfig_update_tag(wmKeyMap * keymap,wmKeyMapItem * kmi)1760 void WM_keyconfig_update_tag(wmKeyMap *keymap, wmKeyMapItem *kmi)
1761 {
1762 /* quick tag to do delayed keymap updates */
1763 wm_keymap_update_flag |= WM_KEYMAP_UPDATE_RECONFIGURE;
1764
1765 if (keymap) {
1766 keymap->flag |= KEYMAP_UPDATE;
1767 }
1768 if (kmi) {
1769 kmi->flag |= KMI_UPDATE;
1770 }
1771 }
1772
WM_keyconfig_update_operatortype(void)1773 void WM_keyconfig_update_operatortype(void)
1774 {
1775 wm_keymap_update_flag |= WM_KEYMAP_UPDATE_OPERATORTYPE;
1776 }
1777
wm_keymap_test_and_clear_update(wmKeyMap * km)1778 static bool wm_keymap_test_and_clear_update(wmKeyMap *km)
1779 {
1780 wmKeyMapItem *kmi;
1781 int update;
1782
1783 update = (km->flag & KEYMAP_UPDATE);
1784 km->flag &= ~KEYMAP_UPDATE;
1785
1786 for (kmi = km->items.first; kmi; kmi = kmi->next) {
1787 update = update || (kmi->flag & KMI_UPDATE);
1788 kmi->flag &= ~KMI_UPDATE;
1789 }
1790
1791 return (update != 0);
1792 }
1793
wm_keymap_preset(wmWindowManager * wm,wmKeyMap * km)1794 static wmKeyMap *wm_keymap_preset(wmWindowManager *wm, wmKeyMap *km)
1795 {
1796 wmKeyConfig *keyconf = WM_keyconfig_active(wm);
1797 wmKeyMap *keymap;
1798
1799 keymap = WM_keymap_list_find(&keyconf->keymaps, km->idname, km->spaceid, km->regionid);
1800 if (!keymap && wm->defaultconf) {
1801 keymap = WM_keymap_list_find(&wm->defaultconf->keymaps, km->idname, km->spaceid, km->regionid);
1802 }
1803
1804 return keymap;
1805 }
1806
WM_keyconfig_update(wmWindowManager * wm)1807 void WM_keyconfig_update(wmWindowManager *wm)
1808 {
1809 wmKeyMap *km, *defaultmap, *addonmap, *usermap, *kmn;
1810 wmKeyMapItem *kmi;
1811 wmKeyMapDiffItem *kmdi;
1812 bool compat_update = false;
1813
1814 if (G.background) {
1815 return;
1816 }
1817
1818 if (wm_keymap_update_flag == 0) {
1819 return;
1820 }
1821
1822 if (wm_keymap_update_flag & WM_KEYMAP_UPDATE_OPERATORTYPE) {
1823 /* an operatortype has been removed, this wont happen often
1824 * but when it does we have to check _every_ keymap item */
1825 wmKeyConfig *kc;
1826
1827 ListBase *keymaps_lb[] = {
1828 &U.user_keymaps,
1829 &wm->userconf->keymaps,
1830 &wm->defaultconf->keymaps,
1831 &wm->addonconf->keymaps,
1832 NULL,
1833 };
1834
1835 int i;
1836
1837 for (i = 0; keymaps_lb[i]; i++) {
1838 wm_keymap_item_properties_update_ot_from_list(keymaps_lb[i]);
1839 }
1840
1841 for (kc = wm->keyconfigs.first; kc; kc = kc->next) {
1842 wm_keymap_item_properties_update_ot_from_list(&kc->keymaps);
1843 }
1844
1845 wm_keymap_update_flag &= ~WM_KEYMAP_UPDATE_OPERATORTYPE;
1846 }
1847
1848 if (wm_keymap_update_flag == 0) {
1849 return;
1850 }
1851
1852 /* update operator properties for non-modal user keymaps */
1853 for (km = U.user_keymaps.first; km; km = km->next) {
1854 if ((km->flag & KEYMAP_MODAL) == 0) {
1855 for (kmdi = km->diff_items.first; kmdi; kmdi = kmdi->next) {
1856 if (kmdi->add_item) {
1857 wm_keymap_item_properties_set(kmdi->add_item);
1858 }
1859 if (kmdi->remove_item) {
1860 wm_keymap_item_properties_set(kmdi->remove_item);
1861 }
1862 }
1863
1864 for (kmi = km->items.first; kmi; kmi = kmi->next) {
1865 wm_keymap_item_properties_set(kmi);
1866 }
1867 }
1868 }
1869
1870 /* update U.user_keymaps with user key configuration changes */
1871 for (km = wm->userconf->keymaps.first; km; km = km->next) {
1872 /* only diff if the user keymap was modified */
1873 if (wm_keymap_test_and_clear_update(km)) {
1874 /* find keymaps */
1875 defaultmap = wm_keymap_preset(wm, km);
1876 addonmap = WM_keymap_list_find(
1877 &wm->addonconf->keymaps, km->idname, km->spaceid, km->regionid);
1878
1879 /* diff */
1880 if (defaultmap) {
1881 wm_keymap_diff_update(&U.user_keymaps, defaultmap, addonmap, km);
1882 }
1883 }
1884 }
1885
1886 /* create user key configuration from preset + addon + user preferences */
1887 for (km = wm->defaultconf->keymaps.first; km; km = km->next) {
1888 /* find keymaps */
1889 defaultmap = wm_keymap_preset(wm, km);
1890 addonmap = WM_keymap_list_find(&wm->addonconf->keymaps, km->idname, km->spaceid, km->regionid);
1891 usermap = WM_keymap_list_find(&U.user_keymaps, km->idname, km->spaceid, km->regionid);
1892
1893 /* For now only the default map defines modal key-maps,
1894 * if we support modal keymaps for 'addonmap', these will need to be enabled too. */
1895 wm_user_modal_keymap_set_items(wm, defaultmap);
1896
1897 /* add */
1898 kmn = wm_keymap_patch_update(&wm->userconf->keymaps, defaultmap, addonmap, usermap);
1899
1900 if (kmn) {
1901 kmn->modal_items = km->modal_items;
1902 kmn->poll = km->poll;
1903 kmn->poll_modal_item = km->poll_modal_item;
1904 }
1905
1906 /* in case of old non-diff keymaps, force extra update to create diffs */
1907 compat_update = compat_update || (usermap && !(usermap->flag & KEYMAP_DIFF));
1908 }
1909
1910 wm_keymap_update_flag &= ~WM_KEYMAP_UPDATE_RECONFIGURE;
1911
1912 BLI_assert(wm_keymap_update_flag == 0);
1913
1914 if (compat_update) {
1915 WM_keyconfig_update_tag(NULL, NULL);
1916 WM_keyconfig_update(wm);
1917 }
1918 }
1919
1920 /** \} */
1921
1922 /* -------------------------------------------------------------------- */
1923 /** \name Event Handling
1924 *
1925 * Handlers have pointers to the keymap in the default configuration.
1926 * During event handling this function is called to get the keymap from the final configuration.
1927 * \{ */
1928
WM_keymap_active(const wmWindowManager * wm,wmKeyMap * keymap)1929 wmKeyMap *WM_keymap_active(const wmWindowManager *wm, wmKeyMap *keymap)
1930 {
1931 wmKeyMap *km;
1932
1933 if (!keymap) {
1934 return NULL;
1935 }
1936
1937 /* first user defined keymaps */
1938 km = WM_keymap_list_find(
1939 &wm->userconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
1940
1941 if (km) {
1942 return km;
1943 }
1944
1945 return keymap;
1946 }
1947
1948 /** \} */
1949
1950 /* -------------------------------------------------------------------- */
1951 /** \name Keymap Editor
1952 *
1953 * In the keymap editor the user key configuration is edited.
1954 * \{ */
1955
WM_keymap_item_restore_to_default(wmWindowManager * wm,wmKeyMap * keymap,wmKeyMapItem * kmi)1956 void WM_keymap_item_restore_to_default(wmWindowManager *wm, wmKeyMap *keymap, wmKeyMapItem *kmi)
1957 {
1958 wmKeyMap *defaultmap, *addonmap;
1959 wmKeyMapItem *orig;
1960
1961 if (!keymap) {
1962 return;
1963 }
1964
1965 /* construct default keymap from preset + addons */
1966 defaultmap = wm_keymap_preset(wm, keymap);
1967 addonmap = WM_keymap_list_find(
1968 &wm->addonconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
1969
1970 if (addonmap) {
1971 defaultmap = wm_keymap_copy(defaultmap);
1972 wm_keymap_addon_add(defaultmap, addonmap);
1973 }
1974
1975 /* find original item */
1976 orig = WM_keymap_item_find_id(defaultmap, kmi->id);
1977
1978 if (orig) {
1979 /* restore to original */
1980 if (!STREQ(orig->idname, kmi->idname)) {
1981 BLI_strncpy(kmi->idname, orig->idname, sizeof(kmi->idname));
1982 WM_keymap_item_properties_reset(kmi, NULL);
1983 }
1984
1985 if (orig->properties) {
1986 if (kmi->properties) {
1987 IDP_FreeProperty(kmi->properties);
1988 kmi->properties = NULL;
1989 }
1990
1991 kmi->properties = IDP_CopyProperty(orig->properties);
1992 kmi->ptr->data = kmi->properties;
1993 }
1994
1995 kmi->propvalue = orig->propvalue;
1996 kmi->type = orig->type;
1997 kmi->val = orig->val;
1998 kmi->shift = orig->shift;
1999 kmi->ctrl = orig->ctrl;
2000 kmi->alt = orig->alt;
2001 kmi->oskey = orig->oskey;
2002 kmi->keymodifier = orig->keymodifier;
2003 kmi->maptype = orig->maptype;
2004 kmi->flag = (kmi->flag & ~KMI_REPEAT_IGNORE) | (orig->flag & KMI_REPEAT_IGNORE);
2005
2006 WM_keyconfig_update_tag(keymap, kmi);
2007 }
2008
2009 /* free temporary keymap */
2010 if (addonmap) {
2011 WM_keymap_clear(defaultmap);
2012 MEM_freeN(defaultmap);
2013 }
2014 }
2015
WM_keymap_restore_to_default(wmKeyMap * keymap,wmWindowManager * wm)2016 void WM_keymap_restore_to_default(wmKeyMap *keymap, wmWindowManager *wm)
2017 {
2018 /* remove keymap from U.user_keymaps and update */
2019 wmKeyMap *usermap = WM_keymap_list_find(
2020 &U.user_keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
2021
2022 if (usermap) {
2023 WM_keymap_clear(usermap);
2024 BLI_freelinkN(&U.user_keymaps, usermap);
2025
2026 WM_keyconfig_update_tag(NULL, NULL);
2027 WM_keyconfig_update(wm);
2028 }
2029 }
2030
WM_keymap_item_find_id(wmKeyMap * keymap,int id)2031 wmKeyMapItem *WM_keymap_item_find_id(wmKeyMap *keymap, int id)
2032 {
2033 wmKeyMapItem *kmi;
2034
2035 for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
2036 if (kmi->id == id) {
2037 return kmi;
2038 }
2039 }
2040
2041 return NULL;
2042 }
2043
WM_bool_as_string(bool test)2044 const char *WM_bool_as_string(bool test)
2045 {
2046 return test ? IFACE_("ON") : IFACE_("OFF");
2047 }
2048
2049 /** \} */
2050