fe2bc58faf2e148147c9be0afcfff1d51c32cb56
[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                 
2343                 /* sync, don't sync, or follow scene setting */
2344                 if(sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2345                 else if(sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2346                 else sync= (scene->audio.flag & AUDIO_SYNC);
2347                 
2348                 if(sync) {
2349                         /* skip frames */
2350                         int step = floor(wt->duration * FPS);
2351                         if(sad->flag & ANIMPLAY_FLAG_REVERSE) // XXX does this option work with audio?
2352                                 scene->r.cfra -= step;
2353                         else
2354                                 scene->r.cfra += step;
2355                         wt->duration -= ((float)step)/FPS;
2356                 }
2357                 else {
2358                         /* one frame +/- */
2359                         if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2360                                 scene->r.cfra--;
2361                         else
2362                                 scene->r.cfra++;
2363                 }
2364                 
2365                 /* reset 'jumped' flag before checking if we need to jump... */
2366                 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2367                 
2368                 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2369                         /* jump back to end? */
2370                         if (scene->r.psfra) {
2371                                 if (scene->r.cfra < scene->r.psfra) {
2372                                         scene->r.cfra= scene->r.pefra;
2373                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2374                                 }
2375                         }
2376                         else {
2377                                 if (scene->r.cfra < scene->r.sfra) {
2378                                         scene->r.cfra= scene->r.efra;
2379                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2380                                 }
2381                         }
2382                 }
2383                 else {
2384                         /* jump back to start? */
2385                         if (scene->r.psfra) {
2386                                 if (scene->r.cfra > scene->r.pefra) {
2387                                         scene->r.cfra= scene->r.psfra;
2388                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2389                                 }
2390                         }
2391                         else {
2392                                 if (scene->r.cfra > scene->r.efra) {
2393                                         scene->r.cfra= scene->r.sfra;
2394                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2395                                 }
2396                         }
2397                 }
2398                 
2399                 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2400                 ED_update_for_newframe(C, 1);
2401                 
2402                 sound_update_playing(C);
2403                 
2404                 for(sa= screen->areabase.first; sa; sa= sa->next) {
2405                         ARegion *ar;
2406                         for(ar= sa->regionbase.first; ar; ar= ar->next) {
2407                                 if(ar==sad->ar)
2408                                         ED_region_tag_redraw(ar);
2409                                 else
2410                                         if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2411                                                 ED_region_tag_redraw(ar);
2412                         }
2413                 }
2414                 
2415                 /* recalculate the timestep for the timer now that we've finished calculating this,
2416                  * since the frames-per-second value may have been changed
2417                  */
2418                 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2419                 wt->timestep= (1.0/FPS);
2420                 
2421                 return OPERATOR_FINISHED;
2422         }
2423         return OPERATOR_PASS_THROUGH;
2424 }
2425
2426 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2427 {
2428         /* identifiers */
2429         ot->name= "Animation Step";
2430         ot->description= "Step through animation by position.";
2431         ot->idname= "SCREEN_OT_animation_step";
2432         
2433         /* api callbacks */
2434         ot->invoke= screen_animation_step;
2435         
2436         ot->poll= ED_operator_screenactive;
2437         
2438 }
2439
2440 /* ****************** anim player, starts or ends timer ***************** */
2441
2442 /* toggle operator */
2443 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
2444 {
2445         bScreen *screen= CTX_wm_screen(C);
2446         
2447         if(screen->animtimer) {
2448                 /* stop playback now */
2449                 ED_screen_animation_timer(C, 0, 0, 0);
2450                 sound_stop_all(C);
2451         }
2452         else {
2453                 ScrArea *sa= CTX_wm_area(C);
2454                 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2455                 int sync= -1;
2456                 
2457                 if(RNA_property_is_set(op->ptr, "sync"))
2458                         sync= (RNA_boolean_get(op->ptr, "sync"));
2459                 
2460                 /* timeline gets special treatment since it has it's own menu for determining redraws */
2461                 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2462                         SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2463                         
2464                         ED_screen_animation_timer(C, stime->redraws, sync, mode);
2465                         
2466                         /* update region if TIME_REGION was set, to leftmost 3d window */
2467                         ED_screen_animation_timer_update(screen, stime->redraws);
2468                 }
2469                 else {
2470                         int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2471                         
2472                         /* XXX - would like a better way to deal with this situation - Campbell */
2473                         if((sa) && (sa->spacetype == SPACE_SEQ)) {
2474                                 redraws |= TIME_SEQ;
2475                         }
2476                         
2477                         ED_screen_animation_timer(C, redraws, sync, mode);
2478                         
2479                         if(screen->animtimer) {
2480                                 wmTimer *wt= screen->animtimer;
2481                                 ScreenAnimData *sad= wt->customdata;
2482                                 
2483                                 sad->ar= CTX_wm_region(C);
2484                         }
2485                 }
2486         }
2487         
2488         return OPERATOR_FINISHED;
2489 }
2490
2491 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2492 {
2493         /* identifiers */
2494         ot->name= "Play Animation";
2495         ot->description= "Play animation.";
2496         ot->idname= "SCREEN_OT_animation_play";
2497         
2498         /* api callbacks */
2499         ot->invoke= screen_animation_play;
2500         
2501         ot->poll= ED_operator_screenactive;
2502         
2503         RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2504         RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate and stay in sync with audio.");
2505 }
2506
2507 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2508 {
2509         bScreen *screen= CTX_wm_screen(C);
2510         
2511         if(screen->animtimer) {
2512                 ScreenAnimData *sad= screen->animtimer->customdata;
2513                 Scene *scene= CTX_data_scene(C);
2514                 
2515                 /* reset current frame before stopping, and just send a notifier to deal with the rest 
2516                  * (since playback still needs to be stopped)
2517                  */
2518                 scene->r.cfra= sad->sfra;
2519                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2520                 
2521                 /* call the other "toggling" operator to clean up now */
2522                 return screen_animation_play(C, op, event);
2523         }
2524         
2525         return OPERATOR_PASS_THROUGH;
2526 }
2527
2528 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2529 {
2530         /* identifiers */
2531         ot->name= "Cancel Animation";
2532         ot->description= "Cancel animation, returning to the original frame.";
2533         ot->idname= "SCREEN_OT_animation_cancel";
2534         
2535         /* api callbacks */
2536         ot->invoke= screen_animation_cancel;
2537         
2538         ot->poll= ED_operator_screenactive;
2539 }
2540
2541 /* ************** border select operator (template) ***************************** */
2542
2543 /* operator state vars used: (added by default WM callbacks)   
2544  xmin, ymin     
2545  xmax, ymax     
2546  
2547  customdata: the wmGesture pointer
2548  
2549  callbacks:
2550  
2551  exec() has to be filled in by user
2552  
2553  invoke() default WM function
2554  adds modal handler
2555  
2556  modal()        default WM function 
2557  accept modal events while doing it, calls exec(), handles ESC and border drawing
2558  
2559  poll() has to be filled in by user for context
2560  */
2561 #if 0
2562 static int border_select_do(bContext *C, wmOperator *op)
2563 {
2564         int event_type= RNA_int_get(op->ptr, "event_type");
2565         
2566         if(event_type==LEFTMOUSE)
2567                 printf("border select do select\n");
2568         else if(event_type==RIGHTMOUSE)
2569                 printf("border select deselect\n");
2570         else 
2571                 printf("border select do something\n");
2572         
2573         return 1;
2574 }
2575
2576 static void SCREEN_OT_border_select(wmOperatorType *ot)
2577 {
2578         /* identifiers */
2579         ot->name= "Border select";
2580         ot->idname= "SCREEN_OT_border_select";
2581         
2582         /* api callbacks */
2583         ot->exec= border_select_do;
2584         ot->invoke= WM_border_select_invoke;
2585         ot->modal= WM_border_select_modal;
2586         
2587         ot->poll= ED_operator_areaactive;
2588         
2589         /* rna */
2590         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2591         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2592         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2593         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2594         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2595         
2596 }
2597 #endif
2598
2599 /* ****************************** render invoking ***************** */
2600
2601 /* set callbacks, exported to sequence render too. 
2602  Only call in foreground (UI) renders. */
2603
2604 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2605 /* window as the last possible alternative.                                                                        */
2606 static ScrArea *biggest_non_image_area(bContext *C)
2607 {
2608         bScreen *sc= CTX_wm_screen(C);
2609         ScrArea *sa, *big= NULL;
2610         int size, maxsize= 0, bwmaxsize= 0;
2611         short foundwin= 0;
2612         
2613         for(sa= sc->areabase.first; sa; sa= sa->next) {
2614                 if(sa->winx > 30 && sa->winy > 30) {
2615                         size= sa->winx*sa->winy;
2616                         if(sa->spacetype == SPACE_BUTS) {
2617                                 if(foundwin == 0 && size > bwmaxsize) {
2618                                         bwmaxsize= size;
2619                                         big= sa;        
2620                                 }
2621                         }
2622                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2623                                 maxsize= size;
2624                                 big= sa;
2625                                 foundwin= 1;
2626                         }
2627                 }
2628         }
2629         
2630         return big;
2631 }
2632
2633 static ScrArea *biggest_area(bContext *C)
2634 {
2635         bScreen *sc= CTX_wm_screen(C);
2636         ScrArea *sa, *big= NULL;
2637         int size, maxsize= 0;
2638         
2639         for(sa= sc->areabase.first; sa; sa= sa->next) {
2640                 size= sa->winx*sa->winy;
2641                 if(size > maxsize) {
2642                         maxsize= size;
2643                         big= sa;
2644                 }
2645         }
2646         return big;
2647 }
2648
2649
2650 static ScrArea *find_area_showing_r_result(bContext *C)
2651 {
2652         wmWindowManager *wm= CTX_wm_manager(C);
2653         wmWindow *win;
2654         ScrArea *sa = NULL;
2655         SpaceImage *sima;
2656         
2657         /* find an imagewindow showing render result */
2658         for(win=wm->windows.first; win; win=win->next) {
2659                 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2660                         if(sa->spacetype==SPACE_IMAGE) {
2661                                 sima= sa->spacedata.first;
2662                                 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2663                                         break;
2664                         }
2665                 }
2666         }
2667         
2668         return sa;
2669 }
2670
2671 static ScrArea *find_area_image_empty(bContext *C)
2672 {
2673         bScreen *sc= CTX_wm_screen(C);
2674         ScrArea *sa;
2675         SpaceImage *sima;
2676         
2677         /* find an imagewindow showing render result */
2678         for(sa=sc->areabase.first; sa; sa= sa->next) {
2679                 if(sa->spacetype==SPACE_IMAGE) {
2680                         sima= sa->spacedata.first;
2681                         if(!sima->image)
2682                                 break;
2683                 }
2684         }
2685         return sa;
2686 }
2687
2688 #if 0 // XXX not used
2689 static ScrArea *find_empty_image_area(bContext *C)
2690 {
2691         bScreen *sc= CTX_wm_screen(C);
2692         ScrArea *sa;
2693         SpaceImage *sima;
2694         
2695         /* find an imagewindow showing render result */
2696         for(sa=sc->areabase.first; sa; sa= sa->next) {
2697                 if(sa->spacetype==SPACE_IMAGE) {
2698                         sima= sa->spacedata.first;
2699                         if(!sima->image)
2700                                 break;
2701                 }
2702         }
2703         return sa;
2704 }
2705 #endif // XXX not used
2706
2707 /* new window uses x,y to set position */
2708 static void screen_set_image_output(bContext *C, int mx, int my)
2709 {
2710         wmWindow *win= CTX_wm_window(C);
2711         Scene *scene= CTX_data_scene(C);
2712         ScrArea *sa= NULL;
2713         SpaceImage *sima;
2714         
2715         if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2716                 rcti rect;
2717                 int sizex, sizey;
2718                 
2719                 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2720                 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2721                 
2722                 /* arbitrary... miniature image window views don't make much sense */
2723                 if(sizex < 320) sizex= 320;
2724                 if(sizey < 256) sizey= 256;
2725                 
2726                 /* XXX some magic to calculate postition */
2727                 rect.xmin= mx + win->posx - sizex/2;
2728                 rect.ymin= my + win->posy - sizey/2;
2729                 rect.xmax= rect.xmin + sizex;
2730                 rect.ymax= rect.ymin + sizey;
2731                 
2732                 /* changes context! */
2733                 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2734                 
2735                 sa= CTX_wm_area(C);
2736         }
2737         else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2738                 /* this function returns with changed context */
2739                 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2740                 sa= CTX_wm_area(C);
2741         }
2742         
2743         if(!sa) {
2744                 sa= find_area_showing_r_result(C);
2745                 if(sa==NULL)
2746                         sa= find_area_image_empty(C);
2747                 
2748                 if(sa==NULL) {
2749                         /* find largest open non-image area */
2750                         sa= biggest_non_image_area(C);
2751                         if(sa) {
2752                                 ED_area_newspace(C, sa, SPACE_IMAGE);
2753                                 sima= sa->spacedata.first;
2754                                 
2755                                 /* makes ESC go back to prev space */
2756                                 sima->flag |= SI_PREVSPACE;
2757                         }
2758                         else {
2759                                 /* use any area of decent size */
2760                                 sa= biggest_area(C);
2761                                 if(sa->spacetype!=SPACE_IMAGE) {
2762                                         // XXX newspace(sa, SPACE_IMAGE);
2763                                         sima= sa->spacedata.first;
2764                                         
2765                                         /* makes ESC go back to prev space */
2766                                         sima->flag |= SI_PREVSPACE;
2767                                 }
2768                         }
2769                 }
2770         }       
2771         sima= sa->spacedata.first;
2772         
2773         /* get the correct image, and scale it */
2774         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2775         
2776         //      if(G.displaymode==2) { // XXX
2777         if(sa->full) {
2778                 sima->flag |= SI_FULLWINDOW|SI_PREVSPACE;
2779                 
2780                 //                      ed_screen_fullarea(C, win, sa);
2781         }
2782         //      }
2783         
2784 }
2785
2786 /* executes blocking render */
2787 static int screen_render_exec(bContext *C, wmOperator *op)
2788 {
2789         Scene *scene= CTX_data_scene(C);
2790         Render *re= RE_GetRender(scene->id.name);
2791         
2792         if(re==NULL) {
2793                 re= RE_NewRender(scene->id.name);
2794         }
2795         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2796         
2797         if(RNA_boolean_get(op->ptr, "animation"))
2798                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step);
2799         else
2800                 RE_BlenderFrame(re, scene, scene->r.cfra);
2801         
2802         // no redraw needed, we leave state as we entered it
2803         ED_update_for_newframe(C, 1);
2804         
2805         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2806         
2807         return OPERATOR_FINISHED;
2808 }
2809
2810 typedef struct RenderJob {
2811         Scene *scene;
2812         Render *re;
2813         wmWindow *win;
2814         int anim;
2815         Image *image;
2816         ImageUser iuser;
2817         short *stop;
2818         short *do_update;
2819 } RenderJob;
2820
2821 static void render_freejob(void *rjv)
2822 {
2823         RenderJob *rj= rjv;
2824         
2825         MEM_freeN(rj);
2826 }
2827
2828 /* str is IMA_RW_MAXTEXT in size */
2829 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2830 {
2831         char info_time_str[32]; // used to be extern to header_info.c
2832         uintptr_t mem_in_use, mmap_in_use;
2833         float megs_used_memory, mmap_used_memory;
2834         char *spos= str;
2835         
2836         mem_in_use= MEM_get_memory_in_use();
2837         mmap_in_use= MEM_get_mapped_memory_in_use();
2838         
2839         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2840         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2841         
2842         if(scene->lay & 0xFF000000)
2843                 spos+= sprintf(spos, "Localview | ");
2844         else if(scene->r.scemode & R_SINGLE_LAYER)
2845                 spos+= sprintf(spos, "Single Layer | ");
2846         
2847         if(rs->statstr) {
2848                 spos+= sprintf(spos, "%s ", rs->statstr);
2849         }
2850         else {
2851                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2852                 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2853                 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2854                 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2855                 
2856                 if(rs->curfield)
2857                         spos+= sprintf(spos, "Field %d ", rs->curfield);
2858                 if(rs->curblur)
2859                         spos+= sprintf(spos, "Blur %d ", rs->curblur);
2860         }
2861         
2862         BLI_timestr(rs->lastframetime, info_time_str);
2863         spos+= sprintf(spos, "Time:%s ", info_time_str);
2864         
2865         if(rs->infostr && rs->infostr[0])
2866                 spos+= sprintf(spos, "| %s ", rs->infostr);
2867         
2868         /* very weak... but 512 characters is quite safe */
2869         if(spos >= str+IMA_RW_MAXTEXT)
2870                 printf("WARNING! renderwin text beyond limit \n");
2871         
2872 }
2873
2874 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2875 {
2876         RenderJob *rj= rjv;
2877         
2878         /* malloc OK here, stats_draw is not in tile threads */
2879         if(rj->image->render_text==NULL)
2880                 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
2881         
2882         make_renderinfo_string(rs, rj->scene, rj->image->render_text);
2883         
2884         /* make jobs timer to send notifier */
2885         *(rj->do_update)= 1;
2886         
2887 }
2888
2889 /* called inside thread! */
2890 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
2891 {
2892         float x1, y1, *rectf= NULL;
2893         int ymin, ymax, xmin, xmax;
2894         int rymin, rxmin;
2895         char *rectc;
2896         
2897         /* if renrect argument, we only refresh scanlines */
2898         if(renrect) {
2899                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
2900                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
2901                         return;
2902                 
2903                 /* xmin here is first subrect x coord, xmax defines subrect width */
2904                 xmin = renrect->xmin + rr->crop;
2905                 xmax = renrect->xmax - xmin - rr->crop;
2906                 if (xmax<2) return;
2907                 
2908                 ymin= renrect->ymin + rr->crop;
2909                 ymax= renrect->ymax - ymin - rr->crop;
2910                 if(ymax<2)
2911                         return;
2912                 renrect->ymin= renrect->ymax;
2913                 
2914         }
2915         else {
2916                 xmin = ymin = rr->crop;
2917                 xmax = rr->rectx - 2*rr->crop;
2918                 ymax = rr->recty - 2*rr->crop;
2919         }
2920         
2921         /* xmin ymin is in tile coords. transform to ibuf */
2922         rxmin= rr->tilerect.xmin + xmin;
2923         if(rxmin >= ibuf->x) return;
2924         rymin= rr->tilerect.ymin + ymin;
2925         if(rymin >= ibuf->y) return;
2926         
2927         if(rxmin + xmax > ibuf->x)
2928                 xmax= ibuf->x - rxmin;
2929         if(rymin + ymax > ibuf->y)
2930                 ymax= ibuf->y - rymin;
2931         
2932         if(xmax < 1 || ymax < 1) return;
2933         
2934         /* find current float rect for display, first case is after composit... still weak */
2935         if(rr->rectf)
2936                 rectf= rr->rectf;
2937         else {
2938                 if(rr->rect32)
2939                         return;
2940                 else {
2941                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
2942                         rectf= rr->renlay->rectf;
2943                 }
2944         }
2945         if(rectf==NULL) return;
2946         
2947         rectf+= 4*(rr->rectx*ymin + xmin);
2948         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
2949         
2950         /* XXX make nice consistent functions for this */
2951         if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
2952                 for(y1= 0; y1<ymax; y1++) {
2953                         float *rf= rectf;
2954                         float srgb[3];
2955                         char *rc= rectc;
2956                         
2957                         /* XXX temp. because crop offset */
2958                         if( rectc >= (char *)(ibuf->rect)) {
2959                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2960                                         srgb[0]= linearrgb_to_srgb(rf[0]);
2961                                         srgb[1]= linearrgb_to_srgb(rf[1]);
2962                                         srgb[2]= linearrgb_to_srgb(rf[2]);
2963                                         
2964                                         rc[0]= FTOCHAR(srgb[0]);
2965                                         rc[1]= FTOCHAR(srgb[1]);
2966                                         rc[2]= FTOCHAR(srgb[2]);
2967                                         rc[3]= FTOCHAR(rf[3]);
2968                                 }
2969                         }
2970                         rectf += 4*rr->rectx;
2971                         rectc += 4*ibuf->x;
2972                 }
2973         } else {
2974                 for(y1= 0; y1<ymax; y1++) {
2975                         float *rf= rectf;
2976                         char *rc= rectc;
2977                         
2978                         /* XXX temp. because crop offset */
2979                         if( rectc >= (char *)(ibuf->rect)) {
2980                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2981                                         rc[0]= FTOCHAR(rf[0]);
2982                                         rc[1]= FTOCHAR(rf[1]);
2983                                         rc[2]= FTOCHAR(rf[2]);
2984                                         rc[3]= FTOCHAR(rf[3]);
2985                                 }
2986                         }
2987                         rectf += 4*rr->rectx;
2988                         rectc += 4*ibuf->x;
2989                 }
2990         }
2991         
2992 }
2993
2994 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
2995 {
2996         RenderJob *rj= rjv;
2997         ImBuf *ibuf;
2998         void *lock;
2999         
3000         ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
3001         if(ibuf) {
3002                 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
3003                 
3004                 /* make jobs timer to send notifier */
3005                 *(rj->do_update)= 1;
3006         }
3007         BKE_image_release_ibuf(rj->image, lock);
3008 }
3009
3010 static void render_startjob(void *rjv, short *stop, short *do_update)
3011 {
3012         RenderJob *rj= rjv;
3013         
3014         rj->stop= stop;
3015         rj->do_update= do_update;
3016         
3017         if(rj->anim)
3018                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step);
3019         else
3020                 RE_BlenderFrame(rj->re, rj->scene, rj->scene->r.cfra);
3021 }
3022
3023 /* called by render, check job 'stop' value or the global */
3024 static int render_breakjob(void *rjv)
3025 {
3026         RenderJob *rj= rjv;
3027         
3028         if(G.afbreek)
3029                 return 1;
3030         if(rj->stop && *(rj->stop))
3031                 return 1;
3032         return 0;
3033 }
3034
3035 /* catch esc */
3036 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3037 {
3038         /* no running blender, remove handler and pass through */
3039         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
3040                 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
3041         
3042         /* running render */
3043         switch (event->type) {
3044                 case ESCKEY:
3045                         return OPERATOR_RUNNING_MODAL;
3046                         break;
3047         }
3048         return OPERATOR_PASS_THROUGH;
3049 }
3050
3051 /* using context, starts job */
3052 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3053 {
3054         /* new render clears all callbacks */
3055         Scene *scene= CTX_data_scene(C);
3056         Render *re;
3057         wmJob *steve;
3058         RenderJob *rj;
3059         Image *ima;
3060         
3061         /* only one render job at a time */
3062         if(WM_jobs_test(CTX_wm_manager(C), scene))
3063                 return OPERATOR_CANCELLED;
3064         
3065         /* stop all running jobs, currently previews frustrate Render */
3066         WM_jobs_stop_all(CTX_wm_manager(C));
3067         
3068         /* handle UI stuff */
3069         WM_cursor_wait(1);
3070         
3071         /* flush multires changes (for sculpt) */
3072         multires_force_update(CTX_data_active_object(C));
3073         
3074         /* get editmode results */
3075         ED_object_exit_editmode(C, EM_FREEDATA|EM_DO_UNDO);     /* 0 = does not exit editmode */
3076         
3077         // store spare
3078         // get view3d layer, local layer, make this nice api call to render
3079         // store spare
3080         
3081         /* ensure at least 1 area shows result */
3082         screen_set_image_output(C, event->x, event->y);
3083         
3084         /* job custom data */
3085         rj= MEM_callocN(sizeof(RenderJob), "render job");
3086         rj->scene= scene;
3087         rj->win= CTX_wm_window(C);
3088         rj->anim= RNA_boolean_get(op->ptr, "animation");
3089         rj->iuser.scene= scene;
3090         rj->iuser.ok= 1;
3091         
3092         /* setup job */
3093         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY);
3094         WM_jobs_customdata(steve, rj, render_freejob);
3095         WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
3096         WM_jobs_callbacks(steve, render_startjob, NULL, NULL);
3097         
3098         /* get a render result image, and make sure it is empty */
3099         ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
3100         BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
3101         rj->image= ima;
3102         
3103         /* setup new render */
3104         re= RE_NewRender(scene->id.name);
3105         RE_test_break_cb(re, rj, render_breakjob);
3106         RE_display_draw_cb(re, rj, image_rect_update);
3107         RE_stats_draw_cb(re, rj, image_renderinfo_cb);
3108         
3109         rj->re= re;
3110         G.afbreek= 0;
3111         
3112         //      BKE_report in render!
3113         //      RE_error_cb(re, error_cb);
3114         
3115         WM_jobs_start(CTX_wm_manager(C), steve);
3116         
3117         G.afbreek= 0;
3118         
3119         WM_cursor_wait(0);
3120         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
3121         
3122         /* add modal handler for ESC */
3123         WM_event_add_modal_handler(C, op);
3124         
3125         return OPERATOR_RUNNING_MODAL;
3126 }
3127
3128
3129 /* contextual render, using current scene, view3d? */
3130 static void SCREEN_OT_render(wmOperatorType *ot)
3131 {
3132         /* identifiers */
3133         ot->name= "Render";
3134         ot->description= "Render active scene.";
3135         ot->idname= "SCREEN_OT_render";
3136         
3137         /* api callbacks */
3138         ot->invoke= screen_render_invoke;
3139         ot->modal= screen_render_modal;
3140         ot->exec= screen_render_exec;
3141         
3142         ot->poll= ED_operator_screenactive;
3143         
3144         RNA_def_boolean(ot->srna, "animation", 0, "Animation", "");
3145 }
3146
3147 /* ****************************** opengl render *************************** */
3148
3149 typedef struct OGLRender {
3150         Render *re;
3151         Scene *scene;
3152         
3153         View3D *v3d;
3154         RegionView3D *rv3d;
3155         ARegion *ar;
3156         
3157         Image *ima;
3158         ImageUser iuser;
3159         
3160         GPUOffScreen *ofs;
3161         int sizex, sizey;
3162         
3163         bMovieHandle *mh;
3164         int cfrao, nfra;
3165         
3166         wmTimer *timer;
3167 } OGLRender;
3168
3169 static void screen_opengl_render_apply(OGLRender *oglrender)
3170 {
3171         Scene *scene= oglrender->scene;
3172         ARegion *ar= oglrender->ar;
3173         View3D *v3d= oglrender->v3d;
3174         RegionView3D *rv3d= oglrender->rv3d;
3175         RenderResult *rr;
3176         ImBuf *ibuf;
3177         void *lock;
3178         float winmat[4][4];
3179         int sizex= oglrender->sizex;
3180         int sizey= oglrender->sizey;
3181         
3182         /* bind */
3183         GPU_offscreen_bind(oglrender->ofs);
3184         
3185         /* render 3d view */
3186         if(rv3d->persp==RV3D_CAMOB && v3d->camera) {
3187                 RE_GetCameraWindow(oglrender->re, v3d->camera, scene->r.cfra, winmat);
3188                 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat);
3189         }
3190         else
3191                 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, NULL);
3192         
3193         /* read in pixels & stamp */
3194         rr= RE_AcquireResultRead(oglrender->re);
3195         glReadPixels(0, 0, sizex, sizey, GL_RGBA, GL_FLOAT, rr->rectf);
3196         if((scene->r.scemode & R_STAMP_INFO) && (scene->r.stamp & R_STAMP_DRAW))
3197                 BKE_stamp_buf(scene, (unsigned char *)rr->rect32, rr->rectf, rr->rectx, rr->recty, 3);
3198         RE_ReleaseResult(oglrender->re);
3199         
3200         /* update byte from float buffer */
3201         ibuf= BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
3202         if(ibuf) image_buffer_rect_update(NULL, rr, ibuf, NULL);
3203         BKE_image_release_ibuf(oglrender->ima, lock);
3204         
3205         /* unbind */
3206         GPU_offscreen_unbind(oglrender->ofs);
3207 }
3208
3209 static int screen_opengl_render_init(bContext *C, wmOperator *op)
3210 {
3211         /* new render clears all callbacks */
3212         Scene *scene= CTX_data_scene(C);
3213         RenderResult *rr;
3214         GPUOffScreen *ofs;