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