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