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