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