camera switching via markers
[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 #include <string.h>
29
30 #include <GL/glew.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_math.h"
35 #include "BLI_blenlib.h"
36 #include "BLI_editVert.h"
37 #include "BLI_dlrbTree.h"
38
39 #include "DNA_armature_types.h"
40 #include "DNA_image_types.h"
41 #include "DNA_lattice_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_mesh_types.h"
44 #include "DNA_curve_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_meta_types.h"
47 #include "DNA_view3d_types.h"
48
49 #include "BKE_blender.h"
50 #include "BKE_colortools.h"
51 #include "BKE_context.h"
52 #include "BKE_customdata.h"
53 #include "BKE_global.h"
54 #include "BKE_image.h"
55 #include "BKE_idprop.h"
56 #include "BKE_library.h"
57 #include "BKE_main.h"
58 #include "BKE_mesh.h"
59 #include "BKE_multires.h"
60 #include "BKE_report.h"
61 #include "BKE_scene.h"
62 #include "BKE_screen.h"
63 #include "BKE_utildefines.h"
64 #include "BKE_sound.h"
65 #include "BKE_writeavi.h"
66
67 #include "WM_api.h"
68 #include "WM_types.h"
69
70 #include "ED_util.h"
71 #include "ED_screen.h"
72 #include "ED_mesh.h"
73 #include "ED_object.h"
74 #include "ED_screen_types.h"
75 #include "ED_keyframes_draw.h"
76 #include "ED_view3d.h"
77
78 #include "RE_pipeline.h"
79 #include "IMB_imbuf.h"
80 #include "IMB_imbuf_types.h"
81
82 #include "RNA_access.h"
83 #include "RNA_define.h"
84
85 #include "UI_interface.h"
86 #include "UI_resources.h"
87
88 #include "GPU_extensions.h"
89
90 #include "wm_window.h"
91
92 #include "screen_intern.h"      /* own module include */
93
94 #define KM_MODAL_CANCEL         1
95 #define KM_MODAL_APPLY          2
96 #define KM_MODAL_STEP10         3
97 #define KM_MODAL_STEP10_OFF     4
98
99 /* ************** Exported Poll tests ********************** */
100
101 int ED_operator_regionactive(bContext *C)
102 {
103         if(CTX_wm_window(C)==NULL) return 0;
104         if(CTX_wm_screen(C)==NULL) return 0;
105         if(CTX_wm_region(C)==NULL) return 0;
106         return 1;
107 }
108
109 int ED_operator_areaactive(bContext *C)
110 {
111         if(CTX_wm_window(C)==NULL) return 0;
112         if(CTX_wm_screen(C)==NULL) return 0;
113         if(CTX_wm_area(C)==NULL) return 0;
114         return 1;
115 }
116
117 int ED_operator_screenactive(bContext *C)
118 {
119         if(CTX_wm_window(C)==NULL) return 0;
120         if(CTX_wm_screen(C)==NULL) return 0;
121         return 1;
122 }
123
124 /* when mouse is over area-edge */
125 int ED_operator_screen_mainwinactive(bContext *C)
126 {
127         if(CTX_wm_window(C)==NULL) return 0;
128         if(CTX_wm_screen(C)==NULL) return 0;
129         if (CTX_wm_screen(C)->subwinactive!=CTX_wm_screen(C)->mainwin) return 0;
130         return 1;
131 }
132
133 int ED_operator_scene_editable(bContext *C)
134 {
135         Scene *scene= CTX_data_scene(C);
136         if(scene && scene->id.lib==NULL)
137                 return 1;
138         return 0;
139 }
140
141 static int ed_spacetype_test(bContext *C, int type)
142 {
143         if(ED_operator_areaactive(C)) {
144                 SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
145                 return sl && (sl->spacetype == type);
146         }
147         return 0;
148 }
149
150 int ED_operator_view3d_active(bContext *C)
151 {
152         return ed_spacetype_test(C, SPACE_VIEW3D);
153 }
154
155 int ED_operator_timeline_active(bContext *C)
156 {
157         return ed_spacetype_test(C, SPACE_TIME);
158 }
159
160 int ED_operator_outliner_active(bContext *C)
161 {
162         return ed_spacetype_test(C, SPACE_OUTLINER);
163 }
164
165 int ED_operator_file_active(bContext *C)
166 {
167         return ed_spacetype_test(C, SPACE_FILE);
168 }
169
170 int ED_operator_action_active(bContext *C)
171 {
172         return ed_spacetype_test(C, SPACE_ACTION);
173 }
174
175 int ED_operator_buttons_active(bContext *C)
176 {
177         return ed_spacetype_test(C, SPACE_BUTS);
178 }
179
180 int ED_operator_node_active(bContext *C)
181 {
182         SpaceNode *snode= CTX_wm_space_node(C);
183         
184         if(snode && snode->edittree)
185                 return 1;
186         
187         return 0;
188 }
189
190 // XXX rename
191 int ED_operator_ipo_active(bContext *C)
192 {
193         return ed_spacetype_test(C, SPACE_IPO);
194 }
195
196 int ED_operator_sequencer_active(bContext *C)
197 {
198         return ed_spacetype_test(C, SPACE_SEQ);
199 }
200
201 int ED_operator_image_active(bContext *C)
202 {
203         return ed_spacetype_test(C, SPACE_IMAGE);
204 }
205
206 int ED_operator_nla_active(bContext *C)
207 {
208         return ed_spacetype_test(C, SPACE_NLA);
209 }
210
211 int ED_operator_logic_active(bContext *C)
212 {
213         return ed_spacetype_test(C, SPACE_LOGIC);
214 }
215
216 int ED_operator_object_active(bContext *C)
217 {
218         return NULL != CTX_data_active_object(C);
219 }
220
221 int ED_operator_object_active_editable(bContext *C)
222 {
223         Object *ob=CTX_data_active_object(C);
224         return ((ob != NULL) && !(ob->id.lib));
225 }
226
227 int ED_operator_editmesh(bContext *C)
228 {
229         Object *obedit= CTX_data_edit_object(C);
230         if(obedit && obedit->type==OB_MESH)
231                 return NULL != ((Mesh *)obedit->data)->edit_mesh;
232         return 0;
233 }
234
235 int ED_operator_editmesh_view3d(bContext *C)
236 {
237         return ED_operator_editmesh(C) && ED_operator_view3d_active(C);
238 }
239
240 int ED_operator_editarmature(bContext *C)
241 {
242         Object *obedit= CTX_data_edit_object(C);
243         if(obedit && obedit->type==OB_ARMATURE)
244                 return NULL != ((bArmature *)obedit->data)->edbo;
245         return 0;
246 }
247
248 int ED_operator_posemode(bContext *C)
249 {
250         Object *obact= CTX_data_active_object(C);
251         Object *obedit= CTX_data_edit_object(C);
252         
253         if ((obact != obedit) && (obact) && (obact->type==OB_ARMATURE))
254                 return (obact->mode & OB_MODE_POSE)!=0;
255         
256         return 0;
257 }
258
259
260 int ED_operator_uvedit(bContext *C)
261 {
262         Object *obedit= CTX_data_edit_object(C);
263         EditMesh *em= NULL;
264         
265         if(obedit && obedit->type==OB_MESH)
266                 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
267         
268         if(em && (em->faces.first) && (CustomData_has_layer(&em->fdata, CD_MTFACE))) {
269                 BKE_mesh_end_editmesh(obedit->data, em);
270                 return 1;
271         }
272         
273         if(obedit)
274                 BKE_mesh_end_editmesh(obedit->data, em);
275         return 0;
276 }
277
278 int ED_operator_uvmap(bContext *C)
279 {
280         Object *obedit= CTX_data_edit_object(C);
281         EditMesh *em= NULL;
282         
283         if(obedit && obedit->type==OB_MESH)
284                 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
285         
286         if(em && (em->faces.first)) {
287                 BKE_mesh_end_editmesh(obedit->data, em);
288                 return 1;
289         }
290         
291         if(obedit)
292                 BKE_mesh_end_editmesh(obedit->data, em);
293         return 0;
294 }
295
296 int ED_operator_editsurfcurve(bContext *C)
297 {
298         Object *obedit= CTX_data_edit_object(C);
299         if(obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
300                 return NULL != ((Curve *)obedit->data)->editnurb;
301         return 0;
302 }
303
304
305 int ED_operator_editcurve(bContext *C)
306 {
307         Object *obedit= CTX_data_edit_object(C);
308         if(obedit && obedit->type==OB_CURVE)
309                 return NULL != ((Curve *)obedit->data)->editnurb;
310         return 0;
311 }
312
313 int ED_operator_editsurf(bContext *C)
314 {
315         Object *obedit= CTX_data_edit_object(C);
316         if(obedit && obedit->type==OB_SURF)
317                 return NULL != ((Curve *)obedit->data)->editnurb;
318         return 0;
319 }
320
321 int ED_operator_editfont(bContext *C)
322 {
323         Object *obedit= CTX_data_edit_object(C);
324         if(obedit && obedit->type==OB_FONT)
325                 return NULL != ((Curve *)obedit->data)->editfont;
326         return 0;
327 }
328
329 int ED_operator_editlattice(bContext *C)
330 {
331         Object *obedit= CTX_data_edit_object(C);
332         if(obedit && obedit->type==OB_LATTICE)
333                 return NULL != ((Lattice *)obedit->data)->editlatt;
334         return 0;
335 }
336
337 int ED_operator_editmball(bContext *C)
338 {
339         Object *obedit= CTX_data_edit_object(C);
340         if(obedit && obedit->type==OB_MBALL)
341                 return NULL != ((MetaBall *)obedit->data)->editelems;
342         return 0;
343 }
344
345 /* *************************** action zone operator ************************** */
346
347 /* operator state vars used:  
348  none
349  
350  functions:
351  
352  apply() set actionzone event
353  
354  exit() free customdata
355  
356  callbacks:
357  
358  exec() never used
359  
360  invoke() check if in zone  
361  add customdata, put mouseco and area in it
362  add modal handler
363  
364  modal()        accept modal events while doing it
365  call apply() with gesture info, active window, nonactive window
366  call exit() and remove handler when LMB confirm
367  
368  */
369
370 typedef struct sActionzoneData {
371         ScrArea *sa1, *sa2;
372         AZone *az;
373         int x, y, gesture_dir, modifier;
374 } sActionzoneData;
375
376 /* used by other operators too */
377 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
378 {
379         ScrArea *sa= NULL;
380         sa= scr->areabase.first;
381         while(sa) {
382                 if(BLI_in_rcti(&sa->totrct, x, y)) break;
383                 sa= sa->next;
384         }
385         
386         return sa;
387 }
388
389 /* quick poll to save operators to be created and handled */
390 static int actionzone_area_poll(bContext *C)
391 {
392         wmWindow *win= CTX_wm_window(C);
393         ScrArea *sa= CTX_wm_area(C);
394         
395         if(sa && win) {
396                 AZone *az;
397                 int x= win->eventstate->x;
398                 int y= win->eventstate->y;
399                 
400                 for(az= sa->actionzones.first; az; az= az->next)
401                         if(BLI_in_rcti(&az->rect, x, y))
402                                 return 1;
403         }       
404         return 0;
405 }
406
407 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
408 {
409         AZone *az= NULL;
410         
411         for(az= sa->actionzones.first; az; az= az->next) {
412                 if(BLI_in_rcti(&az->rect, x, y)) {
413                         if(az->type == AZONE_AREA) {
414                                 if(isect_point_tri_v2_int(az->x1, az->y1, az->x2, az->y2, x, y)) 
415                                         break;
416                         }
417                         else if(az->type == AZONE_REGION) {
418                                 break;
419                         }
420                 }
421         }
422         
423         return az;
424 }
425
426
427 static void actionzone_exit(bContext *C, wmOperator *op)
428 {
429         if(op->customdata)
430                 MEM_freeN(op->customdata);
431         op->customdata= NULL;
432 }
433
434 /* send EVT_ACTIONZONE event */
435 static void actionzone_apply(bContext *C, wmOperator *op, int type)
436 {
437         wmEvent event;
438         wmWindow *win= CTX_wm_window(C);
439         sActionzoneData *sad= op->customdata;
440         
441         sad->modifier= RNA_int_get(op->ptr, "modifier");
442         
443         event= *(win->eventstate);      /* XXX huh huh? make api call */
444         if(type==AZONE_AREA)
445                 event.type= EVT_ACTIONZONE_AREA;
446         else
447                 event.type= EVT_ACTIONZONE_REGION;
448         event.customdata= op->customdata;
449         event.customdatafree= TRUE;
450         op->customdata= NULL;
451         
452         wm_event_add(win, &event);
453 }
454
455 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
456 {
457         AZone *az= is_in_area_actionzone(CTX_wm_area(C), event->x, event->y);
458         sActionzoneData *sad;
459         
460         /* quick escape */
461         if(az==NULL)
462                 return OPERATOR_PASS_THROUGH;
463         
464         /* ok we do the actionzone */
465         sad= op->customdata= MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
466         sad->sa1= CTX_wm_area(C);
467         sad->az= az;
468         sad->x= event->x; sad->y= event->y;
469         
470         /* region azone directly reacts on mouse clicks */
471         if(sad->az->type==AZONE_REGION) {
472                 actionzone_apply(C, op, AZONE_REGION);
473                 actionzone_exit(C, op);
474                 return OPERATOR_FINISHED;
475         }
476         else {
477                 /* add modal handler */
478                 WM_event_add_modal_handler(C, op);
479                 
480                 return OPERATOR_RUNNING_MODAL;
481         }
482 }
483
484
485 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
486 {
487         sActionzoneData *sad= op->customdata;
488         int deltax, deltay;
489         int mindelta= sad->az->type==AZONE_REGION?1:12;
490         
491         switch(event->type) {
492                 case MOUSEMOVE:
493                         /* calculate gesture direction */
494                         deltax= (event->x - sad->x);
495                         deltay= (event->y - sad->y);
496                         
497                         if(deltay > ABS(deltax))
498                                 sad->gesture_dir= 'n';
499                         else if(deltax > ABS(deltay))
500                                 sad->gesture_dir= 'e';
501                         else if(deltay < -ABS(deltax))
502                                 sad->gesture_dir= 's';
503                         else
504                                 sad->gesture_dir= 'w';
505                         
506                         /* gesture is large enough? */
507                         if(ABS(deltax) > mindelta || ABS(deltay) > mindelta) {
508                                 
509                                 /* second area, for join */
510                                 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
511                                 /* apply sends event */
512                                 actionzone_apply(C, op, sad->az->type);
513                                 actionzone_exit(C, op);
514                                 
515                                 return OPERATOR_FINISHED;
516                         }
517                         break;
518                 case ESCKEY:
519                         actionzone_exit(C, op);
520                         return OPERATOR_CANCELLED;
521                 case LEFTMOUSE:                         
522                         actionzone_exit(C, op);
523                         return OPERATOR_CANCELLED;
524                         
525         }
526         
527         return OPERATOR_RUNNING_MODAL;
528 }
529
530 static void SCREEN_OT_actionzone(wmOperatorType *ot)
531 {
532         /* identifiers */
533         ot->name= "Handle area action zones";
534         ot->description= "Handle area action zones for mouse actions/gestures.";
535         ot->idname= "SCREEN_OT_actionzone";
536         
537         ot->invoke= actionzone_invoke;
538         ot->modal= actionzone_modal;
539         ot->poll= actionzone_area_poll;
540         
541         ot->flag= OPTYPE_BLOCKING;
542         
543         RNA_def_int(ot->srna, "modifier", 0, 0, 2, "modifier", "modifier state", 0, 2);
544 }
545
546 /* ************** swap area operator *********************************** */
547
548 /* operator state vars used:  
549  sa1            start area
550  sa2            area to swap with
551  
552  functions:
553  
554  init()   set custom data for operator, based on actionzone event custom data
555  
556  cancel()       cancel the operator
557  
558  exit() cleanup, send notifier
559  
560  callbacks:
561  
562  invoke() gets called on shift+lmb drag in actionzone
563  call init(), add handler
564  
565  modal()  accept modal events while doing it
566  
567  */
568
569 typedef struct sAreaSwapData {
570         ScrArea *sa1, *sa2;
571 } sAreaSwapData;
572
573 static int area_swap_init(bContext *C, wmOperator *op, wmEvent *event)
574 {
575         sAreaSwapData *sd= NULL;
576         sActionzoneData *sad= event->customdata;
577         
578         if(sad==NULL || sad->sa1==NULL)
579                 return 0;
580         
581         sd= MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
582         sd->sa1= sad->sa1;
583         sd->sa2= sad->sa2;
584         op->customdata= sd;
585         
586         return 1;
587 }
588
589
590 static void area_swap_exit(bContext *C, wmOperator *op)
591 {
592         if(op->customdata)
593                 MEM_freeN(op->customdata);
594         op->customdata= NULL;
595 }
596
597 static int area_swap_cancel(bContext *C, wmOperator *op)
598 {
599         area_swap_exit(C, op);
600         return OPERATOR_CANCELLED;
601 }
602
603 static int area_swap_invoke(bContext *C, wmOperator *op, wmEvent *event)
604 {
605         
606         if(!area_swap_init(C, op, event))
607                 return OPERATOR_PASS_THROUGH;
608         
609         /* add modal handler */
610         WM_cursor_modal(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
611         WM_event_add_modal_handler(C, op);
612         
613         return OPERATOR_RUNNING_MODAL;
614         
615 }
616
617 static int area_swap_modal(bContext *C, wmOperator *op, wmEvent *event)
618 {
619         sActionzoneData *sad= op->customdata;
620         
621         switch(event->type) {
622                 case MOUSEMOVE:
623                         /* second area, for join */
624                         sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
625                         break;
626                 case LEFTMOUSE: /* release LMB */
627                         if(event->val==KM_RELEASE) {
628                                 if(!sad->sa2 || sad->sa1 == sad->sa2) {
629                                         
630                                         return area_swap_cancel(C, op);
631                                 }
632                                 ED_area_swapspace(C, sad->sa1, sad->sa2);
633                                 
634                                 area_swap_exit(C, op);
635                                 
636                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
637                                 
638                                 return OPERATOR_FINISHED;
639                         }
640                         break;
641                         
642                 case ESCKEY:
643                         return area_swap_cancel(C, op);
644         }
645         return OPERATOR_RUNNING_MODAL;
646 }
647
648 static void SCREEN_OT_area_swap(wmOperatorType *ot)
649 {
650         ot->name= "Swap areas";
651         ot->description= "Swap selected areas screen positions.";
652         ot->idname= "SCREEN_OT_area_swap";
653         
654         ot->invoke= area_swap_invoke;
655         ot->modal= area_swap_modal;
656         ot->poll= ED_operator_areaactive;
657         
658         ot->flag= OPTYPE_BLOCKING;
659 }
660
661 /* *********** Duplicate area as new window operator ****************** */
662
663 /* operator callback */
664 static int area_dupli_invoke(bContext *C, wmOperator *op, wmEvent *event)
665 {
666         wmWindow *newwin, *win;
667         bScreen *newsc, *sc;
668         ScrArea *sa;
669         rcti rect;
670         
671         win= CTX_wm_window(C);
672         sc= CTX_wm_screen(C);
673         sa= CTX_wm_area(C);
674         
675         /* XXX hrmf! */
676         if(event->type==EVT_ACTIONZONE_AREA) {
677                 sActionzoneData *sad= event->customdata;
678                 
679                 if(sad==NULL)
680                         return OPERATOR_PASS_THROUGH;
681                 
682                 sa= sad->sa1;
683         }
684         
685         /*  poll() checks area context, but we don't accept full-area windows */
686         if(sc->full != SCREENNORMAL) {
687                 if(event->type==EVT_ACTIONZONE_AREA)
688                         actionzone_exit(C, op);
689                 return OPERATOR_CANCELLED;
690         }
691         
692         /* adds window to WM */
693         rect= sa->totrct;
694         BLI_translate_rcti(&rect, win->posx, win->posy);
695         newwin= WM_window_open(C, &rect);
696         
697         /* allocs new screen and adds to newly created window, using window size */
698         newsc= ED_screen_add(newwin, CTX_data_scene(C), sc->id.name+2);
699         newwin->screen= newsc;
700         
701         /* copy area to new screen */
702         area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
703         
704         /* screen, areas init */
705         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
706         
707         if(event->type==EVT_ACTIONZONE_AREA)
708                 actionzone_exit(C, op);
709         
710         return OPERATOR_FINISHED;
711 }
712
713 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
714 {
715         ot->name= "Duplicate Area into New Window";
716         ot->description= "Duplicate selected area into new window.";
717         ot->idname= "SCREEN_OT_area_dupli";
718         
719         ot->invoke= area_dupli_invoke;
720         ot->poll= ED_operator_areaactive;
721 }
722
723
724 /* ************** move area edge operator *********************************** */
725
726 /* operator state vars used:  
727  x, y                           mouse coord near edge
728  delta            movement of edge
729  
730  functions:
731  
732  init()   set default property values, find edge based on mouse coords, test
733  if the edge can be moved, select edges, calculate min and max movement
734  
735  apply()        apply delta on selection
736  
737  exit() cleanup, send notifier
738  
739  cancel() cancel moving
740  
741  callbacks:
742  
743  exec()   execute without any user interaction, based on properties
744  call init(), apply(), exit()
745  
746  invoke() gets called on mouse click near edge
747  call init(), add handler
748  
749  modal()  accept modal events while doing it
750  call apply() with delta motion
751  call exit() and remove handler
752  
753  */
754
755 typedef struct sAreaMoveData {
756         int bigger, smaller, origval, step;
757         char dir;
758 } sAreaMoveData;
759
760 /* helper call to move area-edge, sets limits */
761 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
762 {
763         ScrArea *sa;
764         
765         /* we check all areas and test for free space with MINSIZE */
766         *bigger= *smaller= 100000;
767         
768         for(sa= sc->areabase.first; sa; sa= sa->next) {
769                 if(dir=='h') {
770                         int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
771                         
772                         /* if top or down edge selected, test height */
773                         if(sa->v1->flag && sa->v4->flag)
774                                 *bigger= MIN2(*bigger, y1);
775                         else if(sa->v2->flag && sa->v3->flag)
776                                 *smaller= MIN2(*smaller, y1);
777                 }
778                 else {
779                         int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
780                         
781                         /* if left or right edge selected, test width */
782                         if(sa->v1->flag && sa->v2->flag)
783                                 *bigger= MIN2(*bigger, x1);
784                         else if(sa->v3->flag && sa->v4->flag)
785                                 *smaller= MIN2(*smaller, x1);
786                 }
787         }
788 }
789
790 /* validate selection inside screen, set variables OK */
791 /* return 0: init failed */
792 static int area_move_init (bContext *C, wmOperator *op)
793 {
794         bScreen *sc= CTX_wm_screen(C);
795         ScrEdge *actedge;
796         sAreaMoveData *md;
797         int x, y;
798         
799         /* required properties */
800         x= RNA_int_get(op->ptr, "x");
801         y= RNA_int_get(op->ptr, "y");
802         
803         /* setup */
804         actedge= screen_find_active_scredge(sc, x, y);
805         if(actedge==NULL) return 0;
806         
807         md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
808         op->customdata= md;
809         
810         md->dir= scredge_is_horizontal(actedge)?'h':'v';
811         if(md->dir=='h') md->origval= actedge->v1->vec.y;
812         else md->origval= actedge->v1->vec.x;
813         
814         select_connected_scredge(sc, actedge);
815         /* now all vertices with 'flag==1' are the ones that can be moved. */
816         
817         area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
818         
819         return 1;
820 }
821
822 /* moves selected screen edge amount of delta, used by split & move */
823 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
824 {
825         wmWindow *win= CTX_wm_window(C);
826         bScreen *sc= CTX_wm_screen(C);
827         ScrVert *v1;
828         
829         delta= CLAMPIS(delta, -smaller, bigger);
830         
831         for (v1= sc->vertbase.first; v1; v1= v1->next) {
832                 if (v1->flag) {
833                         /* that way a nice AREAGRID  */
834                         if((dir=='v') && v1->vec.x>0 && v1->vec.x<win->sizex-1) {
835                                 v1->vec.x= origval + delta;
836                                 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
837                         }
838                         if((dir=='h') && v1->vec.y>0 && v1->vec.y<win->sizey-1) {
839                                 v1->vec.y= origval + delta;
840                                 
841                                 v1->vec.y+= AREAGRID-1;
842                                 v1->vec.y-= (v1->vec.y % AREAGRID);
843                                 
844                                 /* prevent too small top header */
845                                 if(v1->vec.y > win->sizey-AREAMINY)
846                                         v1->vec.y= win->sizey-AREAMINY;
847                         }
848                 }
849         }
850         
851         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
852 }
853
854 static void area_move_apply(bContext *C, wmOperator *op)
855 {
856         sAreaMoveData *md= op->customdata;
857         int delta;
858         
859         delta= RNA_int_get(op->ptr, "delta");
860         area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
861 }
862
863 static void area_move_exit(bContext *C, wmOperator *op)
864 {
865         if(op->customdata)
866                 MEM_freeN(op->customdata);
867         op->customdata= NULL;
868         
869         /* this makes sure aligned edges will result in aligned grabbing */
870         removedouble_scrverts(CTX_wm_screen(C));
871         removedouble_scredges(CTX_wm_screen(C));
872 }
873
874 static int area_move_exec(bContext *C, wmOperator *op)
875 {
876         if(!area_move_init(C, op))
877                 return OPERATOR_CANCELLED;
878         
879         area_move_apply(C, op);
880         area_move_exit(C, op);
881         
882         return OPERATOR_FINISHED;
883 }
884
885 /* interaction callback */
886 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
887 {
888         RNA_int_set(op->ptr, "x", event->x);
889         RNA_int_set(op->ptr, "y", event->y);
890         
891         if(!area_move_init(C, op)) 
892                 return OPERATOR_PASS_THROUGH;
893         
894         /* add temp handler */
895         WM_event_add_modal_handler(C, op);
896         
897         return OPERATOR_RUNNING_MODAL;
898 }
899
900 static int area_move_cancel(bContext *C, wmOperator *op)
901 {
902         
903         RNA_int_set(op->ptr, "delta", 0);
904         area_move_apply(C, op);
905         area_move_exit(C, op);
906         
907         return OPERATOR_CANCELLED;
908 }
909
910 /* modal callback for while moving edges */
911 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
912 {
913         sAreaMoveData *md= op->customdata;
914         int delta, x, y;
915         
916         /* execute the events */
917         switch(event->type) {
918                 case MOUSEMOVE:
919                         
920                         x= RNA_int_get(op->ptr, "x");
921                         y= RNA_int_get(op->ptr, "y");
922                         
923                         delta= (md->dir == 'v')? event->x - x: event->y - y;
924                         if(md->step) delta= delta - (delta % md->step);
925                         RNA_int_set(op->ptr, "delta", delta);
926                         
927                         area_move_apply(C, op);
928                         break;
929                         
930                 case EVT_MODAL_MAP:
931                         
932                         switch (event->val) {
933                                 case KM_MODAL_APPLY:
934                                         area_move_exit(C, op);
935                                         return OPERATOR_FINISHED;
936                                         
937                                 case KM_MODAL_CANCEL:
938                                         return area_move_cancel(C, op);
939                                         
940                                 case KM_MODAL_STEP10:
941                                         md->step= 10;
942                                         break;
943                                 case KM_MODAL_STEP10_OFF:
944                                         md->step= 0;
945                                         break;
946                         }
947         }
948         
949         return OPERATOR_RUNNING_MODAL;
950 }
951
952 static void SCREEN_OT_area_move(wmOperatorType *ot)
953 {
954         /* identifiers */
955         ot->name= "Move area edges";
956         ot->description= "Move selected area edges.";
957         ot->idname= "SCREEN_OT_area_move";
958         
959         ot->exec= area_move_exec;
960         ot->invoke= area_move_invoke;
961         ot->cancel= area_move_cancel;
962         ot->modal= area_move_modal;
963         ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
964         
965         ot->flag= OPTYPE_BLOCKING;
966         
967         /* rna */
968         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
969         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
970         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
971 }
972
973 /* ************** split area operator *********************************** */
974
975 /* 
976  operator state vars:  
977  fac              spit point
978  dir              direction 'v' or 'h'
979  
980  operator customdata:
981  area                           pointer to (active) area
982  x, y                   last used mouse pos
983  (more, see below)
984  
985  functions:
986  
987  init()   set default property values, find area based on context
988  
989  apply()        split area based on state vars
990  
991  exit() cleanup, send notifier
992  
993  cancel() remove duplicated area
994  
995  callbacks:
996  
997  exec()   execute without any user interaction, based on state vars
998  call init(), apply(), exit()
999  
1000  invoke() gets called on mouse click in action-widget
1001  call init(), add modal handler
1002  call apply() with initial motion
1003  
1004  modal()  accept modal events while doing it
1005  call move-areas code with delta motion
1006  call exit() or cancel() and remove handler
1007  
1008  */
1009
1010 #define SPLIT_STARTED   1
1011 #define SPLIT_PROGRESS  2
1012
1013 typedef struct sAreaSplitData
1014         {
1015                 int x, y;       /* last used mouse position */
1016                 
1017                 int origval;                    /* for move areas */
1018                 int bigger, smaller;    /* constraints for moving new edge */
1019                 int delta;                              /* delta move edge */
1020                 int origmin, origsize;  /* to calculate fac, for property storage */
1021                 
1022                 ScrEdge *nedge;                 /* new edge */
1023                 ScrArea *sarea;                 /* start area */
1024                 ScrArea *narea;                 /* new area */
1025         } sAreaSplitData;
1026
1027 /* generic init, no UI stuff here */
1028 static int area_split_init(bContext *C, wmOperator *op)
1029 {
1030         ScrArea *sa= CTX_wm_area(C);
1031         sAreaSplitData *sd;
1032         int dir;
1033         
1034         /* required context */
1035         if(sa==NULL) return 0;
1036         
1037         /* required properties */
1038         dir= RNA_enum_get(op->ptr, "direction");
1039         
1040         /* minimal size */
1041         if(dir=='v' && sa->winx < 2*AREAMINX) return 0;
1042         if(dir=='h' && sa->winy < 2*AREAMINY) return 0;
1043         
1044         /* custom data */
1045         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1046         op->customdata= sd;
1047         
1048         sd->sarea= sa;
1049         sd->origsize= dir=='v' ? sa->winx:sa->winy;
1050         sd->origmin = dir=='v' ? sa->totrct.xmin:sa->totrct.ymin;
1051         
1052         return 1;
1053 }
1054
1055 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1056 /* used with split operator */
1057 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1058 {
1059         ScrVert *sav1= sa->v1;
1060         ScrVert *sav2= sa->v2;
1061         ScrVert *sav3= sa->v3;
1062         ScrVert *sav4= sa->v4;
1063         ScrVert *sbv1= sb->v1;
1064         ScrVert *sbv2= sb->v2;
1065         ScrVert *sbv3= sb->v3;
1066         ScrVert *sbv4= sb->v4;
1067         
1068         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
1069                 return screen_findedge(screen, sav1, sav2);
1070         }
1071         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
1072                 return screen_findedge(screen, sav2, sav3);
1073         }
1074         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
1075                 return screen_findedge(screen, sav3, sav4);
1076         }
1077         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
1078                 return screen_findedge(screen, sav1, sav4);
1079         }
1080         
1081         return NULL;
1082 }
1083
1084
1085 /* do the split, return success */
1086 static int area_split_apply(bContext *C, wmOperator *op)
1087 {
1088         bScreen *sc= CTX_wm_screen(C);
1089         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1090         float fac;
1091         int dir;
1092         
1093         fac= RNA_float_get(op->ptr, "factor");
1094         dir= RNA_enum_get(op->ptr, "direction");
1095         
1096         sd->narea= area_split(CTX_wm_window(C), sc, sd->sarea, dir, fac);
1097         
1098         if(sd->narea) {
1099                 ScrVert *sv;
1100                 
1101                 sd->nedge= area_findsharededge(sc, sd->sarea, sd->narea);
1102                 
1103                 /* select newly created edge, prepare for moving edge */
1104                 for(sv= sc->vertbase.first; sv; sv= sv->next)
1105                         sv->flag = 0;
1106                 
1107                 sd->nedge->v1->flag= 1;
1108                 sd->nedge->v2->flag= 1;
1109                 
1110                 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
1111                 else sd->origval= sd->nedge->v1->vec.x;
1112                 
1113                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1114                 
1115                 return 1;
1116         }               
1117         
1118         return 0;
1119 }
1120
1121 static void area_split_exit(bContext *C, wmOperator *op)
1122 {
1123         if (op->customdata) {
1124                 MEM_freeN(op->customdata);
1125                 op->customdata = NULL;
1126         }
1127         
1128         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1129         
1130         /* this makes sure aligned edges will result in aligned grabbing */
1131         removedouble_scrverts(CTX_wm_screen(C));
1132         removedouble_scredges(CTX_wm_screen(C));
1133 }
1134
1135
1136 /* UI callback, adds new handler */
1137 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
1138 {
1139         sAreaSplitData *sd;
1140         
1141         if(event->type==EVT_ACTIONZONE_AREA) {
1142                 sActionzoneData *sad= event->customdata;
1143                 int dir;
1144                 
1145                 if(sad->modifier>0) {
1146                         return OPERATOR_PASS_THROUGH;
1147                 }
1148                 
1149                 /* no full window splitting allowed */
1150                 if(CTX_wm_area(C)->full)
1151                         return OPERATOR_PASS_THROUGH;
1152                 
1153                 /* verify *sad itself */
1154                 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
1155                         return OPERATOR_PASS_THROUGH;
1156                 
1157                 /* is this our *sad? if areas not equal it should be passed on */
1158                 if(CTX_wm_area(C)!=sad->sa1 || sad->sa1!=sad->sa2)
1159                         return OPERATOR_PASS_THROUGH;
1160                 
1161                 /* prepare operator state vars */
1162                 if(sad->gesture_dir=='n' || sad->gesture_dir=='s') {
1163                         dir= 'h';
1164                         RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1165                 }
1166                 else {
1167                         dir= 'v';
1168                         RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1169                 }
1170                 RNA_enum_set(op->ptr, "direction", dir);
1171                 
1172                 /* general init, also non-UI case, adds customdata, sets area and defaults */
1173                 if(!area_split_init(C, op))
1174                         return OPERATOR_PASS_THROUGH;
1175                 
1176                 sd= (sAreaSplitData *)op->customdata;
1177                 
1178                 sd->x= event->x;
1179                 sd->y= event->y;
1180                 
1181                 /* do the split */
1182                 if(area_split_apply(C, op)) {
1183                         area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
1184                         
1185                         /* add temp handler for edge move or cancel */
1186                         WM_event_add_modal_handler(C, op);
1187                         
1188                         return OPERATOR_RUNNING_MODAL;
1189                 }
1190                 
1191         }
1192         else {
1193                 /* nonmodal for now */
1194                 return op->type->exec(C, op);
1195         }
1196         
1197         return OPERATOR_PASS_THROUGH;
1198 }
1199
1200 /* function to be called outside UI context, or for redo */
1201 static int area_split_exec(bContext *C, wmOperator *op)
1202 {
1203         
1204         if(!area_split_init(C, op))
1205                 return OPERATOR_CANCELLED;
1206         
1207         area_split_apply(C, op);
1208         area_split_exit(C, op);
1209         
1210         return OPERATOR_FINISHED;
1211 }
1212
1213
1214 static int area_split_cancel(bContext *C, wmOperator *op)
1215 {
1216         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1217         
1218         if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1219                 if (CTX_wm_area(C) == sd->narea) {
1220                         CTX_wm_area_set(C, NULL);
1221                         CTX_wm_region_set(C, NULL);
1222                 }
1223                 sd->narea = NULL;
1224         }
1225         area_split_exit(C, op);
1226         
1227         return OPERATOR_CANCELLED;
1228 }
1229
1230 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
1231 {
1232         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1233         float fac;
1234         int dir;
1235         
1236         /* execute the events */
1237         switch(event->type) {
1238                 case MOUSEMOVE:
1239                         dir= RNA_enum_get(op->ptr, "direction");
1240                         
1241                         sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
1242                         area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1243                         
1244                         fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
1245                         RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1246                         
1247                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1248                         break;
1249                         
1250                 case LEFTMOUSE:
1251                         if(event->val==KM_RELEASE) { /* mouse up */
1252                                 area_split_exit(C, op);
1253                                 return OPERATOR_FINISHED;
1254                         }
1255                         break;
1256                 case RIGHTMOUSE: /* cancel operation */
1257                 case ESCKEY:
1258                         return area_split_cancel(C, op);
1259         }
1260         
1261         return OPERATOR_RUNNING_MODAL;
1262 }
1263
1264 static EnumPropertyItem prop_direction_items[] = {
1265 {'h', "HORIZONTAL", 0, "Horizontal", ""},
1266 {'v', "VERTICAL", 0, "Vertical", ""},
1267 {0, NULL, 0, NULL, NULL}};
1268
1269 static void SCREEN_OT_area_split(wmOperatorType *ot)
1270 {
1271         ot->name = "Split area";
1272         ot->description= "Split selected area into new windows.";
1273         ot->idname = "SCREEN_OT_area_split";
1274         
1275         ot->exec= area_split_exec;
1276         ot->invoke= area_split_invoke;
1277         ot->modal= area_split_modal;
1278         
1279         ot->poll= ED_operator_areaactive;
1280         ot->flag= OPTYPE_BLOCKING;
1281         
1282         /* rna */
1283         RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1284         RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1285 }
1286
1287
1288
1289 /* ************** scale region edge operator *********************************** */
1290
1291 typedef struct RegionMoveData {
1292         AZone *az;
1293         ARegion *ar;
1294         ScrArea *sa;
1295         int bigger, smaller, origval;
1296         int origx, origy;
1297         int maxsize;
1298         char edge;
1299         
1300 } RegionMoveData;
1301
1302
1303 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, char edge)
1304 {
1305         ARegion *ar;
1306         int dist;
1307         
1308         if(edge=='l' || edge=='r') {
1309                 dist = sa->totrct.xmax - sa->totrct.xmin;
1310         } else {        /* t, b */
1311                 dist = sa->totrct.ymax - sa->totrct.ymin;
1312         }
1313         
1314         /* subtractwidth of regions on opposite side 
1315          * prevents dragging regions into other opposite regions */
1316         for (ar=sa->regionbase.first; ar; ar=ar->next)
1317         {
1318                 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1319                         dist -= ar->winy;
1320                 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1321                         dist -= ar->winy;
1322                 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1323                         dist -= ar->winx;
1324                 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1325                         dist -= ar->winx;
1326         }
1327         
1328         return dist;
1329 }
1330
1331 static int region_scale_invoke(bContext *C, wmOperator *op, wmEvent *event)
1332 {
1333         sActionzoneData *sad= event->customdata;
1334         AZone *az;
1335         
1336         if(event->type!=EVT_ACTIONZONE_REGION) {
1337                 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");   
1338                 return OPERATOR_CANCELLED;
1339         }
1340         
1341         az = sad->az;
1342         
1343         if(az->ar) {
1344                 RegionMoveData *rmd= MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1345                 
1346                 op->customdata= rmd;
1347                 
1348                 rmd->az = az;
1349                 rmd->ar= az->ar;
1350                 rmd->sa = sad->sa1;
1351                 rmd->edge= az->edge;
1352                 rmd->origx= event->x;
1353                 rmd->origy= event->y;
1354                 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1355                 if(rmd->edge=='l' || rmd->edge=='r') {
1356                         rmd->origval= rmd->ar->type->minsizex;
1357                 } else {
1358                         rmd->origval= rmd->ar->type->minsizey;
1359                 }
1360                 CLAMP(rmd->maxsize, 0, 1000);
1361                 
1362                 /* add temp handler */
1363                 WM_event_add_modal_handler(C, op);
1364                 
1365                 return OPERATOR_RUNNING_MODAL;
1366         }
1367         
1368         return OPERATOR_FINISHED;
1369 }
1370
1371 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1372 {
1373         RegionMoveData *rmd= op->customdata;
1374         int delta;
1375         
1376         /* execute the events */
1377         switch(event->type) {
1378                 case MOUSEMOVE:
1379                         
1380                         if(rmd->edge=='l' || rmd->edge=='r') {
1381                                 delta= event->x - rmd->origx;
1382                                 if(rmd->edge=='l') delta= -delta;
1383                                 
1384                                 rmd->ar->type->minsizex= rmd->origval + delta;
1385                                 CLAMP(rmd->ar->type->minsizex, 0, rmd->maxsize);
1386                                 
1387                                 if(rmd->ar->type->minsizex < 24) {
1388                                         rmd->ar->type->minsizex= rmd->origval;
1389                                         if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1390                                                 ED_region_toggle_hidden(C, rmd->ar);
1391                                 }
1392                                 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1393                                         ED_region_toggle_hidden(C, rmd->ar);
1394                         }
1395                         else {
1396                                 delta= event->y - rmd->origy;
1397                                 if(rmd->edge=='b') delta= -delta;
1398                                 
1399                                 rmd->ar->type->minsizey= rmd->origval + delta;
1400                                 CLAMP(rmd->ar->type->minsizey, 0, rmd->maxsize);
1401                                 
1402                                 if(rmd->ar->type->minsizey < 24) {
1403                                         rmd->ar->type->minsizey= rmd->origval;
1404                                         if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1405                                                 ED_region_toggle_hidden(C, rmd->ar);
1406                                 }
1407                                 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1408                                         ED_region_toggle_hidden(C, rmd->ar);
1409                         }
1410                         
1411                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1412                         
1413                         break;
1414                         
1415                 case LEFTMOUSE:
1416                         if(event->val==KM_RELEASE) {
1417                                 
1418                                 if(ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1419                                         if(rmd->ar->flag & RGN_FLAG_HIDDEN) {
1420                                                 ED_region_toggle_hidden(C, rmd->ar);
1421                                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1422                                         }
1423                                 }
1424                                 MEM_freeN(op->customdata);
1425                                 op->customdata = NULL;
1426                                 
1427                                 return OPERATOR_FINISHED;
1428                         }
1429                         break;
1430                         
1431                 case ESCKEY:
1432                         ;
1433         }
1434         
1435         return OPERATOR_RUNNING_MODAL;
1436 }
1437
1438
1439 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1440 {
1441         /* identifiers */
1442         ot->name= "Scale Region Size";
1443         ot->description= "Scale selected area.";
1444         ot->idname= "SCREEN_OT_region_scale";
1445         
1446         ot->invoke= region_scale_invoke;
1447         ot->modal= region_scale_modal;
1448         
1449         ot->poll= ED_operator_areaactive;
1450         
1451         ot->flag= OPTYPE_BLOCKING;
1452 }
1453
1454
1455 /* ************** frame change operator ***************************** */
1456
1457 /* function to be called outside UI context, or for redo */
1458 static int frame_offset_exec(bContext *C, wmOperator *op)
1459 {
1460         int delta;
1461         
1462         delta = RNA_int_get(op->ptr, "delta");
1463         
1464         CTX_data_scene(C)->r.cfra += delta;
1465         
1466         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1467         
1468         return OPERATOR_FINISHED;
1469 }
1470
1471 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
1472 {
1473         ot->name = "Frame Offset";
1474         ot->idname = "SCREEN_OT_frame_offset";
1475         
1476         ot->exec= frame_offset_exec;
1477         
1478         ot->poll= ED_operator_screenactive;
1479         ot->flag= 0;
1480         
1481         /* rna */
1482         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1483 }
1484
1485
1486 /* function to be called outside UI context, or for redo */
1487 static int frame_jump_exec(bContext *C, wmOperator *op)
1488 {
1489         Scene *scene= CTX_data_scene(C);
1490         
1491         if (RNA_boolean_get(op->ptr, "end"))
1492                 CFRA= PEFRA;
1493         else
1494                 CFRA= PSFRA;
1495         
1496         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1497         
1498         return OPERATOR_FINISHED;
1499 }
1500
1501 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1502 {
1503         ot->name = "Jump to Endpoint";
1504         ot->description= "Jump to first/last frame in frame range.";
1505         ot->idname = "SCREEN_OT_frame_jump";
1506         
1507         ot->exec= frame_jump_exec;
1508         
1509         ot->poll= ED_operator_screenactive;
1510         ot->flag= 0;
1511         
1512         /* rna */
1513         RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range.");
1514 }
1515
1516
1517 /* ************** jump to keyframe operator ***************************** */
1518
1519 /* function to be called outside UI context, or for redo */
1520 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1521 {
1522         Scene *scene= CTX_data_scene(C);
1523         Object *ob= CTX_data_active_object(C);
1524         DLRBT_Tree keys;
1525         ActKeyColumn *ak;
1526         float cfra= (scene)? (float)(CFRA) : 0.0f;
1527         short next= RNA_boolean_get(op->ptr, "next");
1528         
1529         /* sanity checks */
1530         if (scene == NULL)
1531                 return OPERATOR_CANCELLED;
1532         
1533         /* init binarytree-list for getting keyframes */
1534         BLI_dlrbTree_init(&keys);
1535         
1536         /* populate tree with keyframe nodes */
1537         if (scene && scene->adt)
1538                 scene_to_keylist(NULL, scene, &keys, NULL);
1539         if (ob && ob->adt)
1540                 ob_to_keylist(NULL, ob, &keys, NULL);
1541         
1542         /* build linked-list for searching */
1543         BLI_dlrbTree_linkedlist_sync(&keys);
1544         
1545         /* find matching keyframe in the right direction */
1546         if (next)
1547                 ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1548         else
1549                 ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1550         
1551         /* set the new frame (if keyframe found) */
1552         if (ak) 
1553                 CFRA= (int)ak->cfra;
1554         else
1555                 BKE_report(op->reports, RPT_ERROR, "No more keyframes to jump to in this direction");
1556         
1557         /* free temp stuff */
1558         BLI_dlrbTree_free(&keys);
1559         
1560         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1561         
1562         return OPERATOR_FINISHED;
1563 }
1564
1565 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1566 {
1567         ot->name = "Jump to Keyframe";
1568         ot->description= "Jump to previous/next keyframe.";
1569         ot->idname = "SCREEN_OT_keyframe_jump";
1570         
1571         ot->exec= keyframe_jump_exec;
1572         
1573         ot->poll= ED_operator_screenactive;
1574         ot->flag= 0;
1575         
1576         /* rna */
1577         RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1578 }
1579
1580 /* ************** switch screen operator ***************************** */
1581
1582
1583 /* function to be called outside UI context, or for redo */
1584 static int screen_set_exec(bContext *C, wmOperator *op)
1585 {
1586         bScreen *screen= CTX_wm_screen(C);
1587         ScrArea *sa= CTX_wm_area(C);
1588         int tot= BLI_countlist(&CTX_data_main(C)->screen);
1589         int delta= RNA_int_get(op->ptr, "delta");
1590         
1591         /* return to previous state before switching screens */
1592         if(sa && sa->full)
1593                 ED_screen_full_restore(C, sa);
1594         
1595         if(delta==1) {
1596                 while(tot--) {
1597                         screen= screen->id.next;
1598                         if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1599                         if(screen->winid==0 && screen->full==0)
1600                                 break;
1601                 }
1602         }
1603         else if(delta== -1) {
1604                 while(tot--) {
1605                         screen= screen->id.prev;
1606                         if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1607                         if(screen->winid==0 && screen->full==0)
1608                                 break;
1609                 }
1610         }
1611         else {
1612                 screen= NULL;
1613         }
1614         
1615         if(screen) {
1616                 ED_screen_set(C, screen);
1617                 return OPERATOR_FINISHED;
1618         }
1619         return OPERATOR_CANCELLED;
1620 }
1621
1622 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1623 {
1624         ot->name = "Set Screen";
1625         ot->description= "Cycle through available screens.";
1626         ot->idname = "SCREEN_OT_screen_set";
1627         
1628         ot->exec= screen_set_exec;
1629         ot->poll= ED_operator_screenactive;
1630         
1631         /* rna */
1632         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1633 }
1634
1635 /* ************** screen full-area operator ***************************** */
1636
1637
1638 /* function to be called outside UI context, or for redo */
1639 static int screen_full_area_exec(bContext *C, wmOperator *op)
1640 {
1641         ed_screen_fullarea(C, CTX_wm_window(C), CTX_wm_area(C));
1642         return OPERATOR_FINISHED;
1643 }
1644
1645 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1646 {
1647         ot->name = "Toggle Full Screen";
1648         ot->description= "Toggle display selected area as fullscreen.";
1649         ot->idname = "SCREEN_OT_screen_full_area";
1650         
1651         ot->exec= screen_full_area_exec;
1652         ot->poll= ED_operator_areaactive;
1653         ot->flag= 0;
1654         
1655 }
1656
1657
1658
1659 /* ************** join area operator ********************************************** */
1660
1661 /* operator state vars used:  
1662  x1, y1     mouse coord in first area, which will disappear
1663  x2, y2     mouse coord in 2nd area, which will become joined
1664  
1665  functions:
1666  
1667  init()   find edge based on state vars 
1668  test if the edge divides two areas, 
1669  store active and nonactive area,
1670  
1671  apply()  do the actual join
1672  
1673  exit() cleanup, send notifier
1674  
1675  callbacks:
1676  
1677  exec() calls init, apply, exit 
1678  
1679  invoke() sets mouse coords in x,y
1680  call init()
1681  add modal handler
1682  
1683  modal()        accept modal events while doing it
1684  call apply() with active window and nonactive window
1685  call exit() and remove handler when LMB confirm
1686  
1687  */
1688
1689 typedef struct sAreaJoinData
1690         {
1691                 ScrArea *sa1;   /* first area to be considered */
1692                 ScrArea *sa2;   /* second area to be considered */
1693                 ScrArea *scr;   /* designed for removal */
1694                 
1695         } sAreaJoinData;
1696
1697
1698 /* validate selection inside screen, set variables OK */
1699 /* return 0: init failed */
1700 /* XXX todo: find edge based on (x,y) and set other area? */
1701 static int area_join_init(bContext *C, wmOperator *op)
1702 {
1703         ScrArea *sa1, *sa2;
1704         sAreaJoinData* jd= NULL;
1705         int x1, y1;
1706         int x2, y2;
1707         
1708         /* required properties, make negative to get return 0 if not set by caller */
1709         x1= RNA_int_get(op->ptr, "x1");
1710         y1= RNA_int_get(op->ptr, "y1");
1711         x2= RNA_int_get(op->ptr, "x2");
1712         y2= RNA_int_get(op->ptr, "y2");
1713         
1714         sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1715         sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1716         if(sa1==NULL || sa2==NULL || sa1==sa2)
1717                 return 0;
1718         
1719         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1720         
1721         jd->sa1 = sa1;
1722         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1723         jd->sa2 = sa2;
1724         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1725         
1726         op->customdata= jd;
1727         
1728         return 1;
1729 }
1730
1731 /* apply the join of the areas (space types) */
1732 static int area_join_apply(bContext *C, wmOperator *op)
1733 {
1734         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1735         if (!jd) return 0;
1736         
1737         if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1738                 return 0;
1739         }
1740         if (CTX_wm_area(C) == jd->sa2) {
1741                 CTX_wm_area_set(C, NULL);
1742                 CTX_wm_region_set(C, NULL);
1743         }
1744         
1745         return 1;
1746 }
1747
1748 /* finish operation */
1749 static void area_join_exit(bContext *C, wmOperator *op)
1750 {
1751         if (op->customdata) {
1752                 MEM_freeN(op->customdata);
1753                 op->customdata = NULL;
1754         }
1755         
1756         /* this makes sure aligned edges will result in aligned grabbing */
1757         removedouble_scredges(CTX_wm_screen(C));
1758         removenotused_scredges(CTX_wm_screen(C));
1759         removenotused_scrverts(CTX_wm_screen(C));
1760 }
1761
1762 static int area_join_exec(bContext *C, wmOperator *op)
1763 {
1764         if(!area_join_init(C, op)) 
1765                 return OPERATOR_CANCELLED;
1766         
1767         area_join_apply(C, op);
1768         area_join_exit(C, op);
1769         
1770         return OPERATOR_FINISHED;
1771 }
1772
1773 /* interaction callback */
1774 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1775 {
1776         
1777         if(event->type==EVT_ACTIONZONE_AREA) {
1778                 sActionzoneData *sad= event->customdata;
1779                 
1780                 if(sad->modifier>0) {
1781                         return OPERATOR_PASS_THROUGH;
1782                 }
1783                 
1784                 /* verify *sad itself */
1785                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1786                         return OPERATOR_PASS_THROUGH;
1787                 
1788                 /* is this our *sad? if areas equal it should be passed on */
1789                 if(sad->sa1==sad->sa2)
1790                         return OPERATOR_PASS_THROUGH;
1791                 
1792                 /* prepare operator state vars */
1793                 RNA_int_set(op->ptr, "x1", sad->x);
1794                 RNA_int_set(op->ptr, "y1", sad->y);
1795                 RNA_int_set(op->ptr, "x2", event->x);
1796                 RNA_int_set(op->ptr, "y2", event->y);
1797                 
1798                 if(!area_join_init(C, op)) 
1799                         return OPERATOR_PASS_THROUGH;
1800                 
1801                 /* add temp handler */
1802                 WM_event_add_modal_handler(C, op);
1803                 
1804                 return OPERATOR_RUNNING_MODAL;
1805         }
1806         
1807         return OPERATOR_PASS_THROUGH;
1808 }
1809
1810 static int area_join_cancel(bContext *C, wmOperator *op)
1811 {
1812         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1813         
1814         if (jd->sa1) {
1815                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1816                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1817         }
1818         if (jd->sa2) {
1819                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1820                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1821         }
1822         
1823         WM_event_add_notifier(C, NC_WINDOW, NULL);
1824         
1825         area_join_exit(C, op);
1826         
1827         return OPERATOR_CANCELLED;
1828 }
1829
1830 /* modal callback while selecting area (space) that will be removed */
1831 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1832 {
1833         bScreen *sc= CTX_wm_screen(C);
1834         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1835         
1836         /* execute the events */
1837         switch(event->type) {
1838                         
1839                 case MOUSEMOVE: 
1840                 {
1841                         ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1842                         int dir;
1843                         
1844                         if (sa) {                                       
1845                                 if (jd->sa1 != sa) {
1846                                         dir = area_getorientation(sc, jd->sa1, sa);
1847                                         if (dir >= 0) {
1848                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1849                                                 jd->sa2 = sa;
1850                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1851                                         } 
1852                                         else {
1853                                                 /* we are not bordering on the previously selected area 
1854                                                  we check if area has common border with the one marked for removal
1855                                                  in this case we can swap areas.
1856                                                  */
1857                                                 dir = area_getorientation(sc, sa, jd->sa2);
1858                                                 if (dir >= 0) {
1859                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1860                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1861                                                         jd->sa1 = jd->sa2;
1862                                                         jd->sa2 = sa;
1863                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1864                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1865                                                 } 
1866                                                 else {
1867                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1868                                                         jd->sa2 = NULL;
1869                                                 }
1870                                         }
1871                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
1872                                 } 
1873                                 else {
1874                                         /* we are back in the area previously selected for keeping 
1875                                          * we swap the areas if possible to allow user to choose */
1876                                         if (jd->sa2 != NULL) {
1877                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1878                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1879                                                 jd->sa1 = jd->sa2;
1880                                                 jd->sa2 = sa;
1881                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1882                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1883                                                 dir = area_getorientation(sc, jd->sa1, jd->sa2);
1884                                                 if (dir < 0) {
1885                                                         printf("oops, didn't expect that!\n");
1886                                                 }
1887                                         } 
1888                                         else {
1889                                                 dir = area_getorientation(sc, jd->sa1, sa);
1890                                                 if (dir >= 0) {
1891                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1892                                                         jd->sa2 = sa;
1893                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1894                                                 }
1895                                         }
1896                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
1897                                 }
1898                         }
1899                 }
1900                         break;
1901                 case LEFTMOUSE:
1902                         if(event->val==KM_RELEASE) {
1903                                 area_join_apply(C, op);
1904                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1905                                 area_join_exit(C, op);
1906                                 return OPERATOR_FINISHED;
1907                         }
1908                         break;
1909                         
1910                 case RIGHTMOUSE:
1911                 case ESCKEY:
1912                         return area_join_cancel(C, op);
1913         }
1914         
1915         return OPERATOR_RUNNING_MODAL;
1916 }
1917
1918 /* Operator for joining two areas (space types) */
1919 static void SCREEN_OT_area_join(wmOperatorType *ot)
1920 {
1921         /* identifiers */
1922         ot->name= "Join area";
1923         ot->description= "Join selected areas into new window.";
1924         ot->idname= "SCREEN_OT_area_join";
1925         
1926         /* api callbacks */
1927         ot->exec= area_join_exec;
1928         ot->invoke= area_join_invoke;
1929         ot->modal= area_join_modal;
1930         ot->poll= ED_operator_areaactive;
1931         
1932         ot->flag= OPTYPE_BLOCKING;
1933         
1934         /* rna */
1935         RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1936         RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
1937         RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
1938         RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
1939 }
1940
1941 /* ************** repeat last operator ***************************** */
1942
1943 static int repeat_last_exec(bContext *C, wmOperator *op)
1944 {
1945         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
1946         
1947         if(lastop)
1948                 WM_operator_repeat(C, lastop);
1949         
1950         return OPERATOR_CANCELLED;
1951 }
1952
1953 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
1954 {
1955         /* identifiers */
1956         ot->name= "Repeat Last";
1957         ot->description= "Repeat last action.";
1958         ot->idname= "SCREEN_OT_repeat_last";
1959         
1960         /* api callbacks */
1961         ot->exec= repeat_last_exec;
1962         
1963         ot->poll= ED_operator_screenactive;
1964         
1965 }
1966
1967 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
1968 {
1969         wmWindowManager *wm= CTX_wm_manager(C);
1970         wmOperator *lastop;
1971         uiPopupMenu *pup;
1972         uiLayout *layout;
1973         int items, i;
1974         
1975         items= BLI_countlist(&wm->operators);
1976         if(items==0)
1977                 return OPERATOR_CANCELLED;
1978         
1979         pup= uiPupMenuBegin(C, op->type->name, 0);
1980         layout= uiPupMenuLayout(pup);
1981         
1982         for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
1983                 uiItemIntO(layout, lastop->type->name, 0, op->type->idname, "index", i);
1984         
1985         uiPupMenuEnd(C, pup);
1986         
1987         return OPERATOR_CANCELLED;
1988 }
1989
1990 static int repeat_history_exec(bContext *C, wmOperator *op)
1991 {
1992         wmWindowManager *wm= CTX_wm_manager(C);
1993         
1994         op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
1995         if(op) {
1996                 /* let's put it as last operator in list */
1997                 BLI_remlink(&wm->operators, op);
1998                 BLI_addtail(&wm->operators, op);
1999                 
2000                 WM_operator_repeat(C, op);
2001         }
2002         
2003         return OPERATOR_FINISHED;
2004 }
2005
2006 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2007 {
2008         /* identifiers */
2009         ot->name= "Repeat History";
2010         ot->description= "Display menu for previous actions performed.";
2011         ot->idname= "SCREEN_OT_repeat_history";
2012         
2013         /* api callbacks */
2014         ot->invoke= repeat_history_invoke;
2015         ot->exec= repeat_history_exec;
2016         
2017         ot->poll= ED_operator_screenactive;
2018         
2019         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2020 }
2021
2022 /* ********************** redo operator ***************************** */
2023
2024 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
2025 {
2026         wmWindowManager *wm= CTX_wm_manager(C);
2027         wmOperator *lastop;
2028         
2029         /* only for operators that are registered and did an undo push */
2030         for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
2031                 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
2032                         break;
2033         
2034         if(lastop)
2035                 WM_operator_redo_popup(C, lastop);
2036         
2037         return OPERATOR_CANCELLED;
2038 }
2039
2040 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2041 {
2042         /* identifiers */
2043         ot->name= "Redo Last";
2044         ot->description= "Display menu for last action performed.";
2045         ot->idname= "SCREEN_OT_redo_last";
2046         
2047         /* api callbacks */
2048         ot->invoke= redo_last_invoke;
2049         
2050         ot->poll= ED_operator_screenactive;
2051 }
2052
2053 /* ************** region four-split operator ***************************** */
2054
2055 /* insert a region in the area region list */
2056 static int region_quadview_exec(bContext *C, wmOperator *op)
2057 {
2058         ARegion *ar= CTX_wm_region(C);
2059         
2060         /* some rules... */
2061         if(ar->regiontype!=RGN_TYPE_WINDOW)
2062                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2063         else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2064                 ScrArea *sa= CTX_wm_area(C);
2065                 ARegion *arn;
2066                 
2067                 /* keep current region */
2068                 ar->alignment= 0;
2069                 
2070                 if(sa->spacetype==SPACE_VIEW3D) {
2071                         RegionView3D *rv3d= ar->regiondata;
2072                         rv3d->viewlock= 0;
2073                         rv3d->rflag &= ~RV3D_CLIPPING;
2074                 }
2075                 
2076                 for(ar= sa->regionbase.first; ar; ar= arn) {
2077                         arn= ar->next;
2078                         if(ar->alignment==RGN_ALIGN_QSPLIT) {
2079                                 ED_region_exit(C, ar);
2080                                 BKE_area_region_free(sa->type, ar);
2081                                 BLI_remlink(&sa->regionbase, ar);
2082                                 MEM_freeN(ar);
2083                         }
2084                 }
2085                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2086         }
2087         else if(ar->next)
2088                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2089         else {
2090                 ScrArea *sa= CTX_wm_area(C);
2091                 ARegion *newar;
2092                 int count;
2093                 
2094                 ar->alignment= RGN_ALIGN_QSPLIT;
2095                 
2096                 for(count=0; count<3; count++) {
2097                         newar= BKE_area_region_copy(sa->type, ar);
2098                         BLI_addtail(&sa->regionbase, newar);
2099                 }
2100                 
2101                 /* lock views and set them */
2102                 if(sa->spacetype==SPACE_VIEW3D) {
2103                         RegionView3D *rv3d;
2104                         
2105                         rv3d= ar->regiondata;
2106                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2107                         
2108                         ar= ar->next;
2109                         rv3d= ar->regiondata;
2110                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2111                         
2112                         ar= ar->next;
2113                         rv3d= ar->regiondata;
2114                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2115                         
2116                         ar= ar->next;
2117                         rv3d= ar->regiondata;
2118                         rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2119                 }
2120                 
2121                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2122         }
2123         
2124         
2125         return OPERATOR_FINISHED;
2126 }
2127
2128 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
2129 {
2130         /* identifiers */
2131         ot->name= "Toggle Quad View";
2132         ot->description= "Split selected area into camera, front, right & top views.";
2133         ot->idname= "SCREEN_OT_region_quadview";
2134         
2135         /* api callbacks */
2136         //      ot->invoke= WM_operator_confirm;
2137         ot->exec= region_quadview_exec;
2138         ot->poll= ED_operator_areaactive;
2139         ot->flag= 0;
2140 }
2141
2142
2143
2144 /* ************** region flip operator ***************************** */
2145
2146 /* flip a region alignment */
2147 static int region_flip_exec(bContext *C, wmOperator *op)
2148 {
2149         ARegion *ar= CTX_wm_region(C);
2150         
2151         if (!ar)
2152                 return OPERATOR_CANCELLED;
2153         
2154         if(ar->alignment==RGN_ALIGN_TOP)
2155                 ar->alignment= RGN_ALIGN_BOTTOM;
2156         else if(ar->alignment==RGN_ALIGN_BOTTOM)
2157                 ar->alignment= RGN_ALIGN_TOP;
2158         else if(ar->alignment==RGN_ALIGN_LEFT)
2159                 ar->alignment= RGN_ALIGN_RIGHT;
2160         else if(ar->alignment==RGN_ALIGN_RIGHT)
2161                 ar->alignment= RGN_ALIGN_LEFT;
2162         
2163         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2164         
2165         return OPERATOR_FINISHED;
2166 }
2167
2168
2169 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2170 {
2171         /* identifiers */
2172         ot->name= "Flip Region";
2173         ot->idname= "SCREEN_OT_region_flip";
2174         
2175         /* api callbacks */
2176         ot->exec= region_flip_exec;
2177         
2178         ot->poll= ED_operator_areaactive;
2179         ot->flag= 0;
2180 }
2181
2182 /* ************** header flip operator ***************************** */
2183
2184 /* flip a header region alignment */
2185 static int header_flip_exec(bContext *C, wmOperator *op)
2186 {
2187         ARegion *ar= CTX_wm_region(C);
2188         
2189         /* find the header region 
2190          *      - try context first, but upon failing, search all regions in area...
2191          */
2192         if((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) {
2193                 ScrArea *sa= CTX_wm_area(C);
2194                 
2195                 /* loop over all regions until a matching one is found */
2196                 for (ar= sa->regionbase.first; ar; ar= ar->next) {
2197                         if(ar->regiontype == RGN_TYPE_HEADER)
2198                                 break;
2199                 }
2200                 
2201                 /* don't do anything if no region */
2202                 if(ar == NULL)
2203                         return OPERATOR_CANCELLED;
2204         }       
2205         
2206         /* copied from SCREEN_OT_region_flip */
2207         if(ar->alignment==RGN_ALIGN_TOP)
2208                 ar->alignment= RGN_ALIGN_BOTTOM;
2209         else if(ar->alignment==RGN_ALIGN_BOTTOM)
2210                 ar->alignment= RGN_ALIGN_TOP;
2211         else if(ar->alignment==RGN_ALIGN_LEFT)
2212                 ar->alignment= RGN_ALIGN_RIGHT;
2213         else if(ar->alignment==RGN_ALIGN_RIGHT)
2214                 ar->alignment= RGN_ALIGN_LEFT;
2215         
2216         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2217         printf("executed header region flip\n");
2218         
2219         return OPERATOR_FINISHED;
2220 }
2221
2222
2223 static void SCREEN_OT_header_flip(wmOperatorType *ot)
2224 {
2225         /* identifiers */
2226         ot->name= "Flip Header Region";
2227         ot->idname= "SCREEN_OT_header_flip";
2228         
2229         /* api callbacks */
2230         ot->exec= header_flip_exec;
2231         
2232         ot->poll= ED_operator_areaactive;
2233         ot->flag= 0;
2234 }
2235
2236 /* ************** header tools operator ***************************** */
2237
2238 static int header_toolbox_invoke(bContext *C, wmOperator *op, wmEvent *event)
2239 {
2240         ScrArea *sa= CTX_wm_area(C);
2241         ARegion *ar= CTX_wm_region(C);
2242         uiPopupMenu *pup;
2243         uiLayout *layout;
2244         
2245         pup= uiPupMenuBegin(C, "Header", 0);
2246         layout= uiPupMenuLayout(pup);
2247         
2248         // XXX SCREEN_OT_region_flip doesn't work - gets wrong context for active region, so added custom operator
2249         if (ar->alignment == RGN_ALIGN_TOP)
2250                 uiItemO(layout, "Flip to Bottom", 0, "SCREEN_OT_header_flip");  
2251         else
2252                 uiItemO(layout, "Flip to Top", 0, "SCREEN_OT_header_flip");
2253         
2254         uiItemS(layout);
2255         
2256         /* file browser should be fullscreen all the time, but other regions can be maximised/restored... */
2257         if (sa->spacetype != SPACE_FILE) {
2258                 if (sa->full) 
2259                         uiItemO(layout, "Tile Window", 0, "SCREEN_OT_screen_full_area");
2260                 else
2261                         uiItemO(layout, "Maximize Window", 0, "SCREEN_OT_screen_full_area");
2262         }
2263         
2264         uiPupMenuEnd(C, pup);
2265         
2266         return OPERATOR_CANCELLED;
2267 }
2268
2269 void SCREEN_OT_header_toolbox(wmOperatorType *ot)
2270 {
2271         /* identifiers */
2272         ot->name= "Header Toolbox";
2273         ot->description="Display header region toolbox";
2274         ot->idname= "SCREEN_OT_header_toolbox";
2275         
2276         /* api callbacks */
2277         ot->invoke= header_toolbox_invoke;
2278 }
2279
2280 /* ****************** anim player, with timer ***************** */
2281
2282 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2283 {
2284         if(regiontype==RGN_TYPE_WINDOW) {
2285                 
2286                 switch (spacetype) {
2287                         case SPACE_VIEW3D:
2288                                 if(redraws & TIME_ALL_3D_WIN)
2289                                         return 1;
2290                                 break;
2291                         case SPACE_IPO:
2292                         case SPACE_ACTION:
2293                         case SPACE_NLA:
2294                                 if(redraws & TIME_ALL_ANIM_WIN)
2295                                         return 1;
2296                                 break;
2297                         case SPACE_TIME:
2298                                 /* if only 1 window or 3d windows, we do timeline too */
2299                                 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2300                                         return 1;
2301                                 break;
2302                         case SPACE_BUTS:
2303                                 if(redraws & TIME_ALL_BUTS_WIN)
2304                                         return 1;
2305                                 break;
2306                         case SPACE_SEQ:
2307                                 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2308                                         return 1;
2309                                 break;
2310                         case SPACE_NODE:
2311                                 if(redraws & (TIME_NODES))
2312                                         return 1;
2313                                 break;
2314                         case SPACE_IMAGE:
2315                                 if(redraws & TIME_ALL_IMAGE_WIN)
2316                                         return 1;
2317                                 break;
2318                                 
2319                 }
2320         }
2321         else if(regiontype==RGN_TYPE_UI) {
2322                 if(redraws & TIME_ALL_BUTS_WIN)
2323                         return 1;
2324         }
2325         else if(regiontype==RGN_TYPE_HEADER) {
2326                 if(spacetype==SPACE_TIME)
2327                         return 1;
2328         }
2329         return 0;
2330 }
2331
2332 static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event)
2333 {
2334         bScreen *screen= CTX_wm_screen(C);
2335         
2336         if(screen->animtimer==event->customdata) {
2337                 Scene *scene= CTX_data_scene(C);
2338                 wmTimer *wt= screen->animtimer;
2339                 ScreenAnimData *sad= wt->customdata;
2340                 ScrArea *sa;
2341                 int sync;
2342 #ifdef DURIAN_CAMERA_SWITCH
2343                 Object *camera_orig= scene->camera;
2344 #endif
2345                 
2346                 /* sync, don't sync, or follow scene setting */
2347                 if(sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2348                 else if(sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2349                 else sync= (scene->audio.flag & AUDIO_SYNC);
2350                 
2351                 if(sync) {
2352                         /* skip frames */
2353                         int step = floor(wt->duration * FPS);
2354                         if(sad->flag & ANIMPLAY_FLAG_REVERSE) // XXX does this option work with audio?
2355                                 scene->r.cfra -= step;
2356                         else
2357                                 scene->r.cfra += step;
2358                         wt->duration -= ((float)step)/FPS;
2359                 }
2360                 else {
2361                         /* one frame +/- */
2362                         if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2363                                 scene->r.cfra--;
2364                         else
2365                                 scene->r.cfra++;
2366                 }
2367                 
2368                 /* reset 'jumped' flag before checking if we need to jump... */
2369                 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2370                 
2371                 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2372                         /* jump back to end? */
2373                         if (scene->r.psfra) {
2374                                 if (scene->r.cfra < scene->r.psfra) {
2375                                         scene->r.cfra= scene->r.pefra;
2376                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2377                                 }
2378                         }
2379                         else {
2380                                 if (scene->r.cfra < scene->r.sfra) {
2381                                         scene->r.cfra= scene->r.efra;
2382                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2383                                 }
2384                         }
2385                 }
2386                 else {
2387                         /* jump back to start? */
2388                         if (scene->r.psfra) {
2389                                 if (scene->r.cfra > scene->r.pefra) {
2390                                         scene->r.cfra= scene->r.psfra;
2391                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2392                                 }
2393                         }
2394                         else {
2395                                 if (scene->r.cfra > scene->r.efra) {
2396                                         scene->r.cfra= scene->r.sfra;
2397                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2398                                 }
2399                         }
2400                 }
2401                 
2402                 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2403
2404                 ED_update_for_newframe(C, 1);
2405                 
2406                 sound_update_playing(C);
2407
2408                 for(sa= screen->areabase.first; sa; sa= sa->next) {
2409                         ARegion *ar;
2410                         for(ar= sa->regionbase.first; ar; ar= ar->next) {
2411                                 if(ar==sad->ar)
2412                                         ED_region_tag_redraw(ar);
2413                                 else
2414                                         if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2415                                                 ED_region_tag_redraw(ar);
2416                         }
2417                 }
2418                 
2419                 /* recalculate the timestep for the timer now that we've finished calculating this,
2420                  * since the frames-per-second value may have been changed
2421                  */
2422                 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2423                 wt->timestep= (1.0/FPS);
2424                 
2425                 return OPERATOR_FINISHED;
2426         }
2427         return OPERATOR_PASS_THROUGH;
2428 }
2429
2430 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2431 {
2432         /* identifiers */
2433         ot->name= "Animation Step";
2434         ot->description= "Step through animation by position.";
2435         ot->idname= "SCREEN_OT_animation_step";
2436         
2437         /* api callbacks */
2438         ot->invoke= screen_animation_step;
2439         
2440         ot->poll= ED_operator_screenactive;
2441         
2442 }
2443
2444 /* ****************** anim player, starts or ends timer ***************** */
2445
2446 /* toggle operator */
2447 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
2448 {
2449         bScreen *screen= CTX_wm_screen(C);
2450         
2451         if(screen->animtimer) {
2452                 /* stop playback now */
2453                 ED_screen_animation_timer(C, 0, 0, 0);
2454                 sound_stop_all(C);
2455         }
2456         else {
2457                 ScrArea *sa= CTX_wm_area(C);
2458                 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2459                 int sync= -1;
2460                 
2461                 if(RNA_property_is_set(op->ptr, "sync"))
2462                         sync= (RNA_boolean_get(op->ptr, "sync"));
2463                 
2464                 /* timeline gets special treatment since it has it's own menu for determining redraws */
2465                 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2466                         SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2467                         
2468                         ED_screen_animation_timer(C, stime->redraws, sync, mode);
2469                         
2470                         /* update region if TIME_REGION was set, to leftmost 3d window */
2471                         ED_screen_animation_timer_update(screen, stime->redraws);
2472                 }
2473                 else {
2474                         int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2475                         
2476                         /* XXX - would like a better way to deal with this situation - Campbell */
2477                         if((sa) && (sa->spacetype == SPACE_SEQ)) {
2478                                 redraws |= TIME_SEQ;
2479                         }
2480                         
2481                         ED_screen_animation_timer(C, redraws, sync, mode);
2482                         
2483                         if(screen->animtimer) {
2484                                 wmTimer *wt= screen->animtimer;
2485                                 ScreenAnimData *sad= wt->customdata;
2486                                 
2487                                 sad->ar= CTX_wm_region(C);
2488                         }
2489                 }
2490         }
2491         
2492         return OPERATOR_FINISHED;
2493 }
2494
2495 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2496 {
2497         /* identifiers */
2498         ot->name= "Play Animation";
2499         ot->description= "Play animation.";
2500         ot->idname= "SCREEN_OT_animation_play";
2501         
2502         /* api callbacks */
2503         ot->invoke= screen_animation_play;
2504         
2505         ot->poll= ED_operator_screenactive;
2506         
2507         RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2508         RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate and stay in sync with audio.");
2509 }
2510
2511 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2512 {
2513         bScreen *screen= CTX_wm_screen(C);
2514         
2515         if(screen->animtimer) {
2516                 ScreenAnimData *sad= screen->animtimer->customdata;
2517                 Scene *scene= CTX_data_scene(C);
2518                 
2519                 /* reset current frame before stopping, and just send a notifier to deal with the rest 
2520                  * (since playback still needs to be stopped)
2521                  */
2522                 scene->r.cfra= sad->sfra;
2523                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2524                 
2525                 /* call the other "toggling" operator to clean up now */
2526                 return screen_animation_play(C, op, event);
2527         }
2528         
2529         return OPERATOR_PASS_THROUGH;
2530 }
2531
2532 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2533 {
2534         /* identifiers */
2535         ot->name= "Cancel Animation";
2536         ot->description= "Cancel animation, returning to the original frame.";
2537         ot->idname= "SCREEN_OT_animation_cancel";
2538         
2539         /* api callbacks */
2540         ot->invoke= screen_animation_cancel;
2541         
2542         ot->poll= ED_operator_screenactive;
2543 }
2544
2545 /* ************** border select operator (template) ***************************** */
2546
2547 /* operator state vars used: (added by default WM callbacks)   
2548  xmin, ymin     
2549  xmax, ymax     
2550  
2551  customdata: the wmGesture pointer
2552  
2553  callbacks:
2554  
2555  exec() has to be filled in by user
2556  
2557  invoke() default WM function
2558  adds modal handler
2559  
2560  modal()        default WM function 
2561  accept modal events while doing it, calls exec(), handles ESC and border drawing
2562  
2563  poll() has to be filled in by user for context
2564  */
2565 #if 0
2566 static int border_select_do(bContext *C, wmOperator *op)
2567 {
2568         int event_type= RNA_int_get(op->ptr, "event_type");
2569         
2570         if(event_type==LEFTMOUSE)
2571                 printf("border select do select\n");
2572         else if(event_type==RIGHTMOUSE)
2573                 printf("border select deselect\n");
2574         else 
2575                 printf("border select do something\n");
2576         
2577         return 1;
2578 }
2579
2580 static void SCREEN_OT_border_select(wmOperatorType *ot)
2581 {
2582         /* identifiers */
2583         ot->name= "Border select";
2584         ot->idname= "SCREEN_OT_border_select";
2585         
2586         /* api callbacks */
2587         ot->exec= border_select_do;
2588         ot->invoke= WM_border_select_invoke;
2589         ot->modal= WM_border_select_modal;
2590         
2591         ot->poll= ED_operator_areaactive;
2592         
2593         /* rna */
2594         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2595         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2596         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2597         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2598         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2599         
2600 }
2601 #endif
2602
2603 /* ****************************** render invoking ***************** */
2604
2605 /* set callbacks, exported to sequence render too. 
2606  Only call in foreground (UI) renders. */
2607
2608 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2609 /* window as the last possible alternative.                                                                        */
2610 static ScrArea *biggest_non_image_area(bContext *C)
2611 {
2612         bScreen *sc= CTX_wm_screen(C);
2613         ScrArea *sa, *big= NULL;
2614         int size, maxsize= 0, bwmaxsize= 0;
2615         short foundwin= 0;
2616         
2617         for(sa= sc->areabase.first; sa; sa= sa->next) {
2618                 if(sa->winx > 30 && sa->winy > 30) {
2619                         size= sa->winx*sa->winy;
2620                         if(sa->spacetype == SPACE_BUTS) {
2621                                 if(foundwin == 0 && size > bwmaxsize) {
2622                                         bwmaxsize= size;
2623                                         big= sa;        
2624                                 }
2625                         }
2626                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2627                                 maxsize= size;
2628                                 big= sa;
2629                                 foundwin= 1;
2630                         }
2631                 }
2632         }
2633         
2634         return big;
2635 }
2636
2637 static ScrArea *biggest_area(bContext *C)
2638 {
2639         bScreen *sc= CTX_wm_screen(C);
2640         ScrArea *sa, *big= NULL;
2641         int size, maxsize= 0;
2642         
2643         for(sa= sc->areabase.first; sa; sa= sa->next) {
2644                 size= sa->winx*sa->winy;
2645                 if(size > maxsize) {
2646                         maxsize= size;
2647                         big= sa;
2648                 }
2649         }
2650         return big;
2651 }
2652
2653
2654 static ScrArea *find_area_showing_r_result(bContext *C)
2655 {
2656         wmWindowManager *wm= CTX_wm_manager(C);
2657         wmWindow *win;
2658         ScrArea *sa = NULL;
2659         SpaceImage *sima;
2660         
2661         /* find an imagewindow showing render result */
2662         for(win=wm->windows.first; win; win=win->next) {
2663                 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2664                         if(sa->spacetype==SPACE_IMAGE) {
2665                                 sima= sa->spacedata.first;
2666                                 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2667                                         break;
2668                         }
2669                 }
2670         }
2671         
2672         return sa;
2673 }
2674
2675 static ScrArea *find_area_image_empty(bContext *C)
2676 {
2677         bScreen *sc= CTX_wm_screen(C);
2678         ScrArea *sa;
2679         SpaceImage *sima;
2680         
2681         /* find an imagewindow showing render result */
2682         for(sa=sc->areabase.first; sa; sa= sa->next) {
2683                 if(sa->spacetype==SPACE_IMAGE) {
2684                         sima= sa->spacedata.first;
2685                         if(!sima->image)
2686                                 break;
2687                 }
2688         }
2689         return sa;
2690 }
2691
2692 #if 0 // XXX not used
2693 static ScrArea *find_empty_image_area(bContext *C)
2694 {
2695         bScreen *sc= CTX_wm_screen(C);
2696         ScrArea *sa;
2697         SpaceImage *sima;
2698         
2699         /* find an imagewindow showing render result */
2700         for(sa=sc->areabase.first; sa; sa= sa->next) {
2701                 if(sa->spacetype==SPACE_IMAGE) {
2702                         sima= sa->spacedata.first;
2703                         if(!sima->image)
2704                                 break;
2705                 }
2706         }
2707         return sa;
2708 }
2709 #endif // XXX not used
2710
2711 /* new window uses x,y to set position */
2712 static void screen_set_image_output(bContext *C, int mx, int my)
2713 {
2714         wmWindow *win= CTX_wm_window(C);
2715         Scene *scene= CTX_data_scene(C);
2716         ScrArea *sa= NULL;
2717         SpaceImage *sima;
2718         
2719         if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2720                 rcti rect;
2721                 int sizex, sizey;
2722                 
2723                 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2724                 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2725                 
2726                 /* arbitrary... miniature image window views don't make much sense */
2727                 if(sizex < 320) sizex= 320;
2728                 if(sizey < 256) sizey= 256;
2729                 
2730                 /* XXX some magic to calculate postition */
2731                 rect.xmin= mx + win->posx - sizex/2;
2732                 rect.ymin= my + win->posy - sizey/2;
2733                 rect.xmax= rect.xmin + sizex;
2734                 rect.ymax= rect.ymin + sizey;
2735                 
2736                 /* changes context! */
2737                 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2738                 
2739                 sa= CTX_wm_area(C);
2740         }
2741         else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2742                 /* this function returns with changed context */
2743                 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2744                 sa= CTX_wm_area(C);
2745         }
2746         
2747         if(!sa) {
2748                 sa= find_area_showing_r_result(C);
2749                 if(sa==NULL)
2750                         sa= find_area_image_empty(C);
2751                 
2752                 if(sa==NULL) {
2753                         /* find largest open non-image area */
2754                         sa= biggest_non_image_area(C);
2755                         if(sa) {
2756                                 ED_area_newspace(C, sa, SPACE_IMAGE);
2757                                 sima= sa->spacedata.first;
2758                                 
2759                                 /* makes ESC go back to prev space */
2760                                 sima->flag |= SI_PREVSPACE;
2761                         }
2762                         else {
2763                                 /* use any area of decent size */
2764                                 sa= biggest_area(C);
2765                                 if(sa->spacetype!=SPACE_IMAGE) {
2766                                         // XXX newspace(sa, SPACE_IMAGE);
2767                                         sima= sa->spacedata.first;
2768                                         
2769                                         /* makes ESC go back to prev space */
2770                                         sima->flag |= SI_PREVSPACE;
2771                                 }
2772                         }
2773                 }
2774         }       
2775         sima= sa->spacedata.first;
2776         
2777         /* get the correct image, and scale it */
2778         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2779         
2780         //      if(G.displaymode==2) { // XXX
2781         if(sa->full) {
2782                 sima->flag |= SI_FULLWINDOW|SI_PREVSPACE;
2783                 
2784                 //                      ed_screen_fullarea(C, win, sa);
2785         }
2786         //      }
2787         
2788 }
2789
2790 /* executes blocking render */
2791 static int screen_render_exec(bContext *C, wmOperator *op)
2792 {
2793         Scene *scene= CTX_data_scene(C);
2794         Render *re= RE_GetRender(scene->id.name);
2795         
2796         if(re==NULL) {
2797                 re= RE_NewRender(scene->id.name);
2798         }
2799         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2800         
2801         if(RNA_boolean_get(op->ptr, "animation"))
2802                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step);
2803         else
2804                 RE_BlenderFrame(re, scene, scene->r.cfra);
2805         
2806         // no redraw needed, we leave state as we entered it
2807         ED_update_for_newframe(C, 1);
2808         
2809         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2810         
2811         return OPERATOR_FINISHED;
2812 }
2813
2814 typedef struct RenderJob {
2815         Scene *scene;
2816         Render *re;
2817         wmWindow *win;
2818         int anim;
2819         Image *image;
2820         ImageUser iuser;
2821         short *stop;
2822         short *do_update;
2823 } RenderJob;
2824
2825 static void render_freejob(void *rjv)
2826 {
2827         RenderJob *rj= rjv;
2828         
2829         MEM_freeN(rj);
2830 }
2831
2832 /* str is IMA_RW_MAXTEXT in size */
2833 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2834 {
2835         char info_time_str[32]; // used to be extern to header_info.c
2836         uintptr_t mem_in_use, mmap_in_use;
2837         float megs_used_memory, mmap_used_memory;
2838         char *spos= str;
2839         
2840         mem_in_use= MEM_get_memory_in_use();
2841         mmap_in_use= MEM_get_mapped_memory_in_use();
2842         
2843         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2844         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2845         
2846         if(scene->lay & 0xFF000000)
2847                 spos+= sprintf(spos, "Localview | ");
2848         else if(scene->r.scemode & R_SINGLE_LAYER)
2849                 spos+= sprintf(spos, "Single Layer | ");
2850         
2851         if(rs->statstr) {
2852                 spos+= sprintf(spos, "%s ", rs->statstr);
2853         }
2854         else {
2855                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2856                 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2857                 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2858                 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2859                 
2860                 if(rs->curfield)
2861                         spos+= sprintf(spos, "Field %d ", rs->curfield);
2862                 if(rs->curblur)
2863                         spos+= sprintf(spos, "Blur %d ", rs->curblur);
2864         }
2865         
2866         BLI_timestr(rs->lastframetime, info_time_str);
2867         spos+= sprintf(spos, "Time:%s ", info_time_str);
2868         
2869         if(rs->infostr && rs->infostr[0])
2870                 spos+= sprintf(spos, "| %s ", rs->infostr);
2871         
2872         /* very weak... but 512 characters is quite safe */
2873         if(spos >= str+IMA_RW_MAXTEXT)
2874                 printf("WARNING! renderwin text beyond limit \n");
2875         
2876 }
2877
2878 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2879 {
2880         RenderJob *rj= rjv;
2881         
2882         /* malloc OK here, stats_draw is not in tile threads */
2883         if(rj->image->render_text==NULL)
2884                 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
2885         
2886         make_renderinfo_string(rs, rj->scene, rj->image->render_text);
2887         
2888         /* make jobs timer to send notifier */
2889         *(rj->do_update)= 1;
2890         
2891 }
2892
2893 /* called inside thread! */
2894 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
2895 {
2896         float x1, y1, *rectf= NULL;
2897         int ymin, ymax, xmin, xmax;
2898         int rymin, rxmin;
2899         char *rectc;
2900         
2901         /* if renrect argument, we only refresh scanlines */
2902         if(renrect) {
2903                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
2904                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
2905                         return;
2906                 
2907                 /* xmin here is first subrect x coord, xmax defines subrect width */
2908                 xmin = renrect->xmin + rr->crop;
2909                 xmax = renrect->xmax - xmin - rr->crop;
2910                 if (xmax<2) return;
2911                 
2912                 ymin= renrect->ymin + rr->crop;
2913                 ymax= renrect->ymax - ymin - rr->crop;
2914                 if(ymax<2)
2915                         return;
2916                 renrect->ymin= renrect->ymax;
2917                 
2918         }
2919         else {
2920                 xmin = ymin = rr->crop;
2921                 xmax = rr->rectx - 2*rr->crop;
2922                 ymax = rr->recty - 2*rr->crop;
2923         }
2924         
2925         /* xmin ymin is in tile coords. transform to ibuf */
2926         rxmin= rr->tilerect.xmin + xmin;
2927         if(rxmin >= ibuf->x) return;
2928         rymin= rr->tilerect.ymin + ymin;
2929         if(rymin >= ibuf->y) return;
2930         
2931         if(rxmin + xmax > ibuf->x)
2932                 xmax= ibuf->x - rxmin;
2933         if(rymin + ymax > ibuf->y)
2934                 ymax= ibuf->y - rymin;
2935         
2936         if(xmax < 1 || ymax < 1) return;
2937         
2938         /* find current float rect for display, first case is after composit... still weak */
2939         if(rr->rectf)
2940                 rectf= rr->rectf;
2941         else {
2942                 if(rr->rect32)
2943                         return;
2944                 else {
2945                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
2946                         rectf= rr->renlay->rectf;
2947                 }
2948         }
2949         if(rectf==NULL) return;
2950         
2951         rectf+= 4*(rr->rectx*ymin + xmin);
2952         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
2953         
2954         /* XXX make nice consistent functions for this */
2955         if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
2956                 for(y1= 0; y1<ymax; y1++) {
2957                         float *rf= rectf;
2958                         float srgb[3];
2959                         char *rc= rectc;
2960                         
2961                         /* XXX temp. because crop offset */
2962                         if( rectc >= (char *)(ibuf->rect)) {
2963                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2964                                         srgb[0]= linearrgb_to_srgb(rf[0]);
2965                                         srgb[1]= linearrgb_to_srgb(rf[1]);
2966                                         srgb[2]= linearrgb_to_srgb(rf[2]);
2967                                         
2968                                         rc[0]= FTOCHAR(srgb[0]);
2969                                         rc[1]= FTOCHAR(srgb[1]);
2970                                         rc[2]= FTOCHAR(srgb[2]);
2971                                         rc[3]= FTOCHAR(rf[3]);
2972                                 }
2973                         }
2974                         rectf += 4*rr->rectx;
2975                         rectc += 4*ibuf->x;
2976                 }
2977         } else {
2978                 for(y1= 0; y1<ymax; y1++) {
2979                         float *rf= rectf;
2980                         char *rc= rectc;
2981                         
2982                         /* XXX temp. because crop offset */
2983                         if( rectc >= (char *)(ibuf->rect)) {
2984                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2985                                         rc[0]= FTOCHAR(rf[0]);
2986                                         rc[1]= FTOCHAR(rf[1]);
2987                                         rc[2]= FTOCHAR(rf[2]);
2988                                         rc[3]= FTOCHAR(rf[3]);
2989                                 }
2990                         }
2991                         rectf += 4*rr->rectx;
2992                         rectc += 4*ibuf->x;
2993                 }
2994         }
2995         
2996 }
2997
2998 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
2999 {
3000         RenderJob *rj= rjv;
3001         ImBuf *ibuf;
3002         void *lock;
3003         
3004         ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
3005         if(ibuf) {
3006                 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
3007                 
3008                 /* make jobs timer to send notifier */
3009                 *(rj->do_update)= 1;
3010         }
3011         BKE_image_release_ibuf(rj->image, lock);
3012 }
3013
3014 static void render_startjob(void *rjv, short *stop, short *do_update)
3015 {
3016         RenderJob *rj= rjv;
3017         
3018         rj->stop= stop;
3019         rj->do_update= do_update;
3020         
3021         if(rj->anim)
3022                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step);
3023         else
3024                 RE_BlenderFrame(rj->re, rj->scene, rj->scene->r.cfra);
3025 }
3026
3027 /* called by render, check job 'stop' value or the global */
3028 static int render_breakjob(void *rjv)
3029 {
3030         RenderJob *rj= rjv;
3031         
3032         if(G.afbreek)
3033                 return 1;
3034         if(rj->stop && *(rj->stop))
3035                 return 1;
3036         return 0;
3037 }
3038
3039 /* catch esc */
3040 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3041 {
3042         /* no running blender, remove handler and pass through */
3043         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
3044                 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
3045         
3046         /* running render */
3047         switch (event->type) {
3048                 case ESCKEY:
3049                         return OPERATOR_RUNNING_MODAL;
3050                         break;
3051         }
3052         return OPERATOR_PASS_THROUGH;
3053 }
3054
3055 /* using context, starts job */
3056 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3057 {
3058         /* new render clears all callbacks */
3059         Scene *scene= CTX_data_scene(C);
3060         Render *re;
3061         wmJob *steve;
3062         RenderJob *rj;
3063         Image *ima;
3064         
3065         /* only one render job at a time */
3066         if(WM_jobs_test(CTX_wm_manager(C), scene))
3067                 return OPERATOR_CANCELLED;
3068         
3069         /* stop all running jobs, currently previews frustrate Render */
3070         WM_jobs_stop_all(CTX_wm_manager(C));
3071         
3072         /* handle UI stuff */
3073         WM_cursor_wait(1);
3074         
3075         /* flush multires changes (for sculpt) */
3076         multires_force_update(CTX_data_active_object(C));
3077         
3078         /* get editmode results */
3079         ED_object_exit_editmode(C, EM_FREEDATA|EM_DO_UNDO);     /* 0 = does not exit editmode */
3080         
3081         // store spare
3082         // get view3d layer, local layer, make this nice api call to render
3083         // store spare
3084         
3085         /* ensure at least 1 area shows result */
3086         screen_set_image_output(C, event->x, event->y);
3087         
3088         /* job custom data */
3089         rj= MEM_callocN(sizeof(RenderJob), "render job");
3090         rj->scene= scene;
3091         rj->win= CTX_wm_window(C);
3092         rj->anim= RNA_boolean_get(op->ptr, "animation");
3093         rj->iuser.scene= scene;
3094         rj->iuser.ok= 1;
3095         
3096         /* setup job */
3097         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY);
3098         WM_jobs_customdata(steve, rj, render_freejob);
3099         WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
3100         WM_jobs_callbacks(steve, render_startjob, NULL, NULL);
3101         
3102         /* get a render result image, and make sure it is empty */
3103         ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
3104         BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
3105         rj->image= ima;
3106         
3107         /* setup new render */
3108         re= RE_NewRender(scene->id.name);
3109         RE_test_break_cb(re, rj, render_breakjob);
3110         RE_display_draw_cb(re, rj, image_rect_update);
3111         RE_stats_draw_cb(re, rj, image_renderinfo_cb);
3112         
3113         rj->re= re;
3114         G.afbreek= 0;
3115         
3116         //      BKE_report in render!
3117         //      RE_error_cb(re, error_cb);
3118         
3119         WM_jobs_start(CTX_wm_manager(C), steve);
3120         
3121         G.afbreek= 0;
3122         
3123         WM_cursor_wait(0);
3124         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
3125         
3126         /* add modal handler for ESC */
3127         WM_event_add_modal_handler(C, op);
3128         
3129         return OPERATOR_RUNNING_MODAL;
3130 }
3131
3132
3133 /* contextual render, using current scene, view3d? */
3134 static void SCREEN_OT_render(wmOperatorType *ot)
3135 {
3136         /* identifiers */
3137         ot->name= "Render";
3138         ot->description= "Render active scene.";
3139         ot->idname= "SCREEN_OT_render";
3140         
3141         /* api callbacks */
3142         ot->invoke= screen_render_invoke;
3143         ot->modal= screen_render_modal;
3144         ot->exec= screen_render_exec;
3145         
3146         ot->poll= ED_operator_screenactive;
3147         
3148         RNA_def_boolean(ot->srna, "animation", 0, "Animation", "");
3149 }
3150
3151 /* ****************************** opengl render *************************** */
3152
3153 typedef struct OGLRender {
3154         Render *re;
3155         Scene *scene;
3156         
3157         View3D *v3d;
3158         RegionView3D *rv3d;
3159         ARegion *ar;
3160         
3161         Image *ima;
3162         ImageUser iuser;
3163         
3164         GPUOffScreen *ofs;
3165         int sizex, sizey;
3166         
3167         bMovieHandle *mh;
3168         int cfrao, nfra;
3169         
3170         wmTimer *timer;
3171 } OGLRender;
3172
3173 static void screen_opengl_render_apply(OGLRender *oglrender)
3174 {
3175         Scene *scene= oglrender->scene;
3176         ARegion *ar= oglrender->ar;
3177         View3D *v3d= oglrender->v3d;
3178         RegionView3D *rv3d= oglrender->rv3d;
3179         RenderResult *rr;
3180         ImBuf *ibuf;
3181         void *lock;
3182         float winmat[4][4];
3183         int sizex= oglrender->sizex;
3184         int sizey= oglrender->sizey;
3185         
3186         /* bind */
3187         GPU_offscreen_bind(oglrender->ofs);
3188         
3189         /* render 3d view */
3190         if(rv3d->persp==RV3D_CAMOB && v3d->camera) {
3191                 RE_GetCameraWindow(oglrender->re, v3d->camera, scene->r.cfra, winmat);
3192                 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat);
3193         }
3194         else
3195                 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, NULL);
3196         
3197         /* read in pixels & stamp */
3198         rr= RE_AcquireResultRead(oglrender->re);
3199         glReadPixels(0, 0, sizex, sizey, GL_RGBA, GL_FLOAT, rr->rectf);
3200         if((scene->r.scemode & R_STAMP_INFO) && (scene->r.stamp & R_STAMP_DRAW))
3201                 BKE_stamp_buf(scene, (unsigned char *)rr->rect32, rr->rectf, rr->rectx, rr->recty, 3);
3202         RE_ReleaseResult(oglrender->re);
3203         
3204         /* update byte from float buffer */
3205         ibuf= BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
3206         if(ibuf) image_buffer_rect_update(NULL, rr, ibuf, NULL);
3207         BKE_image_release_ibuf(oglrender->ima, lock);
3208         
3209         /* unbind */
3210         GPU_offscreen_unbind(oglrender->ofs);
3211 }
3212
3213 static int screen_opengl_render_init(bContext *C, wmOperator *op)
3214 {
3215         /* new render clears all callbacks */
3216         Scene *scene= CTX_data_scene(C);
3217         RenderResult *rr;
3218         GPUOffScreen *ofs;
3219         OGLRender *oglrender;
3220         int sizex, sizey;