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