54841f0e06342fad63bd815775c76608b8a69f2d
[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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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
32 #include "DNA_listBase.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_windowmanager_types.h"
36 #include "DNA_userdef_types.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include "GHOST_C-api.h"
41
42 #include "BLI_blenlib.h"
43
44 #include "BKE_blender.h"
45 #include "BKE_context.h"
46 #include "BKE_idprop.h"
47 #include "BKE_global.h"
48 #include "BKE_object.h"
49 #include "BKE_report.h"
50 #include "BKE_scene.h"
51 #include "BKE_utildefines.h"
52 #include "BKE_pointcache.h"
53
54 #include "ED_fileselect.h"
55 #include "ED_info.h"
56 #include "ED_screen.h"
57 #include "ED_space_api.h"
58 #include "ED_util.h"
59
60 #include "RNA_access.h"
61
62 #include "UI_interface.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66 #include "wm.h"
67 #include "wm_window.h"
68 #include "wm_event_system.h"
69 #include "wm_event_types.h"
70
71 /* ************ event management ************** */
72
73 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
74 {
75         wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
76         
77         *event= *event_to_add;
78         BLI_addtail(&win->queue, event);
79 }
80
81 void wm_event_free(wmEvent *event)
82 {
83         if(event->customdata && event->customdatafree)
84                 MEM_freeN(event->customdata);
85         MEM_freeN(event);
86 }
87
88 void wm_event_free_all(wmWindow *win)
89 {
90         wmEvent *event;
91         
92         while((event= win->queue.first)) {
93                 BLI_remlink(&win->queue, event);
94                 wm_event_free(event);
95         }
96 }
97
98 /* ********************* notifiers, listeners *************** */
99
100 /* XXX: in future, which notifiers to send to other windows? */
101 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
102 {
103         wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
104         
105         note->wm= CTX_wm_manager(C);
106         BLI_addtail(&note->wm->queue, note);
107         
108         note->window= CTX_wm_window(C);
109         
110         if(CTX_wm_region(C))
111                 note->swinid= CTX_wm_region(C)->swinid;
112         
113         note->category= type & NOTE_CATEGORY;
114         note->data= type & NOTE_DATA;
115         note->subtype= type & NOTE_SUBTYPE;
116         note->action= type & NOTE_ACTION;
117         
118         note->reference= reference;
119 }
120
121 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
122 {
123         wmNotifier *note= wm->queue.first;
124         
125         if(note) BLI_remlink(&wm->queue, note);
126         return note;
127 }
128
129 /* called in mainloop */
130 void wm_event_do_notifiers(bContext *C)
131 {
132         wmWindowManager *wm= CTX_wm_manager(C);
133         wmNotifier *note, *next;
134         wmWindow *win;
135         
136         if(wm==NULL)
137                 return;
138         
139         /* cache & catch WM level notifiers, such as frame change, scene/screen set */
140         for(win= wm->windows.first; win; win= win->next) {
141                 int do_anim= 0;
142                 
143                 CTX_wm_window_set(C, win);
144                 
145                 for(note= wm->queue.first; note; note= next) {
146                         next= note->next;
147
148                         if(note->category==NC_WM) {
149                                 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
150                                         wm->file_saved= 1;
151                                         wm_window_title(wm, win);
152                                 }
153                                 else if(note->data==ND_DATACHANGED)
154                                         wm_window_title(wm, win);
155                         }
156                         if(note->window==win) {
157                                 if(note->category==NC_SCREEN) {
158                                         if(note->data==ND_SCREENBROWSE) {
159                                                 ED_screen_set(C, note->reference);      // XXX hrms, think this over!
160                                                 printf("screen set %p\n", note->reference);
161                                         }
162                                         else if(note->data==ND_SCREENDELETE) {
163                                                 ED_screen_delete(C, note->reference);   // XXX hrms, think this over!
164                                                 printf("screen delete %p\n", note->reference);
165                                         }
166                                 }
167                                 else if(note->category==NC_SCENE) {
168                                         if(note->data==ND_SCENEBROWSE) {
169                                                 ED_screen_set_scene(C, note->reference);        // XXX hrms, think this over!
170                                                 printf("scene set %p\n", note->reference);
171                                         }
172                                         if(note->data==ND_SCENEDELETE) {
173                                                 ED_screen_delete_scene(C, note->reference);     // XXX hrms, think this over!
174                                                 printf("scene delete %p\n", note->reference);
175                                         }
176                                         else if(note->data==ND_FRAME)
177                                                 do_anim= 1;
178                                 }
179                         }
180                         if(ELEM4(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE)) {
181                                 ED_info_stats_clear(CTX_data_scene(C));
182                                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
183                         }
184                 }
185                 if(do_anim) {
186                         /* depsgraph gets called, might send more notifiers */
187                         ED_update_for_newframe(C, 1);
188                 }
189         }
190         
191         /* the notifiers are sent without context, to keep it clean */
192         while( (note=wm_notifier_next(wm)) ) {
193                 wmWindow *win;
194                 
195                 for(win= wm->windows.first; win; win= win->next) {
196                         
197                         /* filter out notifiers */
198                         if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
199                         else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
200                         else {
201                                 ScrArea *sa;
202                                 ARegion *ar;
203
204                                 /* XXX context in notifiers? */
205                                 CTX_wm_window_set(C, win);
206
207                                 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
208                                 ED_screen_do_listen(win, note);
209
210                                 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
211                                         ED_region_do_listen(ar, note);
212                                 }
213                                 
214                                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
215                                         ED_area_do_listen(sa, note);
216                                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
217                                                 ED_region_do_listen(ar, note);
218                                         }
219                                 }
220                         }
221                 }
222                 
223                 MEM_freeN(note);
224         }
225         
226         /* cached: editor refresh callbacks now, they get context */
227         for(win= wm->windows.first; win; win= win->next) {
228                 Scene *sce, *scene= win->screen->scene;
229                 ScrArea *sa;
230                 Base *base;
231                 
232                 CTX_wm_window_set(C, win);
233                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
234                         if(sa->do_refresh) {
235                                 CTX_wm_area_set(C, sa);
236                                 ED_area_do_refresh(C, sa);
237                         }
238                 }
239                 
240                 if(G.rendering==0) { // XXX make lock in future, or separated derivedmesh users in scene
241                         
242                         /* update all objects, drivers, matrices, displists, etc. Flags set by depgraph or manual, 
243                                 no layer check here, gets correct flushed */
244                         /* sets first, we allow per definition current scene to have dependencies on sets */
245                         if(scene->set) {
246                                 for(SETLOOPER(scene->set, base))
247                                         object_handle_update(scene, base->object);
248                         }
249                         
250                         for(base= scene->base.first; base; base= base->next) {
251                                 object_handle_update(scene, base->object);
252                         }
253
254                         BKE_ptcache_quick_cache_all(scene);
255                 }               
256         }
257         CTX_wm_window_set(C, NULL);
258 }
259
260 /* ********************* operators ******************* */
261
262 int WM_operator_poll(bContext *C, wmOperatorType *ot)
263 {
264         wmOperatorTypeMacro *otmacro;
265         
266         for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
267                 wmOperatorType *ot= WM_operatortype_find(otmacro->idname, 0);
268                 
269                 if(0==WM_operator_poll(C, ot))
270                         return 0;
271         }
272         
273         /* python needs operator type, so we added exception for it */
274         if(ot->pyop_poll)
275                 return ot->pyop_poll(C, ot);
276         else if(ot->poll)
277                 return ot->poll(C);
278
279         return 1;
280 }
281
282 /* if repeat is true, it doesn't register again, nor does it free */
283 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
284 {
285         int retval= OPERATOR_CANCELLED;
286         
287         if(op==NULL || op->type==NULL)
288                 return retval;
289         
290         if(0==WM_operator_poll(C, op->type))
291                 return retval;
292         
293         if(op->type->exec)
294                 retval= op->type->exec(C, op);
295         
296         if(!(retval & OPERATOR_RUNNING_MODAL))
297                 if(op->reports->list.first)
298                         uiPupMenuReports(C, op->reports);
299         
300         if(retval & OPERATOR_FINISHED) {
301                 op->customdata= NULL;
302
303                 if(op->type->flag & OPTYPE_UNDO)
304                         ED_undo_push_op(C, op);
305                 
306                 if(repeat==0) {
307                         if((op->type->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
308                                 wm_operator_register(C, op);
309                         else
310                                 WM_operator_free(op);
311                 }
312         }
313         else if(repeat==0)
314                 WM_operator_free(op);
315         
316         return retval;
317         
318 }
319
320 /* for running operators with frozen context (modal handlers, menus) */
321 int WM_operator_call(bContext *C, wmOperator *op)
322 {
323         return wm_operator_exec(C, op, 0);
324 }
325
326 /* do this operator again, put here so it can share above code */
327 int WM_operator_repeat(bContext *C, wmOperator *op)
328 {
329         return wm_operator_exec(C, op, 1);
330 }
331
332 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
333 {
334         wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname);    /* XXX operatortype names are static still. for debug */
335         
336         /* XXX adding new operator could be function, only happens here now */
337         op->type= ot;
338         BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
339         
340         /* initialize properties, either copy or create */
341         op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
342         if(properties && properties->data) {
343                 op->properties= IDP_CopyProperty(properties->data);
344         }
345         else {
346                 IDPropertyTemplate val = {0};
347                 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
348         }
349         RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
350
351         /* initialize error reports */
352         if (reports) {
353                 op->reports= reports; /* must be initialized already */
354         }
355         else {
356                 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
357                 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
358         }
359         
360         /* recursive filling of operator macro list */
361         if(ot->macro.first) {
362                 static wmOperator *motherop= NULL;
363                 wmOperatorTypeMacro *otmacro;
364                 
365                 /* ensure all ops are in execution order in 1 list */
366                 if(motherop==NULL) 
367                         motherop= op;
368                 
369                 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
370                         wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
371                         wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
372                         
373                         BLI_addtail(&motherop->macro, opm);
374                         opm->opm= motherop; /* pointer to mom, for modal() */
375                 }
376                 
377                 motherop= NULL;
378         }
379         
380         return op;
381 }
382
383 static void wm_operator_print(wmOperator *op)
384 {
385         char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
386         printf("%s\n", buf);
387         MEM_freeN(buf);
388 }
389
390 static void wm_region_mouse_co(bContext *C, wmEvent *event)
391 {
392         ARegion *ar= CTX_wm_region(C);
393         if(ar) {
394                 /* compatibility convention */
395                 event->mval[0]= event->x - ar->winrct.xmin;
396                 event->mval[1]= event->y - ar->winrct.ymin;
397         }
398 }
399
400 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports)
401 {
402         wmWindowManager *wm= CTX_wm_manager(C);
403         int retval= OPERATOR_PASS_THROUGH;
404
405         if(WM_operator_poll(C, ot)) {
406                 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
407                 
408                 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
409                         printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname); 
410                 
411                 if(op->type->invoke && event) {
412                         wm_region_mouse_co(C, event);
413                         retval= op->type->invoke(C, op, event);
414                 }
415                 else if(op->type->exec)
416                         retval= op->type->exec(C, op);
417                 else
418                         printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
419
420                 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
421                  * currently python only uses this */
422                 if(!(retval & OPERATOR_RUNNING_MODAL) && reports==NULL) {
423                         if(op->reports->list.first) /* only show the report if the report list was not given in the function */
424                                 uiPupMenuReports(C, op->reports);
425                 
426                 if (retval & OPERATOR_FINISHED) /* todo - this may conflict with the other wm_operator_print, if theres ever 2 prints for 1 action will may need to add modal check here */
427                         if(G.f & G_DEBUG)
428                                 wm_operator_print(op);
429                 }
430
431                 if(retval & OPERATOR_FINISHED) {
432                         op->customdata= NULL;
433
434                         if(ot->flag & OPTYPE_UNDO)
435                                 ED_undo_push_op(C, op);
436                         
437                         if((ot->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
438                                 wm_operator_register(C, op);
439                         else
440                                 WM_operator_free(op);
441                 }
442                 else if(retval & OPERATOR_RUNNING_MODAL) {
443                         /* grab cursor during blocking modal ops (X11) */
444                         if(ot->flag & OPTYPE_BLOCKING)
445                                 WM_cursor_grab(CTX_wm_window(C), 1, (U.uiflag & USER_CONTINUOUS_MOUSE));
446                 }
447                 else
448                         WM_operator_free(op);
449         }
450
451         return retval;
452 }
453
454 /* WM_operator_name_call is the main accessor function
455  * this is for python to access since its done the operator lookup
456  * 
457  * invokes operator in context */
458 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
459 {
460         wmWindow *window= CTX_wm_window(C);
461         wmEvent *event;
462         
463         int retval;
464
465         /* dummie test */
466         if(ot && C && window) {
467                 event= window->eventstate;
468                 switch(context) {
469                         
470                         case WM_OP_EXEC_REGION_WIN:
471                                 event= NULL;    /* pass on without break */
472                         case WM_OP_INVOKE_REGION_WIN: 
473                         {
474                                 /* forces operator to go to the region window, for header menus */
475                                 ARegion *ar= CTX_wm_region(C);
476                                 ScrArea *area= CTX_wm_area(C);
477                                 
478                                 if(area) {
479                                         ARegion *ar1= area->regionbase.first;
480                                         for(; ar1; ar1= ar1->next)
481                                                 if(ar1->regiontype==RGN_TYPE_WINDOW)
482                                                         break;
483                                         if(ar1)
484                                                 CTX_wm_region_set(C, ar1);
485                                 }
486                                 
487                                 retval= wm_operator_invoke(C, ot, event, properties, reports);
488                                 
489                                 /* set region back */
490                                 CTX_wm_region_set(C, ar);
491                                 
492                                 return retval;
493                         }
494                         case WM_OP_EXEC_AREA:
495                                 event= NULL;    /* pass on without break */
496                         case WM_OP_INVOKE_AREA:
497                         {
498                                         /* remove region from context */
499                                 ARegion *ar= CTX_wm_region(C);
500
501                                 CTX_wm_region_set(C, NULL);
502                                 retval= wm_operator_invoke(C, ot, event, properties, reports);
503                                 CTX_wm_region_set(C, ar);
504
505                                 return retval;
506                         }
507                         case WM_OP_EXEC_SCREEN:
508                                 event= NULL;    /* pass on without break */
509                         case WM_OP_INVOKE_SCREEN:
510                         {
511                                 /* remove region + area from context */
512                                 ARegion *ar= CTX_wm_region(C);
513                                 ScrArea *area= CTX_wm_area(C);
514
515                                 CTX_wm_region_set(C, NULL);
516                                 CTX_wm_area_set(C, NULL);
517                                 retval= wm_operator_invoke(C, ot, event, properties, reports);
518                                 CTX_wm_region_set(C, ar);
519                                 CTX_wm_area_set(C, area);
520
521                                 return retval;
522                         }
523                         case WM_OP_EXEC_DEFAULT:
524                                 event= NULL;    /* pass on without break */
525                         case WM_OP_INVOKE_DEFAULT:
526                                 return wm_operator_invoke(C, ot, event, properties, reports);
527                 }
528         }
529         
530         return 0;
531 }
532
533
534 /* invokes operator in context */
535 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
536 {
537         wmOperatorType *ot= WM_operatortype_find(opstring, 0);
538         if(ot)
539                 return wm_operator_call_internal(C, ot, context, properties, NULL);
540
541         return 0;
542 }
543
544 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
545    - wmOperatorType is used instead of operator name since python alredy has the operator type
546    - poll() must be called by python before this runs.
547    - reports can be passed to this function (so python can report them as exceptions)
548 */
549 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
550 {
551         int retval= OPERATOR_CANCELLED;
552
553 #if 0
554         wmOperator *op;
555         wmWindowManager *wm=    CTX_wm_manager(C);
556         op= wm_operator_create(wm, ot, properties, reports);
557
558         if (op->type->exec)
559                 retval= op->type->exec(C, op);
560         else
561                 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
562 #endif
563
564         retval= wm_operator_call_internal(C, ot, context, properties, reports);
565         
566         /* keep the reports around if needed later */
567         if (retval & OPERATOR_RUNNING_MODAL || ot->flag & OPTYPE_REGISTER)
568         {
569                 reports->flag |= RPT_FREE;
570         }
571         
572         return retval;
573 }
574
575
576 /* ********************* handlers *************** */
577
578 /* future extra customadata free? */
579 static void wm_event_free_handler(wmEventHandler *handler)
580 {
581         MEM_freeN(handler);
582 }
583
584 /* only set context when area/region is part of screen */
585 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
586 {
587         bScreen *screen= CTX_wm_screen(C);
588         
589         if(screen && handler->op) {
590                 if(handler->op_area==NULL)
591                         CTX_wm_area_set(C, NULL);
592                 else {
593                         ScrArea *sa;
594                         
595                         for(sa= screen->areabase.first; sa; sa= sa->next)
596                                 if(sa==handler->op_area)
597                                         break;
598                         if(sa==NULL) {
599                                 /* when changing screen layouts with running modal handlers (like render display), this
600                                    is not an error to print */
601                                 if(handler->op==NULL)
602                                         printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
603                         }
604                         else {
605                                 ARegion *ar;
606                                 CTX_wm_area_set(C, sa);
607                                 for(ar= sa->regionbase.first; ar; ar= ar->next)
608                                         if(ar==handler->op_region)
609                                                 break;
610                                 /* XXX no warning print here, after full-area and back regions are remade */
611                                 if(ar)
612                                         CTX_wm_region_set(C, ar);
613                         }
614                 }
615         }
616 }
617
618 /* called on exit or remove area, only here call cancel callback */
619 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
620 {
621         wmEventHandler *handler;
622         
623         /* C is zero on freeing database, modal handlers then already were freed */
624         while((handler=handlers->first)) {
625                 BLI_remlink(handlers, handler);
626                 
627                 if(handler->op) {
628                         if(handler->op->type->cancel) {
629                                 ScrArea *area= CTX_wm_area(C);
630                                 ARegion *region= CTX_wm_region(C);
631                                 
632                                 wm_handler_op_context(C, handler);
633
634                                 handler->op->type->cancel(C, handler->op);
635
636                                 CTX_wm_area_set(C, area);
637                                 CTX_wm_region_set(C, region);
638                         }
639
640                         WM_cursor_grab(CTX_wm_window(C), 0, -1);
641                         WM_operator_free(handler->op);
642                 }
643                 else if(handler->ui_remove) {
644                         ScrArea *area= CTX_wm_area(C);
645                         ARegion *region= CTX_wm_region(C);
646                         ARegion *menu= CTX_wm_menu(C);
647                         
648                         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
649                         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
650                         if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
651
652                         handler->ui_remove(C, handler->ui_userdata);
653
654                         CTX_wm_area_set(C, area);
655                         CTX_wm_region_set(C, region);
656                         CTX_wm_menu_set(C, menu);
657                 }
658
659                 wm_event_free_handler(handler);
660         }
661 }
662
663 /* do userdef mappings */
664 static int wm_userdef_event_map(int kmitype)
665 {
666         switch(kmitype) {
667                 case SELECTMOUSE:
668                         if(U.flag & USER_LMOUSESELECT)
669                                 return LEFTMOUSE;
670                         else
671                                 return RIGHTMOUSE;
672                         
673                 case ACTIONMOUSE:
674                         if(U.flag & USER_LMOUSESELECT)
675                                 return RIGHTMOUSE;
676                         else
677                                 return LEFTMOUSE;
678                         
679                 case WHEELOUTMOUSE:
680                         if(U.uiflag & USER_WHEELZOOMDIR)
681                                 return WHEELUPMOUSE;
682                         else
683                                 return WHEELDOWNMOUSE;
684                         
685                 case WHEELINMOUSE:
686                         if(U.uiflag & USER_WHEELZOOMDIR)
687                                 return WHEELDOWNMOUSE;
688                         else
689                                 return WHEELUPMOUSE;
690                         
691                 case EVT_TWEAK_A:
692                         if(U.flag & USER_LMOUSESELECT)
693                                 return EVT_TWEAK_R;
694                         else
695                                 return EVT_TWEAK_L;
696                         
697                 case EVT_TWEAK_S:
698                         if(U.flag & USER_LMOUSESELECT)
699                                 return EVT_TWEAK_L;
700                         else
701                                 return EVT_TWEAK_R;
702         }
703         
704         return kmitype;
705 }
706
707 static int wm_eventmatch(wmEvent *winevent, wmKeymapItem *kmi)
708 {
709         int kmitype= wm_userdef_event_map(kmi->type);
710
711         if(kmi->inactive) return 0;
712
713         /* exception for middlemouse emulation */
714         if((U.flag & USER_TWOBUTTONMOUSE) && (kmi->type == MIDDLEMOUSE)) {
715                 if(winevent->type == LEFTMOUSE && winevent->alt) {
716                         wmKeymapItem tmp= *kmi;
717
718                         tmp.type= winevent->type;
719                         tmp.alt= winevent->alt;
720                         if(wm_eventmatch(winevent, &tmp))
721                                 return 1;
722                 }
723         }
724
725         /* the matching rules */
726         if(kmitype==KM_TEXTINPUT)
727                 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
728         if(kmitype!=KM_ANY)
729                 if(winevent->type!=kmitype) return 0;
730         
731         if(kmi->val!=KM_ANY)
732                 if(winevent->val!=kmi->val) return 0;
733         
734         /* modifiers also check bits, so it allows modifier order */
735         if(kmi->shift!=KM_ANY)
736                 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
737         if(kmi->ctrl!=KM_ANY)
738                 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
739         if(kmi->alt!=KM_ANY)
740                 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
741         if(kmi->oskey!=KM_ANY)
742                 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
743         
744         if(kmi->keymodifier)
745                 if(winevent->keymodifier!=kmi->keymodifier) return 0;
746                 
747         /* key modifiers always check when event has it */
748         /* otherwise regular keypresses with keymodifier still work */
749         if(winevent->keymodifier)
750                 if(ISTEXTINPUT(winevent->type)) 
751                         if(winevent->keymodifier!=kmi->keymodifier) return 0;
752         
753         return 1;
754 }
755
756 static int wm_event_always_pass(wmEvent *event)
757 {
758         /* some events we always pass on, to ensure proper communication */
759         return ELEM5(event->type, TIMER, TIMER0, TIMER1, TIMER2, TIMERJOBS);
760 }
761
762 /* operator exists */
763 static void wm_event_modalkeymap(wmOperator *op, wmEvent *event)
764 {
765         if(op->type->modalkeymap) {
766                 wmKeymapItem *kmi;
767                 
768                 for(kmi= op->type->modalkeymap->keymap.first; kmi; kmi= kmi->next) {
769                         if(wm_eventmatch(event, kmi)) {
770                                         
771                                 event->type= EVT_MODAL_MAP;
772                                 event->val= kmi->propvalue;
773                         }
774                 }
775         }
776 }
777
778 /* Warning: this function removes a modal handler, when finished */
779 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
780 {
781         int retval= OPERATOR_PASS_THROUGH;
782         
783         /* derived, modal or blocking operator */
784         if(handler->op) {
785                 wmOperator *op= handler->op;
786                 wmOperatorType *ot= op->type;
787
788                 if(ot->modal) {
789                         /* we set context to where modal handler came from */
790                         ScrArea *area= CTX_wm_area(C);
791                         ARegion *region= CTX_wm_region(C);
792                         
793                         wm_handler_op_context(C, handler);
794                         wm_region_mouse_co(C, event);
795                         wm_event_modalkeymap(op, event);
796                         
797                         retval= ot->modal(C, op, event);
798
799                         /* putting back screen context, reval can pass trough after modal failures! */
800                         if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
801                                 CTX_wm_area_set(C, area);
802                                 CTX_wm_region_set(C, region);
803                         }
804                         else {
805                                 /* this special cases is for areas and regions that get removed */
806                                 CTX_wm_area_set(C, NULL);
807                                 CTX_wm_region_set(C, NULL);
808                         }
809
810                         if(!(retval & OPERATOR_RUNNING_MODAL))
811                                 if(op->reports->list.first)
812                                         uiPupMenuReports(C, op->reports);
813
814                         if (retval & OPERATOR_FINISHED) {
815                                 if(G.f & G_DEBUG)
816                                         wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
817                         }                       
818
819                         if(retval & OPERATOR_FINISHED) {
820                                 op->customdata= NULL;
821
822                                 if(ot->flag & OPTYPE_UNDO)
823                                         ED_undo_push_op(C, op);
824                                 
825                                 if((ot->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
826                                         wm_operator_register(C, op);
827                                 else
828                                         WM_operator_free(op);
829                                 handler->op= NULL;
830                         }
831                         else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
832                                 WM_operator_free(op);
833                                 handler->op= NULL;
834                         }
835                         
836                         /* remove modal handler, operator itself should have been cancelled and freed */
837                         if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
838                                 WM_cursor_grab(CTX_wm_window(C), 0, -1);
839
840                                 BLI_remlink(handlers, handler);
841                                 wm_event_free_handler(handler);
842                                 
843                                 /* prevent silly errors from operator users */
844                                 //retval &= ~OPERATOR_PASS_THROUGH;
845                         }
846                         
847                 }
848                 else
849                         printf("wm_handler_operator_call error\n");
850         }
851         else {
852                 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
853
854                 if(ot)
855                         retval= wm_operator_invoke(C, ot, event, properties, NULL);
856         }
857
858         if(retval & OPERATOR_PASS_THROUGH)
859                 return WM_HANDLER_CONTINUE;
860
861         return WM_HANDLER_BREAK;
862 }
863
864 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event)
865 {
866         ScrArea *area= CTX_wm_area(C);
867         ARegion *region= CTX_wm_region(C);
868         ARegion *menu= CTX_wm_menu(C);
869         int retval, always_pass;
870                         
871         /* we set context to where ui handler came from */
872         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
873         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
874         if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
875
876         /* in advance to avoid access to freed event on window close */
877         always_pass= wm_event_always_pass(event);
878
879         retval= handler->ui_handle(C, event, handler->ui_userdata);
880
881         /* putting back screen context */
882         if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
883                 CTX_wm_area_set(C, area);
884                 CTX_wm_region_set(C, region);
885                 CTX_wm_menu_set(C, menu);
886         }
887         else {
888                 /* this special cases is for areas and regions that get removed */
889                 CTX_wm_area_set(C, NULL);
890                 CTX_wm_region_set(C, NULL);
891                 CTX_wm_menu_set(C, NULL);
892         }
893
894         if(retval == WM_UI_HANDLER_BREAK)
895                 return WM_HANDLER_BREAK;
896
897         return WM_HANDLER_CONTINUE;
898 }
899
900 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
901 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
902 {
903         SpaceFile *sfile;
904         int action= WM_HANDLER_CONTINUE;
905         
906         if(event->type != EVT_FILESELECT)
907                 return action;
908         if(handler->op != (wmOperator *)event->customdata)
909                 return action;
910         
911         switch(event->val) {
912                 case EVT_FILESELECT_OPEN: 
913                 case EVT_FILESELECT_FULL_OPEN: 
914                         {       
915                                 if(event->val==EVT_FILESELECT_OPEN)
916                                         ED_area_newspace(C, handler->op_area, SPACE_FILE);
917                                 else
918                                         ED_screen_full_newspace(C, handler->op_area, SPACE_FILE);
919                                 
920                                 /* settings for filebrowser, sfile is not operator owner but sends events */
921                                 sfile= (SpaceFile*)CTX_wm_space_data(C);
922                                 sfile->op= handler->op;
923
924                                 ED_fileselect_set_params(sfile);
925                                 
926                                 action= WM_HANDLER_BREAK;
927                         }
928                         break;
929                         
930                 case EVT_FILESELECT_EXEC:
931                 case EVT_FILESELECT_CANCEL:
932                         {
933                                 /* XXX validate area and region? */
934                                 bScreen *screen= CTX_wm_screen(C);
935                                 char *path= RNA_string_get_alloc(handler->op->ptr, "path", NULL, 0);
936                                 
937                                 if(screen != handler->filescreen)
938                                         ED_screen_full_prevspace(C);
939                                 else
940                                         ED_area_prevspace(C);
941                                 
942                                 /* remlink now, for load file case */
943                                 BLI_remlink(handlers, handler);
944                                 
945                                 wm_handler_op_context(C, handler);
946
947                                 if(event->val==EVT_FILESELECT_EXEC) {
948                                         /* a bit weak, might become arg for WM_event_fileselect? */
949                                         /* XXX also extension code in image-save doesnt work for this yet */
950                                         if(strncmp(handler->op->type->name, "Save", 4)==0) {
951                                                 /* this gives ownership to pupmenu */
952                                                 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
953                                         }
954                                         else {
955                                                 int retval= handler->op->type->exec(C, handler->op);
956                                                 
957                                                 if (retval & OPERATOR_FINISHED)
958                                                         if(G.f & G_DEBUG)
959                                                                 wm_operator_print(handler->op);
960                                                 
961                                                 WM_operator_free(handler->op);
962                                         }
963                                 }
964                                 else {
965                                         if(handler->op->type->cancel)
966                                                 handler->op->type->cancel(C, handler->op);
967
968                                         WM_operator_free(handler->op);
969                                 }
970
971                                 CTX_wm_area_set(C, NULL);
972                                 
973                                 wm_event_free_handler(handler);
974                                 if(path)
975                                         MEM_freeN(path);
976                                 
977                                 action= WM_HANDLER_BREAK;
978                         }
979                         break;
980         }
981         
982         return action;
983 }
984
985 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
986 {
987         if(handler->bbwin) {
988                 if(handler->bblocal) {
989                         rcti rect= *handler->bblocal;
990                         BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
991
992                         if(BLI_in_rcti(&rect, event->x, event->y))
993                                 return 1;
994                         else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
995                                 return 1;
996                         else
997                                 return 0;
998                 }
999                 else {
1000                         if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1001                                 return 1;
1002                         else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1003                                 return 1;
1004                         else
1005                                 return 0;
1006                 }
1007         }
1008         return 1;
1009 }
1010
1011 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1012 {
1013         wmEventHandler *handler, *nexthandler;
1014         int action= WM_HANDLER_CONTINUE;
1015         int always_pass;
1016
1017         if(handlers==NULL) return action;
1018
1019         /* modal handlers can get removed in this loop, we keep the loop this way */
1020         for(handler= handlers->first; handler; handler= nexthandler) {
1021                 nexthandler= handler->next;
1022
1023                 /* optional boundbox */
1024                 if(handler_boundbox_test(handler, event)) {
1025                         /* in advance to avoid access to freed event on window close */
1026                         always_pass= wm_event_always_pass(event);
1027                 
1028                         /* modal+blocking handler */
1029                         if(handler->flag & WM_HANDLER_BLOCKING)
1030                                 action= WM_HANDLER_BREAK;
1031
1032                         if(handler->keymap) {
1033                                 wmKeyMap *keymap= handler->keymap;
1034                                 wmKeymapItem *kmi;
1035                                 
1036                                 if(!keymap->poll || keymap->poll(C)) {
1037                                         for(kmi= keymap->keymap.first; kmi; kmi= kmi->next) {
1038                                                 if(wm_eventmatch(event, kmi)) {
1039                                                         
1040                                                         event->keymap_idname= kmi->idname;      /* weak, but allows interactive callback to not use rawkey */
1041                                                         
1042                                                         action= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1043                                                         if(action==WM_HANDLER_BREAK)  /* not always_pass here, it denotes removed handler */
1044                                                                 break;
1045                                                 }
1046                                         }
1047                                 }
1048                         }
1049                         else if(handler->ui_handle) {
1050                                 action= wm_handler_ui_call(C, handler, event);
1051                         }
1052                         else if(handler->type==WM_HANDLER_FILESELECT) {
1053                                 /* screen context changes here */
1054                                 action= wm_handler_fileselect_call(C, handlers, handler, event);
1055                         }
1056                         else {
1057                                 /* modal, swallows all */
1058                                 action= wm_handler_operator_call(C, handlers, handler, event, NULL);
1059                         }
1060
1061                         if(action==WM_HANDLER_BREAK) {
1062                                 if(always_pass)
1063                                         action= WM_HANDLER_CONTINUE;
1064                                 else
1065                                         break;
1066                         }
1067                 }
1068                 
1069                 /* fileread case */
1070                 if(CTX_wm_window(C)==NULL)
1071                         break;
1072         }
1073         return action;
1074 }
1075
1076 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1077 {
1078         if(wm_event_always_pass(event))
1079                 return 1;
1080         if(BLI_in_rcti(rect, event->x, event->y))
1081            return 1;
1082         if(event->type==MOUSEMOVE) {
1083                 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1084                         return 1;
1085                 }
1086                 return 0;
1087         }
1088         return 0;
1089 }
1090
1091 static ScrArea *area_event_inside(bContext *C, int x, int y)
1092 {
1093         bScreen *screen= CTX_wm_screen(C);
1094         ScrArea *sa;
1095         
1096         if(screen)
1097                 for(sa= screen->areabase.first; sa; sa= sa->next)
1098                         if(BLI_in_rcti(&sa->totrct, x, y))
1099                                 return sa;
1100         return NULL;
1101 }
1102
1103 static ARegion *region_event_inside(bContext *C, int x, int y)
1104 {
1105         bScreen *screen= CTX_wm_screen(C);
1106         ScrArea *area= CTX_wm_area(C);
1107         ARegion *ar;
1108         
1109         if(screen && area)
1110                 for(ar= area->regionbase.first; ar; ar= ar->next)
1111                         if(BLI_in_rcti(&ar->winrct, x, y))
1112                                 return ar;
1113         return NULL;
1114 }
1115
1116 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1117 {
1118         if(ar) {
1119                 for(; pc; pc= pc->next) {
1120                         if(pc->poll(C)) {
1121                                 wmWindow *win= CTX_wm_window(C);
1122                                 win->screen->do_draw_paintcursor= 1;
1123
1124                                 if(win->drawmethod != USER_DRAW_TRIPLE)
1125                                         ED_region_tag_redraw(ar);
1126                         }
1127                 }
1128         }
1129 }
1130
1131 /* called on mousemove, check updates for paintcursors */
1132 /* context was set on active area and region */
1133 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1134 {
1135         wmWindowManager *wm= CTX_wm_manager(C);
1136         
1137         if(wm->paintcursors.first) {
1138                 ARegion *ar= CTX_wm_region(C);
1139                 if(ar)
1140                         wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1141                 
1142                 /* if previous position was not in current region, we have to set a temp new context */
1143                 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1144                         ScrArea *sa= CTX_wm_area(C);
1145                         
1146                         CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1147                         CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1148
1149                         wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1150                         
1151                         CTX_wm_area_set(C, sa);
1152                         CTX_wm_region_set(C, ar);
1153                 }
1154         }
1155 }
1156
1157 /* called in main loop */
1158 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
1159 void wm_event_do_handlers(bContext *C)
1160 {
1161         wmWindow *win;
1162
1163         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1164                 wmEvent *event;
1165                 
1166                 if( win->screen==NULL )
1167                         wm_event_free_all(win);
1168                 
1169                 while( (event= win->queue.first) ) {
1170                         int action;
1171
1172                         CTX_wm_window_set(C, win);
1173                         
1174                         /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1175                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1176                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1177                         
1178                         /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1179                         wm_window_make_drawable(C, win);
1180                         
1181                         /* first we do priority handlers, modal + some limited keymaps */
1182                         action= wm_handlers_do(C, event, &win->modalhandlers);
1183                         
1184                         /* fileread case */
1185                         if(CTX_wm_window(C)==NULL)
1186                                 return;
1187                         
1188                         /* builtin tweak, if action is break it removes tweak */
1189                         wm_tweakevent_test(C, event, action);
1190
1191                         if(action==WM_HANDLER_CONTINUE) {
1192                                 ScrArea *sa;
1193                                 ARegion *ar;
1194                                 int doit= 0;
1195                                 
1196                                 /* XXX to solve, here screen handlers? */
1197                                 if(event->type==MOUSEMOVE) {
1198                                         /* state variables in screen, cursors */
1199                                         ED_screen_set_subwinactive(win, event); 
1200                                         /* for regions having custom cursors */
1201                                         wm_paintcursor_test(C, event);
1202                                 }
1203
1204                                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1205                                         if(wm_event_inside_i(event, &sa->totrct)) {
1206                                                 CTX_wm_area_set(C, sa);
1207
1208                                                 if(action==WM_HANDLER_CONTINUE) {
1209                                                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
1210                                                                 if(wm_event_inside_i(event, &ar->winrct)) {
1211                                                                         CTX_wm_region_set(C, ar);
1212                                                                         action= wm_handlers_do(C, event, &ar->handlers);
1213
1214                                                                         doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1215                                                                         
1216                                                                         if(action==WM_HANDLER_BREAK)
1217                                                                                 break;
1218                                                                 }
1219                                                         }
1220                                                 }
1221
1222                                                 CTX_wm_region_set(C, NULL);
1223
1224                                                 if(action==WM_HANDLER_CONTINUE)
1225                                                         action= wm_handlers_do(C, event, &sa->handlers);
1226
1227                                                 CTX_wm_area_set(C, NULL);
1228
1229                                                 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1230                                         }
1231                                 }
1232                                 
1233                                 if(action==WM_HANDLER_CONTINUE) {
1234                                         /* also some non-modal handlers need active area/region */
1235                                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1236                                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1237
1238                                         action= wm_handlers_do(C, event, &win->handlers);
1239
1240                                         /* fileread case */
1241                                         if(CTX_wm_window(C)==NULL)
1242                                                 return;
1243                                 }
1244
1245                                 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad? 
1246                                    doing it on ghost queue gives errors when mousemoves go over area borders */
1247                                 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1248                                         win->eventstate->prevx= event->x;
1249                                         win->eventstate->prevy= event->y;
1250                                 }
1251                         }
1252                         
1253                         /* unlink and free here, blender-quit then frees all */
1254                         BLI_remlink(&win->queue, event);
1255                         wm_event_free(event);
1256                         
1257                 }
1258                 
1259                 /* only add mousemove when queue was read entirely */
1260                 if(win->addmousemove && win->eventstate) {
1261                         wmEvent event= *(win->eventstate);
1262                         event.type= MOUSEMOVE;
1263                         event.prevx= event.x;
1264                         event.prevy= event.y;
1265                         wm_event_add(win, &event);
1266                         win->addmousemove= 0;
1267                 }
1268                 
1269                 CTX_wm_window_set(C, NULL);
1270         }
1271 }
1272
1273 /* ********** filesector handling ************ */
1274
1275 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1276 {
1277         /* add to all windows! */
1278         wmWindow *win;
1279         
1280         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1281                 wmEvent event= *win->eventstate;
1282                 
1283                 event.type= EVT_FILESELECT;
1284                 event.val= eventval;
1285                 event.customdata= ophandle;             // only as void pointer type check
1286
1287                 wm_event_add(win, &event);
1288         }
1289 }
1290
1291 /* operator is supposed to have a filled "path" property */
1292 /* optional property: filetype (XXX enum?) */
1293
1294 /* Idea is to keep a handler alive on window queue, owning the operator.
1295    The filewindow can send event to make it execute, thus ensuring
1296    executing happens outside of lower level queues, with UI refreshed. 
1297    Should also allow multiwin solutions */
1298
1299 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1300 {
1301         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1302         wmWindow *win= CTX_wm_window(C);
1303         int full= 1;    // XXX preset?
1304         
1305         handler->type= WM_HANDLER_FILESELECT;
1306         handler->op= op;
1307         handler->op_area= CTX_wm_area(C);
1308         handler->op_region= CTX_wm_region(C);
1309         handler->filescreen= CTX_wm_screen(C);
1310         
1311         BLI_addhead(&win->modalhandlers, handler);
1312         
1313         WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1314 }
1315
1316 /* lets not expose struct outside wm? */
1317 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1318 {
1319         handler->flag= flag;
1320 }
1321
1322 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1323 {
1324         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1325         wmWindow *win= CTX_wm_window(C);
1326         
1327         /* operator was part of macro */
1328         if(op->opm) {
1329                 /* give the mother macro to the handler */
1330                 handler->op= op->opm;
1331                 /* mother macro opm becomes the macro element */
1332                 handler->op->opm= op;
1333         }
1334         else
1335                 handler->op= op;
1336         
1337         handler->op_area= CTX_wm_area(C);               /* means frozen screen context for modal handlers! */
1338         handler->op_region= CTX_wm_region(C);
1339         
1340         BLI_addhead(&win->modalhandlers, handler);
1341
1342         return handler;
1343 }
1344
1345 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1346 {
1347         wmEventHandler *handler;
1348
1349         if(!keymap) {
1350                 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1351                 return NULL;
1352         }
1353
1354         /* only allow same keymap once */
1355         for(handler= handlers->first; handler; handler= handler->next)
1356                 if(handler->keymap==keymap)
1357                         return handler;
1358         
1359         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1360         BLI_addtail(handlers, handler);
1361         handler->keymap= keymap;
1362
1363         return handler;
1364 }
1365
1366 /* priorities not implemented yet, for time being just insert in begin of list */
1367 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int priority)
1368 {
1369         wmEventHandler *handler;
1370         
1371         WM_event_remove_keymap_handler(handlers, keymap);
1372         
1373         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1374         BLI_addhead(handlers, handler);
1375         handler->keymap= keymap;
1376         
1377         return handler;
1378 }
1379
1380 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1381 {
1382         wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1383         
1384         if(handler) {
1385                 handler->bblocal= bblocal;
1386                 handler->bbwin= bbwin;
1387         }
1388         return handler;
1389 }
1390
1391 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1392 {
1393         wmEventHandler *handler;
1394         
1395         for(handler= handlers->first; handler; handler= handler->next) {
1396                 if(handler->keymap==keymap) {
1397                         BLI_remlink(handlers, handler);
1398                         wm_event_free_handler(handler);
1399                         break;
1400                 }
1401         }
1402 }
1403
1404 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1405 {
1406         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1407         handler->ui_handle= func;
1408         handler->ui_remove= remove;
1409         handler->ui_userdata= userdata;
1410         handler->ui_area= (C)? CTX_wm_area(C): NULL;
1411         handler->ui_region= (C)? CTX_wm_region(C): NULL;
1412         handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1413         
1414         BLI_addhead(handlers, handler);
1415         
1416         return handler;
1417 }
1418
1419 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1420 {
1421         wmEventHandler *handler;
1422         
1423         for(handler= handlers->first; handler; handler= handler->next) {
1424                 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1425                         BLI_remlink(handlers, handler);
1426                         wm_event_free_handler(handler);
1427                         break;
1428                 }
1429         }
1430 }
1431
1432 void WM_event_add_mousemove(bContext *C)
1433 {
1434         wmWindow *window= CTX_wm_window(C);
1435         
1436         window->addmousemove= 1;
1437 }
1438
1439 /* for modal callbacks, check configuration for how to interpret exit with tweaks  */
1440 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
1441 {
1442         /* user preset or keymap? dunno... */
1443         int tweak_modal= (U.flag & USER_DRAGIMMEDIATE)==0;
1444         
1445         switch(tweak_event) {
1446                 case EVT_TWEAK_L:
1447                 case EVT_TWEAK_M:
1448                 case EVT_TWEAK_R:
1449                         if(evt->val==tweak_modal)
1450                                 return 1;
1451                 default:
1452                         /* this case is when modal callcback didnt get started with a tweak */
1453                         if(evt->val)
1454                                 return 1;
1455         }
1456         return 0;
1457 }
1458
1459
1460 /* ********************* ghost stuff *************** */
1461
1462 static int convert_key(GHOST_TKey key) 
1463 {
1464         if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
1465                 return (AKEY + ((int) key - GHOST_kKeyA));
1466         } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
1467                 return (ZEROKEY + ((int) key - GHOST_kKey0));
1468         } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
1469                 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
1470         } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
1471                 return (F1KEY + ((int) key - GHOST_kKeyF1));
1472         } else {
1473                 switch (key) {
1474                         case GHOST_kKeyBackSpace:               return BACKSPACEKEY;
1475                         case GHOST_kKeyTab:                             return TABKEY;
1476                         case GHOST_kKeyLinefeed:                return LINEFEEDKEY;
1477                         case GHOST_kKeyClear:                   return 0;
1478                         case GHOST_kKeyEnter:                   return RETKEY;
1479                                 
1480                         case GHOST_kKeyEsc:                             return ESCKEY;
1481                         case GHOST_kKeySpace:                   return SPACEKEY;
1482                         case GHOST_kKeyQuote:                   return QUOTEKEY;
1483                         case GHOST_kKeyComma:                   return COMMAKEY;
1484                         case GHOST_kKeyMinus:                   return MINUSKEY;
1485                         case GHOST_kKeyPeriod:                  return PERIODKEY;
1486                         case GHOST_kKeySlash:                   return SLASHKEY;
1487                                 
1488                         case GHOST_kKeySemicolon:               return SEMICOLONKEY;
1489                         case GHOST_kKeyEqual:                   return EQUALKEY;
1490                                 
1491                         case GHOST_kKeyLeftBracket:             return LEFTBRACKETKEY;
1492                         case GHOST_kKeyRightBracket:    return RIGHTBRACKETKEY;
1493                         case GHOST_kKeyBackslash:               return BACKSLASHKEY;
1494                         case GHOST_kKeyAccentGrave:             return ACCENTGRAVEKEY;
1495                                 
1496                         case GHOST_kKeyLeftShift:               return LEFTSHIFTKEY;
1497                         case GHOST_kKeyRightShift:              return RIGHTSHIFTKEY;
1498                         case GHOST_kKeyLeftControl:             return LEFTCTRLKEY;
1499                         case GHOST_kKeyRightControl:    return RIGHTCTRLKEY;
1500                         case GHOST_kKeyCommand:                 return COMMANDKEY;
1501                         case GHOST_kKeyLeftAlt:                 return LEFTALTKEY;
1502                         case GHOST_kKeyRightAlt:                return RIGHTALTKEY;
1503                                 
1504                         case GHOST_kKeyCapsLock:                return CAPSLOCKKEY;
1505                         case GHOST_kKeyNumLock:                 return 0;
1506                         case GHOST_kKeyScrollLock:              return 0;
1507                                 
1508                         case GHOST_kKeyLeftArrow:               return LEFTARROWKEY;
1509                         case GHOST_kKeyRightArrow:              return RIGHTARROWKEY;
1510                         case GHOST_kKeyUpArrow:                 return UPARROWKEY;
1511                         case GHOST_kKeyDownArrow:               return DOWNARROWKEY;
1512                                 
1513                         case GHOST_kKeyPrintScreen:             return 0;
1514                         case GHOST_kKeyPause:                   return PAUSEKEY;
1515                                 
1516                         case GHOST_kKeyInsert:                  return INSERTKEY;
1517                         case GHOST_kKeyDelete:                  return DELKEY;
1518                         case GHOST_kKeyHome:                    return HOMEKEY;
1519                         case GHOST_kKeyEnd:                             return ENDKEY;
1520                         case GHOST_kKeyUpPage:                  return PAGEUPKEY;
1521                         case GHOST_kKeyDownPage:                return PAGEDOWNKEY;
1522                                 
1523                         case GHOST_kKeyNumpadPeriod:    return PADPERIOD;
1524                         case GHOST_kKeyNumpadEnter:             return PADENTER;
1525                         case GHOST_kKeyNumpadPlus:              return PADPLUSKEY;
1526                         case GHOST_kKeyNumpadMinus:             return PADMINUS;
1527                         case GHOST_kKeyNumpadAsterisk:  return PADASTERKEY;
1528                         case GHOST_kKeyNumpadSlash:             return PADSLASHKEY;
1529                                 
1530                         case GHOST_kKeyGrLess:              return GRLESSKEY; 
1531                                 
1532                         default:
1533                                 return UNKNOWNKEY;      /* GHOST_kKeyUnknown */
1534                 }
1535         }
1536 }
1537
1538 /* adds customdata to event */
1539 static void update_tablet_data(wmWindow *win, wmEvent *event)
1540 {
1541         const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
1542         
1543         /* if there's tablet data from an active tablet device then add it */
1544         if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
1545                 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
1546                 
1547                 wmtab->Active = (int)td->Active;
1548                 wmtab->Pressure = td->Pressure;
1549                 wmtab->Xtilt = td->Xtilt;
1550                 wmtab->Ytilt = td->Ytilt;
1551                 
1552                 event->custom= EVT_DATA_TABLET;
1553                 event->customdata= wmtab;
1554                 event->customdatafree= 1;
1555         } 
1556 }
1557
1558
1559 /* windows store own event queues, no bContext here */
1560 void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
1561 {
1562         wmEvent event, *evt= win->eventstate;
1563         
1564         /* initialize and copy state (only mouse x y and modifiers) */
1565         event= *evt;
1566         
1567         switch (type) {
1568                 /* mouse move */
1569                 case GHOST_kEventCursorMove: {
1570                         if(win->active) {
1571                                 GHOST_TEventCursorData *cd= customdata;
1572 #if defined(__APPLE__) && defined(GHOST_COCOA)
1573                                 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
1574                                 event.type= MOUSEMOVE;
1575                                 event.x= evt->x = cd->x;
1576                                 event.y = evt->y = cd->y;
1577 #else
1578                                 int cx, cy;
1579                                 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
1580                                 event.type= MOUSEMOVE;
1581                                 event.x= evt->x= cx;
1582                                 event.y= evt->y= (win->sizey-1) - cy;
1583 #endif
1584                                 update_tablet_data(win, &event);
1585                                 wm_event_add(win, &event);
1586                         }
1587                         break;
1588                 }
1589                 /* mouse button */
1590                 case GHOST_kEventButtonDown:
1591                 case GHOST_kEventButtonUp: {
1592                         GHOST_TEventButtonData *bd= customdata;
1593                         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 */
1594                         
1595                         if (bd->button == GHOST_kButtonMaskLeft)
1596                                 event.type= LEFTMOUSE;
1597                         else if (bd->button == GHOST_kButtonMaskRight)
1598                                 event.type= RIGHTMOUSE;
1599                         else if (bd->button == GHOST_kButtonMaskButton4)
1600                                 event.type= BUTTON4MOUSE;
1601                         else if (bd->button == GHOST_kButtonMaskButton5)
1602                                 event.type= BUTTON5MOUSE;
1603                         else
1604                                 event.type= MIDDLEMOUSE;
1605                         
1606                         update_tablet_data(win, &event);
1607                         wm_event_add(win, &event);
1608                         
1609                         break;
1610                 }
1611                 /* keyboard */
1612                 case GHOST_kEventKeyDown:
1613                 case GHOST_kEventKeyUp: {
1614                         GHOST_TEventKeyData *kd= customdata;
1615                         event.type= convert_key(kd->key);
1616                         event.ascii= kd->ascii;
1617                         event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
1618                         
1619                         /* exclude arrow keys, esc, etc from text input */
1620                         if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
1621                                 event.ascii= '\0';
1622                         
1623                         /* modifiers */
1624                         if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
1625                                 event.shift= evt->shift= (event.val==KM_PRESS);
1626                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
1627                                    event.shift= evt->shift = 3;         // define?
1628                         } 
1629                         else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
1630                                 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
1631                                 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
1632                                    event.ctrl= evt->ctrl = 3;           // define?
1633                         } 
1634                         else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
1635                                 event.alt= evt->alt= (event.val==KM_PRESS);
1636                                 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
1637                                    event.alt= evt->alt = 3;             // define?
1638                         } 
1639                         else if (event.type==COMMANDKEY) {
1640                                 event.oskey= evt->oskey= (event.val==KM_PRESS);
1641                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
1642                                    event.oskey= evt->oskey = 3;         // define?
1643                         }
1644                         else {
1645                                 if(event.val==KM_PRESS && event.keymodifier==0)
1646                                         evt->keymodifier= event.type; /* only set in eventstate, for next event */
1647                                 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
1648                                         event.keymodifier= evt->keymodifier= 0;
1649                         }
1650
1651                         /* this case happens on some systems that on holding a key pressed,
1652                            generate press events without release, we still want to keep the
1653                            modifier in win->eventstate, but for the press event of the same
1654                            key we don't want the key modifier */
1655                         if(event.keymodifier == event.type)
1656                                 event.keymodifier= 0;
1657                         
1658                         /* if test_break set, it catches this. XXX Keep global for now? */
1659                         if(event.type==ESCKEY)
1660                                 G.afbreek= 1;
1661
1662                         wm_event_add(win, &event);
1663                         
1664                         break;
1665                 }
1666                         
1667                 case GHOST_kEventWheel: {
1668                         GHOST_TEventWheelData* wheelData = customdata;
1669                         
1670                         if (wheelData->z > 0)
1671                                 event.type= WHEELUPMOUSE;
1672                         else
1673                                 event.type= WHEELDOWNMOUSE;
1674                         
1675                         event.val= KM_PRESS;
1676                         wm_event_add(win, &event);
1677                         
1678                         break;
1679                 }
1680                 case GHOST_kEventTimer: {
1681                         event.type= TIMER;
1682                         event.custom= EVT_DATA_TIMER;
1683                         event.customdata= customdata;
1684                         wm_event_add(win, &event);
1685
1686                         break;
1687                 }
1688
1689                 case GHOST_kEventUnknown:
1690                 case GHOST_kNumEventTypes:
1691                         break;
1692         }
1693 }