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