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