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