- correct typos in comments.
[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                                                                         /* prevent hanging on file read */
1482                                                                         BLI_freelistN(event->customdata);
1483                                                                         event->customdata= NULL;
1484                                                                         event->custom= 0;
1485                                                                 }
1486                                                         }
1487                                                 }
1488                                         }
1489                                 }
1490                         }
1491                         else {
1492                                 /* modal, swallows all */
1493                                 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1494                         }
1495
1496                         if(action & WM_HANDLER_BREAK) {
1497                                 if(always_pass)
1498                                         action &= ~WM_HANDLER_BREAK;
1499                                 else
1500                                         break;
1501                         }
1502                 }
1503                 
1504                 /* fileread case */
1505                 if(CTX_wm_window(C)==NULL)
1506                         return action;
1507         }
1508
1509         /* test for CLICK event */
1510         if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1511                 wmWindow *win = CTX_wm_window(C);
1512
1513                 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1514                         /* test for double click first,
1515                          * note1: this can be problematic because single click operators can get the
1516                          *   double click event but then with old mouse coords which is highly confusing,
1517                          *   so check for mouse moves too.
1518                          * note2: the first click event will be handled but still used to create a
1519                          *   double click event if clicking again quickly.
1520                          *   If no double click events are found itwill fallback to a single click.
1521                          *   So a double click event can result in 2 successive single click calls
1522                          *   if its not handled by the keymap - campbell */
1523                         if (    (ABS(event->x - win->eventstate->prevclickx)) <= 2 &&
1524                                         (ABS(event->y - win->eventstate->prevclicky)) <= 2 &&
1525                                         ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time)
1526                         ) {
1527                                 event->val = KM_DBL_CLICK;
1528                                 /* removed this because in cases where we're this is used as a single click
1529                                  * event, this will give old coords, since the distance is checked above, using new coords should be ok. */
1530                                 //   event->x = win->eventstate->prevclickx;
1531                                 //   event->y = win->eventstate->prevclicky;
1532                                 action |= wm_handlers_do(C, event, handlers);
1533                         }
1534
1535                         if (wm_action_not_handled(action)) {
1536                                 event->val = KM_CLICK;
1537                                 action |= wm_handlers_do(C, event, handlers);
1538                         }
1539
1540
1541                         /* revert value if not handled */
1542                         if (wm_action_not_handled(action)) {
1543                                 event->val = KM_RELEASE;
1544                         }
1545                 }
1546         }
1547
1548         return action;
1549 }
1550
1551 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1552 {
1553         if(wm_event_always_pass(event))
1554                 return 1;
1555         if(BLI_in_rcti(rect, event->x, event->y))
1556            return 1;
1557         if(event->type==MOUSEMOVE) {
1558                 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1559                         return 1;
1560                 }
1561                 return 0;
1562         }
1563         return 0;
1564 }
1565
1566 static ScrArea *area_event_inside(bContext *C, int x, int y)
1567 {
1568         bScreen *screen= CTX_wm_screen(C);
1569         ScrArea *sa;
1570         
1571         if(screen)
1572                 for(sa= screen->areabase.first; sa; sa= sa->next)
1573                         if(BLI_in_rcti(&sa->totrct, x, y))
1574                                 return sa;
1575         return NULL;
1576 }
1577
1578 static ARegion *region_event_inside(bContext *C, int x, int y)
1579 {
1580         bScreen *screen= CTX_wm_screen(C);
1581         ScrArea *area= CTX_wm_area(C);
1582         ARegion *ar;
1583         
1584         if(screen && area)
1585                 for(ar= area->regionbase.first; ar; ar= ar->next)
1586                         if(BLI_in_rcti(&ar->winrct, x, y))
1587                                 return ar;
1588         return NULL;
1589 }
1590
1591 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1592 {
1593         if(ar) {
1594                 for(; pc; pc= pc->next) {
1595                         if(pc->poll == NULL || pc->poll(C)) {
1596                                 wmWindow *win= CTX_wm_window(C);
1597                                 win->screen->do_draw_paintcursor= 1;
1598                                 wm_tag_redraw_overlay(win, ar);
1599                         }
1600                 }
1601         }
1602 }
1603
1604 /* called on mousemove, check updates for paintcursors */
1605 /* context was set on active area and region */
1606 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1607 {
1608         wmWindowManager *wm= CTX_wm_manager(C);
1609         
1610         if(wm->paintcursors.first) {
1611                 ARegion *ar= CTX_wm_region(C);
1612                 if(ar)
1613                         wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1614                 
1615                 /* if previous position was not in current region, we have to set a temp new context */
1616                 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1617                         ScrArea *sa= CTX_wm_area(C);
1618                         
1619                         CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1620                         CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1621
1622                         wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1623                         
1624                         CTX_wm_area_set(C, sa);
1625                         CTX_wm_region_set(C, ar);
1626                 }
1627         }
1628 }
1629
1630 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1631 {
1632         if(wm->drags.first==NULL) return;
1633         
1634         if(event->type==MOUSEMOVE)
1635                 win->screen->do_draw_drag= 1;
1636         else if(event->type==ESCKEY) {
1637                 BLI_freelistN(&wm->drags);
1638                 win->screen->do_draw_drag= 1;
1639         }
1640         else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1641                 event->type= EVT_DROP;
1642                 
1643                 /* create customdata, first free existing */
1644                 if(event->customdata) {
1645                         if(event->customdatafree)
1646                                 MEM_freeN(event->customdata);
1647                 }
1648                 
1649                 event->custom= EVT_DATA_LISTBASE;
1650                 event->customdata= &wm->drags;
1651                 event->customdatafree= 1;
1652                 
1653                 /* clear drop icon */
1654                 win->screen->do_draw_drag= 1;
1655                 
1656                 /* restore cursor (disabled, see wm_dragdrop.c) */
1657                 // WM_cursor_restore(win);
1658         }
1659         
1660         /* overlap fails otherwise */
1661         if(win->screen->do_draw_drag)
1662                 if(win->drawmethod == USER_DRAW_OVERLAP)
1663                         win->screen->do_draw= 1;
1664         
1665 }
1666
1667 /* called in main loop */
1668 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
1669 void wm_event_do_handlers(bContext *C)
1670 {
1671         wmWindowManager *wm= CTX_wm_manager(C);
1672         wmWindow *win;
1673
1674         for(win= wm->windows.first; win; win= win->next) {
1675                 wmEvent *event;
1676                 
1677                 if( win->screen==NULL )
1678                         wm_event_free_all(win);
1679                 else {
1680                         Scene* scene = win->screen->scene;
1681                         
1682                         if(scene) {
1683                                 int playing = sound_scene_playing(win->screen->scene);
1684                                 
1685                                 if(playing != -1) {
1686                                         CTX_wm_window_set(C, win);
1687                                         CTX_wm_screen_set(C, win->screen);
1688                                         CTX_data_scene_set(C, scene);
1689                                         
1690                                         if(((playing == 1) && (!win->screen->animtimer)) || ((playing == 0) && (win->screen->animtimer))){
1691                                                 ED_screen_animation_play(C, -1, 1);
1692                                         }
1693                                         
1694                                         if(playing == 0) {
1695                                                 int ncfra = sound_sync_scene(scene) * FPS + 0.5;
1696                                                 if(ncfra != scene->r.cfra)      {
1697                                                         scene->r.cfra = ncfra;
1698                                                         ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
1699                                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
1700                                                 }
1701                                         }
1702                                         
1703                                         CTX_data_scene_set(C, NULL);
1704                                         CTX_wm_screen_set(C, NULL);
1705                                         CTX_wm_window_set(C, NULL);
1706                                 }
1707                         }
1708                 }
1709                 
1710                 while( (event= win->queue.first) ) {
1711                         int action = WM_HANDLER_CONTINUE;
1712
1713                         if((G.f & G_DEBUG) && event && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1714                                 printf("pass on evt %d val %d\n", event->type, event->val); 
1715                         
1716                         wm_eventemulation(event);
1717
1718                         CTX_wm_window_set(C, win);
1719                         
1720                         /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1721                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1722                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1723                         
1724                         /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1725                         wm_window_make_drawable(C, win);
1726                         
1727                         /* first we do priority handlers, modal + some limited keymaps */
1728                         action |= wm_handlers_do(C, event, &win->modalhandlers);
1729                         
1730                         /* fileread case */
1731                         if(CTX_wm_window(C)==NULL)
1732                                 return;
1733                         
1734                         /* check dragging, creates new event or frees, adds draw tag */
1735                         wm_event_drag_test(wm, win, event);
1736                         
1737                         /* builtin tweak, if action is break it removes tweak */
1738                         wm_tweakevent_test(C, event, action);
1739
1740                         if((action & WM_HANDLER_BREAK) == 0) {
1741                                 ScrArea *sa;
1742                                 ARegion *ar;
1743                                 int doit= 0;
1744                                 
1745                                 /* XXX to solve, here screen handlers? */
1746                                 if(event->type==MOUSEMOVE) {
1747                                         /* state variables in screen, cursors */
1748                                         ED_screen_set_subwinactive(win, event); 
1749                                         /* for regions having custom cursors */
1750                                         wm_paintcursor_test(C, event);
1751                                 }
1752
1753                                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1754                                         if(wm_event_inside_i(event, &sa->totrct)) {
1755                                                 CTX_wm_area_set(C, sa);
1756
1757                                                 if((action & WM_HANDLER_BREAK) == 0) {
1758                                                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
1759                                                                 if(wm_event_inside_i(event, &ar->winrct)) {
1760                                                                         CTX_wm_region_set(C, ar);
1761                                                                         
1762                                                                         /* does polls for drop regions and checks uibuts */
1763                                                                         /* need to be here to make sure region context is true */
1764                                                                         if(ELEM(event->type, MOUSEMOVE, EVT_DROP)) {
1765                                                                                 wm_region_mouse_co(C, event);
1766                                                                                 wm_drags_check_ops(C, event);
1767                                                                         }
1768                                                                         
1769                                                                         action |= wm_handlers_do(C, event, &ar->handlers);
1770
1771                                                                         /* fileread case (python), [#29489] */
1772                                                                         if(CTX_wm_window(C)==NULL)
1773                                                                                 return;
1774
1775                                                                         doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1776                                                                         
1777                                                                         if(action & WM_HANDLER_BREAK)
1778                                                                                 break;
1779                                                                 }
1780                                                         }
1781                                                 }
1782
1783                                                 CTX_wm_region_set(C, NULL);
1784
1785                                                 if((action & WM_HANDLER_BREAK) == 0)
1786                                                         action |= wm_handlers_do(C, event, &sa->handlers);
1787
1788                                                 CTX_wm_area_set(C, NULL);
1789
1790                                                 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1791                                         }
1792                                 }
1793                                 
1794                                 if((action & WM_HANDLER_BREAK) == 0) {
1795                                         /* also some non-modal handlers need active area/region */
1796                                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1797                                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1798
1799                                         action |= wm_handlers_do(C, event, &win->handlers);
1800
1801                                         /* fileread case */
1802                                         if(CTX_wm_window(C)==NULL)
1803                                                 return;
1804                                 }
1805
1806                                 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad? 
1807                                    doing it on ghost queue gives errors when mousemoves go over area borders */
1808                                 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1809                                         win->eventstate->prevx= event->x;
1810                                         win->eventstate->prevy= event->y;
1811                                 }
1812                         }
1813                         
1814                         /* store last event for this window */
1815                         /* mousemove and timer events don't overwrite last type */
1816                         if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1817                                 if (wm_action_not_handled(action)) {
1818                                         if (win->eventstate->prevtype == event->type) {
1819                                                 /* set click time on first click (press -> release) */
1820                                                 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1821                                                         win->eventstate->prevclicktime = PIL_check_seconds_timer();
1822                                                         win->eventstate->prevclickx = event->x;
1823                                                         win->eventstate->prevclicky = event->y;
1824                                                 }
1825                                         } else {
1826                                                 /* reset click time if event type not the same */
1827                                                 win->eventstate->prevclicktime = 0;
1828                                         }
1829
1830                                         win->eventstate->prevval = event->val;
1831                                         win->eventstate->prevtype = event->type;
1832                                 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1833                                         win->eventstate->prevtype = event->type;
1834                                         win->eventstate->prevval = event->val;
1835                                         win->eventstate->prevclicktime = PIL_check_seconds_timer();
1836                                         win->eventstate->prevclickx = event->x;
1837                                         win->eventstate->prevclicky = event->y;
1838                                 } else { /* reset if not */
1839                                         win->eventstate->prevtype = -1;
1840                                         win->eventstate->prevval = 0;
1841                                         win->eventstate->prevclicktime = 0;
1842                                 }
1843                         }
1844
1845                         /* unlink and free here, blender-quit then frees all */
1846                         BLI_remlink(&win->queue, event);
1847                         wm_event_free(event);
1848                         
1849                 }
1850                 
1851                 /* only add mousemove when queue was read entirely */
1852                 if(win->addmousemove && win->eventstate) {
1853                         wmEvent tevent= *(win->eventstate);
1854                         tevent.type= MOUSEMOVE;
1855                         tevent.prevx= tevent.x;
1856                         tevent.prevy= tevent.y;
1857                         wm_event_add(win, &tevent);
1858                         win->addmousemove= 0;
1859                 }
1860                 
1861                 CTX_wm_window_set(C, NULL);
1862         }
1863 }
1864
1865 /* ********** filesector handling ************ */
1866
1867 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1868 {
1869         /* add to all windows! */
1870         wmWindow *win;
1871         
1872         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1873                 wmEvent event= *win->eventstate;
1874                 
1875                 event.type= EVT_FILESELECT;
1876                 event.val= eventval;
1877                 event.customdata= ophandle;             // only as void pointer type check
1878
1879                 wm_event_add(win, &event);
1880         }
1881 }
1882
1883 /* operator is supposed to have a filled "path" property */
1884 /* optional property: filetype (XXX enum?) */
1885
1886 /* Idea is to keep a handler alive on window queue, owning the operator.
1887    The filewindow can send event to make it execute, thus ensuring
1888    executing happens outside of lower level queues, with UI refreshed. 
1889    Should also allow multiwin solutions */
1890
1891 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1892 {
1893         wmEventHandler *handler;
1894         wmWindow *win= CTX_wm_window(C);
1895         int full= 1;    // XXX preset?
1896
1897         /* only allow file selector open per window bug [#23553] */
1898         for(handler= win->modalhandlers.first; handler; handler=handler->next) {
1899                 if(handler->type == WM_HANDLER_FILESELECT)
1900                         return;
1901         }
1902         
1903         handler = MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1904         
1905         handler->type= WM_HANDLER_FILESELECT;
1906         handler->op= op;
1907         handler->op_area= CTX_wm_area(C);
1908         handler->op_region= CTX_wm_region(C);
1909         handler->filescreen= CTX_wm_screen(C);
1910         
1911         BLI_addhead(&win->modalhandlers, handler);
1912         
1913         /* check props once before invoking if check is available
1914          * ensures initial properties are valid */
1915         if(op->type->check) {
1916                 op->type->check(C, op); /* ignore return value */
1917         }
1918
1919         WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1920 }
1921
1922 /* lets not expose struct outside wm? */
1923 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1924 {
1925         handler->flag= flag;
1926 }
1927
1928 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1929 {
1930         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1931         wmWindow *win= CTX_wm_window(C);
1932         
1933         /* operator was part of macro */
1934         if(op->opm) {
1935                 /* give the mother macro to the handler */
1936                 handler->op= op->opm;
1937                 /* mother macro opm becomes the macro element */
1938                 handler->op->opm= op;
1939         }
1940         else
1941                 handler->op= op;
1942         
1943         handler->op_area= CTX_wm_area(C);               /* means frozen screen context for modal handlers! */
1944         handler->op_region= CTX_wm_region(C);
1945         
1946         BLI_addhead(&win->modalhandlers, handler);
1947
1948         return handler;
1949 }
1950
1951 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1952 {
1953         wmEventHandler *handler;
1954
1955         if(!keymap) {
1956                 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1957                 return NULL;
1958         }
1959
1960         /* only allow same keymap once */
1961         for(handler= handlers->first; handler; handler= handler->next)
1962                 if(handler->keymap==keymap)
1963                         return handler;
1964         
1965         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1966         BLI_addtail(handlers, handler);
1967         handler->keymap= keymap;
1968
1969         return handler;
1970 }
1971
1972 /* priorities not implemented yet, for time being just insert in begin of list */
1973 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int UNUSED(priority))
1974 {
1975         wmEventHandler *handler;
1976         
1977         WM_event_remove_keymap_handler(handlers, keymap);
1978         
1979         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1980         BLI_addhead(handlers, handler);
1981         handler->keymap= keymap;
1982         
1983         return handler;
1984 }
1985
1986 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1987 {
1988         wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1989         
1990         if(handler) {
1991                 handler->bblocal= bblocal;
1992                 handler->bbwin= bbwin;
1993         }
1994         return handler;
1995 }
1996
1997 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1998 {
1999         wmEventHandler *handler;
2000         
2001         for(handler= handlers->first; handler; handler= handler->next) {
2002                 if(handler->keymap==keymap) {
2003                         BLI_remlink(handlers, handler);
2004                         wm_event_free_handler(handler);
2005                         break;
2006                 }
2007         }
2008 }
2009
2010 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
2011 {
2012         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
2013         handler->ui_handle= func;
2014         handler->ui_remove= remove;
2015         handler->ui_userdata= userdata;
2016         handler->ui_area= (C)? CTX_wm_area(C): NULL;
2017         handler->ui_region= (C)? CTX_wm_region(C): NULL;
2018         handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
2019         
2020         BLI_addhead(handlers, handler);
2021         
2022         return handler;
2023 }
2024
2025 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
2026 {
2027         wmEventHandler *handler;
2028         
2029         for(handler= handlers->first; handler; handler= handler->next) {
2030                 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
2031                         BLI_remlink(handlers, handler);
2032                         wm_event_free_handler(handler);
2033                         break;
2034                 }
2035         }
2036 }
2037
2038 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
2039 {
2040         wmEventHandler *handler;
2041
2042         /* only allow same dropbox once */
2043         for(handler= handlers->first; handler; handler= handler->next)
2044                 if(handler->dropboxes==dropboxes)
2045                         return handler;
2046         
2047         handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
2048         
2049         /* dropbox stored static, no free or copy */
2050         handler->dropboxes= dropboxes;
2051         BLI_addhead(handlers, handler);
2052         
2053         return handler;
2054 }
2055
2056 /* XXX solution works, still better check the real cause (ton) */
2057 void WM_event_remove_area_handler(ListBase *handlers, void *area)
2058 {
2059         wmEventHandler *handler, *nexthandler;
2060
2061         for(handler = handlers->first; handler; handler= nexthandler) {
2062                 nexthandler = handler->next;
2063                 if (handler->type != WM_HANDLER_FILESELECT) {
2064                         if (handler->ui_area == area) {
2065                                 BLI_remlink(handlers, handler);
2066                                 wm_event_free_handler(handler);
2067                         }
2068                 }
2069         }
2070 }
2071
2072 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
2073 {
2074         BLI_remlink(handlers, handler);
2075         wm_event_free_handler(handler);
2076 }
2077
2078 void WM_event_add_mousemove(bContext *C)
2079 {
2080         wmWindow *window= CTX_wm_window(C);
2081         
2082         window->addmousemove= 1;
2083 }
2084
2085 /* for modal callbacks, check configuration for how to interpret exit with tweaks  */
2086 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
2087 {
2088         /* user preset or keymap? dunno... */
2089         // XXX WTH is this?
2090         int tweak_modal= (U.flag & USER_RELEASECONFIRM)==0;
2091         
2092         switch(tweak_event) {
2093                 case EVT_TWEAK_L:
2094                 case EVT_TWEAK_M:
2095                 case EVT_TWEAK_R:
2096                         if(evt->val==tweak_modal)
2097                                 return 1;
2098                 default:
2099                         /* this case is when modal callcback didnt get started with a tweak */
2100                         if(evt->val)
2101                                 return 1;
2102         }
2103         return 0;
2104 }
2105
2106 /* ********************* ghost stuff *************** */
2107
2108 static int convert_key(GHOST_TKey key) 
2109 {
2110         if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
2111                 return (AKEY + ((int) key - GHOST_kKeyA));
2112         } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
2113                 return (ZEROKEY + ((int) key - GHOST_kKey0));
2114         } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
2115                 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
2116         } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF19) {
2117                 return (F1KEY + ((int) key - GHOST_kKeyF1));
2118         } else {
2119                 switch (key) {
2120                         case GHOST_kKeyBackSpace:               return BACKSPACEKEY;
2121                         case GHOST_kKeyTab:                             return TABKEY;
2122                         case GHOST_kKeyLinefeed:                return LINEFEEDKEY;
2123                         case GHOST_kKeyClear:                   return 0;
2124                         case GHOST_kKeyEnter:                   return RETKEY;
2125                                 
2126                         case GHOST_kKeyEsc:                             return ESCKEY;
2127                         case GHOST_kKeySpace:                   return SPACEKEY;
2128                         case GHOST_kKeyQuote:                   return QUOTEKEY;
2129                         case GHOST_kKeyComma:                   return COMMAKEY;
2130                         case GHOST_kKeyMinus:                   return MINUSKEY;
2131                         case GHOST_kKeyPeriod:                  return PERIODKEY;
2132                         case GHOST_kKeySlash:                   return SLASHKEY;
2133                                 
2134                         case GHOST_kKeySemicolon:               return SEMICOLONKEY;
2135                         case GHOST_kKeyEqual:                   return EQUALKEY;
2136                                 
2137                         case GHOST_kKeyLeftBracket:             return LEFTBRACKETKEY;
2138                         case GHOST_kKeyRightBracket:    return RIGHTBRACKETKEY;
2139                         case GHOST_kKeyBackslash:               return BACKSLASHKEY;
2140                         case GHOST_kKeyAccentGrave:             return ACCENTGRAVEKEY;
2141                                 
2142                         case GHOST_kKeyLeftShift:               return LEFTSHIFTKEY;
2143                         case GHOST_kKeyRightShift:              return RIGHTSHIFTKEY;
2144                         case GHOST_kKeyLeftControl:             return LEFTCTRLKEY;
2145                         case GHOST_kKeyRightControl:    return RIGHTCTRLKEY;
2146                         case GHOST_kKeyOS:                              return OSKEY;
2147                         case GHOST_kKeyLeftAlt:                 return LEFTALTKEY;
2148                         case GHOST_kKeyRightAlt:                return RIGHTALTKEY;
2149                                 
2150                         case GHOST_kKeyCapsLock:                return CAPSLOCKKEY;
2151                         case GHOST_kKeyNumLock:                 return 0;
2152                         case GHOST_kKeyScrollLock:              return 0;
2153                                 
2154                         case GHOST_kKeyLeftArrow:               return LEFTARROWKEY;
2155                         case GHOST_kKeyRightArrow:              return RIGHTARROWKEY;
2156                         case GHOST_kKeyUpArrow:                 return UPARROWKEY;
2157                         case GHOST_kKeyDownArrow:               return DOWNARROWKEY;
2158                                 
2159                         case GHOST_kKeyPrintScreen:             return 0;
2160                         case GHOST_kKeyPause:                   return PAUSEKEY;
2161                                 
2162                         case GHOST_kKeyInsert:                  return INSERTKEY;
2163                         case GHOST_kKeyDelete:                  return DELKEY;
2164                         case GHOST_kKeyHome:                    return HOMEKEY;
2165                         case GHOST_kKeyEnd:                             return ENDKEY;
2166                         case GHOST_kKeyUpPage:                  return PAGEUPKEY;
2167                         case GHOST_kKeyDownPage:                return PAGEDOWNKEY;
2168                                 
2169                         case GHOST_kKeyNumpadPeriod:    return PADPERIOD;
2170                         case GHOST_kKeyNumpadEnter:             return PADENTER;
2171                         case GHOST_kKeyNumpadPlus:              return PADPLUSKEY;
2172                         case GHOST_kKeyNumpadMinus:             return PADMINUS;
2173                         case GHOST_kKeyNumpadAsterisk:  return PADASTERKEY;
2174                         case GHOST_kKeyNumpadSlash:             return PADSLASHKEY;
2175                                 
2176                         case GHOST_kKeyGrLess:              return GRLESSKEY; 
2177                                 
2178                         default:
2179                                 return UNKNOWNKEY;      /* GHOST_kKeyUnknown */
2180                 }
2181         }
2182 }
2183
2184 /* adds customdata to event */
2185 static void update_tablet_data(wmWindow *win, wmEvent *event)
2186 {
2187         const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
2188         
2189         /* if there's tablet data from an active tablet device then add it */
2190         if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2191                 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
2192                 
2193                 wmtab->Active = (int)td->Active;
2194                 wmtab->Pressure = td->Pressure;
2195                 wmtab->Xtilt = td->Xtilt;
2196                 wmtab->Ytilt = td->Ytilt;
2197                 
2198                 event->custom= EVT_DATA_TABLET;
2199                 event->customdata= wmtab;
2200                 event->customdatafree= 1;
2201         } 
2202 }
2203
2204 /* imperfect but probably usable... draw/enable drags to other windows */
2205 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2206 {
2207         short mx= evt->x, my= evt->y;
2208         
2209         if(wm->windows.first== wm->windows.last)
2210                 return NULL;
2211         
2212         /* top window bar... */
2213         if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) { 
2214                 wmWindow *owin;
2215                 wmEventHandler *handler;
2216                 
2217                 /* let's skip windows having modal handlers now */
2218                 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2219                 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2220                         if(handler->ui_handle || handler->op)
2221                                 return NULL;
2222                 
2223                 /* to desktop space */
2224                 mx+= win->posx;
2225                 my+= win->posy;
2226                 
2227                 /* check other windows to see if it has mouse inside */
2228                 for(owin= wm->windows.first; owin; owin= owin->next) {
2229                         
2230                         if(owin!=win) {
2231                                 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2232                                    mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2233                                         evt->x= mx-owin->posx;
2234                                         evt->y= my-owin->posy;
2235                                         
2236                                         return owin;
2237                                 }
2238                         }
2239                 }
2240         }
2241         return NULL;
2242 }
2243
2244 /* windows store own event queues, no bContext here */
2245 /* time is in 1000s of seconds, from ghost */
2246 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int UNUSED(time), void *customdata)
2247 {
2248         wmWindow *owin;
2249         wmEvent event, *evt= win->eventstate;
2250         
2251         /* initialize and copy state (only mouse x y and modifiers) */
2252         event= *evt;
2253         
2254         switch (type) {
2255                 /* mouse move */
2256                 case GHOST_kEventCursorMove: {
2257                         if(win->active) {
2258                                 GHOST_TEventCursorData *cd= customdata;
2259                                 wmEvent *lastevent= win->queue.last;
2260                                 
2261 #if defined(__APPLE__) && defined(GHOST_COCOA)
2262                                 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2263                                 evt->x= cd->x;
2264                                 evt->y= cd->y;
2265 #else
2266                                 int cx, cy;
2267                                 
2268                                 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2269                                 evt->x= cx;
2270                                 evt->y= (win->sizey-1) - cy;
2271 #endif
2272                                 
2273                                 event.x= evt->x;
2274                                 event.y= evt->y;
2275
2276                                 event.type= MOUSEMOVE;
2277
2278                                 /* some painting operators want accurate mouse events, they can
2279                                    handle in between mouse move moves, others can happily ignore
2280                                    them for better performance */
2281                                 if(lastevent && lastevent->type == MOUSEMOVE)
2282                                         lastevent->type = INBETWEEN_MOUSEMOVE;
2283
2284                                 update_tablet_data(win, &event);
2285                                 wm_event_add(win, &event);
2286                                 
2287                                 /* also add to other window if event is there, this makes overdraws disappear nicely */
2288                                 /* it remaps mousecoord to other window in event */
2289                                 owin= wm_event_cursor_other_windows(wm, win, &event);
2290                                 if(owin) {
2291                                         wmEvent oevent= *(owin->eventstate);
2292                                         
2293                                         oevent.x=owin->eventstate->x= event.x;
2294                                         oevent.y=owin->eventstate->y= event.y;
2295                                         oevent.type= MOUSEMOVE;
2296                                         
2297                                         update_tablet_data(owin, &oevent);
2298                                         wm_event_add(owin, &oevent);
2299                                 }
2300                                 
2301                         }
2302                         break;
2303                 }
2304                 case GHOST_kEventTrackpad: {
2305                         GHOST_TEventTrackpadData * pd = customdata;
2306                         switch (pd->subtype) {
2307                                 case GHOST_kTrackpadEventMagnify:
2308                                         event.type = MOUSEZOOM;
2309                                         break;
2310                                 case GHOST_kTrackpadEventRotate:
2311                                         event.type = MOUSEROTATE;
2312                                         break;
2313                                 case GHOST_kTrackpadEventScroll:
2314                                 default:
2315                                         event.type= MOUSEPAN;
2316                                         break;
2317                         }
2318 #if defined(__APPLE__) && defined(GHOST_COCOA)
2319                         //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2320                         event.x= evt->x = pd->x;
2321                         event.y = evt->y = pd->y;
2322 #else
2323                         {
2324                         int cx, cy;
2325                         GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2326                         event.x= evt->x= cx;
2327                         event.y= evt->y= (win->sizey-1) - cy;
2328                         }
2329 #endif
2330                         // Use prevx/prevy so we can calculate the delta later
2331                         event.prevx= event.x - pd->deltaX;
2332                         event.prevy= event.y - pd->deltaY;
2333                         
2334                         update_tablet_data(win, &event);
2335                         wm_event_add(win, &event);
2336                         break;
2337                 }
2338                 /* mouse button */
2339                 case GHOST_kEventButtonDown:
2340                 case GHOST_kEventButtonUp: {
2341                         GHOST_TEventButtonData *bd= customdata;
2342                         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 */
2343                         
2344                         if (bd->button == GHOST_kButtonMaskLeft)
2345                                 event.type= LEFTMOUSE;
2346                         else if (bd->button == GHOST_kButtonMaskRight)
2347                                 event.type= RIGHTMOUSE;
2348                         else if (bd->button == GHOST_kButtonMaskButton4)
2349                                 event.type= BUTTON4MOUSE;
2350                         else if (bd->button == GHOST_kButtonMaskButton5)
2351                                 event.type= BUTTON5MOUSE;
2352                         else
2353                                 event.type= MIDDLEMOUSE;
2354                         
2355                         /* add to other window if event is there (not to both!) */
2356                         owin= wm_event_cursor_other_windows(wm, win, &event);
2357                         if(owin) {
2358                                 wmEvent oevent= *(owin->eventstate);
2359                                 
2360                                 oevent.x= event.x;
2361                                 oevent.y= event.y;
2362                                 oevent.type= event.type;
2363                                 oevent.val= event.val;
2364                                 
2365                                 update_tablet_data(owin, &oevent);
2366                                 wm_event_add(owin, &oevent);
2367                         }
2368                         else {
2369                                 update_tablet_data(win, &event);
2370                                 wm_event_add(win, &event);
2371                         }
2372                         
2373                         break;
2374                 }
2375                 /* keyboard */
2376                 case GHOST_kEventKeyDown:
2377                 case GHOST_kEventKeyUp: {
2378                         GHOST_TEventKeyData *kd= customdata;
2379                         event.type= convert_key(kd->key);
2380                         event.ascii= kd->ascii;
2381                         event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2382                         
2383                         /* exclude arrow keys, esc, etc from text input */
2384                         if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2385                                 event.ascii= '\0';
2386                         
2387                         /* modifiers */
2388                         if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2389                                 event.shift= evt->shift= (event.val==KM_PRESS);
2390                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2391                                    event.shift= evt->shift = 3;         // define?
2392                         } 
2393                         else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2394                                 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2395                                 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2396                                    event.ctrl= evt->ctrl = 3;           // define?
2397                         } 
2398                         else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2399                                 event.alt= evt->alt= (event.val==KM_PRESS);
2400                                 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2401                                    event.alt= evt->alt = 3;             // define?
2402                         } 
2403                         else if (event.type==OSKEY) {
2404                                 event.oskey= evt->oskey= (event.val==KM_PRESS);
2405                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2406                                    event.oskey= evt->oskey = 3;         // define?
2407                         }
2408                         else {
2409                                 if(event.val==KM_PRESS && event.keymodifier==0)
2410                                         evt->keymodifier= event.type; /* only set in eventstate, for next event */
2411                                 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2412                                         event.keymodifier= evt->keymodifier= 0;
2413                         }
2414
2415                         /* this case happens on some systems that on holding a key pressed,
2416                            generate press events without release, we still want to keep the
2417                            modifier in win->eventstate, but for the press event of the same
2418                            key we don't want the key modifier */
2419                         if(event.keymodifier == event.type)
2420                                 event.keymodifier= 0;
2421                         
2422                         /* if test_break set, it catches this. XXX Keep global for now? */
2423                         if(event.type==ESCKEY)
2424                                 G.afbreek= 1;
2425                         
2426                         wm_event_add(win, &event);
2427                         
2428                         break;
2429                 }
2430                         
2431                 case GHOST_kEventWheel: {
2432                         GHOST_TEventWheelData* wheelData = customdata;
2433                         
2434                         if (wheelData->z > 0)
2435                                 event.type= WHEELUPMOUSE;
2436                         else
2437                                 event.type= WHEELDOWNMOUSE;
2438                         
2439                         event.val= KM_PRESS;
2440                         wm_event_add(win, &event);
2441                         
2442                         break;
2443                 }
2444                 case GHOST_kEventTimer: {
2445                         event.type= TIMER;
2446                         event.custom= EVT_DATA_TIMER;
2447                         event.customdata= customdata;
2448                         wm_event_add(win, &event);
2449
2450                         break;
2451                 }
2452
2453                 case GHOST_kEventUnknown:
2454                 case GHOST_kNumEventTypes:
2455                         break;
2456
2457                 case GHOST_kEventWindowDeactivate: {
2458                         event.type= WINDEACTIVATE;
2459                         wm_event_add(win, &event);
2460
2461                         break;
2462                         
2463                 }
2464
2465         }
2466 }