2.5: Restored statistics in the info header.
[blender.git] / source / blender / windowmanager / intern / wm_event_system.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 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(note->category == NC_SCENE || note->category == NC_OBJECT) {
181                                 ED_info_stats_clear(CTX_data_scene(C));
182                                 WM_event_add_notifier(C, NC_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, ipos, 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 static 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         if(ot->poll)
274                 return ot->poll(C);
275         
276         return 1;
277 }
278
279 /* if repeat is true, it doesn't register again, nor does it free */
280 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
281 {
282         int retval= OPERATOR_CANCELLED;
283         
284         if(op==NULL || op->type==NULL)
285                 return retval;
286         
287         if(0==wm_operator_poll(C, op->type))
288                 return retval;
289         
290         if(op->type->exec)
291                 retval= op->type->exec(C, op);
292         
293         if(!(retval & OPERATOR_RUNNING_MODAL))
294                 if(op->reports->list.first)
295                         uiPupMenuReports(C, op->reports);
296         
297         if(retval & OPERATOR_FINISHED) {
298                 if(op->type->flag & OPTYPE_UNDO)
299                         ED_undo_push_op(C, op);
300                 
301                 if(repeat==0) {
302                         if((op->type->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
303                                 wm_operator_register(C, op);
304                         else
305                                 WM_operator_free(op);
306                 }
307         }
308         else if(repeat==0)
309                 WM_operator_free(op);
310         
311         return retval;
312         
313 }
314
315 /* for running operators with frozen context (modal handlers, menus) */
316 int WM_operator_call(bContext *C, wmOperator *op)
317 {
318         return wm_operator_exec(C, op, 0);
319 }
320
321 /* do this operator again, put here so it can share above code */
322 int WM_operator_repeat(bContext *C, wmOperator *op)
323 {
324         return wm_operator_exec(C, op, 1);
325 }
326
327 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
328 {
329         wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname);    /* XXX operatortype names are static still. for debug */
330         
331         /* XXX adding new operator could be function, only happens here now */
332         op->type= ot;
333         BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
334         
335         /* initialize properties, either copy or create */
336         op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
337         if(properties && properties->data) {
338                 op->properties= IDP_CopyProperty(properties->data);
339         }
340         else {
341                 IDPropertyTemplate val = {0};
342                 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
343         }
344         RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
345
346         /* initialize error reports */
347         if (reports) {
348                 op->reports= reports; /* must be initialized alredy */
349         }
350         else {
351                 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
352                 BKE_reports_init(op->reports, RPT_STORE);
353         }
354         
355         /* recursive filling of operator macro list */
356         if(ot->macro.first) {
357                 static wmOperator *motherop= NULL;
358                 wmOperatorTypeMacro *otmacro;
359                 
360                 /* ensure all ops are in execution order in 1 list */
361                 if(motherop==NULL) 
362                         motherop= op;
363                 
364                 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
365                         wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
366                         wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
367                         
368                         BLI_addtail(&motherop->macro, opm);
369                         opm->opm= motherop; /* pointer to mom, for modal() */
370                 }
371                 
372                 motherop= NULL;
373         }
374         
375         return op;
376 }
377
378 static void wm_operator_print(wmOperator *op)
379 {
380         char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
381         printf("%s\n", buf);
382         MEM_freeN(buf);
383 }
384
385 static void wm_region_mouse_co(bContext *C, wmEvent *event)
386 {
387         ARegion *ar= CTX_wm_region(C);
388         if(ar) {
389                 /* compatibility convention */
390                 event->mval[0]= event->x - ar->winrct.xmin;
391                 event->mval[1]= event->y - ar->winrct.ymin;
392         }
393 }
394
395 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties)
396 {
397         wmWindowManager *wm= CTX_wm_manager(C);
398         int retval= OPERATOR_PASS_THROUGH;
399
400         if(wm_operator_poll(C, ot)) {
401                 wmOperator *op= wm_operator_create(wm, ot, properties, NULL);
402                 
403                 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
404                         printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname); 
405                 
406                 if(op->type->invoke && event) {
407                         wm_region_mouse_co(C, event);
408                         retval= op->type->invoke(C, op, event);
409                 }
410                 else if(op->type->exec)
411                         retval= op->type->exec(C, op);
412                 else
413                         printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
414
415                 if(!(retval & OPERATOR_RUNNING_MODAL)) {
416                         if(op->reports->list.first) /* only show the report if the report list was not given in the function */
417                                 uiPupMenuReports(C, op->reports);
418                 
419                 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 */
420                         if(G.f & G_DEBUG)
421                                 wm_operator_print(op);
422                 }
423
424                 if(retval & OPERATOR_FINISHED) {
425                         if(ot->flag & OPTYPE_UNDO)
426                                 ED_undo_push_op(C, op);
427                         
428                         if((ot->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
429                                 wm_operator_register(C, op);
430                         else
431                                 WM_operator_free(op);
432                 }
433                 else if(retval & OPERATOR_RUNNING_MODAL) {
434                         /* grab cursor during blocking modal ops (X11) */
435                         if(ot->flag & OPTYPE_BLOCKING)
436                                 WM_cursor_grab(CTX_wm_window(C), 1);
437                 }
438                 else
439                         WM_operator_free(op);
440         }
441
442         return retval;
443 }
444
445 /* invokes operator in context */
446 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
447 {
448         wmOperatorType *ot= WM_operatortype_find(opstring, 0);
449         wmWindow *window= CTX_wm_window(C);
450         wmEvent *event;
451         
452         int retval;
453
454         /* dummie test */
455         if(ot && C && window) {
456                 event= window->eventstate;
457                 switch(context) {
458                         
459                         case WM_OP_EXEC_REGION_WIN:
460                                 event= NULL;    /* pass on without break */
461                         case WM_OP_INVOKE_REGION_WIN: 
462                         {
463                                 /* forces operator to go to the region window, for header menus */
464                                 ARegion *ar= CTX_wm_region(C);
465                                 ScrArea *area= CTX_wm_area(C);
466                                 
467                                 if(area) {
468                                         ARegion *ar1= area->regionbase.first;
469                                         for(; ar1; ar1= ar1->next)
470                                                 if(ar1->regiontype==RGN_TYPE_WINDOW)
471                                                         break;
472                                         if(ar1)
473                                                 CTX_wm_region_set(C, ar1);
474                                 }
475                                 
476                                 retval= wm_operator_invoke(C, ot, event, properties);
477                                 
478                                 /* set region back */
479                                 CTX_wm_region_set(C, ar);
480                                 
481                                 return retval;
482                         }
483                         case WM_OP_EXEC_AREA:
484                                 event= NULL;    /* pass on without break */
485                         case WM_OP_INVOKE_AREA:
486                         {
487                                         /* remove region from context */
488                                 ARegion *ar= CTX_wm_region(C);
489
490                                 CTX_wm_region_set(C, NULL);
491                                 retval= wm_operator_invoke(C, ot, event, properties);
492                                 CTX_wm_region_set(C, ar);
493
494                                 return retval;
495                         }
496                         case WM_OP_EXEC_SCREEN:
497                                 event= NULL;    /* pass on without break */
498                         case WM_OP_INVOKE_SCREEN:
499                         {
500                                 /* remove region + area from context */
501                                 ARegion *ar= CTX_wm_region(C);
502                                 ScrArea *area= CTX_wm_area(C);
503
504                                 CTX_wm_region_set(C, NULL);
505                                 CTX_wm_area_set(C, NULL);
506                                 retval= wm_operator_invoke(C, ot, event, properties);
507                                 CTX_wm_region_set(C, ar);
508                                 CTX_wm_area_set(C, area);
509
510                                 return retval;
511                         }
512                         case WM_OP_EXEC_DEFAULT:
513                                 event= NULL;    /* pass on without break */
514                         case WM_OP_INVOKE_DEFAULT:
515                                 return wm_operator_invoke(C, ot, event, properties);
516                 }
517         }
518         
519         return 0;
520 }
521
522 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
523    - wmOperatorType is used instead of operator name since python alredy has the operator type
524    - poll() must be called by python before this runs.
525    - reports can be passed to this function (so python can report them as exceptions)
526 */
527 int WM_operator_call_py(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
528 {
529         wmWindowManager *wm=    CTX_wm_manager(C);
530         wmOperator *op=                 wm_operator_create(wm, ot, properties, reports);
531         int retval= OPERATOR_CANCELLED;
532         
533         if (op->type->exec)
534                 retval= op->type->exec(C, op);
535         else
536                 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
537         
538         if (reports)
539                 op->reports= NULL; /* dont let the operator free reports passed to this function */
540         WM_operator_free(op);
541         
542         return retval;
543 }
544
545
546 /* ********************* handlers *************** */
547
548 /* future extra customadata free? */
549 static void wm_event_free_handler(wmEventHandler *handler)
550 {
551         MEM_freeN(handler);
552 }
553
554 /* only set context when area/region is part of screen */
555 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
556 {
557         bScreen *screen= CTX_wm_screen(C);
558         
559         if(screen && handler->op) {
560                 if(handler->op_area==NULL)
561                         CTX_wm_area_set(C, NULL);
562                 else {
563                         ScrArea *sa;
564                         
565                         for(sa= screen->areabase.first; sa; sa= sa->next)
566                                 if(sa==handler->op_area)
567                                         break;
568                         if(sa==NULL) {
569                                 /* when changing screen layouts with running modal handlers (like render display), this
570                                    is not an error to print */
571                                 if(handler->op==NULL)
572                                         printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
573                         }
574                         else {
575                                 ARegion *ar;
576                                 CTX_wm_area_set(C, sa);
577                                 for(ar= sa->regionbase.first; ar; ar= ar->next)
578                                         if(ar==handler->op_region)
579                                                 break;
580                                 /* XXX no warning print here, after full-area and back regions are remade */
581                                 if(ar)
582                                         CTX_wm_region_set(C, ar);
583                         }
584                 }
585         }
586 }
587
588 /* called on exit or remove area, only here call cancel callback */
589 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
590 {
591         wmEventHandler *handler;
592         
593         /* C is zero on freeing database, modal handlers then already were freed */
594         while((handler=handlers->first)) {
595                 BLI_remlink(handlers, handler);
596                 
597                 if(handler->op) {
598                         if(handler->op->type->cancel) {
599                                 ScrArea *area= CTX_wm_area(C);
600                                 ARegion *region= CTX_wm_region(C);
601                                 
602                                 wm_handler_op_context(C, handler);
603
604                                 handler->op->type->cancel(C, handler->op);
605
606                                 CTX_wm_area_set(C, area);
607                                 CTX_wm_region_set(C, region);
608                         }
609
610                         WM_operator_free(handler->op);
611                         WM_cursor_grab(CTX_wm_window(C), 0);
612                 }
613                 else if(handler->ui_remove) {
614                         ScrArea *area= CTX_wm_area(C);
615                         ARegion *region= CTX_wm_region(C);
616                         ARegion *menu= CTX_wm_menu(C);
617                         
618                         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
619                         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
620                         if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
621
622                         handler->ui_remove(C, handler->ui_userdata);
623
624                         CTX_wm_area_set(C, area);
625                         CTX_wm_region_set(C, region);
626                         CTX_wm_menu_set(C, menu);
627                 }
628
629                 wm_event_free_handler(handler);
630         }
631 }
632
633 /* do userdef mappings */
634 static int wm_userdef_event_map(int kmitype)
635 {
636         switch(kmitype) {
637                 case SELECTMOUSE:
638                         if(U.flag & USER_LMOUSESELECT)
639                                 return LEFTMOUSE;
640                         else
641                                 return RIGHTMOUSE;
642                         
643                 case ACTIONMOUSE:
644                         if(U.flag & USER_LMOUSESELECT)
645                                 return RIGHTMOUSE;
646                         else
647                                 return LEFTMOUSE;
648                         
649                 case WHEELOUTMOUSE:
650                         if(U.uiflag & USER_WHEELZOOMDIR)
651                                 return WHEELUPMOUSE;
652                         else
653                                 return WHEELDOWNMOUSE;
654                         
655                 case WHEELINMOUSE:
656                         if(U.uiflag & USER_WHEELZOOMDIR)
657                                 return WHEELDOWNMOUSE;
658                         else
659                                 return WHEELUPMOUSE;
660                         
661                 case EVT_TWEAK_A:
662                         if(U.flag & USER_LMOUSESELECT)
663                                 return EVT_TWEAK_R;
664                         else
665                                 return EVT_TWEAK_L;
666                         
667                 case EVT_TWEAK_S:
668                         if(U.flag & USER_LMOUSESELECT)
669                                 return EVT_TWEAK_L;
670                         else
671                                 return EVT_TWEAK_R;
672         }
673         
674         return kmitype;
675 }
676
677 static int wm_eventmatch(wmEvent *winevent, wmKeymapItem *kmi)
678 {
679         int kmitype= wm_userdef_event_map(kmi->type);
680
681         if(kmi->inactive) return 0;
682
683         /* exception for middlemouse emulation */
684         if((U.flag & USER_TWOBUTTONMOUSE) && (kmi->type == MIDDLEMOUSE)) {
685                 if(winevent->type == LEFTMOUSE && winevent->alt) {
686                         wmKeymapItem tmp= *kmi;
687
688                         tmp.type= winevent->type;
689                         tmp.alt= winevent->alt;
690                         if(wm_eventmatch(winevent, &tmp))
691                                 return 1;
692                 }
693         }
694
695         /* the matching rules */
696         if(kmitype==KM_TEXTINPUT)
697                 if(ISKEYBOARD(winevent->type)) return 1;
698         if(kmitype!=KM_ANY)
699                 if(winevent->type!=kmitype) return 0;
700         
701         if(kmi->val!=KM_ANY)
702                 if(winevent->val!=kmi->val) return 0;
703         
704         /* modifiers also check bits, so it allows modifier order */
705         if(kmi->shift!=KM_ANY)
706                 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
707         if(kmi->ctrl!=KM_ANY)
708                 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
709         if(kmi->alt!=KM_ANY)
710                 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
711         if(kmi->oskey!=KM_ANY)
712                 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
713         
714         if(kmi->keymodifier)
715                 if(winevent->keymodifier!=kmi->keymodifier) return 0;
716                 
717         /* key modifiers always check when event has it */
718         /* otherwise regular keypresses with keymodifier still work */
719         if(winevent->keymodifier)
720                 if(ISKEYBOARD(winevent->type)) 
721                         if(winevent->keymodifier!=kmi->keymodifier) return 0;
722         
723         return 1;
724 }
725
726 static int wm_event_always_pass(wmEvent *event)
727 {
728         /* some events we always pass on, to ensure proper communication */
729         return ELEM5(event->type, TIMER, TIMER0, TIMER1, TIMER2, TIMERJOBS);
730 }
731
732 /* operator exists */
733 static void wm_event_modalkeymap(wmOperator *op, wmEvent *event)
734 {
735         if(op->type->modalkeymap) {
736                 wmKeymapItem *kmi;
737                 
738                 for(kmi= op->type->modalkeymap->keymap.first; kmi; kmi= kmi->next) {
739                         if(wm_eventmatch(event, kmi)) {
740                                         
741                                 event->type= EVT_MODAL_MAP;
742                                 event->val= kmi->propvalue;
743                         }
744                 }
745         }
746 }
747
748 /* Warning: this function removes a modal handler, when finished */
749 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
750 {
751         int retval= OPERATOR_PASS_THROUGH;
752         
753         /* derived, modal or blocking operator */
754         if(handler->op) {
755                 wmOperator *op= handler->op;
756                 wmOperatorType *ot= op->type;
757
758                 if(ot->modal) {
759                         /* we set context to where modal handler came from */
760                         ScrArea *area= CTX_wm_area(C);
761                         ARegion *region= CTX_wm_region(C);
762                         
763                         wm_handler_op_context(C, handler);
764                         wm_region_mouse_co(C, event);
765                         wm_event_modalkeymap(op, event);
766                         
767                         retval= ot->modal(C, op, event);
768
769                         /* putting back screen context, reval can pass trough after modal failures! */
770                         if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
771                                 CTX_wm_area_set(C, area);
772                                 CTX_wm_region_set(C, region);
773                         }
774                         else {
775                                 /* this special cases is for areas and regions that get removed */
776                                 CTX_wm_area_set(C, NULL);
777                                 CTX_wm_region_set(C, NULL);
778                         }
779
780                         if(!(retval & OPERATOR_RUNNING_MODAL))
781                                 if(op->reports->list.first)
782                                         uiPupMenuReports(C, op->reports);
783
784                         if (retval & OPERATOR_FINISHED) {
785                                 if(G.f & G_DEBUG)
786                                         wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
787                         }                       
788
789                         if(retval & OPERATOR_FINISHED) {
790                                 if(ot->flag & OPTYPE_UNDO)
791                                         ED_undo_push_op(C, op);
792                                 
793                                 if((ot->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
794                                         wm_operator_register(C, op);
795                                 else
796                                         WM_operator_free(op);
797                                 handler->op= NULL;
798                         }
799                         else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
800                                 WM_operator_free(op);
801                                 handler->op= NULL;
802                         }
803                         
804                         /* remove modal handler, operator itself should have been cancelled and freed */
805                         if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
806                                 WM_cursor_grab(CTX_wm_window(C), 0);
807
808                                 BLI_remlink(handlers, handler);
809                                 wm_event_free_handler(handler);
810                                 
811                                 /* prevent silly errors from operator users */
812                                 //retval &= ~OPERATOR_PASS_THROUGH;
813                         }
814                         
815                 }
816                 else
817                         printf("wm_handler_operator_call error\n");
818         }
819         else {
820                 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
821
822                 if(ot)
823                         retval= wm_operator_invoke(C, ot, event, properties);
824         }
825
826         if(retval & OPERATOR_PASS_THROUGH)
827                 return WM_HANDLER_CONTINUE;
828
829         return WM_HANDLER_BREAK;
830 }
831
832 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event)
833 {
834         ScrArea *area= CTX_wm_area(C);
835         ARegion *region= CTX_wm_region(C);
836         ARegion *menu= CTX_wm_menu(C);
837         int retval, always_pass;
838                         
839         /* we set context to where ui handler came from */
840         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
841         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
842         if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
843
844         /* in advance to avoid access to freed event on window close */
845         always_pass= wm_event_always_pass(event);
846
847         retval= handler->ui_handle(C, event, handler->ui_userdata);
848
849         /* putting back screen context */
850         if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
851                 CTX_wm_area_set(C, area);
852                 CTX_wm_region_set(C, region);
853                 CTX_wm_menu_set(C, menu);
854         }
855         else {
856                 /* this special cases is for areas and regions that get removed */
857                 CTX_wm_area_set(C, NULL);
858                 CTX_wm_region_set(C, NULL);
859                 CTX_wm_menu_set(C, NULL);
860         }
861
862         if(retval == WM_UI_HANDLER_BREAK)
863                 return WM_HANDLER_BREAK;
864
865         return WM_HANDLER_CONTINUE;
866 }
867
868 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
869 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
870 {
871         SpaceFile *sfile;
872         int action= WM_HANDLER_CONTINUE;
873         
874         if(event->type != EVT_FILESELECT)
875                 return action;
876         if(handler->op != (wmOperator *)event->customdata)
877                 return action;
878         
879         switch(event->val) {
880                 case EVT_FILESELECT_OPEN: 
881                 case EVT_FILESELECT_FULL_OPEN: 
882                         {
883                                 char *dir= NULL; char *path= RNA_string_get_alloc(handler->op->ptr, "filename", NULL, 0);
884                                         
885                                 if(event->val==EVT_FILESELECT_OPEN)
886                                         ED_area_newspace(C, handler->op_area, SPACE_FILE);
887                                 else
888                                         ED_screen_full_newspace(C, handler->op_area, SPACE_FILE);
889                                 
890                                 /* settings for filebrowser, sfile is not operator owner but sends events */
891                                 sfile= (SpaceFile*)CTX_wm_space_data(C);
892                                 sfile->op= handler->op;
893
894                                 ED_fileselect_set_params(sfile);
895                                 dir = NULL;
896                                 MEM_freeN(path);
897                                 
898                                 action= WM_HANDLER_BREAK;
899                         }
900                         break;
901                         
902                 case EVT_FILESELECT_EXEC:
903                 case EVT_FILESELECT_CANCEL:
904                         {
905                                 /* XXX validate area and region? */
906                                 bScreen *screen= CTX_wm_screen(C);
907                                 char *path= RNA_string_get_alloc(handler->op->ptr, "filename", NULL, 0);
908                                 
909                                 if(screen != handler->filescreen)
910                                         ED_screen_full_prevspace(C);
911                                 else
912                                         ED_area_prevspace(C);
913                                 
914                                 /* remlink now, for load file case */
915                                 BLI_remlink(handlers, handler);
916                                 
917                                 if(event->val==EVT_FILESELECT_EXEC) {
918                                         wm_handler_op_context(C, handler);
919                                 
920                                         /* a bit weak, might become arg for WM_event_fileselect? */
921                                         /* XXX also extension code in image-save doesnt work for this yet */
922                                         if(strncmp(handler->op->type->name, "Save", 4)==0) {
923                                                 /* this gives ownership to pupmenu */
924                                                 uiPupMenuSaveOver(C, handler->op, path);
925                                         }
926                                         else {
927                                                 int retval= handler->op->type->exec(C, handler->op);
928                                                 
929                                                 if (retval & OPERATOR_FINISHED)
930                                                         if(G.f & G_DEBUG)
931                                                                 wm_operator_print(handler->op);
932                                                 
933                                                 WM_operator_free(handler->op);
934                                         }
935                                         
936                                         CTX_wm_area_set(C, NULL);
937                                 }
938                                 else 
939                                         WM_operator_free(handler->op);
940                                 
941                                 wm_event_free_handler(handler);
942                                 MEM_freeN(path);
943                                 
944                                 action= WM_HANDLER_BREAK;
945                         }
946                         break;
947         }
948         
949         return action;
950 }
951
952 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
953 {
954         if(handler->bbwin) {
955                 if(handler->bblocal) {
956                         rcti rect= *handler->bblocal;
957                         BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
958
959                         if(BLI_in_rcti(&rect, event->x, event->y))
960                                 return 1;
961                         else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
962                                 return 1;
963                         else
964                                 return 0;
965                 }
966                 else {
967                         if(BLI_in_rcti(handler->bbwin, event->x, event->y))
968                                 return 1;
969                         else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
970                                 return 1;
971                         else
972                                 return 0;
973                 }
974         }
975         return 1;
976 }
977
978 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
979 {
980         wmEventHandler *handler, *nexthandler;
981         int action= WM_HANDLER_CONTINUE;
982         int always_pass;
983
984         if(handlers==NULL) return action;
985         
986         /* modal handlers can get removed in this loop, we keep the loop this way */
987         for(handler= handlers->first; handler; handler= nexthandler) {
988                 nexthandler= handler->next;
989
990                 /* optional boundbox */
991                 if(handler_boundbox_test(handler, event)) {
992                         /* in advance to avoid access to freed event on window close */
993                         always_pass= wm_event_always_pass(event);
994                 
995                         /* modal+blocking handler */
996                         if(handler->flag & WM_HANDLER_BLOCKING)
997                                 action= WM_HANDLER_BREAK;
998
999                         if(handler->keymap) {
1000                                 wmKeymapItem *kmi;
1001                                 
1002                                 for(kmi= handler->keymap->first; kmi; kmi= kmi->next) {
1003                                         if(wm_eventmatch(event, kmi)) {
1004                                                 
1005                                                 event->keymap_idname= kmi->idname;      /* weak, but allows interactive callback to not use rawkey */
1006                                                 
1007                                                 action= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1008                                                 if(action==WM_HANDLER_BREAK)  /* not wm_event_always_pass(event) here, it denotes removed handler */
1009                                                         break;
1010                                         }
1011                                 }
1012                         }
1013                         else if(handler->ui_handle) {
1014                                 action= wm_handler_ui_call(C, handler, event);
1015                         }
1016                         else if(handler->type==WM_HANDLER_FILESELECT) {
1017                                 /* screen context changes here */
1018                                 action= wm_handler_fileselect_call(C, handlers, handler, event);
1019                         }
1020                         else {
1021                                 /* modal, swallows all */
1022                                 action= wm_handler_operator_call(C, handlers, handler, event, NULL);
1023                         }
1024
1025                         if(!always_pass && action==WM_HANDLER_BREAK)
1026                                 break;
1027                 }
1028                 
1029                 /* fileread case */
1030                 if(CTX_wm_window(C)==NULL)
1031                         break;
1032         }
1033         return action;
1034 }
1035
1036 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1037 {
1038         if(BLI_in_rcti(rect, event->x, event->y))
1039            return 1;
1040         if(event->type==MOUSEMOVE) {
1041                 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1042                         return 1;
1043                 }
1044                 return 0;
1045         }
1046         return 0;
1047 }
1048
1049 static ScrArea *area_event_inside(bContext *C, int x, int y)
1050 {
1051         bScreen *screen= CTX_wm_screen(C);
1052         ScrArea *sa;
1053         
1054         if(screen)
1055                 for(sa= screen->areabase.first; sa; sa= sa->next)
1056                         if(BLI_in_rcti(&sa->totrct, x, y))
1057                                 return sa;
1058         return NULL;
1059 }
1060
1061 static ARegion *region_event_inside(bContext *C, int x, int y)
1062 {
1063         bScreen *screen= CTX_wm_screen(C);
1064         ScrArea *area= CTX_wm_area(C);
1065         ARegion *ar;
1066         
1067         if(screen && area)
1068                 for(ar= area->regionbase.first; ar; ar= ar->next)
1069                         if(BLI_in_rcti(&ar->winrct, x, y))
1070                                 return ar;
1071         return NULL;
1072 }
1073
1074 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1075 {
1076         if(ar) {
1077                 for(; pc; pc= pc->next) {
1078                         if(pc->poll(C)) {
1079                                 wmWindow *win= CTX_wm_window(C);
1080                                 win->screen->do_draw_paintcursor= 1;
1081
1082                                 if(win->drawmethod != USER_DRAW_TRIPLE)
1083                                         ED_region_tag_redraw(ar);
1084                         }
1085                 }
1086         }
1087 }
1088
1089 /* called on mousemove, check updates for paintcursors */
1090 /* context was set on active area and region */
1091 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1092 {
1093         wmWindowManager *wm= CTX_wm_manager(C);
1094         
1095         if(wm->paintcursors.first) {
1096                 ARegion *ar= CTX_wm_region(C);
1097                 if(ar)
1098                         wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1099                 
1100                 /* if previous position was not in current region, we have to set a temp new context */
1101                 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1102                         ScrArea *sa= CTX_wm_area(C);
1103                         
1104                         CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1105                         CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1106
1107                         wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1108                         
1109                         CTX_wm_area_set(C, sa);
1110                         CTX_wm_region_set(C, ar);
1111                 }
1112         }
1113 }
1114
1115 /* called in main loop */
1116 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
1117 void wm_event_do_handlers(bContext *C)
1118 {
1119         wmWindow *win;
1120
1121         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1122                 wmEvent *event;
1123                 
1124                 if( win->screen==NULL )
1125                         wm_event_free_all(win);
1126                 
1127                 while( (event= win->queue.first) ) {
1128                         int action;
1129                         
1130                         CTX_wm_window_set(C, win);
1131                         
1132                         /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1133                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1134                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1135                         
1136                         /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1137                         wm_window_make_drawable(C, win);
1138                         
1139                         action= wm_handlers_do(C, event, &win->handlers);
1140                         
1141                         /* fileread case */
1142                         if(CTX_wm_window(C)==NULL) {
1143                                 return;
1144                         }
1145                         
1146                         /* builtin tweak, if action is break it removes tweak */
1147                         if(!wm_event_always_pass(event))
1148                                 wm_tweakevent_test(C, event, action);
1149                         
1150                         if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
1151                                 ScrArea *sa;
1152                                 ARegion *ar;
1153                                 int doit= 0;
1154                                 
1155                                 /* XXX to solve, here screen handlers? */
1156                                 if(!wm_event_always_pass(event)) {
1157                                         if(event->type==MOUSEMOVE) {
1158                                                 /* state variables in screen, cursors */
1159                                                 ED_screen_set_subwinactive(win, event); 
1160                                                 /* for regions having custom cursors */
1161                                                 wm_paintcursor_test(C, event);
1162                                         }
1163                                 }
1164                                 
1165                                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1166                                         if(wm_event_always_pass(event) || wm_event_inside_i(event, &sa->totrct)) {
1167                                                 
1168                                                 CTX_wm_area_set(C, sa);
1169                                                 CTX_wm_region_set(C, NULL);
1170                                                 action= wm_handlers_do(C, event, &sa->handlers);
1171
1172                                                 if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
1173                                                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
1174                                                                 if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct)) {
1175                                                                         CTX_wm_region_set(C, ar);
1176                                                                         action= wm_handlers_do(C, event, &ar->handlers);
1177
1178                                                                         doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1179                                                                         
1180                                                                         if(!wm_event_always_pass(event)) {
1181                                                                                 if(action==WM_HANDLER_BREAK)
1182                                                                                         break;
1183                                                                         }
1184                                                                 }
1185                                                         }
1186                                                 }
1187                                                 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1188                                         }
1189                                 }
1190                                 
1191                                 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad? 
1192                                    doing it on ghost queue gives errors when mousemoves go over area borders */
1193                                 if(doit && win->screen->subwinactive != win->screen->mainwin) {
1194                                         win->eventstate->prevx= event->x;
1195                                         win->eventstate->prevy= event->y;
1196                                 }
1197                         }
1198                         
1199                         /* unlink and free here, blender-quit then frees all */
1200                         BLI_remlink(&win->queue, event);
1201                         wm_event_free(event);
1202                         
1203                 }
1204                 
1205                 /* only add mousemove when queue was read entirely */
1206                 if(win->addmousemove) {
1207                         wmEvent event= *(win->eventstate);
1208                         event.type= MOUSEMOVE;
1209                         event.prevx= event.x;
1210                         event.prevy= event.y;
1211                         wm_event_add(win, &event);
1212                         win->addmousemove= 0;
1213                 }
1214                 
1215                 CTX_wm_window_set(C, NULL);
1216         }
1217 }
1218
1219 /* ********** filesector handling ************ */
1220
1221 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1222 {
1223         /* add to all windows! */
1224         wmWindow *win;
1225         
1226         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1227                 wmEvent event= *win->eventstate;
1228                 
1229                 event.type= EVT_FILESELECT;
1230                 event.val= eventval;
1231                 event.customdata= ophandle;             // only as void pointer type check
1232
1233                 wm_event_add(win, &event);
1234         }
1235 }
1236
1237 /* operator is supposed to have a filled "filename" property */
1238 /* optional property: filetype (XXX enum?) */
1239
1240 /* Idea is to keep a handler alive on window queue, owning the operator.
1241    The filewindow can send event to make it execute, thus ensuring
1242    executing happens outside of lower level queues, with UI refreshed. 
1243    Should also allow multiwin solutions */
1244
1245 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1246 {
1247         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1248         wmWindow *win= CTX_wm_window(C);
1249         int full= 1;    // XXX preset?
1250         
1251         handler->type= WM_HANDLER_FILESELECT;
1252         handler->op= op;
1253         handler->op_area= CTX_wm_area(C);
1254         handler->op_region= CTX_wm_region(C);
1255         handler->filescreen= CTX_wm_screen(C);
1256         
1257         BLI_addhead(&win->handlers, handler);
1258         
1259         WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1260 }
1261
1262 /* lets not expose struct outside wm? */
1263 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1264 {
1265         handler->flag= flag;
1266 }
1267
1268 wmEventHandler *WM_event_add_modal_handler(bContext *C, ListBase *handlers, wmOperator *op)
1269 {
1270         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1271         
1272         /* operator was part of macro */
1273         if(op->opm) {
1274                 /* give the mother macro to the handler */
1275                 handler->op= op->opm;
1276                 /* mother macro opm becomes the macro element */
1277                 handler->op->opm= op;
1278         }
1279         else
1280                 handler->op= op;
1281         
1282         handler->op_area= CTX_wm_area(C);               /* means frozen screen context for modal handlers! */
1283         handler->op_region= CTX_wm_region(C);
1284         
1285         BLI_addhead(handlers, handler);
1286
1287         return handler;
1288 }
1289
1290 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, ListBase *keymap)
1291 {
1292         wmEventHandler *handler;
1293
1294         /* only allow same keymap once */
1295         for(handler= handlers->first; handler; handler= handler->next)
1296                 if(handler->keymap==keymap)
1297                         return handler;
1298         
1299         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1300         BLI_addtail(handlers, handler);
1301         handler->keymap= keymap;
1302
1303         return handler;
1304 }
1305
1306 /* priorities not implemented yet, for time being just insert in begin of list */
1307 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, ListBase *keymap, int priority)
1308 {
1309         wmEventHandler *handler;
1310         
1311         WM_event_remove_keymap_handler(handlers, keymap);
1312         
1313         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1314         BLI_addhead(handlers, handler);
1315         handler->keymap= keymap;
1316         
1317         return handler;
1318 }
1319
1320 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, ListBase *keymap, rcti *bblocal, rcti *bbwin)
1321 {
1322         wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1323         
1324         if(handler) {
1325                 handler->bblocal= bblocal;
1326                 handler->bbwin= bbwin;
1327         }
1328         return handler;
1329 }
1330
1331 void WM_event_remove_keymap_handler(ListBase *handlers, ListBase *keymap)
1332 {
1333         wmEventHandler *handler;
1334         
1335         for(handler= handlers->first; handler; handler= handler->next) {
1336                 if(handler->keymap==keymap) {
1337                         BLI_remlink(handlers, handler);
1338                         wm_event_free_handler(handler);
1339                         break;
1340                 }
1341         }
1342 }
1343
1344 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1345 {
1346         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1347         handler->ui_handle= func;
1348         handler->ui_remove= remove;
1349         handler->ui_userdata= userdata;
1350         handler->ui_area= (C)? CTX_wm_area(C): NULL;
1351         handler->ui_region= (C)? CTX_wm_region(C): NULL;
1352         handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1353         
1354         BLI_addhead(handlers, handler);
1355         
1356         return handler;
1357 }
1358
1359 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1360 {
1361         wmEventHandler *handler;
1362         
1363         for(handler= handlers->first; handler; handler= handler->next) {
1364                 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1365                         BLI_remlink(handlers, handler);
1366                         wm_event_free_handler(handler);
1367                         break;
1368                 }
1369         }
1370 }
1371
1372 void WM_event_add_mousemove(bContext *C)
1373 {
1374         wmWindow *window= CTX_wm_window(C);
1375         
1376         window->addmousemove= 1;
1377 }
1378
1379 /* for modal callbacks, check configuration for how to interpret exit with tweaks  */
1380 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
1381 {
1382         /* user preset or keymap? dunno... */
1383         int tweak_modal= (U.flag & USER_DRAGIMMEDIATE)==0;
1384         
1385         switch(tweak_event) {
1386                 case EVT_TWEAK_L:
1387                 case EVT_TWEAK_M:
1388                 case EVT_TWEAK_R:
1389                         if(evt->val==tweak_modal)
1390                                 return 1;
1391                 default:
1392                         /* this case is when modal callcback didnt get started with a tweak */
1393                         if(evt->val)
1394                                 return 1;
1395         }
1396         return 0;
1397 }
1398
1399
1400 /* ********************* ghost stuff *************** */
1401
1402 static int convert_key(GHOST_TKey key) 
1403 {
1404         if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
1405                 return (AKEY + ((int) key - GHOST_kKeyA));
1406         } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
1407                 return (ZEROKEY + ((int) key - GHOST_kKey0));
1408         } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
1409                 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
1410         } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
1411                 return (F1KEY + ((int) key - GHOST_kKeyF1));
1412         } else {
1413                 switch (key) {
1414                         case GHOST_kKeyBackSpace:               return BACKSPACEKEY;
1415                         case GHOST_kKeyTab:                             return TABKEY;
1416                         case GHOST_kKeyLinefeed:                return LINEFEEDKEY;
1417                         case GHOST_kKeyClear:                   return 0;
1418                         case GHOST_kKeyEnter:                   return RETKEY;
1419                                 
1420                         case GHOST_kKeyEsc:                             return ESCKEY;
1421                         case GHOST_kKeySpace:                   return SPACEKEY;
1422                         case GHOST_kKeyQuote:                   return QUOTEKEY;
1423                         case GHOST_kKeyComma:                   return COMMAKEY;
1424                         case GHOST_kKeyMinus:                   return MINUSKEY;
1425                         case GHOST_kKeyPeriod:                  return PERIODKEY;
1426                         case GHOST_kKeySlash:                   return SLASHKEY;
1427                                 
1428                         case GHOST_kKeySemicolon:               return SEMICOLONKEY;
1429                         case GHOST_kKeyEqual:                   return EQUALKEY;
1430                                 
1431                         case GHOST_kKeyLeftBracket:             return LEFTBRACKETKEY;
1432                         case GHOST_kKeyRightBracket:    return RIGHTBRACKETKEY;
1433                         case GHOST_kKeyBackslash:               return BACKSLASHKEY;
1434                         case GHOST_kKeyAccentGrave:             return ACCENTGRAVEKEY;
1435                                 
1436                         case GHOST_kKeyLeftShift:               return LEFTSHIFTKEY;
1437                         case GHOST_kKeyRightShift:              return RIGHTSHIFTKEY;
1438                         case GHOST_kKeyLeftControl:             return LEFTCTRLKEY;
1439                         case GHOST_kKeyRightControl:    return RIGHTCTRLKEY;
1440                         case GHOST_kKeyCommand:                 return COMMANDKEY;
1441                         case GHOST_kKeyLeftAlt:                 return LEFTALTKEY;
1442                         case GHOST_kKeyRightAlt:                return RIGHTALTKEY;
1443                                 
1444                         case GHOST_kKeyCapsLock:                return CAPSLOCKKEY;
1445                         case GHOST_kKeyNumLock:                 return 0;
1446                         case GHOST_kKeyScrollLock:              return 0;
1447                                 
1448                         case GHOST_kKeyLeftArrow:               return LEFTARROWKEY;
1449                         case GHOST_kKeyRightArrow:              return RIGHTARROWKEY;
1450                         case GHOST_kKeyUpArrow:                 return UPARROWKEY;
1451                         case GHOST_kKeyDownArrow:               return DOWNARROWKEY;
1452                                 
1453                         case GHOST_kKeyPrintScreen:             return 0;
1454                         case GHOST_kKeyPause:                   return PAUSEKEY;
1455                                 
1456                         case GHOST_kKeyInsert:                  return INSERTKEY;
1457                         case GHOST_kKeyDelete:                  return DELKEY;
1458                         case GHOST_kKeyHome:                    return HOMEKEY;
1459                         case GHOST_kKeyEnd:                             return ENDKEY;
1460                         case GHOST_kKeyUpPage:                  return PAGEUPKEY;
1461                         case GHOST_kKeyDownPage:                return PAGEDOWNKEY;
1462                                 
1463                         case GHOST_kKeyNumpadPeriod:    return PADPERIOD;
1464                         case GHOST_kKeyNumpadEnter:             return PADENTER;
1465                         case GHOST_kKeyNumpadPlus:              return PADPLUSKEY;
1466                         case GHOST_kKeyNumpadMinus:             return PADMINUS;
1467                         case GHOST_kKeyNumpadAsterisk:  return PADASTERKEY;
1468                         case GHOST_kKeyNumpadSlash:             return PADSLASHKEY;
1469                                 
1470                         case GHOST_kKeyGrLess:              return GRLESSKEY; 
1471                                 
1472                         default:
1473                                 return UNKNOWNKEY;      /* GHOST_kKeyUnknown */
1474                 }
1475         }
1476 }
1477
1478 /* adds customdata to event */
1479 static void update_tablet_data(wmWindow *win, wmEvent *event)
1480 {
1481         const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
1482         
1483         /* if there's tablet data from an active tablet device then add it */
1484         if ((td != NULL) && td->Active) {
1485                 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
1486                 
1487                 wmtab->Active = td->Active;
1488                 wmtab->Pressure = td->Pressure;
1489                 wmtab->Xtilt = td->Xtilt;
1490                 wmtab->Ytilt = td->Ytilt;
1491                 
1492                 event->custom= EVT_DATA_TABLET;
1493                 event->customdata= wmtab;
1494                 event->customdatafree= 1;
1495         } 
1496 }
1497
1498
1499 /* windows store own event queues, no bContext here */
1500 void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
1501 {
1502         wmEvent event, *evt= win->eventstate;
1503         
1504         /* initialize and copy state (only mouse x y and modifiers) */
1505         event= *evt;
1506         
1507         switch (type) {
1508                 /* mouse move */
1509                 case GHOST_kEventCursorMove: {
1510                         if(win->active) {
1511                                 GHOST_TEventCursorData *cd= customdata;
1512                                 int cx, cy;
1513
1514                                 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
1515                                 
1516                                 event.type= MOUSEMOVE;
1517                                 event.x= evt->x= cx;
1518                                 event.y= evt->y= (win->sizey-1) - cy;
1519                                 
1520                                 update_tablet_data(win, &event);
1521                                 wm_event_add(win, &event);
1522                         }
1523                         break;
1524                 }
1525                 /* mouse button */
1526                 case GHOST_kEventButtonDown:
1527                 case GHOST_kEventButtonUp: {
1528                         GHOST_TEventButtonData *bd= customdata;
1529                         event.val= (type==GHOST_kEventButtonDown);
1530                         
1531                         if (bd->button == GHOST_kButtonMaskLeft)
1532                                 event.type= LEFTMOUSE;
1533                         else if (bd->button == GHOST_kButtonMaskRight)
1534                                 event.type= RIGHTMOUSE;
1535                         else
1536                                 event.type= MIDDLEMOUSE;
1537                         
1538                         update_tablet_data(win, &event);
1539                         wm_event_add(win, &event);
1540                         
1541                         break;
1542                 }
1543                 /* keyboard */
1544                 case GHOST_kEventKeyDown:
1545                 case GHOST_kEventKeyUp: {
1546                         GHOST_TEventKeyData *kd= customdata;
1547                         event.type= convert_key(kd->key);
1548                         event.ascii= kd->ascii;
1549                         event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
1550                         
1551                         /* exclude arrow keys, esc, etc from text input */
1552                         if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>14))
1553                                 event.ascii= '\0';
1554                         
1555                         /* modifiers */
1556                         if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
1557                                 event.shift= evt->shift= (event.val==KM_PRESS);
1558                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
1559                                    event.shift= evt->shift = 3;         // define?
1560                         } 
1561                         else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
1562                                 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
1563                                 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
1564                                    event.ctrl= evt->ctrl = 3;           // define?
1565                         } 
1566                         else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
1567                                 event.alt= evt->alt= (event.val==KM_PRESS);
1568                                 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
1569                                    event.alt= evt->alt = 3;             // define?
1570                         } 
1571                         else if (event.type==COMMANDKEY) {
1572                                 event.oskey= evt->oskey= (event.val==KM_PRESS);
1573                                 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
1574                                    event.oskey= evt->oskey = 3;         // define?
1575                         }
1576                         else {
1577                                 if(event.val==KM_PRESS && event.keymodifier==0)
1578                                         evt->keymodifier= event.type; /* only set in eventstate, for next event */
1579                                 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
1580                                         event.keymodifier= evt->keymodifier= 0;
1581                         }
1582                         
1583                         /* if test_break set, it catches this. XXX Keep global for now? */
1584                         if(event.type==ESCKEY)
1585                                 G.afbreek= 1;
1586
1587                         wm_event_add(win, &event);
1588                         
1589                         break;
1590                 }
1591                         
1592                 case GHOST_kEventWheel: {
1593                         GHOST_TEventWheelData* wheelData = customdata;
1594                         
1595                         if (wheelData->z > 0)
1596                                 event.type= WHEELUPMOUSE;
1597                         else
1598                                 event.type= WHEELDOWNMOUSE;
1599                         
1600                         event.val= KM_PRESS;
1601                         wm_event_add(win, &event);
1602                         
1603                         break;
1604                 }
1605                 case GHOST_kEventTimer: {
1606                         event.type= TIMER;
1607                         event.custom= EVT_DATA_TIMER;
1608                         event.customdata= customdata;
1609                         wm_event_add(win, &event);
1610
1611                         break;
1612                 }
1613
1614                 case GHOST_kEventUnknown:
1615                 case GHOST_kNumEventTypes:
1616                         break;
1617         }
1618 }