Animation Playback - Return to original frame
[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_math.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(isect_point_tri_v2_int(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         float cfra= (scene)? (float)(CFRA) : 0.0f;
1472         short next= RNA_boolean_get(op->ptr, "next");
1473         
1474         /* sanity checks */
1475         if (scene == NULL)
1476                 return OPERATOR_CANCELLED;
1477         
1478         /* init binarytree-list for getting keyframes */
1479         BLI_dlrbTree_init(&keys);
1480         
1481         /* populate tree with keyframe nodes */
1482         if (scene && scene->adt)
1483                 scene_to_keylist(NULL, scene, &keys, NULL);
1484         if (ob && ob->adt)
1485                 ob_to_keylist(NULL, ob, &keys, NULL);
1486                 
1487         /* build linked-list for searching */
1488         BLI_dlrbTree_linkedlist_sync(&keys);
1489         
1490         /* find matching keyframe in the right direction */
1491         if (next)
1492                 ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1493         else
1494                 ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1495         
1496         /* set the new frame (if keyframe found) */
1497         if (ak) 
1498                 CFRA= (int)ak->cfra;
1499         else
1500                 BKE_report(op->reports, RPT_ERROR, "No more keyframes to jump to in this direction");
1501         
1502         /* free temp stuff */
1503         BLI_dlrbTree_free(&keys);
1504         
1505         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1506
1507         return OPERATOR_FINISHED;
1508 }
1509
1510 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1511 {
1512         ot->name = "Jump to Keyframe";
1513         ot->description= "Jump to previous/next keyframe.";
1514         ot->idname = "SCREEN_OT_keyframe_jump";
1515
1516         ot->exec= keyframe_jump_exec;
1517
1518         ot->poll= ED_operator_screenactive;
1519         ot->flag= 0;
1520
1521         /* rna */
1522         RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1523 }
1524
1525 /* ************** switch screen operator ***************************** */
1526
1527
1528 /* function to be called outside UI context, or for redo */
1529 static int screen_set_exec(bContext *C, wmOperator *op)
1530 {
1531         bScreen *screen= CTX_wm_screen(C);
1532         ScrArea *sa= CTX_wm_area(C);
1533         int tot= BLI_countlist(&CTX_data_main(C)->screen);
1534         int delta= RNA_int_get(op->ptr, "delta");
1535         
1536         /* this screen is 'fake', solve later XXX */
1537         if(sa && sa->full)
1538                 return OPERATOR_CANCELLED;
1539         
1540         if(delta==1) {
1541                 while(tot--) {
1542                         screen= screen->id.next;
1543                         if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1544                         if(screen->winid==0 && screen->full==0)
1545                                 break;
1546                 }
1547         }
1548         else if(delta== -1) {
1549                 while(tot--) {
1550                         screen= screen->id.prev;
1551                         if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1552                         if(screen->winid==0 && screen->full==0)
1553                                 break;
1554                 }
1555         }
1556         else {
1557                 screen= NULL;
1558         }
1559         
1560         if(screen) {
1561                 ED_screen_set(C, screen);
1562                 return OPERATOR_FINISHED;
1563         }
1564         return OPERATOR_CANCELLED;
1565 }
1566
1567 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1568 {
1569         ot->name = "Set Screen";
1570         ot->description= "Cycle through available screens.";
1571         ot->idname = "SCREEN_OT_screen_set";
1572         
1573         ot->exec= screen_set_exec;
1574         ot->poll= ED_operator_screenactive;
1575         
1576         /* rna */
1577         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1578 }
1579
1580 /* ************** screen full-area operator ***************************** */
1581
1582
1583 /* function to be called outside UI context, or for redo */
1584 static int screen_full_area_exec(bContext *C, wmOperator *op)
1585 {
1586         ed_screen_fullarea(C, CTX_wm_window(C), CTX_wm_area(C));
1587         return OPERATOR_FINISHED;
1588 }
1589
1590 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1591 {
1592         ot->name = "Toggle Full Screen";
1593         ot->description= "Toggle display selected area as fullscreen.";
1594         ot->idname = "SCREEN_OT_screen_full_area";
1595         
1596         ot->exec= screen_full_area_exec;
1597         ot->poll= ED_operator_areaactive;
1598         ot->flag= 0;
1599
1600 }
1601
1602
1603
1604 /* ************** join area operator ********************************************** */
1605
1606 /* operator state vars used:  
1607                         x1, y1     mouse coord in first area, which will disappear
1608                         x2, y2     mouse coord in 2nd area, which will become joined
1609
1610 functions:
1611
1612    init()   find edge based on state vars 
1613                         test if the edge divides two areas, 
1614                         store active and nonactive area,
1615             
1616    apply()  do the actual join
1617
1618    exit()       cleanup, send notifier
1619
1620 callbacks:
1621
1622    exec()       calls init, apply, exit 
1623    
1624    invoke() sets mouse coords in x,y
1625             call init()
1626             add modal handler
1627
1628    modal()      accept modal events while doing it
1629                         call apply() with active window and nonactive window
1630             call exit() and remove handler when LMB confirm
1631
1632 */
1633
1634 typedef struct sAreaJoinData
1635 {
1636         ScrArea *sa1;   /* first area to be considered */
1637         ScrArea *sa2;   /* second area to be considered */
1638         ScrArea *scr;   /* designed for removal */
1639
1640 } sAreaJoinData;
1641
1642
1643 /* validate selection inside screen, set variables OK */
1644 /* return 0: init failed */
1645 /* XXX todo: find edge based on (x,y) and set other area? */
1646 static int area_join_init(bContext *C, wmOperator *op)
1647 {
1648         ScrArea *sa1, *sa2;
1649         sAreaJoinData* jd= NULL;
1650         int x1, y1;
1651         int x2, y2;
1652
1653         /* required properties, make negative to get return 0 if not set by caller */
1654         x1= RNA_int_get(op->ptr, "x1");
1655         y1= RNA_int_get(op->ptr, "y1");
1656         x2= RNA_int_get(op->ptr, "x2");
1657         y2= RNA_int_get(op->ptr, "y2");
1658         
1659         sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1660         sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1661         if(sa1==NULL || sa2==NULL || sa1==sa2)
1662                 return 0;
1663
1664         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1665                 
1666         jd->sa1 = sa1;
1667         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1668         jd->sa2 = sa2;
1669         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1670         
1671         op->customdata= jd;
1672         
1673         return 1;
1674 }
1675
1676 /* apply the join of the areas (space types) */
1677 static int area_join_apply(bContext *C, wmOperator *op)
1678 {
1679         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1680         if (!jd) return 0;
1681
1682         if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1683                 return 0;
1684         }
1685         if (CTX_wm_area(C) == jd->sa2) {
1686                 CTX_wm_area_set(C, NULL);
1687                 CTX_wm_region_set(C, NULL);
1688         }
1689
1690         return 1;
1691 }
1692
1693 /* finish operation */
1694 static void area_join_exit(bContext *C, wmOperator *op)
1695 {
1696         if (op->customdata) {
1697                 MEM_freeN(op->customdata);
1698                 op->customdata = NULL;
1699         }
1700
1701         /* this makes sure aligned edges will result in aligned grabbing */
1702         removedouble_scredges(CTX_wm_screen(C));
1703         removenotused_scredges(CTX_wm_screen(C));
1704         removenotused_scrverts(CTX_wm_screen(C));
1705 }
1706
1707 static int area_join_exec(bContext *C, wmOperator *op)
1708 {
1709         if(!area_join_init(C, op)) 
1710                 return OPERATOR_CANCELLED;
1711         
1712         area_join_apply(C, op);
1713         area_join_exit(C, op);
1714
1715         return OPERATOR_FINISHED;
1716 }
1717
1718 /* interaction callback */
1719 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1720 {
1721
1722         if(event->type==EVT_ACTIONZONE_AREA) {
1723                 sActionzoneData *sad= event->customdata;
1724
1725                 if(sad->modifier>0) {
1726                         return OPERATOR_PASS_THROUGH;
1727                 }
1728                 
1729                 /* verify *sad itself */
1730                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1731                         return OPERATOR_PASS_THROUGH;
1732                 
1733                 /* is this our *sad? if areas equal it should be passed on */
1734                 if(sad->sa1==sad->sa2)
1735                         return OPERATOR_PASS_THROUGH;
1736                 
1737                 /* prepare operator state vars */
1738                 RNA_int_set(op->ptr, "x1", sad->x);
1739                 RNA_int_set(op->ptr, "y1", sad->y);
1740                 RNA_int_set(op->ptr, "x2", event->x);
1741                 RNA_int_set(op->ptr, "y2", event->y);
1742
1743                 if(!area_join_init(C, op)) 
1744                         return OPERATOR_PASS_THROUGH;
1745         
1746                 /* add temp handler */
1747                 WM_event_add_modal_handler(C, op);
1748         
1749                 return OPERATOR_RUNNING_MODAL;
1750         }
1751         
1752         return OPERATOR_PASS_THROUGH;
1753 }
1754
1755 static int area_join_cancel(bContext *C, wmOperator *op)
1756 {
1757         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1758
1759         if (jd->sa1) {
1760                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1761                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1762         }
1763         if (jd->sa2) {
1764                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1765                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1766         }
1767
1768         WM_event_add_notifier(C, NC_WINDOW, NULL);
1769         
1770         area_join_exit(C, op);
1771
1772         return OPERATOR_CANCELLED;
1773 }
1774
1775 /* modal callback while selecting area (space) that will be removed */
1776 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1777 {
1778         bScreen *sc= CTX_wm_screen(C);
1779         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1780         
1781         /* execute the events */
1782         switch(event->type) {
1783                         
1784                 case MOUSEMOVE: 
1785                         {
1786                                 ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1787                                 int dir;
1788                                 
1789                                 if (sa) {                                       
1790                                         if (jd->sa1 != sa) {
1791                                                 dir = area_getorientation(sc, jd->sa1, sa);
1792                                                 if (dir >= 0) {
1793                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1794                                                         jd->sa2 = sa;
1795                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1796                                                 } 
1797                                                 else {
1798                                                         /* we are not bordering on the previously selected area 
1799                                                            we check if area has common border with the one marked for removal
1800                                                            in this case we can swap areas.
1801                                                         */
1802                                                         dir = area_getorientation(sc, sa, jd->sa2);
1803                                                         if (dir >= 0) {
1804                                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1805                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1806                                                                 jd->sa1 = jd->sa2;
1807                                                                 jd->sa2 = sa;
1808                                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1809                                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1810                                                         } 
1811                                                         else {
1812                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1813                                                                 jd->sa2 = NULL;
1814                                                         }
1815                                                 }
1816                                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
1817                                         } 
1818                                         else {
1819                                                 /* we are back in the area previously selected for keeping 
1820                                                  * we swap the areas if possible to allow user to choose */
1821                                                 if (jd->sa2 != NULL) {
1822                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1823                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1824                                                         jd->sa1 = jd->sa2;
1825                                                         jd->sa2 = sa;
1826                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1827                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1828                                                         dir = area_getorientation(sc, jd->sa1, jd->sa2);
1829                                                         if (dir < 0) {
1830                                                                 printf("oops, didn't expect that!\n");
1831                                                         }
1832                                                 } 
1833                                                 else {
1834                                                         dir = area_getorientation(sc, jd->sa1, sa);
1835                                                         if (dir >= 0) {
1836                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1837                                                                 jd->sa2 = sa;
1838                                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1839                                                         }
1840                                                 }
1841                                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
1842                                         }
1843                                 }
1844                         }
1845                         break;
1846                 case LEFTMOUSE:
1847                         if(event->val==KM_RELEASE) {
1848                                 area_join_apply(C, op);
1849                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1850                                 area_join_exit(C, op);
1851                                 return OPERATOR_FINISHED;
1852                         }
1853                         break;
1854                         
1855                 case ESCKEY:
1856                         return area_join_cancel(C, op);
1857         }
1858
1859         return OPERATOR_RUNNING_MODAL;
1860 }
1861
1862 /* Operator for joining two areas (space types) */
1863 static void SCREEN_OT_area_join(wmOperatorType *ot)
1864 {
1865         /* identifiers */
1866         ot->name= "Join area";
1867         ot->description= "Join selected areas into new window.";
1868         ot->idname= "SCREEN_OT_area_join";
1869         
1870         /* api callbacks */
1871         ot->exec= area_join_exec;
1872         ot->invoke= area_join_invoke;
1873         ot->modal= area_join_modal;
1874         ot->poll= ED_operator_areaactive;
1875
1876         ot->flag= OPTYPE_BLOCKING;
1877
1878         /* rna */
1879         RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1880         RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
1881         RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
1882         RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
1883 }
1884
1885 /* ************** repeat last operator ***************************** */
1886
1887 static int repeat_last_exec(bContext *C, wmOperator *op)
1888 {
1889         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
1890         
1891         if(lastop)
1892                 WM_operator_repeat(C, lastop);
1893         
1894         return OPERATOR_CANCELLED;
1895 }
1896
1897 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
1898 {
1899         /* identifiers */
1900         ot->name= "Repeat Last";
1901         ot->description= "Repeat last action.";
1902         ot->idname= "SCREEN_OT_repeat_last";
1903         
1904         /* api callbacks */
1905         ot->exec= repeat_last_exec;
1906         
1907         ot->poll= ED_operator_screenactive;
1908         
1909 }
1910
1911 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
1912 {
1913         wmWindowManager *wm= CTX_wm_manager(C);
1914         wmOperator *lastop;
1915         uiPopupMenu *pup;
1916         uiLayout *layout;
1917         int items, i;
1918         
1919         items= BLI_countlist(&wm->operators);
1920         if(items==0)
1921                 return OPERATOR_CANCELLED;
1922         
1923         pup= uiPupMenuBegin(C, op->type->name, 0);
1924         layout= uiPupMenuLayout(pup);
1925
1926         for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
1927                 uiItemIntO(layout, lastop->type->name, 0, op->type->idname, "index", i);
1928
1929         uiPupMenuEnd(C, pup);
1930         
1931         return OPERATOR_CANCELLED;
1932 }
1933
1934 static int repeat_history_exec(bContext *C, wmOperator *op)
1935 {
1936         wmWindowManager *wm= CTX_wm_manager(C);
1937         
1938         op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
1939         if(op) {
1940                 /* let's put it as last operator in list */
1941                 BLI_remlink(&wm->operators, op);
1942                 BLI_addtail(&wm->operators, op);
1943                 
1944                 WM_operator_repeat(C, op);
1945         }
1946                                          
1947         return OPERATOR_FINISHED;
1948 }
1949
1950 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
1951 {
1952         /* identifiers */
1953         ot->name= "Repeat History";
1954         ot->description= "Display menu for previous actions performed.";
1955         ot->idname= "SCREEN_OT_repeat_history";
1956         
1957         /* api callbacks */
1958         ot->invoke= repeat_history_invoke;
1959         ot->exec= repeat_history_exec;
1960         
1961         ot->poll= ED_operator_screenactive;
1962         
1963         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
1964 }
1965
1966 /* ********************** redo operator ***************************** */
1967
1968 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
1969 {
1970         wmWindowManager *wm= CTX_wm_manager(C);
1971         wmOperator *lastop;
1972
1973         /* only for operators that are registered and did an undo push */
1974         for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
1975                 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
1976                         break;
1977         
1978         if(lastop)
1979                 WM_operator_redo_popup(C, lastop);
1980
1981         return OPERATOR_CANCELLED;
1982 }
1983
1984 static void SCREEN_OT_redo_last(wmOperatorType *ot)
1985 {
1986         /* identifiers */
1987         ot->name= "Redo Last";
1988         ot->description= "Display menu for last action performed.";
1989         ot->idname= "SCREEN_OT_redo_last";
1990         
1991         /* api callbacks */
1992         ot->invoke= redo_last_invoke;
1993         
1994         ot->poll= ED_operator_screenactive;
1995 }
1996
1997 /* ************** region four-split operator ***************************** */
1998
1999 /* insert a region in the area region list */
2000 static int region_foursplit_exec(bContext *C, wmOperator *op)
2001 {
2002         ARegion *ar= CTX_wm_region(C);
2003         
2004         /* some rules... */
2005         if(ar->regiontype!=RGN_TYPE_WINDOW)
2006                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2007         else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2008                 ScrArea *sa= CTX_wm_area(C);
2009                 ARegion *arn;
2010                 
2011                 /* keep current region */
2012                 ar->alignment= 0;
2013                 
2014                 if(sa->spacetype==SPACE_VIEW3D) {
2015                         RegionView3D *rv3d= ar->regiondata;
2016                         rv3d->viewlock= 0;
2017                         rv3d->rflag &= ~RV3D_CLIPPING;
2018                 }
2019                 
2020                 for(ar= sa->regionbase.first; ar; ar= arn) {
2021                         arn= ar->next;
2022                         if(ar->alignment==RGN_ALIGN_QSPLIT) {
2023                                 ED_region_exit(C, ar);
2024                                 BKE_area_region_free(sa->type, ar);
2025                                 BLI_remlink(&sa->regionbase, ar);
2026                                 MEM_freeN(ar);
2027                         }
2028                 }
2029                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2030         }
2031         else if(ar->next)
2032                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2033         else {
2034                 ScrArea *sa= CTX_wm_area(C);
2035                 ARegion *newar;
2036                 int count;
2037                 
2038                 ar->alignment= RGN_ALIGN_QSPLIT;
2039                 
2040                 for(count=0; count<3; count++) {
2041                         newar= BKE_area_region_copy(sa->type, ar);
2042                         BLI_addtail(&sa->regionbase, newar);
2043                 }
2044                 
2045                 /* lock views and set them */
2046                 if(sa->spacetype==SPACE_VIEW3D) {
2047                         RegionView3D *rv3d;
2048                         
2049                         rv3d= ar->regiondata;
2050                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2051                         
2052                         ar= ar->next;
2053                         rv3d= ar->regiondata;
2054                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2055                         
2056                         ar= ar->next;
2057                         rv3d= ar->regiondata;
2058                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2059                         
2060                         ar= ar->next;
2061                         rv3d= ar->regiondata;
2062                         rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2063                 }
2064                 
2065                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2066         }
2067         
2068         
2069         return OPERATOR_FINISHED;
2070 }
2071
2072 static void SCREEN_OT_region_foursplit(wmOperatorType *ot)
2073 {
2074         /* identifiers */
2075         ot->name= "Toggle Quad View";
2076         ot->description= "Split selected area into camera, front, right & top views.";
2077         ot->idname= "SCREEN_OT_region_foursplit";
2078         
2079         /* api callbacks */
2080 //      ot->invoke= WM_operator_confirm;
2081         ot->exec= region_foursplit_exec;
2082         ot->poll= ED_operator_areaactive;
2083         ot->flag= 0;
2084 }
2085
2086
2087
2088 /* ************** region flip operator ***************************** */
2089
2090 /* flip a region alignment */
2091 static int region_flip_exec(bContext *C, wmOperator *op)
2092 {
2093         ARegion *ar= CTX_wm_region(C);
2094
2095         if(ar->alignment==RGN_ALIGN_TOP)
2096                 ar->alignment= RGN_ALIGN_BOTTOM;
2097         else if(ar->alignment==RGN_ALIGN_BOTTOM)
2098                 ar->alignment= RGN_ALIGN_TOP;
2099         else if(ar->alignment==RGN_ALIGN_LEFT)
2100                 ar->alignment= RGN_ALIGN_RIGHT;
2101         else if(ar->alignment==RGN_ALIGN_RIGHT)
2102                 ar->alignment= RGN_ALIGN_LEFT;
2103         
2104         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2105         printf("executed region flip\n");
2106         
2107         return OPERATOR_FINISHED;
2108 }
2109
2110
2111 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2112 {
2113         /* identifiers */
2114         ot->name= "Flip Region";
2115         ot->idname= "SCREEN_OT_region_flip";
2116         
2117         /* api callbacks */
2118         ot->exec= region_flip_exec;
2119         
2120         ot->poll= ED_operator_areaactive;
2121         ot->flag= 0;
2122
2123 }
2124
2125 /* ****************** anim player, with timer ***************** */
2126
2127 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2128 {
2129         if(regiontype==RGN_TYPE_WINDOW) {
2130
2131                 switch (spacetype) {
2132                         case SPACE_VIEW3D:
2133                                 if(redraws & TIME_ALL_3D_WIN)
2134                                         return 1;
2135                                 break;
2136                         case SPACE_IPO:
2137                         case SPACE_ACTION:
2138                         case SPACE_NLA:
2139                                 if(redraws & TIME_ALL_ANIM_WIN)
2140                                         return 1;
2141                                 break;
2142                         case SPACE_TIME:
2143                                 /* if only 1 window or 3d windows, we do timeline too */
2144                                 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2145                                         return 1;
2146                                 break;
2147                         case SPACE_BUTS:
2148                                 if(redraws & TIME_ALL_BUTS_WIN)
2149                                         return 1;
2150                                 break;
2151                         case SPACE_SEQ:
2152                                 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2153                                         return 1;
2154                                 break;
2155                         case SPACE_NODE:
2156                                 if(redraws & (TIME_NODES))
2157                                         return 1;
2158                                 break;
2159                         case SPACE_IMAGE:
2160                                 if(redraws & TIME_ALL_IMAGE_WIN)
2161                                         return 1;
2162                                 break;
2163                                 
2164                 }
2165         }
2166         else if(regiontype==RGN_TYPE_UI) {
2167                 if(redraws & TIME_ALL_BUTS_WIN)
2168                         return 1;
2169         }
2170         else if(regiontype==RGN_TYPE_HEADER) {
2171                 if(spacetype==SPACE_TIME)
2172                         return 1;
2173         }
2174         return 0;
2175 }
2176
2177 static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event)
2178 {
2179         bScreen *screen= CTX_wm_screen(C);
2180         
2181         if(screen->animtimer==event->customdata) {
2182                 Scene *scene= CTX_data_scene(C);
2183                 wmTimer *wt= screen->animtimer;
2184                 ScreenAnimData *sad= wt->customdata;
2185                 ScrArea *sa;
2186                 int sync;
2187                 
2188                 /* sync, don't sync, or follow scene setting */
2189                 if(sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2190                 else if(sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2191                 else sync= (scene->audio.flag & AUDIO_SYNC);
2192                 
2193                 if(sync) {
2194                         /* skip frames */
2195                         int step = floor(wt->duration * FPS);
2196                         if(sad->flag & ANIMPLAY_FLAG_REVERSE) // XXX does this option work with audio?
2197                                 scene->r.cfra -= step;
2198                         else
2199                                 scene->r.cfra += step;
2200                         wt->duration -= ((float)step)/FPS;
2201                 }
2202                 else {
2203                         /* one frame +/- */
2204                         if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2205                                 scene->r.cfra--;
2206                         else
2207                                 scene->r.cfra++;
2208                 }
2209                 
2210                 /* reset 'jumped' flag before checking if we need to jump... */
2211                 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2212                 
2213                 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2214                         /* jump back to end? */
2215                         if (scene->r.psfra) {
2216                                 if (scene->r.cfra < scene->r.psfra) {
2217                                         scene->r.cfra= scene->r.pefra;
2218                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2219                                 }
2220                         }
2221                         else {
2222                                 if (scene->r.cfra < scene->r.sfra) {
2223                                         scene->r.cfra= scene->r.efra;
2224                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2225                                 }
2226                         }
2227                 }
2228                 else {
2229                         /* jump back to start? */
2230                         if (scene->r.psfra) {
2231                                 if (scene->r.cfra > scene->r.pefra) {
2232                                         scene->r.cfra= scene->r.psfra;
2233                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2234                                 }
2235                         }
2236                         else {
2237                                 if (scene->r.cfra > scene->r.efra) {
2238                                         scene->r.cfra= scene->r.sfra;
2239                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2240                                 }
2241                         }
2242                 }
2243                 
2244                 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2245                 ED_update_for_newframe(C, 1);
2246                 
2247                 sound_update_playing(C);
2248                 
2249                 for(sa= screen->areabase.first; sa; sa= sa->next) {
2250                         ARegion *ar;
2251                         for(ar= sa->regionbase.first; ar; ar= ar->next) {
2252                                 if(ar==sad->ar)
2253                                         ED_region_tag_redraw(ar);
2254                                 else
2255                                         if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2256                                                 ED_region_tag_redraw(ar);
2257                         }
2258                 }
2259                 
2260                 /* recalculate the timestep for the timer now that we've finished calculating this,
2261                  * since the frames-per-second value may have been changed
2262                  */
2263                 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2264                 wt->timestep= (1.0/FPS);
2265                 
2266                 return OPERATOR_FINISHED;
2267         }
2268         return OPERATOR_PASS_THROUGH;
2269 }
2270
2271 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2272 {
2273         /* identifiers */
2274         ot->name= "Animation Step";
2275         ot->description= "Step through animation by position.";
2276         ot->idname= "SCREEN_OT_animation_step";
2277         
2278         /* api callbacks */
2279         ot->invoke= screen_animation_step;
2280         
2281         ot->poll= ED_operator_screenactive;
2282         
2283 }
2284
2285 /* ****************** anim player, starts or ends timer ***************** */
2286
2287 /* toggle operator */
2288 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
2289 {
2290         bScreen *screen= CTX_wm_screen(C);
2291         
2292         if(screen->animtimer) {
2293                 /* stop playback now */
2294                 ED_screen_animation_timer(C, 0, 0, 0);
2295                 sound_stop_all(C);
2296         }
2297         else {
2298                 ScrArea *sa= CTX_wm_area(C);
2299                 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2300                 int sync= -1;
2301
2302                 if(RNA_property_is_set(op->ptr, "sync"))
2303                         sync= (RNA_boolean_get(op->ptr, "sync"));
2304                 
2305                 /* timeline gets special treatment since it has it's own menu for determining redraws */
2306                 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2307                         SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2308                         
2309                         ED_screen_animation_timer(C, stime->redraws, sync, mode);
2310                         
2311                         /* update region if TIME_REGION was set, to leftmost 3d window */
2312                         ED_screen_animation_timer_update(C, stime->redraws);
2313                 }
2314                 else {
2315                         int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2316
2317                         /* XXX - would like a better way to deal with this situation - Campbell */
2318                         if((sa) && (sa->spacetype == SPACE_SEQ)) {
2319                                 redraws |= TIME_SEQ;
2320                         }
2321
2322                         ED_screen_animation_timer(C, redraws, sync, mode);
2323                         
2324                         if(screen->animtimer) {
2325                                 wmTimer *wt= screen->animtimer;
2326                                 ScreenAnimData *sad= wt->customdata;
2327                                 
2328                                 sad->ar= CTX_wm_region(C);
2329                         }
2330                 }
2331         }
2332         
2333         return OPERATOR_FINISHED;
2334 }
2335
2336 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2337 {
2338         /* identifiers */
2339         ot->name= "Play Animation";
2340         ot->description= "Play animation.";
2341         ot->idname= "SCREEN_OT_animation_play";
2342         
2343         /* api callbacks */
2344         ot->invoke= screen_animation_play;
2345         
2346         ot->poll= ED_operator_screenactive;
2347         
2348         RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2349         RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate and stay in sync with audio.");
2350 }
2351
2352 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2353 {
2354         bScreen *screen= CTX_wm_screen(C);
2355         
2356         if(screen->animtimer) {
2357                 ScreenAnimData *sad= screen->animtimer->customdata;
2358                 Scene *scene= CTX_data_scene(C);
2359                 
2360                 /* reset current frame before stopping, and just send a notifier to deal with the rest 
2361                  * (since playback still needs to be stopped)
2362                  */
2363                 scene->r.cfra= sad->sfra;
2364                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2365                 
2366                 /* call the other "toggling" operator to clean up now */
2367                 return screen_animation_play(C, op, event);
2368         }
2369         
2370         return OPERATOR_PASS_THROUGH;
2371 }
2372
2373 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2374 {
2375         /* identifiers */
2376         ot->name= "Cancel Animation";
2377         ot->description= "Cancel animation, returning to the original frame.";
2378         ot->idname= "SCREEN_OT_animation_cancel";
2379         
2380         /* api callbacks */
2381         ot->invoke= screen_animation_cancel;
2382         
2383         ot->poll= ED_operator_screenactive;
2384 }
2385
2386 /* ************** border select operator (template) ***************************** */
2387
2388 /* operator state vars used: (added by default WM callbacks)   
2389         xmin, ymin     
2390         xmax, ymax     
2391
2392         customdata: the wmGesture pointer
2393
2394 callbacks:
2395
2396         exec()  has to be filled in by user
2397
2398         invoke() default WM function
2399                          adds modal handler
2400
2401         modal() default WM function 
2402                         accept modal events while doing it, calls exec(), handles ESC and border drawing
2403         
2404         poll()  has to be filled in by user for context
2405 */
2406 #if 0
2407 static int border_select_do(bContext *C, wmOperator *op)
2408 {
2409         int event_type= RNA_int_get(op->ptr, "event_type");
2410         
2411         if(event_type==LEFTMOUSE)
2412                 printf("border select do select\n");
2413         else if(event_type==RIGHTMOUSE)
2414                 printf("border select deselect\n");
2415         else 
2416                 printf("border select do something\n");
2417         
2418         return 1;
2419 }
2420
2421 static void SCREEN_OT_border_select(wmOperatorType *ot)
2422 {
2423         /* identifiers */
2424         ot->name= "Border select";
2425         ot->idname= "SCREEN_OT_border_select";
2426         
2427         /* api callbacks */
2428         ot->exec= border_select_do;
2429         ot->invoke= WM_border_select_invoke;
2430         ot->modal= WM_border_select_modal;
2431         
2432         ot->poll= ED_operator_areaactive;
2433         
2434         /* rna */
2435         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2436         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2437         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2438         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2439         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2440
2441 }
2442 #endif
2443
2444 /* ****************************** render invoking ***************** */
2445
2446 /* set callbacks, exported to sequence render too. 
2447 Only call in foreground (UI) renders. */
2448
2449 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2450 /* window as the last possible alternative.                                                                        */
2451 static ScrArea *biggest_non_image_area(bContext *C)
2452 {
2453         bScreen *sc= CTX_wm_screen(C);
2454         ScrArea *sa, *big= NULL;
2455         int size, maxsize= 0, bwmaxsize= 0;
2456         short foundwin= 0;
2457         
2458         for(sa= sc->areabase.first; sa; sa= sa->next) {
2459                 if(sa->winx > 30 && sa->winy > 30) {
2460                         size= sa->winx*sa->winy;
2461                         if(sa->spacetype == SPACE_BUTS) {
2462                                 if(foundwin == 0 && size > bwmaxsize) {
2463                                         bwmaxsize= size;
2464                                         big= sa;        
2465                                 }
2466                         }
2467                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2468                                 maxsize= size;
2469                                 big= sa;
2470                                 foundwin= 1;
2471                         }
2472                 }
2473         }
2474         
2475         return big;
2476 }
2477
2478 static ScrArea *biggest_area(bContext *C)
2479 {
2480         bScreen *sc= CTX_wm_screen(C);
2481         ScrArea *sa, *big= NULL;
2482         int size, maxsize= 0;
2483         
2484         for(sa= sc->areabase.first; sa; sa= sa->next) {
2485                 size= sa->winx*sa->winy;
2486                 if(size > maxsize) {
2487                         maxsize= size;
2488                         big= sa;
2489                 }
2490         }
2491         return big;
2492 }
2493
2494
2495 static ScrArea *find_area_showing_r_result(bContext *C)
2496 {
2497         wmWindowManager *wm= CTX_wm_manager(C);
2498         wmWindow *win;
2499         ScrArea *sa = NULL;
2500         SpaceImage *sima;
2501         
2502         /* find an imagewindow showing render result */
2503         for(win=wm->windows.first; win; win=win->next) {
2504                 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2505                         if(sa->spacetype==SPACE_IMAGE) {
2506                                 sima= sa->spacedata.first;
2507                                 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2508                                         break;
2509                         }
2510                 }
2511         }
2512
2513         return sa;
2514 }
2515
2516 static ScrArea *find_area_image_empty(bContext *C)
2517 {
2518         bScreen *sc= CTX_wm_screen(C);
2519         ScrArea *sa;
2520         SpaceImage *sima;
2521         
2522         /* find an imagewindow showing render result */
2523         for(sa=sc->areabase.first; sa; sa= sa->next) {
2524                 if(sa->spacetype==SPACE_IMAGE) {
2525                         sima= sa->spacedata.first;
2526                         if(!sima->image)
2527                                 break;
2528                 }
2529         }
2530         return sa;
2531 }
2532
2533 #if 0 // XXX not used
2534 static ScrArea *find_empty_image_area(bContext *C)
2535 {
2536         bScreen *sc= CTX_wm_screen(C);
2537         ScrArea *sa;
2538         SpaceImage *sima;
2539         
2540         /* find an imagewindow showing render result */
2541         for(sa=sc->areabase.first; sa; sa= sa->next) {
2542                 if(sa->spacetype==SPACE_IMAGE) {
2543                         sima= sa->spacedata.first;
2544                         if(!sima->image)
2545                                 break;
2546                 }
2547         }
2548         return sa;
2549 }
2550 #endif // XXX not used
2551
2552 /* new window uses x,y to set position */
2553 static void screen_set_image_output(bContext *C, int mx, int my)
2554 {
2555         wmWindow *win= CTX_wm_window(C);
2556         Scene *scene= CTX_data_scene(C);
2557         ScrArea *sa= NULL;
2558         SpaceImage *sima;
2559         
2560         if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2561                 rcti rect;
2562                 int sizex, sizey;
2563                 
2564                 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2565                 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2566                 
2567                 /* arbitrary... miniature image window views don't make much sense */
2568                 if(sizex < 320) sizex= 320;
2569                 if(sizey < 256) sizey= 256;
2570                 
2571                 /* XXX some magic to calculate postition */
2572                 rect.xmin= mx + win->posx - sizex/2;
2573                 rect.ymin= my + win->posy - sizey/2;
2574                 rect.xmax= rect.xmin + sizex;
2575                 rect.ymax= rect.ymin + sizey;
2576                 
2577                 /* changes context! */
2578                 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2579                 
2580                 sa= CTX_wm_area(C);
2581         }
2582         else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2583                 /* this function returns with changed context */
2584                 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2585                 sa= CTX_wm_area(C);
2586         }
2587         
2588         if(!sa) {
2589                 sa= find_area_showing_r_result(C);
2590                 if(sa==NULL)
2591                         sa= find_area_image_empty(C);
2592                 
2593                 if(sa==NULL) {
2594                         /* find largest open non-image area */
2595                         sa= biggest_non_image_area(C);
2596                         if(sa) {
2597                                 ED_area_newspace(C, sa, SPACE_IMAGE);
2598                                 sima= sa->spacedata.first;
2599                                 
2600                                 /* makes ESC go back to prev space */
2601                                 sima->flag |= SI_PREVSPACE;
2602                         }
2603                         else {
2604                                 /* use any area of decent size */
2605                                 sa= biggest_area(C);
2606                                 if(sa->spacetype!=SPACE_IMAGE) {
2607                                         // XXX newspace(sa, SPACE_IMAGE);
2608                                         sima= sa->spacedata.first;
2609                                         
2610                                         /* makes ESC go back to prev space */
2611                                         sima->flag |= SI_PREVSPACE;
2612                                 }
2613                         }
2614                 }
2615         }       
2616         sima= sa->spacedata.first;
2617         
2618         /* get the correct image, and scale it */
2619         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2620         
2621 //      if(G.displaymode==2) { // XXX
2622                 if(sa->full) {
2623                         sima->flag |= SI_FULLWINDOW|SI_PREVSPACE;
2624                         
2625 //                      ed_screen_fullarea(C, win, sa);
2626                 }
2627 //      }
2628         
2629 }
2630
2631 /* executes blocking render */
2632 static int screen_render_exec(bContext *C, wmOperator *op)
2633 {
2634         Scene *scene= CTX_data_scene(C);
2635         Render *re= RE_GetRender(scene->id.name);
2636         
2637         if(re==NULL) {
2638                 re= RE_NewRender(scene->id.name);
2639         }
2640         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2641         
2642         if(RNA_boolean_get(op->ptr, "animation"))
2643                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step);
2644         else
2645                 RE_BlenderFrame(re, scene, scene->r.cfra);
2646         
2647         // no redraw needed, we leave state as we entered it
2648         ED_update_for_newframe(C, 1);
2649         
2650         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2651
2652         return OPERATOR_FINISHED;
2653 }
2654
2655 typedef struct RenderJob {
2656         Scene *scene;
2657         Render *re;
2658         wmWindow *win;
2659         int anim;
2660         Image *image;
2661         ImageUser iuser;
2662         short *stop;
2663         short *do_update;
2664 } RenderJob;
2665
2666 static void render_freejob(void *rjv)
2667 {
2668         RenderJob *rj= rjv;
2669         
2670         MEM_freeN(rj);
2671 }
2672
2673 /* str is IMA_RW_MAXTEXT in size */
2674 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2675 {
2676         char info_time_str[32]; // used to be extern to header_info.c
2677         uintptr_t mem_in_use, mmap_in_use;
2678         float megs_used_memory, mmap_used_memory;
2679         char *spos= str;
2680         
2681         mem_in_use= MEM_get_memory_in_use();
2682         mmap_in_use= MEM_get_mapped_memory_in_use();
2683         
2684         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2685         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2686         
2687         if(scene->lay & 0xFF000000)
2688                 spos+= sprintf(spos, "Localview | ");
2689         else if(scene->r.scemode & R_SINGLE_LAYER)
2690                 spos+= sprintf(spos, "Single Layer | ");
2691         
2692         if(rs->statstr) {
2693                 spos+= sprintf(spos, "%s ", rs->statstr);
2694         }
2695         else {
2696                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2697                 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2698                 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2699                 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2700                 
2701                 if(rs->curfield)
2702                         spos+= sprintf(spos, "Field %d ", rs->curfield);
2703                 if(rs->curblur)
2704                         spos+= sprintf(spos, "Blur %d ", rs->curblur);
2705         }
2706         
2707         BLI_timestr(rs->lastframetime, info_time_str);
2708         spos+= sprintf(spos, "Time:%s ", info_time_str);
2709         
2710         if(rs->infostr && rs->infostr[0])
2711                 spos+= sprintf(spos, "| %s ", rs->infostr);
2712         
2713         /* very weak... but 512 characters is quite safe */
2714         if(spos >= str+IMA_RW_MAXTEXT)
2715                 printf("WARNING! renderwin text beyond limit \n");
2716         
2717 }
2718
2719 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2720 {
2721         RenderJob *rj= rjv;
2722         
2723         /* malloc OK here, stats_draw is not in tile threads */
2724         if(rj->image->render_text==NULL)
2725                 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
2726         
2727         make_renderinfo_string(rs, rj->scene, rj->image->render_text);
2728         
2729         /* make jobs timer to send notifier */
2730         *(rj->do_update)= 1;
2731
2732 }
2733
2734 /* called inside thread! */
2735 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
2736 {
2737         float x1, y1, *rectf= NULL;
2738         int ymin, ymax, xmin, xmax;
2739         int rymin, rxmin;
2740         char *rectc;
2741         
2742         /* if renrect argument, we only refresh scanlines */
2743         if(renrect) {
2744                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
2745                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
2746                         return;
2747                 
2748                 /* xmin here is first subrect x coord, xmax defines subrect width */
2749                 xmin = renrect->xmin + rr->crop;
2750                 xmax = renrect->xmax - xmin - rr->crop;
2751                 if (xmax<2) return;
2752                 
2753                 ymin= renrect->ymin + rr->crop;
2754                 ymax= renrect->ymax - ymin - rr->crop;
2755                 if(ymax<2)
2756                         return;
2757                 renrect->ymin= renrect->ymax;
2758                 
2759         }
2760         else {
2761                 xmin = ymin = rr->crop;
2762                 xmax = rr->rectx - 2*rr->crop;
2763                 ymax = rr->recty - 2*rr->crop;
2764         }
2765         
2766         /* xmin ymin is in tile coords. transform to ibuf */
2767         rxmin= rr->tilerect.xmin + xmin;
2768         if(rxmin >= ibuf->x) return;
2769         rymin= rr->tilerect.ymin + ymin;
2770         if(rymin >= ibuf->y) return;
2771         
2772         if(rxmin + xmax > ibuf->x)
2773                 xmax= ibuf->x - rxmin;
2774         if(rymin + ymax > ibuf->y)
2775                 ymax= ibuf->y - rymin;
2776         
2777         if(xmax < 1 || ymax < 1) return;
2778         
2779         /* find current float rect for display, first case is after composit... still weak */
2780         if(rr->rectf)
2781                 rectf= rr->rectf;
2782         else {
2783                 if(rr->rect32)
2784                         return;
2785                 else {
2786                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
2787                         rectf= rr->renlay->rectf;
2788                 }
2789         }
2790         if(rectf==NULL) return;
2791         
2792         rectf+= 4*(rr->rectx*ymin + xmin);
2793         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
2794
2795         /* XXX make nice consistent functions for this */
2796         if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
2797                 for(y1= 0; y1<ymax; y1++) {
2798                         float *rf= rectf;
2799                         float srgb[3];
2800                         char *rc= rectc;
2801                         
2802                         /* XXX temp. because crop offset */
2803                         if( rectc >= (char *)(ibuf->rect)) {
2804                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2805                                         srgb[0]= linearrgb_to_srgb(rf[0]);
2806                                         srgb[1]= linearrgb_to_srgb(rf[1]);
2807                                         srgb[2]= linearrgb_to_srgb(rf[2]);
2808
2809                                         rc[0]= FTOCHAR(srgb[0]);
2810                                         rc[1]= FTOCHAR(srgb[1]);
2811                                         rc[2]= FTOCHAR(srgb[2]);
2812                                         rc[3]= FTOCHAR(rf[3]);
2813                                 }
2814                         }
2815                         rectf += 4*rr->rectx;
2816                         rectc += 4*ibuf->x;
2817                 }
2818         } else {
2819                 for(y1= 0; y1<ymax; y1++) {
2820                         float *rf= rectf;
2821                         char *rc= rectc;
2822                         
2823                         /* XXX temp. because crop offset */
2824                         if( rectc >= (char *)(ibuf->rect)) {
2825                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2826                                         rc[0]= FTOCHAR(rf[0]);
2827                                         rc[1]= FTOCHAR(rf[1]);
2828                                         rc[2]= FTOCHAR(rf[2]);
2829                                         rc[3]= FTOCHAR(rf[3]);
2830                                 }
2831                         }
2832                         rectf += 4*rr->rectx;
2833                         rectc += 4*ibuf->x;
2834                 }
2835         }
2836         
2837 }
2838
2839 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
2840 {
2841         RenderJob *rj= rjv;
2842         ImBuf *ibuf;
2843         void *lock;
2844         
2845         ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
2846         if(ibuf) {
2847                 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
2848
2849                 /* make jobs timer to send notifier */
2850                 *(rj->do_update)= 1;
2851         }
2852         BKE_image_release_ibuf(rj->image, lock);
2853 }
2854
2855 static void render_startjob(void *rjv, short *stop, short *do_update)
2856 {
2857         RenderJob *rj= rjv;
2858         
2859         rj->stop= stop;
2860         rj->do_update= do_update;
2861         
2862         if(rj->anim)
2863                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step);
2864         else
2865                 RE_BlenderFrame(rj->re, rj->scene, rj->scene->r.cfra);
2866 }
2867
2868 /* called by render, check job 'stop' value or the global */
2869 static int render_breakjob(void *rjv)
2870 {
2871         RenderJob *rj= rjv;
2872         
2873         if(G.afbreek)
2874                 return 1;
2875         if(rj->stop && *(rj->stop))
2876                 return 1;
2877         return 0;
2878 }
2879
2880 /* catch esc */
2881 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
2882 {
2883         /* no running blender, remove handler and pass through */
2884         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
2885            return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
2886         
2887         /* running render */
2888         switch (event->type) {
2889                 case ESCKEY:
2890                         return OPERATOR_RUNNING_MODAL;
2891                         break;
2892         }
2893         return OPERATOR_PASS_THROUGH;
2894 }
2895
2896 /* using context, starts job */
2897 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
2898 {
2899         /* new render clears all callbacks */
2900         Scene *scene= CTX_data_scene(C);
2901         Render *re;
2902         wmJob *steve;
2903         RenderJob *rj;
2904         Image *ima;
2905         
2906         /* only one render job at a time */
2907         if(WM_jobs_test(CTX_wm_manager(C), scene))
2908                 return OPERATOR_CANCELLED;
2909         
2910         /* stop all running jobs, currently previews frustrate Render */
2911         WM_jobs_stop_all(CTX_wm_manager(C));
2912         
2913         /* handle UI stuff */
2914         WM_cursor_wait(1);
2915
2916         /* flush multires changes (for sculpt) */
2917         multires_force_update(CTX_data_active_object(C));
2918         
2919         /* get editmode results */
2920         ED_object_exit_editmode(C, EM_DO_UNDO); /* 0 = does not exit editmode */
2921         
2922         // store spare
2923         // get view3d layer, local layer, make this nice api call to render
2924         // store spare
2925         
2926         /* ensure at least 1 area shows result */
2927         screen_set_image_output(C, event->x, event->y);
2928
2929         /* job custom data */
2930         rj= MEM_callocN(sizeof(RenderJob), "render job");
2931         rj->scene= scene;
2932         rj->win= CTX_wm_window(C);
2933         rj->anim= RNA_boolean_get(op->ptr, "animation");
2934         rj->iuser.scene= scene;
2935         rj->iuser.ok= 1;
2936         
2937         /* setup job */
2938         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY);
2939         WM_jobs_customdata(steve, rj, render_freejob);
2940         WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
2941         WM_jobs_callbacks(steve, render_startjob, NULL, NULL);
2942         
2943         /* get a render result image, and make sure it is empty */
2944         ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2945         BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
2946         rj->image= ima;
2947         
2948         /* setup new render */
2949         re= RE_NewRender(scene->id.name);
2950         RE_test_break_cb(re, rj, render_breakjob);
2951         RE_display_draw_cb(re, rj, image_rect_update);
2952         RE_stats_draw_cb(re, rj, image_renderinfo_cb);
2953         
2954         rj->re= re;
2955         G.afbreek= 0;
2956         
2957         //      BKE_report in render!
2958         //      RE_error_cb(re, error_cb);
2959
2960         WM_jobs_start(CTX_wm_manager(C), steve);
2961         
2962         G.afbreek= 0;
2963         
2964         WM_cursor_wait(0);
2965         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2966
2967         /* add modal handler for ESC */
2968         WM_event_add_modal_handler(C, op);
2969         
2970         return OPERATOR_RUNNING_MODAL;
2971 }
2972
2973
2974 /* contextual render, using current scene, view3d? */
2975 static void SCREEN_OT_render(wmOperatorType *ot)
2976 {
2977         /* identifiers */
2978         ot->name= "Render";
2979         ot->description= "Render active scene.";
2980         ot->idname= "SCREEN_OT_render";
2981         
2982         /* api callbacks */
2983         ot->invoke= screen_render_invoke;
2984         ot->modal= screen_render_modal;
2985         ot->exec= screen_render_exec;
2986         
2987         ot->poll= ED_operator_screenactive;
2988         
2989         RNA_def_boolean(ot->srna, "animation", 0, "Animation", "");
2990 }
2991
2992 /* ****************************** opengl render *************************** */
2993
2994 typedef struct OGLRender {
2995         Render *re;
2996         Scene *scene;
2997
2998         View3D *v3d;
2999         RegionView3D *rv3d;
3000         ARegion *ar;
3001
3002         Image *ima;
3003         ImageUser iuser;
3004
3005         GPUOffScreen *ofs;
3006         int sizex, sizey;
3007
3008         bMovieHandle *mh;
3009         int cfrao, nfra;
3010
3011         wmTimer *timer;
3012 } OGLRender;
3013
3014 static void screen_opengl_render_apply(OGLRender *oglrender)
3015 {
3016         Scene *scene= oglrender->scene;
3017         ARegion *ar= oglrender->ar;
3018         View3D *v3d= oglrender->v3d;
3019         RegionView3D *rv3d= oglrender->rv3d;
3020         RenderResult *rr;
3021         ImBuf *ibuf;
3022         void *lock;
3023         float winmat[4][4];
3024         int sizex= oglrender->sizex;
3025         int sizey= oglrender->sizey;
3026
3027         /* bind */
3028         GPU_offscreen_bind(oglrender->ofs);
3029
3030         /* render 3d view */
3031         if(rv3d->persp==RV3D_CAMOB && v3d->camera) {
3032                 RE_GetCameraWindow(oglrender->re, v3d->camera, scene->r.cfra, winmat);
3033                 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat);
3034         }
3035         else
3036                 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, NULL);
3037
3038         /* read in pixels & stamp */
3039         rr= RE_AcquireResultRead(oglrender->re);
3040         glReadPixels(0, 0, sizex, sizey, GL_RGBA, GL_FLOAT, rr->rectf);
3041         if((scene->r.scemode & R_STAMP_INFO) && (scene->r.stamp & R_STAMP_DRAW))
3042                 BKE_stamp_buf(scene, (unsigned char *)rr->rect32, rr->rectf, rr->rectx, rr->recty, 3);
3043         RE_ReleaseResult(oglrender->re);
3044
3045         /* update byte from float buffer */
3046         ibuf= BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
3047         if(ibuf) image_buffer_rect_update(NULL, rr, ibuf, NULL);
3048         BKE_image_release_ibuf(oglrender->ima, lock);
3049
3050         /* unbind */
3051         GPU_offscreen_unbind(oglrender->ofs);
3052 }
3053
3054 static int screen_opengl_render_init(bContext *C, wmOperator *op)
3055 {
3056         /* new render clears all callbacks */
3057         Scene *scene= CTX_data_scene(C);
3058         RenderResult *rr;
3059         GPUOffScreen *ofs;
3060         OGLRender *oglrender;
3061         int sizex, sizey;
3062
3063         /* ensure we have a 3d view */
3064         if(!ED_view3d_context_activate(C))
3065                 return 0;
3066         
3067         /* only one render job at a time */
3068         if(WM_jobs_test(CTX_wm_manager(C), scene))
3069                 return 0;
3070         
3071         /* stop all running jobs, currently previews frustrate Render */
3072         WM_jobs_stop_all(CTX_wm_manager(C));
3073         
3074         /* handle UI stuff */
3075         WM_cursor_wait(1);
3076
3077         /* create offscreen buffer */
3078         sizex= (scene->r.size*scene->r.xsch)/100;
3079         sizey= (scene->r.size*scene->r.ysch)/100;
3080
3081         view3d_operator_needs_opengl(C);
3082         ofs= GPU_offscreen_create(sizex, sizey);
3083
3084         if(!ofs) {
3085                 BKE_report(op->reports, RPT_ERROR, "Failed to create OpenGL offscreen buffer.");
3086                 return 0;
3087         }
3088
3089         /* allocate opengl render */
3090         oglrender= MEM_callocN(sizeof(OGLRender), "OGLRender");
3091         op->customdata= oglrender;
3092
3093         oglrender->ofs= ofs;
3094         oglrender->sizex= sizex;
3095         oglrender->sizey= sizey;
3096         oglrender->scene= scene;
3097
3098         oglrender->v3d= CTX_wm_view3d(C);
3099         oglrender->ar= CTX_wm_region(C);
3100         oglrender->rv3d= CTX_wm_region_view3d(C);
3101
3102         /* create image and image user */
3103         oglrender->ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
3104         BKE_image_signal(oglrender->ima, NULL, IMA_SIGNAL_FREE);
3105
3106         oglrender->iuser.scene= scene;
3107         oglrender->iuser.ok= 1;
3108
3109         /* create render and render result */
3110         oglrender->re= RE_NewRender(scene->id.name);
3111         RE_InitState(oglrender->re, NULL, &scene->r, sizex, sizey, NULL);
3112
3113         rr= RE_AcquireResultWrite(oglrender->re);
3114         if(rr->rectf==NULL)
3115                 rr->rectf= MEM_mallocN(sizeof(float)*4*sizex*sizex, "32 bits rects");
3116         RE_ReleaseResult(oglrender->re);
3117
3118         return 1;
3119 }
3120
3121 static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
3122 {
3123         Scene *scene= oglrender->scene;
3124
3125         if(oglrender->mh) {
3126                 if(BKE_imtype_is_movie(scene->r.imtype))
3127                         oglrender->mh->end_movie();
3128         }
3129
3130         if(oglrender->timer) {
3131                 scene->r.cfra= oglrender->cfrao;
3132                 scene_update_for_newframe(scene, scene->lay);
3133
3134                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), oglrender->timer);
3135         }
3136
3137         WM_cursor_wait(0);
3138         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, oglrender->scene);
3139
3140         GPU_offscreen_free(oglrender->ofs);
3141
3142         MEM_freeN(oglrender);
3143 }
3144
3145 static int screen_opengl_render_cancel(bContext *C, wmOperator *op)
3146 {
3147         screen_opengl_render_end(C, op->customdata);
3148
3149         return OPERATOR_CANCELLED;
3150 }
3151
3152 static int screen_opengl_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3153 {
3154         OGLRender *oglrender= op->customdata;
3155         Scene *scene= oglrender->scene;
3156         ImBuf *ibuf;
3157         void *lock;
3158         char name[FILE_MAXDIR+FILE_MAXFILE];
3159         unsigned int lay;
3160         int ok= 0;
3161
3162         switch(event->type) {
3163                 case ESCKEY:
3164                         /* cancel */
3165                         screen_opengl_render_end(C, op->customdata);
3166                         return OPERATOR_FINISHED;
3167                 case TIMER:
3168                         /* render frame? */
3169                         if(oglrender->timer == event->customdata)
3170                                 break;
3171                 default:
3172                         /* nothing to do */
3173                         return OPERATOR_RUNNING_MODAL;
3174         }
3175
3176         /* go to next frame */
3177         while(CFRA<oglrender->nfra) {
3178                 if(scene->lay & 0xFF000000)
3179                         lay= scene->lay & 0xFF000000;
3180                 else
3181                         lay= scene->lay;
3182
3183                 scene_update_for_newframe(scene, lay);
3184                 CFRA++;
3185         }
3186
3187         scene_update_for_newframe(scene, scene->lay);
3188
3189         /* render into offscreen buffer */
3190         screen_opengl_render_apply(oglrender);
3191         
3192         /* save to disk */
3193         ibuf= BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
3194
3195         if(ibuf) {
3196                 if(BKE_imtype_is_movie(scene->r.imtype)) {
3197                         oglrender->mh->append_movie(&scene->r, CFRA, (int*)ibuf->rect, oglrender->sizex, oglrender->sizey);
3198                         printf("Append frame %d", scene->r.cfra);
3199                         ok= 1;
3200                 }
3201                 else {
3202                         BKE_makepicstring(scene, name, scene->r.pic, scene->r.cfra, scene->r.imtype);
3203                         ok= BKE_write_ibuf(scene, ibuf, name, scene->r.imtype, scene->r.subimtype, scene->r.quality);
3204                         
3205                         if(ok==0) printf("write error: cannot save %s\n", name);
3206                         else printf("saved: %s", name);
3207                 }
3208         }
3209
3210         BKE_image_release_ibuf(oglrender->ima, lock);
3211
3212         /* movie stats prints have no line break */
3213         printf("\n");
3214         
3215         /* go to next frame */
3216         oglrender->nfra += scene->r.frame_step;
3217         scene->r.cfra++;
3218
3219         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, oglrender->scene);
3220
3221         /* stop at the end or on error */
3222         if(scene->r.cfra > EFRA || !ok) {
3223                 screen_opengl_render_end(C, op->customdata);
3224                 return OPERATOR_FINISHED;
3225         }
3226
3227         return OPERATOR_RUNNING_MODAL;
3228 }
3229
3230 static int screen_opengl_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3231 {
3232         int anim= RNA_boolean_get(op->ptr, "animation");
3233
3234         if(!screen_opengl_render_init(C, op))
3235                 return OPERATOR_CANCELLED;
3236         
3237         if(!anim) {
3238                 /* render image */
3239                 screen_opengl_render_apply(op->customdata);
3240                 screen_opengl_render_end(C, op->customdata);
3241                 screen_set_image_output(C, event->x, event->y);
3242
3243                 return OPERATOR_FINISHED;
3244         }
3245         else {
3246                 /* initialize animation */
3247                 OGLRender *oglrender;
3248                 Scene *scene;
3249
3250                 oglrender= op->customdata;
3251                 scene= oglrender->scene;
3252
3253                 oglrender->mh= BKE_get_movie_handle(scene->r.imtype);
3254                 if(BKE_imtype_is_movie(scene->r.imtype))
3255                         oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey);
3256
3257                 oglrender->cfrao= scene->r.cfra;
3258                 oglrender->nfra= SFRA;
3259                 scene->r.cfra= SFRA;
3260
3261                 WM_event_add_modal_handler(C, op);
3262                 oglrender->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
3263
3264                 screen_set_image_output(C, event->x, event->y);
3265
3266                 return OPERATOR_RUNNING_MODAL;
3267         }
3268 }
3269
3270 static void SCREEN_OT_opengl_render(wmOperatorType *ot)
3271 {
3272         /* identifiers */
3273         ot->name= "OpenGL Render";
3274         ot->description= "OpenGL render active viewport.";
3275         ot->idname= "SCREEN_OT_opengl_render";
3276         
3277         /* api callbacks */
3278         ot->invoke= screen_opengl_render_invoke;
3279         ot->modal= screen_opengl_render_modal;
3280         ot->cancel= screen_opengl_render_cancel;
3281         
3282         ot->poll= ED_operator_screenactive;
3283         
3284         RNA_def_boolean(ot->srna, "animation", 0, "Animation", "");
3285 }
3286
3287 /* *********************** cancel render viewer *************** */
3288
3289 static int render_view_cancel_exec(bContext *C, wmOperator *unused)
3290 {
3291         wmWindow *win= CTX_wm_window(C);
3292         ScrArea *sa= CTX_wm_area(C);
3293         SpaceImage *sima= sa->spacedata.first;
3294         
3295         /* test if we have a temp screen in front */
3296         if(CTX_wm_window(C)->screen->full==SCREENTEMP) {
3297                 wm_window_lower(CTX_wm_window(C));
3298                 return OPERATOR_FINISHED;
3299         }
3300         /* determine if render already shows */
3301         else if(sima->flag & SI_PREVSPACE) {
3302                 sima->flag &= ~SI_PREVSPACE;
3303                 
3304                 if(sima->flag & SI_FULLWINDOW) {
3305                         sima->flag &= ~SI_FULLWINDOW;
3306                         ED_screen_full_prevspace(C);
3307                 }
3308                 else
3309                         ED_area_prevspace(C);
3310
3311                 return OPERATOR_FINISHED;