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