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