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, &region->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, &region->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, &region->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, &region->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