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