At last... this merge should finally do the trick!
[blender-staging.git] / source / blender / editors / screen / screen_ops.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) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 #include <math.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_arithb.h"
32 #include "BLI_blenlib.h"
33 #include "BLI_editVert.h"
34
35 #include "DNA_armature_types.h"
36 #include "DNA_image_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_mesh_types.h"
39 #include "DNA_curve_types.h"
40 #include "DNA_scene_types.h"
41
42 #include "BKE_blender.h"
43 #include "BKE_context.h"
44 #include "BKE_customdata.h"
45 #include "BKE_global.h"
46 #include "BKE_image.h"
47 #include "BKE_idprop.h"
48 #include "BKE_library.h"
49 #include "BKE_main.h"
50 #include "BKE_mesh.h"
51 #include "BKE_multires.h"
52 #include "BKE_report.h"
53 #include "BKE_screen.h"
54 #include "BKE_utildefines.h"
55
56 #include "WM_api.h"
57 #include "WM_types.h"
58
59 #include "ED_util.h"
60 #include "ED_screen.h"
61 #include "ED_mesh.h"
62 #include "ED_screen_types.h"
63
64 #include "RE_pipeline.h"
65 #include "IMB_imbuf.h"
66 #include "IMB_imbuf_types.h"
67
68 #include "RNA_access.h"
69 #include "RNA_define.h"
70
71 #include "UI_interface.h"
72 #include "UI_resources.h"
73
74 #include "screen_intern.h"      /* own module include */
75
76 /* ************** Exported Poll tests ********************** */
77
78 int ED_operator_regionactive(bContext *C)
79 {
80         if(CTX_wm_window(C)==NULL) return 0;
81         if(CTX_wm_screen(C)==NULL) return 0;
82         if(CTX_wm_region(C)==NULL) return 0;
83         return 1;
84 }
85
86 int ED_operator_areaactive(bContext *C)
87 {
88         if(CTX_wm_window(C)==NULL) return 0;
89         if(CTX_wm_screen(C)==NULL) return 0;
90         if(CTX_wm_area(C)==NULL) return 0;
91         return 1;
92 }
93
94 int ED_operator_screenactive(bContext *C)
95 {
96         if(CTX_wm_window(C)==NULL) return 0;
97         if(CTX_wm_screen(C)==NULL) return 0;
98         return 1;
99 }
100
101 /* when mouse is over area-edge */
102 int ED_operator_screen_mainwinactive(bContext *C)
103 {
104         if(CTX_wm_window(C)==NULL) return 0;
105         if(CTX_wm_screen(C)==NULL) return 0;
106         if (CTX_wm_screen(C)->subwinactive!=CTX_wm_screen(C)->mainwin) return 0;
107         return 1;
108 }
109
110 int ED_operator_scene_editable(bContext *C)
111 {
112         Scene *scene= CTX_data_scene(C);
113         if(scene && scene->id.lib==NULL)
114                 return 1;
115         return 0;
116 }
117
118 static int ed_spacetype_test(bContext *C, int type)
119 {
120         if(ED_operator_areaactive(C)) {
121                 SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
122                 return sl && (sl->spacetype == type);
123         }
124         return 0;
125 }
126
127 int ED_operator_view3d_active(bContext *C)
128 {
129         return ed_spacetype_test(C, SPACE_VIEW3D);
130 }
131
132 int ED_operator_timeline_active(bContext *C)
133 {
134         return ed_spacetype_test(C, SPACE_TIME);
135 }
136
137 int ED_operator_outliner_active(bContext *C)
138 {
139         return ed_spacetype_test(C, SPACE_OUTLINER);
140 }
141
142 int ED_operator_file_active(bContext *C)
143 {
144         return ed_spacetype_test(C, SPACE_FILE);
145 }
146
147 int ED_operator_action_active(bContext *C)
148 {
149         return ed_spacetype_test(C, SPACE_ACTION);
150 }
151
152 int ED_operator_buttons_active(bContext *C)
153 {
154         return ed_spacetype_test(C, SPACE_BUTS);
155 }
156
157 int ED_operator_node_active(bContext *C)
158 {
159         if(ed_spacetype_test(C, SPACE_NODE)) {
160                 SpaceNode *snode= (SpaceNode *)CTX_wm_space_data(C);
161                 if(snode->edittree)
162                         return 1;
163         }
164         return 0;
165 }
166
167 // XXX rename
168 int ED_operator_ipo_active(bContext *C)
169 {
170         return ed_spacetype_test(C, SPACE_IPO);
171 }
172
173 int ED_operator_sequencer_active(bContext *C)
174 {
175         return ed_spacetype_test(C, SPACE_SEQ);
176 }
177
178 int ED_operator_image_active(bContext *C)
179 {
180         return ed_spacetype_test(C, SPACE_IMAGE);
181 }
182
183 int ED_operator_nla_active(bContext *C)
184 {
185         return ed_spacetype_test(C, SPACE_NLA);
186 }
187
188 int ED_operator_logic_active(bContext *C)
189 {
190         return ed_spacetype_test(C, SPACE_LOGIC);
191 }
192
193 int ED_operator_object_active(bContext *C)
194 {
195         return NULL != CTX_data_active_object(C);
196 }
197
198 int ED_operator_editmesh(bContext *C)
199 {
200         Object *obedit= CTX_data_edit_object(C);
201         if(obedit && obedit->type==OB_MESH)
202                 return NULL != ((Mesh *)obedit->data)->edit_mesh;
203         return 0;
204 }
205
206 int ED_operator_editarmature(bContext *C)
207 {
208         Object *obedit= CTX_data_edit_object(C);
209         if(obedit && obedit->type==OB_ARMATURE)
210                 return NULL != ((bArmature *)obedit->data)->edbo;
211         return 0;
212 }
213
214 int ED_operator_posemode(bContext *C)
215 {
216         Object *obact= CTX_data_active_object(C);
217         Object *obedit= CTX_data_edit_object(C);
218         
219         if ((obact != obedit) && (obact) && (obact->type==OB_ARMATURE))
220                 return (obact->flag & OB_POSEMODE)!=0;
221                 
222         return 0;
223 }
224
225
226 int ED_operator_uvedit(bContext *C)
227 {
228         Object *obedit= CTX_data_edit_object(C);
229         EditMesh *em= NULL;
230
231         if(obedit && obedit->type==OB_MESH)
232                 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
233
234         if(em && (em->faces.first) && (CustomData_has_layer(&em->fdata, CD_MTFACE))) {
235                 BKE_mesh_end_editmesh(obedit->data, em);
236                 return 1;
237         }
238
239         if(obedit)
240                 BKE_mesh_end_editmesh(obedit->data, em);
241         return 0;
242 }
243
244 int ED_operator_uvmap(bContext *C)
245 {
246         Object *obedit= CTX_data_edit_object(C);
247         EditMesh *em= NULL;
248
249         if(obedit && obedit->type==OB_MESH)
250                 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
251
252         if(em && (em->faces.first)) {
253                 BKE_mesh_end_editmesh(obedit->data, em);
254                 return 1;
255         }
256
257         if(obedit)
258                 BKE_mesh_end_editmesh(obedit->data, em);
259         return 0;
260 }
261
262 int ED_operator_editsurfcurve(bContext *C)
263 {
264         Object *obedit= CTX_data_edit_object(C);
265         if(obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
266                 return NULL != ((Curve *)obedit->data)->editnurb;
267         return 0;
268 }
269
270
271 int ED_operator_editcurve(bContext *C)
272 {
273         Object *obedit= CTX_data_edit_object(C);
274         if(obedit && obedit->type==OB_CURVE)
275                 return NULL != ((Curve *)obedit->data)->editnurb;
276         return 0;
277 }
278
279 int ED_operator_editsurf(bContext *C)
280 {
281         Object *obedit= CTX_data_edit_object(C);
282         if(obedit && obedit->type==OB_SURF)
283                 return NULL != ((Curve *)obedit->data)->editnurb;
284         return 0;
285 }
286
287 int ED_operator_editfont(bContext *C)
288 {
289         Object *obedit= CTX_data_edit_object(C);
290         if(obedit && obedit->type==OB_FONT)
291                 return NULL != ((Curve *)obedit->data)->editfont;
292         return 0;
293 }
294
295 /* *************************** action zone operator ************************** */
296
297 /* operator state vars used:  
298         none
299
300 functions:
301
302         apply() set actionzone event
303
304         exit()  free customdata
305         
306 callbacks:
307
308         exec()  never used
309
310         invoke() check if in zone  
311                 add customdata, put mouseco and area in it
312                 add modal handler
313
314         modal() accept modal events while doing it
315                 call apply() with gesture info, active window, nonactive window
316                 call exit() and remove handler when LMB confirm
317
318 */
319
320 typedef struct sActionzoneData {
321         ScrArea *sa1, *sa2;
322         AZone *az;
323         int x, y, gesture_dir, modifier;
324 } sActionzoneData;
325
326 /* used by other operators too */
327 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
328 {
329         ScrArea *sa= NULL;
330         sa= scr->areabase.first;
331         while(sa) {
332                 if(BLI_in_rcti(&sa->totrct, x, y)) break;
333                 sa= sa->next;
334         }
335         
336         return sa;
337 }
338
339 /* quick poll to save operators to be created and handled */
340 static int actionzone_area_poll(bContext *C)
341 {
342         wmWindow *win= CTX_wm_window(C);
343         ScrArea *sa= CTX_wm_area(C);
344         
345         if(sa && win) {
346                 AZone *az;
347                 int x= win->eventstate->x;
348                 int y= win->eventstate->y;
349                 
350                 for(az= sa->actionzones.first; az; az= az->next)
351                         if(BLI_in_rcti(&az->rect, x, y))
352                            return 1;
353         }       
354         return 0;
355 }
356
357 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
358 {
359         AZone *az= NULL;
360         
361         for(az= sa->actionzones.first; az; az= az->next) {
362                 if(BLI_in_rcti(&az->rect, x, y)) {
363                         if(az->type == AZONE_AREA) {
364                                 if(IsPointInTri2DInts(az->x1, az->y1, az->x2, az->y2, x, y)) 
365                                         break;
366                         }
367                         else if(az->type == AZONE_REGION) {
368                                 float v1[2], v2[2], v3[2], pt[2];
369                                 
370                                 v1[0]= az->x1; v1[1]= az->y1;
371                                 v2[0]= az->x2; v2[1]= az->y2;
372                                 v3[0]= az->x3; v3[1]= az->y3;
373                                 pt[0]= x; pt[1]=y;
374
375                                 if(IsPointInTri2D(v1, v2, v3, pt)) 
376                                         break;
377                         }
378                 }
379         }
380         
381         return az;
382 }
383
384
385 static void actionzone_exit(bContext *C, wmOperator *op)
386 {
387         if(op->customdata)
388                 MEM_freeN(op->customdata);
389         op->customdata= NULL;
390 }
391
392 /* send EVT_ACTIONZONE event */
393 static void actionzone_apply(bContext *C, wmOperator *op, int type)
394 {
395         wmEvent event;
396         wmWindow *win= CTX_wm_window(C);
397         sActionzoneData *sad= op->customdata;
398         
399         sad->modifier= RNA_int_get(op->ptr, "modifier");
400         
401         event= *(win->eventstate);      /* XXX huh huh? make api call */
402         if(type==AZONE_AREA)
403                 event.type= EVT_ACTIONZONE_AREA;
404         else
405                 event.type= EVT_ACTIONZONE_REGION;
406         event.customdata= op->customdata;
407         event.customdatafree= TRUE;
408         op->customdata= NULL;
409         
410         wm_event_add(win, &event);
411 }
412
413 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
414 {
415         AZone *az= is_in_area_actionzone(CTX_wm_area(C), event->x, event->y);
416         sActionzoneData *sad;
417         
418         /* quick escape */
419         if(az==NULL)
420                 return OPERATOR_PASS_THROUGH;
421         
422         /* ok we do the actionzone */
423         sad= op->customdata= MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
424         sad->sa1= CTX_wm_area(C);
425         sad->az= az;
426         sad->x= event->x; sad->y= event->y;
427         
428         /* region azone directly reacts on mouse clicks */
429         if(sad->az->type==AZONE_REGION) {
430                 actionzone_apply(C, op, AZONE_REGION);
431                 actionzone_exit(C, op);
432                 return OPERATOR_FINISHED;
433         }
434         else {
435                 /* add modal handler */
436                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
437                 
438                 return OPERATOR_RUNNING_MODAL;
439         }
440 }
441
442
443 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
444 {
445         sActionzoneData *sad= op->customdata;
446         int deltax, deltay;
447         int mindelta= sad->az->type==AZONE_REGION?1:12;
448         
449         switch(event->type) {
450                 case MOUSEMOVE:
451                         /* calculate gesture direction */
452                         deltax= (event->x - sad->x);
453                         deltay= (event->y - sad->y);
454                         
455                         if(deltay > ABS(deltax))
456                                 sad->gesture_dir= 'n';
457                         else if(deltax > ABS(deltay))
458                                 sad->gesture_dir= 'e';
459                         else if(deltay < -ABS(deltax))
460                                 sad->gesture_dir= 's';
461                         else
462                                 sad->gesture_dir= 'w';
463                         
464                         /* gesture is large enough? */
465                         if(ABS(deltax) > mindelta || ABS(deltay) > mindelta) {
466                                 
467                                 /* second area, for join */
468                                 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
469                                 /* apply sends event */
470                                 actionzone_apply(C, op, sad->az->type);
471                                 actionzone_exit(C, op);
472                                 
473                                 return OPERATOR_FINISHED;
474                         }
475                                 break;
476                 case ESCKEY:
477                         actionzone_exit(C, op);
478                         return OPERATOR_CANCELLED;
479                 case LEFTMOUSE:                         
480                         actionzone_exit(C, op);
481                         return OPERATOR_CANCELLED;
482
483         }
484         
485         return OPERATOR_RUNNING_MODAL;
486 }
487
488 void SCREEN_OT_actionzone(wmOperatorType *ot)
489 {
490         /* identifiers */
491         ot->name= "Handle area action zones";
492         ot->idname= "SCREEN_OT_actionzone";
493         
494         ot->invoke= actionzone_invoke;
495         ot->modal= actionzone_modal;
496         
497         ot->poll= actionzone_area_poll;
498         RNA_def_int(ot->srna, "modifier", 0, 0, 2, "modifier", "modifier state", 0, 2);
499 }
500
501 /* ************** swap area operator *********************************** */
502
503 /* operator state vars used:  
504                                         sa1             start area
505                                         sa2             area to swap with
506
507         functions:
508
509         init()   set custom data for operator, based on actionzone event custom data
510
511         cancel()        cancel the operator
512
513         exit()  cleanup, send notifier
514
515         callbacks:
516
517         invoke() gets called on shift+lmb drag in actionzone
518             call init(), add handler
519
520         modal()  accept modal events while doing it
521
522 */
523
524 typedef struct sAreaSwapData {
525         ScrArea *sa1, *sa2;
526 } sAreaSwapData;
527
528 static int area_swap_init(bContext *C, wmOperator *op, wmEvent *event)
529 {
530         sAreaSwapData *sd= NULL;
531         sActionzoneData *sad= event->customdata;
532
533         if(sad==NULL || sad->sa1==NULL)
534                                         return 0;
535         
536         sd= MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
537         sd->sa1= sad->sa1;
538         sd->sa2= sad->sa2;
539         op->customdata= sd;
540
541         return 1;
542 }
543
544
545 static void area_swap_exit(bContext *C, wmOperator *op)
546 {
547         if(op->customdata)
548                 MEM_freeN(op->customdata);
549         op->customdata= NULL;
550 }
551
552 static int area_swap_cancel(bContext *C, wmOperator *op)
553 {
554         area_swap_exit(C, op);
555         return OPERATOR_CANCELLED;
556 }
557
558 static int area_swap_invoke(bContext *C, wmOperator *op, wmEvent *event)
559 {
560
561         if(!area_swap_init(C, op, event))
562                 return OPERATOR_PASS_THROUGH;
563
564         /* add modal handler */
565         WM_cursor_modal(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
566         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
567         
568         return OPERATOR_RUNNING_MODAL;
569
570 }
571
572 static int area_swap_modal(bContext *C, wmOperator *op, wmEvent *event)
573 {
574         sActionzoneData *sad= op->customdata;
575
576         switch(event->type) {
577                 case MOUSEMOVE:
578                         /* second area, for join */
579                         sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
580                         break;
581                 case LEFTMOUSE: /* release LMB */
582                         if(event->val==0) {
583                                 if(sad->sa1 == sad->sa2) {
584
585                                         return area_swap_cancel(C, op);
586                                 }
587                                 ED_area_swapspace(C, sad->sa1, sad->sa2);
588
589                                 area_swap_exit(C, op);
590
591                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
592
593                                 return OPERATOR_FINISHED;
594                         }
595                         break;
596
597                 case ESCKEY:
598                         return area_swap_cancel(C, op);
599         }
600         return OPERATOR_RUNNING_MODAL;
601 }
602
603 static void SCREEN_OT_area_swap(wmOperatorType *ot)
604 {
605         ot->name= "Swap areas";
606         ot->idname= "SCREEN_OT_area_swap";
607
608         ot->invoke= area_swap_invoke;
609         ot->modal= area_swap_modal;
610         ot->poll= ED_operator_areaactive;
611 }
612
613 /* *********** Duplicate area as new window operator ****************** */
614
615 /* operator callback */
616 static int area_dupli_invoke(bContext *C, wmOperator *op, wmEvent *event)
617 {
618         wmWindow *newwin, *win;
619         bScreen *newsc, *sc;
620         ScrArea *sa;
621         rcti rect;
622         
623         win= CTX_wm_window(C);
624         sc= CTX_wm_screen(C);
625         sa= CTX_wm_area(C);
626         
627         /* XXX hrmf! */
628         if(event->type==EVT_ACTIONZONE_AREA) {
629                 sActionzoneData *sad= event->customdata;
630
631                 if(sad==NULL)
632                         return OPERATOR_PASS_THROUGH;
633         
634                 sa= sad->sa1;
635         }
636         
637         /*  poll() checks area context, but we don't accept full-area windows */
638         if(sc->full != SCREENNORMAL) {
639                 if(event->type==EVT_ACTIONZONE_AREA)
640                         actionzone_exit(C, op);
641                 return OPERATOR_CANCELLED;
642         }
643         
644         /* adds window to WM */
645         rect= sa->totrct;
646         BLI_translate_rcti(&rect, win->posx, win->posy);
647         newwin= WM_window_open(C, &rect);
648         
649         /* allocs new screen and adds to newly created window, using window size */
650         newsc= screen_add(newwin, CTX_data_scene(C), sc->id.name+2);
651         newwin->screen= newsc;
652         
653         /* copy area to new screen */
654         area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
655         
656         /* screen, areas init */
657         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
658
659         if(event->type==EVT_ACTIONZONE_AREA)
660                 actionzone_exit(C, op);
661         
662         return OPERATOR_FINISHED;
663 }
664
665 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
666 {
667         ot->name= "Duplicate Area into New Window";
668         ot->idname= "SCREEN_OT_area_dupli";
669         
670         ot->invoke= area_dupli_invoke;
671         ot->poll= ED_operator_areaactive;
672 }
673
674
675 /* ************** move area edge operator *********************************** */
676
677 /* operator state vars used:  
678            x, y                         mouse coord near edge
679            delta            movement of edge
680
681         functions:
682
683         init()   set default property values, find edge based on mouse coords, test
684             if the edge can be moved, select edges, calculate min and max movement
685
686         apply() apply delta on selection
687
688         exit()  cleanup, send notifier
689
690         cancel() cancel moving
691
692         callbacks:
693
694         exec()   execute without any user interaction, based on properties
695             call init(), apply(), exit()
696
697         invoke() gets called on mouse click near edge
698             call init(), add handler
699
700         modal()  accept modal events while doing it
701                         call apply() with delta motion
702             call exit() and remove handler
703
704 */
705
706 typedef struct sAreaMoveData {
707         int bigger, smaller, origval;
708         char dir;
709 } sAreaMoveData;
710
711 /* helper call to move area-edge, sets limits */
712 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
713 {
714         ScrArea *sa;
715         
716         /* we check all areas and test for free space with MINSIZE */
717         *bigger= *smaller= 100000;
718         
719         for(sa= sc->areabase.first; sa; sa= sa->next) {
720                 if(dir=='h') {
721                         int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
722                         
723                         /* if top or down edge selected, test height */
724                         if(sa->v1->flag && sa->v4->flag)
725                                 *bigger= MIN2(*bigger, y1);
726                         else if(sa->v2->flag && sa->v3->flag)
727                                 *smaller= MIN2(*smaller, y1);
728                 }
729                 else {
730                         int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
731                         
732                         /* if left or right edge selected, test width */
733                         if(sa->v1->flag && sa->v2->flag)
734                                 *bigger= MIN2(*bigger, x1);
735                         else if(sa->v3->flag && sa->v4->flag)
736                                 *smaller= MIN2(*smaller, x1);
737                 }
738         }
739 }
740
741 /* validate selection inside screen, set variables OK */
742 /* return 0: init failed */
743 static int area_move_init (bContext *C, wmOperator *op)
744 {
745         bScreen *sc= CTX_wm_screen(C);
746         ScrEdge *actedge;
747         sAreaMoveData *md;
748         int x, y;
749
750         /* required properties */
751         x= RNA_int_get(op->ptr, "x");
752         y= RNA_int_get(op->ptr, "y");
753
754         /* setup */
755         actedge= screen_find_active_scredge(sc, x, y);
756         if(actedge==NULL) return 0;
757
758         md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
759         op->customdata= md;
760
761         md->dir= scredge_is_horizontal(actedge)?'h':'v';
762         if(md->dir=='h') md->origval= actedge->v1->vec.y;
763         else md->origval= actedge->v1->vec.x;
764         
765         select_connected_scredge(sc, actedge);
766         /* now all vertices with 'flag==1' are the ones that can be moved. */
767
768         area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
769         
770         return 1;
771 }
772
773 /* moves selected screen edge amount of delta, used by split & move */
774 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
775 {
776         wmWindow *win= CTX_wm_window(C);
777         bScreen *sc= CTX_wm_screen(C);
778         ScrVert *v1;
779         
780         delta= CLAMPIS(delta, -smaller, bigger);
781         
782         for (v1= sc->vertbase.first; v1; v1= v1->next) {
783                 if (v1->flag) {
784                         /* that way a nice AREAGRID  */
785                         if((dir=='v') && v1->vec.x>0 && v1->vec.x<win->sizex-1) {
786                                 v1->vec.x= origval + delta;
787                                 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
788                         }
789                         if((dir=='h') && v1->vec.y>0 && v1->vec.y<win->sizey-1) {
790                                 v1->vec.y= origval + delta;
791
792                                 v1->vec.y+= AREAGRID-1;
793                                 v1->vec.y-= (v1->vec.y % AREAGRID);
794                                 
795                                 /* prevent too small top header */
796                                 if(v1->vec.y > win->sizey-AREAMINY)
797                                         v1->vec.y= win->sizey-AREAMINY;
798                         }
799                 }
800         }
801
802         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
803 }
804
805 static void area_move_apply(bContext *C, wmOperator *op)
806 {
807         sAreaMoveData *md= op->customdata;
808         int delta;
809         
810         delta= RNA_int_get(op->ptr, "delta");
811         area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
812 }
813
814 static void area_move_exit(bContext *C, wmOperator *op)
815 {
816         if(op->customdata)
817                 MEM_freeN(op->customdata);
818         op->customdata= NULL;
819         
820         /* this makes sure aligned edges will result in aligned grabbing */
821         removedouble_scrverts(CTX_wm_screen(C));
822         removedouble_scredges(CTX_wm_screen(C));
823 }
824
825 static int area_move_exec(bContext *C, wmOperator *op)
826 {
827         if(!area_move_init(C, op))
828                 return OPERATOR_CANCELLED;
829         
830         area_move_apply(C, op);
831         area_move_exit(C, op);
832         
833         return OPERATOR_FINISHED;
834 }
835
836 /* interaction callback */
837 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
838 {
839         RNA_int_set(op->ptr, "x", event->x);
840         RNA_int_set(op->ptr, "y", event->y);
841
842         if(!area_move_init(C, op)) 
843                 return OPERATOR_PASS_THROUGH;
844         
845         /* add temp handler */
846         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
847         
848         return OPERATOR_RUNNING_MODAL;
849 }
850
851 static int area_move_cancel(bContext *C, wmOperator *op)
852 {
853
854         RNA_int_set(op->ptr, "delta", 0);
855         area_move_apply(C, op);
856         area_move_exit(C, op);
857
858         return OPERATOR_CANCELLED;
859 }
860
861 /* modal callback for while moving edges */
862 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
863 {
864         sAreaMoveData *md;
865         int delta, x, y;
866
867         md= op->customdata;
868
869         x= RNA_int_get(op->ptr, "x");
870         y= RNA_int_get(op->ptr, "y");
871
872         /* execute the events */
873         switch(event->type) {
874                 case MOUSEMOVE:
875                         delta= (md->dir == 'v')? event->x - x: event->y - y;
876                         RNA_int_set(op->ptr, "delta", delta);
877
878                         area_move_apply(C, op);
879                         break;
880                         
881                 case LEFTMOUSE:
882                         if(event->val==0) {
883                                 area_move_exit(C, op);
884                                 return OPERATOR_FINISHED;
885                         }
886                         break;
887                         
888                 case ESCKEY:
889                         return area_move_cancel(C, op);
890         }
891         
892         return OPERATOR_RUNNING_MODAL;
893 }
894
895 void SCREEN_OT_area_move(wmOperatorType *ot)
896 {
897         /* identifiers */
898         ot->name= "Move area edges";
899         ot->idname= "SCREEN_OT_area_move";
900
901         ot->exec= area_move_exec;
902         ot->invoke= area_move_invoke;
903         ot->cancel= area_move_cancel;
904         ot->modal= area_move_modal;
905
906         ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
907
908         /* rna */
909         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
910         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
911         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
912 }
913
914 /* ************** split area operator *********************************** */
915
916 /* 
917 operator state vars:  
918         fac              spit point
919         dir              direction 'v' or 'h'
920
921 operator customdata:
922         area                    pointer to (active) area
923         x, y                    last used mouse pos
924         (more, see below)
925
926 functions:
927
928         init()   set default property values, find area based on context
929
930         apply() split area based on state vars
931
932         exit()  cleanup, send notifier
933
934         cancel() remove duplicated area
935
936 callbacks:
937
938         exec()   execute without any user interaction, based on state vars
939             call init(), apply(), exit()
940
941         invoke() gets called on mouse click in action-widget
942             call init(), add modal handler
943                         call apply() with initial motion
944
945         modal()  accept modal events while doing it
946             call move-areas code with delta motion
947             call exit() or cancel() and remove handler
948
949 */
950
951 #define SPLIT_STARTED   1
952 #define SPLIT_PROGRESS  2
953
954 typedef struct sAreaSplitData
955 {
956         int x, y;       /* last used mouse position */
957         
958         int origval;                    /* for move areas */
959         int bigger, smaller;    /* constraints for moving new edge */
960         int delta;                              /* delta move edge */
961         int origmin, origsize;  /* to calculate fac, for property storage */
962
963         ScrEdge *nedge;                 /* new edge */
964         ScrArea *sarea;                 /* start area */
965         ScrArea *narea;                 /* new area */
966 } sAreaSplitData;
967
968 /* generic init, no UI stuff here */
969 static int area_split_init(bContext *C, wmOperator *op)
970 {
971         ScrArea *sa= CTX_wm_area(C);
972         sAreaSplitData *sd;
973         int dir;
974         
975         /* required context */
976         if(sa==NULL) return 0;
977         
978         /* required properties */
979         dir= RNA_enum_get(op->ptr, "direction");
980         
981         /* minimal size */
982         if(dir=='v' && sa->winx < 2*AREAMINX) return 0;
983         if(dir=='h' && sa->winy < 2*AREAMINY) return 0;
984            
985         /* custom data */
986         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
987         op->customdata= sd;
988         
989         sd->sarea= sa;
990         sd->origsize= dir=='v' ? sa->winx:sa->winy;
991         sd->origmin = dir=='v' ? sa->totrct.xmin:sa->totrct.ymin;
992         
993         return 1;
994 }
995
996 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
997 /* used with split operator */
998 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
999 {
1000         ScrVert *sav1= sa->v1;
1001         ScrVert *sav2= sa->v2;
1002         ScrVert *sav3= sa->v3;
1003         ScrVert *sav4= sa->v4;
1004         ScrVert *sbv1= sb->v1;
1005         ScrVert *sbv2= sb->v2;
1006         ScrVert *sbv3= sb->v3;
1007         ScrVert *sbv4= sb->v4;
1008         
1009         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
1010                 return screen_findedge(screen, sav1, sav2);
1011         }
1012         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
1013                 return screen_findedge(screen, sav2, sav3);
1014         }
1015         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
1016                 return screen_findedge(screen, sav3, sav4);
1017         }
1018         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
1019                 return screen_findedge(screen, sav1, sav4);
1020         }
1021
1022         return NULL;
1023 }
1024
1025
1026 /* do the split, return success */
1027 static int area_split_apply(bContext *C, wmOperator *op)
1028 {
1029         bScreen *sc= CTX_wm_screen(C);
1030         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1031         float fac;
1032         int dir;
1033         
1034         fac= RNA_float_get(op->ptr, "factor");
1035         dir= RNA_enum_get(op->ptr, "direction");
1036
1037         sd->narea= area_split(CTX_wm_window(C), sc, sd->sarea, dir, fac);
1038         
1039         if(sd->narea) {
1040                 ScrVert *sv;
1041                 
1042                 sd->nedge= area_findsharededge(sc, sd->sarea, sd->narea);
1043         
1044                 /* select newly created edge, prepare for moving edge */
1045                 for(sv= sc->vertbase.first; sv; sv= sv->next)
1046                         sv->flag = 0;
1047                 
1048                 sd->nedge->v1->flag= 1;
1049                 sd->nedge->v2->flag= 1;
1050
1051                 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
1052                 else sd->origval= sd->nedge->v1->vec.x;
1053
1054                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1055                 
1056                 return 1;
1057         }               
1058         
1059         return 0;
1060 }
1061
1062 static void area_split_exit(bContext *C, wmOperator *op)
1063 {
1064         if (op->customdata) {
1065                 MEM_freeN(op->customdata);
1066                 op->customdata = NULL;
1067         }
1068         
1069         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1070
1071         /* this makes sure aligned edges will result in aligned grabbing */
1072         removedouble_scrverts(CTX_wm_screen(C));
1073         removedouble_scredges(CTX_wm_screen(C));
1074 }
1075
1076
1077 /* UI callback, adds new handler */
1078 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
1079 {
1080         sAreaSplitData *sd;
1081         
1082         if(event->type==EVT_ACTIONZONE_AREA) {
1083                 sActionzoneData *sad= event->customdata;
1084                 int dir;
1085
1086                 if(sad->modifier>0) {
1087                         return OPERATOR_PASS_THROUGH;
1088                 }
1089                 
1090                 /* no full window splitting allowed */
1091                 if(CTX_wm_area(C)->full)
1092                         return OPERATOR_PASS_THROUGH;
1093                 
1094                 /* verify *sad itself */
1095                 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
1096                         return OPERATOR_PASS_THROUGH;
1097                 
1098                 /* is this our *sad? if areas not equal it should be passed on */
1099                 if(CTX_wm_area(C)!=sad->sa1 || sad->sa1!=sad->sa2)
1100                         return OPERATOR_PASS_THROUGH;
1101                 
1102                 /* prepare operator state vars */
1103                 if(sad->gesture_dir=='n' || sad->gesture_dir=='s') {
1104                         dir= 'h';
1105                         RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1106                 }
1107                 else {
1108                         dir= 'v';
1109                         RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1110                 }
1111                 RNA_enum_set(op->ptr, "direction", dir);
1112
1113                 /* general init, also non-UI case, adds customdata, sets area and defaults */
1114                 if(!area_split_init(C, op))
1115                         return OPERATOR_PASS_THROUGH;
1116                 
1117                 sd= (sAreaSplitData *)op->customdata;
1118                 
1119                 sd->x= event->x;
1120                 sd->y= event->y;
1121                 
1122                 /* do the split */
1123                 if(area_split_apply(C, op)) {
1124                         area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
1125                         
1126                         /* add temp handler for edge move or cancel */
1127                         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1128                         
1129                         return OPERATOR_RUNNING_MODAL;
1130                 }
1131                 
1132         }
1133         else {
1134                 /* nonmodal for now */
1135                 return op->type->exec(C, op);
1136         }
1137         
1138         return OPERATOR_PASS_THROUGH;
1139 }
1140
1141 /* function to be called outside UI context, or for redo */
1142 static int area_split_exec(bContext *C, wmOperator *op)
1143 {
1144         
1145         if(!area_split_init(C, op))
1146                 return OPERATOR_CANCELLED;
1147         
1148         area_split_apply(C, op);
1149         area_split_exit(C, op);
1150         
1151         return OPERATOR_FINISHED;
1152 }
1153
1154
1155 static int area_split_cancel(bContext *C, wmOperator *op)
1156 {
1157         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1158
1159         if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1160                 if (CTX_wm_area(C) == sd->narea) {
1161                         CTX_wm_area_set(C, NULL);
1162                         CTX_wm_region_set(C, NULL);
1163                 }
1164                 sd->narea = NULL;
1165         }
1166         area_split_exit(C, op);
1167
1168         return OPERATOR_CANCELLED;
1169 }
1170
1171 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
1172 {
1173         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1174         float fac;
1175         int dir;
1176
1177         /* execute the events */
1178         switch(event->type) {
1179                 case MOUSEMOVE:
1180                         dir= RNA_enum_get(op->ptr, "direction");
1181                         
1182                         sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
1183                         area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1184                         
1185                         fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
1186                         RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1187                         
1188                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1189                         break;
1190                         
1191                 case LEFTMOUSE:
1192                         if(event->val==0) { /* mouse up */
1193                                 area_split_exit(C, op);
1194                                 return OPERATOR_FINISHED;
1195                         }
1196                         break;
1197                 case RIGHTMOUSE: /* cancel operation */
1198                 case ESCKEY:
1199                         return area_split_cancel(C, op);
1200         }
1201         
1202         return OPERATOR_RUNNING_MODAL;
1203 }
1204
1205 static EnumPropertyItem prop_direction_items[] = {
1206         {'h', "HORIZONTAL", 0, "Horizontal", ""},
1207         {'v', "VERTICAL", 0, "Vertical", ""},
1208         {0, NULL, 0, NULL, NULL}};
1209
1210 void SCREEN_OT_area_split(wmOperatorType *ot)
1211 {
1212         ot->name = "Split area";
1213         ot->idname = "SCREEN_OT_area_split";
1214         
1215         ot->exec= area_split_exec;
1216         ot->invoke= area_split_invoke;
1217         ot->modal= area_split_modal;
1218         
1219         ot->poll= ED_operator_areaactive;
1220         ot->flag= OPTYPE_REGISTER;
1221         
1222         /* rna */
1223         RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1224         RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1225 }
1226
1227
1228
1229 /* ************** scale region edge operator *********************************** */
1230
1231 typedef struct RegionMoveData {
1232         ARegion *ar;
1233         int bigger, smaller, origval;
1234         int origx, origy;
1235         char edge;
1236         
1237 } RegionMoveData;
1238
1239 static int region_scale_invoke(bContext *C, wmOperator *op, wmEvent *event)
1240 {
1241         sActionzoneData *sad= event->customdata;
1242         AZone *az= sad->az;
1243         
1244         if(az->ar) {
1245                 RegionMoveData *rmd= MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1246                 
1247                 op->customdata= rmd;
1248                 
1249                 rmd->ar= az->ar;
1250                 rmd->edge= az->edge;
1251                 rmd->origx= event->x;
1252                 rmd->origy= event->y;
1253                 if(rmd->edge=='l' || rmd->edge=='r') 
1254                         rmd->origval= rmd->ar->type->minsizex;
1255                 else
1256                         rmd->origval= rmd->ar->type->minsizey;
1257                 
1258                 /* add temp handler */
1259                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1260                 
1261                 return OPERATOR_RUNNING_MODAL;
1262         }
1263         
1264         return OPERATOR_FINISHED;
1265 }
1266
1267 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1268 {
1269         RegionMoveData *rmd= op->customdata;
1270         int delta;
1271         
1272         /* execute the events */
1273         switch(event->type) {
1274                 case MOUSEMOVE:
1275                         
1276                         if(rmd->edge=='l' || rmd->edge=='r') {
1277                                 delta= event->x - rmd->origx;
1278                                 if(rmd->edge=='l') delta= -delta;
1279                                 rmd->ar->type->minsizex= rmd->origval + delta;
1280                                 CLAMP(rmd->ar->type->minsizex, 0, 1000);
1281                                 if(rmd->ar->type->minsizex < 10) {
1282                                         rmd->ar->type->minsizex= 10;
1283                                         rmd->ar->flag |= RGN_FLAG_HIDDEN;
1284                                 }
1285                                 else
1286                                         rmd->ar->flag &= ~RGN_FLAG_HIDDEN;
1287                         }
1288                         else {
1289                                 delta= event->y - rmd->origy;
1290                                 if(rmd->edge=='b') delta= -delta;
1291                                 rmd->ar->type->minsizey= rmd->origval + delta;
1292                                 CLAMP(rmd->ar->type->minsizey, 0, 1000);
1293                                 if(rmd->ar->type->minsizey < 10) {
1294                                         rmd->ar->type->minsizey= 10;
1295                                         rmd->ar->flag |= RGN_FLAG_HIDDEN;
1296                                 }
1297                                 else
1298                                         rmd->ar->flag &= ~RGN_FLAG_HIDDEN;
1299                         }
1300                         
1301                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1302                                         
1303                         break;
1304                         
1305                 case LEFTMOUSE:
1306                         if(event->val==0) {
1307                                 
1308                                 if(ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1309                                         rmd->ar->flag ^= RGN_FLAG_HIDDEN;
1310                                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1311                                 }                               
1312                                 MEM_freeN(op->customdata);
1313                                 op->customdata = NULL;
1314
1315                                 return OPERATOR_FINISHED;
1316                         }
1317                         break;
1318                         
1319                 case ESCKEY:
1320                         ;
1321         }
1322         
1323         return OPERATOR_RUNNING_MODAL;
1324 }
1325
1326
1327 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1328 {
1329         /* identifiers */
1330         ot->name= "Scale Region Size";
1331         ot->idname= "SCREEN_OT_region_scale";
1332         
1333         ot->invoke= region_scale_invoke;
1334         ot->modal= region_scale_modal;
1335         
1336         ot->poll= ED_operator_areaactive;
1337 }
1338
1339
1340 /* ************** frame change operator ***************************** */
1341
1342
1343 /* function to be called outside UI context, or for redo */
1344 static int frame_offset_exec(bContext *C, wmOperator *op)
1345 {
1346         int delta;
1347
1348         delta = RNA_int_get(op->ptr, "delta");
1349
1350         CTX_data_scene(C)->r.cfra += delta;
1351
1352         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1353
1354         return OPERATOR_FINISHED;
1355 }
1356
1357 void SCREEN_OT_frame_offset(wmOperatorType *ot)
1358 {
1359         ot->name = "Frame Offset";
1360         ot->idname = "SCREEN_OT_frame_offset";
1361
1362         ot->exec= frame_offset_exec;
1363
1364         ot->poll= ED_operator_screenactive;
1365         ot->flag= 0;
1366
1367         /* rna */
1368         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1369 }
1370
1371 /* ************** switch screen operator ***************************** */
1372
1373
1374 /* function to be called outside UI context, or for redo */
1375 static int screen_set_exec(bContext *C, wmOperator *op)
1376 {
1377         bScreen *screen= CTX_wm_screen(C);
1378         ScrArea *sa= CTX_wm_area(C);
1379         int tot= BLI_countlist(&CTX_data_main(C)->screen);
1380         int delta= RNA_int_get(op->ptr, "delta");
1381         
1382         /* this screen is 'fake', solve later XXX */
1383         if(sa && sa->full)
1384                 return OPERATOR_CANCELLED;
1385         
1386         if(delta==1) {
1387                 while(tot--) {
1388                         screen= screen->id.next;
1389                         if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1390                         if(screen->winid==0 && screen->full==0)
1391                                 break;
1392                 }
1393         }
1394         else if(delta== -1) {
1395                 while(tot--) {
1396                         screen= screen->id.prev;
1397                         if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1398                         if(screen->winid==0 && screen->full==0)
1399                                 break;
1400                 }
1401         }
1402         else {
1403                 screen= NULL;
1404         }
1405         
1406         if(screen) {
1407                 ED_screen_set(C, screen);
1408                 return OPERATOR_FINISHED;
1409         }
1410         return OPERATOR_CANCELLED;
1411 }
1412
1413 void SCREEN_OT_screen_set(wmOperatorType *ot)
1414 {
1415         ot->name = "Set Screen";
1416         ot->idname = "SCREEN_OT_screen_set";
1417         
1418         ot->exec= screen_set_exec;
1419         ot->poll= ED_operator_screenactive;
1420         
1421         /* rna */
1422         RNA_def_pointer_runtime(ot->srna, "screen", &RNA_Screen, "Screen", "");
1423         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1424 }
1425
1426 /* ************** screen full-area operator ***************************** */
1427
1428
1429 /* function to be called outside UI context, or for redo */
1430 static int screen_full_area_exec(bContext *C, wmOperator *op)
1431 {
1432         ed_screen_fullarea(C, CTX_wm_area(C));
1433         return OPERATOR_FINISHED;
1434 }
1435
1436 void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1437 {
1438         ot->name = "Toggle Make Area Fullscreen";
1439         ot->idname = "SCREEN_OT_screen_full_area";
1440         
1441         ot->exec= screen_full_area_exec;
1442         ot->poll= ED_operator_areaactive;
1443         ot->flag= 0;
1444
1445 }
1446
1447
1448
1449 /* ************** join area operator ********************************************** */
1450
1451 /* operator state vars used:  
1452                         x1, y1     mouse coord in first area, which will disappear
1453                         x2, y2     mouse coord in 2nd area, which will become joined
1454
1455 functions:
1456
1457    init()   find edge based on state vars 
1458                         test if the edge divides two areas, 
1459                         store active and nonactive area,
1460             
1461    apply()  do the actual join
1462
1463    exit()       cleanup, send notifier
1464
1465 callbacks:
1466
1467    exec()       calls init, apply, exit 
1468    
1469    invoke() sets mouse coords in x,y
1470             call init()
1471             add modal handler
1472
1473    modal()      accept modal events while doing it
1474                         call apply() with active window and nonactive window
1475             call exit() and remove handler when LMB confirm
1476
1477 */
1478
1479 typedef struct sAreaJoinData
1480 {
1481         ScrArea *sa1;   /* first area to be considered */
1482         ScrArea *sa2;   /* second area to be considered */
1483         ScrArea *scr;   /* designed for removal */
1484
1485 } sAreaJoinData;
1486
1487
1488 /* validate selection inside screen, set variables OK */
1489 /* return 0: init failed */
1490 /* XXX todo: find edge based on (x,y) and set other area? */
1491 static int area_join_init(bContext *C, wmOperator *op)
1492 {
1493         ScrArea *sa1, *sa2;
1494         sAreaJoinData* jd= NULL;
1495         int x1, y1;
1496         int x2, y2;
1497
1498         /* required properties, make negative to get return 0 if not set by caller */
1499         x1= RNA_int_get(op->ptr, "x1");
1500         y1= RNA_int_get(op->ptr, "y1");
1501         x2= RNA_int_get(op->ptr, "x2");
1502         y2= RNA_int_get(op->ptr, "y2");
1503         
1504         sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1505         sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1506         if(sa1==NULL || sa2==NULL || sa1==sa2)
1507                 return 0;
1508
1509         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1510                 
1511         jd->sa1 = sa1;
1512         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1513         jd->sa2 = sa2;
1514         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1515         
1516         op->customdata= jd;
1517         
1518         return 1;
1519 }
1520
1521 /* apply the join of the areas (space types) */
1522 static int area_join_apply(bContext *C, wmOperator *op)
1523 {
1524         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1525         if (!jd) return 0;
1526
1527         if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1528                 return 0;
1529         }
1530         if (CTX_wm_area(C) == jd->sa2) {
1531                 CTX_wm_area_set(C, NULL);
1532                 CTX_wm_region_set(C, NULL);
1533         }
1534
1535         return 1;
1536 }
1537
1538 /* finish operation */
1539 static void area_join_exit(bContext *C, wmOperator *op)
1540 {
1541         if (op->customdata) {
1542                 MEM_freeN(op->customdata);
1543                 op->customdata = NULL;
1544         }
1545
1546         /* this makes sure aligned edges will result in aligned grabbing */
1547         removedouble_scredges(CTX_wm_screen(C));
1548         removenotused_scredges(CTX_wm_screen(C));
1549         removenotused_scrverts(CTX_wm_screen(C));
1550 }
1551
1552 static int area_join_exec(bContext *C, wmOperator *op)
1553 {
1554         if(!area_join_init(C, op)) 
1555                 return OPERATOR_CANCELLED;
1556         
1557         area_join_apply(C, op);
1558         area_join_exit(C, op);
1559
1560         return OPERATOR_FINISHED;
1561 }
1562
1563 /* interaction callback */
1564 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1565 {
1566
1567         if(event->type==EVT_ACTIONZONE_AREA) {
1568                 sActionzoneData *sad= event->customdata;
1569
1570                 if(sad->modifier>0) {
1571                         return OPERATOR_PASS_THROUGH;
1572                 }
1573                 
1574                 /* verify *sad itself */
1575                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1576                         return OPERATOR_PASS_THROUGH;
1577                 
1578                 /* is this our *sad? if areas equal it should be passed on */
1579                 if(sad->sa1==sad->sa2)
1580                         return OPERATOR_PASS_THROUGH;
1581                 
1582                 /* prepare operator state vars */
1583                 RNA_int_set(op->ptr, "x1", sad->x);
1584                 RNA_int_set(op->ptr, "y1", sad->y);
1585                 RNA_int_set(op->ptr, "x2", event->x);
1586                 RNA_int_set(op->ptr, "y2", event->y);
1587
1588                 if(!area_join_init(C, op)) 
1589                         return OPERATOR_PASS_THROUGH;
1590         
1591                 /* add temp handler */
1592                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1593         
1594                 return OPERATOR_RUNNING_MODAL;
1595         }
1596         
1597         return OPERATOR_PASS_THROUGH;
1598 }
1599
1600 static int area_join_cancel(bContext *C, wmOperator *op)
1601 {
1602         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1603
1604         if (jd->sa1) {
1605                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1606                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1607         }
1608         if (jd->sa2) {
1609                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1610                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1611         }
1612
1613         WM_event_add_notifier(C, NC_WINDOW, NULL);
1614         
1615         area_join_exit(C, op);
1616
1617         return OPERATOR_CANCELLED;
1618 }
1619
1620 /* modal callback while selecting area (space) that will be removed */
1621 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1622 {
1623         bScreen *sc= CTX_wm_screen(C);
1624         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1625         
1626         /* execute the events */
1627         switch(event->type) {
1628                         
1629                 case MOUSEMOVE: 
1630                         {
1631                                 ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1632                                 int dir;
1633                                 
1634                                 if (sa) {                                       
1635                                         if (jd->sa1 != sa) {
1636                                                 dir = area_getorientation(sc, jd->sa1, sa);
1637                                                 if (dir >= 0) {
1638                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1639                                                         jd->sa2 = sa;
1640                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1641                                                 } 
1642                                                 else {
1643                                                         /* we are not bordering on the previously selected area 
1644                                                            we check if area has common border with the one marked for removal
1645                                                            in this case we can swap areas.
1646                                                         */
1647                                                         dir = area_getorientation(sc, sa, jd->sa2);
1648                                                         if (dir >= 0) {
1649                                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1650                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1651                                                                 jd->sa1 = jd->sa2;
1652                                                                 jd->sa2 = sa;
1653                                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1654                                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1655                                                         } 
1656                                                         else {
1657                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1658                                                                 jd->sa2 = NULL;
1659                                                         }
1660                                                 }
1661                                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
1662                                         } 
1663                                         else {
1664                                                 /* we are back in the area previously selected for keeping 
1665                                                  * we swap the areas if possible to allow user to choose */
1666                                                 if (jd->sa2 != NULL) {
1667                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1668                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1669                                                         jd->sa1 = jd->sa2;
1670                                                         jd->sa2 = sa;
1671                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1672                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1673                                                         dir = area_getorientation(sc, jd->sa1, jd->sa2);
1674                                                         if (dir < 0) {
1675                                                                 printf("oops, didn't expect that!\n");
1676                                                         }
1677                                                 } 
1678                                                 else {
1679                                                         dir = area_getorientation(sc, jd->sa1, sa);
1680                                                         if (dir >= 0) {
1681                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1682                                                                 jd->sa2 = sa;
1683                                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1684                                                         }
1685                                                 }
1686                                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
1687                                         }
1688                                 }
1689                         }
1690                         break;
1691                 case LEFTMOUSE:
1692                         if(event->val==0) {
1693                                 area_join_apply(C, op);
1694                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1695                                 area_join_exit(C, op);
1696                                 return OPERATOR_FINISHED;
1697                         }
1698                         break;
1699                         
1700                 case ESCKEY:
1701                         return area_join_cancel(C, op);
1702         }
1703
1704         return OPERATOR_RUNNING_MODAL;
1705 }
1706
1707 /* Operator for joining two areas (space types) */
1708 void SCREEN_OT_area_join(wmOperatorType *ot)
1709 {
1710         /* identifiers */
1711         ot->name= "Join area";
1712         ot->idname= "SCREEN_OT_area_join";
1713         
1714         /* api callbacks */
1715         ot->exec= area_join_exec;
1716         ot->invoke= area_join_invoke;
1717         ot->modal= area_join_modal;
1718
1719         ot->poll= ED_operator_areaactive;
1720
1721         /* rna */
1722         RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1723         RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
1724         RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
1725         RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
1726 }
1727
1728 /* ************** repeat last operator ***************************** */
1729
1730 static int repeat_last_exec(bContext *C, wmOperator *op)
1731 {
1732         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
1733         
1734         if(lastop)
1735                 WM_operator_repeat(C, lastop);
1736         
1737         return OPERATOR_CANCELLED;
1738 }
1739
1740 void SCREEN_OT_repeat_last(wmOperatorType *ot)
1741 {
1742         /* identifiers */
1743         ot->name= "Repeat Last";
1744         ot->idname= "SCREEN_OT_repeat_last";
1745         
1746         /* api callbacks */
1747         ot->exec= repeat_last_exec;
1748         
1749         ot->poll= ED_operator_screenactive;
1750         
1751 }
1752
1753 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
1754 {
1755         wmWindowManager *wm= CTX_wm_manager(C);
1756         wmOperator *lastop;
1757         uiPopupMenu *pup;
1758         uiLayout *layout;
1759         int items, i;
1760         
1761         items= BLI_countlist(&wm->operators);
1762         if(items==0)
1763                 return OPERATOR_CANCELLED;
1764         
1765         pup= uiPupMenuBegin(C, op->type->name, 0);
1766         layout= uiPupMenuLayout(pup);
1767
1768         for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
1769                 uiItemIntO(layout, lastop->type->name, 0, op->type->idname, "index", i);
1770
1771         uiPupMenuEnd(C, pup);
1772         
1773         return OPERATOR_CANCELLED;
1774 }
1775
1776 static int repeat_history_exec(bContext *C, wmOperator *op)
1777 {
1778         wmWindowManager *wm= CTX_wm_manager(C);
1779         
1780         op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
1781         if(op) {
1782                 /* let's put it as last operator in list */
1783                 BLI_remlink(&wm->operators, op);
1784                 BLI_addtail(&wm->operators, op);
1785                 
1786                 WM_operator_repeat(C, op);
1787         }
1788                                          
1789         return OPERATOR_FINISHED;
1790 }
1791
1792 void SCREEN_OT_repeat_history(wmOperatorType *ot)
1793 {
1794         /* identifiers */
1795         ot->name= "Repeat History";
1796         ot->idname= "SCREEN_OT_repeat_history";
1797         
1798         /* api callbacks */
1799         ot->invoke= repeat_history_invoke;
1800         ot->exec= repeat_history_exec;
1801         
1802         ot->poll= ED_operator_screenactive;
1803         
1804         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
1805 }
1806
1807 /* ********************** redo operator ***************************** */
1808
1809 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
1810 {
1811         wmWindowManager *wm= CTX_wm_manager(C);
1812         wmOperator *lastop;
1813
1814         /* only for operators that are registered and did an undo push */
1815         for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
1816                 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
1817                         break;
1818         
1819         if(lastop)
1820                 WM_operator_redo_popup(C, lastop);
1821
1822         return OPERATOR_CANCELLED;
1823 }
1824
1825 void SCREEN_OT_redo_last(wmOperatorType *ot)
1826 {
1827         /* identifiers */
1828         ot->name= "Redo Last";
1829         ot->idname= "SCREEN_OT_redo_last";
1830         
1831         /* api callbacks */
1832         ot->invoke= redo_last_invoke;
1833         
1834         ot->poll= ED_operator_screenactive;
1835 }
1836
1837 /* ************** region split operator ***************************** */
1838
1839 /* insert a region in the area region list */
1840 static int region_split_exec(bContext *C, wmOperator *op)
1841 {
1842         ARegion *ar= CTX_wm_region(C);
1843         
1844         if(ar->regiontype==RGN_TYPE_HEADER)
1845                 BKE_report(op->reports, RPT_ERROR, "Cannot split header");
1846         else if(ar->alignment==RGN_ALIGN_QSPLIT)
1847                 BKE_report(op->reports, RPT_ERROR, "Cannot split further");
1848         else {
1849                 ScrArea *sa= CTX_wm_area(C);
1850                 ARegion *newar= BKE_area_region_copy(sa->type, ar);
1851                 int dir= RNA_enum_get(op->ptr, "type");
1852         
1853                 BLI_insertlinkafter(&sa->regionbase, ar, newar);
1854                 
1855                 newar->alignment= ar->alignment;
1856                 
1857                 if(dir=='h')
1858                         ar->alignment= RGN_ALIGN_HSPLIT;
1859                 else
1860                         ar->alignment= RGN_ALIGN_VSPLIT;
1861                 
1862                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1863         }
1864         
1865         return OPERATOR_FINISHED;
1866 }
1867
1868 void SCREEN_OT_region_split(wmOperatorType *ot)
1869 {
1870         /* identifiers */
1871         ot->name= "Split Region";
1872         ot->idname= "SCREEN_OT_region_split";
1873         
1874         /* api callbacks */
1875         ot->invoke= WM_menu_invoke;
1876         ot->exec= region_split_exec;
1877         ot->poll= ED_operator_areaactive;
1878         
1879         RNA_def_enum(ot->srna, "type", prop_direction_items, 'h', "Direction", "");
1880 }
1881
1882 /* ************** region four-split operator ***************************** */
1883
1884 /* insert a region in the area region list */
1885 static int region_foursplit_exec(bContext *C, wmOperator *op)
1886 {
1887         ARegion *ar= CTX_wm_region(C);
1888         
1889         /* some rules... */
1890         if(ar->regiontype!=RGN_TYPE_WINDOW)
1891                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
1892         else if(ar->alignment==RGN_ALIGN_QSPLIT) {
1893                 ScrArea *sa= CTX_wm_area(C);
1894                 ARegion *arn;
1895                 
1896                 /* keep current region */
1897                 ar->alignment= 0;
1898                 
1899                 if(sa->spacetype==SPACE_VIEW3D) {
1900                         RegionView3D *rv3d= ar->regiondata;
1901                         rv3d->viewlock= 0;
1902                         rv3d->rflag &= ~RV3D_CLIPPING;
1903                 }
1904                 
1905                 for(ar= sa->regionbase.first; ar; ar= arn) {
1906                         arn= ar->next;
1907                         if(ar->alignment==RGN_ALIGN_QSPLIT) {
1908                                 ED_region_exit(C, ar);
1909                                 BKE_area_region_free(sa->type, ar);
1910                                 BLI_remlink(&sa->regionbase, ar);
1911                                 MEM_freeN(ar);
1912                         }
1913                 }
1914                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1915         }
1916         else if(ar->next)
1917                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
1918         else {
1919                 ScrArea *sa= CTX_wm_area(C);
1920                 ARegion *newar;
1921                 int count;
1922                 
1923                 ar->alignment= RGN_ALIGN_QSPLIT;
1924                 
1925                 for(count=0; count<3; count++) {
1926                         newar= BKE_area_region_copy(sa->type, ar);
1927                         BLI_addtail(&sa->regionbase, newar);
1928                 }
1929                 
1930                 /* lock views and set them */
1931                 if(sa->spacetype==SPACE_VIEW3D) {
1932                         RegionView3D *rv3d;
1933                         
1934                         rv3d= ar->regiondata;
1935                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_FRONT; rv3d->persp= V3D_ORTHO;
1936                         
1937                         ar= ar->next;
1938                         rv3d= ar->regiondata;
1939                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_TOP; rv3d->persp= V3D_ORTHO;
1940                         
1941                         ar= ar->next;
1942                         rv3d= ar->regiondata;
1943                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_RIGHT; rv3d->persp= V3D_ORTHO;
1944                         
1945                         ar= ar->next;
1946                         rv3d= ar->regiondata;
1947                         rv3d->view= V3D_VIEW_CAMERA; rv3d->persp= V3D_CAMOB;
1948                 }
1949                 
1950                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1951         }
1952         
1953         
1954         return OPERATOR_FINISHED;
1955 }
1956
1957 void SCREEN_OT_region_foursplit(wmOperatorType *ot)
1958 {
1959         /* identifiers */
1960         ot->name= "Split Region in 4 Parts";
1961         ot->idname= "SCREEN_OT_region_foursplit";
1962         
1963         /* api callbacks */
1964         ot->invoke= WM_operator_confirm;
1965         ot->exec= region_foursplit_exec;
1966         ot->poll= ED_operator_areaactive;
1967         ot->flag= OPTYPE_REGISTER;
1968 }
1969
1970
1971
1972 /* ************** region flip operator ***************************** */
1973
1974 /* flip a region alignment */
1975 static int region_flip_exec(bContext *C, wmOperator *op)
1976 {
1977         ARegion *ar= CTX_wm_region(C);
1978
1979         if(ar->alignment==RGN_ALIGN_TOP)
1980                 ar->alignment= RGN_ALIGN_BOTTOM;
1981         else if(ar->alignment==RGN_ALIGN_BOTTOM)
1982                 ar->alignment= RGN_ALIGN_TOP;
1983         else if(ar->alignment==RGN_ALIGN_LEFT)
1984                 ar->alignment= RGN_ALIGN_RIGHT;
1985         else if(ar->alignment==RGN_ALIGN_RIGHT)
1986                 ar->alignment= RGN_ALIGN_LEFT;
1987         
1988         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1989         printf("executed region flip\n");
1990         
1991         return OPERATOR_FINISHED;
1992 }
1993
1994
1995 void SCREEN_OT_region_flip(wmOperatorType *ot)
1996 {
1997         /* identifiers */
1998         ot->name= "Flip Region";
1999         ot->idname= "SCREEN_OT_region_flip";
2000         
2001         /* api callbacks */
2002         ot->exec= region_flip_exec;
2003         
2004         ot->poll= ED_operator_areaactive;
2005         ot->flag= OPTYPE_REGISTER;
2006
2007 }
2008
2009 /* ****************** anim player, with timer ***************** */
2010
2011 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2012 {
2013         if(regiontype==RGN_TYPE_WINDOW) {
2014
2015                 switch (spacetype) {
2016                         case SPACE_VIEW3D:
2017                                 if(redraws & TIME_ALL_3D_WIN)
2018                                         return 1;
2019                                 break;
2020                         case SPACE_IPO:
2021                         case SPACE_ACTION:
2022                         case SPACE_NLA:
2023                                 if(redraws & TIME_ALL_ANIM_WIN)
2024                                         return 1;
2025                                 break;
2026                         case SPACE_TIME:
2027                                 /* if only 1 window or 3d windows, we do timeline too */
2028                                 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2029                                         return 1;
2030                                 break;
2031                         case SPACE_BUTS:
2032                                 if(redraws & TIME_ALL_BUTS_WIN)
2033                                         return 1;
2034                                 break;
2035                         case SPACE_SEQ:
2036                                 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2037                                         return 1;
2038                                 break;
2039                         case SPACE_IMAGE:
2040                                 if(redraws & TIME_ALL_IMAGE_WIN)
2041                                         return 1;
2042                                 break;
2043                                 
2044                 }
2045         }
2046         else if(regiontype==RGN_TYPE_UI) {
2047                 if(redraws & TIME_ALL_BUTS_WIN)
2048                         return 1;
2049         }
2050         else if(regiontype==RGN_TYPE_HEADER) {
2051                 if(spacetype==SPACE_TIME)
2052                         return 1;
2053         }
2054         return 0;
2055 }
2056
2057 static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event)
2058 {
2059         bScreen *screen= CTX_wm_screen(C);
2060         
2061         if(screen->animtimer==event->customdata) {
2062                 Scene *scene= CTX_data_scene(C);
2063                 wmTimer *wt= screen->animtimer;
2064                 ScreenAnimData *sad= wt->customdata;
2065                 ScrArea *sa;
2066                 
2067                 if(scene->audio.flag & AUDIO_SYNC) {
2068                         int step = floor(wt->duration * FPS);
2069                         scene->r.cfra += step;
2070                         wt->duration -= ((float)step)/FPS;
2071                 }
2072                 else
2073                         scene->r.cfra++;
2074                 
2075                 if (scene->r.psfra) {
2076                         if(scene->r.cfra > scene->r.pefra)
2077                                 scene->r.cfra= scene->r.psfra;
2078                 }
2079                 else {
2080                         if(scene->r.cfra > scene->r.efra)
2081                                 scene->r.cfra= scene->r.sfra;
2082                 }
2083
2084                 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2085                 ED_update_for_newframe(C, 1);
2086                 
2087                 for(sa= screen->areabase.first; sa; sa= sa->next) {
2088                         ARegion *ar;
2089                         for(ar= sa->regionbase.first; ar; ar= ar->next) {
2090                                 if(ar==sad->ar)
2091                                         ED_region_tag_redraw(ar);
2092                                 else
2093                                         if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2094                                                 ED_region_tag_redraw(ar);
2095                         }
2096                 }
2097                 
2098                 //WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2099                 
2100                 return OPERATOR_FINISHED;
2101         }
2102         return OPERATOR_PASS_THROUGH;
2103 }
2104
2105 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2106 {
2107         /* identifiers */
2108         ot->name= "Animation Step";
2109         ot->idname= "SCREEN_OT_animation_step";
2110         
2111         /* api callbacks */
2112         ot->invoke= screen_animation_step;
2113         
2114         ot->poll= ED_operator_screenactive;
2115         
2116 }
2117
2118 /* ****************** anim player, starts or ends timer ***************** */
2119
2120 /* toggle operator */
2121 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
2122 {
2123         bScreen *screen= CTX_wm_screen(C);
2124         
2125         if(screen->animtimer) {
2126                 ED_screen_animation_timer(C, 0, 0);
2127         }
2128         else {
2129                 /* todo: RNA properties to define play types */
2130                 ED_screen_animation_timer(C, TIME_REGION|TIME_ALL_3D_WIN, 1);
2131                 
2132                 if(screen->animtimer) {
2133                         wmTimer *wt= screen->animtimer;
2134                         ScreenAnimData *sad= wt->customdata;
2135                         
2136                         sad->ar= CTX_wm_region(C);
2137                 }
2138         }
2139         
2140         return OPERATOR_FINISHED;
2141 }
2142
2143 void SCREEN_OT_animation_play(wmOperatorType *ot)
2144 {
2145         /* identifiers */
2146         ot->name= "Animation player";
2147         ot->idname= "SCREEN_OT_animation_play";
2148         
2149         /* api callbacks */
2150         ot->invoke= screen_animation_play;
2151         
2152         ot->poll= ED_operator_screenactive;
2153         
2154         
2155 }
2156
2157 /* ************** border select operator (template) ***************************** */
2158
2159 /* operator state vars used: (added by default WM callbacks)   
2160         xmin, ymin     
2161         xmax, ymax     
2162
2163         customdata: the wmGesture pointer
2164
2165 callbacks:
2166
2167         exec()  has to be filled in by user
2168
2169         invoke() default WM function
2170                          adds modal handler
2171
2172         modal() default WM function 
2173                         accept modal events while doing it, calls exec(), handles ESC and border drawing
2174         
2175         poll()  has to be filled in by user for context
2176 */
2177 #if 0
2178 static int border_select_do(bContext *C, wmOperator *op)
2179 {
2180         int event_type= RNA_int_get(op->ptr, "event_type");
2181         
2182         if(event_type==LEFTMOUSE)
2183                 printf("border select do select\n");
2184         else if(event_type==RIGHTMOUSE)
2185                 printf("border select deselect\n");
2186         else 
2187                 printf("border select do something\n");
2188         
2189         return 1;
2190 }
2191
2192 void SCREEN_OT_border_select(wmOperatorType *ot)
2193 {
2194         /* identifiers */
2195         ot->name= "Border select";
2196         ot->idname= "SCREEN_OT_border_select";
2197         
2198         /* api callbacks */
2199         ot->exec= border_select_do;
2200         ot->invoke= WM_border_select_invoke;
2201         ot->modal= WM_border_select_modal;
2202         
2203         ot->poll= ED_operator_areaactive;
2204         
2205         /* rna */
2206         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2207         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2208         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2209         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2210         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2211
2212 }
2213 #endif
2214
2215 /* ****************************** render invoking ***************** */
2216
2217 /* set callbacks, exported to sequence render too. 
2218 Only call in foreground (UI) renders. */
2219
2220 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2221 /* window as the last possible alternative.                                                                        */
2222 static ScrArea *biggest_non_image_area(bContext *C)
2223 {
2224         bScreen *sc= CTX_wm_screen(C);
2225         ScrArea *sa, *big= NULL;
2226         int size, maxsize= 0, bwmaxsize= 0;
2227         short foundwin= 0;
2228         
2229         for(sa= sc->areabase.first; sa; sa= sa->next) {
2230                 if(sa->winx > 30 && sa->winy > 30) {
2231                         size= sa->winx*sa->winy;
2232                         if(sa->spacetype == SPACE_BUTS) {
2233                                 if(foundwin == 0 && size > bwmaxsize) {
2234                                         bwmaxsize= size;
2235                                         big= sa;        
2236                                 }
2237                         }
2238                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2239                                 maxsize= size;
2240                                 big= sa;
2241                                 foundwin= 1;
2242                         }
2243                 }
2244         }
2245         
2246         return big;
2247 }
2248
2249 static ScrArea *biggest_area(bContext *C)
2250 {
2251         bScreen *sc= CTX_wm_screen(C);
2252         ScrArea *sa, *big= NULL;
2253         int size, maxsize= 0;
2254         
2255         for(sa= sc->areabase.first; sa; sa= sa->next) {
2256                 size= sa->winx*sa->winy;
2257                 if(size > maxsize) {
2258                         maxsize= size;
2259                         big= sa;
2260                 }
2261         }
2262         return big;
2263 }
2264
2265
2266 static ScrArea *find_area_showing_r_result(bContext *C)
2267 {
2268         bScreen *sc= CTX_wm_screen(C);
2269         ScrArea *sa;
2270         SpaceImage *sima;
2271         
2272         /* find an imagewindow showing render result */
2273         for(sa=sc->areabase.first; sa; sa= sa->next) {
2274                 if(sa->spacetype==SPACE_IMAGE) {
2275                         sima= sa->spacedata.first;
2276                         if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2277                                 break;
2278                 }
2279         }
2280         return sa;
2281 }
2282
2283 static void screen_set_image_output(bContext *C)
2284 {
2285         ScrArea *sa;
2286         SpaceImage *sima;
2287         
2288         sa= find_area_showing_r_result(C);
2289         
2290         if(sa==NULL) {
2291                 /* find largest open non-image area */
2292                 sa= biggest_non_image_area(C);
2293                 if(sa) {
2294                         ED_area_newspace(C, sa, SPACE_IMAGE);
2295                         sima= sa->spacedata.first;
2296                         
2297                         /* makes ESC go back to prev space */
2298                         sima->flag |= SI_PREVSPACE;
2299                 }
2300                 else {
2301                         /* use any area of decent size */
2302                         sa= biggest_area(C);
2303                         if(sa->spacetype!=SPACE_IMAGE) {
2304                                 // XXX newspace(sa, SPACE_IMAGE);
2305                                 sima= sa->spacedata.first;
2306                                 
2307                                 /* makes ESC go back to prev space */
2308                                 sima->flag |= SI_PREVSPACE;
2309                         }
2310                 }
2311         }
2312         
2313         sima= sa->spacedata.first;
2314         
2315         /* get the correct image, and scale it */
2316         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2317         
2318         if(G.displaymode==2) { // XXX
2319                 if(sa->full==0) {
2320                         sima->flag |= SI_FULLWINDOW;
2321                         
2322                         ed_screen_fullarea(C, sa);
2323                 }
2324         }
2325         
2326 }
2327
2328 /* executes blocking render */
2329 static int screen_render_exec(bContext *C, wmOperator *op)
2330 {
2331         Scene *scene= CTX_data_scene(C);
2332         Render *re= RE_GetRender(scene->id.name);
2333         
2334         if(re==NULL) {
2335                 re= RE_NewRender(scene->id.name);
2336         }
2337         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2338         
2339         if(RNA_boolean_get(op->ptr, "anim"))
2340                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->frame_step);
2341         else
2342                 RE_BlenderFrame(re, scene, scene->r.cfra);
2343         
2344         // no redraw needed, we leave state as we entered it
2345         ED_update_for_newframe(C, 1);
2346         
2347         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2348
2349         return OPERATOR_FINISHED;
2350 }
2351
2352 typedef struct RenderJob {
2353         Scene *scene;
2354         Render *re;
2355         wmWindow *win;
2356         int anim;
2357         Image *image;
2358         ImageUser iuser;
2359         short *stop;
2360         short *do_update;
2361 } RenderJob;
2362
2363 static void render_freejob(void *rjv)
2364 {
2365         RenderJob *rj= rjv;
2366         
2367         MEM_freeN(rj);
2368 }
2369
2370 /* called inside thread! */
2371 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
2372 {
2373         RenderJob *rj= rjv;
2374         ImBuf *ibuf;
2375         float x1, y1, *rectf= NULL;
2376         int ymin, ymax, xmin, xmax;
2377         int rymin, rxmin;
2378         char *rectc;
2379         
2380         ibuf= BKE_image_get_ibuf(rj->image, &rj->iuser);
2381         if(ibuf==NULL) return;
2382
2383         /* if renrect argument, we only refresh scanlines */
2384         if(renrect) {
2385                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
2386                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
2387                         return;
2388                 
2389                 /* xmin here is first subrect x coord, xmax defines subrect width */
2390                 xmin = renrect->xmin + rr->crop;
2391                 xmax = renrect->xmax - xmin - rr->crop;
2392                 if (xmax<2) return;
2393                 
2394                 ymin= renrect->ymin + rr->crop;
2395                 ymax= renrect->ymax - ymin - rr->crop;
2396                 if(ymax<2)
2397                         return;
2398                 renrect->ymin= renrect->ymax;
2399                 
2400         }
2401         else {
2402                 xmin = ymin = rr->crop;
2403                 xmax = rr->rectx - 2*rr->crop;
2404                 ymax = rr->recty - 2*rr->crop;
2405         }
2406         
2407         /* xmin ymin is in tile coords. transform to ibuf */
2408         rxmin= rr->tilerect.xmin + xmin;
2409         if(rxmin >= ibuf->x) return;
2410         rymin= rr->tilerect.ymin + ymin;
2411         if(rymin >= ibuf->y) return;
2412         
2413         if(rxmin + xmax > ibuf->x)
2414                 xmax= ibuf->x - rxmin;
2415         if(rymin + ymax > ibuf->y)
2416                 ymax= ibuf->y - rymin;
2417         
2418         if(xmax < 1 || ymax < 1) return;
2419         
2420         /* find current float rect for display, first case is after composit... still weak */
2421         if(rr->rectf)
2422                 rectf= rr->rectf;
2423         else {
2424                 if(rr->rect32)
2425                         return;
2426                 else {
2427                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
2428                         rectf= rr->renlay->rectf;
2429                 }
2430         }
2431         if(rectf==NULL) return;
2432         
2433         rectf+= 4*(rr->rectx*ymin + xmin);
2434         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
2435
2436         for(y1= 0; y1<ymax; y1++) {
2437                 float *rf= rectf;
2438                 char *rc= rectc;
2439                 
2440                 /* XXX temp. because crop offset */
2441                 if( rectc >= (char *)(ibuf->rect)) {
2442                         for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2443                                 rc[0]= FTOCHAR(rf[0]);
2444                                 rc[1]= FTOCHAR(rf[1]);
2445                                 rc[2]= FTOCHAR(rf[2]);
2446                                 rc[3]= FTOCHAR(rf[3]);
2447                         }
2448                 }
2449                 rectf += 4*rr->rectx;
2450                 rectc += 4*ibuf->x;
2451         }
2452         
2453         /* make jobs timer to send notifier */
2454         *(rj->do_update)= 1;
2455 }
2456
2457 static void render_startjob(void *rjv, short *stop, short *do_update)
2458 {
2459         RenderJob *rj= rjv;
2460         
2461         rj->stop= stop;
2462         rj->do_update= do_update;
2463         
2464         if(rj->anim)
2465                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->frame_step);
2466         else
2467                 RE_BlenderFrame(rj->re, rj->scene, rj->scene->r.cfra);
2468 }
2469
2470 /* called by render, check job 'stop' value or the global */
2471 static int render_breakjob(void *rjv)
2472 {
2473         RenderJob *rj= rjv;
2474         
2475         if(G.afbreek)
2476                 return 1;
2477         if(rj->stop && *(rj->stop))
2478                 return 1;
2479         return 0;
2480 }
2481
2482 /* catch esc */
2483 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
2484 {
2485         /* no running blender, remove handler and pass through */
2486         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
2487            return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
2488         
2489         /* running render */
2490         switch (event->type) {
2491                 case ESCKEY:
2492                         return OPERATOR_RUNNING_MODAL;
2493                         break;
2494         }
2495         return OPERATOR_PASS_THROUGH;
2496 }
2497
2498 /* using context, starts job */
2499 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
2500 {
2501         /* new render clears all callbacks */
2502         Scene *scene= CTX_data_scene(C);
2503         Render *re;
2504         wmJob *steve;
2505         RenderJob *rj;
2506         Image *ima;
2507         
2508         /* only one job at a time */
2509         if(WM_jobs_test(CTX_wm_manager(C), scene))
2510                 return OPERATOR_CANCELLED;
2511         
2512         /* handle UI stuff */
2513         WM_cursor_wait(1);
2514
2515         /* flush multires changes (for sculpt) */
2516         multires_force_update(CTX_data_active_object(C));
2517         
2518         // get editmode results
2519         // store spare
2520         // get view3d layer, local layer, make this nice api call to render
2521         // store spare
2522         
2523         /* ensure at least 1 area shows result */
2524         screen_set_image_output(C);
2525
2526         /* job custom data */
2527         rj= MEM_callocN(sizeof(RenderJob), "render job");
2528         rj->scene= scene;
2529         rj->win= CTX_wm_window(C);
2530         rj->anim= RNA_boolean_get(op->ptr, "anim");
2531         rj->iuser.scene= scene;
2532         rj->iuser.ok= 1;
2533         
2534         /* setup job */
2535         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene);
2536         WM_jobs_customdata(steve, rj, render_freejob);
2537         WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
2538         WM_jobs_callbacks(steve, render_startjob, NULL, NULL);
2539         
2540         /* get a render result image, and make sure it is empty */
2541         ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2542         BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
2543         rj->image= ima;
2544         
2545         /* setup new render */
2546         re= RE_NewRender(scene->id.name);
2547         RE_test_break_cb(re, rj, render_breakjob);
2548         RE_display_draw_cb(re, rj, image_rect_update);
2549         rj->re= re;
2550         G.afbreek= 0;
2551         
2552         //      BKE_report in render!
2553         //      RE_error_cb(re, error_cb);
2554
2555         WM_jobs_start(CTX_wm_manager(C), steve);
2556         
2557         G.afbreek= 0;
2558         
2559         WM_cursor_wait(0);
2560         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2561
2562         /* add modal handler for ESC */
2563         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
2564         
2565         return OPERATOR_RUNNING_MODAL;
2566 }
2567
2568
2569 /* contextual render, using current scene, view3d? */
2570 void SCREEN_OT_render(wmOperatorType *ot)
2571 {
2572         /* identifiers */
2573         ot->name= "Render";
2574         ot->idname= "SCREEN_OT_render";
2575         
2576         /* api callbacks */
2577         ot->invoke= screen_render_invoke;
2578         ot->modal= screen_render_modal;
2579         ot->exec= screen_render_exec;
2580         
2581         ot->poll= ED_operator_screenactive;
2582         
2583         RNA_def_int(ot->srna, "layers", 0, 0, INT_MAX, "Layers", "", 0, INT_MAX);
2584         RNA_def_boolean(ot->srna, "anim", 0, "Animation", "");
2585 }
2586
2587 /* *********************** cancel render viewer *************** */
2588
2589 static int render_view_cancel_exec(bContext *C, wmOperator *unused)
2590 {
2591         ScrArea *sa= CTX_wm_area(C);
2592         SpaceImage *sima= sa->spacedata.first;
2593         
2594         if(sima->flag & SI_PREVSPACE) {
2595                 sima->flag &= ~SI_PREVSPACE;
2596                 
2597                 if(sima->flag & SI_FULLWINDOW) {
2598                         sima->flag &= ~SI_FULLWINDOW;
2599                         ED_screen_full_prevspace(C);
2600                 }
2601                 else
2602                         ED_area_prevspace(C);
2603         }
2604         else if(sima->flag & SI_FULLWINDOW) {
2605                 sima->flag &= ~SI_FULLWINDOW;
2606                 ed_screen_fullarea(C, sa);
2607         }               
2608         
2609         return OPERATOR_FINISHED;
2610 }
2611
2612 void SCREEN_OT_render_view_cancel(struct wmOperatorType *ot)
2613 {
2614         /* identifiers */
2615         ot->name= "Cancel Render View";
2616         ot->idname= "SCREEN_OT_render_view_cancel";
2617         
2618         /* api callbacks */
2619         ot->exec= render_view_cancel_exec;
2620         ot->poll= ED_operator_image_active;
2621 }
2622
2623
2624 /* ****************  Assigning operatortypes to global list, adding handlers **************** */
2625
2626 /* called in spacetypes.c */
2627 void ED_operatortypes_screen(void)
2628 {
2629         /* generic UI stuff */
2630         WM_operatortype_append(SCREEN_OT_actionzone);
2631         WM_operatortype_append(SCREEN_OT_repeat_last);
2632         WM_operatortype_append(SCREEN_OT_repeat_history);
2633         WM_operatortype_append(SCREEN_OT_redo_last);
2634         
2635         /* screen tools */
2636         WM_operatortype_append(SCREEN_OT_area_move);
2637         WM_operatortype_append(SCREEN_OT_area_split);
2638         WM_operatortype_append(SCREEN_OT_area_join);
2639         WM_operatortype_append(SCREEN_OT_area_dupli);
2640         WM_operatortype_append(SCREEN_OT_area_swap);
2641         WM_operatortype_append(SCREEN_OT_region_split);
2642         WM_operatortype_append(SCREEN_OT_region_foursplit);
2643         WM_operatortype_append(SCREEN_OT_region_flip);
2644         WM_operatortype_append(SCREEN_OT_region_scale);
2645         WM_operatortype_append(SCREEN_OT_screen_set);
2646         WM_operatortype_append(SCREEN_OT_screen_full_area);
2647         WM_operatortype_append(SCREEN_OT_screenshot);
2648         WM_operatortype_append(SCREEN_OT_screencast);
2649         
2650         /*frame changes*/
2651         WM_operatortype_append(SCREEN_OT_frame_offset);
2652         WM_operatortype_append(SCREEN_OT_animation_step);
2653         WM_operatortype_append(SCREEN_OT_animation_play);
2654         
2655         /* render */
2656         WM_operatortype_append(SCREEN_OT_render);
2657         WM_operatortype_append(SCREEN_OT_render_view_cancel);
2658         
2659         /* tools shared by more space types */
2660         WM_operatortype_append(ED_OT_undo);
2661         WM_operatortype_append(ED_OT_redo);     
2662         
2663 }
2664
2665 /* called in spacetypes.c */
2666 void ED_keymap_screen(wmWindowManager *wm)
2667 {
2668         ListBase *keymap= WM_keymap_listbase(wm, "Screen", 0, 0);
2669         
2670         /* standard timers */
2671         WM_keymap_add_item(keymap, "SCREEN_OT_animation_step", TIMER0, KM_ANY, KM_ANY, 0);
2672         
2673         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_actionzone", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "modifier", 0);
2674         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_actionzone", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "modifier", 1);
2675         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_actionzone", LEFTMOUSE, KM_PRESS, KM_ALT, 0)->ptr, "modifier", 2);
2676         
2677         /* screen tools */
2678         WM_keymap_verify_item(keymap, "SCREEN_OT_area_split", EVT_ACTIONZONE_AREA, 0, 0, 0);
2679         WM_keymap_verify_item(keymap, "SCREEN_OT_area_join", EVT_ACTIONZONE_AREA, 0, 0, 0);
2680         WM_keymap_verify_item(keymap, "SCREEN_OT_area_dupli", EVT_ACTIONZONE_AREA, 0, KM_SHIFT, 0);
2681         WM_keymap_verify_item(keymap, "SCREEN_OT_area_swap", EVT_ACTIONZONE_AREA, 0, KM_ALT, 0);
2682         WM_keymap_verify_item(keymap, "SCREEN_OT_region_scale", EVT_ACTIONZONE_REGION, 0, 0, 0);
2683                         /* area move after action zones */
2684         WM_keymap_verify_item(keymap, "SCREEN_OT_area_move", LEFTMOUSE, KM_PRESS, 0, 0);
2685         
2686         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1);
2687         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1);
2688         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", UPARROWKEY, KM_PRESS, KM_CTRL, 0);
2689         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", DOWNARROWKEY, KM_PRESS, KM_CTRL, 0);
2690         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", SPACEKEY, KM_PRESS, KM_CTRL, 0);
2691         WM_keymap_add_item(keymap, "SCREEN_OT_screenshot", F3KEY, KM_PRESS, KM_CTRL, 0);
2692         WM_keymap_add_item(keymap, "SCREEN_OT_screencast", F3KEY, KM_PRESS, KM_ALT, 0);
2693
2694          /* tests */
2695         WM_keymap_add_item(keymap, "SCREEN_OT_region_split", SKEY, KM_PRESS, KM_CTRL|KM_ALT, 0);
2696         WM_keymap_add_item(keymap, "SCREEN_OT_region_foursplit", SKEY, KM_PRESS, KM_CTRL|KM_ALT|KM_SHIFT, 0);
2697         
2698         WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_history", F3KEY, KM_PRESS, 0, 0);
2699         WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_last", F4KEY, KM_PRESS, 0, 0);
2700         WM_keymap_add_item(keymap, "SCREEN_OT_region_flip", F5KEY, KM_PRESS, 0, 0);
2701         WM_keymap_verify_item(keymap, "SCREEN_OT_redo_last", F6KEY, KM_PRESS, 0, 0);
2702         
2703         RNA_string_set(WM_keymap_add_item(keymap, "SCRIPT_OT_python_file_run", F7KEY, KM_PRESS, 0, 0)->ptr, "filename", "test.py");
2704         WM_keymap_verify_item(keymap, "SCRIPT_OT_python_run_ui_scripts", F8KEY, KM_PRESS, 0, 0);
2705
2706         /* files */
2707         WM_keymap_add_item(keymap, "FILE_OT_exec", RETKEY, KM_PRESS, 0, 0);
2708         WM_keymap_add_item(keymap, "FILE_OT_cancel", ESCKEY, KM_PRESS, 0, 0);
2709         
2710         /* undo */
2711         WM_keymap_add_item(keymap, "ED_OT_undo", ZKEY, KM_PRESS, KM_CTRL, 0);
2712         WM_keymap_add_item(keymap, "ED_OT_undo", ZKEY, KM_PRESS, KM_OSKEY, 0);
2713         WM_keymap_add_item(keymap, "ED_OT_redo", ZKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0);
2714         WM_keymap_add_item(keymap, "ED_OT_redo", ZKEY, KM_PRESS, KM_SHIFT|KM_OSKEY, 0);
2715                                                   
2716         /* render */
2717         WM_keymap_add_item(keymap, "SCREEN_OT_render", F12KEY, KM_PRESS, 0, 0);
2718         WM_keymap_add_item(keymap, "SCREEN_OT_render_view_cancel", ESCKEY, KM_PRESS, 0, 0);
2719         
2720         /* frame offsets & play */
2721         keymap= WM_keymap_listbase(wm, "Frames", 0, 0);
2722         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 10);
2723         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -10);
2724         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -1);
2725         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 1);
2726         WM_keymap_add_item(keymap, "SCREEN_OT_animation_play", AKEY, KM_PRESS, KM_ALT, 0);
2727         
2728 }
2729