Enabling AV-sync again. You can now choose between No sync, Frame Dropping or AV...
[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 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
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                 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2553                 int sync= -1;
2554                 if(mode == 1) // XXX only play audio forwards!?
2555                         sound_play_scene(scene);
2556                 
2557                 if(RNA_property_is_set(op->ptr, "sync"))
2558                         sync= (RNA_boolean_get(op->ptr, "sync"));
2559                 
2560                 /* timeline gets special treatment since it has it's own menu for determining redraws */
2561                 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2562                         SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2563                         
2564                         ED_screen_animation_timer(C, stime->redraws, sync, mode);
2565                         
2566                         /* update region if TIME_REGION was set, to leftmost 3d window */
2567                         ED_screen_animation_timer_update(screen, stime->redraws);
2568                 }
2569                 else {
2570                         int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2571                         
2572                         /* XXX - would like a better way to deal with this situation - Campbell */
2573                         if((sa) && (sa->spacetype == SPACE_SEQ)) {
2574                                 redraws |= TIME_SEQ;
2575                         }
2576                         
2577                         ED_screen_animation_timer(C, redraws, sync, mode);
2578                         
2579                         if(screen->animtimer) {
2580                                 wmTimer *wt= screen->animtimer;
2581                                 ScreenAnimData *sad= wt->customdata;
2582                                 
2583                                 sad->ar= CTX_wm_region(C);
2584                         }
2585                 }
2586         }
2587         
2588         return OPERATOR_FINISHED;
2589 }
2590
2591 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2592 {
2593         /* identifiers */
2594         ot->name= "Play Animation";
2595         ot->description= "Play animation";
2596         ot->idname= "SCREEN_OT_animation_play";
2597         
2598         /* api callbacks */
2599         ot->invoke= screen_animation_play;
2600         
2601         ot->poll= ED_operator_screenactive;
2602         
2603         RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2604         RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate and stay in sync with audio.");
2605 }
2606
2607 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2608 {
2609         bScreen *screen= CTX_wm_screen(C);
2610         
2611         if(screen->animtimer) {
2612                 ScreenAnimData *sad= screen->animtimer->customdata;
2613                 Scene *scene= CTX_data_scene(C);
2614                 
2615                 /* reset current frame before stopping, and just send a notifier to deal with the rest 
2616                  * (since playback still needs to be stopped)
2617                  */
2618                 scene->r.cfra= sad->sfra;
2619                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2620                 
2621                 /* call the other "toggling" operator to clean up now */
2622                 return screen_animation_play(C, op, event);
2623         }
2624         
2625         return OPERATOR_PASS_THROUGH;
2626 }
2627
2628 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2629 {
2630         /* identifiers */
2631         ot->name= "Cancel Animation";
2632         ot->description= "Cancel animation, returning to the original frame";
2633         ot->idname= "SCREEN_OT_animation_cancel";
2634         
2635         /* api callbacks */
2636         ot->invoke= screen_animation_cancel;
2637         
2638         ot->poll= ED_operator_screenactive;
2639 }
2640
2641 /* ************** border select operator (template) ***************************** */
2642
2643 /* operator state vars used: (added by default WM callbacks)   
2644  xmin, ymin     
2645  xmax, ymax     
2646  
2647  customdata: the wmGesture pointer
2648  
2649  callbacks:
2650  
2651  exec() has to be filled in by user
2652  
2653  invoke() default WM function
2654  adds modal handler
2655  
2656  modal()        default WM function 
2657  accept modal events while doing it, calls exec(), handles ESC and border drawing
2658  
2659  poll() has to be filled in by user for context
2660  */
2661 #if 0
2662 static int border_select_do(bContext *C, wmOperator *op)
2663 {
2664         int event_type= RNA_int_get(op->ptr, "event_type");
2665         
2666         if(event_type==LEFTMOUSE)
2667                 printf("border select do select\n");
2668         else if(event_type==RIGHTMOUSE)
2669                 printf("border select deselect\n");
2670         else 
2671                 printf("border select do something\n");
2672         
2673         return 1;
2674 }
2675
2676 static void SCREEN_OT_border_select(wmOperatorType *ot)
2677 {
2678         /* identifiers */
2679         ot->name= "Border select";
2680         ot->idname= "SCREEN_OT_border_select";
2681         
2682         /* api callbacks */
2683         ot->exec= border_select_do;
2684         ot->invoke= WM_border_select_invoke;
2685         ot->modal= WM_border_select_modal;
2686         
2687         ot->poll= ED_operator_areaactive;
2688         
2689         /* rna */
2690         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2691         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2692         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2693         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2694         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2695         
2696 }
2697 #endif
2698
2699 /* ****************************** render invoking ***************** */
2700
2701 /* set callbacks, exported to sequence render too. 
2702  Only call in foreground (UI) renders. */
2703
2704 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2705 /* window as the last possible alternative.                                                                        */
2706 static ScrArea *biggest_non_image_area(bContext *C)
2707 {
2708         bScreen *sc= CTX_wm_screen(C);
2709         ScrArea *sa, *big= NULL;
2710         int size, maxsize= 0, bwmaxsize= 0;
2711         short foundwin= 0;
2712         
2713         for(sa= sc->areabase.first; sa; sa= sa->next) {
2714                 if(sa->winx > 30 && sa->winy > 30) {
2715                         size= sa->winx*sa->winy;
2716                         if(sa->spacetype == SPACE_BUTS) {
2717                                 if(foundwin == 0 && size > bwmaxsize) {
2718                                         bwmaxsize= size;
2719                                         big= sa;        
2720                                 }
2721                         }
2722                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2723                                 maxsize= size;
2724                                 big= sa;
2725                                 foundwin= 1;
2726                         }
2727                 }
2728         }
2729         
2730         return big;
2731 }
2732
2733 static ScrArea *biggest_area(bContext *C)
2734 {
2735         bScreen *sc= CTX_wm_screen(C);
2736         ScrArea *sa, *big= NULL;
2737         int size, maxsize= 0;
2738         
2739         for(sa= sc->areabase.first; sa; sa= sa->next) {
2740                 size= sa->winx*sa->winy;
2741                 if(size > maxsize) {
2742                         maxsize= size;
2743                         big= sa;
2744                 }
2745         }
2746         return big;
2747 }
2748
2749
2750 static ScrArea *find_area_showing_r_result(bContext *C)
2751 {
2752         wmWindowManager *wm= CTX_wm_manager(C);
2753         wmWindow *win;
2754         ScrArea *sa = NULL;
2755         SpaceImage *sima;
2756         
2757         /* find an imagewindow showing render result */
2758         for(win=wm->windows.first; win; win=win->next) {
2759                 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2760                         if(sa->spacetype==SPACE_IMAGE) {
2761                                 sima= sa->spacedata.first;
2762                                 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2763                                         break;
2764                         }
2765                 }
2766         }
2767         
2768         return sa;
2769 }
2770
2771 static ScrArea *find_area_image_empty(bContext *C)
2772 {
2773         bScreen *sc= CTX_wm_screen(C);
2774         ScrArea *sa;
2775         SpaceImage *sima;
2776         
2777         /* find an imagewindow showing render result */
2778         for(sa=sc->areabase.first; sa; sa= sa->next) {
2779                 if(sa->spacetype==SPACE_IMAGE) {
2780                         sima= sa->spacedata.first;
2781                         if(!sima->image)
2782                                 break;
2783                 }
2784         }
2785         return sa;
2786 }
2787
2788 #if 0 // XXX not used
2789 static ScrArea *find_empty_image_area(bContext *C)
2790 {
2791         bScreen *sc= CTX_wm_screen(C);
2792         ScrArea *sa;
2793         SpaceImage *sima;
2794         
2795         /* find an imagewindow showing render result */
2796         for(sa=sc->areabase.first; sa; sa= sa->next) {
2797                 if(sa->spacetype==SPACE_IMAGE) {
2798                         sima= sa->spacedata.first;
2799                         if(!sima->image)
2800                                 break;
2801                 }
2802         }
2803         return sa;
2804 }
2805 #endif // XXX not used
2806
2807 /* new window uses x,y to set position */
2808 static void screen_set_image_output(bContext *C, int mx, int my)
2809 {
2810         wmWindow *win= CTX_wm_window(C);
2811         Scene *scene= CTX_data_scene(C);
2812         ScrArea *sa= NULL;
2813         SpaceImage *sima;
2814         int area_was_image=0;
2815         
2816         if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2817                 rcti rect;
2818                 int sizex, sizey;
2819                 
2820                 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2821                 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2822                 
2823                 /* arbitrary... miniature image window views don't make much sense */
2824                 if(sizex < 320) sizex= 320;
2825                 if(sizey < 256) sizey= 256;
2826                 
2827                 /* XXX some magic to calculate postition */
2828                 rect.xmin= mx + win->posx - sizex/2;
2829                 rect.ymin= my + win->posy - sizey/2;
2830                 rect.xmax= rect.xmin + sizex;
2831                 rect.ymax= rect.ymin + sizey;
2832                 
2833                 /* changes context! */
2834                 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2835                 
2836                 sa= CTX_wm_area(C);
2837         }
2838         else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2839                 if (CTX_wm_area(C)->spacetype == SPACE_IMAGE)
2840                         area_was_image = 1;
2841                 
2842                 /* this function returns with changed context */
2843                 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2844                 sa= CTX_wm_area(C);
2845         }
2846         
2847         if(!sa) {
2848                 sa= find_area_showing_r_result(C);
2849                 if(sa==NULL)
2850                         sa= find_area_image_empty(C);
2851                 
2852                 if(sa==NULL) {
2853                         /* find largest open non-image area */
2854                         sa= biggest_non_image_area(C);
2855                         if(sa) {
2856                                 ED_area_newspace(C, sa, SPACE_IMAGE);
2857                                 sima= sa->spacedata.first;
2858                                 
2859                                 /* makes ESC go back to prev space */
2860                                 sima->flag |= SI_PREVSPACE;
2861                         }
2862                         else {
2863                                 /* use any area of decent size */
2864                                 sa= biggest_area(C);
2865                                 if(sa->spacetype!=SPACE_IMAGE) {
2866                                         // XXX newspace(sa, SPACE_IMAGE);
2867                                         sima= sa->spacedata.first;
2868                                         
2869                                         /* makes ESC go back to prev space */
2870                                         sima->flag |= SI_PREVSPACE;
2871                                 }
2872                         }
2873                 }
2874         }       
2875         sima= sa->spacedata.first;
2876         
2877         /* get the correct image, and scale it */
2878         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2879         
2880         
2881         /* if we're rendering to full screen, set appropriate hints on image editor
2882          * so it can restore properly on pressing esc */
2883         if(sa->full) {
2884                 sima->flag |= SI_FULLWINDOW;
2885                 
2886                 /* Tell the image editor to revert to previous space in space list on close
2887                  * _only_ if it wasn't already an image editor when the render was invoked */
2888                 if (area_was_image == 0)
2889                         sima->flag |= SI_PREVSPACE;
2890                 else {
2891                         /* Leave it alone so the image editor will just go back from 
2892                          * full screen to the original tiled setup */
2893                         ;
2894                 }
2895                 
2896         }
2897
2898 }
2899
2900 /* executes blocking render */
2901 static int screen_render_exec(bContext *C, wmOperator *op)
2902 {
2903         Scene *scene= CTX_data_scene(C);
2904         Render *re= RE_GetRender(scene->id.name, RE_SLOT_VIEW);
2905         
2906         if(re==NULL) {
2907                 re= RE_NewRender(scene->id.name, RE_SLOT_VIEW);
2908         }
2909         RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2910         
2911         if(RNA_boolean_get(op->ptr, "animation"))
2912                 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step, op->reports);
2913         else
2914                 RE_BlenderFrame(re, scene, NULL, scene->r.cfra);
2915         
2916         // no redraw needed, we leave state as we entered it
2917         ED_update_for_newframe(C, 1);
2918         
2919         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2920         
2921         return OPERATOR_FINISHED;
2922 }
2923
2924 typedef struct RenderJob {
2925         Scene *scene;
2926         Render *re;
2927         wmWindow *win;
2928         SceneRenderLayer *srl;
2929         int anim;
2930         Image *image;
2931         ImageUser iuser;
2932         short *stop;
2933         short *do_update;
2934         ReportList *reports;
2935 } RenderJob;
2936
2937 static void render_freejob(void *rjv)
2938 {
2939         RenderJob *rj= rjv;
2940         
2941         MEM_freeN(rj);
2942 }
2943
2944 /* str is IMA_RW_MAXTEXT in size */
2945 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2946 {
2947         char info_time_str[32]; // used to be extern to header_info.c
2948         uintptr_t mem_in_use, mmap_in_use;
2949         float megs_used_memory, mmap_used_memory;
2950         char *spos= str;
2951         
2952         mem_in_use= MEM_get_memory_in_use();
2953         mmap_in_use= MEM_get_mapped_memory_in_use();
2954         
2955         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2956         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2957         
2958         if(scene->lay & 0xFF000000)
2959                 spos+= sprintf(spos, "Localview | ");
2960         else if(scene->r.scemode & R_SINGLE_LAYER)
2961                 spos+= sprintf(spos, "Single Layer | ");
2962         
2963         if(rs->statstr) {
2964                 spos+= sprintf(spos, "%s ", rs->statstr);
2965         }
2966         else {
2967                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2968                 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2969                 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2970                 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2971                 
2972                 if(rs->curfield)
2973                         spos+= sprintf(spos, "Field %d ", rs->curfield);
2974                 if(rs->curblur)
2975                         spos+= sprintf(spos, "Blur %d ", rs->curblur);
2976         }
2977         
2978         BLI_timestr(rs->lastframetime, info_time_str);
2979         spos+= sprintf(spos, "Time:%s ", info_time_str);
2980         
2981         if(rs->infostr && rs->infostr[0])
2982                 spos+= sprintf(spos, "| %s ", rs->infostr);
2983         
2984         /* very weak... but 512 characters is quite safe */
2985         if(spos >= str+IMA_RW_MAXTEXT)
2986                 if (G.f & G_DEBUG)
2987                         printf("WARNING! renderwin text beyond limit \n");
2988         
2989 }
2990
2991 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2992 {
2993         RenderJob *rj= rjv;
2994         
2995         /* malloc OK here, stats_draw is not in tile threads */
2996         if(rj->image->render_text==NULL)
2997                 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
2998         
2999         make_renderinfo_string(rs, rj->scene, rj->image->render_text);
3000         
3001         /* make jobs timer to send notifier */
3002         *(rj->do_update)= 1;
3003         
3004 }
3005
3006 /* called inside thread! */
3007 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
3008 {
3009         float x1, y1, *rectf= NULL;
3010         int ymin, ymax, xmin, xmax;
3011         int rymin, rxmin;
3012         char *rectc;
3013         
3014         /* if renrect argument, we only refresh scanlines */
3015         if(renrect) {
3016                 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
3017                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
3018                         return;
3019                 
3020                 /* xmin here is first subrect x coord, xmax defines subrect width */
3021                 xmin = renrect->xmin + rr->crop;
3022                 xmax = renrect->xmax - xmin - rr->crop;
3023                 if (xmax<2) return;
3024                 
3025                 ymin= renrect->ymin + rr->crop;
3026                 ymax= renrect->ymax - ymin - rr->crop;
3027                 if(ymax<2)
3028                         return;
3029                 renrect->ymin= renrect->ymax;
3030                 
3031         }
3032         else {
3033                 xmin = ymin = rr->crop;
3034                 xmax = rr->rectx - 2*rr->crop;
3035                 ymax = rr->recty - 2*rr->crop;
3036         }
3037         
3038         /* xmin ymin is in tile coords. transform to ibuf */
3039         rxmin= rr->tilerect.xmin + xmin;
3040         if(rxmin >= ibuf->x) return;
3041         rymin= rr->tilerect.ymin + ymin;
3042         if(rymin >= ibuf->y) return;
3043         
3044         if(rxmin + xmax > ibuf->x)
3045                 xmax= ibuf->x - rxmin;
3046         if(rymin + ymax > ibuf->y)
3047                 ymax= ibuf->y - rymin;
3048         
3049         if(xmax < 1 || ymax < 1) return;
3050         
3051         /* find current float rect for display, first case is after composit... still weak */
3052         if(rr->rectf)
3053                 rectf= rr->rectf;
3054         else {
3055                 if(rr->rect32)
3056                         return;
3057                 else {
3058                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
3059                         rectf= rr->renlay->rectf;
3060                 }
3061         }
3062         if(rectf==NULL) return;
3063         
3064         if(ibuf->rect==NULL)
3065                 imb_addrectImBuf(ibuf);
3066
3067         rectf+= 4*(rr->rectx*ymin + xmin);
3068         rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
3069         
3070         /* XXX make nice consistent functions for this */
3071         if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
3072                 for(y1= 0; y1<ymax; y1++) {
3073                         float *rf= rectf;
3074                         float srgb[3];
3075                         char *rc= rectc;
3076                         
3077                         /* XXX temp. because crop offset */
3078                         if( rectc >= (char *)(ibuf->rect)) {
3079                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3080                                         srgb[0]= linearrgb_to_srgb(rf[0]);
3081                                         srgb[1]= linearrgb_to_srgb(rf[1]);
3082                                         srgb[2]= linearrgb_to_srgb(rf[2]);
3083                                         
3084                                         rc[0]= FTOCHAR(srgb[0]);
3085                                         rc[1]= FTOCHAR(srgb[1]);
3086                                         rc[2]= FTOCHAR(srgb[2]);
3087                                         rc[3]= FTOCHAR(rf[3]);
3088                                 }
3089                         }
3090                         rectf += 4*rr->rectx;
3091                         rectc += 4*ibuf->x;
3092                 }
3093         } else {
3094                 for(y1= 0; y1<ymax; y1++) {
3095                         float *rf= rectf;
3096                         char *rc= rectc;
3097                         
3098                         /* XXX temp. because crop offset */
3099                         if( rectc >= (char *)(ibuf->rect)) {
3100                                 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3101                                         rc[0]= FTOCHAR(rf[0]);
3102                                         rc[1]= FTOCHAR(rf[1]);
3103                                         rc[2]= FTOCHAR(rf[2]);
3104                                         rc[3]= FTOCHAR(rf[3]);
3105                                 }
3106                         }
3107                         rectf += 4*rr->rectx;
3108                         rectc += 4*ibuf->x;
3109                 }
3110         }
3111         
3112 }
3113
3114 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
3115 {
3116         RenderJob *rj= rjv;
3117         ImBuf *ibuf;
3118         void *lock;
3119         
3120         ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
3121         if(ibuf) {
3122                 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
3123                 
3124                 /* make jobs timer to send notifier */
3125                 *(rj->do_update)= 1;
3126         }
3127         BKE_image_release_ibuf(rj->image, lock);
3128 }
3129
3130 static void render_startjob(void *rjv, short *stop, short *do_update)
3131 {
3132         RenderJob *rj= rjv;
3133 //      Main *mainp= BKE_undo_get_main(&rj->scene);
3134         
3135         rj->stop= stop;
3136         rj->do_update= do_update;
3137         
3138 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2)
3139         // Workaround for Apple gcc 4.2.1 omp vs background thread bug
3140         pthread_setspecific (gomp_tls_key, thread_tls_data);
3141 #endif
3142
3143         if(rj->anim)
3144                 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step, rj->reports);
3145         else
3146                 RE_BlenderFrame(rj->re, rj->scene, rj->srl, rj->scene->r.cfra);
3147         
3148 //      if(mainp)
3149 //              free_main(mainp);
3150 }
3151
3152 /* called by render, check job 'stop' value or the global */
3153 static int render_breakjob(void *rjv)
3154 {
3155         RenderJob *rj= rjv;
3156         
3157         if(G.afbreek)
3158                 return 1;
3159         if(rj->stop && *(rj->stop))
3160                 return 1;
3161         return 0;
3162 }
3163
3164 /* catch esc */
3165 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3166 {
3167         /* no running blender, remove handler and pass through */
3168         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
3169                 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
3170         
3171         /* running render */
3172         switch (event->type) {
3173                 case ESCKEY:
3174                         return OPERATOR_RUNNING_MODAL;
3175                         break;
3176         }
3177         return OPERATOR_PASS_THROUGH;
3178 }
3179
3180 /* using context, starts job */
3181 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3182 {
3183         /* new render clears all callbacks */
3184         Scene *scene= CTX_data_scene(C);
3185         SceneRenderLayer *srl=NULL;
3186         Render *re;
3187         wmJob *steve;
3188         RenderJob *rj;
3189         Image *ima;
3190         
3191         /* only one render job at a time */
3192         if(WM_jobs_test(CTX_wm_manager(C), scene))
3193                 return OPERATOR_CANCELLED;
3194         
3195         /* stop all running jobs, currently previews frustrate Render */
3196         WM_jobs_stop_all(CTX_wm_manager(C));
3197         
3198         /* handle UI stuff */
3199         WM_cursor_wait(1);
3200         
3201         /* flush multires changes (for sculpt) */
3202         multires_force_render_update(CTX_data_active_object(C));
3203         
3204         /* get editmode results */
3205         ED_object_exit_editmode(C, EM_FREEDATA|EM_DO_UNDO);     /* 0 = does not exit editmode */
3206         
3207         // store spare