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