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