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