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