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