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