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