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