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