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