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