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