2.5
[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_windowmanager_types.h"
35 #include "DNA_userdef_types.h"
36
37 #include "MEM_guardedalloc.h"
38
39 #include "GHOST_C-api.h"
40
41 #include "BLI_blenlib.h"
42
43 #include "BKE_blender.h"
44 #include "BKE_context.h"
45 #include "BKE_idprop.h"
46 #include "BKE_global.h"
47 #include "BKE_report.h"
48 #include "BKE_utildefines.h"
49
50 #include "ED_screen.h"
51 #include "ED_space_api.h"
52 #include "ED_util.h"
53
54 #include "RNA_access.h"
55
56 #include "UI_interface.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60 #include "wm.h"
61 #include "wm_window.h"
62 #include "wm_event_system.h"
63 #include "wm_event_types.h"
64
65 /* ************ event management ************** */
66
67 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
68 {
69         wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
70         
71         *event= *event_to_add;
72         BLI_addtail(&win->queue, event);
73 }
74
75 static void wm_event_free(wmEvent *event)
76 {
77         if(event->customdata && event->customdatafree)
78                 MEM_freeN(event->customdata);
79         MEM_freeN(event);
80 }
81
82 void wm_event_free_all(wmWindow *win)
83 {
84         wmEvent *event;
85         
86         while((event= win->queue.first)) {
87                 BLI_remlink(&win->queue, event);
88                 wm_event_free(event);
89         }
90 }
91
92 /* ********************* notifiers, listeners *************** */
93
94 /* XXX: in future, which notifiers to send to other windows? */
95 void WM_event_add_notifier(bContext *C, unsigned int type, void *reference)
96 {
97         wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
98         
99         note->wm= CTX_wm_manager(C);
100         BLI_addtail(&note->wm->queue, note);
101         
102         note->window= CTX_wm_window(C);
103         
104         if(CTX_wm_region(C))
105                 note->swinid= CTX_wm_region(C)->swinid;
106         
107         note->category= type & NOTE_CATEGORY;
108         note->data= type & NOTE_DATA;
109         note->subtype= type & NOTE_SUBTYPE;
110         note->action= type & NOTE_ACTION;
111         
112         note->reference= reference;
113 }
114
115 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
116 {
117         wmNotifier *note= wm->queue.first;
118         
119         if(note) BLI_remlink(&wm->queue, note);
120         return note;
121 }
122
123 /* called in mainloop */
124 void wm_event_do_notifiers(bContext *C)
125 {
126         wmWindowManager *wm= CTX_wm_manager(C);
127         wmNotifier *note;
128         wmWindow *win;
129         
130         if(wm==NULL)
131                 return;
132         
133         /* cache & catch WM level notifiers, such as frame change, scene/screen set */
134         for(win= wm->windows.first; win; win= win->next) {
135                 int do_anim= 0;
136                 
137                 CTX_wm_window_set(C, win);
138                 
139                 for(note= wm->queue.first; note; note= note->next) {
140                         if(note->window==win) {
141                                 if(note->category==NC_SCREEN) {
142                                         if(note->data==ND_SCREENBROWSE)
143                                                 ED_screen_set(C, note->reference);      // XXX hrms, think this over!
144                                 }
145                                 else if(note->category==NC_SCENE) {
146                                         if(note->data==ND_SCENEBROWSE) {
147                                                 ED_screen_set_scene(C, note->reference);        // XXX hrms, think this over!
148                                         }
149                                         else if(note->data==ND_FRAME)
150                                                 do_anim= 1;
151                                 }
152                         }
153                 }
154                 if(do_anim) {
155                         /* depsgraph gets called, might send more notifiers */
156                         ED_update_for_newframe(C, 1);
157                 }
158         }
159         
160         /* the notifiers are sent without context, to keep it clean */
161         while( (note=wm_notifier_next(wm)) ) {
162                 wmWindow *win;
163                 
164                 for(win= wm->windows.first; win; win= win->next) {
165                         ScrArea *sa;
166                         ARegion *ar;
167
168                         /* XXX context in notifiers? */
169                         CTX_wm_window_set(C, win);
170
171                         /* printf("notifier win %d screen %s\n", win->winid, win->screen->id.name+2); */
172                         ED_screen_do_listen(win, note);
173
174                         for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
175                                 ED_region_do_listen(ar, note);
176                         }
177                         
178                         for(sa= win->screen->areabase.first; sa; sa= sa->next) {
179                                 ED_area_do_listen(sa, note);
180                                 for(ar=sa->regionbase.first; ar; ar= ar->next) {
181                                         ED_region_do_listen(ar, note);
182                                 }
183                         }
184                 }
185                 
186                 CTX_wm_window_set(C, NULL);
187                 
188                 MEM_freeN(note);
189         }
190         
191         /* cached: editor refresh callbacks now, they get context */
192         for(win= wm->windows.first; win; win= win->next) {
193                 ScrArea *sa;
194                 CTX_wm_window_set(C, win);
195                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
196                         if(sa->do_refresh) {
197                                 CTX_wm_area_set(C, sa);
198                                 ED_area_do_refresh(C, sa);
199                         }
200                 }
201         }
202         CTX_wm_window_set(C, NULL);
203 }
204
205 /* ********************* operators ******************* */
206
207 /* if repeat is true, it doesn't register again, nor does it free */
208 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
209 {
210         int retval= OPERATOR_CANCELLED;
211         
212         if(op==NULL || op->type==NULL)
213                 return retval;
214         
215         if(op->type->poll && op->type->poll(C)==0)
216                 return retval;
217         
218         if(op->type->exec)
219                 retval= op->type->exec(C, op);
220         
221         if(!(retval & OPERATOR_RUNNING_MODAL))
222                 if(op->reports->list.first)
223                         uiPupMenuReports(C, op->reports);
224         
225         if(retval & OPERATOR_FINISHED) {
226                 if(op->type->flag & OPTYPE_UNDO)
227                         ED_undo_push_op(C, op);
228                 
229                 if(repeat==0) {
230                         if(op->type->flag & OPTYPE_REGISTER)
231                                 wm_operator_register(CTX_wm_manager(C), op);
232                         else
233                                 WM_operator_free(op);
234                 }
235         }
236         else if(repeat==0)
237                 WM_operator_free(op);
238         
239         return retval;
240         
241 }
242
243 /* for running operators with frozen context (modal handlers, menus) */
244 int WM_operator_call(bContext *C, wmOperator *op)
245 {
246         return wm_operator_exec(C, op, 0);
247 }
248
249 /* do this operator again, put here so it can share above code */
250 int WM_operator_repeat(bContext *C, wmOperator *op)
251 {
252         return wm_operator_exec(C, op, 1);
253 }
254
255 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
256 {
257         wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname);    /* XXX operatortype names are static still. for debug */
258         
259         /* XXX adding new operator could be function, only happens here now */
260         op->type= ot;
261         BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
262         
263         /* initialize properties, either copy or create */
264         op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
265         if(properties && properties->data) {
266                 op->properties= IDP_CopyProperty(properties->data);
267         }
268         else {
269                 IDPropertyTemplate val = {0};
270                 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
271         }
272         RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
273
274         /* initialize error reports */
275         if (reports) {
276                 op->reports= reports; /* must be initialized alredy */
277         }
278         else {
279                 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
280                 BKE_reports_init(op->reports, RPT_STORE);
281         }
282         
283         return op;
284 }
285
286 static void wm_operator_print(wmOperator *op)
287 {
288         char *buf = WM_operator_pystring(op);
289         printf("%s\n", buf);
290         MEM_freeN(buf);
291 }
292
293 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties)
294 {
295         wmWindowManager *wm= CTX_wm_manager(C);
296         int retval= OPERATOR_PASS_THROUGH;
297
298         if(ot->poll==NULL || ot->poll(C)) {
299                 wmOperator *op= wm_operator_create(wm, ot, properties, NULL);
300                 
301                 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
302                         printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname); 
303                 
304                 if(op->type->invoke && event)
305                         retval= (*op->type->invoke)(C, op, event);
306                 else if(op->type->exec)
307                         retval= op->type->exec(C, op);
308                 else
309                         printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
310
311                 if(!(retval & OPERATOR_RUNNING_MODAL)) {
312                         if(op->reports->list.first) /* only show the report if the report list was not given in the function */
313                                 uiPupMenuReports(C, op->reports);
314                 
315                 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 */
316                         if(G.f & G_DEBUG)
317                                 wm_operator_print(op);
318                 }
319
320                 if(retval & OPERATOR_FINISHED) {
321                         if(ot->flag & OPTYPE_UNDO)
322                                 ED_undo_push_op(C, op);
323                         
324                         if(ot->flag & OPTYPE_REGISTER)
325                                 wm_operator_register(wm, op);
326                         else
327                                 WM_operator_free(op);
328                 }
329                 else if(!(retval & OPERATOR_RUNNING_MODAL)) {
330                         WM_operator_free(op);
331                 }
332         }
333
334         return retval;
335 }
336
337 /* invokes operator in context */
338 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
339 {
340         wmOperatorType *ot= WM_operatortype_find(opstring);
341         wmWindow *window= CTX_wm_window(C);
342         wmEvent *event;
343         
344         int retval;
345
346         /* dummie test */
347         if(ot && C && window) {
348                 event= window->eventstate;
349                 switch(context) {
350                         
351                         case WM_OP_EXEC_REGION_WIN:
352                                 event= NULL;    /* pass on without break */
353                         case WM_OP_INVOKE_REGION_WIN: 
354                         {
355                                 /* forces operator to go to the region window, for header menus */
356                                 ARegion *ar= CTX_wm_region(C);
357                                 ScrArea *area= CTX_wm_area(C);
358                                 
359                                 if(area) {
360                                         ARegion *ar1= area->regionbase.first;
361                                         for(; ar1; ar1= ar1->next)
362                                                 if(ar1->regiontype==RGN_TYPE_WINDOW)
363                                                         break;
364                                         if(ar1)
365                                                 CTX_wm_region_set(C, ar1);
366                                 }
367                                 
368                                 retval= wm_operator_invoke(C, ot, event, properties);
369                                 
370                                 /* set region back */
371                                 CTX_wm_region_set(C, ar);
372                                 
373                                 return retval;
374                         }
375                         case WM_OP_EXEC_AREA:
376                                 event= NULL;    /* pass on without break */
377                         case WM_OP_INVOKE_AREA:
378                         {
379                                         /* remove region from context */
380                                 ARegion *ar= CTX_wm_region(C);
381
382                                 CTX_wm_region_set(C, NULL);
383                                 retval= wm_operator_invoke(C, ot, event, properties);
384                                 CTX_wm_region_set(C, ar);
385
386                                 return retval;
387                         }
388                         case WM_OP_EXEC_SCREEN:
389                                 event= NULL;    /* pass on without break */
390                         case WM_OP_INVOKE_SCREEN:
391                         {
392                                 /* remove region + area from context */
393                                 ARegion *ar= CTX_wm_region(C);
394                                 ScrArea *area= CTX_wm_area(C);
395
396                                 CTX_wm_region_set(C, NULL);
397                                 CTX_wm_area_set(C, NULL);
398                                 retval= wm_operator_invoke(C, ot, window->eventstate, properties);
399                                 CTX_wm_region_set(C, ar);
400                                 CTX_wm_area_set(C, area);
401
402                                 return retval;
403                         }
404                         case WM_OP_EXEC_DEFAULT:
405                                 event= NULL;    /* pass on without break */
406                         case WM_OP_INVOKE_DEFAULT:
407                                 return wm_operator_invoke(C, ot, event, properties);
408                 }
409         }
410         
411         return 0;
412 }
413
414 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
415    - wmOperatorType is used instead of operator name since python alredy has the operator type
416    - poll() must be called by python before this runs.
417    - reports can be passed to this function (so python can report them as exceptions)
418 */
419 int WM_operator_call_py(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
420 {
421         wmWindowManager *wm=    CTX_wm_manager(C);
422         wmOperator *op=                 wm_operator_create(wm, ot, properties, reports);
423         int retval=                             op->type->exec(C, op);
424         
425         if (reports)
426                 op->reports= NULL; /* dont let the operator free reports passed to this function */
427         WM_operator_free(op);
428         
429         return retval;
430 }
431
432
433 /* ********************* handlers *************** */
434
435 /* not handler itself, is called by UI to move handlers to other queues, so don't close modal ones */
436 static void wm_event_free_handler(wmEventHandler *handler)
437 {
438         
439 }
440
441 /* called on exit or remove area, only here call cancel callback */
442 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
443 {
444         wmEventHandler *handler;
445         
446         /* C is zero on freeing database, modal handlers then already were freed */
447         while((handler=handlers->first)) {
448                 BLI_remlink(handlers, handler);
449                 
450                 if(handler->op) {
451                         if(handler->op->type->cancel) {
452                                 ScrArea *area= CTX_wm_area(C);
453                                 ARegion *region= CTX_wm_region(C);
454                                 
455                                 CTX_wm_area_set(C, handler->op_area);
456                                 CTX_wm_region_set(C, handler->op_region);
457
458                                 handler->op->type->cancel(C, handler->op);
459
460                                 CTX_wm_area_set(C, area);
461                                 CTX_wm_region_set(C, region);
462                         }
463
464                         WM_operator_free(handler->op);
465                 }
466                 else if(handler->ui_remove) {
467                         ScrArea *area= CTX_wm_area(C);
468                         ARegion *region= CTX_wm_region(C);
469                         
470                         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
471                         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
472
473                         handler->ui_remove(C, handler->ui_userdata);
474
475                         CTX_wm_area_set(C, area);
476                         CTX_wm_region_set(C, region);
477                 }
478
479                 wm_event_free_handler(handler);
480                 MEM_freeN(handler);
481         }
482 }
483
484 /* do userdef mappings */
485 static int wm_userdef_event_map(int kmitype)
486 {
487         switch(kmitype) {
488                 case SELECTMOUSE:
489                         if(U.flag & USER_LMOUSESELECT)
490                                 return LEFTMOUSE;
491                         else
492                                 return RIGHTMOUSE;
493                         
494                 case ACTIONMOUSE:
495                         if(U.flag & USER_LMOUSESELECT)
496                                 return RIGHTMOUSE;
497                         else
498                                 return LEFTMOUSE;
499                         
500                 case WHEELOUTMOUSE:
501                         if(U.uiflag & USER_WHEELZOOMDIR)
502                                 return WHEELUPMOUSE;
503                         else
504                                 return WHEELDOWNMOUSE;
505                         
506                 case WHEELINMOUSE:
507                         if(U.uiflag & USER_WHEELZOOMDIR)
508                                 return WHEELDOWNMOUSE;
509                         else
510                                 return WHEELUPMOUSE;
511                         
512                 case EVT_TWEAK_A:
513                         if(U.flag & USER_LMOUSESELECT)
514                                 return EVT_TWEAK_R;
515                         else
516                                 return EVT_TWEAK_L;
517                         
518                 case EVT_TWEAK_S:
519                         if(U.flag & USER_LMOUSESELECT)
520                                 return EVT_TWEAK_L;
521                         else
522                                 return EVT_TWEAK_R;
523         }
524         
525         return kmitype;
526 }
527
528 static int wm_eventmatch(wmEvent *winevent, wmKeymapItem *kmi)
529 {
530         int kmitype= wm_userdef_event_map(kmi->type);
531
532         /* the matching rules */
533         if(kmitype==KM_TEXTINPUT)
534                 if(ISKEYBOARD(winevent->type)) return 1;
535         if(kmitype!=KM_ANY)
536                 if(winevent->type!=kmitype) return 0;
537         
538         if(kmi->val!=KM_ANY)
539                 if(winevent->val!=kmi->val) return 0;
540         
541         /* modifiers also check bits, so it allows modifier order */
542         if(kmi->shift!=KM_ANY)
543                 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
544         if(kmi->ctrl!=KM_ANY)
545                 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
546         if(kmi->alt!=KM_ANY)
547                 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
548         if(kmi->oskey!=KM_ANY)
549                 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
550         if(kmi->keymodifier)
551                 if(winevent->keymodifier!=kmi->keymodifier) return 0;
552         
553         return 1;
554 }
555
556 static int wm_event_always_pass(wmEvent *event)
557 {
558         /* some events we always pass on, to ensure proper communication */
559         return ELEM5(event->type, TIMER, TIMER0, TIMER1, TIMER2, TIMERJOBS);
560 }
561
562 /* Warning: this function removes a modal handler, when finished */
563 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
564 {
565         int retval= OPERATOR_PASS_THROUGH;
566         
567         /* derived, modal or blocking operator */
568         if(handler->op) {
569                 wmOperator *op= handler->op;
570                 wmOperatorType *ot= op->type;
571
572                 if(ot->modal) {
573                         /* we set context to where modal handler came from */
574                         ScrArea *area= CTX_wm_area(C);
575                         ARegion *region= CTX_wm_region(C);
576                         
577                         CTX_wm_area_set(C, handler->op_area);
578                         CTX_wm_region_set(C, handler->op_region);
579                         
580                         retval= ot->modal(C, op, event);
581
582                         /* putting back screen context, reval can pass trough after modal failures! */
583                         if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
584                                 CTX_wm_area_set(C, area);
585                                 CTX_wm_region_set(C, region);
586                         }
587                         else {
588                                 /* this special cases is for areas and regions that get removed */
589                                 CTX_wm_area_set(C, NULL);
590                                 CTX_wm_region_set(C, NULL);
591                         }
592
593                         if(!(retval & OPERATOR_RUNNING_MODAL))
594                                 if(op->reports->list.first)
595                                         uiPupMenuReports(C, op->reports);
596
597                         if (retval & OPERATOR_FINISHED) {
598                                 if(G.f & G_DEBUG)
599                                         wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
600                         }                       
601
602                         if(retval & OPERATOR_FINISHED) {
603                                 if(ot->flag & OPTYPE_UNDO)
604                                         ED_undo_push_op(C, op);
605                                 
606                                 if(ot->flag & OPTYPE_REGISTER)
607                                         wm_operator_register(CTX_wm_manager(C), op);
608                                 else
609                                         WM_operator_free(op);
610                                 handler->op= NULL;
611                         }
612                         else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
613                                 WM_operator_free(op);
614                                 handler->op= NULL;
615                         }
616                         
617                         /* remove modal handler, operator itself should have been cancelled and freed */
618                         if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
619                                 BLI_remlink(handlers, handler);
620                                 wm_event_free_handler(handler);
621                                 MEM_freeN(handler);
622                                 
623                                 /* prevent silly errors from operator users */
624                                 //retval &= ~OPERATOR_PASS_THROUGH;
625                         }
626                         
627                 }
628                 else
629                         printf("wm_handler_operator_call error\n");
630         }
631         else {
632                 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname);
633
634                 if(ot)
635                         retval= wm_operator_invoke(C, ot, event, properties);
636         }
637
638         if(retval & OPERATOR_PASS_THROUGH)
639                 return WM_HANDLER_CONTINUE;
640
641         return WM_HANDLER_BREAK;
642 }
643
644 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event)
645 {
646         ScrArea *area= CTX_wm_area(C);
647         ARegion *region= CTX_wm_region(C);
648         int retval;
649                         
650         /* we set context to where ui handler came from */
651         if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
652         if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
653
654         retval= handler->ui_handle(C, event, handler->ui_userdata);
655
656         /* putting back screen context */
657         if((retval != WM_UI_HANDLER_BREAK) || wm_event_always_pass(event)) {
658                 CTX_wm_area_set(C, area);
659                 CTX_wm_region_set(C, region);
660         }
661         else {
662                 /* this special cases is for areas and regions that get removed */
663                 CTX_wm_area_set(C, NULL);
664                 CTX_wm_region_set(C, NULL);
665         }
666
667         if(retval == WM_UI_HANDLER_BREAK)
668                 return WM_HANDLER_BREAK;
669
670         return WM_HANDLER_CONTINUE;
671 }
672
673 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
674 {
675         if(handler->bbwin) {
676                 if(handler->bblocal) {
677                         rcti rect= *handler->bblocal;
678                         BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
679                         return BLI_in_rcti(&rect, event->x, event->y);
680                 }
681                 else 
682                         return BLI_in_rcti(handler->bbwin, event->x, event->y);
683         }
684         return 1;
685 }
686
687 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
688 {
689         wmEventHandler *handler, *nexthandler;
690         int action= WM_HANDLER_CONTINUE;
691
692         if(handlers==NULL) return action;
693         
694         /* modal handlers can get removed in this loop, we keep the loop this way */
695         for(handler= handlers->first; handler; handler= nexthandler) {
696                 nexthandler= handler->next;
697
698                 /* optional boundbox */
699                 if(handler_boundbox_test(handler, event)) {
700                 
701                         /* modal+blocking handler */
702                         if(handler->flag & WM_HANDLER_BLOCKING)
703                                 action= WM_HANDLER_BREAK;
704
705                         if(handler->keymap) {
706                                 wmKeymapItem *kmi;
707                                 
708                                 for(kmi= handler->keymap->first; kmi; kmi= kmi->next) {
709                                         if(wm_eventmatch(event, kmi)) {
710                                                 
711                                                 event->keymap_idname= kmi->idname;      /* weak, but allows interactive callback to not use rawkey */
712                                                 
713                                                 action= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
714                                                 if(action==WM_HANDLER_BREAK)  /* not wm_event_always_pass(event) here, it denotes removed handler */
715                                                         break;
716                                         }
717                                 }
718                         }
719                         else if(handler->ui_handle) {
720                                 action= wm_handler_ui_call(C, handler, event);
721                         }
722                         else {
723                                 /* modal, swallows all */
724                                 action= wm_handler_operator_call(C, handlers, handler, event, NULL);
725                         }
726
727                         if(!wm_event_always_pass(event) && action==WM_HANDLER_BREAK)
728                                 break;
729                 }
730                 
731                 /* fileread case */
732                 if(CTX_wm_window(C)==NULL)
733                         break;
734         }
735         return action;
736 }
737
738 static int wm_event_inside_i(wmEvent *event, rcti *rect)
739 {
740         if(BLI_in_rcti(rect, event->x, event->y))
741            return 1;
742         if(event->type==MOUSEMOVE) {
743                 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
744                         return 1;
745                 }
746                 return 0;
747         }
748         return 0;
749 }
750
751 static ScrArea *area_event_inside(bContext *C, int x, int y)
752 {
753         bScreen *screen= CTX_wm_screen(C);
754         ScrArea *sa;
755         
756         if(screen)
757                 for(sa= screen->areabase.first; sa; sa= sa->next)
758                         if(BLI_in_rcti(&sa->totrct, x, y))
759                                 return sa;
760         return NULL;
761 }
762
763 static ARegion *region_event_inside(bContext *C, int x, int y)
764 {
765         bScreen *screen= CTX_wm_screen(C);
766         ScrArea *area= CTX_wm_area(C);
767         ARegion *ar;
768         
769         if(screen && area)
770                 for(ar= area->regionbase.first; ar; ar= ar->next)
771                         if(BLI_in_rcti(&ar->winrct, x, y))
772                                 return ar;
773         return NULL;
774 }
775
776 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
777 {
778         if(ar) {
779                 for(; pc; pc= pc->next) {
780                         if(pc->poll(C)) {
781                                 wmWindow *win= CTX_wm_window(C);
782                                 win->screen->do_draw_paintcursor= 1;
783
784                                 if(win->drawmethod != USER_DRAW_TRIPLE)
785                                         ED_region_tag_redraw(ar);
786                         }
787                 }
788         }
789 }
790
791 /* called on mousemove, check updates for paintcursors */
792 /* context was set on active area and region */
793 static void wm_paintcursor_test(bContext *C, wmEvent *event)
794 {
795         wmWindowManager *wm= CTX_wm_manager(C);
796         
797         if(wm->paintcursors.first) {
798                 ARegion *ar= CTX_wm_region(C);
799                 if(ar)
800                         wm_paintcursor_tag(C, wm->paintcursors.first, ar);
801                 
802                 /* if previous position was not in current region, we have to set a temp new context */
803                 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
804                         ScrArea *sa= CTX_wm_area(C);
805                         
806                         CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
807                         CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
808
809                         wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
810                         
811                         CTX_wm_area_set(C, sa);
812                         CTX_wm_region_set(C, ar);
813                 }
814         }
815 }
816
817 /* called in main loop */
818 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
819 void wm_event_do_handlers(bContext *C)
820 {
821         wmWindow *win;
822
823         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
824                 wmEvent *event;
825                 
826                 if( win->screen==NULL )
827                         wm_event_free_all(win);
828                 
829                 while( (event= win->queue.first) ) {
830                         int action;
831                         
832                         CTX_wm_window_set(C, win);
833                         
834                         /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
835                         CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
836                         CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
837                         
838                         /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
839                         wm_window_make_drawable(C, win);
840                         
841                         action= wm_handlers_do(C, event, &win->handlers);
842                         
843                         /* fileread case */
844                         if(CTX_wm_window(C)==NULL) {
845                                 return;
846                         }
847                         
848                         /* builtin tweak, if action is break it removes tweak */
849                         if(!wm_event_always_pass(event))
850                                 wm_tweakevent_test(C, event, action);
851                         
852                         if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
853                                 ScrArea *sa;
854                                 ARegion *ar;
855                                 int doit= 0;
856                                 
857                                 /* XXX to solve, here screen handlers? */
858                                 if(!wm_event_always_pass(event)) {
859                                         if(event->type==MOUSEMOVE) {
860                                                 /* state variables in screen, cursors */
861                                                 ED_screen_set_subwinactive(win, event); 
862                                                 /* for regions having custom cursors */
863                                                 wm_paintcursor_test(C, event);
864                                         }
865                                 }
866                                 
867                                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
868                                         if(wm_event_always_pass(event) || wm_event_inside_i(event, &sa->totrct)) {
869                                                 
870                                                 CTX_wm_area_set(C, sa);
871                                                 CTX_wm_region_set(C, NULL);
872                                                 action= wm_handlers_do(C, event, &sa->handlers);
873
874                                                 if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
875                                                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
876                                                                 if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct)) {
877                                                                         CTX_wm_region_set(C, ar);
878                                                                         action= wm_handlers_do(C, event, &ar->handlers);
879
880                                                                         doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
881                                                                         
882                                                                         if(!wm_event_always_pass(event)) {
883                                                                                 if(action==WM_HANDLER_BREAK)
884                                                                                         break;
885                                                                         }
886                                                                 }
887                                                         }
888                                                 }
889                                                 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
890                                         }
891                                 }
892                                 
893                                 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad? 
894                                    doing it on ghost queue gives errors when mousemoves go over area borders */
895                                 if(doit && win->screen->subwinactive != win->screen->mainwin) {
896                                         win->eventstate->prevx= event->x;
897                                         win->eventstate->prevy= event->y;
898                                 }
899                         }
900                         
901                         /* unlink and free here, blender-quit then frees all */
902                         BLI_remlink(&win->queue, event);
903                         wm_event_free(event);
904                         
905                 }
906                 CTX_wm_window_set(C, NULL);
907         }
908 }
909
910 /* lets not expose struct outside wm? */
911 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
912 {
913         handler->flag= flag;
914 }
915
916 wmEventHandler *WM_event_add_modal_handler(bContext *C, ListBase *handlers, wmOperator *op)
917 {
918         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
919         handler->op= op;
920         handler->op_area= CTX_wm_area(C);               /* means frozen screen context for modal handlers! */
921         handler->op_region= CTX_wm_region(C);
922         
923         BLI_addhead(handlers, handler);
924
925         return handler;
926 }
927
928 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, ListBase *keymap)
929 {
930         wmEventHandler *handler;
931
932         /* only allow same keymap once */
933         for(handler= handlers->first; handler; handler= handler->next)
934                 if(handler->keymap==keymap)
935                         return handler;
936         
937         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
938         BLI_addtail(handlers, handler);
939         handler->keymap= keymap;
940
941         return handler;
942 }
943
944 /* priorities not implemented yet, for time being just insert in begin of list */
945 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, ListBase *keymap, int priority)
946 {
947         wmEventHandler *handler;
948         
949         WM_event_remove_keymap_handler(handlers, keymap);
950         
951         handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
952         BLI_addhead(handlers, handler);
953         handler->keymap= keymap;
954         
955         return handler;
956 }
957
958 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, ListBase *keymap, rcti *bblocal, rcti *bbwin)
959 {
960         wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
961         
962         if(handler) {
963                 handler->bblocal= bblocal;
964                 handler->bbwin= bbwin;
965         }
966         return handler;
967 }
968
969 void WM_event_remove_keymap_handler(ListBase *handlers, ListBase *keymap)
970 {
971         wmEventHandler *handler;
972         
973         for(handler= handlers->first; handler; handler= handler->next) {
974                 if(handler->keymap==keymap) {
975                         BLI_remlink(handlers, handler);
976                         wm_event_free_handler(handler);
977                         MEM_freeN(handler);
978                         break;
979                 }
980         }
981 }
982
983 wmEventHandler *WM_event_add_ui_handler(bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
984 {
985         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
986         handler->ui_handle= func;
987         handler->ui_remove= remove;
988         handler->ui_userdata= userdata;
989         handler->ui_area= (C)? CTX_wm_area(C): NULL;
990         handler->ui_region= (C)? CTX_wm_region(C): NULL;
991         
992         BLI_addhead(handlers, handler);
993         
994         return handler;
995 }
996
997 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
998 {
999         wmEventHandler *handler;
1000         
1001         for(handler= handlers->first; handler; handler= handler->next) {
1002                 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1003                         BLI_remlink(handlers, handler);
1004                         wm_event_free_handler(handler);
1005                         MEM_freeN(handler);
1006                         break;
1007                 }
1008         }
1009 }
1010
1011 void WM_event_add_mousemove(bContext *C)
1012 {
1013         wmWindow *window= CTX_wm_window(C);
1014         wmEvent event= *(window->eventstate);
1015         event.type= MOUSEMOVE;
1016         event.prevx= event.x;
1017         event.prevy= event.y;
1018         wm_event_add(window, &event);
1019 }
1020
1021 /* for modal callbacks, check configuration for how to interpret exit with tweaks  */
1022 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
1023 {
1024         /* user preset or keymap? dunno... */
1025         int tweak_modal= (U.flag & USER_DRAGIMMEDIATE)==0;
1026         
1027         switch(tweak_event) {
1028                 case EVT_TWEAK_L:
1029                 case EVT_TWEAK_M:
1030                 case EVT_TWEAK_R:
1031                         if(evt->val==tweak_modal)
1032                                 return 1;
1033                 default:
1034                         /* this case is when modal callcback didnt get started with a tweak */
1035                         if(evt->val)
1036                                 return 1;
1037         }
1038         return 0;
1039 }
1040
1041
1042 /* ********************* ghost stuff *************** */
1043
1044 static int convert_key(GHOST_TKey key) 
1045 {
1046         if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
1047                 return (AKEY + ((int) key - GHOST_kKeyA));
1048         } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
1049                 return (ZEROKEY + ((int) key - GHOST_kKey0));
1050         } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
1051                 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
1052         } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
1053                 return (F1KEY + ((int) key - GHOST_kKeyF1));
1054         } else {
1055                 switch (key) {
1056                         case GHOST_kKeyBackSpace:               return BACKSPACEKEY;
1057                         case GHOST_kKeyTab:                             return TABKEY;
1058                         case GHOST_kKeyLinefeed:                return LINEFEEDKEY;
1059                         case GHOST_kKeyClear:                   return 0;
1060                         case GHOST_kKeyEnter:                   return RETKEY;
1061                                 
1062                         case GHOST_kKeyEsc:                             return ESCKEY;
1063                         case GHOST_kKeySpace:                   return SPACEKEY;
1064                         case GHOST_kKeyQuote:                   return QUOTEKEY;
1065                         case GHOST_kKeyComma:                   return COMMAKEY;
1066                         case GHOST_kKeyMinus:                   return MINUSKEY;
1067                         case GHOST_kKeyPeriod:                  return PERIODKEY;
1068                         case GHOST_kKeySlash:                   return SLASHKEY;
1069                                 
1070                         case GHOST_kKeySemicolon:               return SEMICOLONKEY;
1071                         case GHOST_kKeyEqual:                   return EQUALKEY;
1072                                 
1073                         case GHOST_kKeyLeftBracket:             return LEFTBRACKETKEY;
1074                         case GHOST_kKeyRightBracket:    return RIGHTBRACKETKEY;
1075                         case GHOST_kKeyBackslash:               return BACKSLASHKEY;
1076                         case GHOST_kKeyAccentGrave:             return ACCENTGRAVEKEY;
1077                                 
1078                         case GHOST_kKeyLeftShift:               return LEFTSHIFTKEY;
1079                         case GHOST_kKeyRightShift:              return RIGHTSHIFTKEY;
1080                         case GHOST_kKeyLeftControl:             return LEFTCTRLKEY;
1081                         case GHOST_kKeyRightControl:    return RIGHTCTRLKEY;
1082                         case GHOST_kKeyCommand:                 return COMMANDKEY;
1083                         case GHOST_kKeyLeftAlt:                 return LEFTALTKEY;
1084                         case GHOST_kKeyRightAlt:                return RIGHTALTKEY;
1085                                 
1086                         case GHOST_kKeyCapsLock:                return CAPSLOCKKEY;
1087                         case GHOST_kKeyNumLock:                 return 0;
1088                         case GHOST_kKeyScrollLock:              return 0;
1089                                 
1090                         case GHOST_kKeyLeftArrow:               return LEFTARROWKEY;
1091                         case GHOST_kKeyRightArrow:              return RIGHTARROWKEY;
1092                         case GHOST_kKeyUpArrow:                 return UPARROWKEY;
1093                         case GHOST_kKeyDownArrow:               return DOWNARROWKEY;
1094                                 
1095                         case GHOST_kKeyPrintScreen:             return 0;
1096                         case GHOST_kKeyPause:                   return PAUSEKEY;
1097                                 
1098                         case GHOST_kKeyInsert:                  return INSERTKEY;
1099                         case GHOST_kKeyDelete:                  return DELKEY;
1100                         case GHOST_kKeyHome:                    return HOMEKEY;
1101                         case GHOST_kKeyEnd:                             return ENDKEY;
1102                         case GHOST_kKeyUpPage:                  return PAGEUPKEY;
1103                         case GHOST_kKeyDownPage:                return PAGEDOWNKEY;
1104                                 
1105                         case GHOST_kKeyNumpadPeriod:    return PADPERIOD;
1106                         case GHOST_kKeyNumpadEnter:             return PADENTER;
1107                         case GHOST_kKeyNumpadPlus:              return PADPLUSKEY;
1108                         case GHOST_kKeyNumpadMinus:             return PADMINUS;
1109                         case GHOST_kKeyNumpadAsterisk:  return PADASTERKEY;
1110                         case GHOST_kKeyNumpadSlash:             return PADSLASHKEY;
1111                                 
1112                         case GHOST_kKeyGrLess:              return GRLESSKEY; 
1113                                 
1114                         default:
1115                                 return UNKNOWNKEY;      /* GHOST_kKeyUnknown */
1116                 }
1117         }
1118 }
1119
1120 /* adds customdata to event */
1121 static void update_tablet_data(wmWindow *win, wmEvent *event)
1122 {
1123         const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
1124         
1125         /* if there's tablet data from an active tablet device then add it */
1126         if ((td != NULL) && td->Active) {
1127                 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
1128                 
1129                 wmtab->Active = td->Active;
1130                 wmtab->Pressure = td->Pressure;
1131                 wmtab->Xtilt = td->Xtilt;
1132                 wmtab->Ytilt = td->Ytilt;
1133                 
1134                 event->custom= EVT_DATA_TABLET;
1135                 event->customdata= wmtab;
1136                 event->customdatafree= 1;
1137         } 
1138 }
1139
1140
1141 /* windows store own event queues, no bContext here */
1142 void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
1143 {
1144         wmEvent event, *evt= win->eventstate;
1145         
1146         /* initialize and copy state (only mouse x y and modifiers) */
1147         event= *evt;
1148         
1149         switch (type) {
1150                 /* mouse move */
1151                 case GHOST_kEventCursorMove: {
1152                         if(win->active) {
1153                                 GHOST_TEventCursorData *cd= customdata;
1154                                 int cx, cy;
1155
1156                                 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
1157                                 
1158                                 event.type= MOUSEMOVE;
1159                                 event.x= evt->x= cx;
1160                                 event.y= evt->y= (win->sizey-1) - cy;
1161                                 
1162                                 update_tablet_data(win, &event);
1163                                 wm_event_add(win, &event);
1164                         }
1165                         break;
1166                 }
1167                 /* mouse button */
1168                 case GHOST_kEventButtonDown:
1169                 case GHOST_kEventButtonUp: {
1170                         GHOST_TEventButtonData *bd= customdata;
1171                         event.val= (type==GHOST_kEventButtonDown);
1172                         
1173                         if (bd->button == GHOST_kButtonMaskLeft)
1174                                 event.type= LEFTMOUSE;
1175                         else if (bd->button == GHOST_kButtonMaskRight)
1176                                 event.type= RIGHTMOUSE;
1177                         else
1178                                 event.type= MIDDLEMOUSE;
1179                         
1180                         if(event.val)
1181                                 event.keymodifier= evt->keymodifier= event.type;
1182                         else
1183                                 event.keymodifier= evt->keymodifier= 0;
1184                         
1185                         update_tablet_data(win, &event);
1186                         wm_event_add(win, &event);
1187                         
1188                         break;
1189                 }
1190                 /* keyboard */
1191                 case GHOST_kEventKeyDown:
1192                 case GHOST_kEventKeyUp: {
1193                         GHOST_TEventKeyData *kd= customdata;
1194                         event.type= convert_key(kd->key);
1195                         event.ascii= kd->ascii;
1196                         event.val= (type==GHOST_kEventKeyDown); /* XXX eventmatch uses defines, bad code... */
1197                         
1198                         /* exclude arrow keys, esc, etc from text input */
1199                         if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>14))
1200                                 event.ascii= '\0';
1201                         
1202                         /* modifiers */
1203                         if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
1204                                 event.shift= evt->shift= event.val;
1205                                 if(event.val && (evt->ctrl || evt->alt || evt->oskey))
1206                                    event.shift= evt->shift = 3;         // define?
1207                         } 
1208                         else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
1209                                 event.ctrl= evt->ctrl= event.val;
1210                                 if(event.val && (evt->shift || evt->alt || evt->oskey))
1211                                    event.ctrl= evt->ctrl = 3;           // define?
1212                         } 
1213                         else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
1214                                 event.alt= evt->alt= event.val;
1215                                 if(event.val && (evt->ctrl || evt->shift || evt->oskey))
1216                                    event.alt= evt->alt = 3;             // define?
1217                         } 
1218                         else if (event.type==COMMANDKEY) {
1219                                 event.oskey= evt->oskey= event.val;
1220                                 if(event.val && (evt->ctrl || evt->alt || evt->shift))
1221                                    event.oskey= evt->oskey = 3;         // define?
1222                         }
1223                         
1224                         /* if test_break set, it catches this. Keep global for now? */
1225                         if(event.type==ESCKEY)
1226                                 G.afbreek= 1;
1227                         
1228                         wm_event_add(win, &event);
1229                         
1230                         break;
1231                 }
1232                         
1233                 case GHOST_kEventWheel: {
1234                         GHOST_TEventWheelData* wheelData = customdata;
1235                         
1236                         if (wheelData->z > 0)
1237                                 event.type= WHEELUPMOUSE;
1238                         else
1239                                 event.type= WHEELDOWNMOUSE;
1240                         
1241                         event.val= KM_PRESS;
1242                         wm_event_add(win, &event);
1243                         
1244                         break;
1245                 }
1246                 case GHOST_kEventTimer: {
1247                         event.type= TIMER;
1248                         event.custom= EVT_DATA_TIMER;
1249                         event.customdata= customdata;
1250                         wm_event_add(win, &event);
1251
1252                         break;
1253                 }
1254
1255                 case GHOST_kEventUnknown:
1256                 case GHOST_kNumEventTypes:
1257                         break;
1258         }
1259 }
1260