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