2.5 Audio:
[blender-staging.git] / source / blender / editors / screen / screen_ops.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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                 int maxsize;
1378                 
1379                 op->customdata= rmd;
1380                 
1381                 rmd->az = az;
1382                 rmd->ar= az->ar;
1383                 rmd->sa = sad->sa1;
1384                 rmd->edge= az->edge;
1385                 rmd->origx= event->x;
1386                 rmd->origy= event->y;
1387                 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1388                 
1389                 /* if not set we do now, otherwise it uses type */
1390                 if(rmd->ar->sizex==0) 
1391                         rmd->ar->sizex= rmd->ar->type->prefsizex;
1392                 if(rmd->ar->sizey==0) 
1393                         rmd->ar->sizey= rmd->ar->type->prefsizey;
1394                 
1395                 /* now copy to regionmovedata */
1396                 if(rmd->edge=='l' || rmd->edge=='r') {
1397                         rmd->origval= rmd->ar->sizex;
1398                 } else {
1399                         rmd->origval= rmd->ar->sizey;
1400                 }
1401                 
1402                 /* limit headers to standard height for now */
1403                 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1404                         maxsize = rmd->ar->type->prefsizey;
1405                 else
1406                         maxsize = 1000;
1407                 
1408                 CLAMP(rmd->maxsize, 0, maxsize);
1409                 
1410                 /* add temp handler */
1411                 WM_event_add_modal_handler(C, op);
1412                 
1413                 return OPERATOR_RUNNING_MODAL;
1414         }
1415         
1416         return OPERATOR_FINISHED;
1417 }
1418
1419 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1420 {
1421         RegionMoveData *rmd= op->customdata;
1422         int delta;
1423         
1424         /* execute the events */
1425         switch(event->type) {
1426                 case MOUSEMOVE:
1427                         
1428                         if(rmd->edge=='l' || rmd->edge=='r') {
1429                                 delta= event->x - rmd->origx;
1430                                 if(rmd->edge=='l') delta= -delta;
1431                                 
1432                                 rmd->ar->sizex= rmd->origval + delta;
1433                                 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
1434                                 
1435                                 if(rmd->ar->sizex < 24) {
1436                                         rmd->ar->sizex= rmd->origval;
1437                                         if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1438                                                 ED_region_toggle_hidden(C, rmd->ar);
1439                                 }
1440                                 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1441                                         ED_region_toggle_hidden(C, rmd->ar);
1442                         }
1443                         else {
1444                                 delta= event->y - rmd->origy;
1445                                 if(rmd->edge=='b') delta= -delta;
1446                                 
1447                                 rmd->ar->sizey= rmd->origval + delta;
1448                                 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
1449                                 
1450                                 if(rmd->ar->sizey < 24) {
1451                                         rmd->ar->sizey= rmd->origval;
1452                                         if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1453                                                 ED_region_toggle_hidden(C, rmd->ar);
1454                                 }
1455                                 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1456                                         ED_region_toggle_hidden(C, rmd->ar);
1457                         }
1458 #ifdef WM_FAST_DRAW
1459                         ED_area_tag_redraw(rmd->sa);
1460 #endif
1461                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1462                         
1463                         break;
1464                         
1465                 case LEFTMOUSE:
1466                         if(event->val==KM_RELEASE) {
1467                                 
1468                                 if(ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1469                                         if(rmd->ar->flag & RGN_FLAG_HIDDEN) {
1470                                                 ED_region_toggle_hidden(C, rmd->ar);
1471 #ifdef WM_FAST_DRAW
1472                                                 ED_area_tag_redraw(rmd->sa);
1473 #endif
1474                                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1475                                         }
1476                                 }
1477                                 MEM_freeN(op->customdata);
1478                                 op->customdata = NULL;
1479                                 
1480                                 return OPERATOR_FINISHED;
1481                         }
1482                         break;
1483                         
1484                 case ESCKEY:
1485                         ;
1486         }
1487         
1488         return OPERATOR_RUNNING_MODAL;
1489 }
1490
1491
1492 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1493 {
1494         /* identifiers */
1495         ot->name= "Scale Region Size";
1496         ot->description= "Scale selected area";
1497         ot->idname= "SCREEN_OT_region_scale";
1498         
1499         ot->invoke= region_scale_invoke;
1500         ot->modal= region_scale_modal;
1501         
1502         ot->poll= ED_operator_areaactive;
1503         
1504         ot->flag= OPTYPE_BLOCKING;
1505 }
1506
1507
1508 /* ************** frame change operator ***************************** */
1509
1510 /* function to be called outside UI context, or for redo */
1511 static int frame_offset_exec(bContext *C, wmOperator *op)
1512 {
1513         int delta;
1514         
1515         delta = RNA_int_get(op->ptr, "delta");
1516
1517         CTX_data_scene(C)->r.cfra += delta;
1518         
1519         sound_seek_scene(C);
1520
1521         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1522         
1523         return OPERATOR_FINISHED;
1524 }
1525
1526 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
1527 {
1528         ot->name = "Frame Offset";
1529         ot->idname = "SCREEN_OT_frame_offset";
1530         
1531         ot->exec= frame_offset_exec;
1532         
1533         ot->poll= ED_operator_screenactive;
1534         ot->flag= 0;
1535         
1536         /* rna */
1537         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1538 }
1539
1540
1541 /* function to be called outside UI context, or for redo */
1542 static int frame_jump_exec(bContext *C, wmOperator *op)
1543 {
1544         Scene *scene= CTX_data_scene(C);
1545         
1546         if (RNA_boolean_get(op->ptr, "end"))
1547                 CFRA= PEFRA;
1548         else
1549                 CFRA= PSFRA;
1550         
1551         sound_seek_scene(C);
1552
1553         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1554         
1555         return OPERATOR_FINISHED;
1556 }
1557
1558 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1559 {
1560         ot->name = "Jump to Endpoint";
1561         ot->description= "Jump to first/last frame in frame range";
1562         ot->idname = "SCREEN_OT_frame_jump";
1563         
1564         ot->exec= frame_jump_exec;
1565         
1566         ot->poll= ED_operator_screenactive;
1567         ot->flag= 0;
1568         
1569         /* rna */
1570         RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range.");
1571 }
1572
1573
1574 /* ************** jump to keyframe operator ***************************** */
1575
1576 /* function to be called outside UI context, or for redo */
1577 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1578 {
1579         Scene *scene= CTX_data_scene(C);
1580         Object *ob= CTX_data_active_object(C);
1581         DLRBT_Tree keys;
1582         ActKeyColumn *ak;
1583         float cfra= (scene)? (float)(CFRA) : 0.0f;
1584         short next= RNA_boolean_get(op->ptr, "next");
1585         
1586         /* sanity checks */
1587         if (scene == NULL)
1588                 return OPERATOR_CANCELLED;
1589         
1590         /* init binarytree-list for getting keyframes */
1591         BLI_dlrbTree_init(&keys);
1592         
1593         /* populate tree with keyframe nodes */
1594         if (scene && scene->adt)
1595                 scene_to_keylist(NULL, scene, &keys, NULL);
1596         if (ob && ob->adt)
1597                 ob_to_keylist(NULL, ob, &keys, NULL);
1598         
1599         /* build linked-list for searching */
1600         BLI_dlrbTree_linkedlist_sync(&keys);
1601         
1602         /* find matching keyframe in the right direction */
1603         if (next)
1604                 ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1605         else
1606                 ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1607         
1608         /* set the new frame (if keyframe found) */
1609         if (ak) 
1610                 CFRA= (int)ak->cfra;
1611         else
1612                 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
1613         
1614         /* free temp stuff */
1615         BLI_dlrbTree_free(&keys);
1616         
1617         sound_seek_scene(C);
1618
1619         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1620         
1621         return OPERATOR_FINISHED;
1622 }
1623
1624 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1625 {
1626         ot->name = "Jump to Keyframe";
1627         ot->description= "Jump to previous/next keyframe";
1628         ot->idname = "SCREEN_OT_keyframe_jump";
1629         
1630         ot->exec= keyframe_jump_exec;
1631         
1632         ot->poll= ED_operator_screenactive;
1633         ot->flag= 0;
1634         
1635         /* rna */
1636         RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1637 }
1638
1639 /* ************** switch screen operator ***************************** */
1640
1641
1642 /* function to be called outside UI context, or for redo */
1643 static int screen_set_exec(bContext *C, wmOperator *op)
1644 {
1645         bScreen *screen= CTX_wm_screen(C);
1646         ScrArea *sa= CTX_wm_area(C);
1647         int tot= BLI_countlist(&CTX_data_main(C)->screen);
1648         int delta= RNA_int_get(op->ptr, "delta");
1649         
1650         /* return to previous state before switching screens */
1651         if(sa && sa->full)
1652                 ED_screen_full_restore(C, sa);
1653         
1654         if(delta==1) {
1655                 while(tot--) {
1656                         screen= screen->id.next;
1657                         if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1658                         if(screen->winid==0 && screen->full==0)
1659                                 break;
1660                 }
1661         }
1662         else if(delta== -1) {
1663                 while(tot--) {
1664                         screen= screen->id.prev;
1665                         if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1666                         if(screen->winid==0 && screen->full==0)
1667                                 break;
1668                 }
1669         }
1670         else {
1671                 screen= NULL;
1672         }
1673         
1674         if(screen) {
1675                 ED_screen_set(C, screen);
1676                 return OPERATOR_FINISHED;
1677         }
1678         return OPERATOR_CANCELLED;
1679 }
1680
1681 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1682 {
1683         ot->name = "Set Screen";
1684         ot->description= "Cycle through available screens";
1685         ot->idname = "SCREEN_OT_screen_set";
1686         
1687         ot->exec= screen_set_exec;
1688         ot->poll= ED_operator_screenactive;
1689         
1690         /* rna */
1691         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1692 }
1693
1694 /* ************** screen full-area operator ***************************** */
1695
1696
1697 /* function to be called outside UI context, or for redo */
1698 static int screen_full_area_exec(bContext *C, wmOperator *op)
1699 {
1700         ed_screen_fullarea(C, CTX_wm_window(C), CTX_wm_area(C));
1701         return OPERATOR_FINISHED;
1702 }
1703
1704 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1705 {
1706         ot->name = "Toggle Full Screen";
1707         ot->description= "Toggle display selected area as fullscreen";
1708         ot->idname = "SCREEN_OT_screen_full_area";
1709         
1710         ot->exec= screen_full_area_exec;
1711         ot->poll= ED_operator_areaactive;
1712         ot->flag= 0;
1713         
1714 }
1715
1716
1717
1718 /* ************** join area operator ********************************************** */
1719
1720 /* operator state vars used:  
1721  x1, y1     mouse coord in first area, which will disappear
1722  x2, y2     mouse coord in 2nd area, which will become joined
1723  
1724  functions:
1725  
1726  init()   find edge based on state vars 
1727  test if the edge divides two areas, 
1728  store active and nonactive area,
1729  
1730  apply()  do the actual join
1731  
1732  exit() cleanup, send notifier
1733  
1734  callbacks:
1735  
1736  exec() calls init, apply, exit 
1737  
1738  invoke() sets mouse coords in x,y
1739  call init()
1740  add modal handler
1741  
1742  modal()        accept modal events while doing it
1743  call apply() with active window and nonactive window
1744  call exit() and remove handler when LMB confirm
1745  
1746  */
1747
1748 typedef struct sAreaJoinData
1749         {
1750                 ScrArea *sa1;   /* first area to be considered */
1751                 ScrArea *sa2;   /* second area to be considered */
1752                 ScrArea *scr;   /* designed for removal */
1753                 
1754         } sAreaJoinData;
1755
1756
1757 /* validate selection inside screen, set variables OK */
1758 /* return 0: init failed */
1759 /* XXX todo: find edge based on (x,y) and set other area? */
1760 static int area_join_init(bContext *C, wmOperator *op)
1761 {
1762         ScrArea *sa1, *sa2;
1763         sAreaJoinData* jd= NULL;
1764         int x1, y1;
1765         int x2, y2;
1766         
1767         /* required properties, make negative to get return 0 if not set by caller */
1768         x1= RNA_int_get(op->ptr, "x1");
1769         y1= RNA_int_get(op->ptr, "y1");
1770         x2= RNA_int_get(op->ptr, "x2");
1771         y2= RNA_int_get(op->ptr, "y2");
1772         
1773         sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1774         sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1775         if(sa1==NULL || sa2==NULL || sa1==sa2)
1776                 return 0;
1777         
1778         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1779         
1780         jd->sa1 = sa1;
1781         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1782         jd->sa2 = sa2;
1783         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1784         
1785         op->customdata= jd;
1786         
1787         return 1;
1788 }
1789
1790 /* apply the join of the areas (space types) */
1791 static int area_join_apply(bContext *C, wmOperator *op)
1792 {
1793         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1794         if (!jd) return 0;
1795         
1796         if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1797                 return 0;
1798         }
1799         if (CTX_wm_area(C) == jd->sa2) {
1800                 CTX_wm_area_set(C, NULL);
1801                 CTX_wm_region_set(C, NULL);
1802         }
1803         
1804         return 1;
1805 }
1806
1807 /* finish operation */
1808 static void area_join_exit(bContext *C, wmOperator *op)
1809 {
1810         if (op->customdata) {
1811                 MEM_freeN(op->customdata);
1812                 op->customdata = NULL;
1813         }
1814         
1815         /* this makes sure aligned edges will result in aligned grabbing */
1816         removedouble_scredges(CTX_wm_screen(C));
1817         removenotused_scredges(CTX_wm_screen(C));
1818         removenotused_scrverts(CTX_wm_screen(C));
1819 }
1820
1821 static int area_join_exec(bContext *C, wmOperator *op)
1822 {
1823         if(!area_join_init(C, op)) 
1824                 return OPERATOR_CANCELLED;
1825         
1826         area_join_apply(C, op);
1827         area_join_exit(C, op);
1828         
1829         return OPERATOR_FINISHED;
1830 }
1831
1832 /* interaction callback */
1833 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1834 {
1835         
1836         if(event->type==EVT_ACTIONZONE_AREA) {
1837                 sActionzoneData *sad= event->customdata;
1838                 
1839                 if(sad->modifier>0) {
1840                         return OPERATOR_PASS_THROUGH;
1841                 }
1842                 
1843                 /* verify *sad itself */
1844                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1845                         return OPERATOR_PASS_THROUGH;
1846                 
1847                 /* is this our *sad? if areas equal it should be passed on */
1848                 if(sad->sa1==sad->sa2)
1849                         return OPERATOR_PASS_THROUGH;
1850                 
1851                 /* prepare operator state vars */
1852                 RNA_int_set(op->ptr, "x1", sad->x);
1853                 RNA_int_set(op->ptr, "y1", sad->y);
1854                 RNA_int_set(op->ptr, "x2", event->x);
1855                 RNA_int_set(op->ptr, "y2", event->y);
1856                 
1857                 if(!area_join_init(C, op)) 
1858                         return OPERATOR_PASS_THROUGH;
1859                 
1860                 /* add temp handler */
1861                 WM_event_add_modal_handler(C, op);
1862                 
1863                 return OPERATOR_RUNNING_MODAL;
1864         }
1865         
1866         return OPERATOR_PASS_THROUGH;
1867 }
1868
1869 static int area_join_cancel(bContext *C, wmOperator *op)
1870 {
1871         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1872         
1873         if (jd->sa1) {
1874                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1875                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1876         }
1877         if (jd->sa2) {
1878                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1879                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1880         }
1881         
1882         WM_event_add_notifier(C, NC_WINDOW, NULL);
1883         
1884         area_join_exit(C, op);
1885         
1886         return OPERATOR_CANCELLED;
1887 }
1888
1889 /* modal callback while selecting area (space) that will be removed */
1890 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1891 {
1892         bScreen *sc= CTX_wm_screen(C);
1893         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1894         
1895         /* execute the events */
1896         switch(event->type) {
1897                         
1898                 case MOUSEMOVE: 
1899                 {
1900                         ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1901                         int dir;
1902                         
1903                         if (sa) {                                       
1904                                 if (jd->sa1 != sa) {
1905                                         dir = area_getorientation(sc, jd->sa1, sa);
1906                                         if (dir >= 0) {
1907                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1908                                                 jd->sa2 = sa;
1909                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1910                                         } 
1911                                         else {
1912                                                 /* we are not bordering on the previously selected area 
1913                                                  we check if area has common border with the one marked for removal
1914                                                  in this case we can swap areas.
1915                                                  */
1916                                                 dir = area_getorientation(sc, sa, jd->sa2);
1917                                                 if (dir >= 0) {
1918                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1919                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1920                                                         jd->sa1 = jd->sa2;
1921                                                         jd->sa2 = sa;
1922                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1923                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1924                                                 } 
1925                                                 else {
1926                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1927                                                         jd->sa2 = NULL;
1928                                                 }
1929                                         }
1930                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
1931                                 } 
1932                                 else {
1933                                         /* we are back in the area previously selected for keeping 
1934                                          * we swap the areas if possible to allow user to choose */
1935                                         if (jd->sa2 != NULL) {
1936                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1937                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1938                                                 jd->sa1 = jd->sa2;
1939                                                 jd->sa2 = sa;
1940                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1941                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1942                                                 dir = area_getorientation(sc, jd->sa1, jd->sa2);
1943                                                 if (dir < 0) {
1944                                                         printf("oops, didn't expect that!\n");
1945                                                 }
1946                                         } 
1947                                         else {
1948                                                 dir = area_getorientation(sc, jd->sa1, sa);
1949                                                 if (dir >= 0) {
1950                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1951                                                         jd->sa2 = sa;
1952                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1953                                                 }
1954                                         }
1955                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
1956                                 }
1957                         }
1958                 }
1959                         break;
1960                 case LEFTMOUSE:
1961                         if(event->val==KM_RELEASE) {
1962 #ifdef WM_FAST_DRAW
1963                                 ED_area_tag_redraw(jd->sa1);
1964                                 ED_area_tag_redraw(jd->sa2);
1965 #endif
1966                                 area_join_apply(C, op);
1967                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1968                                 area_join_exit(C, op);
1969                                 return OPERATOR_FINISHED;
1970                         }
1971                         break;
1972                         
1973                 case RIGHTMOUSE:
1974                 case ESCKEY:
1975                         return area_join_cancel(C, op);
1976         }
1977         
1978         return OPERATOR_RUNNING_MODAL;
1979 }
1980
1981 /* Operator for joining two areas (space types) */
1982 static void SCREEN_OT_area_join(wmOperatorType *ot)
1983 {
1984         /* identifiers */
1985         ot->name= "Join area";
1986         ot->description= "Join selected areas into new window";
1987         ot->idname= "SCREEN_OT_area_join";
1988         
1989         /* api callbacks */
1990         ot->exec= area_join_exec;
1991         ot->invoke= area_join_invoke;
1992         ot->modal= area_join_modal;
1993         ot->poll= ED_operator_areaactive;
1994         
1995         ot->flag= OPTYPE_BLOCKING;
1996         
1997         /* rna */
1998         RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1999         RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2000         RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2001         RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2002 }
2003
2004 /* ************** repeat last operator ***************************** */
2005
2006 static int repeat_last_exec(bContext *C, wmOperator *op)
2007 {
2008         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
2009         
2010         if(lastop)
2011                 WM_operator_repeat(C, lastop);
2012         
2013         return OPERATOR_CANCELLED;
2014 }
2015
2016 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2017 {
2018         /* identifiers */
2019         ot->name= "Repeat Last";
2020         ot->description= "Repeat last action";
2021         ot->idname= "SCREEN_OT_repeat_last";
2022         
2023         /* api callbacks */
2024         ot->exec= repeat_last_exec;
2025         
2026         ot->poll= ED_operator_screenactive;
2027         
2028 }
2029
2030 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
2031 {
2032         wmWindowManager *wm= CTX_wm_manager(C);
2033         wmOperator *lastop;
2034         uiPopupMenu *pup;
2035         uiLayout *layout;
2036         int items, i;
2037         
2038         items= BLI_countlist(&wm->operators);
2039         if(items==0)
2040                 return OPERATOR_CANCELLED;
2041         
2042         pup= uiPupMenuBegin(C, op->type->name, 0);
2043         layout= uiPupMenuLayout(pup);
2044         
2045         for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
2046                 uiItemIntO(layout, lastop->type->name, 0, op->type->idname, "index", i);
2047         
2048         uiPupMenuEnd(C, pup);
2049         
2050         return OPERATOR_CANCELLED;
2051 }
2052
2053 static int repeat_history_exec(bContext *C, wmOperator *op)
2054 {
2055         wmWindowManager *wm= CTX_wm_manager(C);
2056         
2057         op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2058         if(op) {
2059                 /* let's put it as last operator in list */
2060                 BLI_remlink(&wm->operators, op);
2061                 BLI_addtail(&wm->operators, op);
2062                 
2063                 WM_operator_repeat(C, op);
2064         }
2065         
2066         return OPERATOR_FINISHED;
2067 }
2068
2069 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2070 {
2071         /* identifiers */
2072         ot->name= "Repeat History";
2073         ot->description= "Display menu for previous actions performed";
2074         ot->idname= "SCREEN_OT_repeat_history";
2075         
2076         /* api callbacks */
2077         ot->invoke= repeat_history_invoke;
2078         ot->exec= repeat_history_exec;
2079         
2080         ot->poll= ED_operator_screenactive;
2081         
2082         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2083 }
2084
2085 /* ********************** redo operator ***************************** */
2086
2087 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
2088 {
2089         wmWindowManager *wm= CTX_wm_manager(C);
2090         wmOperator *lastop;
2091         
2092         /* only for operators that are registered and did an undo push */
2093         for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
2094                 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
2095                         break;
2096         
2097         if(lastop)
2098                 WM_operator_redo_popup(C, lastop);
2099         
2100         return OPERATOR_CANCELLED;
2101 }
2102
2103 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2104 {
2105         /* identifiers */
2106         ot->name= "Redo Last";
2107         ot->description= "Display menu for last action performed";
2108         ot->idname= "SCREEN_OT_redo_last";
2109         
2110         /* api callbacks */
2111         ot->invoke= redo_last_invoke;
2112         
2113         ot->poll= ED_operator_screenactive;
2114 }
2115
2116 /* ************** region four-split operator ***************************** */
2117
2118 /* insert a region in the area region list */
2119 static int region_quadview_exec(bContext *C, wmOperator *op)
2120 {
2121         ARegion *ar= CTX_wm_region(C);
2122         
2123         /* some rules... */
2124         if(ar->regiontype!=RGN_TYPE_WINDOW)
2125                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2126         else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2127                 ScrArea *sa= CTX_wm_area(C);
2128                 ARegion *arn;
2129                 
2130                 /* keep current region */
2131                 ar->alignment= 0;
2132                 
2133                 if(sa->spacetype==SPACE_VIEW3D) {
2134                         RegionView3D *rv3d= ar->regiondata;
2135                         rv3d->viewlock= 0;
2136                         rv3d->rflag &= ~RV3D_CLIPPING;
2137                 }
2138                 
2139                 for(ar= sa->regionbase.first; ar; ar= arn) {
2140                         arn= ar->next;
2141                         if(ar->alignment==RGN_ALIGN_QSPLIT) {
2142                                 ED_region_exit(C, ar);
2143                                 BKE_area_region_free(sa->type, ar);
2144                                 BLI_remlink(&sa->regionbase, ar);
2145                                 MEM_freeN(ar);
2146                         }
2147                 }
2148 #ifdef WM_FAST_DRAW
2149                 ED_area_tag_redraw(sa);
2150 #endif
2151                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2152         }
2153         else if(ar->next)
2154                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2155         else {
2156                 ScrArea *sa= CTX_wm_area(C);
2157                 ARegion *newar;
2158                 int count;
2159                 
2160                 ar->alignment= RGN_ALIGN_QSPLIT;
2161                 
2162                 for(count=0; count<3; count++) {
2163                         newar= BKE_area_region_copy(sa->type, ar);
2164                         BLI_addtail(&sa->regionbase, newar);
2165                 }
2166                 
2167                 /* lock views and set them */
2168                 if(sa->spacetype==SPACE_VIEW3D) {
2169                         RegionView3D *rv3d;
2170                         
2171                         rv3d= ar->regiondata;
2172                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2173                         
2174                         ar= ar->next;
2175                         rv3d= ar->regiondata;
2176                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2177                         
2178                         ar= ar->next;
2179                         rv3d= ar->regiondata;
2180                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2181                         
2182                         ar= ar->next;
2183                         rv3d= ar->regiondata;
2184                         rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2185                 }
2186                 
2187 #ifdef WM_FAST_DRAW
2188                 ED_area_tag_redraw(sa);
2189 #endif
2190                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2191         }
2192         
2193         
2194         return OPERATOR_FINISHED;
2195 }
2196
2197 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
2198 {
2199         /* identifiers */
2200         ot->name= "Toggle Quad View";
2201         ot->description= "Split selected area into camera, front, right & top views";
2202         ot->idname= "SCREEN_OT_region_quadview";
2203         
2204         /* api callbacks */
2205         //      ot->invoke= WM_operator_confirm;
2206         ot->exec= region_quadview_exec;
2207         ot->poll= ED_operator_areaactive;
2208         ot->flag= 0;
2209 }
2210
2211
2212
2213 /* ************** region flip operator ***************************** */
2214
2215 /* flip a region alignment */
2216 static int region_flip_exec(bContext *C, wmOperator *op)
2217 {
2218         ARegion *ar= CTX_wm_region(C);
2219         
2220         if (!ar)
2221                 return OPERATOR_CANCELLED;
2222         
2223         if(ar->alignment==RGN_ALIGN_TOP)
2224                 ar->alignment= RGN_ALIGN_BOTTOM;
2225         else if(ar->alignment==RGN_ALIGN_BOTTOM)
2226                 ar->alignment= RGN_ALIGN_TOP;
2227         else if(ar->alignment==RGN_ALIGN_LEFT)
2228                 ar->alignment= RGN_ALIGN_RIGHT;
2229         else if(ar->alignment==RGN_ALIGN_RIGHT)
2230                 ar->alignment= RGN_ALIGN_LEFT;
2231         
2232 #ifdef WM_FAST_DRAW
2233                 ED_area_tag_redraw(CTX_wm_area(C));
2234 #endif
2235         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2236         
2237         return OPERATOR_FINISHED;
2238 }
2239
2240
2241 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2242 {
2243         /* identifiers */
2244         ot->name= "Flip Region";
2245         ot->idname= "SCREEN_OT_region_flip";
2246         
2247         /* api callbacks */
2248         ot->exec= region_flip_exec;
2249         
2250         ot->poll= ED_operator_areaactive;
2251         ot->flag= 0;
2252 }
2253
2254 /* ************** header flip operator ***************************** */
2255
2256 /* flip a header region alignment */
2257 static int header_flip_exec(bContext *C, wmOperator *op)
2258 {
2259         ARegion *ar= CTX_wm_region(C);
2260         
2261         /* find the header region 
2262          *      - try context first, but upon failing, search all regions in area...
2263          */
2264         if((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) {
2265                 ScrArea *sa= CTX_wm_area(C);
2266                 
2267                 /* loop over all regions until a matching one is found */
2268                 for (ar= sa->regionbase.first; ar; ar= ar->next) {
2269                         if(ar->regiontype == RGN_TYPE_HEADER)
2270                                 break;
2271                 }
2272                 
2273                 /* don't do anything if no region */
2274                 if(ar == NULL)
2275                         return OPERATOR_CANCELLED;
2276         }       
2277         
2278         /* copied from SCREEN_OT_region_flip */
2279         if(ar->alignment==RGN_ALIGN_TOP)
2280                 ar->alignment= RGN_ALIGN_BOTTOM;
2281         else if(ar->alignment==RGN_ALIGN_BOTTOM)
2282                 ar->alignment= RGN_ALIGN_TOP;
2283         else if(ar->alignment==RGN_ALIGN_LEFT)
2284                 ar->alignment= RGN_ALIGN_RIGHT;
2285         else if(ar->alignment==RGN_ALIGN_RIGHT)
2286                 ar->alignment= RGN_ALIGN_LEFT;
2287         
2288 #ifdef WM_FAST_DRAW
2289         ED_area_tag_redraw(CTX_wm_area(C));
2290 #endif
2291
2292         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2293         
2294         return OPERATOR_FINISHED;
2295 }
2296
2297
2298 static void SCREEN_OT_header_flip(wmOperatorType *ot)
2299 {
2300         /* identifiers */
2301         ot->name= "Flip Header Region";
2302         ot->idname= "SCREEN_OT_header_flip";
2303         
2304         /* api callbacks */
2305         ot->exec= header_flip_exec;
2306         
2307         ot->poll= ED_operator_areaactive;
2308         ot->flag= 0;
2309 }
2310
2311 /* ************** header tools operator ***************************** */
2312
2313 static int header_toolbox_invoke(bContext *C, wmOperator *op, wmEvent *event)
2314 {
2315         ScrArea *sa= CTX_wm_area(C);
2316         ARegion *ar= CTX_wm_region(C);
2317         uiPopupMenu *pup;
2318         uiLayout *layout;
2319         
2320         pup= uiPupMenuBegin(C, "Header", 0);
2321         layout= uiPupMenuLayout(pup);
2322         
2323         // XXX SCREEN_OT_region_flip doesn't work - gets wrong context for active region, so added custom operator
2324         if (ar->alignment == RGN_ALIGN_TOP)
2325                 uiItemO(layout, "Flip to Bottom", 0, "SCREEN_OT_header_flip");  
2326         else
2327                 uiItemO(layout, "Flip to Top", 0, "SCREEN_OT_header_flip");
2328         
2329         uiItemS(layout);
2330         
2331         /* file browser should be fullscreen all the time, but other regions can be maximised/restored... */
2332         if (sa->spacetype != SPACE_FILE) {
2333                 if (sa->full) 
2334                         uiItemO(layout, "Tile Area", 0, "SCREEN_OT_screen_full_area");
2335                 else
2336                         uiItemO(layout, "Maximize Area", 0, "SCREEN_OT_screen_full_area");
2337         }
2338         
2339         uiPupMenuEnd(C, pup);
2340         
2341         return OPERATOR_CANCELLED;
2342 }
2343
2344 void SCREEN_OT_header_toolbox(wmOperatorType *ot)
2345 {
2346         /* identifiers */
2347         ot->name= "Header Toolbox";
2348         ot->description="Display header region toolbox";
2349         ot->idname= "SCREEN_OT_header_toolbox";
2350         
2351         /* api callbacks */
2352         ot->invoke= header_toolbox_invoke;
2353 }
2354
2355 /* ****************** anim player, with timer ***************** */
2356
2357 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2358 {
2359         if(regiontype==RGN_TYPE_WINDOW) {
2360                 
2361                 switch (spacetype) {
2362                         case SPACE_VIEW3D:
2363                                 if(redraws & TIME_ALL_3D_WIN)
2364                                         return 1;
2365                                 break;
2366                         case SPACE_IPO:
2367                         case SPACE_ACTION:
2368                         case SPACE_NLA:
2369                                 if(redraws & TIME_ALL_ANIM_WIN)
2370                                         return 1;
2371                                 break;
2372                         case SPACE_TIME:
2373                                 /* if only 1 window or 3d windows, we do timeline too */
2374                                 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2375                                         return 1;
2376                                 break;
2377                         case SPACE_BUTS:
2378                                 if(redraws & TIME_ALL_BUTS_WIN)
2379                                         return 1;
2380                                 break;
2381                         case SPACE_SEQ:
2382                                 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2383                                         return 1;
2384                                 break;
2385                         case SPACE_NODE:
2386                                 if(redraws & (TIME_NODES))
2387                                         return 1;
2388                                 break;
2389                         case SPACE_IMAGE:
2390                                 if(redraws & TIME_ALL_IMAGE_WIN)
2391                                         return 1;
2392                                 break;
2393                                 
2394                 }
2395         }
2396         else if(regiontype==RGN_TYPE_UI) {
2397                 if(redraws & TIME_ALL_BUTS_WIN)
2398                         return 1;
2399         }
2400         else if(regiontype==RGN_TYPE_HEADER) {
2401                 if(spacetype==SPACE_TIME)
2402                         return 1;
2403         }
2404         else if (regiontype==RGN_TYPE_PREVIEW) {
2405                 switch (spacetype) {
2406                         case SPACE_SEQ:
2407                                 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2408                                         return 1;
2409                                 break;
2410                 }
2411         }
2412         return 0;
2413 }
2414
2415 static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event)
2416 {
2417         bScreen *screen= CTX_wm_screen(C);
2418         
2419         if(screen->animtimer==event->customdata) {
2420                 Scene *scene= CTX_data_scene(C);
2421                 wmTimer *wt= screen->animtimer;
2422                 ScreenAnimData *sad= wt->customdata;
2423                 ScrArea *sa;
2424                 int sync;
2425                 
2426                 /* sync, don't sync, or follow scene setting */
2427                 if(sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2428                 else if(sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2429                 else sync= (scene->flag & SCE_FRAME_DROP);
2430                 
2431                 if((scene->audio.flag & AUDIO_SYNC) && !(sad->flag & ANIMPLAY_FLAG_REVERSE))
2432                 {
2433                         scene->r.cfra = floor(sound_sync_scene(scene) * FPS);
2434                 }
2435                 else
2436                 {
2437                         if(sync) {
2438                                 int step = floor(wt->duration * FPS);
2439                                 /* skip frames */
2440                                 if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2441                                         scene->r.cfra -= step;
2442                                 else
2443                                         scene->r.cfra += step;
2444                                 wt->duration -= ((float)step)/FPS;
2445                         }
2446                         else {
2447                                 /* one frame +/- */
2448                                 if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2449                                         scene->r.cfra--;
2450                                 else
2451                                         scene->r.cfra++;
2452                         }
2453                 }
2454                 
2455                 /* reset 'jumped' flag before checking if we need to jump... */
2456                 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2457                 
2458                 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2459                         /* jump back to end? */
2460                         if (PRVRANGEON) {
2461                                 if (scene->r.cfra < scene->r.psfra) {
2462                                         scene->r.cfra= scene->r.pefra;
2463                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2464                                 }
2465                         }
2466                         else {
2467                                 if (scene->r.cfra < scene->r.sfra) {
2468                                         scene->r.cfra= scene->r.efra;
2469                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2470                                 }
2471                         }
2472                 }
2473                 else {
2474                         /* jump back to start? */
2475                         if (PRVRANGEON) {
2476                                 if (scene->r.cfra > scene->r.pefra) {
2477                                         scene->r.cfra= scene->r.psfra;
2478                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2479                                 }
2480                         }
2481                         else {
2482                                 if (scene->r.cfra > scene->r.efra) {
2483                                         scene->r.cfra= scene->r.sfra;
2484                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2485                                 }
2486                         }
2487                 }
2488                 
2489                 if(sad->flag & ANIMPLAY_FLAG_JUMPED)
2490                         sound_seek_scene(C);
2491                 
2492                 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2493                 ED_update_for_newframe(C, 1);
2494                 
2495                 for(sa= screen->areabase.first; sa; sa= sa->next) {
2496                         ARegion *ar;
2497                         for(ar= sa->regionbase.first; ar; ar= ar->next) {
2498                                 if(ar==sad->ar)
2499                                         ED_region_tag_redraw(ar);
2500                                 else
2501                                         if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2502                                                 ED_region_tag_redraw(ar);
2503                         }
2504                 }
2505                 
2506                 /* update frame rate info too 
2507                  * NOTE: this may not be accurate enough, since we might need this after modifiers/etc. 
2508                  * have been calculated instead of just before updates have been done?
2509                  */
2510                 ED_refresh_viewport_fps(C);
2511                 
2512                 /* recalculate the timestep for the timer now that we've finished calculating this,
2513                  * since the frames-per-second value may have been changed
2514                  */
2515                 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2516                 wt->timestep= (1.0/FPS);
2517                 
2518                 return OPERATOR_FINISHED;
2519         }
2520         return OPERATOR_PASS_THROUGH;
2521 }
2522
2523 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2524 {
2525         /* identifiers */
2526         ot->name= "Animation Step";
2527         ot->description= "Step through animation by position";
2528         ot->idname= "SCREEN_OT_animation_step";
2529         
2530         /* api callbacks */
2531         ot->invoke= screen_animation_step;
2532         
2533         ot->poll= ED_operator_screenactive;
2534         
2535 }
2536
2537 /* ****************** anim player, starts or ends timer ***************** */
2538
2539 /* toggle operator */
2540 int ED_screen_animation_play(bContext *C, int sync, int mode)
2541 {
2542         bScreen *screen= CTX_wm_screen(C);
2543         struct Scene* scene = CTX_data_scene(C);
2544
2545         if(screen->animtimer) {
2546                 /* stop playback now */
2547                 ED_screen_animation_timer(C, 0, 0, 0);
2548                 sound_stop_scene(scene);
2549         }
2550         else {
2551                 ScrArea *sa= CTX_wm_area(C);
2552
2553                 if(mode == 1) // XXX only play audio forwards!?
2554                         sound_play_scene(scene);
2555
2556                 /* timeline gets special treatment since it has it's own menu for determining redraws */
2557                 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2558                         SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2559
2560                         ED_screen_animation_timer(C, stime->redraws, sync, mode);
2561
2562                         /* update region if TIME_REGION was set, to leftmost 3d window */
2563                         ED_screen_animation_timer_update(screen, stime->redraws);
2564                 }
2565                 else {
2566                         int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2567
2568                         /* XXX - would like a better way to deal with this situation - Campbell */
2569                         if((!sa) || (sa->spacetype == SPACE_SEQ)) {
2570                                 redraws |= TIME_SEQ;
2571                         }
2572
2573                         ED_screen_animation_timer(C, redraws, sync, mode);
2574
2575                         if(screen->animtimer) {
2576                                 wmTimer *wt= screen->animtimer;
2577                                 ScreenAnimData *sad= wt->customdata;
2578
2579                                 sad->ar= CTX_wm_region(C);
2580                         }
2581                 }
2582         }
2583
2584         return OPERATOR_FINISHED;
2585 }
2586
2587 static int screen_animation_play_invoke(bContext *C, wmOperator *op, wmEvent *event)
2588 {
2589         int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2590         int sync= -1;
2591
2592         if(RNA_property_is_set(op->ptr, "sync"))
2593                 sync= (RNA_boolean_get(op->ptr, "sync"));
2594
2595         return ED_screen_animation_play(C, sync, mode);
2596 }
2597
2598 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2599 {
2600         /* identifiers */
2601         ot->name= "Play Animation";
2602         ot->description= "Play animation";
2603         ot->idname= "SCREEN_OT_animation_play";
2604         
2605         /* api callbacks */
2606         ot->invoke= screen_animation_play_invoke;
2607         
2608         ot->poll= ED_operator_screenactive;
2609         
2610         RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2611         RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate");
2612 }
2613
2614 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2615 {
2616         bScreen *screen= CTX_wm_screen(C);
2617         
2618         if(screen->animtimer) {
2619                 ScreenAnimData *sad= screen->animtimer->customdata;
2620                 Scene *scene= CTX_data_scene(C);
2621                 
2622                 /* reset current frame before stopping, and just send a notifier to deal with the rest 
2623                  * (since playback still needs to be stopped)
2624                  */
2625                 scene->r.cfra= sad->sfra;
2626                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2627                 
2628                 /* call the other "toggling" operator to clean up now */
2629                 return screen_animation_play_invoke(C, op, event);
2630         }
2631         
2632         return OPERATOR_PASS_THROUGH;
2633 }
2634
2635 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2636 {
2637         /* identifiers */
2638         ot->name= "Cancel Animation";
2639         ot->description= "Cancel animation, returning to the original frame";
2640         ot->idname= "SCREEN_OT_animation_cancel";
2641         
2642         /* api callbacks */
2643         ot->invoke= screen_animation_cancel;
2644         
2645         ot->poll= ED_operator_screenactive;
2646 }
2647
2648 /* ************** border select operator (template) ***************************** */
2649
2650 /* operator state vars used: (added by default WM callbacks)   
2651  xmin, ymin     
2652  xmax, ymax     
2653  
2654  customdata: the wmGesture pointer
2655  
2656  callbacks:
2657  
2658  exec() has to be filled in by user
2659  
2660  invoke() default WM function
2661  adds modal handler
2662  
2663  modal()        default WM function 
2664  accept modal events while doing it, calls exec(), handles ESC and border drawing
2665  
2666  poll() has to be filled in by user for context
2667  */
2668 #if 0
2669 static int border_select_do(bContext *C, wmOperator *op)
2670 {
2671         int event_type= RNA_int_get(op->ptr, "event_type");
2672         
2673         if(event_type==LEFTMOUSE)
2674                 printf("border select do select\n");
2675         else if(event_type==RIGHTMOUSE)
2676                 printf("border select deselect\n");
2677         else 
2678                 printf("border select do something\n");
2679         
2680         return 1;
2681 }
2682
2683 static void SCREEN_OT_border_select(wmOperatorType *ot)
2684 {
2685         /* identifiers */
2686         ot->name= "Border select";
2687         ot->idname= "SCREEN_OT_border_select";
2688         
2689         /* api callbacks */
2690         ot->exec= border_select_do;
2691         ot->invoke= WM_border_select_invoke;
2692         ot->modal= WM_border_select_modal;
2693         
2694         ot->poll= ED_operator_areaactive;
2695         
2696         /* rna */
2697         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2698         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2699         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2700         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2701         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2702         
2703 }
2704 #endif
2705
2706 /* ****************************** render invoking ***************** */
2707
2708 /* set callbacks, exported to sequence render too. 
2709  Only call in foreground (UI) renders. */
2710
2711 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2712 /* window as the last possible alternative.                                                                        */
2713 static ScrArea *biggest_non_image_area(bContext *C)
2714 {
2715         bScreen *sc= CTX_wm_screen(C);
2716         ScrArea *sa, *big= NULL;
2717         int size, maxsize= 0, bwmaxsize= 0;
2718         short foundwin= 0;
2719         
2720         for(sa= sc->areabase.first; sa; sa= sa->next) {
2721                 if(sa->winx > 30 && sa->winy > 30) {
2722                         size= sa->winx*sa->winy;
2723                         if(sa->spacetype == SPACE_BUTS) {
2724                                 if(foundwin == 0 && size > bwmaxsize) {
2725                                         bwmaxsize= size;
2726                                         big= sa;        
2727                                 }
2728                         }
2729                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2730                                 maxsize= size;
2731                                 big= sa;
2732                                 foundwin= 1;
2733                         }
2734                 }
2735         }
2736         
2737         return big;
2738 }
2739
2740 static ScrArea *biggest_area(bContext *C)
2741 {
2742         bScreen *sc= CTX_wm_screen(C);
2743         ScrArea *sa, *big= NULL;
2744         int size, maxsize= 0;
2745         
2746         for(sa= sc->areabase.first; sa; sa= sa->next) {
2747                 size= sa->winx*sa->winy;
2748                 if(size > maxsize) {
2749                         maxsize= size;
2750                         big= sa;
2751                 }
2752         }
2753         return big;
2754 }
2755
2756
2757 static ScrArea *find_area_showing_r_result(bContext *C)
2758 {
2759         wmWindowManager *wm= CTX_wm_manager(C);
2760         wmWindow *win;
2761         ScrArea *sa = NULL;
2762         SpaceImage *sima;
2763         
2764         /* find an imagewindow showing render result */
2765         for(win=wm->windows.first; win; win=win->next) {
2766                 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2767                         if(sa->spacetype==SPACE_IMAGE) {
2768                                 sima= sa->spacedata.first;
2769                                 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2770                                         break;
2771                         }
2772                 }
2773         }
2774         
2775         return sa;
2776 }
2777
2778 static ScrArea *find_area_image_empty(bContext *C)
2779 {
2780         bScreen *sc= CTX_wm_screen(C);
2781         ScrArea *sa;
2782         SpaceImage *sima;
2783         
2784         /* find an imagewindow showing render result */
2785         for(sa=sc->areabase.first; sa; sa= sa->next) {
2786                 if(sa->spacetype==SPACE_IMAGE) {
2787                         sima= sa->spacedata.first;
2788                         if(!sima->image)
2789                                 break;
2790                 }
2791         }
2792         return sa;
2793 }
2794
2795 #if 0 // XXX not used
2796 static ScrArea *find_empty_image_area(bContext *C)
2797 {
2798         bScreen *sc= CTX_wm_screen(C);
2799         ScrArea *sa;
2800         SpaceImage *sima;
2801         
2802         /* find an imagewindow showing render result */
2803         for(sa=sc->areabase.first; sa; sa= sa->next) {
2804                 if(sa->spacetype==SPACE_IMAGE) {
2805                         sima= sa->spacedata.first;
2806                         if(!sima->image)
2807                                 break;
2808                 }
2809         }
2810         return sa;
2811 }
2812 #endif // XXX not used
2813
2814 /* new window uses x,y to set position */
2815 static void screen_set_image_output(bContext *C, int mx, int my)
2816 {
2817         wmWindow *win= CTX_wm_window(C);
2818         Scene *scene= CTX_data_scene(C);
2819         ScrArea *sa= NULL;
2820         SpaceImage *sima;
2821         int area_was_image=0;
2822         
2823         if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2824                 rcti rect;
2825                 int sizex, sizey;
2826                 
2827                 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2828                 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2829                 
2830                 /* arbitrary... miniature image window views don't make much sense */
2831                 if(sizex < 320) sizex= 320;
2832                 if(sizey < 256) sizey= 256;
2833                 
2834                 /* XXX some magic to calculate postition */
2835                 rect.xmin= mx + win->posx - sizex/2;
2836                 rect.ymin= my + win->posy - sizey/2;
2837                 rect.xmax= rect.xmin + sizex;
2838                 rect.ymax= rect.ymin + sizey;
2839                 
2840                 /* changes context! */
2841                 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2842                 
2843                 sa= CTX_wm_area(C);
2844         }
2845         else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2846                 if (CTX_wm_area(C)->spacetype == SPACE_IMAGE)
2847                         area_was_image = 1;
2848                 
2849                 /* this function returns with changed context */
2850                 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2851                 sa= CTX_wm_area(C);
2852         }
2853         
2854         if(!sa) {
2855                 sa= find_area_showing_r_result(C);
2856                 if(sa==NULL)
2857                         sa= find_area_image_empty(C);
2858                 
2859                 if(sa==NULL) {
2860                         /* find largest open non-image area */
2861                         sa= biggest_non_image_area(C);
2862                         if(sa) {
2863                                 ED_area_newspace(C, sa, SPACE_IMAGE);
2864                                 sima= sa->spacedata.first;
2865                                 
2866                                 /* makes ESC go back to prev space */
2867                                 sima->flag |= SI_PREVSPACE;
2868                         }
2869                         else {
2870                                 /* use any area of decent size */
2871                                 sa= biggest_area(C);
2872                                 if(sa->spacetype!=SPACE_IMAGE) {
2873                                         // XXX newspace(sa, SPACE_IMAGE);
2874                                         sima= sa->spacedata.first;
2875                                         
2876                                         /* makes ESC go back to prev space */
2877                                         sima->flag |= SI_PREVSPACE;
2878                                 }
2879                         }
2880                 }
2881         }       
2882         sima= sa->spacedata.first;
2883         
2884         /* get the correct image, and scale it */
2885         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2886         
2887         
2888         /* if we're rendering to full screen, set appropriate hints on image editor
2889          * so it can restore properly on pressing esc */
2890         if(sa->full) {
2891                 sima->flag |= SI_FULLWINDOW;
2892                 
2893                 /* Tell the image editor to revert to previous space in space list on close
2894                  * _only_ if it wasn't already an image editor when the render was invoked */
2895                 if (area_was_image == 0)
2896                         sima->flag |= SI_PREVSPACE;
2897                 else {
2898                         /* Leave it alone so the image editor will just go back from 
2899                          * full screen to the original tiled setup */
2900                         ;
2901                 }
2902                 
2903         }
2904
2905 }
2906
2907 /* executes blocking render */
2908 static int screen_render_exec(bContext *C, wmOperator *op)
2909 {
2910         Scene *scene= CTX_data_scene(C);
2911         Render *re= RE_GetRender(scene->id.name, RE_SLOT_VIEW);
2912         
2913         if(re==NULL) {
2914                 re= RE_NewRender(scene->id.name, RE_SLOT_VIEW);
2915         }
2916         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2917         
2918         if(RNA_boolean_get(op->ptr, "animation"))
2919                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step, op->reports);
2920         else
2921                 RE_BlenderFrame(re, scene, NULL, scene->r.cfra);
2922         
2923         // no redraw needed, we leave state as we entered it
2924         ED_update_for_newframe(C, 1);
2925         
2926         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2927         
2928         return OPERATOR_FINISHED;
2929 }
2930
2931 typedef struct RenderJob {
2932         Scene *scene;
2933         Render *re;
2934         wmWindow *win;
2935         SceneRenderLayer *srl;
2936         int anim;
2937         Image *image;
2938         ImageUser iuser;
2939         short *stop;
2940         short *do_update;
2941         ReportList *reports;
2942 } RenderJob;
2943
2944 static void render_freejob(void *rjv)
2945 {
2946         RenderJob *rj= rjv;
2947         
2948         MEM_freeN(rj);
2949 }
2950
2951 /* str is IMA_RW_MAXTEXT in size */
2952 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2953 {
2954         char info_time_str[32]; // used to be extern to header_info.c
2955         uintptr_t mem_in_use, mmap_in_use;
2956         float megs_used_memory, mmap_used_memory;
2957         char *spos= str;
2958         
2959         mem_in_use= MEM_get_memory_in_use();
2960         mmap_in_use= MEM_get_mapped_memory_in_use();
2961         
2962         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2963         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2964         
2965         if(scene->lay & 0xFF000000)
2966                 spos+= sprintf(spos, "Localview | ");
2967         else if(scene->r.scemode & R_SINGLE_LAYER)
2968                 spos+= sprintf(spos, "Single Layer | ");
2969         
2970         if(rs->statstr) {
2971                 spos+= sprintf(spos, "%s ", rs->statstr);
2972         }
2973         else {
2974                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2975                 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2976                 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2977                 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2978                 
2979                 if(rs->curfield)
2980                         spos+= sprintf(spos, "Field %d ", rs->curfield);
2981                 if(rs->curblur)
2982                         spos+= sprintf(spos, "Blur %d ", rs->curblur);
2983         }
2984         
2985         BLI_timestr(rs->lastframetime, info_time_str);
2986         spos+= sprintf(spos, "Time:%s ", info_time_str);
2987         
2988         if(rs->infostr && rs->infostr[0])
2989                 spos+= sprintf(spos, "| %s ", rs->infostr);
2990         
2991         /* very weak... but 512 characters is quite safe */
2992         if(spos >= str+IMA_RW_MAXTEXT)
2993                 if (G.f & G_DEBUG)
2994                         printf("WARNING! renderwin text beyond limit \n");
2995         
2996 }
2997
2998 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2999 {
3000         RenderJob *rj= rjv;
3001         
3002         /* malloc OK here, stats_draw is not in tile threads */
3003         if(rj->image->render_text==NULL)
3004                 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
3005         
3006         make_renderinfo_string(rs, rj->scene, rj->image->render_text);
3007         
3008         /* make jobs timer to send notifier */
3009         *(rj->do_update)= 1;
3010         
3011 }
3012
3013 /* called inside thread! */
3014 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
3015 {
3016         float x1, y1, *rectf= NULL;
3017         int ymin, ymax, xmin, xmax;
3018         int rymin, rxmin;
3019         char *rectc;
3020         
3021         /* if renrect argument, we only refresh scanlines */
3022         if(renrect) {
3023                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
3024                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
3025                         return;
3026                 
3027                 /* xmin here is first subrect x coord, xmax defines subrect width */
3028                 xmin = renrect->xmin + rr->crop;
3029                 xmax = renrect->xmax - xmin - rr->crop;
3030                 if (xmax<2) return;
3031                 
3032                 ymin= renrect->ymin + rr->crop;
3033                 ymax= renrect->ymax - ymin - rr->crop;
3034                 if(ymax<2)
3035                         return;
3036                 renrect->ymin= renrect->ymax;
3037                 
3038         }
3039         else {
3040                 xmin = ymin = rr->crop;
3041                 xmax = rr->rectx - 2*rr->crop;
3042                 ymax = rr->recty - 2*rr->crop;
3043         }
3044         
3045         /* xmin ymin is in tile coords. transform to ibuf */
3046         rxmin= rr->tilerect.xmin + xmin;
3047         if(rxmin >= ibuf->x) return;
3048         rymin= rr->tilerect.ymin + ymin;
3049         if(rymin >= ibuf->y) return;
3050         
3051         if(rxmin + xmax > ibuf->x)
3052                 xmax= ibuf->x - rxmin;
3053         if(rymin + ymax > ibuf->y)
3054                 ymax= ibuf->y - rymin;
3055         
3056         if(xmax < 1 || ymax < 1) return;
3057         
3058         /* find current float rect for display, first case is after composit... still weak */
3059         if(rr->rectf)
3060                 rectf= rr->rectf;
3061         else {
3062                 if(rr->rect32)
3063                         return;
3064                 else {
3065                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
3066                         rectf= rr->renlay->rectf;
3067                 }
3068         }
3069         if(rectf==NULL) return;
3070         
3071         if(ibuf->rect==NULL)
3072                 imb_addrectImBuf(ibuf);
3073
3074         rectf+= 4*(rr->rectx*ymin + xmin);
3075         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
3076         
3077         /* XXX make nice consistent functions for this */
3078         if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
3079                 for(y1= 0; y1<ymax; y1++) {
3080                         float *rf= rectf;
3081                         float srgb[3];
3082                         char *rc= rectc;
3083                         
3084                         /* XXX temp. because crop offset */
3085                         if( rectc >= (char *)(ibuf->rect)) {
3086                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3087                                         srgb[0]= linearrgb_to_srgb(rf[0]);
3088                                         srgb[1]= linearrgb_to_srgb(rf[1]);
3089                                         srgb[2]= linearrgb_to_srgb(rf[2]);
3090                                         
3091                                         rc[0]= FTOCHAR(srgb[0]);
3092                                         rc[1]= FTOCHAR(srgb[1]);
3093                                         rc[2]= FTOCHAR(srgb[2]);
3094                                         rc[3]= FTOCHAR(rf[3]);
3095                                 }
3096                         }
3097                         rectf += 4*rr->rectx;
3098                         rectc += 4*ibuf->x;
3099                 }
3100         } else {
3101                 for(y1= 0; y1<ymax; y1++) {
3102                         float *rf= rectf;
3103                         char *rc= rectc;
3104                         
3105                         /* XXX temp. because crop offset */
3106                         if( rectc >= (char *)(ibuf->rect)) {
3107                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3108                                         rc[0]= FTOCHAR(rf[0]);
3109                                         rc[1]= FTOCHAR(rf[1]);
3110                                         rc[2]= FTOCHAR(rf[2]);
3111                                         rc[3]= FTOCHAR(rf[3]);
3112                                 }
3113                         }
3114                         rectf += 4*rr->rectx;
3115                         rectc += 4*ibuf->x;
3116                 }
3117         }
3118         
3119 }
3120
3121 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
3122 {
3123         RenderJob *rj= rjv;
3124         ImBuf *ibuf;
3125         void *lock;
3126         
3127         ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
3128         if(ibuf) {
3129                 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
3130                 
3131                 /* make jobs timer to send notifier */
3132                 *(rj->do_update)= 1;
3133         }
3134         BKE_image_release_ibuf(rj->image, lock);
3135 }
3136
3137 static void render_startjob(void *rjv, short *stop, short *do_update)
3138 {
3139         RenderJob *rj= rjv;
3140 //      Main *mainp= BKE_undo_get_main(&rj->scene);
3141         
3142         rj->stop= stop;
3143         rj->do_update= do_update;
3144         
3145 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2)
3146         // Workaround for Apple gcc 4.2.1 omp vs background thread bug
3147         pthread_setspecific (gomp_tls_key, thread_tls_data);
3148 #endif
3149
3150         if(rj->anim)
3151                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step, rj->reports);
3152         else
3153                 RE_BlenderFrame(rj->re, rj->scene, rj->srl, rj->scene->r.cfra);
3154         
3155 //      if(mainp)
3156 //              free_main(mainp);
3157 }
3158
3159 /* called by render, check job 'stop' value or the global */
3160 static int render_breakjob(void *rjv)
3161 {
3162         RenderJob *rj= rjv;
3163         
3164         if(G.afbreek)
3165                 return 1;
3166         if(rj->stop && *(rj->stop))
3167                 return 1;
3168         return 0;
3169 }
3170
3171 /* catch esc */
3172 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3173 {
3174         /* no running blender, remove handler and pass through */
3175         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
3176                 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
3177         
3178         /* running render */
3179         switch (event->type) {
3180                 case ESCKEY:
3181                         return OPERATOR_RUNNING_MODAL;
3182                         break;
3183         }
3184         return OPERATOR_PASS_THROUGH;
3185 }
3186
3187 /* using context, starts job */
3188 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3189 {
3190         /* new render clears all callbacks */
3191         Scene *scene= CTX_data_scene(C);
3192         SceneRenderLayer *srl=NULL;
3193         Render *re;
3194         wmJob *steve;
3195         RenderJob *rj;
3196         Image *ima;
3197         
3198         /* only one render job at a time */
3199         if(WM_jobs_test(CTX_wm_manager(C), scene))
3200                 return OPERATOR_CANCELLED;
3201         
3202         /* stop all running jobs, currently previews frustrate Render */
3203         WM_jobs_stop_all(CTX_wm_manager(C));
3204         
3205         /* handle UI stuff */
3206         WM_cursor_wait(1);
3207         
3208         /* flush multires changes (for sculpt) */
3209         multires_force_render_update(CTX_data_active_object(C));
3210         
3211         /* get editmode results */
3212         ED_object_exit_editmode(C, EM_FREEDATA|EM_DO_UNDO);     /* 0 = does not exit editmode */
3213