4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2007 Blender Foundation.
21 * All rights reserved.
24 * Contributor(s): Blender Foundation
26 * ***** END GPL LICENSE BLOCK *****
32 #include "DNA_listBase.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_windowmanager_types.h"
36 #include "DNA_userdef_types.h"
38 #include "MEM_guardedalloc.h"
40 #include "GHOST_C-api.h"
42 #include "BLI_blenlib.h"
44 #include "BKE_blender.h"
45 #include "BKE_context.h"
46 #include "BKE_idprop.h"
47 #include "BKE_global.h"
49 #include "BKE_report.h"
50 #include "BKE_scene.h"
51 #include "BKE_screen.h"
52 #include "BKE_utildefines.h"
54 #include "ED_fileselect.h"
56 #include "ED_screen.h"
57 #include "ED_space_api.h"
60 #include "RNA_access.h"
62 #include "UI_interface.h"
69 #include "wm_window.h"
70 #include "wm_event_system.h"
71 #include "wm_event_types.h"
73 /* ************ event management ************** */
75 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
77 wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
79 *event= *event_to_add;
80 BLI_addtail(&win->queue, event);
83 void wm_event_free(wmEvent *event)
85 if(event->customdata) {
86 if(event->customdatafree) {
87 /* note: pointer to listbase struct elsewhere */
88 if(event->custom==EVT_DATA_LISTBASE)
89 BLI_freelistN(event->customdata);
91 MEM_freeN(event->customdata);
97 void wm_event_free_all(wmWindow *win)
101 while((event= win->queue.first)) {
102 BLI_remlink(&win->queue, event);
103 wm_event_free(event);
107 /* ********************* notifiers, listeners *************** */
109 /* XXX: in future, which notifiers to send to other windows? */
110 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
112 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
114 note->wm= CTX_wm_manager(C);
115 BLI_addtail(¬e->wm->queue, note);
117 note->window= CTX_wm_window(C);
120 note->swinid= CTX_wm_region(C)->swinid;
122 note->category= type & NOTE_CATEGORY;
123 note->data= type & NOTE_DATA;
124 note->subtype= type & NOTE_SUBTYPE;
125 note->action= type & NOTE_ACTION;
127 note->reference= reference;
130 void WM_main_add_notifier(unsigned int type, void *reference)
133 wmWindowManager *wm= bmain->wm.first;
136 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
139 BLI_addtail(¬e->wm->queue, note);
141 note->category= type & NOTE_CATEGORY;
142 note->data= type & NOTE_DATA;
143 note->subtype= type & NOTE_SUBTYPE;
144 note->action= type & NOTE_ACTION;
146 note->reference= reference;
150 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
152 wmNotifier *note= wm->queue.first;
154 if(note) BLI_remlink(&wm->queue, note);
158 /* called in mainloop */
159 void wm_event_do_notifiers(bContext *C)
161 wmWindowManager *wm= CTX_wm_manager(C);
162 wmNotifier *note, *next;
168 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
169 for(win= wm->windows.first; win; win= win->next) {
172 CTX_wm_window_set(C, win);
174 for(note= wm->queue.first; note; note= next) {
177 if(note->category==NC_WM) {
178 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
180 wm_window_title(wm, win);
182 else if(note->data==ND_DATACHANGED)
183 wm_window_title(wm, win);
185 if(note->window==win) {
186 if(note->category==NC_SCREEN) {
187 if(note->data==ND_SCREENBROWSE) {
188 ED_screen_set(C, note->reference); // XXX hrms, think this over!
190 printf("screen set %p\n", note->reference);
192 else if(note->data==ND_SCREENDELETE) {
193 ED_screen_delete(C, note->reference); // XXX hrms, think this over!
195 printf("screen delete %p\n", note->reference);
200 if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
201 if(note->category==NC_SCENE) {
202 if(note->data==ND_SCENEBROWSE) {
203 ED_screen_set_scene(C, note->reference); // XXX hrms, think this over!
205 printf("scene set %p\n", note->reference);
207 else if(note->data==ND_FRAME)
210 if(note->action == NA_REMOVED) {
211 ED_screen_delete_scene(C, note->reference); // XXX hrms, think this over!
213 printf("scene delete %p\n", note->reference);
218 if(ELEM4(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE)) {
219 ED_info_stats_clear(CTX_data_scene(C));
220 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
224 /* depsgraph gets called, might send more notifiers */
225 ED_update_for_newframe(C, 1);
229 /* the notifiers are sent without context, to keep it clean */
230 while( (note=wm_notifier_next(wm)) ) {
233 for(win= wm->windows.first; win; win= win->next) {
235 /* filter out notifiers */
236 if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
237 else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
242 /* XXX context in notifiers? */
243 CTX_wm_window_set(C, win);
245 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
246 ED_screen_do_listen(win, note);
248 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
249 ED_region_do_listen(ar, note);
252 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
253 ED_area_do_listen(sa, note);
254 for(ar=sa->regionbase.first; ar; ar= ar->next) {
255 ED_region_do_listen(ar, note);
264 /* cached: editor refresh callbacks now, they get context */
265 for(win= wm->windows.first; win; win= win->next) {
268 CTX_wm_window_set(C, win);
269 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
271 CTX_wm_area_set(C, sa);
272 ED_area_do_refresh(C, sa);
276 /* XXX make lock in future, or separated derivedmesh users in scene */
278 /* depsgraph & animation: update tagged datablocks */
279 scene_update_tagged(win->screen->scene);
282 CTX_wm_window_set(C, NULL);
285 /* ********************* ui handler ******************* */
287 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass)
289 ScrArea *area= CTX_wm_area(C);
290 ARegion *region= CTX_wm_region(C);
291 ARegion *menu= CTX_wm_menu(C);
294 /* we set context to where ui handler came from */
295 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
296 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
297 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
299 retval= handler->ui_handle(C, event, handler->ui_userdata);
301 /* putting back screen context */
302 if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
303 CTX_wm_area_set(C, area);
304 CTX_wm_region_set(C, region);
305 CTX_wm_menu_set(C, menu);
308 /* this special cases is for areas and regions that get removed */
309 CTX_wm_area_set(C, NULL);
310 CTX_wm_region_set(C, NULL);
311 CTX_wm_menu_set(C, NULL);
314 if(retval == WM_UI_HANDLER_BREAK)
315 return WM_HANDLER_BREAK;
317 return WM_HANDLER_CONTINUE;
320 static void wm_handler_ui_cancel(bContext *C)
322 wmWindow *win= CTX_wm_window(C);
323 ARegion *ar= CTX_wm_region(C);
324 wmEventHandler *handler, *nexthandler;
329 for(handler= ar->handlers.first; handler; handler= nexthandler) {
330 nexthandler= handler->next;
332 if(handler->ui_handle) {
333 wmEvent event= *(win->eventstate);
334 event.type= EVT_BUT_CANCEL;
335 handler->ui_handle(C, &event, handler->ui_userdata);
340 /* ********************* operators ******************* */
342 int WM_operator_poll(bContext *C, wmOperatorType *ot)
344 wmOperatorTypeMacro *otmacro;
346 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
347 wmOperatorType *ot= WM_operatortype_find(otmacro->idname, 0);
349 if(0==WM_operator_poll(C, ot))
353 /* python needs operator type, so we added exception for it */
355 return ot->pyop_poll(C, ot);
362 static void wm_operator_finished(bContext *C, wmOperator *op, int repeat)
364 wmWindowManager *wm= CTX_wm_manager(C);
366 op->customdata= NULL;
368 /* we don't want to do undo pushes for operators that are being
369 called from operators that already do an undo push. usually
370 this will happen for python operators that call C operators */
371 if(wm->op_undo_depth == 0)
372 if(op->type->flag & OPTYPE_UNDO)
373 ED_undo_push_op(C, op);
377 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
378 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
382 if((wm->op_undo_depth == 0) && (op->type->flag & OPTYPE_REGISTER))
383 wm_operator_register(C, op);
385 WM_operator_free(op);
389 /* if repeat is true, it doesn't register again, nor does it free */
390 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
392 wmWindowManager *wm= CTX_wm_manager(C);
393 int retval= OPERATOR_CANCELLED;
395 if(op==NULL || op->type==NULL)
398 if(0==WM_operator_poll(C, op->type))
402 if(op->type->flag & OPTYPE_UNDO)
405 retval= op->type->exec(C, op);
407 if(op->type->flag & OPTYPE_UNDO)
411 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
412 if(op->reports->list.first)
413 uiPupMenuReports(C, op->reports);
415 if(retval & OPERATOR_FINISHED)
416 wm_operator_finished(C, op, repeat);
418 WM_operator_free(op);
420 return retval | OPERATOR_HANDLED;
424 /* for running operators with frozen context (modal handlers, menus) */
425 int WM_operator_call(bContext *C, wmOperator *op)
427 return wm_operator_exec(C, op, 0);
430 /* do this operator again, put here so it can share above code */
431 int WM_operator_repeat(bContext *C, wmOperator *op)
433 return wm_operator_exec(C, op, 1);
436 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
438 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
440 /* XXX adding new operator could be function, only happens here now */
442 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
444 /* initialize properties, either copy or create */
445 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
446 if(properties && properties->data) {
447 op->properties= IDP_CopyProperty(properties->data);
450 IDPropertyTemplate val = {0};
451 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
453 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
455 /* initialize error reports */
457 op->reports= reports; /* must be initialized already */
460 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
461 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
464 /* recursive filling of operator macro list */
465 if(ot->macro.first) {
466 static wmOperator *motherop= NULL;
467 wmOperatorTypeMacro *otmacro;
470 /* ensure all ops are in execution order in 1 list */
477 /* if properties exist, it will contain everything needed */
479 otmacro= ot->macro.first;
481 RNA_STRUCT_BEGIN(properties, prop) {
486 /* skip invalid properties */
487 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
489 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
490 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
491 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
493 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
495 BLI_addtail(&motherop->macro, opm);
496 opm->opm= motherop; /* pointer to mom, for modal() */
498 otmacro= otmacro->next;
503 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
504 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
505 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
507 BLI_addtail(&motherop->macro, opm);
508 opm->opm= motherop; /* pointer to mom, for modal() */
516 WM_operator_properties_sanitize(op->ptr, 0);
521 static void wm_operator_print(wmOperator *op)
523 char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
528 static void wm_region_mouse_co(bContext *C, wmEvent *event)
530 ARegion *ar= CTX_wm_region(C);
532 /* compatibility convention */
533 event->mval[0]= event->x - ar->winrct.xmin;
534 event->mval[1]= event->y - ar->winrct.ymin;
538 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports)
540 wmWindowManager *wm= CTX_wm_manager(C);
541 int retval= OPERATOR_PASS_THROUGH;
543 if(WM_operator_poll(C, ot)) {
544 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
546 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
547 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
549 if(op->type->invoke && event) {
550 wm_region_mouse_co(C, event);
552 if(op->type->flag & OPTYPE_UNDO)
555 retval= op->type->invoke(C, op, event);
557 if(op->type->flag & OPTYPE_UNDO)
560 else if(op->type->exec) {
561 if(op->type->flag & OPTYPE_UNDO)
564 retval= op->type->exec(C, op);
566 if(op->type->flag & OPTYPE_UNDO)
570 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
572 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
573 * currently python only uses this */
574 if((retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED)) && reports==NULL)
575 if(op->reports->list.first) /* only show the report if the report list was not given in the function */
576 uiPupMenuReports(C, op->reports);
578 if (retval & OPERATOR_FINISHED) { /* todo - this may conflict with the other wm_operator_print, if theres ever 2 prints for 1 action will may need to add modal check here */
580 wm_operator_print(op);
583 if(retval & OPERATOR_HANDLED)
584 ; /* do nothing, wm_operator_exec() has been called somewhere */
585 else if(retval & OPERATOR_FINISHED) {
586 wm_operator_finished(C, op, 0);
588 else if(retval & OPERATOR_RUNNING_MODAL) {
589 /* grab cursor during blocking modal ops (X11)
590 * Also check for macro
592 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
593 int bounds[4] = {-1,-1,-1,-1};
597 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
599 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
603 ARegion *ar= CTX_wm_region(C);
605 bounds[0]= ar->winrct.xmin;
606 bounds[1]= ar->winrct.ymax;
607 bounds[2]= ar->winrct.xmax;
608 bounds[3]= ar->winrct.ymin;
612 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
615 /* cancel UI handlers, typically tooltips that can hang around
616 while dragging the view or worse, that stay there permanently
617 after the modal operator has swallowed all events and passed
618 none to the UI handler */
619 wm_handler_ui_cancel(C);
622 WM_operator_free(op);
628 /* WM_operator_name_call is the main accessor function
629 * this is for python to access since its done the operator lookup
631 * invokes operator in context */
632 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
634 wmWindow *window= CTX_wm_window(C);
642 case WM_OP_INVOKE_DEFAULT:
643 case WM_OP_INVOKE_REGION_WIN:
644 case WM_OP_INVOKE_AREA:
645 case WM_OP_INVOKE_SCREEN:
646 /* window is needed for invoke, cancel operator */
650 event= window->eventstate;
658 case WM_OP_EXEC_REGION_WIN:
659 case WM_OP_INVOKE_REGION_WIN:
660 case WM_OP_EXEC_REGION_CHANNELS:
661 case WM_OP_INVOKE_REGION_CHANNELS:
662 case WM_OP_EXEC_REGION_PREVIEW:
663 case WM_OP_INVOKE_REGION_PREVIEW:
665 /* forces operator to go to the region window/channels/preview, for header menus
666 * but we stay in the same region if we are already in one
668 ARegion *ar= CTX_wm_region(C);
669 ScrArea *area= CTX_wm_area(C);
670 int type = RGN_TYPE_WINDOW;
673 case WM_OP_EXEC_REGION_CHANNELS:
674 case WM_OP_INVOKE_REGION_CHANNELS:
675 type = RGN_TYPE_CHANNELS;
677 case WM_OP_EXEC_REGION_PREVIEW:
678 case WM_OP_INVOKE_REGION_PREVIEW:
679 type = RGN_TYPE_PREVIEW;
682 case WM_OP_EXEC_REGION_WIN:
683 case WM_OP_INVOKE_REGION_WIN:
685 type = RGN_TYPE_WINDOW;
689 if(!(ar && ar->regiontype == type) && area) {
690 ARegion *ar1= BKE_area_find_region_type(area, type);
692 CTX_wm_region_set(C, ar1);
695 retval= wm_operator_invoke(C, ot, event, properties, reports);
697 /* set region back */
698 CTX_wm_region_set(C, ar);
702 case WM_OP_EXEC_AREA:
703 case WM_OP_INVOKE_AREA:
705 /* remove region from context */
706 ARegion *ar= CTX_wm_region(C);
708 CTX_wm_region_set(C, NULL);
709 retval= wm_operator_invoke(C, ot, event, properties, reports);
710 CTX_wm_region_set(C, ar);
714 case WM_OP_EXEC_SCREEN:
715 case WM_OP_INVOKE_SCREEN:
717 /* remove region + area from context */
718 ARegion *ar= CTX_wm_region(C);
719 ScrArea *area= CTX_wm_area(C);
721 CTX_wm_region_set(C, NULL);
722 CTX_wm_area_set(C, NULL);
723 retval= wm_operator_invoke(C, ot, event, properties, reports);
724 CTX_wm_region_set(C, ar);
725 CTX_wm_area_set(C, area);
729 case WM_OP_EXEC_DEFAULT:
730 case WM_OP_INVOKE_DEFAULT:
731 return wm_operator_invoke(C, ot, event, properties, reports);
739 /* invokes operator in context */
740 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
742 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
744 return wm_operator_call_internal(C, ot, context, properties, NULL);
749 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
750 - wmOperatorType is used instead of operator name since python alredy has the operator type
751 - poll() must be called by python before this runs.
752 - reports can be passed to this function (so python can report them as exceptions)
754 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
756 int retval= OPERATOR_CANCELLED;
760 wmWindowManager *wm= CTX_wm_manager(C);
761 op= wm_operator_create(wm, ot, properties, reports);
763 if (op->type->exec) {
764 if(op->type->flag & OPTYPE_UNDO)
767 retval= op->type->exec(C, op);
769 if(op->type->flag & OPTYPE_UNDO)
773 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
776 retval= wm_operator_call_internal(C, ot, context, properties, reports);
778 /* keep the reports around if needed later */
779 if (retval & OPERATOR_RUNNING_MODAL || ot->flag & OPTYPE_REGISTER)
781 reports->flag |= RPT_FREE;
788 /* ********************* handlers *************** */
790 /* future extra customadata free? */
791 void wm_event_free_handler(wmEventHandler *handler)
796 /* only set context when area/region is part of screen */
797 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
799 bScreen *screen= CTX_wm_screen(C);
801 if(screen && handler->op) {
802 if(handler->op_area==NULL)
803 CTX_wm_area_set(C, NULL);
807 for(sa= screen->areabase.first; sa; sa= sa->next)
808 if(sa==handler->op_area)
811 /* when changing screen layouts with running modal handlers (like render display), this
812 is not an error to print */
813 if(handler->op==NULL)
814 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
818 CTX_wm_area_set(C, sa);
819 for(ar= sa->regionbase.first; ar; ar= ar->next)
820 if(ar==handler->op_region)
822 /* XXX no warning print here, after full-area and back regions are remade */
824 CTX_wm_region_set(C, ar);
830 /* called on exit or remove area, only here call cancel callback */
831 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
833 wmEventHandler *handler;
834 wmWindowManager *wm= CTX_wm_manager(C);
836 /* C is zero on freeing database, modal handlers then already were freed */
837 while((handler=handlers->first)) {
838 BLI_remlink(handlers, handler);
841 if(handler->op->type->cancel) {
842 ScrArea *area= CTX_wm_area(C);
843 ARegion *region= CTX_wm_region(C);
845 wm_handler_op_context(C, handler);
847 if(handler->op->type->flag & OPTYPE_UNDO)
850 handler->op->type->cancel(C, handler->op);
852 if(handler->op->type->flag & OPTYPE_UNDO)
855 CTX_wm_area_set(C, area);
856 CTX_wm_region_set(C, region);
859 WM_cursor_ungrab(CTX_wm_window(C));
860 WM_operator_free(handler->op);
862 else if(handler->ui_remove) {
863 ScrArea *area= CTX_wm_area(C);
864 ARegion *region= CTX_wm_region(C);
865 ARegion *menu= CTX_wm_menu(C);
867 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
868 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
869 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
871 handler->ui_remove(C, handler->ui_userdata);
873 CTX_wm_area_set(C, area);
874 CTX_wm_region_set(C, region);
875 CTX_wm_menu_set(C, menu);
878 wm_event_free_handler(handler);
882 /* do userdef mappings */
883 int WM_userdef_event_map(int kmitype)
887 if(U.flag & USER_LMOUSESELECT)
893 if(U.flag & USER_LMOUSESELECT)
899 if(U.uiflag & USER_WHEELZOOMDIR)
902 return WHEELDOWNMOUSE;
905 if(U.uiflag & USER_WHEELZOOMDIR)
906 return WHEELDOWNMOUSE;
911 if(U.flag & USER_LMOUSESELECT)
917 if(U.flag & USER_LMOUSESELECT)
926 static void wm_eventemulation(wmEvent *event)
928 static int mmb_emulated = 0; /* this should be in a data structure somwhere */
930 /* middlemouse emulation */
931 if(U.flag & USER_TWOBUTTONMOUSE) {
932 if(event->type == LEFTMOUSE && (event->alt || mmb_emulated == KM_PRESS)) {
933 event->type = MIDDLEMOUSE;
935 mmb_emulated = event->val;
940 /* rightmouse emulation */
941 if(U.flag & USER_TWOBUTTONMOUSE) {
942 if(event->type == LEFTMOUSE && (event->oskey || mmb_emulated == KM_PRESS)) {
943 event->type = RIGHTMOUSE;
945 mmb_emulated = event->val;
950 /* numpad emulation */
951 if(U.flag & USER_NONUMPAD) {
952 switch(event->type) {
953 case ZEROKEY: event->type = PAD0; break;
954 case ONEKEY: event->type = PAD1; break;
955 case TWOKEY: event->type = PAD2; break;
956 case THREEKEY: event->type = PAD3; break;
957 case FOURKEY: event->type = PAD4; break;
958 case FIVEKEY: event->type = PAD5; break;
959 case SIXKEY: event->type = PAD6; break;
960 case SEVENKEY: event->type = PAD7; break;
961 case EIGHTKEY: event->type = PAD8; break;
962 case NINEKEY: event->type = PAD9; break;
963 case MINUSKEY: event->type = PADMINUS; break;
964 case EQUALKEY: event->type = PADPLUSKEY; break;
965 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
970 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
972 int kmitype= WM_userdef_event_map(kmi->type);
974 if(kmi->flag & KMI_INACTIVE) return 0;
976 /* the matching rules */
977 if(kmitype==KM_TEXTINPUT)
978 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
980 if(winevent->type!=kmitype) return 0;
983 if(winevent->val!=kmi->val) return 0;
985 /* modifiers also check bits, so it allows modifier order */
986 if(kmi->shift!=KM_ANY)
987 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
988 if(kmi->ctrl!=KM_ANY)
989 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
991 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
992 if(kmi->oskey!=KM_ANY)
993 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
996 if(winevent->keymodifier!=kmi->keymodifier) return 0;
998 /* key modifiers always check when event has it */
999 /* otherwise regular keypresses with keymodifier still work */
1000 if(winevent->keymodifier)
1001 if(ISTEXTINPUT(winevent->type))
1002 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1007 static int wm_event_always_pass(wmEvent *event)
1009 /* some events we always pass on, to ensure proper communication */
1010 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
1013 /* operator exists */
1014 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
1016 /* support for modal keymap in macros */
1020 if(op->type->modalkeymap) {
1021 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1024 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1025 if(wm_eventmatch(event, kmi)) {
1027 event->type= EVT_MODAL_MAP;
1028 event->val= kmi->propvalue;
1034 /* Warning: this function removes a modal handler, when finished */
1035 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
1037 int retval= OPERATOR_PASS_THROUGH;
1039 /* derived, modal or blocking operator */
1041 wmOperator *op= handler->op;
1042 wmOperatorType *ot= op->type;
1045 /* we set context to where modal handler came from */
1046 wmWindowManager *wm= CTX_wm_manager(C);
1047 ScrArea *area= CTX_wm_area(C);
1048 ARegion *region= CTX_wm_region(C);
1050 wm_handler_op_context(C, handler);
1051 wm_region_mouse_co(C, event);
1052 wm_event_modalkeymap(C, op, event);
1054 if(ot->flag & OPTYPE_UNDO)
1055 wm->op_undo_depth++;
1057 retval= ot->modal(C, op, event);
1059 if(ot->flag & OPTYPE_UNDO)
1060 wm->op_undo_depth--;
1062 /* putting back screen context, reval can pass trough after modal failures! */
1063 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1064 CTX_wm_area_set(C, area);
1065 CTX_wm_region_set(C, region);
1068 /* this special cases is for areas and regions that get removed */
1069 CTX_wm_area_set(C, NULL);
1070 CTX_wm_region_set(C, NULL);
1073 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
1074 if(op->reports->list.first)
1075 uiPupMenuReports(C, op->reports);
1077 if (retval & OPERATOR_FINISHED) {
1079 wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
1082 if(retval & OPERATOR_FINISHED) {
1083 wm_operator_finished(C, op, 0);
1086 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1087 WM_operator_free(op);
1091 /* remove modal handler, operator itself should have been cancelled and freed */
1092 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1093 WM_cursor_ungrab(CTX_wm_window(C));
1095 BLI_remlink(handlers, handler);
1096 wm_event_free_handler(handler);
1098 /* prevent silly errors from operator users */
1099 //retval &= ~OPERATOR_PASS_THROUGH;
1104 printf("wm_handler_operator_call error\n");
1107 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
1110 retval= wm_operator_invoke(C, ot, event, properties, NULL);
1113 /* Finished and pass through flag as handled */
1114 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1115 return WM_HANDLER_HANDLED;
1117 /* Modal unhandled, break */
1118 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1119 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1121 if(retval & OPERATOR_PASS_THROUGH)
1122 return WM_HANDLER_CONTINUE;
1124 return WM_HANDLER_BREAK;
1127 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1128 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1130 wmWindowManager *wm= CTX_wm_manager(C);
1132 int action= WM_HANDLER_CONTINUE;
1134 if(event->type != EVT_FILESELECT)
1136 if(handler->op != (wmOperator *)event->customdata)
1139 switch(event->val) {
1140 case EVT_FILESELECT_OPEN:
1141 case EVT_FILESELECT_FULL_OPEN:
1145 /* sa can be null when window A is active, but mouse is over window B */
1146 /* in this case, open file select in original window A */
1147 if (handler->op_area == NULL) {
1148 bScreen *screen = CTX_wm_screen(C);
1149 sa = (ScrArea *)screen->areabase.first;
1151 sa = handler->op_area;
1153 if(event->val==EVT_FILESELECT_OPEN)
1154 ED_area_newspace(C, sa, SPACE_FILE);
1156 ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1158 /* settings for filebrowser, sfile is not operator owner but sends events */
1159 sa = CTX_wm_area(C);
1160 sfile= (SpaceFile*)sa->spacedata.first;
1161 sfile->op= handler->op;
1163 ED_fileselect_set_params(sfile);
1165 action= WM_HANDLER_BREAK;
1169 case EVT_FILESELECT_EXEC:
1170 case EVT_FILESELECT_CANCEL:
1172 /* XXX validate area and region? */
1173 bScreen *screen= CTX_wm_screen(C);
1174 char *path= RNA_string_get_alloc(handler->op->ptr, "path", NULL, 0);
1176 if(screen != handler->filescreen)
1177 ED_screen_full_prevspace(C, CTX_wm_area(C));
1179 ED_area_prevspace(C, CTX_wm_area(C));
1181 /* remlink now, for load file case */
1182 BLI_remlink(handlers, handler);
1184 wm_handler_op_context(C, handler);
1186 /* needed for uiPupMenuReports */
1188 if(event->val==EVT_FILESELECT_EXEC) {
1189 /* a bit weak, might become arg for WM_event_fileselect? */
1190 /* XXX also extension code in image-save doesnt work for this yet */
1191 if (RNA_struct_find_property(handler->op->ptr, "check_existing") &&
1192 RNA_boolean_get(handler->op->ptr, "check_existing")) {
1193 /* this gives ownership to pupmenu */
1194 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1199 if(handler->op->type->flag & OPTYPE_UNDO)
1200 wm->op_undo_depth++;
1202 retval= handler->op->type->exec(C, handler->op);
1204 if(handler->op->type->flag & OPTYPE_UNDO)
1205 wm->op_undo_depth--;
1207 if (retval & OPERATOR_FINISHED)
1209 wm_operator_print(handler->op);
1211 if(wm->op_undo_depth == 0)
1212 if(handler->op->type->flag & OPTYPE_UNDO)
1213 ED_undo_push_op(C, handler->op);
1215 if(handler->op->reports->list.first) {
1217 /* FIXME, temp setting window, this is really bad!
1218 * only have because lib linking errors need to be seen by users :(
1219 * it can be removed without breaking anything but then no linking errors - campbell */
1220 wmWindow *win_prev= CTX_wm_window(C);
1222 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1224 handler->op->reports->printlevel = RPT_WARNING;
1225 uiPupMenuReports(C, handler->op->reports);
1227 CTX_wm_window_set(C, win_prev);
1230 WM_operator_free(handler->op);
1234 if(handler->op->type->cancel) {
1235 if(handler->op->type->flag & OPTYPE_UNDO)
1236 wm->op_undo_depth++;
1238 handler->op->type->cancel(C, handler->op);
1240 if(handler->op->type->flag & OPTYPE_UNDO)
1241 wm->op_undo_depth--;
1244 WM_operator_free(handler->op);
1247 CTX_wm_area_set(C, NULL);
1249 wm_event_free_handler(handler);
1253 action= WM_HANDLER_BREAK;
1261 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1263 if(handler->bbwin) {
1264 if(handler->bblocal) {
1265 rcti rect= *handler->bblocal;
1266 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1268 if(BLI_in_rcti(&rect, event->x, event->y))
1270 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1276 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1278 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1287 static int wm_action_not_handled(int action)
1289 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1292 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1294 wmWindowManager *wm= CTX_wm_manager(C);
1295 wmEventHandler *handler, *nexthandler;
1296 int action= WM_HANDLER_CONTINUE;
1299 if(handlers==NULL) return action;
1301 /* modal handlers can get removed in this loop, we keep the loop this way */
1302 for(handler= handlers->first; handler; handler= nexthandler) {
1303 nexthandler= handler->next;
1305 /* optional boundbox */
1306 if(handler_boundbox_test(handler, event)) {
1307 /* in advance to avoid access to freed event on window close */
1308 always_pass= wm_event_always_pass(event);
1310 /* modal+blocking handler */
1311 if(handler->flag & WM_HANDLER_BLOCKING)
1312 action |= WM_HANDLER_BREAK;
1314 if(handler->keymap) {
1315 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1318 if(!keymap->poll || keymap->poll(C)) {
1319 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1320 if(wm_eventmatch(event, kmi)) {
1322 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1324 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1325 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1331 else if(handler->ui_handle) {
1332 action |= wm_handler_ui_call(C, handler, event, always_pass);
1334 else if(handler->type==WM_HANDLER_FILESELECT) {
1335 /* screen context changes here */
1336 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1338 else if(handler->dropboxes) {
1339 if(event->type==EVT_DROP) {
1340 wmDropBox *drop= handler->dropboxes->first;
1341 for(; drop; drop= drop->next) {
1342 /* other drop custom types allowed */
1343 if(event->custom==EVT_DATA_LISTBASE) {
1344 ListBase *lb= (ListBase *)event->customdata;
1346 for(drag= lb->first; drag; drag= drag->next) {
1347 if(drop->poll(C, drag, event)) {
1348 drop->copy(drag, drop);
1350 wm_operator_invoke(C, drop->ot, event, drop->ptr, NULL);
1351 action |= WM_HANDLER_BREAK;
1359 /* modal, swallows all */
1360 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1363 if(action & WM_HANDLER_BREAK) {
1365 action &= ~WM_HANDLER_BREAK;
1372 if(CTX_wm_window(C)==NULL)
1376 /* test for CLICK event */
1377 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1378 wmWindow *win = CTX_wm_window(C);
1380 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1381 /* test for double click first */
1382 if ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time) {
1383 event->val = KM_DBL_CLICK;
1384 action |= wm_handlers_do(C, event, handlers);
1387 if (wm_action_not_handled(action)) {
1388 event->val = KM_CLICK;
1389 action |= wm_handlers_do(C, event, handlers);
1393 /* revert value if not handled */
1394 if (wm_action_not_handled(action)) {
1395 event->val = KM_RELEASE;
1403 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1405 if(wm_event_always_pass(event))
1407 if(BLI_in_rcti(rect, event->x, event->y))
1409 if(event->type==MOUSEMOVE) {
1410 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1418 static ScrArea *area_event_inside(bContext *C, int x, int y)
1420 bScreen *screen= CTX_wm_screen(C);
1424 for(sa= screen->areabase.first; sa; sa= sa->next)
1425 if(BLI_in_rcti(&sa->totrct, x, y))
1430 static ARegion *region_event_inside(bContext *C, int x, int y)
1432 bScreen *screen= CTX_wm_screen(C);
1433 ScrArea *area= CTX_wm_area(C);
1437 for(ar= area->regionbase.first; ar; ar= ar->next)
1438 if(BLI_in_rcti(&ar->winrct, x, y))
1443 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1446 for(; pc; pc= pc->next) {
1447 if(pc->poll == NULL || pc->poll(C)) {
1448 wmWindow *win= CTX_wm_window(C);
1449 win->screen->do_draw_paintcursor= 1;
1451 if(win->drawmethod != USER_DRAW_TRIPLE)
1452 ED_region_tag_redraw(ar);
1458 /* called on mousemove, check updates for paintcursors */
1459 /* context was set on active area and region */
1460 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1462 wmWindowManager *wm= CTX_wm_manager(C);
1464 if(wm->paintcursors.first) {
1465 ARegion *ar= CTX_wm_region(C);
1467 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1469 /* if previous position was not in current region, we have to set a temp new context */
1470 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1471 ScrArea *sa= CTX_wm_area(C);
1473 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1474 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1476 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1478 CTX_wm_area_set(C, sa);
1479 CTX_wm_region_set(C, ar);
1484 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1486 if(wm->drags.first==NULL) return;
1488 if(event->type==MOUSEMOVE)
1489 win->screen->do_draw_drag= 1;
1490 else if(event->type==ESCKEY) {
1491 BLI_freelistN(&wm->drags);
1492 win->screen->do_draw_drag= 1;
1494 else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1495 event->type= EVT_DROP;
1497 /* create customdata, first free existing */
1498 if(event->customdata) {
1499 if(event->customdatafree)
1500 MEM_freeN(event->customdata);
1503 event->custom= EVT_DATA_LISTBASE;
1504 event->customdata= &wm->drags;
1505 event->customdatafree= 1;
1507 /* clear drop icon */
1508 win->screen->do_draw_drag= 1;
1510 /* restore cursor (disabled, see wm_dragdrop.c) */
1511 // WM_cursor_restore(win);
1514 /* overlap fails otherwise */
1515 if(win->screen->do_draw_drag)
1516 if(win->drawmethod == USER_DRAW_OVERLAP)
1517 win->screen->do_draw= 1;
1521 /* called in main loop */
1522 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1523 void wm_event_do_handlers(bContext *C)
1525 wmWindowManager *wm= CTX_wm_manager(C);
1528 for(win= wm->windows.first; win; win= win->next) {
1531 if( win->screen==NULL )
1532 wm_event_free_all(win);
1534 while( (event= win->queue.first) ) {
1535 int action = WM_HANDLER_CONTINUE;
1537 wm_eventemulation(event);
1539 CTX_wm_window_set(C, win);
1541 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1542 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1543 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1545 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1546 wm_window_make_drawable(C, win);
1548 /* first we do priority handlers, modal + some limited keymaps */
1549 action |= wm_handlers_do(C, event, &win->modalhandlers);
1552 if(CTX_wm_window(C)==NULL)
1555 /* check dragging, creates new event or frees, adds draw tag */
1556 wm_event_drag_test(wm, win, event);
1558 /* builtin tweak, if action is break it removes tweak */
1559 wm_tweakevent_test(C, event, action);
1561 if((action & WM_HANDLER_BREAK) == 0) {
1566 /* XXX to solve, here screen handlers? */
1567 if(event->type==MOUSEMOVE) {
1568 /* state variables in screen, cursors */
1569 ED_screen_set_subwinactive(win, event);
1570 /* for regions having custom cursors */
1571 wm_paintcursor_test(C, event);
1574 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1575 if(wm_event_inside_i(event, &sa->totrct)) {
1576 CTX_wm_area_set(C, sa);
1578 if((action & WM_HANDLER_BREAK) == 0) {
1579 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1580 if(wm_event_inside_i(event, &ar->winrct)) {
1581 CTX_wm_region_set(C, ar);
1583 /* does polls for drop regions and checks uibuts */
1584 /* need to be here to make sure region context is true */
1585 if(event->type==MOUSEMOVE) {
1586 wm_region_mouse_co(C, event);
1587 wm_drags_check_ops(C, event);
1590 action |= wm_handlers_do(C, event, &ar->handlers);
1592 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1594 if(action & WM_HANDLER_BREAK)
1600 CTX_wm_region_set(C, NULL);
1602 if((action & WM_HANDLER_BREAK) == 0)
1603 action |= wm_handlers_do(C, event, &sa->handlers);
1605 CTX_wm_area_set(C, NULL);
1607 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1611 if((action & WM_HANDLER_BREAK) == 0) {
1612 /* also some non-modal handlers need active area/region */
1613 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1614 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1616 action |= wm_handlers_do(C, event, &win->handlers);
1619 if(CTX_wm_window(C)==NULL)
1623 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1624 doing it on ghost queue gives errors when mousemoves go over area borders */
1625 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1626 win->eventstate->prevx= event->x;
1627 win->eventstate->prevy= event->y;
1631 /* store last event for this window */
1632 /* mousemove and timer events don't overwrite last type */
1633 if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1634 if (wm_action_not_handled(action)) {
1635 if (win->eventstate->prevtype == event->type) {
1636 /* set click time on first click (press -> release) */
1637 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1638 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1641 /* reset click time if event type not the same */
1642 win->eventstate->prevclicktime = 0;
1645 win->eventstate->prevval = event->val;
1646 win->eventstate->prevtype = event->type;
1647 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1648 win->eventstate->prevtype = event->type;
1649 win->eventstate->prevval = event->val;
1650 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1651 } else { /* reset if not */
1652 win->eventstate->prevtype = -1;
1653 win->eventstate->prevval = 0;
1654 win->eventstate->prevclicktime = 0;
1658 /* unlink and free here, blender-quit then frees all */
1659 BLI_remlink(&win->queue, event);
1660 wm_event_free(event);
1664 /* only add mousemove when queue was read entirely */
1665 if(win->addmousemove && win->eventstate) {
1666 wmEvent event= *(win->eventstate);
1667 event.type= MOUSEMOVE;
1668 event.prevx= event.x;
1669 event.prevy= event.y;
1670 wm_event_add(win, &event);
1671 win->addmousemove= 0;
1674 CTX_wm_window_set(C, NULL);
1678 /* ********** filesector handling ************ */
1680 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1682 /* add to all windows! */
1685 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1686 wmEvent event= *win->eventstate;
1688 event.type= EVT_FILESELECT;
1689 event.val= eventval;
1690 event.customdata= ophandle; // only as void pointer type check
1692 wm_event_add(win, &event);
1696 /* operator is supposed to have a filled "path" property */
1697 /* optional property: filetype (XXX enum?) */
1699 /* Idea is to keep a handler alive on window queue, owning the operator.
1700 The filewindow can send event to make it execute, thus ensuring
1701 executing happens outside of lower level queues, with UI refreshed.
1702 Should also allow multiwin solutions */
1704 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1706 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1707 wmWindow *win= CTX_wm_window(C);
1708 int full= 1; // XXX preset?
1710 handler->type= WM_HANDLER_FILESELECT;
1712 handler->op_area= CTX_wm_area(C);
1713 handler->op_region= CTX_wm_region(C);
1714 handler->filescreen= CTX_wm_screen(C);
1716 BLI_addhead(&win->modalhandlers, handler);
1718 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1721 /* lets not expose struct outside wm? */
1722 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1724 handler->flag= flag;
1727 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1729 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1730 wmWindow *win= CTX_wm_window(C);
1732 /* operator was part of macro */
1734 /* give the mother macro to the handler */
1735 handler->op= op->opm;
1736 /* mother macro opm becomes the macro element */
1737 handler->op->opm= op;
1742 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
1743 handler->op_region= CTX_wm_region(C);
1745 BLI_addhead(&win->modalhandlers, handler);
1750 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1752 wmEventHandler *handler;
1755 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1759 /* only allow same keymap once */
1760 for(handler= handlers->first; handler; handler= handler->next)
1761 if(handler->keymap==keymap)
1764 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1765 BLI_addtail(handlers, handler);
1766 handler->keymap= keymap;
1771 /* priorities not implemented yet, for time being just insert in begin of list */
1772 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int priority)
1774 wmEventHandler *handler;
1776 WM_event_remove_keymap_handler(handlers, keymap);
1778 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1779 BLI_addhead(handlers, handler);
1780 handler->keymap= keymap;
1785 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1787 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1790 handler->bblocal= bblocal;
1791 handler->bbwin= bbwin;
1796 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1798 wmEventHandler *handler;
1800 for(handler= handlers->first; handler; handler= handler->next) {
1801 if(handler->keymap==keymap) {
1802 BLI_remlink(handlers, handler);
1803 wm_event_free_handler(handler);
1809 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1811 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1812 handler->ui_handle= func;
1813 handler->ui_remove= remove;
1814 handler->ui_userdata= userdata;
1815 handler->ui_area= (C)? CTX_wm_area(C): NULL;
1816 handler->ui_region= (C)? CTX_wm_region(C): NULL;
1817 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1819 BLI_addhead(handlers, handler);
1824 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1826 wmEventHandler *handler;
1828 for(handler= handlers->first; handler; handler= handler->next) {
1829 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1830 BLI_remlink(handlers, handler);
1831 wm_event_free_handler(handler);
1837 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
1839 wmEventHandler *handler;
1841 /* only allow same dropbox once */
1842 for(handler= handlers->first; handler; handler= handler->next)
1843 if(handler->dropboxes==dropboxes)
1846 handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
1848 /* dropbox stored static, no free or copy */
1849 handler->dropboxes= dropboxes;
1850 BLI_addhead(handlers, handler);
1855 /* XXX solution works, still better check the real cause (ton) */
1856 void WM_event_remove_area_handler(ListBase *handlers, void *area)
1858 wmEventHandler *handler, *nexthandler;
1860 for(handler = handlers->first; handler; handler= nexthandler) {
1861 nexthandler = handler->next;
1862 if (handler->type != WM_HANDLER_FILESELECT) {
1863 if (handler->ui_area == area) {
1864 BLI_remlink(handlers, handler);
1865 wm_event_free_handler(handler);
1871 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
1873 BLI_remlink(handlers, handler);
1874 wm_event_free_handler(handler);
1877 void WM_event_add_mousemove(bContext *C)
1879 wmWindow *window= CTX_wm_window(C);
1881 window->addmousemove= 1;
1884 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
1885 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
1887 /* user preset or keymap? dunno... */
1888 int tweak_modal= (U.flag & USER_DRAGIMMEDIATE)==0;
1890 switch(tweak_event) {
1894 if(evt->val==tweak_modal)
1897 /* this case is when modal callcback didnt get started with a tweak */
1904 /* ********************* ghost stuff *************** */
1906 static int convert_key(GHOST_TKey key)
1908 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
1909 return (AKEY + ((int) key - GHOST_kKeyA));
1910 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
1911 return (ZEROKEY + ((int) key - GHOST_kKey0));
1912 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
1913 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
1914 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
1915 return (F1KEY + ((int) key - GHOST_kKeyF1));
1918 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
1919 case GHOST_kKeyTab: return TABKEY;
1920 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
1921 case GHOST_kKeyClear: return 0;
1922 case GHOST_kKeyEnter: return RETKEY;
1924 case GHOST_kKeyEsc: return ESCKEY;
1925 case GHOST_kKeySpace: return SPACEKEY;
1926 case GHOST_kKeyQuote: return QUOTEKEY;
1927 case GHOST_kKeyComma: return COMMAKEY;
1928 case GHOST_kKeyMinus: return MINUSKEY;
1929 case GHOST_kKeyPeriod: return PERIODKEY;
1930 case GHOST_kKeySlash: return SLASHKEY;
1932 case GHOST_kKeySemicolon: return SEMICOLONKEY;
1933 case GHOST_kKeyEqual: return EQUALKEY;
1935 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
1936 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
1937 case GHOST_kKeyBackslash: return BACKSLASHKEY;
1938 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
1940 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
1941 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
1942 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
1943 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
1944 case GHOST_kKeyCommand: return COMMANDKEY;
1945 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
1946 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
1948 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
1949 case GHOST_kKeyNumLock: return 0;
1950 case GHOST_kKeyScrollLock: return 0;
1952 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
1953 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
1954 case GHOST_kKeyUpArrow: return UPARROWKEY;
1955 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
1957 case GHOST_kKeyPrintScreen: return 0;
1958 case GHOST_kKeyPause: return PAUSEKEY;
1960 case GHOST_kKeyInsert: return INSERTKEY;
1961 case GHOST_kKeyDelete: return DELKEY;
1962 case GHOST_kKeyHome: return HOMEKEY;
1963 case GHOST_kKeyEnd: return ENDKEY;
1964 case GHOST_kKeyUpPage: return PAGEUPKEY;
1965 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
1967 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
1968 case GHOST_kKeyNumpadEnter: return PADENTER;
1969 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
1970 case GHOST_kKeyNumpadMinus: return PADMINUS;
1971 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
1972 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
1974 case GHOST_kKeyGrLess: return GRLESSKEY;
1977 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
1982 /* adds customdata to event */
1983 static void update_tablet_data(wmWindow *win, wmEvent *event)
1985 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
1987 /* if there's tablet data from an active tablet device then add it */
1988 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
1989 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
1991 wmtab->Active = (int)td->Active;
1992 wmtab->Pressure = td->Pressure;
1993 wmtab->Xtilt = td->Xtilt;
1994 wmtab->Ytilt = td->Ytilt;
1996 event->custom= EVT_DATA_TABLET;
1997 event->customdata= wmtab;
1998 event->customdatafree= 1;
2002 /* imperfect but probably usable... draw/enable drags to other windows */
2003 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2005 short mx= evt->x, my= evt->y;
2007 if(wm->windows.first== wm->windows.last)
2010 /* top window bar... */
2011 if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) {
2013 wmEventHandler *handler;
2015 /* let's skip windows having modal handlers now */
2016 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2017 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2018 if(handler->ui_handle || handler->op)
2021 /* to desktop space */
2025 /* check other windows to see if it has mouse inside */
2026 for(owin= wm->windows.first; owin; owin= owin->next) {
2029 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2030 mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2031 evt->x= mx-owin->posx;
2032 evt->y= my-owin->posy;
2042 /* windows store own event queues, no bContext here */
2043 /* time is in 1000s of seconds, from ghost */
2044 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int time, void *customdata)
2047 wmEvent event, *evt= win->eventstate;
2049 /* initialize and copy state (only mouse x y and modifiers) */
2054 case GHOST_kEventCursorMove: {
2056 GHOST_TEventCursorData *cd= customdata;
2058 #if defined(__APPLE__) && defined(GHOST_COCOA)
2059 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2065 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2067 evt->y= (win->sizey-1) - cy;
2073 event.type= MOUSEMOVE;
2075 update_tablet_data(win, &event);
2076 wm_event_add(win, &event);
2078 /* also add to other window if event is there, this makes overdraws disappear nicely */
2079 /* it remaps mousecoord to other window in event */
2080 owin= wm_event_cursor_other_windows(wm, win, &event);
2082 wmEvent oevent= *(owin->eventstate);
2086 oevent.type= MOUSEMOVE;
2088 *(owin->eventstate)= oevent;
2089 update_tablet_data(owin, &oevent);
2090 wm_event_add(owin, &oevent);
2096 case GHOST_kEventTrackpad: {
2097 GHOST_TEventTrackpadData * pd = customdata;
2098 switch (pd->subtype) {
2099 case GHOST_kTrackpadEventMagnify:
2100 event.type = MOUSEZOOM;
2102 case GHOST_kTrackpadEventRotate:
2103 event.type = MOUSEROTATE;
2105 case GHOST_kTrackpadEventScroll:
2107 event.type= MOUSEPAN;
2110 #if defined(__APPLE__) && defined(GHOST_COCOA)
2111 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2112 event.x= evt->x = pd->x;
2113 event.y = evt->y = pd->y;
2117 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2118 event.x= evt->x= cx;
2119 event.y= evt->y= (win->sizey-1) - cy;
2122 // Use prevx/prevy so we can calculate the delta later
2123 event.prevx= event.x - pd->deltaX;
2124 event.prevy= event.y - pd->deltaY;
2126 update_tablet_data(win, &event);
2127 wm_event_add(win, &event);
2131 case GHOST_kEventButtonDown:
2132 case GHOST_kEventButtonUp: {
2133 GHOST_TEventButtonData *bd= customdata;
2134 event.val= (type==GHOST_kEventButtonDown) ? KM_PRESS:KM_RELEASE; /* Note!, this starts as 0/1 but later is converted to KM_PRESS/KM_RELEASE by tweak */
2136 if (bd->button == GHOST_kButtonMaskLeft)
2137 event.type= LEFTMOUSE;
2138 else if (bd->button == GHOST_kButtonMaskRight)
2139 event.type= RIGHTMOUSE;
2140 else if (bd->button == GHOST_kButtonMaskButton4)
2141 event.type= BUTTON4MOUSE;
2142 else if (bd->button == GHOST_kButtonMaskButton5)
2143 event.type= BUTTON5MOUSE;
2145 event.type= MIDDLEMOUSE;
2147 /* add to other window if event is there (not to both!) */
2148 owin= wm_event_cursor_other_windows(wm, win, &event);
2150 wmEvent oevent= *(owin->eventstate);
2154 oevent.type= event.type;
2155 oevent.val= event.val;
2157 update_tablet_data(owin, &oevent);
2158 wm_event_add(owin, &oevent);
2161 update_tablet_data(win, &event);
2162 wm_event_add(win, &event);
2168 case GHOST_kEventKeyDown:
2169 case GHOST_kEventKeyUp: {
2170 GHOST_TEventKeyData *kd= customdata;
2171 event.type= convert_key(kd->key);
2172 event.ascii= kd->ascii;
2173 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2175 /* exclude arrow keys, esc, etc from text input */
2176 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2180 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2181 event.shift= evt->shift= (event.val==KM_PRESS);
2182 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2183 event.shift= evt->shift = 3; // define?
2185 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2186 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2187 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2188 event.ctrl= evt->ctrl = 3; // define?
2190 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2191 event.alt= evt->alt= (event.val==KM_PRESS);
2192 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2193 event.alt= evt->alt = 3; // define?
2195 else if (event.type==COMMANDKEY) {
2196 event.oskey= evt->oskey= (event.val==KM_PRESS);
2197 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2198 event.oskey= evt->oskey = 3; // define?
2201 if(event.val==KM_PRESS && event.keymodifier==0)
2202 evt->keymodifier= event.type; /* only set in eventstate, for next event */
2203 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2204 event.keymodifier= evt->keymodifier= 0;
2207 /* this case happens on some systems that on holding a key pressed,
2208 generate press events without release, we still want to keep the
2209 modifier in win->eventstate, but for the press event of the same
2210 key we don't want the key modifier */
2211 if(event.keymodifier == event.type)
2212 event.keymodifier= 0;
2214 /* if test_break set, it catches this. XXX Keep global for now? */
2215 if(event.type==ESCKEY)
2218 wm_event_add(win, &event);
2223 case GHOST_kEventWheel: {
2224 GHOST_TEventWheelData* wheelData = customdata;
2226 if (wheelData->z > 0)
2227 event.type= WHEELUPMOUSE;
2229 event.type= WHEELDOWNMOUSE;
2231 event.val= KM_PRESS;
2232 wm_event_add(win, &event);
2236 case GHOST_kEventTimer: {
2238 event.custom= EVT_DATA_TIMER;
2239 event.customdata= customdata;
2240 wm_event_add(win, &event);
2245 case GHOST_kEventUnknown:
2246 case GHOST_kNumEventTypes:
2249 case GHOST_kEventWindowDeactivate: {
2250 event.type= WINDEACTIVATE;
2251 wm_event_add(win, &event);