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