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