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