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