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