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