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