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