Bugfix #24726
[blender.git] / source / blender / windowmanager / intern / wm_event_system.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
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. 
10  *
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.
15  *
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.
19  *
20  * The Original Code is Copyright (C) 2007 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32
33 #include "DNA_listBase.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_windowmanager_types.h"
37 #include "DNA_userdef_types.h"
38
39 #include "MEM_guardedalloc.h"
40
41 #include "GHOST_C-api.h"
42
43 #include "BLI_blenlib.h"
44
45 #include "BKE_blender.h"
46 #include "BKE_context.h"
47 #include "BKE_idprop.h"
48 #include "BKE_global.h"
49 #include "BKE_main.h"
50 #include "BKE_report.h"
51 #include "BKE_scene.h"
52 #include "BKE_screen.h"
53 #include "BKE_utildefines.h"
54 #include "BKE_sound.h"
55
56 #include "ED_fileselect.h"
57 #include "ED_info.h"
58 #include "ED_screen.h"
59 #include "ED_util.h"
60
61 #include "RNA_access.h"
62
63 #include "UI_interface.h"
64
65 #include "PIL_time.h"
66
67 #include "WM_api.h"
68 #include "WM_types.h"
69 #include "wm.h"
70 #include "wm_window.h"
71 #include "wm_event_system.h"
72 #include "wm_event_types.h"
73 #include "wm_draw.h"
74
75 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only);
76
77 /* ************ event management ************** */
78
79 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
80 {
81         wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
82         
83         *event= *event_to_add;
84         BLI_addtail(&win->queue, event);
85 }
86
87 void wm_event_free(wmEvent *event)
88 {
89         if(event->customdata) {
90                 if(event->customdatafree) {
91                         /* note: pointer to listbase struct elsewhere */
92                         if(event->custom==EVT_DATA_LISTBASE)
93                                 BLI_freelistN(event->customdata);
94                         else
95                                 MEM_freeN(event->customdata);
96                 }
97         }
98         MEM_freeN(event);
99 }
100
101 void wm_event_free_all(wmWindow *win)
102 {
103         wmEvent *event;
104         
105         while((event= win->queue.first)) {
106                 BLI_remlink(&win->queue, event);
107                 wm_event_free(event);
108         }
109 }
110
111 /* ********************* notifiers, listeners *************** */
112
113 static int wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, void *reference)
114 {
115         wmNotifier *note;
116
117         for(note=wm->queue.first; note; note=note->next)
118                 if((note->category|note->data|note->subtype|note->action) == type && note->reference == reference)
119                         return 1;
120         
121         return 0;
122 }
123
124 /* XXX: in future, which notifiers to send to other windows? */
125 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
126 {
127         wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
128         
129         note->wm= CTX_wm_manager(C);
130         BLI_addtail(&note->wm->queue, note);
131         
132         note->window= CTX_wm_window(C);
133         
134         if(CTX_wm_region(C))
135                 note->swinid= CTX_wm_region(C)->swinid;
136         
137         note->category= type & NOTE_CATEGORY;
138         note->data= type & NOTE_DATA;
139         note->subtype= type & NOTE_SUBTYPE;
140         note->action= type & NOTE_ACTION;
141         
142         note->reference= reference;
143 }
144
145 void WM_main_add_notifier(unsigned int type, void *reference)
146 {
147         Main *bmain= G.main;
148         wmWindowManager *wm= bmain->wm.first;
149
150         if(wm && !wm_test_duplicate_notifier(wm, type, reference)) {
151                 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
152                 
153                 note->wm= wm;
154                 BLI_addtail(&note->wm->queue, note);
155                 
156                 note->category= type & NOTE_CATEGORY;
157                 note->data= type & NOTE_DATA;
158                 note->subtype= type & NOTE_SUBTYPE;
159                 note->action= type & NOTE_ACTION;
160                 
161                 note->reference= reference;
162         }
163 }
164
165 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
166 {
167         wmNotifier *note= wm->queue.first;
168         
169         if(note) BLI_remlink(&wm->queue, note);
170         return note;
171 }
172
173 /* called in mainloop */
174 void wm_event_do_notifiers(bContext *C)
175 {
176         wmWindowManager *wm= CTX_wm_manager(C);
177         wmNotifier *note, *next;
178         wmWindow *win;
179         
180         if(wm==NULL)
181                 return;
182         
183         /* cache & catch WM level notifiers, such as frame change, scene/screen set */
184         for(win= wm->windows.first; win; win= win->next) {
185                 int do_anim= 0;
186                 
187                 CTX_wm_window_set(C, win);
188                 
189                 for(note= wm->queue.first; note; note= next) {
190                         next= note->next;
191
192                         if(note->category==NC_WM) {
193                                 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
194                                         wm->file_saved= 1;
195                                         wm_window_title(wm, win);
196                                 }
197                                 else if(note->data==ND_DATACHANGED)
198                                         wm_window_title(wm, win);
199                         }
200                         if(note->window==win) {
201                                 if(note->category==NC_SCREEN) {
202                                         if(note->data==ND_SCREENBROWSE) {
203                                                 ED_screen_set(C, note->reference);      // XXX hrms, think this over!
204                                                 if(G.f & G_DEBUG)
205                                                         printf("screen set %p\n", note->reference);
206                                         }
207                                         else if(note->data==ND_SCREENDELETE) {
208                                                 ED_screen_delete(C, note->reference);   // XXX hrms, think this over!
209                                                 if(G.f & G_DEBUG)
210                                                         printf("screen delete %p\n", note->reference);
211                                         }
212                                 }
213                         }
214
215                         if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
216                                 if(note->category==NC_SCENE) {
217                                         if(note->data==ND_SCENEBROWSE) {
218                                                 ED_screen_set_scene(C, note->reference);        // XXX hrms, think this over!
219                                                 if(G.f & G_DEBUG)
220                                                         printf("scene set %p\n", note->reference);
221                                         }
222                                         else if(note->data==ND_FRAME)
223                                                 do_anim= 1;
224                                         
225                                         if(note->action == NA_REMOVED) {
226                                                 ED_screen_delete_scene(C, note->reference);     // XXX hrms, think this over!
227                                                 if(G.f & G_DEBUG)
228                                                         printf("scene delete %p\n", note->reference);
229                                         }
230                                                 
231                                 }
232                         }
233                         if(ELEM5(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE, NC_WM)) {
234                                 ED_info_stats_clear(CTX_data_scene(C));
235                                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
236                         }
237                 }
238                 if(do_anim) {
239
240                         /* XXX, quick frame changes can cause a crash if framechange and rendering
241                          * collide (happens on slow scenes), scene_update_for_newframe can be called
242                          * twice which can depgraph update the same object at once */
243                         if(!G.rendering) {
244
245                                 /* depsgraph gets called, might send more notifiers */
246                                 ED_update_for_newframe(CTX_data_main(C), win->screen->scene, win->screen, 1);
247                         }
248                 }
249         }
250         
251         /* the notifiers are sent without context, to keep it clean */
252         while( (note=wm_notifier_next(wm)) ) {
253                 wmWindow *win;
254                 
255                 for(win= wm->windows.first; win; win= win->next) {
256                         
257                         /* filter out notifiers */
258                         if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
259                         else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
260                         else {
261                                 ScrArea *sa;
262                                 ARegion *ar;
263
264                                 /* XXX context in notifiers? */
265                                 CTX_wm_window_set(C, win);
266
267                                 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
268                                 ED_screen_do_listen(win, note);
269
270                                 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
271                                         ED_region_do_listen(ar, note);
272                                 }
273                                 
274                                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
275                                         ED_area_do_listen(sa, note);
276                                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
277                                                 ED_region_do_listen(ar, note);
278                                         }
279                                 }
280                         }
281                 }
282                 
283                 MEM_freeN(note);
284         }
285         
286         /* cached: editor refresh callbacks now, they get context */
287         for(win= wm->windows.first; win; win= win->next) {
288                 ScrArea *sa;
289                 
290                 CTX_wm_window_set(C, win);
291                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
292                         if(sa->do_refresh) {
293                                 CTX_wm_area_set(C, sa);
294                                 ED_area_do_refresh(C, sa);
295                         }
296                 }
297                 
298                 /* XXX make lock in future, or separated derivedmesh users in scene */
299                 if(!G.rendering)
300                         /* depsgraph & animation: update tagged datablocks */
301                         scene_update_tagged(CTX_data_main(C), win->screen->scene);
302         }
303
304         CTX_wm_window_set(C, NULL);
305 }
306
307 /* ********************* ui handler ******************* */
308
309 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass)
310 {
311         ScrArea *area= CTX_wm_area(C);
312         ARegion *region= CTX_wm_region(C);
313         ARegion *menu= CTX_wm_menu(C);
314         int retval;
315                         
316         /* we set context to where ui handler came from */
317         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
318         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
319         if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
320
321         retval= handler->ui_handle(C, event, handler->ui_userdata);
322
323         /* putting back screen context */
324         if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
325                 CTX_wm_area_set(C, area);
326                 CTX_wm_region_set(C, region);
327                 CTX_wm_menu_set(C, menu);
328         }
329         else {
330                 /* this special cases is for areas and regions that get removed */
331                 CTX_wm_area_set(C, NULL);
332                 CTX_wm_region_set(C, NULL);
333                 CTX_wm_menu_set(C, NULL);
334         }
335
336         if(retval == WM_UI_HANDLER_BREAK)
337                 return WM_HANDLER_BREAK;
338
339         return WM_HANDLER_CONTINUE;
340 }
341
342 static void wm_handler_ui_cancel(bContext *C)
343 {
344         wmWindow *win= CTX_wm_window(C);
345         ARegion *ar= CTX_wm_region(C);
346         wmEventHandler *handler, *nexthandler;
347
348         if(!ar)
349                 return;
350
351         for(handler= ar->handlers.first; handler; handler= nexthandler) {
352                 nexthandler= handler->next;
353
354                 if(handler->ui_handle) {
355                         wmEvent event= *(win->eventstate);
356                         event.type= EVT_BUT_CANCEL;
357                         handler->ui_handle(C, &event, handler->ui_userdata);
358                 }
359         }
360 }
361
362 /* ********************* operators ******************* */
363
364 int WM_operator_poll(bContext *C, wmOperatorType *ot)
365 {
366         wmOperatorTypeMacro *otmacro;
367         
368         for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
369                 wmOperatorType *ot_macro= WM_operatortype_find(otmacro->idname, 0);
370                 
371                 if(0==WM_operator_poll(C, ot_macro))
372                         return 0;
373         }
374         
375         /* python needs operator type, so we added exception for it */
376         if(ot->pyop_poll)
377                 return ot->pyop_poll(C, ot);
378         else if(ot->poll)
379                 return ot->poll(C);
380
381         return 1;
382 }
383
384 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
385 int WM_operator_poll_context(bContext *C, wmOperatorType *ot, int context)
386 {
387         return wm_operator_call_internal(C, ot, NULL, NULL, context, TRUE);
388 }
389
390 static void wm_operator_print(wmOperator *op)
391 {
392         char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
393         printf("%s\n", buf);
394         MEM_freeN(buf);
395 }
396
397 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, int popup)
398 {
399         wmWindowManager *wm = CTX_wm_manager(C);
400         ReportList *reports = CTX_wm_reports(C);
401         char *buf;
402         
403         if(popup)
404                 if(op->reports->list.first)
405                         uiPupMenuReports(C, op->reports);
406         
407         if(retval & OPERATOR_FINISHED) {
408                 if(G.f & G_DEBUG)
409                         wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
410                 
411                 if (op->type->flag & OPTYPE_REGISTER) {
412                         /* Report the python string representation of the operator */
413                         buf = WM_operator_pystring(C, op->type, op->ptr, 1);
414                         BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
415                         MEM_freeN(buf);
416                 }
417         }
418
419         if (op->reports->list.first) {
420                 ReportTimerInfo *rti;
421                 
422                 /* add reports to the global list, otherwise they are not seen */
423                 addlisttolist(&CTX_wm_reports(C)->list, &op->reports->list);
424                 
425                 /* After adding reports to the global list, reset the report timer. */
426                 WM_event_remove_timer(wm, NULL, reports->reporttimer);
427                 
428                 /* Records time since last report was added */
429                 reports->reporttimer= WM_event_add_timer(wm, CTX_wm_window(C), TIMER, 0.02);
430                 
431                 rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
432                 reports->reporttimer->customdata = rti;
433         }
434 }
435
436 /* this function is mainly to check that the rules for freeing
437  * an operator are kept in sync.
438  */
439 static int wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
440 {
441         return (wm->op_undo_depth == 0) && (ot->flag & OPTYPE_REGISTER);
442 }
443
444 static void wm_operator_finished(bContext *C, wmOperator *op, int repeat)
445 {
446         wmWindowManager *wm= CTX_wm_manager(C);
447
448         op->customdata= NULL;
449
450         /* we don't want to do undo pushes for operators that are being
451            called from operators that already do an undo push. usually
452            this will happen for python operators that call C operators */
453         if(wm->op_undo_depth == 0)
454                 if(op->type->flag & OPTYPE_UNDO)
455                         ED_undo_push_op(C, op);
456         
457         if(repeat==0) {
458                 if(G.f & G_DEBUG) {
459                         char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
460                         BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
461                         MEM_freeN(buf);
462                 }
463
464                 if(wm_operator_register_check(wm, op->type))
465                         wm_operator_register(C, op);
466                 else
467                         WM_operator_free(op);
468         }
469 }
470
471 /* if repeat is true, it doesn't register again, nor does it free */
472 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
473 {
474         wmWindowManager *wm= CTX_wm_manager(C);
475         int retval= OPERATOR_CANCELLED;
476         
477         CTX_wm_operator_poll_msg_set(C, NULL);
478         
479         if(op==NULL || op->type==NULL)
480                 return retval;
481         
482         if(0==WM_operator_poll(C, op->type))
483                 return retval;
484         
485         if(op->type->exec) {
486                 if(op->type->flag & OPTYPE_UNDO)
487                         wm->op_undo_depth++;
488
489                 retval= op->type->exec(C, op);
490
491                 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
492                         wm->op_undo_depth--;
493         }
494         
495         if (retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED) && repeat == 0)
496                 wm_operator_reports(C, op, retval, 0);
497         
498         if(retval & OPERATOR_FINISHED)
499                 wm_operator_finished(C, op, repeat);
500         else if(repeat==0)
501                 WM_operator_free(op);
502         
503         return retval | OPERATOR_HANDLED;
504         
505 }
506
507 /* for running operators with frozen context (modal handlers, menus) */
508 int WM_operator_call(bContext *C, wmOperator *op)
509 {
510         return wm_operator_exec(C, op, 0);
511 }
512
513 /* do this operator again, put here so it can share above code */
514 int WM_operator_repeat(bContext *C, wmOperator *op)
515 {
516         return wm_operator_exec(C, op, 1);
517 }
518
519 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
520 {
521         wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname);    /* XXX operatortype names are static still. for debug */
522         
523         /* XXX adding new operator could be function, only happens here now */
524         op->type= ot;
525         BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
526         
527         /* initialize properties, either copy or create */
528         op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
529         if(properties && properties->data) {
530                 op->properties= IDP_CopyProperty(properties->data);
531         }
532         else {
533                 IDPropertyTemplate val = {0};
534                 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
535         }
536         RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
537
538         /* initialize error reports */
539         if (reports) {
540                 op->reports= reports; /* must be initialized already */
541         }
542         else {
543                 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
544                 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
545         }
546         
547         /* recursive filling of operator macro list */
548         if(ot->macro.first) {
549                 static wmOperator *motherop= NULL;
550                 wmOperatorTypeMacro *otmacro;
551                 int root = 0;
552                 
553                 /* ensure all ops are in execution order in 1 list */
554                 if(motherop==NULL) {
555                         motherop = op;
556                         root = 1;
557                 }
558
559                 
560                 /* if properties exist, it will contain everything needed */
561                 if (properties) {
562                         otmacro= ot->macro.first;
563
564                         RNA_STRUCT_BEGIN(properties, prop) {
565
566                                 if (otmacro == NULL)
567                                         break;
568
569                                 /* skip invalid properties */
570                                 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
571                                 {
572                                         wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
573                                         PointerRNA someptr = RNA_property_pointer_get(properties, prop);
574                                         wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
575
576                                         IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
577
578                                         BLI_addtail(&motherop->macro, opm);
579                                         opm->opm= motherop; /* pointer to mom, for modal() */
580
581                                         otmacro= otmacro->next;
582                                 }
583                         }
584                         RNA_STRUCT_END;
585                 } else {
586                         for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
587                                 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
588                                 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
589
590                                 BLI_addtail(&motherop->macro, opm);
591                                 opm->opm= motherop; /* pointer to mom, for modal() */
592                         }
593                 }
594                 
595                 if (root)
596                         motherop= NULL;
597         }
598         
599         WM_operator_properties_sanitize(op->ptr, 0);
600
601         return op;
602 }
603
604 static void wm_region_mouse_co(bContext *C, wmEvent *event)
605 {
606         ARegion *ar= CTX_wm_region(C);
607         if(ar) {
608                 /* compatibility convention */
609                 event->mval[0]= event->x - ar->winrct.xmin;
610                 event->mval[1]= event->y - ar->winrct.ymin;
611         }
612 }
613
614 int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports, short poll_only)
615 {
616         wmWindowManager *wm= CTX_wm_manager(C);
617         int retval= OPERATOR_PASS_THROUGH;
618
619         /* this is done because complicated setup is done to call this function that is better not duplicated */
620         if(poll_only)
621                 return WM_operator_poll(C, ot);
622
623         if(WM_operator_poll(C, ot)) {
624                 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
625                 
626                 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
627                         printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname); 
628                 
629                 if(op->type->invoke && event) {
630                         wm_region_mouse_co(C, event);
631
632                         if(op->type->flag & OPTYPE_UNDO)
633                                 wm->op_undo_depth++;
634
635                         retval= op->type->invoke(C, op, event);
636
637                         if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
638                                 wm->op_undo_depth--;
639                 }
640                 else if(op->type->exec) {
641                         if(op->type->flag & OPTYPE_UNDO)
642                                 wm->op_undo_depth++;
643
644                         retval= op->type->exec(C, op);
645
646                         if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
647                                 wm->op_undo_depth--;
648                 }
649                 else
650                         printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
651                 
652                 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
653                  * currently python only uses this */
654                 if (!(retval & OPERATOR_HANDLED) && retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
655                         /* only show the report if the report list was not given in the function */
656                         wm_operator_reports(C, op, retval, (reports==NULL));
657                         
658                 
659                 if(retval & OPERATOR_HANDLED)
660                         ; /* do nothing, wm_operator_exec() has been called somewhere */
661                 else if(retval & OPERATOR_FINISHED) {
662                         wm_operator_finished(C, op, 0);
663                 }
664                 else if(retval & OPERATOR_RUNNING_MODAL) {
665                         /* grab cursor during blocking modal ops (X11)
666                          * Also check for macro
667                          * */
668                         if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
669                                 int bounds[4] = {-1,-1,-1,-1};
670                                 int wrap;
671
672                                 if (op->opm) {
673                                         wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
674                                 } else {
675                                         wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
676                                 }
677
678                                 if(wrap) {
679                                         ARegion *ar= CTX_wm_region(C);
680                                         if(ar) {
681                                                 bounds[0]= ar->winrct.xmin;
682                                                 bounds[1]= ar->winrct.ymax;
683                                                 bounds[2]= ar->winrct.xmax;
684                                                 bounds[3]= ar->winrct.ymin;
685                                         }
686                                 }
687
688                                 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
689                         }
690
691                         /* cancel UI handlers, typically tooltips that can hang around
692                            while dragging the view or worse, that stay there permanently
693                            after the modal operator has swallowed all events and passed
694                            none to the UI handler */
695                         wm_handler_ui_cancel(C);
696                 }
697                 else
698                         WM_operator_free(op);
699         }
700
701         return retval;
702 }
703
704 /* WM_operator_name_call is the main accessor function
705  * this is for python to access since its done the operator lookup
706  * 
707  * invokes operator in context */
708 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only)
709 {
710         wmWindow *window= CTX_wm_window(C);
711         wmEvent *event;
712         
713         int retval;
714
715         CTX_wm_operator_poll_msg_set(C, NULL);
716
717         /* dummie test */
718         if(ot && C) {
719                 switch(context) {
720                         case WM_OP_INVOKE_DEFAULT:
721                         case WM_OP_INVOKE_REGION_WIN:
722                         case WM_OP_INVOKE_AREA:
723                         case WM_OP_INVOKE_SCREEN:
724                                 /* window is needed for invoke, cancel operator */
725                                 if (window == NULL)
726                                         return 0;
727                                 else
728                                         event= window->eventstate;
729                                 break;
730                         default:
731                                 event = NULL;
732                 }
733
734                 switch(context) {
735                         
736                         case WM_OP_EXEC_REGION_WIN:
737                         case WM_OP_INVOKE_REGION_WIN: 
738                         case WM_OP_EXEC_REGION_CHANNELS:
739                         case WM_OP_INVOKE_REGION_CHANNELS:
740                         case WM_OP_EXEC_REGION_PREVIEW:
741                         case WM_OP_INVOKE_REGION_PREVIEW:
742                         {
743                                 /* forces operator to go to the region window/channels/preview, for header menus
744                                  * but we stay in the same region if we are already in one 
745                                  */
746                                 ARegion *ar= CTX_wm_region(C);
747                                 ScrArea *area= CTX_wm_area(C);
748                                 int type = RGN_TYPE_WINDOW;
749                                 
750                                 switch (context) {
751                                         case WM_OP_EXEC_REGION_CHANNELS:
752                                         case WM_OP_INVOKE_REGION_CHANNELS:
753                                                 type = RGN_TYPE_CHANNELS;
754                                         
755                                         case WM_OP_EXEC_REGION_PREVIEW:
756                                         case WM_OP_INVOKE_REGION_PREVIEW:
757                                                 type = RGN_TYPE_PREVIEW;
758                                                 break;
759                                         
760                                         case WM_OP_EXEC_REGION_WIN:
761                                         case WM_OP_INVOKE_REGION_WIN: 
762                                         default:
763                                                 type = RGN_TYPE_WINDOW;
764                                                 break;
765                                 }
766                                 
767                                 if(!(ar && ar->regiontype == type) && area) {
768                                         ARegion *ar1= BKE_area_find_region_type(area, type);
769                                         if(ar1)
770                                                 CTX_wm_region_set(C, ar1);
771                                 }
772                                 
773                                 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
774                                 
775                                 /* set region back */
776                                 CTX_wm_region_set(C, ar);
777                                 
778                                 return retval;
779                         }
780                         case WM_OP_EXEC_AREA:
781                         case WM_OP_INVOKE_AREA:
782                         {
783                                         /* remove region from context */
784                                 ARegion *ar= CTX_wm_region(C);
785
786                                 CTX_wm_region_set(C, NULL);
787                                 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
788                                 CTX_wm_region_set(C, ar);
789
790                                 return retval;
791                         }
792                         case WM_OP_EXEC_SCREEN:
793                         case WM_OP_INVOKE_SCREEN:
794                         {
795                                 /* remove region + area from context */
796                                 ARegion *ar= CTX_wm_region(C);
797                                 ScrArea *area= CTX_wm_area(C);
798
799                                 CTX_wm_region_set(C, NULL);
800                                 CTX_wm_area_set(C, NULL);
801                                 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
802                                 CTX_wm_region_set(C, ar);
803                                 CTX_wm_area_set(C, area);
804
805                                 return retval;
806                         }
807                         case WM_OP_EXEC_DEFAULT:
808                         case WM_OP_INVOKE_DEFAULT:
809                                 return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
810                 }
811         }
812         
813         return 0;
814 }
815
816
817 /* invokes operator in context */
818 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
819 {
820         wmOperatorType *ot= WM_operatortype_find(opstring, 0);
821         if(ot)
822                 return wm_operator_call_internal(C, ot, properties, NULL, context, FALSE);
823
824         return 0;
825 }
826
827 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
828    - wmOperatorType is used instead of operator name since python already has the operator type
829    - poll() must be called by python before this runs.
830    - reports can be passed to this function (so python can report them as exceptions)
831 */
832 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
833 {
834         wmWindowManager *wm=    CTX_wm_manager(C);
835         int retval= OPERATOR_CANCELLED;
836
837 #if 0
838         wmOperator *op;
839         op= wm_operator_create(wm, ot, properties, reports);
840
841         if (op->type->exec) {
842                 if(op->type->flag & OPTYPE_UNDO)
843                         wm->op_undo_depth++;
844
845                 retval= op->type->exec(C, op);
846
847                 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
848                         wm->op_undo_depth--;
849         }
850         else
851                 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
852 #endif
853
854         retval= wm_operator_call_internal(C, ot, properties, reports, context, FALSE);
855         
856         /* keep the reports around if needed later */
857         if (    (retval & OPERATOR_RUNNING_MODAL) ||
858                         ((retval & OPERATOR_FINISHED) && wm_operator_register_check(wm, ot))
859         ) {
860                 reports->flag |= RPT_FREE; /* let blender manage freeing */
861         }
862         
863         return retval;
864 }
865
866
867 /* ********************* handlers *************** */
868
869 /* future extra customadata free? */
870 void wm_event_free_handler(wmEventHandler *handler)
871 {
872         MEM_freeN(handler);
873 }
874
875 /* only set context when area/region is part of screen */
876 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
877 {
878         bScreen *screen= CTX_wm_screen(C);
879         
880         if(screen && handler->op) {
881                 if(handler->op_area==NULL)
882                         CTX_wm_area_set(C, NULL);
883                 else {
884                         ScrArea *sa;
885                         
886                         for(sa= screen->areabase.first; sa; sa= sa->next)
887                                 if(sa==handler->op_area)
888                                         break;
889                         if(sa==NULL) {
890                                 /* when changing screen layouts with running modal handlers (like render display), this
891                                    is not an error to print */
892                                 if(handler->op==NULL)
893                                         printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
894                         }
895                         else {
896                                 ARegion *ar;
897                                 CTX_wm_area_set(C, sa);
898                                 for(ar= sa->regionbase.first; ar; ar= ar->next)
899                                         if(ar==handler->op_region)
900                                                 break;
901                                 /* XXX no warning print here, after full-area and back regions are remade */
902                                 if(ar)
903                                         CTX_wm_region_set(C, ar);
904                         }
905                 }
906         }
907 }
908
909 /* called on exit or remove area, only here call cancel callback */
910 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
911 {
912         wmEventHandler *handler;
913         wmWindowManager *wm= CTX_wm_manager(C);
914         
915         /* C is zero on freeing database, modal handlers then already were freed */
916         while((handler=handlers->first)) {
917                 BLI_remlink(handlers, handler);
918                 
919                 if(handler->op) {
920                         if(handler->op->type->cancel) {
921                                 ScrArea *area= CTX_wm_area(C);
922                                 ARegion *region= CTX_wm_region(C);
923                                 
924                                 wm_handler_op_context(C, handler);
925
926                                 if(handler->op->type->flag & OPTYPE_UNDO)
927                                         wm->op_undo_depth++;
928
929                                 handler->op->type->cancel(C, handler->op);
930
931                                 if(handler->op->type->flag & OPTYPE_UNDO)
932                                         wm->op_undo_depth--;
933
934                                 CTX_wm_area_set(C, area);
935                                 CTX_wm_region_set(C, region);
936                         }
937
938                         WM_cursor_ungrab(CTX_wm_window(C));
939                         WM_operator_free(handler->op);
940                 }
941                 else if(handler->ui_remove) {
942                         ScrArea *area= CTX_wm_area(C);
943                         ARegion *region= CTX_wm_region(C);
944                         ARegion *menu= CTX_wm_menu(C);
945                         
946                         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
947                         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
948                         if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
949
950                         handler->ui_remove(C, handler->ui_userdata);
951
952                         CTX_wm_area_set(C, area);
953                         CTX_wm_region_set(C, region);
954                         CTX_wm_menu_set(C, menu);
955                 }
956
957                 wm_event_free_handler(handler);
958         }
959 }
960
961 /* do userdef mappings */
962 int WM_userdef_event_map(int kmitype)
963 {
964         switch(kmitype) {
965                 case SELECTMOUSE:
966                         if(U.flag & USER_LMOUSESELECT)
967                                 return LEFTMOUSE;
968                         else
969                                 return RIGHTMOUSE;
970                         
971                 case ACTIONMOUSE:
972                         if(U.flag & USER_LMOUSESELECT)
973                                 return RIGHTMOUSE;
974                         else
975                                 return LEFTMOUSE;
976                         
977                 case WHEELOUTMOUSE:
978                         if(U.uiflag & USER_WHEELZOOMDIR)
979                                 return WHEELUPMOUSE;
980                         else
981                                 return WHEELDOWNMOUSE;
982                         
983                 case WHEELINMOUSE:
984                         if(U.uiflag & USER_WHEELZOOMDIR)
985                                 return WHEELDOWNMOUSE;
986                         else
987                                 return WHEELUPMOUSE;
988                         
989                 case EVT_TWEAK_A:
990                         if(U.flag & USER_LMOUSESELECT)
991                                 return EVT_TWEAK_R;
992                         else
993                                 return EVT_TWEAK_L;
994                         
995                 case EVT_TWEAK_S:
996                         if(U.flag & USER_LMOUSESELECT)
997                                 return EVT_TWEAK_L;
998                         else
999                                 return EVT_TWEAK_R;
1000         }
1001         
1002         return kmitype;
1003 }
1004
1005 static void wm_eventemulation(wmEvent *event)
1006 {
1007         static int mmb_emulated = 0; /* this should be in a data structure somwhere */
1008         
1009         /* middlemouse emulation */
1010         if(U.flag & USER_TWOBUTTONMOUSE) {
1011                 if(event->type == LEFTMOUSE && (event->alt || mmb_emulated == KM_PRESS)) {
1012                         event->type = MIDDLEMOUSE;
1013                         event->alt = 0;
1014                         mmb_emulated = event->val;
1015                 }
1016         }
1017
1018 #ifdef __APPLE__
1019         /* rightmouse emulation */
1020         if(U.flag & USER_TWOBUTTONMOUSE) {
1021                 if(event->type == LEFTMOUSE && (event->oskey || mmb_emulated == KM_PRESS)) {
1022                         event->type = RIGHTMOUSE;
1023                         event->oskey = 0;
1024                         mmb_emulated = event->val;
1025                 }
1026         }
1027 #endif
1028
1029         /* numpad emulation */
1030         if(U.flag & USER_NONUMPAD) {
1031                 switch(event->type) {
1032                         case ZEROKEY: event->type = PAD0; break;
1033                         case ONEKEY: event->type = PAD1; break;
1034                         case TWOKEY: event->type = PAD2; break;
1035                         case THREEKEY: event->type = PAD3; break;
1036                         case FOURKEY: event->type = PAD4; break;
1037                         case FIVEKEY: event->type = PAD5; break;
1038                         case SIXKEY: event->type = PAD6; break;
1039                         case SEVENKEY: event->type = PAD7; break;
1040                         case EIGHTKEY: event->type = PAD8; break;
1041                         case NINEKEY: event->type = PAD9; break;
1042                         case MINUSKEY: event->type = PADMINUS; break;
1043                         case EQUALKEY: event->type = PADPLUSKEY; break;
1044                         case BACKSLASHKEY: event->type = PADSLASHKEY; break;
1045                 }
1046         }
1047 }
1048
1049 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
1050 {
1051         int kmitype= WM_userdef_event_map(kmi->type);
1052
1053         if(kmi->flag & KMI_INACTIVE) return 0;
1054
1055         /* the matching rules */
1056         if(kmitype==KM_TEXTINPUT)
1057                 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
1058         if(kmitype!=KM_ANY)
1059                 if(winevent->type!=kmitype) return 0;
1060         
1061         if(kmi->val!=KM_ANY)
1062                 if(winevent->val!=kmi->val) return 0;
1063         
1064         /* modifiers also check bits, so it allows modifier order */
1065         if(kmi->shift!=KM_ANY)
1066                 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
1067         if(kmi->ctrl!=KM_ANY)
1068                 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
1069         if(kmi->alt!=KM_ANY)
1070                 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
1071         if(kmi->oskey!=KM_ANY)
1072                 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
1073         
1074         if(kmi->keymodifier)
1075                 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1076                 
1077         /* key modifiers always check when event has it */
1078         /* otherwise regular keypresses with keymodifier still work */
1079         if(winevent->keymodifier)
1080                 if(ISTEXTINPUT(winevent->type)) 
1081                         if(winevent->keymodifier!=kmi->keymodifier) return 0;
1082         
1083         return 1;
1084 }
1085
1086 static int wm_event_always_pass(wmEvent *event)
1087 {
1088         /* some events we always pass on, to ensure proper communication */
1089         return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
1090 }
1091
1092 /* operator exists */
1093 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
1094 {
1095         /* support for modal keymap in macros */
1096         if (op->opm)
1097                 op = op->opm;
1098
1099         if(op->type->modalkeymap) {
1100                 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1101                 wmKeyMapItem *kmi;
1102
1103                 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1104                         if(wm_eventmatch(event, kmi)) {
1105                                         
1106                                 event->type= EVT_MODAL_MAP;
1107                                 event->val= kmi->propvalue;
1108                         }
1109                 }
1110         }
1111 }
1112
1113 /* Warning: this function removes a modal handler, when finished */
1114 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
1115 {
1116         int retval= OPERATOR_PASS_THROUGH;
1117         
1118         /* derived, modal or blocking operator */
1119         if(handler->op) {
1120                 wmOperator *op= handler->op;
1121                 wmOperatorType *ot= op->type;
1122
1123                 if(ot->modal) {
1124                         /* we set context to where modal handler came from */
1125                         wmWindowManager *wm= CTX_wm_manager(C);
1126                         ScrArea *area= CTX_wm_area(C);
1127                         ARegion *region= CTX_wm_region(C);
1128                         
1129                         wm_handler_op_context(C, handler);
1130                         wm_region_mouse_co(C, event);
1131                         wm_event_modalkeymap(C, op, event);
1132                         
1133                         if(ot->flag & OPTYPE_UNDO)
1134                                 wm->op_undo_depth++;
1135
1136                         retval= ot->modal(C, op, event);
1137
1138                         if(ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1139                                 wm->op_undo_depth--;
1140
1141                         /* putting back screen context, reval can pass trough after modal failures! */
1142                         if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1143                                 CTX_wm_area_set(C, area);
1144                                 CTX_wm_region_set(C, region);
1145                         }
1146                         else {
1147                                 /* this special cases is for areas and regions that get removed */
1148                                 CTX_wm_area_set(C, NULL);
1149                                 CTX_wm_region_set(C, NULL);
1150                         }               
1151
1152                         if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED))
1153                                 wm_operator_reports(C, op, retval, 0);
1154                         
1155                         if(retval & OPERATOR_FINISHED) {
1156                                 wm_operator_finished(C, op, 0);
1157                                 handler->op= NULL;
1158                         }
1159                         else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1160                                 WM_operator_free(op);
1161                                 handler->op= NULL;
1162                         }
1163                         
1164                         /* remove modal handler, operator itself should have been cancelled and freed */
1165                         if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1166                                 WM_cursor_ungrab(CTX_wm_window(C));
1167
1168                                 BLI_remlink(handlers, handler);
1169                                 wm_event_free_handler(handler);
1170                                 
1171                                 /* prevent silly errors from operator users */
1172                                 //retval &= ~OPERATOR_PASS_THROUGH;
1173                         }
1174                         
1175                 }
1176                 else
1177                         printf("wm_handler_operator_call error\n");
1178         }
1179         else {
1180                 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
1181
1182                 if(ot)
1183                         retval= wm_operator_invoke(C, ot, event, properties, NULL, FALSE);
1184         }
1185
1186         /* Finished and pass through flag as handled */
1187         if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1188                 return WM_HANDLER_HANDLED;
1189
1190         /* Modal unhandled, break */
1191         if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1192                 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1193
1194         if(retval & OPERATOR_PASS_THROUGH)
1195                 return WM_HANDLER_CONTINUE;
1196
1197         return WM_HANDLER_BREAK;
1198 }
1199
1200 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1201 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1202 {
1203         wmWindowManager *wm= CTX_wm_manager(C);
1204         SpaceFile *sfile;
1205         int action= WM_HANDLER_CONTINUE;
1206         
1207         if(event->type != EVT_FILESELECT)
1208                 return action;
1209         if(handler->op != (wmOperator *)event->customdata)
1210                 return action;
1211         
1212         switch(event->val) {
1213                 case EVT_FILESELECT_OPEN: 
1214                 case EVT_FILESELECT_FULL_OPEN: 
1215                         {       
1216                                 ScrArea *sa;
1217                                 
1218                                 /* sa can be null when window A is active, but mouse is over window B */
1219                                 /* in this case, open file select in original window A */
1220                                 if (handler->op_area == NULL) {
1221                                         bScreen *screen = CTX_wm_screen(C);
1222                                         sa = (ScrArea *)screen->areabase.first;
1223                                 } else
1224                                         sa = handler->op_area;
1225                                         
1226                                 if(event->val==EVT_FILESELECT_OPEN)
1227                                         ED_area_newspace(C, sa, SPACE_FILE);
1228                                 else
1229                                         ED_screen_full_newspace(C, sa, SPACE_FILE);     /* sets context */
1230                                 
1231                                 /* settings for filebrowser, sfile is not operator owner but sends events */
1232                                 sa = CTX_wm_area(C);
1233                                 sfile= (SpaceFile*)sa->spacedata.first;
1234                                 sfile->op= handler->op;
1235
1236                                 ED_fileselect_set_params(sfile);
1237                                 
1238                                 action= WM_HANDLER_BREAK;
1239                         }
1240                         break;
1241                         
1242                 case EVT_FILESELECT_EXEC:
1243                 case EVT_FILESELECT_CANCEL:
1244                 case EVT_FILESELECT_EXTERNAL_CANCEL:
1245                         {
1246                                 /* XXX validate area and region? */
1247                                 bScreen *screen= CTX_wm_screen(C);
1248
1249                                 /* remlink now, for load file case before removing*/
1250                                 BLI_remlink(handlers, handler);
1251                                 
1252                                 if(event->val!=EVT_FILESELECT_EXTERNAL_CANCEL) {
1253                                         if(screen != handler->filescreen) {
1254                                                 ED_screen_full_prevspace(C, CTX_wm_area(C));
1255                                         }
1256                                         else {
1257                                                 ED_area_prevspace(C, CTX_wm_area(C));
1258                                         }
1259                                 }
1260                                 
1261                                 wm_handler_op_context(C, handler);
1262
1263                                 /* needed for uiPupMenuReports */
1264
1265                                 if(event->val==EVT_FILESELECT_EXEC) {
1266 #if 0                           // use REDALERT now
1267
1268                                         /* a bit weak, might become arg for WM_event_fileselect? */
1269                                         /* XXX also extension code in image-save doesnt work for this yet */
1270                                         if (RNA_struct_find_property(handler->op->ptr, "check_existing") && 
1271                                                         RNA_boolean_get(handler->op->ptr, "check_existing")) {
1272                                                 char *path= RNA_string_get_alloc(handler->op->ptr, "filepath", NULL, 0);
1273                                                 /* this gives ownership to pupmenu */
1274                                                 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1275                                                 if(path)
1276                                                         MEM_freeN(path);
1277                                         }
1278                                         else
1279 #endif
1280                                         {
1281                                                 int retval;
1282                                                 
1283                                                 if(handler->op->type->flag & OPTYPE_UNDO)
1284                                                         wm->op_undo_depth++;
1285
1286                                                 retval= handler->op->type->exec(C, handler->op);
1287
1288                                                 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1289                                                 if(handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1290                                                         wm->op_undo_depth--;
1291                                                 
1292                                                 if (retval & OPERATOR_FINISHED)
1293                                                         if(G.f & G_DEBUG)
1294                                                                 wm_operator_print(handler->op);
1295                                                 
1296                                                 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1297                                                 if(CTX_wm_manager(C) == wm && wm->op_undo_depth == 0)
1298                                                         if(handler->op->type->flag & OPTYPE_UNDO)
1299                                                                 ED_undo_push_op(C, handler->op);
1300
1301                                                 if(handler->op->reports->list.first) {
1302
1303                                                         /* FIXME, temp setting window, this is really bad!
1304                                                          * only have because lib linking errors need to be seen by users :(
1305                                                          * it can be removed without breaking anything but then no linking errors - campbell */
1306                                                         wmWindow *win_prev= CTX_wm_window(C);
1307                                                         if(win_prev==NULL)
1308                                                                 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1309
1310                                                         handler->op->reports->printlevel = RPT_WARNING;
1311                                                         uiPupMenuReports(C, handler->op->reports);
1312
1313                                                         /* XXX - copied from 'wm_operator_finished()' */
1314                                                         /* add reports to the global list, otherwise they are not seen */
1315                                                         addlisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
1316
1317                                                         CTX_wm_window_set(C, win_prev);
1318                                                 }
1319
1320                                                 WM_operator_free(handler->op);
1321                                         }
1322                                 }
1323                                 else {
1324                                         if(handler->op->type->cancel) {
1325                                                 if(handler->op->type->flag & OPTYPE_UNDO)
1326                                                         wm->op_undo_depth++;
1327
1328                                                 handler->op->type->cancel(C, handler->op);
1329
1330                                                 if(handler->op->type->flag & OPTYPE_UNDO)
1331                                                         wm->op_undo_depth--;
1332                                         }
1333
1334                                         WM_operator_free(handler->op);
1335                                 }
1336
1337                                 CTX_wm_area_set(C, NULL);
1338                                 
1339                                 wm_event_free_handler(handler);
1340                                 
1341                                 action= WM_HANDLER_BREAK;
1342                         }
1343                         break;
1344         }
1345         
1346         return action;
1347 }
1348
1349 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1350 {
1351         if(handler->bbwin) {
1352                 if(handler->bblocal) {
1353                         rcti rect= *handler->bblocal;
1354                         BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1355
1356                         if(BLI_in_rcti(&rect, event->x, event->y))
1357                                 return 1;
1358                         else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1359                                 return 1;
1360                         else
1361                                 return 0;
1362                 }
1363                 else {
1364                         if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1365                                 return 1;
1366                         else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1367                                 return 1;
1368                         else
1369                                 return 0;
1370                 }
1371         }
1372         return 1;
1373 }
1374
1375 static int wm_action_not_handled(int action)
1376 {
1377         return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1378 }
1379
1380 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1381 {
1382         wmWindowManager *wm= CTX_wm_manager(C);
1383         wmEventHandler *handler, *nexthandler;
1384         int action= WM_HANDLER_CONTINUE;
1385         int always_pass;
1386
1387         if(handlers==NULL) return action;
1388
1389         /* modal handlers can get removed in this loop, we keep the loop this way */
1390         for(handler= handlers->first; handler; handler= nexthandler) {
1391                 nexthandler= handler->next;
1392
1393                 /* optional boundbox */
1394                 if(handler_boundbox_test(handler, event)) {
1395                         /* in advance to avoid access to freed event on window close */
1396                         always_pass= wm_event_always_pass(event);
1397                 
1398                         /* modal+blocking handler */
1399                         if(handler->flag & WM_HANDLER_BLOCKING)
1400                                 action |= WM_HANDLER_BREAK;
1401
1402                         if(handler->keymap) {
1403                                 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1404                                 wmKeyMapItem *kmi;
1405                                 
1406                                 if(!keymap->poll || keymap->poll(C)) {
1407                                         for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1408                                                 if(wm_eventmatch(event, kmi)) {
1409                                                         
1410                                                         event->keymap_idname= kmi->idname;      /* weak, but allows interactive callback to not use rawkey */
1411                                                         
1412                                                         action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1413                                                         if(action & WM_HANDLER_BREAK)  /* not always_pass here, it denotes removed handler */
1414                                                                 break;
1415                                                 }
1416                                         }
1417                                 }
1418                         }
1419                         else if(handler->ui_handle) {
1420                                 action |= wm_handler_ui_call(C, handler, event, always_pass);
1421                         }
1422                         else if(handler->type==WM_HANDLER_FILESELECT) {
1423                                 /* screen context changes here */
1424                                 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1425                         }
1426                         else if(handler->dropboxes) {
1427                                 if(event->type==EVT_DROP) {
1428                                         wmDropBox *drop= handler->dropboxes->first;
1429                                         for(; drop; drop= drop->next) {
1430                                                 /* other drop custom types allowed */
1431                                                 if(event->custom==EVT_DATA_LISTBASE) {
1432                                                         ListBase *lb= (ListBase *)event->customdata;
1433                                                         wmDrag *drag;
1434                                                         for(drag= lb->first; drag; drag= drag->next) {
1435                                                                 if(drop->poll(C, drag, event)) {
1436                                                                         drop->copy(drag, drop);
1437                                                                         
1438                                                                         wm_operator_invoke(C, drop->ot, event, drop->ptr, NULL, FALSE);
1439                                                                         action |= WM_HANDLER_BREAK;
1440                                                                 }
1441                                                         }
1442                                                 }
1443                                         }
1444                                 }
1445                         }
1446                         else {
1447                                 /* modal, swallows all */
1448                                 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1449                         }
1450
1451                         if(action & WM_HANDLER_BREAK) {
1452                                 if(always_pass)
1453                                         action &= ~WM_HANDLER_BREAK;
1454                                 else
1455                                         break;
1456                         }
1457                 }
1458                 
1459                 /* fileread case */
1460                 if(CTX_wm_window(C)==NULL)
1461                         return action;
1462         }
1463
1464         /* test for CLICK event */
1465         if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1466                 wmWindow *win = CTX_wm_window(C);
1467
1468                 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1469                         /* test for double click first,
1470                          * note1: this can be problematic because single click operators can get the
1471                          *   double click event but then with old mouse coords which is highly confusing,
1472                          *   so check for mouse moves too.
1473                          * note2: the first click event will be handled but still used to create a
1474                          *   double click event if clicking again quickly.
1475                          *   If no double click events are found itwill fallback to a single click.
1476                          *   So a double click event can result in 2 successive single click calls
1477                          *   if its not handled by the keymap - campbell */
1478                         if (    (ABS(event->x - win->eventstate->prevclickx)) <= 2 &&
1479                                         (ABS(event->y - win->eventstate->prevclicky)) <= 2 &&
1480                                         ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time)
1481                         ) {
1482                                 event->val = KM_DBL_CLICK;
1483                                 /* removed this because in cases where we're this is used as a single click
1484                                  * event, this will give old coords, since the distance is checked above, using new coords should be ok. */
1485                                 //   event->x = win->eventstate->prevclickx;
1486                                 //   event->y = win->eventstate->prevclicky;
1487                                 action |= wm_handlers_do(C, event, handlers);
1488                         }
1489
1490                         if (wm_action_not_handled(action)) {
1491                                 event->val = KM_CLICK;
1492                                 action |= wm_handlers_do(C, event, handlers);
1493                         }
1494
1495
1496                         /* revert value if not handled */
1497                         if (wm_action_not_handled(action)) {
1498                                 event->val = KM_RELEASE;
1499                         }
1500                 }
1501         }
1502
1503         return action;
1504 }
1505
1506 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1507 {
1508         if(wm_event_always_pass(event))
1509                 return 1;
1510         if(BLI_in_rcti(rect, event->x, event->y))
1511            return 1;
1512         if(event->type==MOUSEMOVE) {
1513                 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1514                         return 1;
1515                 }
1516                 return 0;
1517         }
1518         return 0;
1519 }
1520
1521 static ScrArea *area_event_inside(bContext *C, int x, int y)
1522 {
1523         bScreen *screen= CTX_wm_screen(C);
1524         ScrArea *sa;
1525         
1526         if(screen)
1527                 for(sa= screen->areabase.first; sa; sa= sa->next)
1528                         if(BLI_in_rcti(&sa->totrct, x, y))
1529                                 return sa;
1530         return NULL;
1531 }
1532
1533 static ARegion *region_event_inside(bContext *C, int x, int y)
1534 {
1535         bScreen *screen= CTX_wm_screen(C);
1536         ScrArea *area= CTX_wm_area(C);
1537         ARegion *ar;
1538         
1539         if(screen && area)
1540                 for(ar= area->regionbase.first; ar; ar= ar->next)
1541                         if(BLI_in_rcti(&ar->winrct, x, y))
1542                                 return ar;
1543         return NULL;
1544 }
1545
1546 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1547 {
1548         if(ar) {
1549                 for(; pc; pc= pc->next) {
1550                         if(pc->poll == NULL || pc->poll(C)) {
1551                                 wmWindow *win= CTX_wm_window(C);
1552                                 win->screen->do_draw_paintcursor= 1;
1553                                 wm_tag_redraw_overlay(win, ar);
1554                         }
1555                 }
1556         }
1557 }
1558
1559 /* called on mousemove, check updates for paintcursors */
1560 /* context was set on active area and region */
1561 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1562 {
1563         wmWindowManager *wm= CTX_wm_manager(C);
1564         
1565         if(wm->paintcursors.first) {
1566                 ARegion *ar= CTX_wm_region(C);
1567                 if(ar)
1568                         wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1569                 
1570                 /* if previous position was not in current region, we have to set a temp new context */
1571                 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1572                         ScrArea *sa= CTX_wm_area(C);
1573                         
1574                         CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1575                         CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1576
1577                         wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1578                         
1579                         CTX_wm_area_set(C, sa);
1580                         CTX_wm_region_set(C, ar);
1581                 }
1582         }
1583 }
1584
1585 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1586 {
1587         if(wm->drags.first==NULL) return;
1588         
1589         if(event->type==MOUSEMOVE)
1590                 win->screen->do_draw_drag= 1;
1591         else if(event->type==ESCKEY) {
1592                 BLI_freelistN(&wm->drags);
1593                 win->screen->do_draw_drag= 1;
1594         }
1595         else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1596                 event->type= EVT_DROP;
1597                 
1598                 /* create customdata, first free existing */
1599                 if(event->customdata) {
1600                         if(event->customdatafree)
1601                                 MEM_freeN(event->customdata);
1602                 }
1603                 
1604                 event->custom= EVT_DATA_LISTBASE;
1605                 event->customdata= &wm->drags;
1606                 event->customdatafree= 1;
1607                 
1608                 /* clear drop icon */
1609                 win->screen->do_draw_drag= 1;
1610                 
1611                 /* restore cursor (disabled, see wm_dragdrop.c) */
1612                 // WM_cursor_restore(win);
1613         }
1614         
1615         /* overlap fails otherwise */
1616         if(win->screen->do_draw_drag)
1617                 if(win->drawmethod == USER_DRAW_OVERLAP)
1618                         win->screen->do_draw= 1;
1619         
1620 }
1621
1622 /* called in main loop */
1623 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
1624 void wm_event_do_handlers(bContext *C)
1625 {
1626         wmWindowManager *wm= CTX_wm_manager(C);
1627         wmWindow *win;
1628
1629         for(win= wm->windows.first; win; win= win->next) {
1630                 wmEvent *event;
1631                 
1632                 if( win->screen==NULL )
1633                         wm_event_free_all(win);
1634                 else {
1635                         Scene* scene = win->screen->scene;
1636                         
1637                         if(scene) {
1638                                 int playing = sound_scene_playing(win->screen->scene);
1639                                 
1640                                 if(playing != -1) {
1641                                         CTX_wm_window_set(C, win);
1642                                         CTX_wm_screen_set(C, win->screen);
1643                                         CTX_data_scene_set(C, scene);
1644                                         
1645                                         if(((playing == 1) && (!win->screen->animtimer)) || ((playing == 0) && (win->screen->animtimer))){
1646                                                 ED_screen_animation_play(C, -1, 1);
1647                                         }
1648                                         
1649                                         if(playing == 0) {
1650                                                 int ncfra = sound_sync_scene(scene) * FPS + 0.5;
1651                                                 if(ncfra != scene->r.cfra)      {
1652                                                         scene->r.cfra = ncfra;
1653                                                         ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
1654                                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
1655                                                 }
1656                                         }
1657                                         
1658                                         CTX_data_scene_set(C, NULL);
1659                                         CTX_wm_screen_set(C, NULL);
1660                                         CTX_wm_window_set(C, NULL);
1661                                 }
1662                         }
1663                 }
1664                 
1665                 while( (event= win->queue.first) ) {
1666                         int action = WM_HANDLER_CONTINUE;
1667
1668                         if((G.f & G_DEBUG) && event && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1669                                 printf("pass on evt %d val %d\n", event->type, event->val); 
1670                         
1671                         wm_eventemulation(event);
1672
1673                         CTX_wm_window_set(C, win);
1674                         
1675                         /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1676                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1677                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1678                         
1679                         /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1680                         wm_window_make_drawable(C, win);
1681                         
1682                         /* first we do priority handlers, modal + some limited keymaps */
1683                         action |= wm_handlers_do(C, event, &win->modalhandlers);
1684                         
1685                         /* fileread case */
1686                         if(CTX_wm_window(C)==NULL)
1687                                 return;
1688                         
1689                         /* check dragging, creates new event or frees, adds draw tag */
1690                         wm_event_drag_test(wm, win, event);
1691                         
1692                         /* builtin tweak, if action is break it removes tweak */
1693                         wm_tweakevent_test(C, event, action);
1694
1695                         if((action & WM_HANDLER_BREAK) == 0) {
1696                                 ScrArea *sa;
1697                                 ARegion *ar;
1698                                 int doit= 0;
1699                                 
1700                                 /* XXX to solve, here screen handlers? */
1701                                 if(event->type==MOUSEMOVE) {
1702                                         /* state variables in screen, cursors */
1703                                         ED_screen_set_subwinactive(win, event); 
1704                                         /* for regions having custom cursors */
1705                                         wm_paintcursor_test(C, event);
1706                                 }
1707
1708                                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1709                                         if(wm_event_inside_i(event, &sa->totrct)) {
1710                                                 CTX_wm_area_set(C, sa);
1711
1712                                                 if((action & WM_HANDLER_BREAK) == 0) {
1713                                                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
1714                                                                 if(wm_event_inside_i(event, &ar->winrct)) {
1715                                                                         CTX_wm_region_set(C, ar);
1716                                                                         
1717                                                                         /* does polls for drop regions and checks uibuts */
1718                                                                         /* need to be here to make sure region context is true */
1719                                                                         if(ELEM(event->type, MOUSEMOVE, EVT_DROP)) {
1720                                                                                 wm_region_mouse_co(C, event);
1721                                                                                 wm_drags_check_ops(C, event);
1722                                                                         }
1723                                                                         
1724                                                                         action |= wm_handlers_do(C, event, &ar->handlers);
1725                                                                         
1726                                                                         doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1727                                                                         
1728                                                                         if(action & WM_HANDLER_BREAK)
1729                                                                                 break;
1730                                                                 }
1731                                                         }
1732                                                 }
1733
1734                                                 CTX_wm_region_set(C, NULL);
1735
1736                                                 if((action & WM_HANDLER_BREAK) == 0)
1737                                                         action |= wm_handlers_do(C, event, &sa->handlers);
1738
1739                                                 CTX_wm_area_set(C, NULL);
1740
1741                                                 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1742                                         }
1743                                 }
1744                                 
1745                                 if((action & WM_HANDLER_BREAK) == 0) {
1746                                         /* also some non-modal handlers need active area/region */
1747                                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1748                                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1749
1750                                         action |= wm_handlers_do(C, event, &win->handlers);
1751
1752                                         /* fileread case */
1753                                         if(CTX_wm_window(C)==NULL)
1754                                                 return;
1755                                 }
1756
1757                                 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad? 
1758                                    doing it on ghost queue gives errors when mousemoves go over area borders */
1759                                 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1760                                         win->eventstate->prevx= event->x;
1761                                         win->eventstate->prevy= event->y;
1762                                 }
1763                         }
1764                         
1765                         /* store last event for this window */
1766                         /* mousemove and timer events don't overwrite last type */
1767                         if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1768                                 if (wm_action_not_handled(action)) {
1769                                         if (win->eventstate->prevtype == event->type) {
1770                                                 /* set click time on first click (press -> release) */
1771                                                 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1772                                                         win->eventstate->prevclicktime = PIL_check_seconds_timer();
1773                                                         win->eventstate->prevclickx = event->x;
1774                                                         win->eventstate->prevclicky = event->y;
1775                                                 }
1776                                         } else {
1777                                                 /* reset click time if event type not the same */
1778                                                 win->eventstate->prevclicktime = 0;
1779                                         }
1780
1781                                         win->eventstate->prevval = event->val;
1782                                         win->eventstate->prevtype = event->type;
1783                                 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1784                                         win->eventstate->prevtype = event->type;
1785                                         win->eventstate->prevval = event->val;
1786                                         win->eventstate->prevclicktime = PIL_check_seconds_timer();
1787                                         win->eventstate->prevclickx = event->x;
1788                                         win->eventstate->prevclicky = event->y;
1789                                 } else { /* reset if not */
1790                                         win->eventstate->prevtype = -1;
1791                                         win->eventstate->prevval = 0;
1792                                         win->eventstate->prevclicktime = 0;
1793                                 }
1794                         }
1795
1796                         /* unlink and free here, blender-quit then frees all */
1797                         BLI_remlink(&win->queue, event);
1798                         wm_event_free(event);
1799                         
1800                 }
1801                 
1802                 /* only add mousemove when queue was read entirely */
1803                 if(win->addmousemove && win->eventstate) {
1804                         wmEvent event= *(win->eventstate);
1805                         event.type= MOUSEMOVE;
1806                         event.prevx= event.x;
1807                         event.prevy= event.y;
1808                         wm_event_add(win, &event);
1809                         win->addmousemove= 0;
1810                 }
1811                 
1812                 CTX_wm_window_set(C, NULL);
1813         }
1814 }
1815
1816 /* ********** filesector handling ************ */
1817
1818 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1819 {
1820         /* add to all windows! */
1821         wmWindow *win;
1822         
1823         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1824                 wmEvent event= *win->eventstate;
1825                 
1826                 event.type= EVT_FILESELECT;
1827                 event.val= eventval;
1828                 event.customdata= ophandle;             // only as void pointer type check
1829
1830                 wm_event_add(win, &event);
1831         }
1832 }
1833
1834 /* operator is supposed to have a filled "path" property */
1835 /* optional property: filetype (XXX enum?) */
1836
1837 /* Idea is to keep a handler alive on window queue, owning the operator.
1838    The filewindow can send event to make it execute, thus ensuring
1839    executing happens outside of lower level queues, with UI refreshed. 
1840    Should also allow multiwin solutions */
1841
1842 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1843 {
1844         wmEventHandler *handler;
1845         wmWindow *win= CTX_wm_window(C);
1846         int full= 1;    // XXX preset?
1847
1848         /* only allow file selector open per window bug [#23553] */
1849         for(handler= win->modalhandlers.first; handler; handler=handler->next) {
1850                 if(handler->type == WM_HANDLER_FILESELECT)
1851                         return;
1852         }
1853         
1854         handler = MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1855         
1856         handler->type= WM_HANDLER_FILESELECT;
1857         handler->op= op;
1858         handler->op_area= CTX_wm_area(C);
1859         handler->op_region= CTX_wm_region(C);
1860         handler->filescreen= CTX_wm_screen(C);
1861         
1862         BLI_addhead(&win->modalhandlers, handler);
1863         
1864         /* check props once before invoking if check is available
1865          * ensures initial properties are valid */
1866         if(op->type->check) {
1867                 op->type->check(C, op); /* ignore return value */
1868         }
1869
1870         WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1871 }
1872
1873 /* lets not expose struct outside wm? */
1874 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1875 {
1876         handler->flag= flag;
1877 }
1878
1879 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1880 {
1881         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1882         wmWindow *win= CTX_wm_window(C);
1883         
1884         /* operator was part of macro */
1885         if(op->opm) {
1886                 /* give the mother macro to the handler */
1887                 handler->op= op->opm;
1888                 /* mother macro opm becomes the macro element */
1889                 handler->op->opm= op;
1890         }
1891         else
1892                 handler->op= op;
1893         
1894         handler->op_area= CTX_wm_area(C);               /* means frozen screen context for modal handlers! */
1895         handler->op_region= CTX_wm_region(C);
1896         
1897         BLI_addhead(&win->modalhandlers, handler);
1898
1899         return handler;
1900 }
1901
1902 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1903 {
1904         wmEventHandler *handler;
1905
1906         if(!keymap) {
1907                 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1908                 return NULL;
1909         }
1910
1911         /* only allow same keymap once */
1912         for(handler= handlers->first; handler; handler= handler->next)
1913                 if(handler->keymap==keymap)
1914                         return handler;
1915         
1916         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1917         BLI_addtail(handlers, handler);
1918         handler->keymap= keymap;
1919
1920         return handler;
1921 }
1922
1923 /* priorities not implemented yet, for time being just insert in begin of list */
1924 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int UNUSED(priority))
1925 {
1926         wmEventHandler *handler;
1927         
1928         WM_event_remove_keymap_handler(handlers, keymap);
1929         
1930         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1931         BLI_addhead(handlers, handler);
1932         handler->keymap= keymap;
1933         
1934         return handler;
1935 }
1936
1937 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1938 {
1939         wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1940         
1941         if(handler) {
1942                 handler->bblocal= bblocal;
1943                 handler->bbwin= bbwin;
1944         }
1945         return handler;
1946 }
1947
1948 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1949 {
1950         wmEventHandler *handler;
1951         
1952         for(handler= handlers->first; handler; handler= handler->next) {
1953                 if(handler->keymap==keymap) {
1954                         BLI_remlink(handlers, handler);
1955                         wm_event_free_handler(handler);
1956                         break;
1957                 }
1958         }
1959 }
1960
1961 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1962 {
1963         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1964         handler->ui_handle= func;
1965         handler->ui_remove= remove;
1966         handler->ui_userdata= userdata;
1967         handler->ui_area= (C)? CTX_wm_area(C): NULL;
1968         handler->ui_region= (C)? CTX_wm_region(C): NULL;
1969         handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1970         
1971         BLI_addhead(handlers, handler);
1972         
1973         return handler;
1974 }
1975
1976 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1977 {
1978         wmEventHandler *handler;
1979         
1980         for(handler= handlers->first; handler; handler= handler->next) {
1981                 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1982                         BLI_remlink(handlers, handler);
1983                         wm_event_free_handler(handler);
1984                         break;
1985                 }
1986         }
1987 }
1988
1989 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
1990 {
1991         wmEventHandler *handler;
1992
1993         /* only allow same dropbox once */
1994         for(handler= handlers->first; handler; handler= handler->next)
1995                 if(handler->dropboxes==dropboxes)
1996                         return handler;
1997         
1998         handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
1999         
2000         /* dropbox stored static, no free or copy */
2001         handler->dropboxes= dropboxes;
2002         BLI_addhead(handlers, handler);
2003         
2004         return handler;
2005 }
2006
2007 /* XXX solution works, still better check the real cause (ton) */
2008 void WM_event_remove_area_handler(ListBase *handlers, void *area)
2009 {
2010         wmEventHandler *handler, *nexthandler;
2011
2012         for(handler = handlers->first; handler; handler= nexthandler) {
2013                 nexthandler = handler->next;
2014                 if (handler->type != WM_HANDLER_FILESELECT) {
2015                         if (handler->ui_area == area) {
2016                                 BLI_remlink(handlers, handler);
2017                                 wm_event_free_handler(handler);
2018                         }
2019                 }
2020         }
2021 }
2022
2023 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
2024 {
2025         BLI_remlink(handlers, handler);
2026         wm_event_free_handler(handler);
2027 }
2028
2029 void WM_event_add_mousemove(bContext *C)
2030 {
2031         wmWindow *window= CTX_wm_window(C);
2032         
2033         window->addmousemove= 1;
2034 }
2035
2036 /* for modal callbacks, check configuration for how to interpret exit with tweaks  */
2037 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
2038 {
2039         /* user preset or keymap? dunno... */
2040         // XXX WTH is this?
2041         int tweak_modal= (U.flag & USER_RELEASECONFIRM)==0;
2042         
2043         switch(tweak_event) {
2044                 case EVT_TWEAK_L:
2045                 case EVT_TWEAK_M:
2046                 case EVT_TWEAK_R:
2047                         if(evt->val==tweak_modal)
2048                                 return 1;
2049                 default:
2050                         /* this case is when modal callcback didnt get started with a tweak */
2051                         if(evt->val)
2052                                 return 1;
2053         }
2054         return 0;
2055 }
2056
2057 /* ********************* ghost stuff *************** */
2058
2059 static int convert_key(GHOST_TKey key) 
2060 {
2061         if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
2062                 return (AKEY + ((int) key - GHOST_kKeyA));
2063         } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
2064                 return (ZEROKEY + ((int) key - GHOST_kKey0));
2065         } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
2066                 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
2067         } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF19) {
2068                 return (F1KEY + ((int) key - GHOST_kKeyF1));
2069         } else {
2070                 switch (key) {
2071                         case GHOST_kKeyBackSpace:               return BACKSPACEKEY;
2072                         case GHOST_kKeyTab:                             return TABKEY;
2073                         case GHOST_kKeyLinefeed:                return LINEFEEDKEY;
2074                         case GHOST_kKeyClear:                   return 0;
2075                         case GHOST_kKeyEnter:                   return RETKEY;
2076                                 
2077                         case GHOST_kKeyEsc:                             return ESCKEY;
2078                         case GHOST_kKeySpace:                   return SPACEKEY;
2079                         case GHOST_kKeyQuote:                   return QUOTEKEY;
2080                         case GHOST_kKeyComma:                   return COMMAKEY;
2081                         case GHOST_kKeyMinus:                   return MINUSKEY;
2082                         case GHOST_kKeyPeriod:                  return PERIODKEY;
2083                         case GHOST_kKeySlash:                   return SLASHKEY;
2084                                 
2085                         case GHOST_kKeySemicolon:               return SEMICOLONKEY;
2086                         case GHOST_kKeyEqual:                   return EQUALKEY;
2087                                 
2088                         case GHOST_kKeyLeftBracket:             return LEFTBRACKETKEY;
2089                         case GHOST_kKeyRightBracket:    return RIGHTBRACKETKEY;
2090                         case GHOST_kKeyBackslash:               return BACKSLASHKEY;
2091                         case GHOST_kKeyAccentGrave:             return ACCENTGRAVEKEY;
2092                                 
2093                         case GHOST_kKeyLeftShift:               return LEFTSHIFTKEY;
2094                         case GHOST_kKeyRightShift:              return RIGHTSHIFTKEY;
2095                         case GHOST_kKeyLeftControl:             return LEFTCTRLKEY;
2096                         case GHOST_kKeyRightControl:    return RIGHTCTRLKEY;
2097                         case GHOST_kKeyOS:                              return OSKEY;
2098                         case GHOST_kKeyLeftAlt:                 return LEFTALTKEY;
2099                         case GHOST_kKeyRightAlt:                return RIGHTALTKEY;
2100                                 
2101                         case GHOST_kKeyCapsLock:                return CAPSLOCKKEY;
2102                         case GHOST_kKeyNumLock:                 return 0;
2103                         case GHOST_kKeyScrollLock:              return 0;
2104                                 
2105                         case GHOST_kKeyLeftArrow:               return LEFTARROWKEY;
2106                         case GHOST_kKeyRightArrow:              return RIGHTARROWKEY;
2107                         case GHOST_kKeyUpArrow:                 return UPARROWKEY;
2108                         case GHOST_kKeyDownArrow:               return DOWNARROWKEY;
2109                                 
2110                         case GHOST_kKeyPrintScreen:             return 0;
2111                         case GHOST_kKeyPause:                   return PAUSEKEY;
2112                                 
2113                         case GHOST_kKeyInsert:                  return INSERTKEY;
2114                         case GHOST_kKeyDelete:                  return DELKEY;
2115                         case GHOST_kKeyHome:                    return HOMEKEY;
2116                         case GHOST_kKeyEnd:                             return ENDKEY;
2117                         case GHOST_kKeyUpPage:                  return PAGEUPKEY;
2118                         case GHOST_kKeyDownPage:                return PAGEDOWNKEY;
2119                                 
2120                         case GHOST_kKeyNumpadPeriod:    return PADPERIOD;
2121                         case GHOST_kKeyNumpadEnter:             return PADENTER;
2122                         case GHOST_kKeyNumpadPlus:              return PADPLUSKEY;
2123                         case GHOST_kKeyNumpadMinus:             return PADMINUS;
2124                         case GHOST_kKeyNumpadAsterisk:  return PADASTERKEY;
2125                         case GHOST_kKeyNumpadSlash:             return PADSLASHKEY;
2126                                 
2127                         case GHOST_kKeyGrLess:              return GRLESSKEY; 
2128                                 
2129                         default:
2130                                 return UNKNOWNKEY;      /* GHOST_kKeyUnknown */
2131                 }
2132         }
2133 }
2134
2135 /* adds customdata to event */
2136 static void update_tablet_data(wmWindow *win, wmEvent *event)
2137 {
2138         const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
2139         
2140         /* if there's tablet data from an active tablet device then add it */
2141         if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2142                 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
2143                 
2144                 wmtab->Active = (int)td->Active;
2145                 wmtab->Pressure = td->Pressure;
2146                 wmtab->Xtilt = td->Xtilt;
2147                 wmtab->Ytilt = td->Ytilt;
2148                 
2149                 event->custom= EVT_DATA_TABLET;
2150                 event->customdata= wmtab;
2151                 event->customdatafree= 1;
2152         } 
2153 }
2154
2155 /* imperfect but probably usable... draw/enable drags to other windows */
2156 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2157 {
2158         short mx= evt->x, my= evt->y;
2159         
2160         if(wm->windows.first== wm->windows.last)
2161                 return NULL;
2162         
2163         /* top window bar... */
2164         if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) { 
2165                 wmWindow *owin;
2166                 wmEventHandler *handler;
2167                 
2168                 /* let's skip windows having modal handlers now */
2169                 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2170                 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2171                         if(handler->ui_handle || handler->op)
2172                                 return NULL;
2173                 
2174                 /* to desktop space */
2175                 mx+= win->posx;
2176                 my+= win->posy;
2177                 
2178                 /* check other windows to see if it has mouse inside */
2179                 for(owin= wm->windows.first; owin; owin= owin->next) {
2180                         
2181                         if(owin!=win) {
2182                                 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2183                                    mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2184                                         evt->x= mx-owin->posx;
2185                                         evt->y= my-owin->posy;
2186                                         
2187                                         return owin;
2188                                 }
2189                         }
2190                 }
2191         }
2192         return NULL;
2193 }
2194
2195 /* windows store own event queues, no bContext here */
2196 /* time is in 1000s of seconds, from ghost */
2197 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int UNUSED(time), void *customdata)
2198 {
2199         wmWindow *owin;
2200         wmEvent event, *evt= win->eventstate;
2201         
2202         /* initialize and copy state (only mouse x y and modifiers) */
2203         event= *evt;
2204         
2205         switch (type) {
2206                 /* mouse move */
2207                 case GHOST_kEventCursorMove: {
2208                         if(win->active) {
2209                                 GHOST_TEventCursorData *cd= customdata;
2210                                 wmEvent *lastevent= win->queue.last;
2211                                 
2212 #if defined(__APPLE__) && defined(GHOST_COCOA)
2213                                 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2214                                 evt->x= cd->x;
2215                                 evt->y= cd->y;
2216 #else
2217                                 int cx, cy;
2218                                 
2219                                 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2220                                 evt->x= cx;
2221                                 evt->y= (win->sizey-1) - cy;
2222 #endif
2223                                 
2224                                 event.x= evt->x;
2225                                 event.y= evt->y;
2226
2227                                 event.type= MOUSEMOVE;
2228
2229                                 /* some painting operators want accurate mouse events, they can
2230                                    handle inbetween mouse move moves, others can happily ignore
2231                                    them for better performance */
2232                                 if(lastevent && lastevent->type == MOUSEMOVE)
2233                                         lastevent->type = INBETWEEN_MOUSEMOVE;
2234
2235                                 update_tablet_data(win, &event);
2236                                 wm_event_add(win, &event);
2237                                 
2238                                 /* also add to other window if event is there, this makes overdraws disappear nicely */
2239                                 /* it remaps mousecoord to other window in event */
2240                                 owin= wm_event_cursor_other_windows(wm, win, &event);
2241                                 if(owin) {
2242                                         wmEvent oevent= *(owin->eventstate);
2243                                         
2244                                         oevent.x=owin->eventstate->x= event.x;
2245                                         oevent.y=owin->eventstate->y= event.y;
2246                                         oevent.type= MOUSEMOVE;
2247                                         
2248                                         update_tablet_data(owin, &oevent);
2249                                         wm_event_add(owin, &oevent);
2250                                 }
2251                                 
2252                         }
2253                         break;
2254                 }
2255                 case GHOST_kEventTrackpad: {
2256                         GHOST_TEventTrackpadData * pd = customdata;
2257                         switch (pd->subtype) {
2258                                 case GHOST_kTrackpadEventMagnify:
2259                                         event.type = MOUSEZOOM;
2260                                         break;
2261                                 case GHOST_kTrackpadEventRotate:
2262                                         event.type = MOUSEROTATE;
2263                                         break;
2264                                 case GHOST_kTrackpadEventScroll:
2265                                 default:
2266                                         event.type= MOUSEPAN;
2267                                         break;
2268                         }
2269 #if defined(__APPLE__) && defined(GHOST_COCOA)
2270                         //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2271                         event.x= evt->x = pd->x;
2272                         event.y = evt->y = pd->y;
2273 #else
2274                         {
2275                         int cx, cy;
2276                         GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2277                         event.x= evt->x= cx;
2278                         event.y= evt->y= (win->sizey-1) - cy;
2279                         }
2280 #endif
2281                         // Use prevx/prevy so we can calculate the delta later
2282                         event.prevx= event.x - pd->deltaX;
2283                         event.prevy= event.y - pd->deltaY;
2284                         
2285                         update_tablet_data(win, &event);
2286                         wm_event_add(win, &event);
2287                         break;
2288                 }
2289                 /* mouse button */
2290                 case GHOST_kEventButtonDown:
2291                 case GHOST_kEventButtonUp: {
2292                         GHOST_TEventButtonData *bd= customdata;
2293                         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 */
2294                         
2295                         if (bd->button == GHOST_kButtonMaskLeft)
2296                                 event.type= LEFTMOUSE;
2297                         else if (bd->button == GHOST_kButtonMaskRight)
2298                                 event.type= RIGHTMOUSE;
2299                         else if (bd->button == GHOST_kButtonMaskButton4)
2300                                 event.type= BUTTON4MOUSE;
2301                         else if (bd->button == GHOST_kButtonMaskButton5)
2302                                 event.type= BUTTON5MOUSE;
2303                         else
2304                                 event.type= MIDDLEMOUSE;
2305                         
2306                         /* add to other window if event is there (not to both!) */
2307                         owin= wm_event_cursor_other_windows(wm, win, &event);
2308                         if(owin) {
2309                                 wmEvent oevent= *(owin->eventstate);
2310                                 
2311                                 oevent.x= event.x;
2312                                 oevent.y= event.y;
2313                                 oevent.type= event.type;
2314                                 oevent.val= event.val;
2315                                 
2316                                 update_tablet_data(owin, &oevent);
2317                                 wm_event_add(owin, &oevent);
2318                         }
2319                         else {
2320                                 update_tablet_data(win, &event);
2321                                 wm_event_add(win, &event);
2322                         }
2323                         
2324                         break;
2325                 }
2326                 /* keyboard */
2327                 case GHOST_kEventKeyDown:
2328                 case GHOST_kEventKeyUp: {
2329                         GHOST_TEventKeyData *kd= customdata;
2330                         event.type= convert_key(kd->key);
2331                         event.ascii= kd->ascii;
2332                         event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2333                         
2334                         /* exclude arrow keys, esc, etc from text input */
2335                         if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2336                                 event.ascii= '\0';
2337                         
2338                         /* modifiers */
2339                         if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2340                                 event.shift= evt->shift= (event.val==KM_PRESS);
2341                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2342                                    event.shift= evt->shift = 3;         // define?
2343                         } 
2344                         else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2345                                 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2346                                 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2347                                    event.ctrl= evt->ctrl = 3;           // define?
2348                         } 
2349                         else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2350                                 event.alt= evt->alt= (event.val==KM_PRESS);
2351                                 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2352                                    event.alt= evt->alt = 3;             // define?
2353                         } 
2354                         else if (event.type==OSKEY) {
2355                                 event.oskey= evt->oskey= (event.val==KM_PRESS);
2356                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2357                                    event.oskey= evt->oskey = 3;         // define?
2358                         }
2359                         else {
2360                                 if(event.val==KM_PRESS && event.keymodifier==0)
2361                                         evt->keymodifier= event.type; /* only set in eventstate, for next event */
2362                                 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2363                                         event.keymodifier= evt->keymodifier= 0;
2364                         }
2365
2366                         /* this case happens on some systems that on holding a key pressed,
2367                            generate press events without release, we still want to keep the
2368                            modifier in win->eventstate, but for the press event of the same
2369                            key we don't want the key modifier */
2370                         if(event.keymodifier == event.type)
2371                                 event.keymodifier= 0;
2372                         
2373                         /* if test_break set, it catches this. XXX Keep global for now? */
2374                         if(event.type==ESCKEY)
2375                                 G.afbreek= 1;
2376                         
2377                         wm_event_add(win, &event);
2378                         
2379                         break;
2380                 }
2381                         
2382                 case GHOST_kEventWheel: {
2383                         GHOST_TEventWheelData* wheelData = customdata;
2384                         
2385                         if (wheelData->z > 0)
2386                                 event.type= WHEELUPMOUSE;
2387                         else
2388                                 event.type= WHEELDOWNMOUSE;
2389                         
2390                         event.val= KM_PRESS;
2391                         wm_event_add(win, &event);
2392                         
2393                         break;
2394                 }
2395                 case GHOST_kEventTimer: {
2396                         event.type= TIMER;
2397                         event.custom= EVT_DATA_TIMER;
2398                         event.customdata= customdata;
2399                         wm_event_add(win, &event);
2400
2401                         break;
2402                 }
2403
2404                 case GHOST_kEventUnknown:
2405                 case GHOST_kNumEventTypes:
2406                         break;
2407
2408                 case GHOST_kEventWindowDeactivate: {
2409                         event.type= WINDEACTIVATE;
2410                         wm_event_add(win, &event);
2411
2412                         break;
2413                         
2414                 }
2415
2416         }
2417 }