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