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