2.5: Most curve/surface editmode operators back:
[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 "MEM_guardedalloc.h"
28
29 #include "BLI_arithb.h"
30 #include "BLI_blenlib.h"
31 #include "BLI_editVert.h"
32
33 #include "DNA_armature_types.h"
34 #include "DNA_image_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_mesh_types.h"
37 #include "DNA_curve_types.h"
38 #include "DNA_scene_types.h"
39
40 #include "BKE_blender.h"
41 #include "BKE_context.h"
42 #include "BKE_customdata.h"
43 #include "BKE_global.h"
44 #include "BKE_image.h"
45 #include "BKE_idprop.h"
46 #include "BKE_library.h"
47 #include "BKE_main.h"
48 #include "BKE_multires.h"
49 #include "BKE_report.h"
50 #include "BKE_screen.h"
51 #include "BKE_utildefines.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "ED_util.h"
57 #include "ED_screen.h"
58 #include "ED_screen_types.h"
59
60 #include "RE_pipeline.h"
61 #include "IMB_imbuf.h"
62 #include "IMB_imbuf_types.h"
63
64 #include "RNA_access.h"
65 #include "RNA_define.h"
66
67 #include "UI_interface.h"
68 #include "UI_resources.h"
69
70 #include "screen_intern.h"      /* own module include */
71
72 /* ************** Exported Poll tests ********************** */
73
74 int ED_operator_areaactive(bContext *C)
75 {
76         if(CTX_wm_window(C)==NULL) return 0;
77         if(CTX_wm_screen(C)==NULL) return 0;
78         if(CTX_wm_area(C)==NULL) return 0;
79         return 1;
80 }
81
82 int ED_operator_screenactive(bContext *C)
83 {
84         if(CTX_wm_window(C)==NULL) return 0;
85         if(CTX_wm_screen(C)==NULL) return 0;
86         return 1;
87 }
88
89 /* when mouse is over area-edge */
90 int ED_operator_screen_mainwinactive(bContext *C)
91 {
92         if(CTX_wm_window(C)==NULL) return 0;
93         if(CTX_wm_screen(C)==NULL) return 0;
94         if (CTX_wm_screen(C)->subwinactive!=CTX_wm_screen(C)->mainwin) return 0;
95         return 1;
96 }
97
98 int ED_operator_scene_editable(bContext *C)
99 {
100         Scene *scene= CTX_data_scene(C);
101         if(scene && scene->id.lib==NULL)
102                 return 1;
103         return 0;
104 }
105
106 static int ed_spacetype_test(bContext *C, int type)
107 {
108         if(ED_operator_areaactive(C)) {
109                 SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
110                 return sl && (sl->spacetype == type);
111         }
112         return 0;
113 }
114
115 int ED_operator_view3d_active(bContext *C)
116 {
117         return ed_spacetype_test(C, SPACE_VIEW3D);
118 }
119
120 int ED_operator_timeline_active(bContext *C)
121 {
122         return ed_spacetype_test(C, SPACE_TIME);
123 }
124
125 int ED_operator_outliner_active(bContext *C)
126 {
127         if(ed_spacetype_test(C, SPACE_OOPS)) {
128                 SpaceOops *so= (SpaceOops *)CTX_wm_space_data(C);
129                 return (so->type == SO_OUTLINER);
130         }
131         return 0;
132 }
133
134 int ED_operator_file_active(bContext *C)
135 {
136         return ed_spacetype_test(C, SPACE_FILE);
137 }
138
139 int ED_operator_action_active(bContext *C)
140 {
141         return ed_spacetype_test(C, SPACE_ACTION);
142 }
143
144 int ED_operator_buttons_active(bContext *C)
145 {
146         return ed_spacetype_test(C, SPACE_BUTS);
147 }
148
149 int ED_operator_node_active(bContext *C)
150 {
151         if(ed_spacetype_test(C, SPACE_NODE)) {
152                 SpaceNode *snode= (SpaceNode *)CTX_wm_space_data(C);
153                 if(snode->edittree)
154                         return 1;
155         }
156         return 0;
157 }
158
159 int ED_operator_ipo_active(bContext *C)
160 {
161         return ed_spacetype_test(C, SPACE_IPO);
162 }
163
164 int ED_operator_sequencer_active(bContext *C)
165 {
166         return ed_spacetype_test(C, SPACE_SEQ);
167 }
168
169 int ED_operator_image_active(bContext *C)
170 {
171         return ed_spacetype_test(C, SPACE_IMAGE);
172 }
173
174 int ED_operator_object_active(bContext *C)
175 {
176         return NULL != CTX_data_active_object(C);
177 }
178
179 int ED_operator_editmesh(bContext *C)
180 {
181         Object *obedit= CTX_data_edit_object(C);
182         if(obedit && obedit->type==OB_MESH)
183                 return NULL != ((Mesh *)obedit->data)->edit_mesh;
184         return 0;
185 }
186
187 int ED_operator_editarmature(bContext *C)
188 {
189         Object *obedit= CTX_data_edit_object(C);
190         if(obedit && obedit->type==OB_ARMATURE)
191                 return NULL != ((bArmature *)obedit->data)->edbo;
192         return 0;
193 }
194
195 int ED_operator_posemode(bContext *C)
196 {
197         Object *obact= CTX_data_active_object(C);
198         Object *obedit= CTX_data_edit_object(C);
199         
200         if ((obact != obedit) && (obact) && (obact->type==OB_ARMATURE))
201                 return (obact->flag & OB_POSEMODE)!=0;
202                 
203         return 0;
204 }
205
206
207 int ED_operator_uvedit(bContext *C)
208 {
209         Object *obedit= CTX_data_edit_object(C);
210         EditMesh *em= NULL;
211
212         if(obedit && obedit->type==OB_MESH)
213                 em= ((Mesh *)obedit->data)->edit_mesh;
214
215     if(em && (em->faces.first) && (CustomData_has_layer(&em->fdata, CD_MTFACE)))
216                 return 1;
217
218         return 0;
219 }
220
221 int ED_operator_uvmap(bContext *C)
222 {
223         Object *obedit= CTX_data_edit_object(C);
224         EditMesh *em= NULL;
225
226         if(obedit && obedit->type==OB_MESH)
227                 em= ((Mesh *)obedit->data)->edit_mesh;
228
229     if(em && (em->faces.first))
230                 return 1;
231
232         return 0;
233 }
234
235 int ED_operator_editsurfcurve(bContext *C)
236 {
237         Object *obedit= CTX_data_edit_object(C);
238         if(obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
239                 return NULL != ((Curve *)obedit->data)->editnurb;
240         return 0;
241
242         // XXX this test was in many tools, still needed?
243         // if(v3d==0 || (v3d->lay & obedit->lay)==0 ) return 0;
244 }
245
246
247 int ED_operator_editcurve(bContext *C)
248 {
249         Object *obedit= CTX_data_edit_object(C);
250         if(obedit && obedit->type==OB_CURVE)
251                 return NULL != ((Curve *)obedit->data)->editnurb;
252         return 0;
253 }
254
255 int ED_operator_editsurf(bContext *C)
256 {
257         Object *obedit= CTX_data_edit_object(C);
258         if(obedit && obedit->type==OB_SURF)
259                 return NULL != ((Curve *)obedit->data)->editnurb;
260         return 0;
261 }
262
263 /* *************************** action zone operator ************************** */
264
265 /* operator state vars used:  
266         none
267
268 functions:
269
270         apply() set actionzone event
271
272         exit()  free customdata
273         
274 callbacks:
275
276         exec()  never used
277
278         invoke() check if in zone  
279                 add customdata, put mouseco and area in it
280                 add modal handler
281
282         modal() accept modal events while doing it
283                 call apply() with gesture info, active window, nonactive window
284                 call exit() and remove handler when LMB confirm
285
286 */
287
288 typedef struct sActionzoneData {
289         ScrArea *sa1, *sa2;
290         AZone *az;
291         int x, y, gesture_dir;
292 } sActionzoneData;
293
294 /* used by other operators too */
295 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
296 {
297         ScrArea *sa= NULL;
298         sa= scr->areabase.first;
299         while(sa) {
300                 if(BLI_in_rcti(&sa->totrct, x, y)) break;
301                 sa= sa->next;
302         }
303         
304         return sa;
305 }
306
307
308 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
309 {
310         AZone *az= NULL;
311         int i= 0;
312         
313         for(az= sa->actionzones.first, i= 0; az; az= az->next, i++) {
314                 if(az->type == AZONE_TRI) {
315                         if(IsPointInTri2DInts(az->x1, az->y1, az->x2, az->y2, x, y)) 
316                                 break;
317                 }
318                 if(az->type == AZONE_QUAD) {
319                         if(az->x1 < x && x < az->x2 && az->y1 < y && y < az->y2) 
320                                 break;
321                 }
322         }
323         
324         return az;
325 }
326
327 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
328 {
329         AZone *az= is_in_area_actionzone(CTX_wm_area(C), event->x, event->y);
330         sActionzoneData *sad;
331         
332         /* quick escape */
333         if(az==NULL)
334                 return OPERATOR_PASS_THROUGH;
335         
336         /* ok we do the actionzone */
337         sad= op->customdata= MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
338         sad->sa1= CTX_wm_area(C);
339         sad->az= az;
340         sad->x= event->x; sad->y= event->y;
341         
342         /* add modal handler */
343         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
344         
345         return OPERATOR_RUNNING_MODAL;
346 }
347
348 static void actionzone_exit(bContext *C, wmOperator *op)
349 {
350         if(op->customdata)
351                 MEM_freeN(op->customdata);
352         op->customdata= NULL;
353 }
354
355 /* send EVT_ACTIONZONE event */
356 static void actionzone_apply(bContext *C, wmOperator *op)
357 {
358         wmEvent event;
359         wmWindow *win= CTX_wm_window(C);
360         
361         event= *(win->eventstate);      /* XXX huh huh? make api call */
362         event.type= EVT_ACTIONZONE;
363         event.customdata= op->customdata;
364         event.customdatafree= TRUE;
365         op->customdata= NULL;
366         
367         wm_event_add(win, &event);
368 }
369
370 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
371 {
372         sActionzoneData *sad= op->customdata;
373         int deltax, deltay;
374         
375         switch(event->type) {
376                 case MOUSEMOVE:
377                         /* calculate gesture direction */
378                         deltax= (event->x - sad->x);
379                         deltay= (event->y - sad->y);
380                         
381                         if(deltay > ABS(deltax))
382                                 sad->gesture_dir= AZONE_N;
383                         else if(deltax > ABS(deltay))
384                                 sad->gesture_dir= AZONE_E;
385                         else if(deltay < -ABS(deltax))
386                                 sad->gesture_dir= AZONE_S;
387                         else
388                                 sad->gesture_dir= AZONE_W;
389                         
390                         /* gesture is large enough? */
391                         if(ABS(deltax) > 12 || ABS(deltay) > 12) {
392                                 
393                                 /* second area, for join */
394                                 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
395                                 /* apply sends event */
396                                 actionzone_apply(C, op);
397                                 actionzone_exit(C, op);
398                                 
399                                 return OPERATOR_FINISHED;
400                         }
401                                 break;
402                 case ESCKEY:
403                 case LEFTMOUSE:
404                         actionzone_exit(C, op);
405                         return OPERATOR_CANCELLED;
406         }
407         
408         return OPERATOR_RUNNING_MODAL;
409 }
410
411 void SCREEN_OT_actionzone(wmOperatorType *ot)
412 {
413         /* identifiers */
414         ot->name= "Handle area action zones";
415         ot->idname= "SCREEN_OT_actionzone";
416         
417         ot->invoke= actionzone_invoke;
418         ot->modal= actionzone_modal;
419         
420         ot->poll= ED_operator_areaactive;
421 }
422
423
424 /* *********** Rip area operator ****************** */
425
426
427 /* operator callback */
428 /* (ton) removed attempt to merge ripped area with another, don't think this is desired functionality.
429 conventions: 'atomic' and 'dont think for user' :) */
430 static int screen_area_rip_op(bContext *C, wmOperator *op)
431 {
432         wmWindow *newwin, *win;
433         bScreen *newsc, *sc;
434         ScrArea *sa;
435         rcti rect;
436         
437         win= CTX_wm_window(C);
438         sc= CTX_wm_screen(C);
439         sa= CTX_wm_area(C);
440
441         /*  poll() checks area context, but we don't accept full-area windows */
442         if(sc->full != SCREENNORMAL) 
443                 return OPERATOR_CANCELLED;
444         
445         /* adds window to WM */
446         rect= sa->totrct;
447         BLI_translate_rcti(&rect, win->posx, win->posy);
448         newwin= WM_window_open(C, &rect);
449         
450         /* allocs new screen and adds to newly created window, using window size */
451         newsc= screen_add(newwin, CTX_data_scene(C), sc->id.name+2);
452         newwin->screen= newsc;
453         
454         /* copy area to new screen */
455         area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
456         
457         /* screen, areas init */
458         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
459         
460         return OPERATOR_FINISHED;
461 }
462
463 void SCREEN_OT_area_rip(wmOperatorType *ot)
464 {
465         ot->name= "Rip Area into New Window";
466         ot->idname= "SCREEN_OT_area_rip";
467         
468         ot->invoke= WM_operator_confirm;
469         ot->exec= screen_area_rip_op;
470         ot->poll= ED_operator_areaactive;
471 }
472
473
474 /* ************** move area edge operator *********************************** */
475
476 /* operator state vars used:  
477            x, y                         mouse coord near edge
478            delta            movement of edge
479
480         functions:
481
482         init()   set default property values, find edge based on mouse coords, test
483             if the edge can be moved, select edges, calculate min and max movement
484
485         apply() apply delta on selection
486
487         exit()  cleanup, send notifier
488
489         cancel() cancel moving
490
491         callbacks:
492
493         exec()   execute without any user interaction, based on properties
494             call init(), apply(), exit()
495
496         invoke() gets called on mouse click near edge
497             call init(), add handler
498
499         modal()  accept modal events while doing it
500                         call apply() with delta motion
501             call exit() and remove handler
502
503 */
504
505 typedef struct sAreaMoveData {
506         int bigger, smaller, origval;
507         char dir;
508 } sAreaMoveData;
509
510 /* helper call to move area-edge, sets limits */
511 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
512 {
513         ScrArea *sa;
514         
515         /* we check all areas and test for free space with MINSIZE */
516         *bigger= *smaller= 100000;
517         
518         for(sa= sc->areabase.first; sa; sa= sa->next) {
519                 if(dir=='h') {
520                         int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
521                         
522                         /* if top or down edge selected, test height */
523                         if(sa->v1->flag && sa->v4->flag)
524                                 *bigger= MIN2(*bigger, y1);
525                         else if(sa->v2->flag && sa->v3->flag)
526                                 *smaller= MIN2(*smaller, y1);
527                 }
528                 else {
529                         int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
530                         
531                         /* if left or right edge selected, test width */
532                         if(sa->v1->flag && sa->v2->flag)
533                                 *bigger= MIN2(*bigger, x1);
534                         else if(sa->v3->flag && sa->v4->flag)
535                                 *smaller= MIN2(*smaller, x1);
536                 }
537         }
538 }
539
540 /* validate selection inside screen, set variables OK */
541 /* return 0: init failed */
542 static int area_move_init (bContext *C, wmOperator *op)
543 {
544         bScreen *sc= CTX_wm_screen(C);
545         ScrEdge *actedge;
546         sAreaMoveData *md;
547         int x, y;
548
549         /* required properties */
550         x= RNA_int_get(op->ptr, "x");
551         y= RNA_int_get(op->ptr, "y");
552
553         /* setup */
554         actedge= screen_find_active_scredge(sc, x, y);
555         if(actedge==NULL) return 0;
556
557         md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
558         op->customdata= md;
559
560         md->dir= scredge_is_horizontal(actedge)?'h':'v';
561         if(md->dir=='h') md->origval= actedge->v1->vec.y;
562         else md->origval= actedge->v1->vec.x;
563         
564         select_connected_scredge(sc, actedge);
565         /* now all vertices with 'flag==1' are the ones that can be moved. */
566
567         area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
568         
569         return 1;
570 }
571
572 /* moves selected screen edge amount of delta, used by split & move */
573 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
574 {
575         wmWindow *win= CTX_wm_window(C);
576         bScreen *sc= CTX_wm_screen(C);
577         ScrVert *v1;
578         
579         delta= CLAMPIS(delta, -smaller, bigger);
580         
581         for (v1= sc->vertbase.first; v1; v1= v1->next) {
582                 if (v1->flag) {
583                         /* that way a nice AREAGRID  */
584                         if((dir=='v') && v1->vec.x>0 && v1->vec.x<win->sizex-1) {
585                                 v1->vec.x= origval + delta;
586                                 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
587                         }
588                         if((dir=='h') && v1->vec.y>0 && v1->vec.y<win->sizey-1) {
589                                 v1->vec.y= origval + delta;
590
591                                 v1->vec.y+= AREAGRID-1;
592                                 v1->vec.y-= (v1->vec.y % AREAGRID);
593                                 
594                                 /* prevent too small top header */
595                                 if(v1->vec.y > win->sizey-AREAMINY)
596                                         v1->vec.y= win->sizey-AREAMINY;
597                         }
598                 }
599         }
600
601         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
602 }
603
604 static void area_move_apply(bContext *C, wmOperator *op)
605 {
606         sAreaMoveData *md= op->customdata;
607         int delta;
608         
609         delta= RNA_int_get(op->ptr, "delta");
610         area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
611 }
612
613 static void area_move_exit(bContext *C, wmOperator *op)
614 {
615         if(op->customdata)
616                 MEM_freeN(op->customdata);
617         op->customdata= NULL;
618         
619         /* this makes sure aligned edges will result in aligned grabbing */
620         removedouble_scrverts(CTX_wm_screen(C));
621         removedouble_scredges(CTX_wm_screen(C));
622 }
623
624 static int area_move_exec(bContext *C, wmOperator *op)
625 {
626         if(!area_move_init(C, op))
627                 return OPERATOR_CANCELLED;
628         
629         area_move_apply(C, op);
630         area_move_exit(C, op);
631         
632         return OPERATOR_FINISHED;
633 }
634
635 /* interaction callback */
636 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
637 {
638         RNA_int_set(op->ptr, "x", event->x);
639         RNA_int_set(op->ptr, "y", event->y);
640
641         if(!area_move_init(C, op)) 
642                 return OPERATOR_PASS_THROUGH;
643         
644         /* add temp handler */
645         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
646         
647         return OPERATOR_RUNNING_MODAL;
648 }
649
650 static int area_move_cancel(bContext *C, wmOperator *op)
651 {
652
653         RNA_int_set(op->ptr, "delta", 0);
654         area_move_apply(C, op);
655         area_move_exit(C, op);
656
657         return OPERATOR_CANCELLED;
658 }
659
660 /* modal callback for while moving edges */
661 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
662 {
663         sAreaMoveData *md;
664         int delta, x, y;
665
666         md= op->customdata;
667
668         x= RNA_int_get(op->ptr, "x");
669         y= RNA_int_get(op->ptr, "y");
670
671         /* execute the events */
672         switch(event->type) {
673                 case MOUSEMOVE:
674                         delta= (md->dir == 'v')? event->x - x: event->y - y;
675                         RNA_int_set(op->ptr, "delta", delta);
676
677                         area_move_apply(C, op);
678                         break;
679                         
680                 case LEFTMOUSE:
681                         if(event->val==0) {
682                                 area_move_exit(C, op);
683                                 return OPERATOR_FINISHED;
684                         }
685                         break;
686                         
687                 case ESCKEY:
688                         return area_move_cancel(C, op);
689         }
690         
691         return OPERATOR_RUNNING_MODAL;
692 }
693
694 void SCREEN_OT_area_move(wmOperatorType *ot)
695 {
696         /* identifiers */
697         ot->name= "Move area edges";
698         ot->idname= "SCREEN_OT_area_move";
699
700         ot->exec= area_move_exec;
701         ot->invoke= area_move_invoke;
702         ot->cancel= area_move_cancel;
703         ot->modal= area_move_modal;
704
705         ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
706
707         /* rna */
708         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
709         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
710         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
711 }
712
713 /* ************** split area operator *********************************** */
714
715 /* 
716 operator state vars:  
717         fac              spit point
718         dir              direction 'v' or 'h'
719
720 operator customdata:
721         area                    pointer to (active) area
722         x, y                    last used mouse pos
723         (more, see below)
724
725 functions:
726
727         init()   set default property values, find area based on context
728
729         apply() split area based on state vars
730
731         exit()  cleanup, send notifier
732
733         cancel() remove duplicated area
734
735 callbacks:
736
737         exec()   execute without any user interaction, based on state vars
738             call init(), apply(), exit()
739
740         invoke() gets called on mouse click in action-widget
741             call init(), add modal handler
742                         call apply() with initial motion
743
744         modal()  accept modal events while doing it
745             call move-areas code with delta motion
746             call exit() or cancel() and remove handler
747
748 */
749
750 #define SPLIT_STARTED   1
751 #define SPLIT_PROGRESS  2
752
753 typedef struct sAreaSplitData
754 {
755         int x, y;       /* last used mouse position */
756         
757         int origval;                    /* for move areas */
758         int bigger, smaller;    /* constraints for moving new edge */
759         int delta;                              /* delta move edge */
760         int origmin, origsize;  /* to calculate fac, for property storage */
761
762         ScrEdge *nedge;                 /* new edge */
763         ScrArea *sarea;                 /* start area */
764         ScrArea *narea;                 /* new area */
765 } sAreaSplitData;
766
767 /* generic init, no UI stuff here */
768 static int area_split_init(bContext *C, wmOperator *op)
769 {
770         ScrArea *sa= CTX_wm_area(C);
771         sAreaSplitData *sd;
772         int dir;
773         
774         /* required context */
775         if(sa==NULL) return 0;
776         
777         /* required properties */
778         dir= RNA_enum_get(op->ptr, "direction");
779         
780         /* minimal size */
781         if(dir=='v' && sa->winx < 2*AREAMINX) return 0;
782         if(dir=='h' && sa->winy < 2*AREAMINY) return 0;
783            
784         /* custom data */
785         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
786         op->customdata= sd;
787         
788         sd->sarea= sa;
789         sd->origsize= dir=='v' ? sa->winx:sa->winy;
790         sd->origmin = dir=='v' ? sa->totrct.xmin:sa->totrct.ymin;
791         
792         return 1;
793 }
794
795 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
796 /* used with split operator */
797 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
798 {
799         ScrVert *sav1= sa->v1;
800         ScrVert *sav2= sa->v2;
801         ScrVert *sav3= sa->v3;
802         ScrVert *sav4= sa->v4;
803         ScrVert *sbv1= sb->v1;
804         ScrVert *sbv2= sb->v2;
805         ScrVert *sbv3= sb->v3;
806         ScrVert *sbv4= sb->v4;
807         
808         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
809                 return screen_findedge(screen, sav1, sav2);
810         }
811         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
812                 return screen_findedge(screen, sav2, sav3);
813         }
814         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
815                 return screen_findedge(screen, sav3, sav4);
816         }
817         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
818                 return screen_findedge(screen, sav1, sav4);
819         }
820
821         return NULL;
822 }
823
824
825 /* do the split, return success */
826 static int area_split_apply(bContext *C, wmOperator *op)
827 {
828         bScreen *sc= CTX_wm_screen(C);
829         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
830         float fac;
831         int dir;
832         
833         fac= RNA_float_get(op->ptr, "factor");
834         dir= RNA_enum_get(op->ptr, "direction");
835
836         sd->narea= area_split(CTX_wm_window(C), sc, sd->sarea, dir, fac);
837         
838         if(sd->narea) {
839                 ScrVert *sv;
840                 
841                 sd->nedge= area_findsharededge(sc, sd->sarea, sd->narea);
842         
843                 /* select newly created edge, prepare for moving edge */
844                 for(sv= sc->vertbase.first; sv; sv= sv->next)
845                         sv->flag = 0;
846                 
847                 sd->nedge->v1->flag= 1;
848                 sd->nedge->v2->flag= 1;
849
850                 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
851                 else sd->origval= sd->nedge->v1->vec.x;
852
853                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
854                 
855                 return 1;
856         }               
857         
858         return 0;
859 }
860
861 static void area_split_exit(bContext *C, wmOperator *op)
862 {
863         if (op->customdata) {
864                 MEM_freeN(op->customdata);
865                 op->customdata = NULL;
866         }
867         
868         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
869
870         /* this makes sure aligned edges will result in aligned grabbing */
871         removedouble_scrverts(CTX_wm_screen(C));
872         removedouble_scredges(CTX_wm_screen(C));
873 }
874
875
876 /* UI callback, adds new handler */
877 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
878 {
879         sAreaSplitData *sd;
880         
881         if(event->type==EVT_ACTIONZONE) {
882                 sActionzoneData *sad= event->customdata;
883                 int dir;
884                 
885                 /* verify *sad itself */
886                 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
887                         return OPERATOR_PASS_THROUGH;
888                 
889                 /* is this our *sad? if areas not equal it should be passed on */
890                 if(CTX_wm_area(C)!=sad->sa1 || sad->sa1!=sad->sa2)
891                         return OPERATOR_PASS_THROUGH;
892                 
893                 /* prepare operator state vars */
894                 if(sad->gesture_dir==AZONE_N || sad->gesture_dir==AZONE_S) {
895                         dir= 'h';
896                         RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
897                 }
898                 else {
899                         dir= 'v';
900                         RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
901                 }
902                 RNA_enum_set(op->ptr, "direction", dir);
903
904                 /* general init, also non-UI case, adds customdata, sets area and defaults */
905                 if(!area_split_init(C, op))
906                         return OPERATOR_PASS_THROUGH;
907                 
908                 sd= (sAreaSplitData *)op->customdata;
909                 
910                 sd->x= event->x;
911                 sd->y= event->y;
912                 
913                 /* do the split */
914                 if(area_split_apply(C, op)) {
915                         area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
916                         
917                         /* add temp handler for edge move or cancel */
918                         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
919                         
920                         return OPERATOR_RUNNING_MODAL;
921                 }
922                 
923         }
924         else {
925                 /* nonmodal for now */
926                 return op->type->exec(C, op);
927         }
928         
929         return OPERATOR_PASS_THROUGH;
930 }
931
932 /* function to be called outside UI context, or for redo */
933 static int area_split_exec(bContext *C, wmOperator *op)
934 {
935         
936         if(!area_split_init(C, op))
937                 return OPERATOR_CANCELLED;
938         
939         area_split_apply(C, op);
940         area_split_exit(C, op);
941         
942         return OPERATOR_FINISHED;
943 }
944
945
946 static int area_split_cancel(bContext *C, wmOperator *op)
947 {
948         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
949
950         if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
951                 if (CTX_wm_area(C) == sd->narea) {
952                         CTX_wm_area_set(C, NULL);
953                         CTX_wm_region_set(C, NULL);
954                 }
955                 sd->narea = NULL;
956         }
957         area_split_exit(C, op);
958
959         return OPERATOR_CANCELLED;
960 }
961
962 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
963 {
964         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
965         float fac;
966         int dir;
967
968         /* execute the events */
969         switch(event->type) {
970                 case MOUSEMOVE:
971                         dir= RNA_enum_get(op->ptr, "direction");
972                         
973                         sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
974                         area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
975                         
976                         fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
977                         RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
978                         
979                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
980                         break;
981                         
982                 case LEFTMOUSE:
983                         if(event->val==0) { /* mouse up */
984                                 area_split_exit(C, op);
985                                 return OPERATOR_FINISHED;
986                         }
987                         break;
988                 case RIGHTMOUSE: /* cancel operation */
989                 case ESCKEY:
990                         return area_split_cancel(C, op);
991         }
992         
993         return OPERATOR_RUNNING_MODAL;
994 }
995
996 static EnumPropertyItem prop_direction_items[] = {
997         {'h', "HORIZONTAL", "Horizontal", ""},
998         {'v', "VERTICAL", "Vertical", ""},
999         {0, NULL, NULL, NULL}};
1000
1001 void SCREEN_OT_area_split(wmOperatorType *ot)
1002 {
1003         ot->name = "Split area";
1004         ot->idname = "SCREEN_OT_area_split";
1005         
1006         ot->exec= area_split_exec;
1007         ot->invoke= area_split_invoke;
1008         ot->modal= area_split_modal;
1009         
1010         ot->poll= ED_operator_areaactive;
1011         ot->flag= OPTYPE_REGISTER;
1012         
1013         /* rna */
1014         RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1015         RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1016 }
1017
1018 /* ************** frame change operator ***************************** */
1019
1020
1021 /* function to be called outside UI context, or for redo */
1022 static int frame_offset_exec(bContext *C, wmOperator *op)
1023 {
1024         int delta;
1025
1026         delta = RNA_int_get(op->ptr, "delta");
1027
1028         CTX_data_scene(C)->r.cfra += delta;
1029
1030         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1031
1032         return OPERATOR_FINISHED;
1033 }
1034
1035 void SCREEN_OT_frame_offset(wmOperatorType *ot)
1036 {
1037         ot->name = "Frame Offset";
1038         ot->idname = "SCREEN_OT_frame_offset";
1039
1040         ot->exec= frame_offset_exec;
1041
1042         ot->poll= ED_operator_screenactive;
1043         ot->flag= OPTYPE_REGISTER;
1044
1045         /* rna */
1046         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1047 }
1048
1049 /* ************** switch screen operator ***************************** */
1050
1051
1052 /* function to be called outside UI context, or for redo */
1053 static int screen_set_exec(bContext *C, wmOperator *op)
1054 {
1055         bScreen *screen= CTX_wm_screen(C);
1056         ScrArea *sa= CTX_wm_area(C);
1057         int tot= BLI_countlist(&CTX_data_main(C)->screen);
1058         int delta= RNA_int_get(op->ptr, "delta");
1059         
1060         /* this screen is 'fake', solve later XXX */
1061         if(sa && sa->full)
1062                 return OPERATOR_CANCELLED;
1063         
1064         if(delta==1) {
1065                 while(tot--) {
1066                         screen= screen->id.next;
1067                         if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1068                         if(screen->winid==0 && screen->full==0)
1069                                 break;
1070                 }
1071         }
1072         else if(delta== -1) {
1073                 while(tot--) {
1074                         screen= screen->id.prev;
1075                         if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1076                         if(screen->winid==0 && screen->full==0)
1077                                 break;
1078                 }
1079         }
1080         else {
1081                 screen= NULL;
1082         }
1083         
1084         if(screen) {
1085                 ED_screen_set(C, screen);
1086                 return OPERATOR_FINISHED;
1087         }
1088         return OPERATOR_CANCELLED;
1089 }
1090
1091 void SCREEN_OT_screen_set(wmOperatorType *ot)
1092 {
1093         ot->name = "Set Screen";
1094         ot->idname = "SCREEN_OT_screen_set";
1095         
1096         ot->exec= screen_set_exec;
1097         ot->poll= ED_operator_screenactive;
1098         
1099         /* rna */
1100         RNA_def_pointer_runtime(ot->srna, "screen", &RNA_Screen, "Screen", "");
1101         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1102 }
1103
1104 /* ************** screen full-area operator ***************************** */
1105
1106
1107 /* function to be called outside UI context, or for redo */
1108 static int screen_full_area_exec(bContext *C, wmOperator *op)
1109 {
1110         ed_screen_fullarea(C, CTX_wm_area(C));
1111         return OPERATOR_FINISHED;
1112 }
1113
1114 void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1115 {
1116         ot->name = "Toggle Make Area Fullscreen";
1117         ot->idname = "SCREEN_OT_screen_full_area";
1118         
1119         ot->exec= screen_full_area_exec;
1120         ot->poll= ED_operator_areaactive;
1121         ot->flag= OPTYPE_REGISTER;
1122
1123 }
1124
1125
1126
1127 /* ************** join area operator ********************************************** */
1128
1129 /* operator state vars used:  
1130                         x1, y1     mouse coord in first area, which will disappear
1131                         x2, y2     mouse coord in 2nd area, which will become joined
1132
1133 functions:
1134
1135    init()   find edge based on state vars 
1136                         test if the edge divides two areas, 
1137                         store active and nonactive area,
1138             
1139    apply()  do the actual join
1140
1141    exit()       cleanup, send notifier
1142
1143 callbacks:
1144
1145    exec()       calls init, apply, exit 
1146    
1147    invoke() sets mouse coords in x,y
1148             call init()
1149             add modal handler
1150
1151    modal()      accept modal events while doing it
1152                         call apply() with active window and nonactive window
1153             call exit() and remove handler when LMB confirm
1154
1155 */
1156
1157 typedef struct sAreaJoinData
1158 {
1159         ScrArea *sa1;   /* first area to be considered */
1160         ScrArea *sa2;   /* second area to be considered */
1161         ScrArea *scr;   /* designed for removal */
1162
1163 } sAreaJoinData;
1164
1165
1166 /* validate selection inside screen, set variables OK */
1167 /* return 0: init failed */
1168 /* XXX todo: find edge based on (x,y) and set other area? */
1169 static int area_join_init(bContext *C, wmOperator *op)
1170 {
1171         ScrArea *sa1, *sa2;
1172         sAreaJoinData* jd= NULL;
1173         int x1, y1;
1174         int x2, y2;
1175
1176         /* required properties, make negative to get return 0 if not set by caller */
1177         x1= RNA_int_get(op->ptr, "x1");
1178         y1= RNA_int_get(op->ptr, "y1");
1179         x2= RNA_int_get(op->ptr, "x2");
1180         y2= RNA_int_get(op->ptr, "y2");
1181         
1182         sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1183         sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1184         if(sa1==NULL || sa2==NULL || sa1==sa2)
1185                 return 0;
1186
1187         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1188                 
1189         jd->sa1 = sa1;
1190         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1191         jd->sa2 = sa2;
1192         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1193         
1194         op->customdata= jd;
1195         
1196         return 1;
1197 }
1198
1199 /* apply the join of the areas (space types) */
1200 static int area_join_apply(bContext *C, wmOperator *op)
1201 {
1202         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1203         if (!jd) return 0;
1204
1205         if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1206                 return 0;
1207         }
1208         if (CTX_wm_area(C) == jd->sa2) {
1209                 CTX_wm_area_set(C, NULL);
1210                 CTX_wm_region_set(C, NULL);
1211         }
1212
1213         return 1;
1214 }
1215
1216 /* finish operation */
1217 static void area_join_exit(bContext *C, wmOperator *op)
1218 {
1219         if (op->customdata) {
1220                 MEM_freeN(op->customdata);
1221                 op->customdata = NULL;
1222         }
1223
1224         /* this makes sure aligned edges will result in aligned grabbing */
1225         removedouble_scredges(CTX_wm_screen(C));
1226         removenotused_scredges(CTX_wm_screen(C));
1227         removenotused_scrverts(CTX_wm_screen(C));
1228 }
1229
1230 static int area_join_exec(bContext *C, wmOperator *op)
1231 {
1232         if(!area_join_init(C, op)) 
1233                 return OPERATOR_CANCELLED;
1234         
1235         area_join_apply(C, op);
1236         area_join_exit(C, op);
1237
1238         return OPERATOR_FINISHED;
1239 }
1240
1241 /* interaction callback */
1242 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1243 {
1244
1245         if(event->type==EVT_ACTIONZONE) {
1246                 sActionzoneData *sad= event->customdata;
1247                 
1248                 /* verify *sad itself */
1249                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1250                         return OPERATOR_PASS_THROUGH;
1251                 
1252                 /* is this our *sad? if areas equal it should be passed on */
1253                 if(sad->sa1==sad->sa2)
1254                         return OPERATOR_PASS_THROUGH;
1255                 
1256                 /* prepare operator state vars */
1257                 RNA_int_set(op->ptr, "x1", sad->x);
1258                 RNA_int_set(op->ptr, "y1", sad->y);
1259                 RNA_int_set(op->ptr, "x2", event->x);
1260                 RNA_int_set(op->ptr, "y2", event->y);
1261
1262                 if(!area_join_init(C, op)) 
1263                         return OPERATOR_PASS_THROUGH;
1264         
1265                 /* add temp handler */
1266                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1267         
1268                 return OPERATOR_RUNNING_MODAL;
1269         }
1270         
1271         return OPERATOR_PASS_THROUGH;
1272 }
1273
1274 static int area_join_cancel(bContext *C, wmOperator *op)
1275 {
1276         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1277
1278         if (jd->sa1) {
1279                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1280                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1281         }
1282         if (jd->sa2) {
1283                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1284                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1285         }
1286
1287         WM_event_add_notifier(C, NC_WINDOW, NULL);
1288         
1289         area_join_exit(C, op);
1290
1291         return OPERATOR_CANCELLED;
1292 }
1293
1294 /* modal callback while selecting area (space) that will be removed */
1295 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1296 {
1297         bScreen *sc= CTX_wm_screen(C);
1298         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1299         
1300         /* execute the events */
1301         switch(event->type) {
1302                         
1303                 case MOUSEMOVE: 
1304                         {
1305                                 ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1306                                 int dir;
1307                                 
1308                                 if (sa) {                                       
1309                                         if (jd->sa1 != sa) {
1310                                                 dir = area_getorientation(sc, jd->sa1, sa);
1311                                                 if (dir >= 0) {
1312                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1313                                                         jd->sa2 = sa;
1314                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1315                                                 } 
1316                                                 else {
1317                                                         /* we are not bordering on the previously selected area 
1318                                                            we check if area has common border with the one marked for removal
1319                                                            in this case we can swap areas.
1320                                                         */
1321                                                         dir = area_getorientation(sc, sa, jd->sa2);
1322                                                         if (dir >= 0) {
1323                                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1324                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1325                                                                 jd->sa1 = jd->sa2;
1326                                                                 jd->sa2 = sa;
1327                                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1328                                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1329                                                         } 
1330                                                         else {
1331                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1332                                                                 jd->sa2 = NULL;
1333                                                         }
1334                                                 }
1335                                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
1336                                         } 
1337                                         else {
1338                                                 /* we are back in the area previously selected for keeping 
1339                                                  * we swap the areas if possible to allow user to choose */
1340                                                 if (jd->sa2 != NULL) {
1341                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1342                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1343                                                         jd->sa1 = jd->sa2;
1344                                                         jd->sa2 = sa;
1345                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1346                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1347                                                         dir = area_getorientation(sc, jd->sa1, jd->sa2);
1348                                                         if (dir < 0) {
1349                                                                 printf("oops, didn't expect that!\n");
1350                                                         }
1351                                                 } 
1352                                                 else {
1353                                                         dir = area_getorientation(sc, jd->sa1, sa);
1354                                                         if (dir >= 0) {
1355                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1356                                                                 jd->sa2 = sa;
1357                                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1358                                                         }
1359                                                 }
1360                                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
1361                                         }
1362                                 }
1363                         }
1364                         break;
1365                 case LEFTMOUSE:
1366                         if(event->val==0) {
1367                                 area_join_apply(C, op);
1368                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1369                                 area_join_exit(C, op);
1370                                 return OPERATOR_FINISHED;
1371                         }
1372                         break;
1373                         
1374                 case ESCKEY:
1375                         return area_join_cancel(C, op);
1376         }
1377
1378         return OPERATOR_RUNNING_MODAL;
1379 }
1380
1381 /* Operator for joining two areas (space types) */
1382 void SCREEN_OT_area_join(wmOperatorType *ot)
1383 {
1384         /* identifiers */
1385         ot->name= "Join area";
1386         ot->idname= "SCREEN_OT_area_join";
1387         
1388         /* api callbacks */
1389         ot->exec= area_join_exec;
1390         ot->invoke= area_join_invoke;
1391         ot->modal= area_join_modal;
1392
1393         ot->poll= ED_operator_areaactive;
1394
1395         /* rna */
1396         RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1397         RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
1398         RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
1399         RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
1400 }
1401
1402 /* ************** repeat last operator ***************************** */
1403
1404 static int repeat_last_exec(bContext *C, wmOperator *op)
1405 {
1406         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
1407         
1408         if(lastop)
1409                 WM_operator_repeat(C, lastop);
1410         
1411         return OPERATOR_CANCELLED;
1412 }
1413
1414 void SCREEN_OT_repeat_last(wmOperatorType *ot)
1415 {
1416         /* identifiers */
1417         ot->name= "Repeat Last";
1418         ot->idname= "SCREEN_OT_repeat_last";
1419         
1420         /* api callbacks */
1421         ot->exec= repeat_last_exec;
1422         
1423         ot->poll= ED_operator_screenactive;
1424         
1425 }
1426
1427 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
1428 {
1429         wmWindowManager *wm= CTX_wm_manager(C);
1430         wmOperator *lastop;
1431         uiMenuItem *head;
1432         int items, i;
1433         
1434         items= BLI_countlist(&wm->operators);
1435         if(items==0)
1436                 return OPERATOR_CANCELLED;
1437         
1438         head= uiPupMenuBegin(op->type->name, 0);
1439
1440         for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
1441                 uiMenuItemIntO(head, lastop->type->name, 0, op->type->idname, "index", i);
1442
1443         uiPupMenuEnd(C, head);
1444         
1445         return OPERATOR_CANCELLED;
1446 }
1447
1448 static int repeat_history_exec(bContext *C, wmOperator *op)
1449 {
1450         wmWindowManager *wm= CTX_wm_manager(C);
1451         
1452         op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
1453         if(op) {
1454                 /* let's put it as last operator in list */
1455                 BLI_remlink(&wm->operators, op);
1456                 BLI_addtail(&wm->operators, op);
1457                 
1458                 WM_operator_repeat(C, op);
1459         }
1460                                          
1461         return OPERATOR_FINISHED;
1462 }
1463
1464 void SCREEN_OT_repeat_history(wmOperatorType *ot)
1465 {
1466         /* identifiers */
1467         ot->name= "Repeat History";
1468         ot->idname= "SCREEN_OT_repeat_history";
1469         
1470         /* api callbacks */
1471         ot->invoke= repeat_history_invoke;
1472         ot->exec= repeat_history_exec;
1473         
1474         ot->poll= ED_operator_screenactive;
1475         
1476         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
1477 }
1478
1479 /* ********************** redo operator ***************************** */
1480
1481 static int redo_last_exec(bContext *C, wmOperator *op)
1482 {
1483 #if 0
1484         /* XXX context is not correct after popup menu */
1485         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
1486         
1487         if(lastop) {
1488                 ED_undo_pop(C);
1489                 WM_operator_repeat(C, lastop);
1490         }
1491 #endif
1492         
1493         return OPERATOR_CANCELLED;
1494 }
1495
1496 static void redo_last_cb(bContext *C, void *arg1, void *arg2)
1497 {
1498         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
1499         
1500         if(lastop) {
1501                 ED_undo_pop(C);
1502                 WM_operator_repeat(C, lastop);
1503         }
1504         
1505 }
1506
1507 static uiBlock *ui_block_create_redo_last(bContext *C, ARegion *ar, void *arg_op)
1508 {
1509         wmWindowManager *wm= CTX_wm_manager(C);
1510         wmOperator *op= arg_op;
1511         PointerRNA ptr;
1512         uiBlock *block;
1513         int height;
1514         
1515         block= uiBeginBlock(C, ar, "redo_last_popup", UI_EMBOSS, UI_HELV);
1516         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1);
1517         uiBlockSetFunc(block, redo_last_cb, NULL, NULL);
1518
1519         if(!op->properties) {
1520                 IDPropertyTemplate val = {0};
1521                 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
1522         }
1523
1524         RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
1525         height= uiDefAutoButsRNA(block, &ptr);
1526
1527         uiPopupBoundsBlock(block, 4.0f, 0, 0);
1528         uiEndBlock(C, block);
1529
1530         return block;
1531 }
1532
1533 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
1534 {
1535         wmWindowManager *wm= CTX_wm_manager(C);
1536         wmOperator *lastop= wm->operators.last;
1537
1538         if(!lastop)
1539                 return OPERATOR_CANCELLED;
1540
1541         /* only for operators that are registered and did an undo push */
1542         if(!(lastop->type->flag & OPTYPE_REGISTER) || !(lastop->type->flag & OPTYPE_UNDO))
1543                 return OPERATOR_CANCELLED;
1544
1545         uiPupBlockO(C, ui_block_create_redo_last, lastop, op->type->idname, WM_OP_EXEC_DEFAULT);
1546
1547         return OPERATOR_CANCELLED;
1548 }
1549
1550 void SCREEN_OT_redo_last(wmOperatorType *ot)
1551 {
1552         /* identifiers */
1553         ot->name= "Redo Last";
1554         ot->idname= "SCREEN_OT_redo_last";
1555         
1556         /* api callbacks */
1557         ot->invoke= redo_last_invoke;
1558         ot->exec= redo_last_exec;
1559         
1560         ot->poll= ED_operator_screenactive;
1561 }
1562
1563 /* ************** region split operator ***************************** */
1564
1565 /* insert a region in the area region list */
1566 static int region_split_exec(bContext *C, wmOperator *op)
1567 {
1568         ARegion *ar= CTX_wm_region(C);
1569         
1570         if(ar->regiontype==RGN_TYPE_HEADER)
1571                 BKE_report(op->reports, RPT_ERROR, "Cannot split header");
1572         else if(ar->alignment==RGN_ALIGN_QSPLIT)
1573                 BKE_report(op->reports, RPT_ERROR, "Cannot split further");
1574         else {
1575                 ScrArea *sa= CTX_wm_area(C);
1576                 ARegion *newar= BKE_area_region_copy(sa->type, ar);
1577                 int dir= RNA_enum_get(op->ptr, "type");
1578         
1579                 BLI_insertlinkafter(&sa->regionbase, ar, newar);
1580                 
1581                 newar->alignment= ar->alignment;
1582                 
1583                 if(dir=='h')
1584                         ar->alignment= RGN_ALIGN_HSPLIT;
1585                 else
1586                         ar->alignment= RGN_ALIGN_VSPLIT;
1587                 
1588                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1589         }
1590         
1591         return OPERATOR_FINISHED;
1592 }
1593
1594 void SCREEN_OT_region_split(wmOperatorType *ot)
1595 {
1596         /* identifiers */
1597         ot->name= "Split Region";
1598         ot->idname= "SCREEN_OT_region_split";
1599         
1600         /* api callbacks */
1601         ot->invoke= WM_menu_invoke;
1602         ot->exec= region_split_exec;
1603         ot->poll= ED_operator_areaactive;
1604         
1605         RNA_def_enum(ot->srna, "type", prop_direction_items, 'h', "Direction", "");
1606 }
1607
1608 /* ************** region four-split operator ***************************** */
1609
1610 /* insert a region in the area region list */
1611 static int region_foursplit_exec(bContext *C, wmOperator *op)
1612 {
1613         ARegion *ar= CTX_wm_region(C);
1614         
1615         /* some rules... */
1616         if(ar->regiontype!=RGN_TYPE_WINDOW)
1617                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
1618         else if(ar->alignment==RGN_ALIGN_QSPLIT) {
1619                 ScrArea *sa= CTX_wm_area(C);
1620                 ARegion *arn;
1621                 
1622                 /* keep current region */
1623                 ar->alignment= 0;
1624                 
1625                 if(sa->spacetype==SPACE_VIEW3D) {
1626                         RegionView3D *rv3d= ar->regiondata;
1627                         rv3d->viewlock= 0;
1628                 }
1629                 
1630                 for(ar= sa->regionbase.first; ar; ar= arn) {
1631                         arn= ar->next;
1632                         if(ar->alignment==RGN_ALIGN_QSPLIT) {
1633                                 ED_region_exit(C, ar);
1634                                 BKE_area_region_free(sa->type, ar);
1635                                 BLI_remlink(&sa->regionbase, ar);
1636                                 MEM_freeN(ar);
1637                         }
1638                 }
1639                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1640         }
1641         else if(ar->next)
1642                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
1643         else {
1644                 ScrArea *sa= CTX_wm_area(C);
1645                 ARegion *newar;
1646                 int count;
1647                 
1648                 ar->alignment= RGN_ALIGN_QSPLIT;
1649                 
1650                 for(count=0; count<3; count++) {
1651                         newar= BKE_area_region_copy(sa->type, ar);
1652                         BLI_addtail(&sa->regionbase, newar);
1653                 }
1654                 
1655                 /* lock views and set them */
1656                 if(sa->spacetype==SPACE_VIEW3D) {
1657                         RegionView3D *rv3d;
1658                         
1659                         rv3d= ar->regiondata;
1660                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_FRONT; rv3d->persp= V3D_ORTHO;
1661                         
1662                         ar= ar->next;
1663                         rv3d= ar->regiondata;
1664                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_TOP; rv3d->persp= V3D_ORTHO;
1665                         
1666                         ar= ar->next;
1667                         rv3d= ar->regiondata;
1668                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_RIGHT; rv3d->persp= V3D_ORTHO;
1669                         
1670                         ar= ar->next;
1671                         rv3d= ar->regiondata;
1672                         rv3d->view= V3D_VIEW_CAMERA; rv3d->persp= V3D_CAMOB;
1673                 }
1674                 
1675                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1676         }
1677         
1678         
1679         return OPERATOR_FINISHED;
1680 }
1681
1682 void SCREEN_OT_region_foursplit(wmOperatorType *ot)
1683 {
1684         /* identifiers */
1685         ot->name= "Split Region in 4 Parts";
1686         ot->idname= "SCREEN_OT_region_foursplit";
1687         
1688         /* api callbacks */
1689         ot->invoke= WM_operator_confirm;
1690         ot->exec= region_foursplit_exec;
1691         ot->poll= ED_operator_areaactive;
1692         ot->flag= OPTYPE_REGISTER;
1693 }
1694
1695
1696
1697 /* ************** region flip operator ***************************** */
1698
1699 /* flip a region alignment */
1700 static int region_flip_exec(bContext *C, wmOperator *op)
1701 {
1702         ARegion *ar= CTX_wm_region(C);
1703
1704         if(ar->alignment==RGN_ALIGN_TOP)
1705                 ar->alignment= RGN_ALIGN_BOTTOM;
1706         else if(ar->alignment==RGN_ALIGN_BOTTOM)
1707                 ar->alignment= RGN_ALIGN_TOP;
1708         else if(ar->alignment==RGN_ALIGN_LEFT)
1709                 ar->alignment= RGN_ALIGN_RIGHT;
1710         else if(ar->alignment==RGN_ALIGN_RIGHT)
1711                 ar->alignment= RGN_ALIGN_LEFT;
1712         
1713         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1714         printf("executed region flip\n");
1715         
1716         return OPERATOR_FINISHED;
1717 }
1718
1719 static void testfunc(bContext *C, void *argv, int arg)
1720 {
1721         printf("arg %d\n", arg);
1722 }
1723
1724 static void newlevel1(bContext *C, uiMenuItem *head, void *arg)
1725 {
1726         uiMenuFunc(head, testfunc, NULL);
1727         
1728         uiMenuItemVal(head, "First", ICON_PROP_ON, 1);
1729         uiMenuItemVal(head, "Second", ICON_PROP_CON, 2);
1730         uiMenuItemVal(head, "Third", ICON_SMOOTHCURVE, 3);
1731         uiMenuItemVal(head, "Fourth", ICON_SHARPCURVE, 4);      
1732 }
1733
1734 static int testing123(bContext *C, wmOperator *op, wmEvent *event)
1735 {
1736         uiMenuItem *head= uiPupMenuBegin("Hello world", 0);
1737         
1738         uiMenuContext(head, WM_OP_EXEC_DEFAULT);
1739         uiMenuItemO(head, ICON_PROP_ON, "SCREEN_OT_region_flip");
1740         uiMenuItemO(head, ICON_PROP_CON, "SCREEN_OT_screen_full_area");
1741         uiMenuItemO(head, ICON_SMOOTHCURVE, "SCREEN_OT_region_foursplit");
1742         uiMenuLevel(head, "Submenu", newlevel1);
1743         uiMenuItemO(head, ICON_PROP_ON, "SCREEN_OT_area_rip");
1744         
1745         uiPupMenuEnd(C, head);
1746         
1747         /* this operator is only for a menu, not used further */
1748         return OPERATOR_CANCELLED;
1749 }
1750
1751 void SCREEN_OT_region_flip(wmOperatorType *ot)
1752 {
1753         /* identifiers */
1754         ot->name= "Flip Region";
1755         ot->idname= "SCREEN_OT_region_flip";
1756         
1757         /* api callbacks */
1758         ot->invoke= testing123; // XXX WM_operator_confirm;
1759         ot->exec= region_flip_exec;
1760         
1761         ot->poll= ED_operator_areaactive;
1762         ot->flag= OPTYPE_REGISTER;
1763         
1764         RNA_def_int(ot->srna, "test", 0, INT_MIN, INT_MAX, "test", "", INT_MIN, INT_MAX);
1765
1766 }
1767
1768 /* ****************** anim player, typically with timer ***************** */
1769
1770 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
1771 {
1772         bScreen *screen= CTX_wm_screen(C);
1773         
1774         if(screen->animtimer==event->customdata) {
1775                 Scene *scene= CTX_data_scene(C);
1776                 
1777                 scene->r.cfra++;
1778                 
1779                 if (scene->r.psfra) {
1780                         if(scene->r.cfra > scene->r.pefra)
1781                                 scene->r.cfra= scene->r.psfra;
1782                 }
1783                 else {
1784                         if(scene->r.cfra > scene->r.efra)
1785                                 scene->r.cfra= scene->r.sfra;
1786                 }
1787
1788                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1789                 
1790                 return OPERATOR_FINISHED;
1791         }
1792         return OPERATOR_PASS_THROUGH;
1793 }
1794
1795 void SCREEN_OT_animation_play(wmOperatorType *ot)
1796 {
1797         /* identifiers */
1798         ot->name= "Animation player";
1799         ot->idname= "SCREEN_OT_animation_play";
1800         
1801         /* api callbacks */
1802         ot->invoke= screen_animation_play;
1803         
1804         ot->poll= ED_operator_screenactive;
1805         
1806 }
1807
1808 /* ************** border select operator (template) ***************************** */
1809
1810 /* operator state vars used: (added by default WM callbacks)   
1811         xmin, ymin     
1812         xmax, ymax     
1813
1814         customdata: the wmGesture pointer
1815
1816 callbacks:
1817
1818         exec()  has to be filled in by user
1819
1820         invoke() default WM function
1821                          adds modal handler
1822
1823         modal() default WM function 
1824                         accept modal events while doing it, calls exec(), handles ESC and border drawing
1825         
1826         poll()  has to be filled in by user for context
1827 */
1828 #if 0
1829 static int border_select_do(bContext *C, wmOperator *op)
1830 {
1831         int event_type= RNA_int_get(op->ptr, "event_type");
1832         
1833         if(event_type==LEFTMOUSE)
1834                 printf("border select do select\n");
1835         else if(event_type==RIGHTMOUSE)
1836                 printf("border select deselect\n");
1837         else 
1838                 printf("border select do something\n");
1839         
1840         return 1;
1841 }
1842
1843 void SCREEN_OT_border_select(wmOperatorType *ot)
1844 {
1845         /* identifiers */
1846         ot->name= "Border select";
1847         ot->idname= "SCREEN_OT_border_select";
1848         
1849         /* api callbacks */
1850         ot->exec= border_select_do;
1851         ot->invoke= WM_border_select_invoke;
1852         ot->modal= WM_border_select_modal;
1853         
1854         ot->poll= ED_operator_areaactive;
1855         
1856         /* rna */
1857         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
1858         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
1859         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
1860         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
1861         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
1862
1863 }
1864 #endif
1865
1866 /* ****************************** render invoking ***************** */
1867
1868 /* set callbacks, exported to sequence render too. 
1869 Only call in foreground (UI) renders. */
1870
1871 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
1872 /* window as the last possible alternative.                                                                        */
1873 static ScrArea *biggest_non_image_area(bContext *C)
1874 {
1875         bScreen *sc= CTX_wm_screen(C);
1876         ScrArea *sa, *big= NULL;
1877         int size, maxsize= 0, bwmaxsize= 0;
1878         short foundwin= 0;
1879         
1880         for(sa= sc->areabase.first; sa; sa= sa->next) {
1881                 if(sa->winx > 10 && sa->winy > 10) {
1882                         size= sa->winx*sa->winy;
1883                         if(sa->spacetype == SPACE_BUTS) {
1884                                 if(foundwin == 0 && size > bwmaxsize) {
1885                                         bwmaxsize= size;
1886                                         big= sa;        
1887                                 }
1888                         }
1889                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
1890                                 maxsize= size;
1891                                 big= sa;
1892                                 foundwin= 1;
1893                         }
1894                 }
1895         }
1896         
1897         return big;
1898 }
1899
1900 static ScrArea *biggest_area(bContext *C)
1901 {
1902         bScreen *sc= CTX_wm_screen(C);
1903         ScrArea *sa, *big= NULL;
1904         int size, maxsize= 0;
1905         
1906         for(sa= sc->areabase.first; sa; sa= sa->next) {
1907                 size= sa->winx*sa->winy;
1908                 if(size > maxsize) {
1909                         maxsize= size;
1910                         big= sa;
1911                 }
1912         }
1913         return big;
1914 }
1915
1916
1917 static ScrArea *find_area_showing_r_result(bContext *C)
1918 {
1919         bScreen *sc= CTX_wm_screen(C);
1920         ScrArea *sa;
1921         SpaceImage *sima;
1922         
1923         /* find an imagewindow showing render result */
1924         for(sa=sc->areabase.first; sa; sa= sa->next) {
1925                 if(sa->spacetype==SPACE_IMAGE) {
1926                         sima= sa->spacedata.first;
1927                         if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
1928                                 break;
1929                 }
1930         }
1931         return sa;
1932 }
1933
1934 static void screen_set_image_output(bContext *C)
1935 {
1936         ScrArea *sa;
1937         SpaceImage *sima;
1938         
1939         sa= find_area_showing_r_result(C);
1940         
1941         if(sa==NULL) {
1942                 /* find largest open non-image area */
1943                 sa= biggest_non_image_area(C);
1944                 if(sa) {
1945                         ED_area_newspace(C, sa, SPACE_IMAGE);
1946                         sima= sa->spacedata.first;
1947                         
1948                         /* makes ESC go back to prev space */
1949                         sima->flag |= SI_PREVSPACE;
1950                 }
1951                 else {
1952                         /* use any area of decent size */
1953                         sa= biggest_area(C);
1954                         if(sa->spacetype!=SPACE_IMAGE) {
1955                                 // XXX newspace(sa, SPACE_IMAGE);
1956                                 sima= sa->spacedata.first;
1957                                 
1958                                 /* makes ESC go back to prev space */
1959                                 sima->flag |= SI_PREVSPACE;
1960                         }
1961                 }
1962         }
1963         
1964         sima= sa->spacedata.first;
1965         
1966         /* get the correct image, and scale it */
1967         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
1968         
1969         if(G.displaymode==2) { // XXX
1970                 if(sa->full==0) {
1971                         sima->flag |= SI_FULLWINDOW;
1972                         
1973                         ed_screen_fullarea(C, sa);
1974                 }
1975         }
1976         
1977 }
1978
1979 /* executes blocking render */
1980 static int screen_render_exec(bContext *C, wmOperator *op)
1981 {
1982         Scene *scene= CTX_data_scene(C);
1983         Render *re= RE_GetRender(scene->id.name);
1984         
1985         if(re==NULL) {
1986                 re= RE_NewRender(scene->id.name);
1987         }
1988         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
1989         
1990         if(RNA_boolean_get(op->ptr, "anim"))
1991                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->frame_step);
1992         else
1993                 RE_BlenderFrame(re, scene, scene->r.cfra);
1994         
1995         // no redraw needed, we leave state as we entered it
1996         ED_update_for_newframe(C, 1);
1997         
1998         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
1999
2000         return OPERATOR_FINISHED;
2001 }
2002
2003 typedef struct RenderJob {
2004         Scene *scene;
2005         Render *re;
2006         wmWindow *win;
2007         int anim;
2008         Image *image;
2009         ImageUser iuser;
2010         short *stop;
2011         short *do_update;
2012 } RenderJob;
2013
2014 static void render_freejob(void *rjv)
2015 {
2016         RenderJob *rj= rjv;
2017         
2018         MEM_freeN(rj);
2019 }
2020
2021 /* called inside thread! */
2022 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
2023 {
2024         RenderJob *rj= rjv;
2025         ImBuf *ibuf;
2026         float x1, y1, *rectf= NULL;
2027         int ymin, ymax, xmin, xmax;
2028         int rymin, rxmin;
2029         char *rectc;
2030         
2031         ibuf= BKE_image_get_ibuf(rj->image, &rj->iuser);
2032         if(ibuf==NULL) return;
2033
2034         /* if renrect argument, we only refresh scanlines */
2035         if(renrect) {
2036                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
2037                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
2038                         return;
2039                 
2040                 /* xmin here is first subrect x coord, xmax defines subrect width */
2041                 xmin = renrect->xmin;
2042                 xmax = renrect->xmax - xmin;
2043                 if (xmax<2) return;
2044                 
2045                 ymin= renrect->ymin;
2046                 ymax= renrect->ymax - ymin;
2047                 if(ymax<2)
2048                         return;
2049                 renrect->ymin= renrect->ymax;
2050         }
2051         else {
2052                 xmin = ymin = rr->crop;
2053                 xmax = rr->rectx - 2*rr->crop;
2054                 ymax = rr->recty - 2*rr->crop;
2055         }
2056         
2057         /* xmin ymin is in tile coords. transform to ibuf */
2058         rxmin= rr->tilerect.xmin + xmin;
2059         if(rxmin >= ibuf->x) return;
2060         rymin= rr->tilerect.ymin + ymin;
2061         if(rymin >= ibuf->y) return;
2062         
2063         if(rxmin + xmax > ibuf->x)
2064                 xmax= ibuf->x - rxmin;
2065         if(rymin + ymax > ibuf->y)
2066                 ymax= ibuf->y - rymin;
2067         if(xmax < 1 || ymax < 1) return;
2068         
2069         /* find current float rect for display, first case is after composit... still weak */
2070         if(rr->rectf)
2071                 rectf= rr->rectf;
2072         else {
2073                 if(rr->rect32)
2074                         return;
2075                 else {
2076                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
2077                         rectf= rr->renlay->rectf;
2078                 }
2079         }
2080         if(rectf==NULL) return;
2081         
2082         rectf+= 4*(rr->rectx*ymin + xmin);
2083         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
2084
2085         for(y1= 0; y1<ymax; y1++) {
2086                 float *rf= rectf;
2087                 char *rc= rectc;
2088                 
2089                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2090                         rc[0]= FTOCHAR(rf[0]);
2091                         rc[1]= FTOCHAR(rf[1]);
2092                         rc[2]= FTOCHAR(rf[2]);
2093                         rc[3]= FTOCHAR(rf[3]);
2094                 }
2095                 rectf += 4*rr->rectx;
2096                 rectc += 4*ibuf->x;
2097         }
2098         
2099         /* make jobs timer to send notifier */
2100         *(rj->do_update)= 1;
2101 }
2102
2103 static void render_startjob(void *rjv, short *stop, short *do_update)
2104 {
2105         RenderJob *rj= rjv;
2106         
2107         rj->stop= stop;
2108         rj->do_update= do_update;
2109         
2110         if(rj->anim)
2111                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->frame_step);
2112         else
2113                 RE_BlenderFrame(rj->re, rj->scene, rj->scene->r.cfra);
2114 }
2115
2116 /* called by render, check job 'stop' value or the global */
2117 static int render_breakjob(void *rjv)
2118 {
2119         RenderJob *rj= rjv;
2120         
2121         if(G.afbreek)
2122                 return 1;
2123         if(rj->stop && *(rj->stop))
2124                 return 1;
2125         return 0;
2126 }
2127
2128 /* using context, starts job */
2129 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
2130 {
2131         /* new render clears all callbacks */
2132         Scene *scene= CTX_data_scene(C);
2133         Render *re;
2134         wmJob *steve;
2135         RenderJob *rj;
2136         Image *ima;
2137         
2138         /* only one job at a time */
2139         if(WM_jobs_test(CTX_wm_manager(C), scene))
2140                 return OPERATOR_CANCELLED;
2141         
2142         /* handle UI stuff */
2143         WM_cursor_wait(1);
2144
2145         /* flush multires changes (for sculpt) */
2146         multires_force_update(CTX_data_active_object(C));
2147         
2148         // get editmode results
2149         // store spare
2150         // get view3d layer, local layer, make this nice api call to render
2151         // store spare
2152         
2153         /* ensure at least 1 area shows result */
2154         screen_set_image_output(C);
2155
2156         /* job custom data */
2157         rj= MEM_callocN(sizeof(RenderJob), "render job");
2158         rj->scene= scene;
2159         rj->win= CTX_wm_window(C);
2160         rj->anim= RNA_boolean_get(op->ptr, "anim");
2161         rj->iuser.scene= scene;
2162         rj->iuser.ok= 1;
2163         
2164         /* setup job */
2165         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene);
2166         WM_jobs_customdata(steve, rj, render_freejob);
2167         WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
2168         WM_jobs_callbacks(steve, render_startjob, NULL, NULL);
2169         
2170         /* get a render result image, and make sure it is empty */
2171         ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2172         BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
2173         rj->image= ima;
2174         
2175         /* setup new render */
2176         re= RE_NewRender(scene->id.name);
2177         RE_test_break_cb(re, rj, render_breakjob);
2178         RE_display_draw_cb(re, rj, image_rect_update);
2179         rj->re= re;
2180         G.afbreek= 0;
2181         
2182         //      BKE_report in render!
2183         //      RE_error_cb(re, error_cb);
2184
2185         WM_jobs_start(steve);
2186         
2187         G.afbreek= 0;
2188         
2189         WM_cursor_wait(0);
2190         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2191
2192         return OPERATOR_FINISHED;
2193 }
2194
2195
2196 /* contextual render, using current scene, view3d? */
2197 void SCREEN_OT_render(wmOperatorType *ot)
2198 {
2199         /* identifiers */
2200         ot->name= "Render";
2201         ot->idname= "SCREEN_OT_render";
2202         
2203         /* api callbacks */
2204         ot->invoke= screen_render_invoke;
2205         ot->exec= screen_render_exec;
2206         
2207         ot->poll= ED_operator_screenactive;
2208         
2209         RNA_def_int(ot->srna, "layers", 0, 0, INT_MAX, "Layers", "", 0, INT_MAX);
2210         RNA_def_boolean(ot->srna, "anim", 0, "Animation", "");
2211 }
2212
2213 /* *********************** cancel render viewer *************** */
2214
2215 static int render_view_cancel_exec(bContext *C, wmOperator *unused)
2216 {
2217         ScrArea *sa= CTX_wm_area(C);
2218         SpaceImage *sima= sa->spacedata.first;
2219         
2220         if(sima->flag & SI_PREVSPACE) {
2221                 sima->flag &= ~SI_PREVSPACE;
2222                 ED_area_prevspace(C);
2223         }
2224         else if(sima->flag & SI_FULLWINDOW) {
2225                 sima->flag &= ~SI_FULLWINDOW;
2226                 ED_screen_full_prevspace(C);
2227         }
2228         
2229         return OPERATOR_FINISHED;
2230 }
2231
2232 void SCREEN_OT_render_view_cancel(struct wmOperatorType *ot)
2233 {
2234         /* identifiers */
2235         ot->name= "Cancel Render View";
2236         ot->idname= "SCREEN_OT_render_view_cancel";
2237         
2238         /* api callbacks */
2239         ot->exec= render_view_cancel_exec;
2240         ot->poll= ED_operator_image_active;
2241 }
2242
2243
2244 /* ****************  Assigning operatortypes to global list, adding handlers **************** */
2245
2246 /* called in spacetypes.c */
2247 void ED_operatortypes_screen(void)
2248 {
2249         /* generic UI stuff */
2250         WM_operatortype_append(SCREEN_OT_actionzone);
2251         WM_operatortype_append(SCREEN_OT_repeat_last);
2252         WM_operatortype_append(SCREEN_OT_repeat_history);
2253         WM_operatortype_append(SCREEN_OT_redo_last);
2254         
2255         /* screen tools */
2256         WM_operatortype_append(SCREEN_OT_area_move);
2257         WM_operatortype_append(SCREEN_OT_area_split);
2258         WM_operatortype_append(SCREEN_OT_area_join);
2259         WM_operatortype_append(SCREEN_OT_area_rip);
2260         WM_operatortype_append(SCREEN_OT_region_split);
2261         WM_operatortype_append(SCREEN_OT_region_foursplit);
2262         WM_operatortype_append(SCREEN_OT_region_flip);
2263         WM_operatortype_append(SCREEN_OT_screen_set);
2264         WM_operatortype_append(SCREEN_OT_screen_full_area);
2265         
2266         /*frame changes*/
2267         WM_operatortype_append(SCREEN_OT_frame_offset);
2268         WM_operatortype_append(SCREEN_OT_animation_play);
2269         
2270         /* render */
2271         WM_operatortype_append(SCREEN_OT_render);
2272         WM_operatortype_append(SCREEN_OT_render_view_cancel);
2273         
2274         /* tools shared by more space types */
2275         WM_operatortype_append(ED_OT_undo);
2276         WM_operatortype_append(ED_OT_redo);     
2277         
2278 }
2279
2280 /* called in spacetypes.c */
2281 void ED_keymap_screen(wmWindowManager *wm)
2282 {
2283         ListBase *keymap= WM_keymap_listbase(wm, "Screen", 0, 0);
2284         
2285         /* standard timers */
2286         WM_keymap_add_item(keymap, "SCREEN_OT_animation_play", TIMER0, KM_ANY, KM_ANY, 0);
2287         
2288         WM_keymap_verify_item(keymap, "SCREEN_OT_actionzone", LEFTMOUSE, KM_PRESS, 0, 0);
2289         
2290         WM_keymap_verify_item(keymap, "SCREEN_OT_area_move", LEFTMOUSE, KM_PRESS, 0, 0);
2291         WM_keymap_verify_item(keymap, "SCREEN_OT_area_split", EVT_ACTIONZONE, 0, 0, 0);
2292         WM_keymap_verify_item(keymap, "SCREEN_OT_area_join", EVT_ACTIONZONE, 0, 0, 0);
2293         WM_keymap_verify_item(keymap, "SCREEN_OT_area_rip", RKEY, KM_PRESS, KM_CTRL|KM_ALT, 0);
2294         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1);
2295         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1);
2296         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", UPARROWKEY, KM_PRESS, KM_CTRL, 0);
2297         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", DOWNARROWKEY, KM_PRESS, KM_CTRL, 0);
2298         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", SPACEKEY, KM_PRESS, KM_CTRL, 0);
2299
2300          /* tests */
2301         WM_keymap_add_item(keymap, "SCREEN_OT_region_split", SKEY, KM_PRESS, KM_CTRL|KM_ALT, 0);
2302         WM_keymap_add_item(keymap, "SCREEN_OT_region_foursplit", SKEY, KM_PRESS, KM_CTRL|KM_ALT|KM_SHIFT, 0);
2303         
2304         WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_history", F3KEY, KM_PRESS, 0, 0);
2305         WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_last", F4KEY, KM_PRESS, 0, 0);
2306         WM_keymap_add_item(keymap, "SCREEN_OT_region_flip", F5KEY, KM_PRESS, 0, 0);
2307         WM_keymap_verify_item(keymap, "SCREEN_OT_redo_last", F6KEY, KM_PRESS, 0, 0);
2308
2309         /* files */
2310         WM_keymap_add_item(keymap, "ED_FILE_OT_load", RETKEY, KM_PRESS, 0, 0);
2311         WM_keymap_add_item(keymap, "ED_FILE_OT_cancel", ESCKEY, KM_PRESS, 0, 0);
2312         
2313         /* undo */
2314         WM_keymap_add_item(keymap, "ED_OT_undo", ZKEY, KM_PRESS, KM_CTRL, 0);
2315         WM_keymap_add_item(keymap, "ED_OT_undo", ZKEY, KM_PRESS, KM_OSKEY, 0);
2316         WM_keymap_add_item(keymap, "ED_OT_redo", ZKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0);
2317         WM_keymap_add_item(keymap, "ED_OT_redo", ZKEY, KM_PRESS, KM_SHIFT|KM_OSKEY, 0);
2318                                                   
2319         /* render */
2320         WM_keymap_add_item(keymap, "SCREEN_OT_render", F12KEY, KM_PRESS, 0, 0);
2321         WM_keymap_add_item(keymap, "SCREEN_OT_render_view_cancel", ESCKEY, KM_PRESS, 0, 0);
2322         
2323         /* frame offsets */
2324         keymap= WM_keymap_listbase(wm, "Frames", 0, 0);
2325         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 10);
2326         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -10);
2327         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -1);
2328         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 1);
2329         
2330 }
2331