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