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