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