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