d61f9be170d5bcf7b1f5b8f261c90efaebd1b769
[blender.git] / source / blender / windowmanager / intern / wm_event_system.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  * Handle events and notifiers from GHOST input (mouse, keyboard, tablet, ndof).
24  *
25  * Also some operator reports utility functions.
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "DNA_listBase.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_windowmanager_types.h"
35 #include "DNA_userdef_types.h"
36
37 #include "MEM_guardedalloc.h"
38
39 #include "CLG_log.h"
40
41 #include "GHOST_C-api.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_dynstr.h"
45 #include "BLI_utildefines.h"
46 #include "BLI_math.h"
47 #include "BLI_timer.h"
48
49 #include "BKE_context.h"
50 #include "BKE_customdata.h"
51 #include "BKE_idprop.h"
52 #include "BKE_global.h"
53 #include "BKE_layer.h"
54 #include "BKE_main.h"
55 #include "BKE_report.h"
56 #include "BKE_scene.h"
57 #include "BKE_screen.h"
58 #include "BKE_workspace.h"
59
60 #include "BKE_sound.h"
61
62 #include "ED_fileselect.h"
63 #include "ED_info.h"
64 #include "ED_screen.h"
65 #include "ED_view3d.h"
66 #include "ED_util.h"
67 #include "ED_undo.h"
68
69 #include "RNA_access.h"
70
71 #include "UI_interface.h"
72
73 #include "PIL_time.h"
74
75 #include "WM_api.h"
76 #include "WM_types.h"
77 #include "WM_message.h"
78 #include "WM_toolsystem.h"
79
80 #include "wm.h"
81 #include "wm_window.h"
82 #include "wm_event_system.h"
83 #include "wm_event_types.h"
84
85 #include "RNA_enum_types.h"
86
87 #include "DEG_depsgraph.h"
88
89 /* Motion in pixels allowed before we don't consider single/double click,
90  * or detect the start of a tweak event. */
91 #define WM_EVENT_CLICK_TWEAK_THRESHOLD (U.tweak_threshold * U.dpi_fac)
92
93 static void wm_notifier_clear(wmNotifier *note);
94 static void update_tablet_data(wmWindow *win, wmEvent *event);
95
96 static int wm_operator_call_internal(bContext *C,
97                                      wmOperatorType *ot,
98                                      PointerRNA *properties,
99                                      ReportList *reports,
100                                      const short context,
101                                      const bool poll_only,
102                                      wmEvent *event);
103
104 /* ************ event management ************** */
105
106 wmEvent *wm_event_add_ex(wmWindow *win,
107                          const wmEvent *event_to_add,
108                          const wmEvent *event_to_add_after)
109 {
110   wmEvent *event = MEM_mallocN(sizeof(wmEvent), "wmEvent");
111
112   *event = *event_to_add;
113
114   update_tablet_data(win, event);
115
116   if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
117     /* We could have a preference to support relative tablet motion (we can't detect that). */
118     event->is_motion_absolute = ((event->tablet_data != NULL) &&
119                                  (event->tablet_data->Active != GHOST_kTabletModeNone));
120   }
121
122   if (event_to_add_after == NULL) {
123     BLI_addtail(&win->queue, event);
124   }
125   else {
126     /* note, strictly speaking this breaks const-correctness, however we're only changing 'next' member */
127     BLI_insertlinkafter(&win->queue, (void *)event_to_add_after, event);
128   }
129   return event;
130 }
131
132 wmEvent *wm_event_add(wmWindow *win, const wmEvent *event_to_add)
133 {
134   return wm_event_add_ex(win, event_to_add, NULL);
135 }
136
137 wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add)
138 {
139   if ((G.f & G_FLAG_EVENT_SIMULATE) == 0) {
140     BLI_assert(0);
141     return NULL;
142   }
143   wmEvent *event = wm_event_add(win, event_to_add);
144   win->eventstate->x = event->x;
145   win->eventstate->y = event->y;
146   return event;
147 }
148
149 void wm_event_free(wmEvent *event)
150 {
151   if (event->customdata) {
152     if (event->customdatafree) {
153       /* note: pointer to listbase struct elsewhere */
154       if (event->custom == EVT_DATA_DRAGDROP) {
155         ListBase *lb = event->customdata;
156         WM_drag_free_list(lb);
157       }
158       else {
159         MEM_freeN(event->customdata);
160       }
161     }
162   }
163
164   if (event->tablet_data) {
165     MEM_freeN((void *)event->tablet_data);
166   }
167
168   MEM_freeN(event);
169 }
170
171 void wm_event_free_all(wmWindow *win)
172 {
173   wmEvent *event;
174
175   while ((event = BLI_pophead(&win->queue))) {
176     wm_event_free(event);
177   }
178 }
179
180 void wm_event_init_from_window(wmWindow *win, wmEvent *event)
181 {
182   /* make sure we don't copy any owned pointers */
183   BLI_assert(win->eventstate->tablet_data == NULL);
184
185   *event = *(win->eventstate);
186 }
187
188 /* ********************* notifiers, listeners *************** */
189
190 static bool wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, void *reference)
191 {
192   wmNotifier *note;
193
194   for (note = wm->queue.first; note; note = note->next) {
195     if ((note->category | note->data | note->subtype | note->action) == type &&
196         note->reference == reference) {
197       return 1;
198     }
199   }
200
201   return 0;
202 }
203
204 /* XXX: in future, which notifiers to send to other windows? */
205 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
206 {
207   wmWindowManager *wm = CTX_wm_manager(C);
208   wmNotifier *note;
209
210   if (wm_test_duplicate_notifier(wm, type, reference)) {
211     return;
212   }
213
214   note = MEM_callocN(sizeof(wmNotifier), "notifier");
215
216   note->wm = wm;
217   BLI_addtail(&note->wm->queue, note);
218
219   note->window = CTX_wm_window(C);
220
221   note->category = type & NOTE_CATEGORY;
222   note->data = type & NOTE_DATA;
223   note->subtype = type & NOTE_SUBTYPE;
224   note->action = type & NOTE_ACTION;
225
226   note->reference = reference;
227 }
228
229 void WM_main_add_notifier(unsigned int type, void *reference)
230 {
231   Main *bmain = G_MAIN;
232   wmWindowManager *wm = bmain->wm.first;
233   wmNotifier *note;
234
235   if (!wm || wm_test_duplicate_notifier(wm, type, reference)) {
236     return;
237   }
238
239   note = MEM_callocN(sizeof(wmNotifier), "notifier");
240
241   note->wm = wm;
242   BLI_addtail(&note->wm->queue, note);
243
244   note->category = type & NOTE_CATEGORY;
245   note->data = type & NOTE_DATA;
246   note->subtype = type & NOTE_SUBTYPE;
247   note->action = type & NOTE_ACTION;
248
249   note->reference = reference;
250 }
251
252 /**
253  * Clear notifiers by reference, Used so listeners don't act on freed data.
254  */
255 void WM_main_remove_notifier_reference(const void *reference)
256 {
257   Main *bmain = G_MAIN;
258   wmWindowManager *wm = bmain->wm.first;
259
260   if (wm) {
261     wmNotifier *note, *note_next;
262
263     for (note = wm->queue.first; note; note = note_next) {
264       note_next = note->next;
265
266       if (note->reference == reference) {
267         /* don't remove because this causes problems for #wm_event_do_notifiers
268          * which may be looping on the data (deleting screens) */
269         wm_notifier_clear(note);
270       }
271     }
272
273     /* Remap instead. */
274 #if 0
275     if (wm->message_bus) {
276       WM_msg_id_remove(wm->message_bus, reference);
277     }
278 #endif
279   }
280 }
281
282 void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
283 {
284   Main *bmain = G_MAIN;
285   bScreen *sc;
286
287   for (sc = bmain->screens.first; sc; sc = sc->id.next) {
288     ScrArea *sa;
289
290     for (sa = sc->areabase.first; sa; sa = sa->next) {
291       SpaceLink *sl;
292
293       for (sl = sa->spacedata.first; sl; sl = sl->next) {
294         ED_spacedata_id_remap(sa, sl, old_id, new_id);
295       }
296     }
297   }
298
299   wmWindowManager *wm = bmain->wm.first;
300   if (wm && wm->message_bus) {
301     struct wmMsgBus *mbus = wm->message_bus;
302     if (new_id != NULL) {
303       WM_msg_id_update(mbus, old_id, new_id);
304     }
305     else {
306       WM_msg_id_remove(mbus, old_id);
307     }
308   }
309 }
310
311 static void wm_notifier_clear(wmNotifier *note)
312 {
313   /* NULL the entire notifier, only leaving (next, prev) members intact */
314   memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
315 }
316
317 void wm_event_do_depsgraph(bContext *C)
318 {
319   wmWindowManager *wm = CTX_wm_manager(C);
320   /* The whole idea of locked interface is to prevent viewport and whatever
321    * thread to modify the same data. Because of this, we can not perform
322    * dependency graph update.
323    */
324   if (wm->is_interface_locked) {
325     return;
326   }
327   /* Combine datamasks so 1 win doesn't disable UV's in another [#26448]. */
328   CustomData_MeshMasks win_combine_v3d_datamask = {0};
329   for (wmWindow *win = wm->windows.first; win; win = win->next) {
330     const Scene *scene = WM_window_get_active_scene(win);
331     const bScreen *screen = WM_window_get_active_screen(win);
332
333     ED_view3d_screen_datamask(C, scene, screen, &win_combine_v3d_datamask);
334   }
335   /* Update all the dependency graphs of visible view layers. */
336   for (wmWindow *win = wm->windows.first; win; win = win->next) {
337     Scene *scene = WM_window_get_active_scene(win);
338     ViewLayer *view_layer = WM_window_get_active_view_layer(win);
339     Main *bmain = CTX_data_main(C);
340     /* Copied to set's in scene_update_tagged_recursive() */
341     scene->customdata_mask = win_combine_v3d_datamask;
342     /* XXX, hack so operators can enforce datamasks [#26482], gl render */
343     CustomData_MeshMasks_update(&scene->customdata_mask, &scene->customdata_mask_modal);
344     /* TODO(sergey): For now all dependency graphs which are evaluated from
345      * workspace are considered active. This will work all fine with "locked"
346      * view layer and time across windows. This is to be granted separately,
347      * and for until then we have to accept ambiguities when object is shared
348      * across visible view layers and has overrides on it.
349      */
350     Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
351     DEG_make_active(depsgraph);
352     BKE_scene_graph_update_tagged(depsgraph, bmain);
353   }
354 }
355
356 /**
357  * Was part of #wm_event_do_notifiers, split out so it can be called once before entering the #WM_main loop.
358  * This ensures operators don't run before the UI and depsgraph are initialized.
359  */
360 void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
361 {
362   wmWindowManager *wm = CTX_wm_manager(C);
363   /* cached: editor refresh callbacks now, they get context */
364   for (wmWindow *win = wm->windows.first; win; win = win->next) {
365     const bScreen *screen = WM_window_get_active_screen(win);
366     ScrArea *sa;
367
368     CTX_wm_window_set(C, win);
369     for (sa = screen->areabase.first; sa; sa = sa->next) {
370       if (sa->do_refresh) {
371         CTX_wm_area_set(C, sa);
372         ED_area_do_refresh(C, sa);
373       }
374     }
375   }
376
377   wm_event_do_depsgraph(C);
378
379   CTX_wm_window_set(C, NULL);
380 }
381
382 /* called in mainloop */
383 void wm_event_do_notifiers(bContext *C)
384 {
385   wmWindowManager *wm = CTX_wm_manager(C);
386   wmNotifier *note, *next;
387   wmWindow *win;
388
389   if (wm == NULL) {
390     return;
391   }
392
393   BLI_timer_execute();
394
395   /* disable? - keep for now since its used for window level notifiers. */
396 #if 1
397   /* cache & catch WM level notifiers, such as frame change, scene/screen set */
398   for (win = wm->windows.first; win; win = win->next) {
399     Scene *scene = WM_window_get_active_scene(win);
400     bool do_anim = false;
401
402     CTX_wm_window_set(C, win);
403
404     for (note = wm->queue.first; note; note = next) {
405       next = note->next;
406
407       if (note->category == NC_WM) {
408         if (ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
409           wm->file_saved = 1;
410           wm_window_title(wm, win);
411         }
412         else if (note->data == ND_DATACHANGED) {
413           wm_window_title(wm, win);
414         }
415       }
416       if (note->window == win) {
417         if (note->category == NC_SCREEN) {
418           if (note->data == ND_WORKSPACE_SET) {
419             WorkSpace *ref_ws = note->reference;
420
421             UI_popup_handlers_remove_all(C, &win->modalhandlers);
422
423             WM_window_set_active_workspace(C, win, ref_ws);
424             if (G.debug & G_DEBUG_EVENTS) {
425               printf("%s: Workspace set %p\n", __func__, note->reference);
426             }
427           }
428           else if (note->data == ND_WORKSPACE_DELETE) {
429             WorkSpace *workspace = note->reference;
430
431             ED_workspace_delete(workspace, CTX_data_main(C), C, wm);  // XXX hrms, think this over!
432             if (G.debug & G_DEBUG_EVENTS) {
433               printf("%s: Workspace delete %p\n", __func__, workspace);
434             }
435           }
436           else if (note->data == ND_LAYOUTBROWSE) {
437             bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference);
438
439             /* free popup handlers only [#35434] */
440             UI_popup_handlers_remove_all(C, &win->modalhandlers);
441
442             ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */
443             if (G.debug & G_DEBUG_EVENTS) {
444               printf("%s: screen set %p\n", __func__, note->reference);
445             }
446           }
447           else if (note->data == ND_LAYOUTDELETE) {
448             WorkSpace *workspace = WM_window_get_active_workspace(win);
449             WorkSpaceLayout *layout = note->reference;
450
451             ED_workspace_layout_delete(workspace, layout, C);  // XXX hrms, think this over!
452             if (G.debug & G_DEBUG_EVENTS) {
453               printf("%s: screen delete %p\n", __func__, note->reference);
454             }
455           }
456         }
457       }
458
459       if (note->window == win ||
460           (note->window == NULL && (note->reference == NULL || note->reference == scene))) {
461         if (note->category == NC_SCENE) {
462           if (note->data == ND_FRAME) {
463             do_anim = true;
464           }
465         }
466       }
467       if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) {
468         ViewLayer *view_layer = CTX_data_view_layer(C);
469         ED_info_stats_clear(view_layer);
470         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL);
471       }
472     }
473     if (do_anim) {
474
475       /* XXX, quick frame changes can cause a crash if framechange and rendering
476        * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called
477        * twice which can depgraph update the same object at once */
478       if (G.is_rendering == false) {
479         /* depsgraph gets called, might send more notifiers */
480         Depsgraph *depsgraph = CTX_data_depsgraph(C);
481         ED_update_for_newframe(CTX_data_main(C), depsgraph);
482       }
483     }
484   }
485
486   /* the notifiers are sent without context, to keep it clean */
487   while ((note = BLI_pophead(&wm->queue))) {
488     for (win = wm->windows.first; win; win = win->next) {
489       Scene *scene = WM_window_get_active_scene(win);
490       bScreen *screen = WM_window_get_active_screen(win);
491       WorkSpace *workspace = WM_window_get_active_workspace(win);
492
493       /* filter out notifiers */
494       if (note->category == NC_SCREEN && note->reference && note->reference != screen &&
495           note->reference != workspace && note->reference != WM_window_get_active_layout(win)) {
496         /* pass */
497       }
498       else if (note->category == NC_SCENE && note->reference && note->reference != scene) {
499         /* pass */
500       }
501       else {
502         ARegion *ar;
503
504         /* XXX context in notifiers? */
505         CTX_wm_window_set(C, win);
506
507         /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name + 2, note->category); */
508         ED_screen_do_listen(C, note);
509
510         for (ar = screen->regionbase.first; ar; ar = ar->next) {
511           ED_region_do_listen(win, NULL, ar, note, scene);
512         }
513
514         ED_screen_areas_iter(win, screen, sa)
515         {
516           ED_area_do_listen(win, sa, note, scene);
517           for (ar = sa->regionbase.first; ar; ar = ar->next) {
518             ED_region_do_listen(win, sa, ar, note, scene);
519           }
520         }
521       }
522     }
523
524     MEM_freeN(note);
525   }
526 #endif /* if 1 (postpone disabling for in favor of message-bus), eventually. */
527
528   /* Handle message bus. */
529   {
530     for (win = wm->windows.first; win; win = win->next) {
531       CTX_wm_window_set(C, win);
532       WM_msgbus_handle(wm->message_bus, C);
533     }
534     CTX_wm_window_set(C, NULL);
535   }
536
537   wm_event_do_refresh_wm_and_depsgraph(C);
538
539   /* Status bar */
540   if (wm->winactive) {
541     win = wm->winactive;
542     CTX_wm_window_set(C, win);
543     WM_window_cursor_keymap_status_refresh(C, win);
544     CTX_wm_window_set(C, NULL);
545   }
546
547   /* Autorun warning */
548   wm_test_autorun_warning(C);
549 }
550
551 static int wm_event_always_pass(const wmEvent *event)
552 {
553   /* some events we always pass on, to ensure proper communication */
554   return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
555 }
556
557 /* ********************* ui handler ******************* */
558
559 static int wm_handler_ui_call(bContext *C,
560                               wmEventHandler_UI *handler,
561                               const wmEvent *event,
562                               int always_pass)
563 {
564   ScrArea *area = CTX_wm_area(C);
565   ARegion *region = CTX_wm_region(C);
566   ARegion *menu = CTX_wm_menu(C);
567   static bool do_wheel_ui = true;
568   const bool is_wheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN);
569   int retval;
570
571   /* UI code doesn't handle return values - it just always returns break.
572    * to make the DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks */
573   if (((handler->head.flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) && !ISMOUSE_BUTTON(event->type) &&
574       (event->val == KM_DBL_CLICK)) {
575     return WM_HANDLER_CONTINUE;
576   }
577
578   /* UI is quite aggressive with swallowing events, like scrollwheel */
579   /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
580   if (do_wheel_ui == false) {
581     if (is_wheel) {
582       return WM_HANDLER_CONTINUE;
583     }
584     else if (wm_event_always_pass(event) == 0) {
585       do_wheel_ui = true;
586     }
587   }
588
589   /* we set context to where ui handler came from */
590   if (handler->context.area) {
591     CTX_wm_area_set(C, handler->context.area);
592   }
593   if (handler->context.region) {
594     CTX_wm_region_set(C, handler->context.region);
595   }
596   if (handler->context.menu) {
597     CTX_wm_menu_set(C, handler->context.menu);
598   }
599
600   retval = handler->handle_fn(C, event, handler->user_data);
601
602   /* putting back screen context */
603   if ((retval != WM_UI_HANDLER_BREAK) || always_pass) {
604     CTX_wm_area_set(C, area);
605     CTX_wm_region_set(C, region);
606     CTX_wm_menu_set(C, menu);
607   }
608   else {
609     /* this special cases is for areas and regions that get removed */
610     CTX_wm_area_set(C, NULL);
611     CTX_wm_region_set(C, NULL);
612     CTX_wm_menu_set(C, NULL);
613   }
614
615   if (retval == WM_UI_HANDLER_BREAK) {
616     return WM_HANDLER_BREAK;
617   }
618
619   /* event not handled in UI, if wheel then we temporarily disable it */
620   if (is_wheel) {
621     do_wheel_ui = false;
622   }
623
624   return WM_HANDLER_CONTINUE;
625 }
626
627 static void wm_handler_ui_cancel(bContext *C)
628 {
629   wmWindow *win = CTX_wm_window(C);
630   ARegion *ar = CTX_wm_region(C);
631
632   if (!ar) {
633     return;
634   }
635
636   LISTBASE_FOREACH_MUTABLE(wmEventHandler *, handler_base, &ar->handlers)
637   {
638     if (handler_base->type == WM_HANDLER_TYPE_UI) {
639       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
640       BLI_assert(handler->handle_fn != NULL);
641       wmEvent event;
642       wm_event_init_from_window(win, &event);
643       event.type = EVT_BUT_CANCEL;
644       handler->handle_fn(C, &event, handler->user_data);
645     }
646   }
647 }
648
649 /* ********************* operators ******************* */
650
651 bool WM_operator_poll(bContext *C, wmOperatorType *ot)
652 {
653   wmOperatorTypeMacro *otmacro;
654
655   for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
656     wmOperatorType *ot_macro = WM_operatortype_find(otmacro->idname, 0);
657
658     if (0 == WM_operator_poll(C, ot_macro)) {
659       return 0;
660     }
661   }
662
663   /* python needs operator type, so we added exception for it */
664   if (ot->pyop_poll) {
665     return ot->pyop_poll(C, ot);
666   }
667   else if (ot->poll) {
668     return ot->poll(C);
669   }
670
671   return 1;
672 }
673
674 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
675 bool WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
676 {
677   return wm_operator_call_internal(C, ot, NULL, NULL, context, true, NULL);
678 }
679
680 bool WM_operator_check_ui_empty(wmOperatorType *ot)
681 {
682   if (ot->macro.first != NULL) {
683     /* for macros, check all have exec() we can call */
684     wmOperatorTypeMacro *otmacro;
685     for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
686       wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
687       if (otm && !WM_operator_check_ui_empty(otm)) {
688         return false;
689       }
690     }
691     return true;
692   }
693
694   /* Assume a ui callback will draw something. */
695   if (ot->ui) {
696     return false;
697   }
698
699   PointerRNA ptr;
700   WM_operator_properties_create_ptr(&ptr, ot);
701   RNA_STRUCT_BEGIN (&ptr, prop) {
702     int flag = RNA_property_flag(prop);
703     if (flag & PROP_HIDDEN) {
704       continue;
705     }
706     return false;
707   }
708   RNA_STRUCT_END;
709   return true;
710 }
711
712 /**
713  * Sets the active region for this space from the context.
714  *
715  * \see #BKE_area_find_region_active_win
716  */
717 void WM_operator_region_active_win_set(bContext *C)
718 {
719   ScrArea *sa = CTX_wm_area(C);
720   if (sa) {
721     ARegion *ar = CTX_wm_region(C);
722     if (ar && ar->regiontype == RGN_TYPE_WINDOW) {
723       sa->region_active_win = BLI_findindex(&sa->regionbase, ar);
724     }
725   }
726 }
727
728 /* for debugging only, getting inspecting events manually is tedious */
729 void WM_event_print(const wmEvent *event)
730 {
731   if (event) {
732     const char *unknown = "UNKNOWN";
733     const char *type_id = unknown;
734     const char *val_id = unknown;
735
736     RNA_enum_identifier(rna_enum_event_type_items, event->type, &type_id);
737     RNA_enum_identifier(rna_enum_event_value_items, event->val, &val_id);
738
739     printf(
740         "wmEvent  type:%d / %s, val:%d / %s,\n"
741         "         shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d,\n"
742         "         mouse:(%d,%d), ascii:'%c', utf8:'%.*s', keymap_idname:%s, pointer:%p\n",
743         event->type,
744         type_id,
745         event->val,
746         val_id,
747         event->shift,
748         event->ctrl,
749         event->alt,
750         event->oskey,
751         event->keymodifier,
752         event->x,
753         event->y,
754         event->ascii,
755         BLI_str_utf8_size(event->utf8_buf),
756         event->utf8_buf,
757         event->keymap_idname,
758         (const void *)event);
759
760 #ifdef WITH_INPUT_NDOF
761     if (ISNDOF(event->type)) {
762       const wmNDOFMotionData *ndof = event->customdata;
763       if (event->type == NDOF_MOTION) {
764         printf("   ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u\n",
765                UNPACK3(ndof->rvec),
766                UNPACK3(ndof->tvec),
767                ndof->dt,
768                ndof->progress);
769       }
770       else {
771         /* ndof buttons printed already */
772       }
773     }
774 #endif /* WITH_INPUT_NDOF */
775
776     if (event->tablet_data) {
777       const wmTabletData *wmtab = event->tablet_data;
778       printf(" tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)\n",
779              wmtab->Active,
780              wmtab->Pressure,
781              wmtab->Xtilt,
782              wmtab->Ytilt);
783     }
784   }
785   else {
786     printf("wmEvent - NULL\n");
787   }
788 }
789
790 /**
791  * Show the report in the info header.
792  */
793 void WM_report_banner_show(void)
794 {
795   wmWindowManager *wm = G_MAIN->wm.first;
796   ReportList *wm_reports = &wm->reports;
797   ReportTimerInfo *rti;
798
799   /* After adding reports to the global list, reset the report timer. */
800   WM_event_remove_timer(wm, NULL, wm_reports->reporttimer);
801
802   /* Records time since last report was added */
803   wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05);
804
805   rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
806   wm_reports->reporttimer->customdata = rti;
807 }
808
809 bool WM_event_is_last_mousemove(const wmEvent *event)
810 {
811   while ((event = event->next)) {
812     if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
813       return false;
814     }
815   }
816   return true;
817 }
818
819 #ifdef WITH_INPUT_NDOF
820 void WM_ndof_deadzone_set(float deadzone)
821 {
822   GHOST_setNDOFDeadZone(deadzone);
823 }
824 #endif
825
826 static void wm_add_reports(ReportList *reports)
827 {
828   /* if the caller owns them, handle this */
829   if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) {
830     wmWindowManager *wm = G_MAIN->wm.first;
831
832     /* add reports to the global list, otherwise they are not seen */
833     BLI_movelisttolist(&wm->reports.list, &reports->list);
834
835     WM_report_banner_show();
836   }
837 }
838
839 void WM_report(ReportType type, const char *message)
840 {
841   ReportList reports;
842
843   BKE_reports_init(&reports, RPT_STORE);
844   BKE_report(&reports, type, message);
845
846   wm_add_reports(&reports);
847
848   BKE_reports_clear(&reports);
849 }
850
851 void WM_reportf(ReportType type, const char *format, ...)
852 {
853   DynStr *ds;
854   va_list args;
855
856   ds = BLI_dynstr_new();
857   va_start(args, format);
858   BLI_dynstr_vappendf(ds, format, args);
859   va_end(args);
860
861   char *str = BLI_dynstr_get_cstring(ds);
862   WM_report(type, str);
863   MEM_freeN(str);
864
865   BLI_dynstr_free(ds);
866 }
867
868 /* (caller_owns_reports == true) when called from python */
869 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports)
870 {
871   if (G.background == 0 && caller_owns_reports == false) { /* popup */
872     if (op->reports->list.first) {
873       /* FIXME, temp setting window, see other call to UI_popup_menu_reports for why */
874       wmWindow *win_prev = CTX_wm_window(C);
875       ScrArea *area_prev = CTX_wm_area(C);
876       ARegion *ar_prev = CTX_wm_region(C);
877
878       if (win_prev == NULL) {
879         CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
880       }
881
882       UI_popup_menu_reports(C, op->reports);
883
884       CTX_wm_window_set(C, win_prev);
885       CTX_wm_area_set(C, area_prev);
886       CTX_wm_region_set(C, ar_prev);
887     }
888   }
889
890   if (retval & OPERATOR_FINISHED) {
891     CLOG_STR_INFO_N(WM_LOG_OPERATORS, 1, WM_operator_pystring(C, op, false, true));
892
893     if (caller_owns_reports == false) {
894       BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
895     }
896
897     if (op->type->flag & OPTYPE_REGISTER) {
898       if (G.background == 0) { /* ends up printing these in the terminal, gets annoying */
899         /* Report the python string representation of the operator */
900         char *buf = WM_operator_pystring(C, op, false, true);
901         BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
902         MEM_freeN(buf);
903       }
904     }
905   }
906
907   /* if the caller owns them, handle this */
908   wm_add_reports(op->reports);
909 }
910
911 /**
912  * This function is mainly to check that the rules for freeing
913  * an operator are kept in sync.
914  */
915 static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
916 {
917   /* Check undo flag here since undo operators are also added to the list,
918    * to support checking if the same operator is run twice. */
919   return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
920 }
921
922 static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store)
923 {
924   wmWindowManager *wm = CTX_wm_manager(C);
925   enum {
926     NOP,
927     SET,
928     CLEAR,
929   } hud_status = NOP;
930
931   op->customdata = NULL;
932
933   if (store) {
934     WM_operator_last_properties_store(op);
935   }
936
937   /* we don't want to do undo pushes for operators that are being
938    * called from operators that already do an undo push. usually
939    * this will happen for python operators that call C operators */
940   if (wm->op_undo_depth == 0) {
941     if (op->type->flag & OPTYPE_UNDO) {
942       ED_undo_push_op(C, op);
943       if (repeat == 0) {
944         hud_status = CLEAR;
945       }
946     }
947     else if (op->type->flag & OPTYPE_UNDO_GROUPED) {
948       ED_undo_grouped_push_op(C, op);
949       if (repeat == 0) {
950         hud_status = CLEAR;
951       }
952     }
953   }
954
955   if (repeat == 0) {
956     if (G.debug & G_DEBUG_WM) {
957       char *buf = WM_operator_pystring(C, op, false, true);
958       BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
959       MEM_freeN(buf);
960     }
961
962     if (wm_operator_register_check(wm, op->type)) {
963       /* take ownership of reports (in case python provided own) */
964       op->reports->flag |= RPT_FREE;
965
966       wm_operator_register(C, op);
967       WM_operator_region_active_win_set(C);
968
969       if (WM_operator_last_redo(C) == op) {
970         /* Show the redo panel. */
971         hud_status = SET;
972       }
973     }
974     else {
975       WM_operator_free(op);
976     }
977   }
978
979   if (hud_status != NOP) {
980     if (hud_status == SET) {
981       ScrArea *sa = CTX_wm_area(C);
982       if (sa) {
983         ED_area_type_hud_ensure(C, sa);
984       }
985     }
986     else if (hud_status == CLEAR) {
987       ED_area_type_hud_clear(wm, NULL);
988     }
989     else {
990       BLI_assert(0);
991     }
992   }
993 }
994
995 /* if repeat is true, it doesn't register again, nor does it free */
996 static int wm_operator_exec(bContext *C,
997                             wmOperator *op,
998                             const bool repeat,
999                             const bool use_repeat_op_flag,
1000                             const bool store)
1001 {
1002   wmWindowManager *wm = CTX_wm_manager(C);
1003   int retval = OPERATOR_CANCELLED;
1004
1005   CTX_wm_operator_poll_msg_set(C, NULL);
1006
1007   if (op == NULL || op->type == NULL) {
1008     return retval;
1009   }
1010
1011   if (0 == WM_operator_poll(C, op->type)) {
1012     return retval;
1013   }
1014
1015   if (op->type->exec) {
1016     if (op->type->flag & OPTYPE_UNDO) {
1017       wm->op_undo_depth++;
1018     }
1019
1020     if (repeat && use_repeat_op_flag) {
1021       op->flag |= OP_IS_REPEAT;
1022     }
1023     retval = op->type->exec(C, op);
1024     OPERATOR_RETVAL_CHECK(retval);
1025     if (repeat && use_repeat_op_flag) {
1026       op->flag &= ~OP_IS_REPEAT;
1027     }
1028
1029     if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1030       wm->op_undo_depth--;
1031     }
1032   }
1033
1034   /* XXX Disabled the repeat check to address part 2 of #31840.
1035    *     Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
1036    *     why this was needed, but worth to note it in case something turns bad. (mont29) */
1037   if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) {
1038     wm_operator_reports(C, op, retval, false);
1039   }
1040
1041   if (retval & OPERATOR_FINISHED) {
1042     wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
1043   }
1044   else if (repeat == 0) {
1045     /* warning: modal from exec is bad practice, but avoid crashing. */
1046     if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
1047       WM_operator_free(op);
1048     }
1049   }
1050
1051   return retval | OPERATOR_HANDLED;
1052 }
1053
1054 /* simply calls exec with basic checks */
1055 static int wm_operator_exec_notest(bContext *C, wmOperator *op)
1056 {
1057   int retval = OPERATOR_CANCELLED;
1058
1059   if (op == NULL || op->type == NULL || op->type->exec == NULL) {
1060     return retval;
1061   }
1062
1063   retval = op->type->exec(C, op);
1064   OPERATOR_RETVAL_CHECK(retval);
1065
1066   return retval;
1067 }
1068
1069 /**
1070  * for running operators with frozen context (modal handlers, menus)
1071  *
1072  * \param store: Store settings for re-use.
1073  *
1074  * warning: do not use this within an operator to call its self! [#29537] */
1075 int WM_operator_call_ex(bContext *C, wmOperator *op, const bool store)
1076 {
1077   return wm_operator_exec(C, op, false, false, store);
1078 }
1079
1080 int WM_operator_call(bContext *C, wmOperator *op)
1081 {
1082   return WM_operator_call_ex(C, op, false);
1083 }
1084
1085 /**
1086  * This is intended to be used when an invoke operator wants to call exec on its self
1087  * and is basically like running op->type->exec() directly, no poll checks no freeing,
1088  * since we assume whoever called invoke will take care of that
1089  */
1090 int WM_operator_call_notest(bContext *C, wmOperator *op)
1091 {
1092   return wm_operator_exec_notest(C, op);
1093 }
1094
1095 /**
1096  * Execute this operator again, put here so it can share above code
1097  */
1098 int WM_operator_repeat(bContext *C, wmOperator *op)
1099 {
1100   return wm_operator_exec(C, op, true, true, true);
1101 }
1102 int WM_operator_repeat_interactive(bContext *C, wmOperator *op)
1103 {
1104   return wm_operator_exec(C, op, true, false, true);
1105 }
1106 /**
1107  * \return true if #WM_operator_repeat can run
1108  * simple check for now but may become more involved.
1109  * To be sure the operator can run call `WM_operator_poll(C, op->type)` also, since this call
1110  * checks if #WM_operator_repeat() can run at all, not that it WILL run at any time.
1111  */
1112 bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
1113 {
1114   if (op->type->exec != NULL) {
1115     return true;
1116   }
1117   else if (op->opm) {
1118     /* for macros, check all have exec() we can call */
1119     wmOperatorTypeMacro *otmacro;
1120     for (otmacro = op->opm->type->macro.first; otmacro; otmacro = otmacro->next) {
1121       wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1122       if (otm && otm->exec == NULL) {
1123         return false;
1124       }
1125     }
1126     return true;
1127   }
1128
1129   return false;
1130 }
1131
1132 bool WM_operator_is_repeat(const bContext *C, const wmOperator *op)
1133 {
1134   /* may be in the operators list or not */
1135   wmOperator *op_prev;
1136   if (op->prev == NULL && op->next == NULL) {
1137     wmWindowManager *wm = CTX_wm_manager(C);
1138     op_prev = wm->operators.last;
1139   }
1140   else {
1141     op_prev = op->prev;
1142   }
1143   return (op_prev && (op->type == op_prev->type));
1144 }
1145
1146 static wmOperator *wm_operator_create(wmWindowManager *wm,
1147                                       wmOperatorType *ot,
1148                                       PointerRNA *properties,
1149                                       ReportList *reports)
1150 {
1151   /* XXX operatortype names are static still. for debug */
1152   wmOperator *op = MEM_callocN(sizeof(wmOperator), ot->idname);
1153
1154   /* XXX adding new operator could be function, only happens here now */
1155   op->type = ot;
1156   BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
1157
1158   /* initialize properties, either copy or create */
1159   op->ptr = MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
1160   if (properties && properties->data) {
1161     op->properties = IDP_CopyProperty(properties->data);
1162   }
1163   else {
1164     IDPropertyTemplate val = {0};
1165     op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1166   }
1167   RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
1168
1169   /* initialize error reports */
1170   if (reports) {
1171     op->reports = reports; /* must be initialized already */
1172   }
1173   else {
1174     op->reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
1175     BKE_reports_init(op->reports, RPT_STORE | RPT_FREE);
1176   }
1177
1178   /* recursive filling of operator macro list */
1179   if (ot->macro.first) {
1180     static wmOperator *motherop = NULL;
1181     wmOperatorTypeMacro *otmacro;
1182     int root = 0;
1183
1184     /* ensure all ops are in execution order in 1 list */
1185     if (motherop == NULL) {
1186       motherop = op;
1187       root = 1;
1188     }
1189
1190     /* if properties exist, it will contain everything needed */
1191     if (properties) {
1192       otmacro = ot->macro.first;
1193
1194       RNA_STRUCT_BEGIN (properties, prop) {
1195
1196         if (otmacro == NULL) {
1197           break;
1198         }
1199
1200         /* skip invalid properties */
1201         if (STREQ(RNA_property_identifier(prop), otmacro->idname)) {
1202           wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1203           PointerRNA someptr = RNA_property_pointer_get(properties, prop);
1204           wmOperator *opm = wm_operator_create(wm, otm, &someptr, NULL);
1205
1206           IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
1207
1208           BLI_addtail(&motherop->macro, opm);
1209           opm->opm = motherop; /* pointer to mom, for modal() */
1210
1211           otmacro = otmacro->next;
1212         }
1213       }
1214       RNA_STRUCT_END;
1215     }
1216     else {
1217       for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
1218         wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1219         wmOperator *opm = wm_operator_create(wm, otm, otmacro->ptr, NULL);
1220
1221         BLI_addtail(&motherop->macro, opm);
1222         opm->opm = motherop; /* pointer to mom, for modal() */
1223       }
1224     }
1225
1226     if (root) {
1227       motherop = NULL;
1228     }
1229   }
1230
1231   WM_operator_properties_sanitize(op->ptr, 0);
1232
1233   return op;
1234 }
1235
1236 static void wm_region_mouse_co(bContext *C, wmEvent *event)
1237 {
1238   ARegion *ar = CTX_wm_region(C);
1239   if (ar) {
1240     /* compatibility convention */
1241     event->mval[0] = event->x - ar->winrct.xmin;
1242     event->mval[1] = event->y - ar->winrct.ymin;
1243   }
1244   else {
1245     /* these values are invalid (avoid odd behavior by relying on old mval values) */
1246     event->mval[0] = -1;
1247     event->mval[1] = -1;
1248   }
1249 }
1250
1251 #if 1 /* may want to disable operator remembering previous state for testing */
1252
1253 static bool operator_last_properties_init_impl(wmOperator *op, IDProperty *last_properties)
1254 {
1255   bool changed = false;
1256   IDPropertyTemplate val = {0};
1257   IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1258   PropertyRNA *iterprop;
1259
1260   CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname);
1261
1262   iterprop = RNA_struct_iterator_property(op->type->srna);
1263
1264   RNA_PROP_BEGIN (op->ptr, itemptr, iterprop) {
1265     PropertyRNA *prop = itemptr.data;
1266     if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
1267       if (!RNA_property_is_set(op->ptr, prop)) { /* don't override a setting already set */
1268         const char *identifier = RNA_property_identifier(prop);
1269         IDProperty *idp_src = IDP_GetPropertyFromGroup(last_properties, identifier);
1270         if (idp_src) {
1271           IDProperty *idp_dst = IDP_CopyProperty(idp_src);
1272
1273           /* note - in the future this may need to be done recursively,
1274            * but for now RNA doesn't access nested operators */
1275           idp_dst->flag |= IDP_FLAG_GHOST;
1276
1277           /* add to temporary group instead of immediate replace,
1278            * because we are iterating over this group */
1279           IDP_AddToGroup(replaceprops, idp_dst);
1280           changed = true;
1281         }
1282       }
1283     }
1284   }
1285   RNA_PROP_END;
1286
1287   IDP_MergeGroup(op->properties, replaceprops, true);
1288   IDP_FreeProperty(replaceprops);
1289   MEM_freeN(replaceprops);
1290   return changed;
1291 }
1292
1293 bool WM_operator_last_properties_init(wmOperator *op)
1294 {
1295   bool changed = false;
1296   if (op->type->last_properties) {
1297     changed |= operator_last_properties_init_impl(op, op->type->last_properties);
1298     for (wmOperator *opm = op->macro.first; opm; opm = opm->next) {
1299       IDProperty *idp_src = IDP_GetPropertyFromGroup(op->type->last_properties, opm->idname);
1300       if (idp_src) {
1301         changed |= operator_last_properties_init_impl(opm, idp_src);
1302       }
1303     }
1304   }
1305   return changed;
1306 }
1307
1308 bool WM_operator_last_properties_store(wmOperator *op)
1309 {
1310   if (op->type->last_properties) {
1311     IDP_FreeProperty(op->type->last_properties);
1312     MEM_freeN(op->type->last_properties);
1313     op->type->last_properties = NULL;
1314   }
1315
1316   if (op->properties) {
1317     CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname);
1318     op->type->last_properties = IDP_CopyProperty(op->properties);
1319   }
1320
1321   if (op->macro.first != NULL) {
1322     for (wmOperator *opm = op->macro.first; opm; opm = opm->next) {
1323       if (opm->properties) {
1324         if (op->type->last_properties == NULL) {
1325           op->type->last_properties = IDP_New(
1326               IDP_GROUP, &(IDPropertyTemplate){0}, "wmOperatorProperties");
1327         }
1328         IDProperty *idp_macro = IDP_CopyProperty(opm->properties);
1329         STRNCPY(idp_macro->name, opm->idname);
1330         IDP_ReplaceInGroup(op->type->last_properties, idp_macro);
1331       }
1332     }
1333   }
1334
1335   return (op->type->last_properties != NULL);
1336 }
1337
1338 #else
1339
1340 bool WM_operator_last_properties_init(wmOperator *UNUSED(op))
1341 {
1342   return false;
1343 }
1344
1345 bool WM_operator_last_properties_store(wmOperator *UNUSED(op))
1346 {
1347   return false;
1348 }
1349
1350 #endif
1351
1352 /**
1353  * Also used for exec when 'event' is NULL.
1354  */
1355 static int wm_operator_invoke(bContext *C,
1356                               wmOperatorType *ot,
1357                               wmEvent *event,
1358                               PointerRNA *properties,
1359                               ReportList *reports,
1360                               const bool poll_only,
1361                               bool use_last_properties)
1362 {
1363   int retval = OPERATOR_PASS_THROUGH;
1364
1365   /* this is done because complicated setup is done to call this function that is better not duplicated */
1366   if (poll_only) {
1367     return WM_operator_poll(C, ot);
1368   }
1369
1370   if (WM_operator_poll(C, ot)) {
1371     wmWindowManager *wm = CTX_wm_manager(C);
1372     wmOperator *op = wm_operator_create(
1373         wm, ot, properties, reports); /* if reports == NULL, they'll be initialized */
1374     const bool is_nested_call = (wm->op_undo_depth != 0);
1375
1376     if (event != NULL) {
1377       op->flag |= OP_IS_INVOKE;
1378     }
1379
1380     /* initialize setting from previous run */
1381     if (!is_nested_call && use_last_properties) { /* not called by py script */
1382       WM_operator_last_properties_init(op);
1383     }
1384
1385     if ((event == NULL) || (event->type != MOUSEMOVE)) {
1386       CLOG_INFO(WM_LOG_HANDLERS,
1387                 2,
1388                 "handle evt %d win %p op %s",
1389                 event ? event->type : 0,
1390                 CTX_wm_screen(C)->active_region,
1391                 ot->idname);
1392     }
1393
1394     if (op->type->invoke && event) {
1395       wm_region_mouse_co(C, event);
1396
1397       if (op->type->flag & OPTYPE_UNDO) {
1398         wm->op_undo_depth++;
1399       }
1400
1401       retval = op->type->invoke(C, op, event);
1402       OPERATOR_RETVAL_CHECK(retval);
1403
1404       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1405         wm->op_undo_depth--;
1406       }
1407     }
1408     else if (op->type->exec) {
1409       if (op->type->flag & OPTYPE_UNDO) {
1410         wm->op_undo_depth++;
1411       }
1412
1413       retval = op->type->exec(C, op);
1414       OPERATOR_RETVAL_CHECK(retval);
1415
1416       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1417         wm->op_undo_depth--;
1418       }
1419     }
1420     else {
1421       /* debug, important to leave a while, should never happen */
1422       CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
1423     }
1424
1425     /* Note, if the report is given as an argument then assume the caller will deal with displaying them
1426      * currently python only uses this */
1427     if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
1428       /* only show the report if the report list was not given in the function */
1429       wm_operator_reports(C, op, retval, (reports != NULL));
1430     }
1431
1432     if (retval & OPERATOR_HANDLED) {
1433       /* do nothing, wm_operator_exec() has been called somewhere */
1434     }
1435     else if (retval & OPERATOR_FINISHED) {
1436       const bool store = !is_nested_call && use_last_properties;
1437       wm_operator_finished(C, op, false, store);
1438     }
1439     else if (retval & OPERATOR_RUNNING_MODAL) {
1440       /* take ownership of reports (in case python provided own) */
1441       op->reports->flag |= RPT_FREE;
1442
1443       /* grab cursor during blocking modal ops (X11)
1444        * Also check for macro
1445        */
1446       if (ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
1447         int bounds[4] = {-1, -1, -1, -1};
1448         bool wrap;
1449
1450         if (event == NULL) {
1451           wrap = false;
1452         }
1453         else if (op->opm) {
1454           wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) &&
1455                  ((op->opm->flag & OP_IS_MODAL_GRAB_CURSOR) ||
1456                   (op->opm->type->flag & OPTYPE_GRAB_CURSOR));
1457         }
1458         else {
1459           wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) &&
1460                  ((op->flag & OP_IS_MODAL_GRAB_CURSOR) || (ot->flag & OPTYPE_GRAB_CURSOR));
1461         }
1462
1463         /* exception, cont. grab in header is annoying */
1464         if (wrap) {
1465           ARegion *ar = CTX_wm_region(C);
1466           if (ar && ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER)) {
1467             wrap = false;
1468           }
1469         }
1470
1471         if (wrap) {
1472           const rcti *winrect = NULL;
1473           ARegion *ar = CTX_wm_region(C);
1474           ScrArea *sa = CTX_wm_area(C);
1475
1476           if (ar && ar->regiontype == RGN_TYPE_WINDOW &&
1477               BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
1478             winrect = &ar->winrct;
1479           }
1480           else if (sa && BLI_rcti_isect_pt_v(&sa->totrct, &event->x)) {
1481             winrect = &sa->totrct;
1482           }
1483
1484           if (winrect) {
1485             bounds[0] = winrect->xmin;
1486             bounds[1] = winrect->ymax;
1487             bounds[2] = winrect->xmax;
1488             bounds[3] = winrect->ymin;
1489           }
1490         }
1491
1492         WM_cursor_grab_enable(CTX_wm_window(C), wrap, false, bounds);
1493       }
1494
1495       /* cancel UI handlers, typically tooltips that can hang around
1496        * while dragging the view or worse, that stay there permanently
1497        * after the modal operator has swallowed all events and passed
1498        * none to the UI handler */
1499       wm_handler_ui_cancel(C);
1500     }
1501     else {
1502       WM_operator_free(op);
1503     }
1504   }
1505
1506   return retval;
1507 }
1508
1509 /**
1510  * #WM_operator_name_call is the main accessor function
1511  * this is for python to access since its done the operator lookup
1512  *
1513  * invokes operator in context
1514  */
1515 static int wm_operator_call_internal(bContext *C,
1516                                      wmOperatorType *ot,
1517                                      PointerRNA *properties,
1518                                      ReportList *reports,
1519                                      const short context,
1520                                      const bool poll_only,
1521                                      wmEvent *event)
1522 {
1523   int retval;
1524
1525   CTX_wm_operator_poll_msg_set(C, NULL);
1526
1527   /* dummie test */
1528   if (ot) {
1529     wmWindow *window = CTX_wm_window(C);
1530
1531     if (event == NULL) {
1532       switch (context) {
1533         case WM_OP_INVOKE_DEFAULT:
1534         case WM_OP_INVOKE_REGION_WIN:
1535         case WM_OP_INVOKE_REGION_PREVIEW:
1536         case WM_OP_INVOKE_REGION_CHANNELS:
1537         case WM_OP_INVOKE_AREA:
1538         case WM_OP_INVOKE_SCREEN:
1539           /* window is needed for invoke, cancel operator */
1540           if (window == NULL) {
1541             if (poll_only) {
1542               CTX_wm_operator_poll_msg_set(C, "Missing 'window' in context");
1543             }
1544             return 0;
1545           }
1546           else {
1547             event = window->eventstate;
1548           }
1549           break;
1550         default:
1551           event = NULL;
1552           break;
1553       }
1554     }
1555     else {
1556       switch (context) {
1557         case WM_OP_EXEC_DEFAULT:
1558         case WM_OP_EXEC_REGION_WIN:
1559         case WM_OP_EXEC_REGION_PREVIEW:
1560         case WM_OP_EXEC_REGION_CHANNELS:
1561         case WM_OP_EXEC_AREA:
1562         case WM_OP_EXEC_SCREEN:
1563           event = NULL;
1564         default:
1565           break;
1566       }
1567     }
1568
1569     switch (context) {
1570       case WM_OP_EXEC_REGION_WIN:
1571       case WM_OP_INVOKE_REGION_WIN:
1572       case WM_OP_EXEC_REGION_CHANNELS:
1573       case WM_OP_INVOKE_REGION_CHANNELS:
1574       case WM_OP_EXEC_REGION_PREVIEW:
1575       case WM_OP_INVOKE_REGION_PREVIEW: {
1576         /* forces operator to go to the region window/channels/preview, for header menus
1577          * but we stay in the same region if we are already in one
1578          */
1579         ARegion *ar = CTX_wm_region(C);
1580         ScrArea *area = CTX_wm_area(C);
1581         int type = RGN_TYPE_WINDOW;
1582
1583         switch (context) {
1584           case WM_OP_EXEC_REGION_CHANNELS:
1585           case WM_OP_INVOKE_REGION_CHANNELS:
1586             type = RGN_TYPE_CHANNELS;
1587             break;
1588
1589           case WM_OP_EXEC_REGION_PREVIEW:
1590           case WM_OP_INVOKE_REGION_PREVIEW:
1591             type = RGN_TYPE_PREVIEW;
1592             break;
1593
1594           case WM_OP_EXEC_REGION_WIN:
1595           case WM_OP_INVOKE_REGION_WIN:
1596           default:
1597             type = RGN_TYPE_WINDOW;
1598             break;
1599         }
1600
1601         if (!(ar && ar->regiontype == type) && area) {
1602           ARegion *ar1;
1603           if (type == RGN_TYPE_WINDOW) {
1604             ar1 = BKE_area_find_region_active_win(area);
1605           }
1606           else {
1607             ar1 = BKE_area_find_region_type(area, type);
1608           }
1609
1610           if (ar1) {
1611             CTX_wm_region_set(C, ar1);
1612           }
1613         }
1614
1615         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1616
1617         /* set region back */
1618         CTX_wm_region_set(C, ar);
1619
1620         return retval;
1621       }
1622       case WM_OP_EXEC_AREA:
1623       case WM_OP_INVOKE_AREA: {
1624         /* remove region from context */
1625         ARegion *ar = CTX_wm_region(C);
1626
1627         CTX_wm_region_set(C, NULL);
1628         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1629         CTX_wm_region_set(C, ar);
1630
1631         return retval;
1632       }
1633       case WM_OP_EXEC_SCREEN:
1634       case WM_OP_INVOKE_SCREEN: {
1635         /* remove region + area from context */
1636         ARegion *ar = CTX_wm_region(C);
1637         ScrArea *area = CTX_wm_area(C);
1638
1639         CTX_wm_region_set(C, NULL);
1640         CTX_wm_area_set(C, NULL);
1641         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1642         CTX_wm_area_set(C, area);
1643         CTX_wm_region_set(C, ar);
1644
1645         return retval;
1646       }
1647       case WM_OP_EXEC_DEFAULT:
1648       case WM_OP_INVOKE_DEFAULT:
1649         return wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1650     }
1651   }
1652
1653   return 0;
1654 }
1655
1656 /* invokes operator in context */
1657 int WM_operator_name_call_ptr(bContext *C,
1658                               wmOperatorType *ot,
1659                               short context,
1660                               PointerRNA *properties)
1661 {
1662   BLI_assert(ot == WM_operatortype_find(ot->idname, true));
1663   return wm_operator_call_internal(C, ot, properties, NULL, context, false, NULL);
1664 }
1665 int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
1666 {
1667   wmOperatorType *ot = WM_operatortype_find(opstring, 0);
1668   if (ot) {
1669     return WM_operator_name_call_ptr(C, ot, context, properties);
1670   }
1671
1672   return 0;
1673 }
1674
1675 /**
1676  * Call an existent menu. The menu can be created in C or Python.
1677  */
1678 void WM_menu_name_call(bContext *C, const char *menu_name, short context)
1679 {
1680   wmOperatorType *ot = WM_operatortype_find("WM_OT_call_menu", false);
1681   PointerRNA ptr;
1682   WM_operator_properties_create_ptr(&ptr, ot);
1683   RNA_string_set(&ptr, "name", menu_name);
1684   WM_operator_name_call_ptr(C, ot, context, &ptr);
1685   WM_operator_properties_free(&ptr);
1686 }
1687
1688 /**
1689  * Similar to #WM_operator_name_call called with #WM_OP_EXEC_DEFAULT context.
1690  *
1691  * - #wmOperatorType is used instead of operator name since python already has the operator type.
1692  * - `poll()` must be called by python before this runs.
1693  * - reports can be passed to this function (so python can report them as exceptions).
1694  */
1695 int WM_operator_call_py(bContext *C,
1696                         wmOperatorType *ot,
1697                         short context,
1698                         PointerRNA *properties,
1699                         ReportList *reports,
1700                         const bool is_undo)
1701 {
1702   int retval = OPERATOR_CANCELLED;
1703
1704 #if 0
1705   wmOperator *op;
1706   op = wm_operator_create(wm, ot, properties, reports);
1707
1708   if (op->type->exec) {
1709     if (is_undo && op->type->flag & OPTYPE_UNDO)
1710       wm->op_undo_depth++;
1711
1712     retval = op->type->exec(C, op);
1713     OPERATOR_RETVAL_CHECK(retval);
1714
1715     if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1716       wm->op_undo_depth--;
1717   }
1718   else {
1719     CLOG_WARN(WM_LOG_OPERATORS,
1720               "\"%s\" operator has no exec function, Python cannot call it",
1721               op->type->name);
1722   }
1723
1724 #endif
1725
1726   /* not especially nice using undo depth here, its used so py never
1727    * triggers undo or stores operators last used state.
1728    *
1729    * we could have some more obvious way of doing this like passing a flag.
1730    */
1731   wmWindowManager *wm = CTX_wm_manager(C);
1732   if (!is_undo && wm) {
1733     wm->op_undo_depth++;
1734   }
1735
1736   retval = wm_operator_call_internal(C, ot, properties, reports, context, false, NULL);
1737
1738   if (!is_undo && wm && (wm == CTX_wm_manager(C))) {
1739     wm->op_undo_depth--;
1740   }
1741
1742   return retval;
1743 }
1744
1745 /* ********************* handlers *************** */
1746
1747 /* future extra customadata free? */
1748 void wm_event_free_handler(wmEventHandler *handler)
1749 {
1750   MEM_freeN(handler);
1751 }
1752
1753 /* only set context when area/region is part of screen */
1754 static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event)
1755 {
1756   wmWindow *win = CTX_wm_window(C);
1757   bScreen *screen = CTX_wm_screen(C);
1758
1759   if (screen && handler->op) {
1760     if (handler->context.area == NULL) {
1761       CTX_wm_area_set(C, NULL);
1762     }
1763     else {
1764       ScrArea *sa = NULL;
1765
1766       ED_screen_areas_iter(win, screen, sa_iter)
1767       {
1768         if (sa_iter == handler->context.area) {
1769           sa = sa_iter;
1770           break;
1771         }
1772       }
1773
1774       if (sa == NULL) {
1775         /* when changing screen layouts with running modal handlers (like render display), this
1776          * is not an error to print */
1777         if (handler->op == NULL) {
1778           CLOG_ERROR(WM_LOG_HANDLERS,
1779                      "internal error: handler (%s) has invalid area",
1780                      handler->op->type->idname);
1781         }
1782       }
1783       else {
1784         ARegion *ar;
1785         wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL;
1786         CTX_wm_area_set(C, sa);
1787
1788         if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) {
1789           ar = BKE_area_find_region_xy(sa, handler->context.region_type, event->x, event->y);
1790           if (ar) {
1791             handler->context.region = ar;
1792           }
1793         }
1794         else {
1795           ar = NULL;
1796         }
1797
1798         if (ar == NULL) {
1799           for (ar = sa->regionbase.first; ar; ar = ar->next) {
1800             if (ar == handler->context.region) {
1801               break;
1802             }
1803           }
1804         }
1805
1806         /* XXX no warning print here, after full-area and back regions are remade */
1807         if (ar) {
1808           CTX_wm_region_set(C, ar);
1809         }
1810       }
1811     }
1812   }
1813 }
1814
1815 /* called on exit or remove area, only here call cancel callback */
1816 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
1817 {
1818   wmEventHandler *handler_base;
1819   wmWindowManager *wm = CTX_wm_manager(C);
1820
1821   /* C is zero on freeing database, modal handlers then already were freed */
1822   while ((handler_base = BLI_pophead(handlers))) {
1823     BLI_assert(handler_base->type != 0);
1824     if (handler_base->type == WM_HANDLER_TYPE_OP) {
1825       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
1826       if (handler->op) {
1827         wmWindow *win = CTX_wm_window(C);
1828         if (handler->op->type->cancel) {
1829           ScrArea *area = CTX_wm_area(C);
1830           ARegion *region = CTX_wm_region(C);
1831
1832           wm_handler_op_context(C, handler, win->eventstate);
1833
1834           if (handler->op->type->flag & OPTYPE_UNDO) {
1835             wm->op_undo_depth++;
1836           }
1837
1838           handler->op->type->cancel(C, handler->op);
1839
1840           if (handler->op->type->flag & OPTYPE_UNDO) {
1841             wm->op_undo_depth--;
1842           }
1843
1844           CTX_wm_area_set(C, area);
1845           CTX_wm_region_set(C, region);
1846         }
1847
1848         WM_cursor_grab_disable(win, NULL);
1849         WM_operator_free(handler->op);
1850       }
1851     }
1852     else if (handler_base->type == WM_HANDLER_TYPE_UI) {
1853       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
1854
1855       if (handler->remove_fn) {
1856         ScrArea *area = CTX_wm_area(C);
1857         ARegion *region = CTX_wm_region(C);
1858         ARegion *menu = CTX_wm_menu(C);
1859
1860         if (handler->context.area) {
1861           CTX_wm_area_set(C, handler->context.area);
1862         }
1863         if (handler->context.region) {
1864           CTX_wm_region_set(C, handler->context.region);
1865         }
1866         if (handler->context.menu) {
1867           CTX_wm_menu_set(C, handler->context.menu);
1868         }
1869
1870         handler->remove_fn(C, handler->user_data);
1871
1872         CTX_wm_area_set(C, area);
1873         CTX_wm_region_set(C, region);
1874         CTX_wm_menu_set(C, menu);
1875       }
1876     }
1877
1878     wm_event_free_handler(handler_base);
1879   }
1880 }
1881
1882 /* do userdef mappings */
1883 int WM_userdef_event_map(int kmitype)
1884 {
1885   switch (kmitype) {
1886     case WHEELOUTMOUSE:
1887       return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE;
1888     case WHEELINMOUSE:
1889       return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE;
1890   }
1891
1892   return kmitype;
1893 }
1894
1895 /**
1896  * Use so we can check if 'wmEvent.type' is released in modal operators.
1897  *
1898  * An alternative would be to add a 'wmEvent.type_nokeymap'... or similar.
1899  */
1900 int WM_userdef_event_type_from_keymap_type(int kmitype)
1901 {
1902   switch (kmitype) {
1903     case EVT_TWEAK_L:
1904       return LEFTMOUSE;
1905     case EVT_TWEAK_M:
1906       return MIDDLEMOUSE;
1907     case EVT_TWEAK_R:
1908       return RIGHTMOUSE;
1909     case WHEELOUTMOUSE:
1910       return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE;
1911     case WHEELINMOUSE:
1912       return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE;
1913   }
1914
1915   return kmitype;
1916 }
1917
1918 static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
1919 {
1920   if (kmi->flag & KMI_INACTIVE) {
1921     return false;
1922   }
1923
1924   const int kmitype = WM_userdef_event_map(kmi->type);
1925
1926   /* the matching rules */
1927   if (kmitype == KM_TEXTINPUT) {
1928     if (winevent->val == KM_PRESS) { /* prevent double clicks */
1929       /* NOT using ISTEXTINPUT anymore because (at least on Windows) some key codes above 255
1930        * could have printable ascii keys - BUG [#30479] */
1931       if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) {
1932         return true;
1933       }
1934     }
1935   }
1936
1937   if (kmitype != KM_ANY) {
1938     if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) {
1939       const wmTabletData *wmtab = winevent->tablet_data;
1940
1941       if (wmtab == NULL) {
1942         return false;
1943       }
1944       else if (winevent->type != LEFTMOUSE) {
1945         /* tablet events can occur on hover + keypress */
1946         return false;
1947       }
1948       else if ((kmitype == TABLET_STYLUS) && (wmtab->Active != EVT_TABLET_STYLUS)) {
1949         return false;
1950       }
1951       else if ((kmitype == TABLET_ERASER) && (wmtab->Active != EVT_TABLET_ERASER)) {
1952         return false;
1953       }
1954     }
1955     else {
1956       if (winevent->type != kmitype) {
1957         return false;
1958       }
1959     }
1960   }
1961
1962   if (kmi->val != KM_ANY) {
1963     if (winevent->val != kmi->val) {
1964       return false;
1965     }
1966   }
1967
1968   /* Modifiers also check bits, so it allows modifier order.
1969    * Account for rare case of when these keys are used as the 'type' not as modifiers. */
1970   if (kmi->shift != KM_ANY) {
1971     if ((winevent->shift != kmi->shift) && !(winevent->shift & kmi->shift) &&
1972         !ELEM(winevent->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) {
1973       return false;
1974     }
1975   }
1976   if (kmi->ctrl != KM_ANY) {
1977     if (winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl) &&
1978         !ELEM(winevent->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
1979       return false;
1980     }
1981   }
1982   if (kmi->alt != KM_ANY) {
1983     if (winevent->alt != kmi->alt && !(winevent->alt & kmi->alt) &&
1984         !ELEM(winevent->type, LEFTALTKEY, RIGHTALTKEY)) {
1985       return false;
1986     }
1987   }
1988   if (kmi->oskey != KM_ANY) {
1989     if (winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey) &&
1990         (winevent->type != OSKEY)) {
1991       return false;
1992     }
1993   }
1994
1995   /* only keymap entry with keymodifier is checked, means all keys without modifier get handled too. */
1996   /* that is currently needed to make overlapping events work (when you press A - G fast or so). */
1997   if (kmi->keymodifier) {
1998     if (winevent->keymodifier != kmi->keymodifier) {
1999       return false;
2000     }
2001   }
2002
2003   return true;
2004 }
2005
2006 static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap,
2007                                                       wmOperator *op,
2008                                                       const wmEvent *event)
2009 {
2010   for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
2011     if (wm_eventmatch(event, kmi)) {
2012       if ((keymap->poll_modal_item == NULL) || (keymap->poll_modal_item(op, kmi->propvalue))) {
2013         return kmi;
2014       }
2015     }
2016   }
2017   return NULL;
2018 }
2019
2020 /* operator exists */
2021 static void wm_event_modalkeymap(const bContext *C,
2022                                  wmOperator *op,
2023                                  wmEvent *event,
2024                                  bool *dbl_click_disabled)
2025 {
2026   /* support for modal keymap in macros */
2027   if (op->opm) {
2028     op = op->opm;
2029   }
2030
2031   if (op->type->modalkeymap) {
2032     wmKeyMap *keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
2033     wmKeyMapItem *kmi = NULL;
2034
2035     const wmEvent *event_match = NULL;
2036     wmEvent event_no_dbl_click;
2037
2038     if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, event))) {
2039       event_match = event;
2040     }
2041     else if (event->val == KM_DBL_CLICK) {
2042       event_no_dbl_click = *event;
2043       event_no_dbl_click.val = KM_PRESS;
2044       if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, &event_no_dbl_click))) {
2045         event_match = &event_no_dbl_click;
2046       }
2047     }
2048
2049     if (event_match != NULL) {
2050       event->prevtype = event_match->type;
2051       event->prevval = event_match->val;
2052       event->type = EVT_MODAL_MAP;
2053       event->val = kmi->propvalue;
2054     }
2055   }
2056   else {
2057     /* modal keymap checking returns handled events fine, but all hardcoded modal
2058      * handling typically swallows all events (OPERATOR_RUNNING_MODAL).
2059      * This bypass just disables support for double clicks in hardcoded modal handlers */
2060     if (event->val == KM_DBL_CLICK) {
2061       event->val = KM_PRESS;
2062       *dbl_click_disabled = true;
2063     }
2064   }
2065 }
2066
2067 /**
2068  * Check whether operator is allowed to run in case interface is locked,
2069  * If interface is unlocked, will always return truth.
2070  */
2071 static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot)
2072 {
2073   wmWindowManager *wm = CTX_wm_manager(C);
2074
2075   if (wm->is_interface_locked) {
2076     if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) {
2077       return false;
2078     }
2079   }
2080
2081   return true;
2082 }
2083
2084 /* bad hacking event system... better restore event type for checking of KM_CLICK for example */
2085 /* XXX modal maps could use different method (ton) */
2086 static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled)
2087 {
2088   if (event->type == EVT_MODAL_MAP) {
2089     event->type = event->prevtype;
2090     event->prevtype = 0;
2091     event->val = event->prevval;
2092     event->prevval = 0;
2093   }
2094   else if (dbl_click_disabled) {
2095     event->val = KM_DBL_CLICK;
2096   }
2097 }
2098
2099 /* Warning: this function removes a modal handler, when finished */
2100 static int wm_handler_operator_call(bContext *C,
2101                                     ListBase *handlers,
2102                                     wmEventHandler *handler_base,
2103                                     wmEvent *event,
2104                                     PointerRNA *properties)
2105 {
2106   int retval = OPERATOR_PASS_THROUGH;
2107
2108   /* derived, modal or blocking operator */
2109   if ((handler_base->type == WM_HANDLER_TYPE_OP) &&
2110       (((wmEventHandler_Op *)handler_base)->op != NULL)) {
2111     wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2112     wmOperator *op = handler->op;
2113     wmOperatorType *ot = op->type;
2114
2115     if (!wm_operator_check_locked_interface(C, ot)) {
2116       /* Interface is locked and operator is not allowed to run,
2117        * nothing to do in this case.
2118        */
2119     }
2120     else if (ot->modal) {
2121       /* we set context to where modal handler came from */
2122       wmWindowManager *wm = CTX_wm_manager(C);
2123       ScrArea *area = CTX_wm_area(C);
2124       ARegion *region = CTX_wm_region(C);
2125       bool dbl_click_disabled = false;
2126
2127       wm_handler_op_context(C, handler, event);
2128       wm_region_mouse_co(C, event);
2129       wm_event_modalkeymap(C, op, event, &dbl_click_disabled);
2130
2131       if (ot->flag & OPTYPE_UNDO) {
2132         wm->op_undo_depth++;
2133       }
2134
2135       /* warning, after this call all context data and 'event' may be freed. see check below */
2136       retval = ot->modal(C, op, event);
2137       OPERATOR_RETVAL_CHECK(retval);
2138
2139       /* when this is _not_ the case the modal modifier may have loaded
2140        * a new blend file (demo mode does this), so we have to assume
2141        * the event, operator etc have all been freed. - campbell */
2142       if (CTX_wm_manager(C) == wm) {
2143
2144         wm_event_modalmap_end(event, dbl_click_disabled);
2145
2146         if (ot->flag & OPTYPE_UNDO) {
2147           wm->op_undo_depth--;
2148         }
2149
2150         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2151           wm_operator_reports(C, op, retval, false);
2152
2153           if (op->type->modalkeymap) {
2154             wmWindow *win = CTX_wm_window(C);
2155             WM_window_status_area_tag_redraw(win);
2156           }
2157         }
2158         else {
2159           /* not very common, but modal operators may report before finishing */
2160           if (!BLI_listbase_is_empty(&op->reports->list)) {
2161             wm_add_reports(op->reports);
2162           }
2163         }
2164
2165         /* important to run 'wm_operator_finished' before NULLing the context members */
2166         if (retval & OPERATOR_FINISHED) {
2167           wm_operator_finished(C, op, false, true);
2168           handler->op = NULL;
2169         }
2170         else if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2171           WM_operator_free(op);
2172           handler->op = NULL;
2173         }
2174
2175         /* putting back screen context, reval can pass trough after modal failures! */
2176         if ((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
2177           CTX_wm_area_set(C, area);
2178           CTX_wm_region_set(C, region);
2179         }
2180         else {
2181           /* this special cases is for areas and regions that get removed */
2182           CTX_wm_area_set(C, NULL);
2183           CTX_wm_region_set(C, NULL);
2184         }
2185
2186         /* update gizmos during modal handlers */
2187         wm_gizmomaps_handled_modal_update(C, event, handler);
2188
2189         /* remove modal handler, operator itself should have been canceled and freed */
2190         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2191           WM_cursor_grab_disable(CTX_wm_window(C), NULL);
2192
2193           BLI_remlink(handlers, handler);
2194           wm_event_free_handler(&handler->head);
2195
2196           /* prevent silly errors from operator users */
2197           //retval &= ~OPERATOR_PASS_THROUGH;
2198         }
2199       }
2200     }
2201     else {
2202       CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
2203     }
2204   }
2205   else {
2206     wmOperatorType *ot = WM_operatortype_find(event->keymap_idname, 0);
2207
2208     if (ot && wm_operator_check_locked_interface(C, ot)) {
2209       bool use_last_properties = true;
2210       PointerRNA tool_properties = {{0}};
2211
2212       bToolRef *keymap_tool = ((handler_base->type == WM_HANDLER_TYPE_KEYMAP) ?
2213                                    ((wmEventHandler_Keymap *)handler_base)->keymap_tool :
2214                                    NULL);
2215       const bool is_tool = (keymap_tool != NULL);
2216       const bool use_tool_properties = is_tool;
2217
2218       if (use_tool_properties) {
2219         WM_toolsystem_ref_properties_init_for_keymap(
2220             keymap_tool, &tool_properties, properties, ot);
2221         properties = &tool_properties;
2222         use_last_properties = false;
2223       }
2224
2225       retval = wm_operator_invoke(C, ot, event, properties, NULL, false, use_last_properties);
2226
2227       if (use_tool_properties) {
2228         WM_operator_properties_free(&tool_properties);
2229       }
2230
2231       /* Link gizmo if 'WM_GIZMOGROUPTYPE_TOOL_INIT' is set. */
2232       if (retval & OPERATOR_FINISHED) {
2233         if (is_tool) {
2234           bToolRef_Runtime *tref_rt = keymap_tool->runtime;
2235           if (tref_rt->gizmo_group[0]) {
2236             const char *idname = tref_rt->gizmo_group;
2237             wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
2238             if (gzgt != NULL) {
2239               if ((gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) != 0) {
2240                 ARegion *ar = CTX_wm_region(C);
2241                 if (ar != NULL) {
2242                   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
2243                   WM_gizmo_group_type_ensure_ptr_ex(gzgt, gzmap_type);
2244                   wmGizmoGroup *gzgroup = WM_gizmomaptype_group_init_runtime_with_region(
2245                       gzmap_type, gzgt, ar);
2246                   /* We can't rely on drawing to initialize gizmo's since disabling
2247                    * overlays/gizmos will prevent pre-drawing setup calls. (see T60905) */
2248                   WM_gizmogroup_ensure_init(C, gzgroup);
2249                 }
2250               }
2251             }
2252           }
2253         }
2254       }
2255       /* Done linking gizmo. */
2256     }
2257   }
2258   /* Finished and pass through flag as handled */
2259
2260   /* Finished and pass through flag as handled */
2261   if (retval == (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH)) {
2262     return WM_HANDLER_HANDLED;
2263   }
2264
2265   /* Modal unhandled, break */
2266   if (retval == (OPERATOR_PASS_THROUGH | OPERATOR_RUNNING_MODAL)) {
2267     return (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
2268   }
2269
2270   if (retval & OPERATOR_PASS_THROUGH) {
2271     return WM_HANDLER_CONTINUE;
2272   }
2273
2274   return WM_HANDLER_BREAK;
2275 }
2276
2277 /* fileselect handlers are only in the window queue, so it's safe to switch screens or area types */
2278 static int wm_handler_fileselect_do(bContext *C,
2279                                     ListBase *handlers,
2280                                     wmEventHandler_Op *handler,
2281                                     int val)
2282 {
2283   wmWindowManager *wm = CTX_wm_manager(C);
2284   SpaceFile *sfile;
2285   int action = WM_HANDLER_CONTINUE;
2286
2287   switch (val) {
2288     case EVT_FILESELECT_FULL_OPEN: {
2289       ScrArea *sa;
2290
2291       /* sa can be null when window A is active, but mouse is over window B
2292        * in this case, open file select in original window A. Also don't
2293        * use global areas. */
2294       if (handler->context.area == NULL || ED_area_is_global(handler->context.area)) {
2295         bScreen *screen = CTX_wm_screen(C);
2296         sa = (ScrArea *)screen->areabase.first;
2297       }
2298       else {
2299         sa = handler->context.area;
2300       }
2301
2302       if (sa->full) {
2303         /* ensure the first area becomes the file browser, because the second one is the small
2304          * top (info-)area which might be too small (in fullscreens we have max two areas) */
2305         if (sa->prev) {
2306           sa = sa->prev;
2307         }
2308         ED_area_newspace(C, sa, SPACE_FILE, true); /* 'sa' is modified in-place */
2309         /* we already had a fullscreen here -> mark new space as a stacked fullscreen */
2310         sa->flag |= (AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE);
2311       }
2312       else if (sa->spacetype == SPACE_FILE) {
2313         sa = ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED);
2314       }
2315       else {
2316         sa = ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
2317       }
2318
2319       /* note, getting the 'sa' back from the context causes a nasty bug where the newly created
2320        * 'sa' != CTX_wm_area(C). removed the line below and set 'sa' in the 'if' above */
2321       /* sa = CTX_wm_area(C); */
2322
2323       /* settings for filebrowser, sfile is not operator owner but sends events */
2324       sfile = (SpaceFile *)sa->spacedata.first;
2325       sfile->op = handler->op;
2326
2327       ED_fileselect_set_params(sfile);
2328
2329       action = WM_HANDLER_BREAK;
2330       break;
2331     }
2332
2333     case EVT_FILESELECT_EXEC:
2334     case EVT_FILESELECT_CANCEL:
2335     case EVT_FILESELECT_EXTERNAL_CANCEL: {
2336       /* remlink now, for load file case before removing*/
2337       BLI_remlink(handlers, handler);
2338
2339       if (val != EVT_FILESELECT_EXTERNAL_CANCEL) {
2340         ScrArea *sa = CTX_wm_area(C);
2341
2342         if (sa->full) {
2343           ED_screen_full_prevspace(C, sa);
2344         }
2345         /* user may have left fullscreen */
2346         else {
2347           ED_area_prevspace(C, sa);
2348         }
2349       }
2350
2351       wm_handler_op_context(C, handler, CTX_wm_window(C)->eventstate);
2352
2353       /* needed for UI_popup_menu_reports */
2354
2355       if (val == EVT_FILESELECT_EXEC) {
2356         int retval;
2357
2358         if (handler->op->type->flag & OPTYPE_UNDO) {
2359           wm->op_undo_depth++;
2360         }
2361
2362         retval = handler->op->type->exec(C, handler->op);
2363
2364         /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2365         if (handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
2366           wm->op_undo_depth--;
2367         }
2368
2369         /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2370         if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0) {
2371           if (handler->op->type->flag & OPTYPE_UNDO) {
2372             ED_undo_push_op(C, handler->op);
2373           }
2374           else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED) {
2375             ED_undo_grouped_push_op(C, handler->op);
2376           }
2377         }
2378
2379         if (handler->op->reports->list.first) {
2380
2381           /* FIXME, temp setting window, this is really bad!
2382            * only have because lib linking errors need to be seen by users :(
2383            * it can be removed without breaking anything but then no linking errors - campbell */
2384           wmWindow *win_prev = CTX_wm_window(C);
2385           ScrArea *area_prev = CTX_wm_area(C);
2386           ARegion *ar_prev = CTX_wm_region(C);
2387
2388           if (win_prev == NULL) {
2389             CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
2390           }
2391
2392           BKE_report_print_level_set(handler->op->reports, RPT_WARNING);
2393           UI_popup_menu_reports(C, handler->op->reports);
2394
2395           /* XXX - copied from 'wm_operator_finished()' */
2396           /* add reports to the global list, otherwise they are not seen */
2397           BLI_movelisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
2398
2399           /* more hacks, since we meddle with reports, banner display doesn't happen automatic */
2400           WM_report_banner_show();
2401
2402           CTX_wm_window_set(C, win_prev);
2403           CTX_wm_area_set(C, area_prev);
2404           CTX_wm_region_set(C, ar_prev);
2405         }
2406
2407         /* for WM_operator_pystring only, custom report handling is done above */
2408         wm_operator_reports(C, handler->op, retval, true);
2409
2410         if (retval & OPERATOR_FINISHED) {
2411           WM_operator_last_properties_store(handler->op);
2412         }
2413
2414         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2415           WM_operator_free(handler->op);
2416         }
2417       }
2418       else {
2419         if (handler->op->type->cancel) {
2420           if (handler->op->type->flag & OPTYPE_UNDO) {
2421             wm->op_undo_depth++;
2422           }
2423
2424           handler->op->type->cancel(C, handler->op);
2425
2426           if (handler->op->type->flag & OPTYPE_UNDO) {
2427             wm->op_undo_depth--;
2428           }
2429         }
2430
2431         WM_operator_free(handler->op);
2432       }
2433
2434       CTX_wm_area_set(C, NULL);
2435
2436       wm_event_free_handler(&handler->head);
2437
2438       action = WM_HANDLER_BREAK;
2439       break;
2440     }
2441   }
2442
2443   return action;
2444 }
2445
2446 static int wm_handler_fileselect_call(bContext *C,
2447                                       ListBase *handlers,
2448                                       wmEventHandler_Op *handler,
2449                                       const wmEvent *event)
2450 {
2451   int action = WM_HANDLER_CONTINUE;
2452
2453   if (event->type != EVT_FILESELECT) {
2454     return action;
2455   }
2456   if (handler->op != (wmOperator *)event->customdata) {
2457     return action;
2458   }
2459
2460   return wm_handler_fileselect_do(C, handlers, handler, event->val);
2461 }
2462
2463 static bool handler_boundbox_test(wmEventHandler *handler, const wmEvent *event)
2464 {
2465   if (handler->bbwin) {
2466     if (handler->bblocal) {
2467       rcti rect = *handler->bblocal;
2468       BLI_rcti_translate(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
2469
2470       if (BLI_rcti_isect_pt_v(&rect, &event->x)) {
2471         return 1;
2472       }
2473       else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(&rect, &event->prevx)) {
2474         return 1;
2475       }
2476       else {
2477         return 0;
2478       }
2479     }
2480     else {
2481       if (BLI_rcti_isect_pt_v(handler->bbwin, &event->x)) {
2482         return 1;
2483       }
2484       else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(handler->bbwin, &event->prevx)) {
2485         return 1;
2486       }
2487       else {
2488         return 0;
2489       }
2490     }
2491   }
2492   return 1;
2493 }
2494
2495 static int wm_action_not_handled(int action)
2496 {
2497   return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
2498 }
2499
2500 static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers)
2501 {
2502   const bool do_debug_handler =
2503       (G.debug & G_DEBUG_HANDLERS) &&
2504       /* comment this out to flood the console! (if you really want to test) */
2505       !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE);
2506 #define PRINT \
2507   if (do_debug_handler) \
2508   printf
2509
2510   wmWindowManager *wm = CTX_wm_manager(C);
2511   int action = WM_HANDLER_CONTINUE;
2512   int always_pass;
2513
2514   if (handlers == NULL) {
2515     return action;
2516   }
2517
2518   /* modal handlers can get removed in this loop, we keep the loop this way
2519    *
2520    * note: check 'handlers->first' because in rare cases the handlers can be cleared
2521    * by the event that's called, for eg:
2522    *
2523    * Calling a python script which changes the area.type, see [#32232] */
2524   for (wmEventHandler *handler_base = handlers->first, *handler_base_next;
2525        handler_base && handlers->first;
2526        handler_base = handler_base_next) {
2527     handler_base_next = handler_base->next;
2528
2529     /* during this loop, ui handlers for nested menus can tag multiple handlers free */
2530     if (handler_base->flag & WM_HANDLER_DO_FREE) {
2531       /* pass */
2532     }
2533     else if (handler_boundbox_test(handler_base, event)) { /* optional boundbox */
2534       /* in advance to avoid access to freed event on window close */
2535       always_pass = wm_event_always_pass(event);
2536
2537       /* modal+blocking handler_base */
2538       if (handler_base->flag & WM_HANDLER_BLOCKING) {
2539         action |= WM_HANDLER_BREAK;
2540       }
2541
2542       /* Handle all types here. */
2543       if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
2544         wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
2545         wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler);
2546
2547         PRINT("%s:   checking '%s' ...", __func__, keymap->idname);
2548
2549         if (keymap == NULL) {
2550           /* Only callback is allowed to have NULL keymaps. */
2551           BLI_assert(handler->dynamic.keymap_fn);
2552         }
2553         else if (WM_keymap_poll(C, keymap)) {
2554
2555           PRINT("pass\n");
2556
2557           for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
2558             if (wm_eventmatch(event, kmi)) {
2559               struct wmEventHandler_KeymapPost keymap_post = handler->post;
2560
2561               PRINT("%s:     item matched '%s'\n", __func__, kmi->idname);
2562
2563               /* weak, but allows interactive callback to not use rawkey */
2564               event->keymap_idname = kmi->idname;
2565
2566               action |= wm_handler_operator_call(C, handlers, handler_base, event, kmi->ptr);
2567
2568               if (action & WM_HANDLER_BREAK) {
2569                 /* not always_pass here, it denotes removed handler_base */
2570                 CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
2571                 if (keymap_post.post_fn != NULL) {
2572                   keymap_post.post_fn(keymap, kmi, keymap_post.user_data);
2573                 }
2574                 break;
2575               }
2576               else {
2577                 if (action & WM_HANDLER_HANDLED) {
2578                   CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
2579                 }
2580                 else {
2581                   CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
2582                 }
2583               }
2584             }
2585           }
2586         }
2587         else {
2588           PRINT("fail\n");
2589         }
2590       }
2591       else if (handler_base->type == WM_HANDLER_TYPE_UI) {
2592         wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
2593         BLI_assert(handler->handle_fn != NULL);
2594         if (!wm->is_interface_locked) {
2595           action |= wm_handler_ui_call(C, handler, event, always_pass);
2596         }
2597       }
2598       else if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
2599         wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
2600         if (!wm->is_interface_locked && event->type == EVT_DROP) {
2601           wmDropBox *drop = handler->dropboxes->first;
2602           for (; drop; drop = drop->next) {
2603             /* other drop custom types allowed */
2604             if (event->custom == EVT_DATA_DRAGDROP) {
2605               ListBase *lb = (ListBase *)event->customdata;
2606               wmDrag *drag;
2607
2608               for (drag = lb->first; drag; drag = drag->next) {
2609                 const char *tooltip = NULL;
2610                 if (drop->poll(C, drag, event, &tooltip)) {
2611                   /* Optionally copy drag information to operator properties. */
2612                   if (drop->copy) {
2613                     drop->copy(drag, drop);
2614                   }
2615
2616                   /* Pass single matched wmDrag onto the operator. */
2617                   BLI_remlink(lb, drag);
2618                   ListBase single_lb = {drag, drag};
2619                   event->customdata = &single_lb;
2620
2621                   wm_operator_call_internal(
2622                       C, drop->ot, drop->ptr, NULL, drop->opcontext, false, event);
2623                   action |= WM_HANDLER_BREAK;
2624
2625                   /* free the drags */
2626                   WM_drag_free_list(lb);
2627                   WM_drag_free_list(&single_lb);
2628
2629                   event->customdata = NULL;
2630                   event->custom = 0;
2631
2632                   /* XXX fileread case */
2633                   if (CTX_wm_window(C) == NULL) {
2634                     return action;
2635                   }
2636
2637                   /* escape from drag loop, got freed */
2638                   break;
2639                 }
2640               }
2641             }
2642           }
2643         }
2644       }
2645       else if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
2646         wmEventHandler_Gizmo *handler = (wmEventHandler_Gizmo *)handler_base;
2647         ScrArea *area = CTX_wm_area(C);
2648         ARegion *region = CTX_wm_region(C);
2649         wmGizmoMap *gzmap = handler->gizmo_map;
2650         BLI_assert(gzmap != NULL);
2651         wmGizmo *gz = wm_gizmomap_highlight_get(gzmap);
2652
2653         if (region->gizmo_map != handler->gizmo_map) {
2654           WM_gizmomap_tag_refresh(handler->gizmo_map);
2655         }
2656
2657         wm_gizmomap_handler_context_gizmo(C, handler);
2658         wm_region_mouse_co(C, event);
2659
2660         /* handle gizmo highlighting */
2661         if (event->type == MOUSEMOVE && !wm_gizmomap_modal_get(gzmap)) {
2662           int part;
2663           gz = wm_gizmomap_highlight_find(gzmap, C, event, &part);
2664           if (wm_gizmomap_highlight_set(gzmap, C, gz, part) && gz != NULL) {
2665             if (U.flag & USER_TOOLTIPS) {
2666               WM_tooltip_timer_init(C, CTX_wm_window(C), region, WM_gizmomap_tooltip_init);
2667             }
2668           }
2669         }
2670         else {
2671           /* Either we operate on a single highlighted item
2672            * or groups attached to the selected gizmos.
2673            * To simplify things both cases loop over an array of items. */
2674           wmGizmoGroup *gzgroup_first;
2675           bool is_gzgroup_single;
2676
2677           if (ISMOUSE(event->type)) {
2678             /* Keep gz set as-is, just fake single selection. */
2679             if (gz) {
2680               gzgroup_first = gz->parent_gzgroup;
2681             }
2682             else {
2683               gzgroup_first = NULL;
2684             }
2685             is_gzgroup_single = true;
2686           }
2687           else {
2688             if (WM_gizmomap_is_any_selected(gzmap)) {
2689               const ListBase *groups = WM_gizmomap_group_list(gzmap);
2690               gzgroup_first = groups->first;
2691             }
2692             else {
2693               gzgroup_first = NULL;
2694             }
2695             is_gzgroup_single = false;
2696           }
2697
2698           /* Don't use from now on. */
2699           gz = NULL;
2700
2701           for (wmGizmoGroup *gzgroup = gzgroup_first; gzgroup; gzgroup = gzgroup->next) {
2702             /* get user customized keymap from default one */
2703
2704             if ((is_gzgroup_single == false) &&
2705                 /* We might want to change the logic here and use some kind of gizmo edit-mode.
2706                  * For now just use keymap when a selection exists. */
2707                 wm_gizmogroup_is_any_selected(gzgroup) == false) {
2708               continue;
2709             }
2710
2711             wmKeyMap *keymap = WM_keymap_active(wm, gzgroup->type->keymap);
2712             wmKeyMapItem *kmi;
2713
2714             PRINT("%s:   checking '%s' ...", __func__, keymap->idname);
2715
2716             if (WM_keymap_poll(C, keymap)) {
2717               PRINT("pass\n");
2718               for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
2719                 if (wm_eventmatch(event, kmi)) {
2720                   PRINT("%s:     item matched '%s'\n", __func__, kmi->idname);
2721
2722                   /* weak, but allows interactive callback to not use rawkey */
2723                   event->keymap_idname = kmi->idname;
2724
2725                   CTX_wm_gizmo_group_set(C, gzgroup);
2726
2727                   /* handler->op is called later, we want keymap op to be triggered here */
2728                   action |= wm_handler_operator_call(C, handlers, handler_base, event, kmi->ptr);
2729
2730                   CTX_wm_gizmo_group_set(C, NULL);
2731
2732                   if (action & WM_HANDLER_BREAK) {
2733                     if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2734                       printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
2735                     }
2736                     break;
2737                   }
2738                   else {
2739                     if (action & WM_HANDLER_HANDLED) {
2740                       if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2741                         printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
2742                       }
2743                     }
2744                     else {
2745                       PRINT("%s:       un-handled '%s'\n", __func__, kmi->idname);
2746                     }
2747                   }
2748                 }
2749               }
2750             }
2751             else {
2752               PRINT("fail\n");
2753             }
2754
2755             if (action & WM_HANDLER_BREAK) {
2756               break;
2757             }
2758
2759             if (is_gzgroup_single) {
2760               break;
2761             }
2762           }
2763         }
2764
2765         /* restore the area */
2766         CTX_wm_area_set(C, area);
2767         CTX_wm_region_set(C, region);
2768       }
2769       else if (handler_base->type == WM_HANDLER_TYPE_OP) {
2770         wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2771         if (handler->is_fileselect) {
2772           if (!wm->is_interface_locked) {
2773             /* screen context changes here */
2774             action |= wm_handler_fileselect_call(C, handlers, handler, event);
2775           }
2776         }
2777         else {
2778           action |= wm_handler_operator_call(C, handlers, handler_base, event, NULL);
2779         }
2780       }
2781       else {
2782         /* Unreachable (handle all types above). */
2783         BLI_assert(0);
2784       }
2785
2786       if (action & WM_HANDLER_BREAK) {
2787         if (always_pass) {
2788           action &= ~WM_HANDLER_BREAK;
2789         }
2790         else {
2791           break;
2792         }
2793       }
2794     }
2795
2796     /* XXX fileread case, if the wm is freed then the handler's
2797      * will have been too so the code below need not run. */
2798     if (CTX_wm_window(C) == NULL) {
2799       return action;
2800     }
2801
2802     /* XXX code this for all modal ops, and ensure free only happens here */
2803
2804     /* modal ui handler can be tagged to be freed */
2805     if (BLI_findindex(handlers, handler_base) !=
2806         -1) { /* could be freed already by regular modal ops */
2807       if (handler_base->flag & WM_HANDLER_DO_FREE) {
2808         BLI_remlink(handlers, handler_base);
2809         wm_event_free_handler(handler_base);
2810       }
2811     }
2812   }
2813
2814   if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL)) {
2815     wm_cursor_arrow_move(CTX_wm_window(C), event);
2816   }
2817
2818 #undef PRINT
2819
2820   return action;
2821 }
2822
2823 /* this calls handlers twice - to solve (double-)click events */
2824 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
2825 {
2826   int action = wm_handlers_do_intern(C, event, handlers);
2827
2828   /* fileread case */
2829   if (CTX_wm_window(C) == NULL) {
2830     return action;
2831   }
2832
2833   if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
2834
2835     /* Test for CLICK_DRAG events. */
2836     if (wm_action_not_handled(action)) {
2837       if (event->check_drag) {
2838         wmWindow *win = CTX_wm_window(C);
2839         if ((abs(event->x - win->eventstate->prevclickx)) >=
2840                 WM_EVENT_CURSOR_CLICK_DRAG_THRESHOLD ||
2841             (abs(event->y - win->eventstate->prevclicky)) >=
2842                 WM_EVENT_CURSOR_CLICK_DRAG_THRESHOLD) {
2843           int x = event->x;
2844           int y = event->y;
2845           short val = event->val;
2846           short type = event->type;
2847
2848           event->x = win->eventstate->prevclickx;
2849           event->y = win->eventstate->prevclicky;
2850           event->val = KM_CLICK_DRAG;
2851           event->type = win->eventstate->type;
2852
2853           CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG");
2854
2855           action |= wm_handlers_do_intern(C, event, handlers);
2856
2857           event->val = val;
2858           event->type = type;
2859           event->x = x;
2860           event->y = y;
2861
2862           win->eventstate->check_click = false;
2863           win->eventstate->check_drag = false;
2864         }
2865       }
2866     }
2867     else {
2868       wmWindow *win = CTX_wm_window(C);
2869       if (win) {
2870         win->eventstate->check_drag = false;
2871       }
2872     }
2873   }
2874   else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) {
2875     /* All events that don't set wmEvent.prevtype must be ignored. */
2876
2877     /* Test for CLICK events. */
2878     if (wm_action_not_handled(action)) {
2879       wmWindow *win = CTX_wm_window(C);
2880
2881       /* eventstate stores if previous event was a KM_PRESS, in case that
2882        * wasn't handled, the KM_RELEASE will become a KM_CLICK */
2883
2884       if (win != NULL) {
2885         if (event->val == KM_PRESS) {
2886           win->eventstate->check_click = true;
2887           win->eventstate->check_drag = true;
2888         }
2889         else if (event->val == KM_RELEASE) {
2890           win->eventstate->check_drag = false;
2891         }
2892       }
2893
2894       if (win && win->eventstate->prevtype == event->type) {
2895
2896         if ((event->val == KM_RELEASE) && (win->eventstate->prevval == KM_PRESS) &&
2897             (win->eventstate->check_click == true)) {
2898           if ((abs(event->x - win->eventstate->prevclickx)) < WM_EVENT_CLICK_TWEAK_THRESHOLD &&
2899               (abs(event->y - win->eventstate->prevclicky)) < WM_EVENT_CLICK_TWEAK_THRESHOLD) {
2900             /* Position is where the actual click happens, for more
2901              * accurate selecting in case the mouse drifts a little. */
2902             int x = event->x;
2903             int y = event->y;
2904
2905             event->x = win->eventstate->prevclickx;
2906             event->y = win->eventstate->prevclicky;
2907             event->val = KM_CLICK;
2908
2909             CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
2910
2911             action |= wm_handlers_do_intern(C, event, handlers);
2912
2913             event->val = KM_RELEASE;
2914             event->x = x;
2915             event->y = y;
2916           }
2917           else {
2918             win->eventstate->check_click = 0;
2919             win->eventstate->check_drag = 0;
2920           }
2921         }
2922         else if (event->val == KM_DBL_CLICK) {
2923           /* The underlying event is a press, so try and handle this. */
2924           event->val = KM_PRESS;
2925           action |= wm_handlers_do_intern(C, event, handlers);
2926
2927           /* revert value if not handled */
2928           if (wm_action_not_handled(action)) {
2929             event->val = KM_DBL_CLICK;
2930           }
2931         }
2932       }
2933     }
2934     else {
2935       wmWindow *win = CTX_wm_window(C);
2936       if (win) {
2937         win->eventstate->check_click = 0;
2938       }
2939     }
2940   }
2941
2942   return action;
2943 }
2944
2945 static int wm_event_inside_i(wmEvent *event, rcti *rect)
2946 {
2947   if (wm_event_always_pass(event)) {
2948     return 1;
2949   }
2950   if (BLI_rcti_isect_pt_v(rect, &event->x)) {
2951     return 1;
2952   }
2953   return 0;
2954 }
2955
2956 static ScrArea *area_event_inside(bContext *C, const int xy[2])
2957 {
2958   wmWindow *win = CTX_wm_window(C);
2959   bScreen *screen = CTX_wm_screen(C);
2960
2961   if (screen) {
2962     ED_screen_areas_iter(win, screen, sa)
2963     {
2964       if (BLI_rcti_isect_pt_v(&sa->totrct, xy)) {
2965         return sa;
2966       }
2967     }
2968   }
2969   return NULL;
2970 }
2971
2972 static ARegion *region_event_inside(bContext *C, const int xy[2])
2973 {
2974   bScreen *screen = CTX_wm_screen(C);
2975   ScrArea *area = CTX_wm_area(C);
2976   ARegion *ar;
2977
2978   if (screen && area) {
2979     for (ar = area->regionbase.first; ar; ar = ar->next) {
2980       if (BLI_rcti_isect_pt_v(&ar->winrct, xy)) {
2981         return ar;
2982       }
2983     }
2984   }
2985   return NULL;
2986 }
2987
2988 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
2989 {
2990   if (ar) {
2991     for (; pc; pc = pc->next) {
2992       if (pc->poll == NULL || pc->poll(C)) {
2993         wmWindow *win = CTX_wm_window(C);
2994         WM_paint_cursor_tag_redraw(win, ar);
2995       }
2996     }
2997   }
2998 }
2999
3000 /* called on mousemove, check updates for paintcursors */
3001 /* context was set on active area and region */
3002 static void wm_paintcursor_test(bContext *C, const wmEvent *event)
3003 {
3004   wmWindowManager *wm = CTX_wm_manager(C);
3005
3006   if (wm->paintcursors.first) {
3007     ARegion *ar = CTX_wm_region(C);
3008
3009     if (ar) {
3010       wm_paintcursor_tag(C, wm->paintcursors.first, ar);
3011     }
3012
3013     /* if previous position was not in current region, we have to set a temp new context */
3014     if (ar == NULL || !BLI_rcti_isect_pt_v(&ar->winrct, &event->prevx)) {
3015       ScrArea *sa = CTX_wm_area(C);
3016
3017       CTX_wm_area_set(C, area_event_inside(C, &event->prevx));
3018       CTX_wm_region_set(C, region_event_inside(C, &event->prevx));
3019
3020       wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
3021
3022       CTX_wm_area_set(C, sa);
3023       CTX_wm_region_set(C, ar);
3024     }
3025   }
3026 }
3027
3028 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
3029 {
3030   bScreen *screen = WM_window_get_active_screen(win);
3031
3032   if (BLI_listbase_is_empty(&wm->drags)) {
3033     return;
3034   }
3035
3036   if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) {
3037     screen->do_draw_drag = true;
3038   }
3039   else if (event->type == ESCKEY) {
3040     WM_drag_free_list(&wm->drags);
3041
3042     screen->do_draw_drag = true;
3043   }
3044   else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
3045     event->type = EVT_DROP;
3046
3047     /* create customdata, first free existing */
3048     if (event->customdata) {
3049       if (event->customdatafree) {
3050         MEM_freeN(event->customdata);
3051       }
3052     }
3053
3054     event->custom = EVT_DATA_DRAGDROP;
3055     event->customdata = &wm->drags;
3056     event->customdatafree = 1;
3057
3058     /* clear drop icon */
3059     screen->do_draw_drag = true;
3060
3061     /* restore cursor (disabled, see wm_dragdrop.c) */
3062     // WM_cursor_modal_restore(win);
3063   }
3064 }
3065
3066 /* filter out all events of the pie that spawned the last pie unless it's a release event */
3067 static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
3068 {
3069   if (win->lock_pie_event && win->lock_pie_event == event->type) {
3070     if (event->val == KM_RELEASE) {
3071       win->lock_pie_event = EVENT_NONE;
3072       return false;
3073     }
3074     else {
3075       return true;
3076     }
3077   }
3078   else {
3079     return false;
3080   }
3081 }
3082
3083 /* called in main loop */
3084 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
3085 void wm_event_do_handlers(bContext *C)
3086 {
3087   wmWindowManager *wm = CTX_wm_manager(C);
3088   wmWindow *win;
3089
3090   /* update key configuration before handling events */
3091   WM_keyconfig_update(wm);
3092   WM_gizmoconfig_update(CTX_data_main(C));
3093
3094   for (win = wm->windows.first; win; win = win->next) {
3095     bScreen *screen = WM_window_get_active_screen(win);
3096     wmEvent *event;
3097
3098     /* some safety checks - these should always be set! */
3099     BLI_assert(WM_window_get_active_scene(win));
3100     BLI_assert(WM_window_get_active_screen(win));
3101     BLI_assert(WM_window_get_active_workspace(win));
3102
3103     if (screen == NULL) {
3104       wm_event_free_all(win);
3105     }
3106     else {
3107       Scene *scene = WM_window_get_active_scene(win);
3108
3109       if (scene) {
3110         int is_playing_sound = BKE_sound_scene_playing(scene);
3111
3112         if (is_playing_sound != -1) {
3113           bool is_playing_screen;
3114           CTX_wm_window_set(C, win);
3115           CTX_data_scene_set(C, scene);
3116
3117           is_playing_screen = (ED_screen_animation_playing(wm) != NULL);
3118
3119           if (((is_playing_sound == 1) && (is_playing_screen == 0)) ||
3120               ((is_playing_sound == 0) && (is_playing_screen == 1))) {
3121             ED_screen_animation_play(C, -1, 1);
3122           }
3123
3124           if (is_playing_sound == 0) {
3125             const float time = BKE_sound_sync_scene(scene);
3126             if (isfinite(time)) {
3127               int ncfra = time * (float)FPS + 0.5f;
3128               if (ncfra != scene->r.cfra) {
3129                 scene->r.cfra = ncfra;
3130                 Depsgraph *depsgraph = CTX_data_depsgraph(C);
3131                 ED_update_for_newframe(CTX_data_main(C), depsgraph);
3132                 WM_event_add_notifier(C, NC_WINDOW, NULL);
3133               }
3134             }
3135           }
3136
3137           CTX_data_scene_set(C, NULL);
3138           CTX_wm_screen_set(C, NULL);
3139           CTX_wm_window_set(C, NULL);
3140         }
3141       }
3142     }
3143
3144     while ((event = win->queue.first)) {
3145       int action = WM_HANDLER_CONTINUE;
3146
3147       /* active screen might change during handlers, update pointer */
3148       screen = WM_window_get_active_screen(win);
3149
3150       if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) &&
3151           !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3152         printf("\n%s: Handling event\n", __func__);
3153         WM_event_print(event);
3154       }
3155
3156       /* take care of pie event filter */
3157       if (wm_event_pie_filter(win, event)) {
3158         if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3159           CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
3160         }
3161         BLI_remlink(&win->queue, event);
3162         wm_event_free(event);
3163         continue;
3164       }
3165
3166       CTX_wm_window_set(C, win);
3167
3168       /* Clear tool-tip on mouse move. */
3169       if (screen->tool_tip && screen->tool_tip->exit_on_event) {
3170         if (ISMOUSE(event->type)) {
3171           WM_tooltip_clear(C, win);
3172         }
3173       }
3174
3175       /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
3176       CTX_wm_area_set(C, area_event_inside(C, &event->x));
3177       CTX_wm_region_set(C, region_event_inside(C, &event->x));
3178
3179       /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
3180       wm_window_make_drawable(wm, win);
3181
3182       wm_region_mouse_co(C, event);
3183
3184       /* first we do priority handlers, modal + some limited keymaps */
3185       action |= wm_handlers_do(C, event, &win->modalhandlers);
3186
3187       /* fileread case */
3188       if (CTX_wm_window(C) == NULL) {
3189         return;
3190       }
3191
3192       /* check for a tooltip */
3193       if (screen == WM_window_get_active_screen(win)) {
3194         if (screen->tool_tip && screen->tool_tip->timer) {
3195           if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) {
3196             WM_tooltip_init(C, win);
3197           }
3198         }
3199       }
3200
3201       /* check dragging, creates new event or frees, adds draw tag */
3202       wm_event_drag_test(wm, win, event);
3203
3204       /* builtin tweak, if action is break it removes tweak */
3205       wm_tweakevent_test(C, event, action);
3206
3207       if ((action & WM_HANDLER_BREAK) == 0) {
3208         ARegion *ar;
3209
3210         /* Note: setting subwin active should be done here, after modal handlers have been done */
3211         if (event->type == MOUSEMOVE) {
3212           /* state variables in screen, cursors. Also used in wm_draw.c, fails for modal handlers though */
3213           ED_screen_set_active_region(C, win, &event->x);
3214           /* for regions having custom cursors */
3215           wm_paintcursor_test(C, event);
3216         }
3217 #ifdef WITH_INPUT_NDOF
3218         else if (event->type == NDOF_MOTION) {
3219           win->addmousemove = true;
3220         }
3221 #endif
3222
3223         ED_screen_areas_iter(win, screen, sa)
3224         {
3225           /* after restoring a screen from SCREENMAXIMIZED we have to wait
3226            * with the screen handling till the region coordinates are updated */
3227           if (screen->skip_handling == true) {
3228             /* restore for the next iteration of wm_event_do_handlers */
3229             screen->skip_handling = false;
3230             break;
3231           }
3232
3233           /* update azones if needed - done here because it needs to be independent from redraws */
3234           if (sa->flag & AREA_FLAG_ACTIONZONES_UPDATE) {
3235             ED_area_azones_update(sa, &event->x);
3236           }
3237
3238           if (wm_event_inside_i(event, &sa->totrct)) {
3239             CTX_wm_area_set(C, sa);
3240
3241             if ((action & WM_HANDLER_BREAK) == 0) {
3242               for (ar = sa->regionbase.first; ar; ar = ar->next) {
3243                 if (wm_event_inside_i(event, &ar->winrct)) {
3244                   CTX_wm_region_set(C, ar);
3245
3246                   /* call even on non mouse events, since the */
3247                   wm_region_mouse_co(C, event);
3248
3249                   if (!BLI_listbase_is_empty(&wm->drags)) {
3250                     /* does polls for drop regions and checks uibuts */
3251                     /* need to be here to make sure region context is true */
3252                     if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) {
3253                       wm_drags_check_ops(C, event);
3254                     }
3255                   }
3256
3257                   action |= wm_handlers_do(C, event, &ar->handlers);
3258
3259                   /* fileread case (python), [#29489] */
3260                   if (CTX_wm_window(C) == NULL) {
3261                     return;
3262                   }
3263
3264                   if (action & WM_HANDLER_BREAK) {
3265                     break;
3266                   }
3267                 }
3268               }
3269             }
3270
3271             CTX_wm_region_set(C, NULL);
3272
3273             if ((action & WM_HANDLER_BREAK) == 0) {
3274               wm_region_mouse_co(C, event); /* only invalidates event->mval in this case */
3275               action |= wm_handlers_do(C, event, &sa->handlers);
3276             }
3277             CTX_wm_area_set(C, NULL);
3278
3279             /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
3280           }
3281         }
3282
3283         if ((action & WM_HANDLER_BREAK) == 0) {
3284           /* also some non-modal handlers need active area/region */
3285           CTX_wm_area_set(C, area_event_inside(C, &event->x));
3286           CTX_wm_region_set(C, region_event_inside(C, &event->x));
3287
3288           wm_region_mouse_co(C, event);
3289
3290           action |= wm_handlers_do(C, event, &win->handlers);
3291
3292           /* fileread case */
3293           if (CTX_wm_window(C) == NULL) {
3294             return;
3295           }
3296         }
3297       }
3298
3299       /* If press was handled, we don't want to do click. This way
3300        * press in tool keymap can override click in editor keymap.*/
3301       if (ISMOUSE_BUTTON(event->type) && event->val == KM_PRESS &&
3302           !wm_action_not_handled(action)) {
3303         win->eventstate->check_click = false;
3304       }
3305
3306       /* update previous mouse position for following events to use */
3307       win->eventstate->prevx = event->x;
3308       win->eventstate->prevy = event->y;
3309
3310       /* unlink and free here, blender-quit then frees all */
3311       BLI_remlink(&win->queue, event);
3312       wm_event_free(event);
3313     }
3314
3315     /* only add mousemove when queue was read entirely */
3316     if (win->addmousemove && win->eventstate) {
3317       wmEvent tevent = *(win->eventstate);
3318       // printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y);
3319       tevent.type = MOUSEMOVE;
3320       tevent.prevx = tevent.x;
3321       tevent.prevy = tevent.y;
3322       wm_event_add(win, &tevent);
3323       win->addmousemove = 0;
3324     }
3325
3326     CTX_wm_window_set(C, NULL);
3327   }
3328
3329   /* update key configuration after handling events */
3330   WM_keyconfig_update(wm);
3331   WM_gizmoconfig_update(CTX_data_main(C));
3332 }
3333
3334 /* ********** filesector handling ************ */
3335
3336 void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval)
3337 {
3338   /* add to all windows! */
3339   wmWindow *win;
3340
3341   for (win = wm->windows.first; win; win = win->next) {
3342     wmEvent event = *win->eventstate;
3343
3344     event.type = EVT_FILESELECT;
3345     event.val = eventval;
3346     event.customdata = ophandle;  // only as void pointer type check
3347
3348     wm_event_add(win, &event);
3349   }
3350 }
3351
3352 /* operator is supposed to have a filled "path" property */
3353 /* optional property: filetype (XXX enum?) */
3354
3355 /**
3356  * The idea here is to keep a handler alive on window queue, owning the operator.
3357  * The filewindow can send event to make it execute, thus ensuring
3358  * executing happens outside of lower level queues, with UI refreshed.
3359  * Should also allow multiwin solutions
3360  */
3361 void WM_event_add_fileselect(bContext *C, wmOperator *op)
3362 {
3363   wmWindowManager *wm = CTX_wm_manager(C);
3364   wmWindow *win = CTX_wm_window(C);
3365
3366   /* Close any popups, like when opening a file browser from the splash. */
3367   UI_popup_handlers_remove_all(C, &win->modalhandlers);
3368
3369   /* only allow 1 file selector open per window */
3370   LISTBASE_FOREACH_MUTABLE(wmEventHandler *, handler_base, &win->modalhandlers)
3371   {
3372     if (handler_base->type == WM_HANDLER_TYPE_OP) {
3373       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3374       if (handler->is_fileselect == false) {
3375         continue;
3376       }
3377       bScreen *screen = CTX_wm_screen(C);
3378       bool cancel_handler = true;
3379
3380       /* find the area with the file selector for this handler */
3381       ED_screen_areas_iter(win, screen, sa)
3382       {
3383         if (sa->spacetype == SPACE_FILE) {
3384           SpaceFile *sfile = sa->spacedata.first;
3385
3386           if (sfile->op == handler->op) {
3387             CTX_wm_area_set(C, sa);
3388             wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_CANCEL);
3389             cancel_handler = false;
3390             break;
3391           }
3392         }
3393       }
3394
3395       /* if not found we stop the handler without changing the screen */
3396       if (cancel_handler) {
3397         wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL);
3398       }
3399     }
3400   }
3401
3402   wmEventHandler_Op *handler = MEM_callocN(sizeof(*handler), __func__);
3403   handler->head.type = WM_HANDLER_TYPE_OP;
3404
3405   handler->is_fileselect = true;
3406   handler->op = op;
3407   handler->context.area = CTX_wm_area(C);
3408   handler->context.region = CTX_wm_region(C);
3409
3410   BLI_addhead(&win->modalhandlers, handler);
3411
3412   /* check props once before invoking if check is available
3413    * ensures initial properties are valid */
3414   if (op->type->check) {
3415     op->type->check(C, op); /* ignore return value */
3416   }
3417
3418   WM_event_fileselect_event(wm, op, EVT_FILESELECT_FULL_OPEN);
3419 }
3420
3421 #if 0
3422 /* lets not expose struct outside wm? */
3423 static void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
3424 {
3425   handler->flag = flag;
3426 }
3427 #endif
3428
3429 wmEventHandler_Op *WM_event_add_modal_handler(bContext *C, wmOperator *op)
3430 {
3431   wmEventHandler_Op *handler = MEM_callocN(sizeof(*handler), __func__);
3432   handler->head.type = WM_HANDLER_TYPE_OP;
3433   wmWindow *win = CTX_wm_window(C);
3434
3435   /* operator was part of macro */
3436   if (op->opm) {
3437     /* give the mother macro to the handler */
3438     handler->op = op->opm;
3439     /* mother macro opm becomes the macro element */
3440     handler->op->opm = op;
3441   }
3442   else {
3443     handler->op = op;
3444   }
3445
3446   handler->context.area = CTX_wm_area(C); /* means frozen screen context for modal handlers! */
3447   handler->context.region = CTX_wm_region(C);
3448   handler->context.region_type = handler->context.region ? handler->context.region->regiontype :
3449                                                            -1;
3450
3451   BLI_addhead(&win->modalhandlers, handler);
3452
3453   if (op->type->modalkeymap) {
3454     WM_window_status_area_tag_redraw(win);
3455   }
3456
3457   return handler;
3458 }
3459
3460 /**
3461  * Modal handlers store a pointer to an area which might be freed while the handler runs.
3462  * Use this function to NULL all handler pointers to \a old_area.
3463  */
3464 void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area)
3465 {
3466   LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
3467     if (handler_base->type == WM_HANDLER_TYPE_OP) {
3468       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3469       /* fileselect handler is quite special... it needs to keep old area stored in handler, so don't change it */
3470       if ((handler->context.area == old_area) && (handler->is_fileselect == false)) {
3471         handler->context.area = new_area;
3472       }
3473     }
3474   }
3475 }
3476
3477 /**
3478  * Modal handlers store a pointer to a region which might be freed while the handler runs.
3479  * Use this function to NULL all handler pointers to \a old_region.
3480  */
3481 void WM_event_modal_handler_region_replace(wmWindow *win,
3482                                            const ARegion *old_region,
3483                                            ARegion *new_region)
3484 {
3485   LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
3486     if (handler_base->type == WM_HANDLER_TYPE_OP) {
3487       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3488       /* fileselect handler is quite special... it needs to keep old region stored in handler, so don't change it */
3489       if ((handler->context.region == old_region) && (handler->is_fileselect == false)) {
3490         handler->context.region = new_region;
3491         handler->context.region_type =&