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