076f871c4c27ff2324f85308482a1b0edd428136
[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         uiBlockClearFlag(block, UI_BLOCK_LOOP);
1643         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1);
1644         uiBlockSetFunc(block, redo_last_cb, arg_op, NULL);
1645
1646         if(!op->properties) {
1647                 IDPropertyTemplate val = {0};
1648                 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
1649         }
1650
1651         RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
1652         height= uiDefAutoButsRNA(C, block, &ptr);
1653
1654         uiPopupBoundsBlock(block, 4.0f, 0, 0);
1655         uiEndBlock(C, block);
1656
1657         return block;
1658 }
1659
1660 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
1661 {
1662         wmWindowManager *wm= CTX_wm_manager(C);
1663         wmOperator *lastop;
1664
1665         /* only for operators that are registered and did an undo push */
1666         for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
1667                 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
1668                         break;
1669         
1670         if(!lastop)
1671                 return OPERATOR_CANCELLED;
1672
1673         uiPupBlock(C, ui_block_create_redo_last, lastop);
1674
1675         return OPERATOR_CANCELLED;
1676 }
1677
1678 void SCREEN_OT_redo_last(wmOperatorType *ot)
1679 {
1680         /* identifiers */
1681         ot->name= "Redo Last";
1682         ot->idname= "SCREEN_OT_redo_last";
1683         
1684         /* api callbacks */
1685         ot->invoke= redo_last_invoke;
1686         
1687         ot->poll= ED_operator_screenactive;
1688 }
1689
1690 /* ************** region split operator ***************************** */
1691
1692 /* insert a region in the area region list */
1693 static int region_split_exec(bContext *C, wmOperator *op)
1694 {
1695         ARegion *ar= CTX_wm_region(C);
1696         
1697         if(ar->regiontype==RGN_TYPE_HEADER)
1698                 BKE_report(op->reports, RPT_ERROR, "Cannot split header");
1699         else if(ar->alignment==RGN_ALIGN_QSPLIT)
1700                 BKE_report(op->reports, RPT_ERROR, "Cannot split further");
1701         else {
1702                 ScrArea *sa= CTX_wm_area(C);
1703                 ARegion *newar= BKE_area_region_copy(sa->type, ar);
1704                 int dir= RNA_enum_get(op->ptr, "type");
1705         
1706                 BLI_insertlinkafter(&sa->regionbase, ar, newar);
1707                 
1708                 newar->alignment= ar->alignment;
1709                 
1710                 if(dir=='h')
1711                         ar->alignment= RGN_ALIGN_HSPLIT;
1712                 else
1713                         ar->alignment= RGN_ALIGN_VSPLIT;
1714                 
1715                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1716         }
1717         
1718         return OPERATOR_FINISHED;
1719 }
1720
1721 void SCREEN_OT_region_split(wmOperatorType *ot)
1722 {
1723         /* identifiers */
1724         ot->name= "Split Region";
1725         ot->idname= "SCREEN_OT_region_split";
1726         
1727         /* api callbacks */
1728         ot->invoke= WM_menu_invoke;
1729         ot->exec= region_split_exec;
1730         ot->poll= ED_operator_areaactive;
1731         
1732         RNA_def_enum(ot->srna, "type", prop_direction_items, 'h', "Direction", "");
1733 }
1734
1735 /* ************** region four-split operator ***************************** */
1736
1737 /* insert a region in the area region list */
1738 static int region_foursplit_exec(bContext *C, wmOperator *op)
1739 {
1740         ARegion *ar= CTX_wm_region(C);
1741         
1742         /* some rules... */
1743         if(ar->regiontype!=RGN_TYPE_WINDOW)
1744                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
1745         else if(ar->alignment==RGN_ALIGN_QSPLIT) {
1746                 ScrArea *sa= CTX_wm_area(C);
1747                 ARegion *arn;
1748                 
1749                 /* keep current region */
1750                 ar->alignment= 0;
1751                 
1752                 if(sa->spacetype==SPACE_VIEW3D) {
1753                         RegionView3D *rv3d= ar->regiondata;
1754                         rv3d->viewlock= 0;
1755                         rv3d->rflag &= ~RV3D_CLIPPING;
1756                 }
1757                 
1758                 for(ar= sa->regionbase.first; ar; ar= arn) {
1759                         arn= ar->next;
1760                         if(ar->alignment==RGN_ALIGN_QSPLIT) {
1761                                 ED_region_exit(C, ar);
1762                                 BKE_area_region_free(sa->type, ar);
1763                                 BLI_remlink(&sa->regionbase, ar);
1764                                 MEM_freeN(ar);
1765                         }
1766                 }
1767                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1768         }
1769         else if(ar->next)
1770                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
1771         else {
1772                 ScrArea *sa= CTX_wm_area(C);
1773                 ARegion *newar;
1774                 int count;
1775                 
1776                 ar->alignment= RGN_ALIGN_QSPLIT;
1777                 
1778                 for(count=0; count<3; count++) {
1779                         newar= BKE_area_region_copy(sa->type, ar);
1780                         BLI_addtail(&sa->regionbase, newar);
1781                 }
1782                 
1783                 /* lock views and set them */
1784                 if(sa->spacetype==SPACE_VIEW3D) {
1785                         RegionView3D *rv3d;
1786                         
1787                         rv3d= ar->regiondata;
1788                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_FRONT; rv3d->persp= V3D_ORTHO;
1789                         
1790                         ar= ar->next;
1791                         rv3d= ar->regiondata;
1792                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_TOP; rv3d->persp= V3D_ORTHO;
1793                         
1794                         ar= ar->next;
1795                         rv3d= ar->regiondata;
1796                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= V3D_VIEW_RIGHT; rv3d->persp= V3D_ORTHO;
1797                         
1798                         ar= ar->next;
1799                         rv3d= ar->regiondata;
1800                         rv3d->view= V3D_VIEW_CAMERA; rv3d->persp= V3D_CAMOB;
1801                 }
1802                 
1803                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1804         }
1805         
1806         
1807         return OPERATOR_FINISHED;
1808 }
1809
1810 void SCREEN_OT_region_foursplit(wmOperatorType *ot)
1811 {
1812         /* identifiers */
1813         ot->name= "Split Region in 4 Parts";
1814         ot->idname= "SCREEN_OT_region_foursplit";
1815         
1816         /* api callbacks */
1817         ot->invoke= WM_operator_confirm;
1818         ot->exec= region_foursplit_exec;
1819         ot->poll= ED_operator_areaactive;
1820         ot->flag= OPTYPE_REGISTER;
1821 }
1822
1823
1824
1825 /* ************** region flip operator ***************************** */
1826
1827 /* flip a region alignment */
1828 static int region_flip_exec(bContext *C, wmOperator *op)
1829 {
1830         ARegion *ar= CTX_wm_region(C);
1831
1832         if(ar->alignment==RGN_ALIGN_TOP)
1833                 ar->alignment= RGN_ALIGN_BOTTOM;
1834         else if(ar->alignment==RGN_ALIGN_BOTTOM)
1835                 ar->alignment= RGN_ALIGN_TOP;
1836         else if(ar->alignment==RGN_ALIGN_LEFT)
1837                 ar->alignment= RGN_ALIGN_RIGHT;
1838         else if(ar->alignment==RGN_ALIGN_RIGHT)
1839                 ar->alignment= RGN_ALIGN_LEFT;
1840         
1841         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1842         printf("executed region flip\n");
1843         
1844         return OPERATOR_FINISHED;
1845 }
1846
1847 static void testfunc(bContext *C, void *argv, int arg)
1848 {
1849         printf("arg %d\n", arg);
1850 }
1851
1852 static void newlevel1(bContext *C, uiMenuItem *head, void *arg)
1853 {
1854         uiMenuFunc(head, testfunc, NULL);
1855         
1856         uiMenuItemVal(head, "First", ICON_PROP_ON, 1);
1857         uiMenuItemVal(head, "Second", ICON_PROP_CON, 2);
1858         uiMenuItemVal(head, "Third", ICON_SMOOTHCURVE, 3);
1859         uiMenuItemVal(head, "Fourth", ICON_SHARPCURVE, 4);      
1860 }
1861
1862 static int testing123(bContext *C, wmOperator *op, wmEvent *event)
1863 {
1864         uiMenuItem *head= uiPupMenuBegin("Hello world", 0);
1865         
1866         uiMenuContext(head, WM_OP_EXEC_DEFAULT);
1867         uiMenuItemO(head, ICON_PROP_ON, "SCREEN_OT_region_flip");
1868         uiMenuItemO(head, ICON_PROP_CON, "SCREEN_OT_screen_full_area");
1869         uiMenuItemO(head, ICON_SMOOTHCURVE, "SCREEN_OT_region_foursplit");
1870         uiMenuLevel(head, "Submenu", newlevel1);
1871         
1872         uiPupMenuEnd(C, head);
1873         
1874         /* this operator is only for a menu, not used further */
1875         return OPERATOR_CANCELLED;
1876 }
1877
1878 void SCREEN_OT_region_flip(wmOperatorType *ot)
1879 {
1880         /* identifiers */
1881         ot->name= "Flip Region";
1882         ot->idname= "SCREEN_OT_region_flip";
1883         
1884         /* api callbacks */
1885         ot->invoke= testing123; // XXX WM_operator_confirm;
1886         ot->exec= region_flip_exec;
1887         
1888         ot->poll= ED_operator_areaactive;
1889         ot->flag= OPTYPE_REGISTER;
1890         
1891         RNA_def_int(ot->srna, "test", 0, INT_MIN, INT_MAX, "test", "", INT_MIN, INT_MAX);
1892
1893 }
1894
1895 /* ****************** anim player, typically with timer ***************** */
1896
1897 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
1898 {
1899         bScreen *screen= CTX_wm_screen(C);
1900         
1901         if(screen->animtimer==event->customdata) {
1902                 Scene *scene= CTX_data_scene(C);
1903                 
1904                 if(scene->audio.flag & AUDIO_SYNC) {
1905                         wmTimer *wt= screen->animtimer;
1906                         int step = floor(wt->duration * FPS);
1907                         scene->r.cfra += step;
1908                         wt->duration -= ((float)step)/FPS;
1909                 }
1910                 else
1911                         scene->r.cfra++;
1912                 
1913                 if (scene->r.psfra) {
1914                         if(scene->r.cfra > scene->r.pefra)
1915                                 scene->r.cfra= scene->r.psfra;
1916                 }
1917                 else {
1918                         if(scene->r.cfra > scene->r.efra)
1919                                 scene->r.cfra= scene->r.sfra;
1920                 }
1921
1922                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1923                 
1924                 return OPERATOR_FINISHED;
1925         }
1926         return OPERATOR_PASS_THROUGH;
1927 }
1928
1929 void SCREEN_OT_animation_play(wmOperatorType *ot)
1930 {
1931         /* identifiers */
1932         ot->name= "Animation player";
1933         ot->idname= "SCREEN_OT_animation_play";
1934         
1935         /* api callbacks */
1936         ot->invoke= screen_animation_play;
1937         
1938         ot->poll= ED_operator_screenactive;
1939         
1940 }
1941
1942 /* ************** border select operator (template) ***************************** */
1943
1944 /* operator state vars used: (added by default WM callbacks)   
1945         xmin, ymin     
1946         xmax, ymax     
1947
1948         customdata: the wmGesture pointer
1949
1950 callbacks:
1951
1952         exec()  has to be filled in by user
1953
1954         invoke() default WM function
1955                          adds modal handler
1956
1957         modal() default WM function 
1958                         accept modal events while doing it, calls exec(), handles ESC and border drawing
1959         
1960         poll()  has to be filled in by user for context
1961 */
1962 #if 0
1963 static int border_select_do(bContext *C, wmOperator *op)
1964 {
1965         int event_type= RNA_int_get(op->ptr, "event_type");
1966         
1967         if(event_type==LEFTMOUSE)
1968                 printf("border select do select\n");
1969         else if(event_type==RIGHTMOUSE)
1970                 printf("border select deselect\n");
1971         else 
1972                 printf("border select do something\n");
1973         
1974         return 1;
1975 }
1976
1977 void SCREEN_OT_border_select(wmOperatorType *ot)
1978 {
1979         /* identifiers */
1980         ot->name= "Border select";
1981         ot->idname= "SCREEN_OT_border_select";
1982         
1983         /* api callbacks */
1984         ot->exec= border_select_do;
1985         ot->invoke= WM_border_select_invoke;
1986         ot->modal= WM_border_select_modal;
1987         
1988         ot->poll= ED_operator_areaactive;
1989         
1990         /* rna */
1991         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
1992         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
1993         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
1994         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
1995         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
1996
1997 }
1998 #endif
1999
2000 /* ****************************** render invoking ***************** */
2001
2002 /* set callbacks, exported to sequence render too. 
2003 Only call in foreground (UI) renders. */
2004
2005 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2006 /* window as the last possible alternative.                                                                        */
2007 static ScrArea *biggest_non_image_area(bContext *C)
2008 {
2009         bScreen *sc= CTX_wm_screen(C);
2010         ScrArea *sa, *big= NULL;
2011         int size, maxsize= 0, bwmaxsize= 0;
2012         short foundwin= 0;
2013         
2014         for(sa= sc->areabase.first; sa; sa= sa->next) {
2015                 if(sa->winx > 10 && sa->winy > 10) {
2016                         size= sa->winx*sa->winy;
2017                         if(sa->spacetype == SPACE_BUTS) {
2018                                 if(foundwin == 0 && size > bwmaxsize) {
2019                                         bwmaxsize= size;
2020                                         big= sa;        
2021                                 }
2022                         }
2023                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2024                                 maxsize= size;
2025                                 big= sa;
2026                                 foundwin= 1;
2027                         }
2028                 }
2029         }
2030         
2031         return big;
2032 }
2033
2034 static ScrArea *biggest_area(bContext *C)
2035 {
2036         bScreen *sc= CTX_wm_screen(C);
2037         ScrArea *sa, *big= NULL;
2038         int size, maxsize= 0;
2039         
2040         for(sa= sc->areabase.first; sa; sa= sa->next) {
2041                 size= sa->winx*sa->winy;
2042                 if(size > maxsize) {
2043                         maxsize= size;
2044                         big= sa;
2045                 }
2046         }
2047         return big;
2048 }
2049
2050
2051 static ScrArea *find_area_showing_r_result(bContext *C)
2052 {
2053         bScreen *sc= CTX_wm_screen(C);
2054         ScrArea *sa;
2055         SpaceImage *sima;
2056         
2057         /* find an imagewindow showing render result */
2058         for(sa=sc->areabase.first; sa; sa= sa->next) {
2059                 if(sa->spacetype==SPACE_IMAGE) {
2060                         sima= sa->spacedata.first;
2061                         if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2062                                 break;
2063                 }
2064         }
2065         return sa;
2066 }
2067
2068 static void screen_set_image_output(bContext *C)
2069 {
2070         ScrArea *sa;
2071         SpaceImage *sima;
2072         
2073         sa= find_area_showing_r_result(C);
2074         
2075         if(sa==NULL) {
2076                 /* find largest open non-image area */
2077                 sa= biggest_non_image_area(C);
2078                 if(sa) {
2079                         ED_area_newspace(C, sa, SPACE_IMAGE);
2080                         sima= sa->spacedata.first;
2081                         
2082                         /* makes ESC go back to prev space */
2083                         sima->flag |= SI_PREVSPACE;
2084                 }
2085                 else {
2086                         /* use any area of decent size */
2087                         sa= biggest_area(C);
2088                         if(sa->spacetype!=SPACE_IMAGE) {
2089                                 // XXX newspace(sa, SPACE_IMAGE);
2090                                 sima= sa->spacedata.first;
2091                                 
2092                                 /* makes ESC go back to prev space */
2093                                 sima->flag |= SI_PREVSPACE;
2094                         }
2095                 }
2096         }
2097         
2098         sima= sa->spacedata.first;
2099         
2100         /* get the correct image, and scale it */
2101         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2102         
2103         if(G.displaymode==2) { // XXX
2104                 if(sa->full==0) {
2105                         sima->flag |= SI_FULLWINDOW;
2106                         
2107                         ed_screen_fullarea(C, sa);
2108                 }
2109         }
2110         
2111 }
2112
2113 /* executes blocking render */
2114 static int screen_render_exec(bContext *C, wmOperator *op)
2115 {
2116         Scene *scene= CTX_data_scene(C);
2117         Render *re= RE_GetRender(scene->id.name);
2118         
2119         if(re==NULL) {
2120                 re= RE_NewRender(scene->id.name);
2121         }
2122         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2123         
2124         if(RNA_boolean_get(op->ptr, "anim"))
2125                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->frame_step);
2126         else
2127                 RE_BlenderFrame(re, scene, scene->r.cfra);
2128         
2129         // no redraw needed, we leave state as we entered it
2130         ED_update_for_newframe(C, 1);
2131         
2132         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2133
2134         return OPERATOR_FINISHED;
2135 }
2136
2137 typedef struct RenderJob {
2138         Scene *scene;
2139         Render *re;
2140         wmWindow *win;
2141         int anim;
2142         Image *image;
2143         ImageUser iuser;
2144         short *stop;
2145         short *do_update;
2146 } RenderJob;
2147
2148 static void render_freejob(void *rjv)
2149 {
2150         RenderJob *rj= rjv;
2151         
2152         MEM_freeN(rj);
2153 }
2154
2155 /* called inside thread! */
2156 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
2157 {
2158         RenderJob *rj= rjv;
2159         ImBuf *ibuf;
2160         float x1, y1, *rectf= NULL;
2161         int ymin, ymax, xmin, xmax;
2162         int rymin, rxmin;
2163         char *rectc;
2164         
2165         ibuf= BKE_image_get_ibuf(rj->image, &rj->iuser);
2166         if(ibuf==NULL) return;
2167
2168         /* if renrect argument, we only refresh scanlines */
2169         if(renrect) {
2170                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
2171                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
2172                         return;
2173                 
2174                 /* xmin here is first subrect x coord, xmax defines subrect width */
2175                 xmin = renrect->xmin + rr->crop;
2176                 xmax = renrect->xmax - xmin - rr->crop;
2177                 if (xmax<2) return;
2178                 
2179                 ymin= renrect->ymin + rr->crop;
2180                 ymax= renrect->ymax - ymin - rr->crop;
2181                 if(ymax<2)
2182                         return;
2183                 renrect->ymin= renrect->ymax;
2184                 
2185         }
2186         else {
2187                 xmin = ymin = rr->crop;
2188                 xmax = rr->rectx - 2*rr->crop;
2189                 ymax = rr->recty - 2*rr->crop;
2190         }
2191         
2192         /* xmin ymin is in tile coords. transform to ibuf */
2193         rxmin= rr->tilerect.xmin + xmin;
2194         if(rxmin >= ibuf->x) return;
2195         rymin= rr->tilerect.ymin + ymin;
2196         if(rymin >= ibuf->y) return;
2197         
2198         if(rxmin + xmax > ibuf->x)
2199                 xmax= ibuf->x - rxmin;
2200         if(rymin + ymax > ibuf->y)
2201                 ymax= ibuf->y - rymin;
2202         
2203         if(xmax < 1 || ymax < 1) return;
2204         
2205         /* find current float rect for display, first case is after composit... still weak */
2206         if(rr->rectf)
2207                 rectf= rr->rectf;
2208         else {
2209                 if(rr->rect32)
2210                         return;
2211                 else {
2212                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
2213                         rectf= rr->renlay->rectf;
2214                 }
2215         }
2216         if(rectf==NULL) return;
2217         
2218         rectf+= 4*(rr->rectx*ymin + xmin);
2219         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
2220
2221         for(y1= 0; y1<ymax; y1++) {
2222                 float *rf= rectf;
2223                 char *rc= rectc;
2224                 
2225                 /* XXX temp. because crop offset */
2226                 if( rectc >= (char *)(ibuf->rect)) {
2227                         for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2228                                 rc[0]= FTOCHAR(rf[0]);
2229                                 rc[1]= FTOCHAR(rf[1]);
2230                                 rc[2]= FTOCHAR(rf[2]);
2231                                 rc[3]= FTOCHAR(rf[3]);
2232                         }
2233                 }
2234                 rectf += 4*rr->rectx;
2235                 rectc += 4*ibuf->x;
2236         }
2237         
2238         /* make jobs timer to send notifier */
2239         *(rj->do_update)= 1;
2240 }
2241
2242 static void render_startjob(void *rjv, short *stop, short *do_update)
2243 {
2244         RenderJob *rj= rjv;
2245         
2246         rj->stop= stop;
2247         rj->do_update= do_update;
2248         
2249         if(rj->anim)
2250                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->frame_step);
2251         else
2252                 RE_BlenderFrame(rj->re, rj->scene, rj->scene->r.cfra);
2253 }
2254
2255 /* called by render, check job 'stop' value or the global */
2256 static int render_breakjob(void *rjv)
2257 {
2258         RenderJob *rj= rjv;
2259         
2260         if(G.afbreek)
2261                 return 1;
2262         if(rj->stop && *(rj->stop))
2263                 return 1;
2264         return 0;
2265 }
2266
2267 /* catch esc */
2268 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
2269 {
2270         /* no running blender, remove handler and pass through */
2271         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
2272            return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
2273         
2274         /* running render */
2275         switch (event->type) {
2276                 case ESCKEY:
2277                         return OPERATOR_RUNNING_MODAL;
2278                         break;
2279         }
2280         return OPERATOR_PASS_THROUGH;
2281 }
2282
2283 /* using context, starts job */
2284 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
2285 {
2286         /* new render clears all callbacks */
2287         Scene *scene= CTX_data_scene(C);
2288         Render *re;
2289         wmJob *steve;
2290         RenderJob *rj;
2291         Image *ima;
2292         
2293         /* only one job at a time */
2294         if(WM_jobs_test(CTX_wm_manager(C), scene))
2295                 return OPERATOR_CANCELLED;
2296         
2297         /* handle UI stuff */
2298         WM_cursor_wait(1);
2299
2300         /* flush multires changes (for sculpt) */
2301         multires_force_update(CTX_data_active_object(C));
2302         
2303         // get editmode results
2304         // store spare
2305         // get view3d layer, local layer, make this nice api call to render
2306         // store spare
2307         
2308         /* ensure at least 1 area shows result */
2309         screen_set_image_output(C);
2310
2311         /* job custom data */
2312         rj= MEM_callocN(sizeof(RenderJob), "render job");
2313         rj->scene= scene;
2314         rj->win= CTX_wm_window(C);
2315         rj->anim= RNA_boolean_get(op->ptr, "anim");
2316         rj->iuser.scene= scene;
2317         rj->iuser.ok= 1;
2318         
2319         /* setup job */
2320         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene);
2321         WM_jobs_customdata(steve, rj, render_freejob);
2322         WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
2323         WM_jobs_callbacks(steve, render_startjob, NULL, NULL);
2324         
2325         /* get a render result image, and make sure it is empty */
2326         ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2327         BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
2328         rj->image= ima;
2329         
2330         /* setup new render */
2331         re= RE_NewRender(scene->id.name);
2332         RE_test_break_cb(re, rj, render_breakjob);
2333         RE_display_draw_cb(re, rj, image_rect_update);
2334         rj->re= re;
2335         G.afbreek= 0;
2336         
2337         //      BKE_report in render!
2338         //      RE_error_cb(re, error_cb);
2339
2340         WM_jobs_start(steve);
2341         
2342         G.afbreek= 0;
2343         
2344         WM_cursor_wait(0);
2345         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2346
2347         /* add modal handler for ESC */
2348         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
2349         
2350         return OPERATOR_RUNNING_MODAL;
2351 }
2352
2353
2354 /* contextual render, using current scene, view3d? */
2355 void SCREEN_OT_render(wmOperatorType *ot)
2356 {
2357         /* identifiers */
2358         ot->name= "Render";
2359         ot->idname= "SCREEN_OT_render";
2360         
2361         /* api callbacks */
2362         ot->invoke= screen_render_invoke;
2363         ot->modal= screen_render_modal;
2364         ot->exec= screen_render_exec;
2365         
2366         ot->poll= ED_operator_screenactive;
2367         
2368         RNA_def_int(ot->srna, "layers", 0, 0, INT_MAX, "Layers", "", 0, INT_MAX);
2369         RNA_def_boolean(ot->srna, "anim", 0, "Animation", "");
2370 }
2371
2372 /* *********************** cancel render viewer *************** */
2373
2374 static int render_view_cancel_exec(bContext *C, wmOperator *unused)
2375 {
2376         ScrArea *sa= CTX_wm_area(C);
2377         SpaceImage *sima= sa->spacedata.first;
2378         
2379         if(sima->flag & SI_PREVSPACE) {
2380                 sima->flag &= ~SI_PREVSPACE;
2381                 
2382                 if(sima->flag & SI_FULLWINDOW) {
2383                         sima->flag &= ~SI_FULLWINDOW;
2384                         ED_screen_full_prevspace(C);
2385                 }
2386                 else
2387                         ED_area_prevspace(C);
2388         }
2389         else if(sima->flag & SI_FULLWINDOW) {
2390                 sima->flag &= ~SI_FULLWINDOW;
2391                 ed_screen_fullarea(C, sa);
2392         }               
2393         
2394         return OPERATOR_FINISHED;
2395 }
2396
2397 void SCREEN_OT_render_view_cancel(struct wmOperatorType *ot)
2398 {
2399         /* identifiers */
2400         ot->name= "Cancel Render View";
2401         ot->idname= "SCREEN_OT_render_view_cancel";
2402         
2403         /* api callbacks */
2404         ot->exec= render_view_cancel_exec;
2405         ot->poll= ED_operator_image_active;
2406 }
2407
2408
2409 /* ****************  Assigning operatortypes to global list, adding handlers **************** */
2410
2411 /* called in spacetypes.c */
2412 void ED_operatortypes_screen(void)
2413 {
2414         /* generic UI stuff */
2415         WM_operatortype_append(SCREEN_OT_actionzone);
2416         WM_operatortype_append(SCREEN_OT_repeat_last);
2417         WM_operatortype_append(SCREEN_OT_repeat_history);
2418         WM_operatortype_append(SCREEN_OT_redo_last);
2419         
2420         /* screen tools */
2421         WM_operatortype_append(SCREEN_OT_area_move);
2422         WM_operatortype_append(SCREEN_OT_area_split);
2423         WM_operatortype_append(SCREEN_OT_area_join);
2424         WM_operatortype_append(SCREEN_OT_area_dupli);
2425         WM_operatortype_append(SCREEN_OT_area_swap);
2426         WM_operatortype_append(SCREEN_OT_region_split);
2427         WM_operatortype_append(SCREEN_OT_region_foursplit);
2428         WM_operatortype_append(SCREEN_OT_region_flip);
2429         WM_operatortype_append(SCREEN_OT_screen_set);
2430         WM_operatortype_append(SCREEN_OT_screen_full_area);
2431         WM_operatortype_append(SCREEN_OT_screenshot);
2432         WM_operatortype_append(SCREEN_OT_screencast);
2433         
2434         /*frame changes*/
2435         WM_operatortype_append(SCREEN_OT_frame_offset);
2436         WM_operatortype_append(SCREEN_OT_animation_play);
2437         
2438         /* render */
2439         WM_operatortype_append(SCREEN_OT_render);
2440         WM_operatortype_append(SCREEN_OT_render_view_cancel);
2441         
2442         /* tools shared by more space types */
2443         WM_operatortype_append(ED_OT_undo);
2444         WM_operatortype_append(ED_OT_redo);     
2445         
2446 }
2447
2448 /* called in spacetypes.c */
2449 void ED_keymap_screen(wmWindowManager *wm)
2450 {
2451         ListBase *keymap= WM_keymap_listbase(wm, "Screen", 0, 0);
2452         
2453         /* standard timers */
2454         WM_keymap_add_item(keymap, "SCREEN_OT_animation_play", TIMER0, KM_ANY, KM_ANY, 0);
2455         
2456         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_actionzone", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "modifier", 0);
2457         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_actionzone", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "modifier", 1);
2458         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_actionzone", LEFTMOUSE, KM_PRESS, KM_ALT, 0)->ptr, "modifier", 2);
2459         
2460         /* screen tools */
2461         WM_keymap_verify_item(keymap, "SCREEN_OT_area_move", LEFTMOUSE, KM_PRESS, 0, 0);
2462         WM_keymap_verify_item(keymap, "SCREEN_OT_area_split", EVT_ACTIONZONE, 0, 0, 0);
2463         WM_keymap_verify_item(keymap, "SCREEN_OT_area_join", EVT_ACTIONZONE, 0, 0, 0);
2464         WM_keymap_verify_item(keymap, "SCREEN_OT_area_dupli", EVT_ACTIONZONE, 0, KM_SHIFT, 0);
2465         WM_keymap_verify_item(keymap, "SCREEN_OT_area_swap", EVT_ACTIONZONE, 0, KM_ALT, 0);
2466         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1);
2467         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1);
2468         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", UPARROWKEY, KM_PRESS, KM_CTRL, 0);
2469         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", DOWNARROWKEY, KM_PRESS, KM_CTRL, 0);
2470         WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", SPACEKEY, KM_PRESS, KM_CTRL, 0);
2471         WM_keymap_add_item(keymap, "SCREEN_OT_screenshot", F3KEY, KM_PRESS, KM_CTRL, 0);
2472         WM_keymap_add_item(keymap, "SCREEN_OT_screencast", F3KEY, KM_PRESS, KM_ALT, 0);
2473
2474          /* tests */
2475         WM_keymap_add_item(keymap, "SCREEN_OT_region_split", SKEY, KM_PRESS, KM_CTRL|KM_ALT, 0);
2476         WM_keymap_add_item(keymap, "SCREEN_OT_region_foursplit", SKEY, KM_PRESS, KM_CTRL|KM_ALT|KM_SHIFT, 0);
2477         
2478         WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_history", F3KEY, KM_PRESS, 0, 0);
2479         WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_last", F4KEY, KM_PRESS, 0, 0);
2480         WM_keymap_add_item(keymap, "SCREEN_OT_region_flip", F5KEY, KM_PRESS, 0, 0);
2481         WM_keymap_verify_item(keymap, "SCREEN_OT_redo_last", F6KEY, KM_PRESS, 0, 0);
2482
2483         /* files */
2484         WM_keymap_add_item(keymap, "FILE_OT_exec", RETKEY, KM_PRESS, 0, 0);
2485         WM_keymap_add_item(keymap, "FILE_OT_cancel", ESCKEY, KM_PRESS, 0, 0);
2486         
2487         /* undo */
2488         WM_keymap_add_item(keymap, "ED_OT_undo", ZKEY, KM_PRESS, KM_CTRL, 0);
2489         WM_keymap_add_item(keymap, "ED_OT_undo", ZKEY, KM_PRESS, KM_OSKEY, 0);
2490         WM_keymap_add_item(keymap, "ED_OT_redo", ZKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0);
2491         WM_keymap_add_item(keymap, "ED_OT_redo", ZKEY, KM_PRESS, KM_SHIFT|KM_OSKEY, 0);
2492                                                   
2493         /* render */
2494         WM_keymap_add_item(keymap, "SCREEN_OT_render", F12KEY, KM_PRESS, 0, 0);
2495         WM_keymap_add_item(keymap, "SCREEN_OT_render_view_cancel", ESCKEY, KM_PRESS, 0, 0);
2496         
2497         /* frame offsets */
2498         keymap= WM_keymap_listbase(wm, "Frames", 0, 0);
2499         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 10);
2500         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -10);
2501         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -1);
2502         RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 1);
2503         
2504 }
2505