62fdfc140dfe8500006aba88efbbaa2ee8f08738
[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 static 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 && !(((ID *)ob->data)->lib));
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 int actionzone_cancel(bContext *UNUSED(C), wmOperator *op)
631 {
632         actionzone_exit(op);
633
634         return OPERATOR_CANCELLED;
635 }
636
637 static void SCREEN_OT_actionzone(wmOperatorType *ot)
638 {
639         /* identifiers */
640         ot->name= "Handle area action zones";
641         ot->description= "Handle area action zones for mouse actions/gestures";
642         ot->idname= "SCREEN_OT_actionzone";
643         
644         ot->invoke= actionzone_invoke;
645         ot->modal= actionzone_modal;
646         ot->poll= actionzone_area_poll;
647         ot->cancel= actionzone_cancel;
648         
649         ot->flag= OPTYPE_BLOCKING;
650         
651         RNA_def_int(ot->srna, "modifier", 0, 0, 2, "modifier", "modifier state", 0, 2);
652 }
653
654 /* ************** swap area operator *********************************** */
655
656 /* operator state vars used:  
657  sa1            start area
658  sa2            area to swap with
659  
660  functions:
661  
662  init()   set custom data for operator, based on actionzone event custom data
663  
664  cancel()       cancel the operator
665  
666  exit() cleanup, send notifier
667  
668  callbacks:
669  
670  invoke() gets called on shift+lmb drag in actionzone
671  call init(), add handler
672  
673  modal()  accept modal events while doing it
674  
675  */
676
677 typedef struct sAreaSwapData {
678         ScrArea *sa1, *sa2;
679 } sAreaSwapData;
680
681 static int area_swap_init(wmOperator *op, wmEvent *event)
682 {
683         sAreaSwapData *sd= NULL;
684         sActionzoneData *sad= event->customdata;
685         
686         if(sad==NULL || sad->sa1==NULL)
687                 return 0;
688         
689         sd= MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
690         sd->sa1= sad->sa1;
691         sd->sa2= sad->sa2;
692         op->customdata= sd;
693         
694         return 1;
695 }
696
697
698 static void area_swap_exit(bContext *C, wmOperator *op)
699 {
700         WM_cursor_restore(CTX_wm_window(C));
701         if(op->customdata)
702                 MEM_freeN(op->customdata);
703         op->customdata= NULL;
704 }
705
706 static int area_swap_cancel(bContext *C, wmOperator *op)
707 {
708         area_swap_exit(C, op);
709         return OPERATOR_CANCELLED;
710 }
711
712 static int area_swap_invoke(bContext *C, wmOperator *op, wmEvent *event)
713 {
714         
715         if(!area_swap_init(op, event))
716                 return OPERATOR_PASS_THROUGH;
717         
718         /* add modal handler */
719         WM_cursor_modal(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
720         WM_event_add_modal_handler(C, op);
721         
722         return OPERATOR_RUNNING_MODAL;
723         
724 }
725
726 static int area_swap_modal(bContext *C, wmOperator *op, wmEvent *event)
727 {
728         sActionzoneData *sad= op->customdata;
729         
730         switch(event->type) {
731                 case MOUSEMOVE:
732                         /* second area, for join */
733                         sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
734                         break;
735                 case LEFTMOUSE: /* release LMB */
736                         if(event->val==KM_RELEASE) {
737                                 if(!sad->sa2 || sad->sa1 == sad->sa2) {
738                                         
739                                         return area_swap_cancel(C, op);
740                                 }
741
742                                 ED_area_tag_redraw(sad->sa1);
743                                 ED_area_tag_redraw(sad->sa2);
744
745                                 ED_area_swapspace(C, sad->sa1, sad->sa2);
746                                 
747                                 area_swap_exit(C, op);
748                                 
749                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
750                                 
751                                 return OPERATOR_FINISHED;
752                         }
753                         break;
754                         
755                 case ESCKEY:
756                         return area_swap_cancel(C, op);
757         }
758         return OPERATOR_RUNNING_MODAL;
759 }
760
761 static void SCREEN_OT_area_swap(wmOperatorType *ot)
762 {
763         ot->name= "Swap areas";
764         ot->description= "Swap selected areas screen positions";
765         ot->idname= "SCREEN_OT_area_swap";
766         
767         ot->invoke= area_swap_invoke;
768         ot->modal= area_swap_modal;
769         ot->poll= ED_operator_areaactive;
770         ot->cancel= area_swap_cancel;
771         
772         ot->flag= OPTYPE_BLOCKING;
773 }
774
775 /* *********** Duplicate area as new window operator ****************** */
776
777 /* operator callback */
778 static int area_dupli_invoke(bContext *C, wmOperator *op, wmEvent *event)
779 {
780         wmWindow *newwin, *win;
781         bScreen *newsc, *sc;
782         ScrArea *sa;
783         rcti rect;
784         
785         win= CTX_wm_window(C);
786         sc= CTX_wm_screen(C);
787         sa= CTX_wm_area(C);
788         
789         /* XXX hrmf! */
790         if(event->type==EVT_ACTIONZONE_AREA) {
791                 sActionzoneData *sad= event->customdata;
792                 
793                 if(sad==NULL)
794                         return OPERATOR_PASS_THROUGH;
795                 
796                 sa= sad->sa1;
797         }
798         
799         /*  poll() checks area context, but we don't accept full-area windows */
800         if(sc->full != SCREENNORMAL) {
801                 if(event->type==EVT_ACTIONZONE_AREA)
802                         actionzone_exit(op);
803                 return OPERATOR_CANCELLED;
804         }
805         
806         /* adds window to WM */
807         rect= sa->totrct;
808         BLI_translate_rcti(&rect, win->posx, win->posy);
809         newwin= WM_window_open(C, &rect);
810         
811         /* allocs new screen and adds to newly created window, using window size */
812         newsc= ED_screen_add(newwin, CTX_data_scene(C), sc->id.name+2);
813         newwin->screen= newsc;
814         
815         /* copy area to new screen */
816         area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
817
818         ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
819
820         /* screen, areas init */
821         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
822         
823         if(event->type==EVT_ACTIONZONE_AREA)
824                 actionzone_exit(op);
825         
826         return OPERATOR_FINISHED;
827 }
828
829 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
830 {
831         ot->name= "Duplicate Area into New Window";
832         ot->description= "Duplicate selected area into new window";
833         ot->idname= "SCREEN_OT_area_dupli";
834         
835         ot->invoke= area_dupli_invoke;
836         ot->poll= ED_operator_areaactive;
837 }
838
839
840 /* ************** move area edge operator *********************************** */
841
842 /* operator state vars used:  
843  x, y                           mouse coord near edge
844  delta            movement of edge
845  
846  functions:
847  
848  init()   set default property values, find edge based on mouse coords, test
849  if the edge can be moved, select edges, calculate min and max movement
850  
851  apply()        apply delta on selection
852  
853  exit() cleanup, send notifier
854  
855  cancel() cancel moving
856  
857  callbacks:
858  
859  exec()   execute without any user interaction, based on properties
860  call init(), apply(), exit()
861  
862  invoke() gets called on mouse click near edge
863  call init(), add handler
864  
865  modal()  accept modal events while doing it
866  call apply() with delta motion
867  call exit() and remove handler
868  
869  */
870
871 typedef struct sAreaMoveData {
872         int bigger, smaller, origval, step;
873         char dir;
874 } sAreaMoveData;
875
876 /* helper call to move area-edge, sets limits */
877 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
878 {
879         ScrArea *sa;
880         int areaminy= ED_area_headersize()+1;
881         
882         /* we check all areas and test for free space with MINSIZE */
883         *bigger= *smaller= 100000;
884         
885         for(sa= sc->areabase.first; sa; sa= sa->next) {
886                 if(dir=='h') {
887                         int y1= sa->v2->vec.y - sa->v1->vec.y-areaminy;
888                         
889                         /* if top or down edge selected, test height */
890                         if(sa->v1->flag && sa->v4->flag)
891                                 *bigger= MIN2(*bigger, y1);
892                         else if(sa->v2->flag && sa->v3->flag)
893                                 *smaller= MIN2(*smaller, y1);
894                 }
895                 else {
896                         int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
897                         
898                         /* if left or right edge selected, test width */
899                         if(sa->v1->flag && sa->v2->flag)
900                                 *bigger= MIN2(*bigger, x1);
901                         else if(sa->v3->flag && sa->v4->flag)
902                                 *smaller= MIN2(*smaller, x1);
903                 }
904         }
905 }
906
907 /* validate selection inside screen, set variables OK */
908 /* return 0: init failed */
909 static int area_move_init (bContext *C, wmOperator *op)
910 {
911         bScreen *sc= CTX_wm_screen(C);
912         ScrEdge *actedge;
913         sAreaMoveData *md;
914         int x, y;
915         
916         /* required properties */
917         x= RNA_int_get(op->ptr, "x");
918         y= RNA_int_get(op->ptr, "y");
919         
920         /* setup */
921         actedge= screen_find_active_scredge(sc, x, y);
922         if(actedge==NULL) return 0;
923         
924         md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
925         op->customdata= md;
926         
927         md->dir= scredge_is_horizontal(actedge)?'h':'v';
928         if(md->dir=='h') md->origval= actedge->v1->vec.y;
929         else md->origval= actedge->v1->vec.x;
930         
931         select_connected_scredge(sc, actedge);
932         /* now all vertices with 'flag==1' are the ones that can be moved. */
933         
934         area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
935         
936         return 1;
937 }
938
939 /* moves selected screen edge amount of delta, used by split & move */
940 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
941 {
942         wmWindow *win= CTX_wm_window(C);
943         bScreen *sc= CTX_wm_screen(C);
944         ScrVert *v1;
945         ScrArea *sa;
946         int areaminy= ED_area_headersize()+1;
947         
948         delta= CLAMPIS(delta, -smaller, bigger);
949         
950         for (v1= sc->vertbase.first; v1; v1= v1->next) {
951                 if (v1->flag) {
952                         /* that way a nice AREAGRID  */
953                         if((dir=='v') && v1->vec.x>0 && v1->vec.x<win->sizex-1) {
954                                 v1->vec.x= origval + delta;
955                                 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
956                         }
957                         if((dir=='h') && v1->vec.y>0 && v1->vec.y<win->sizey-1) {
958                                 v1->vec.y= origval + delta;
959                                 
960                                 v1->vec.y+= AREAGRID-1;
961                                 v1->vec.y-= (v1->vec.y % AREAGRID);
962                                 
963                                 /* prevent too small top header */
964                                 if(v1->vec.y > win->sizey-areaminy)
965                                         v1->vec.y= win->sizey-areaminy;
966                         }
967                 }
968         }
969
970         for(sa= sc->areabase.first; sa; sa= sa->next) {
971                 if(sa->v1->flag || sa->v2->flag || sa->v3->flag || sa->v4->flag)
972                         ED_area_tag_redraw(sa);
973         }
974
975         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL); /* redraw everything */
976 }
977
978 static void area_move_apply(bContext *C, wmOperator *op)
979 {
980         sAreaMoveData *md= op->customdata;
981         int delta;
982         
983         delta= RNA_int_get(op->ptr, "delta");
984         area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
985 }
986
987 static void area_move_exit(bContext *C, wmOperator *op)
988 {
989         if(op->customdata)
990                 MEM_freeN(op->customdata);
991         op->customdata= NULL;
992         
993         /* this makes sure aligned edges will result in aligned grabbing */
994         removedouble_scrverts(CTX_wm_screen(C));
995         removedouble_scredges(CTX_wm_screen(C));
996 }
997
998 static int area_move_exec(bContext *C, wmOperator *op)
999 {
1000         if(!area_move_init(C, op))
1001                 return OPERATOR_CANCELLED;
1002         
1003         area_move_apply(C, op);
1004         area_move_exit(C, op);
1005         
1006         return OPERATOR_FINISHED;
1007 }
1008
1009 /* interaction callback */
1010 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
1011 {
1012         RNA_int_set(op->ptr, "x", event->x);
1013         RNA_int_set(op->ptr, "y", event->y);
1014         
1015         if(!area_move_init(C, op)) 
1016                 return OPERATOR_PASS_THROUGH;
1017         
1018         /* add temp handler */
1019         WM_event_add_modal_handler(C, op);
1020         
1021         return OPERATOR_RUNNING_MODAL;
1022 }
1023
1024 static int area_move_cancel(bContext *C, wmOperator *op)
1025 {
1026         
1027         RNA_int_set(op->ptr, "delta", 0);
1028         area_move_apply(C, op);
1029         area_move_exit(C, op);
1030         
1031         return OPERATOR_CANCELLED;
1032 }
1033
1034 /* modal callback for while moving edges */
1035 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
1036 {
1037         sAreaMoveData *md= op->customdata;
1038         int delta, x, y;
1039         
1040         /* execute the events */
1041         switch(event->type) {
1042                 case MOUSEMOVE:
1043                         
1044                         x= RNA_int_get(op->ptr, "x");
1045                         y= RNA_int_get(op->ptr, "y");
1046                         
1047                         delta= (md->dir == 'v')? event->x - x: event->y - y;
1048                         if(md->step) delta= delta - (delta % md->step);
1049                         RNA_int_set(op->ptr, "delta", delta);
1050                         
1051                         area_move_apply(C, op);
1052                         break;
1053                         
1054                 case EVT_MODAL_MAP:
1055                         
1056                         switch (event->val) {
1057                                 case KM_MODAL_APPLY:
1058                                         area_move_exit(C, op);
1059                                         return OPERATOR_FINISHED;
1060                                         
1061                                 case KM_MODAL_CANCEL:
1062                                         return area_move_cancel(C, op);
1063                                         
1064                                 case KM_MODAL_STEP10:
1065                                         md->step= 10;
1066                                         break;
1067                                 case KM_MODAL_STEP10_OFF:
1068                                         md->step= 0;
1069                                         break;
1070                         }
1071         }
1072         
1073         return OPERATOR_RUNNING_MODAL;
1074 }
1075
1076 static void SCREEN_OT_area_move(wmOperatorType *ot)
1077 {
1078         /* identifiers */
1079         ot->name= "Move area edges";
1080         ot->description= "Move selected area edges";
1081         ot->idname= "SCREEN_OT_area_move";
1082         
1083         ot->exec= area_move_exec;
1084         ot->invoke= area_move_invoke;
1085         ot->cancel= area_move_cancel;
1086         ot->modal= area_move_modal;
1087         ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
1088         
1089         ot->flag= OPTYPE_BLOCKING;
1090         
1091         /* rna */
1092         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1093         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1094         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1095 }
1096
1097 /* ************** split area operator *********************************** */
1098
1099 /* 
1100  operator state vars:  
1101  fac              spit point
1102  dir              direction 'v' or 'h'
1103  
1104  operator customdata:
1105  area                           pointer to (active) area
1106  x, y                   last used mouse pos
1107  (more, see below)
1108  
1109  functions:
1110  
1111  init()   set default property values, find area based on context
1112  
1113  apply()        split area based on state vars
1114  
1115  exit() cleanup, send notifier
1116  
1117  cancel() remove duplicated area
1118  
1119  callbacks:
1120  
1121  exec()   execute without any user interaction, based on state vars
1122  call init(), apply(), exit()
1123  
1124  invoke() gets called on mouse click in action-widget
1125  call init(), add modal handler
1126  call apply() with initial motion
1127  
1128  modal()  accept modal events while doing it
1129  call move-areas code with delta motion
1130  call exit() or cancel() and remove handler
1131  
1132  */
1133
1134 #define SPLIT_STARTED   1
1135 #define SPLIT_PROGRESS  2
1136
1137 typedef struct sAreaSplitData {
1138         int x, y;       /* last used mouse position */
1139         
1140         int origval;                    /* for move areas */
1141         int bigger, smaller;    /* constraints for moving new edge */
1142         int delta;                              /* delta move edge */
1143         int origmin, origsize;  /* to calculate fac, for property storage */
1144         int previewmode;                /* draw previewline, then split */
1145         
1146         ScrEdge *nedge;                 /* new edge */
1147         ScrArea *sarea;                 /* start area */
1148         ScrArea *narea;                 /* new area */
1149         
1150 } sAreaSplitData;
1151
1152 /* generic init, menu case, doesn't need active area */
1153 static int area_split_menu_init(bContext *C, wmOperator *op)
1154 {
1155         sAreaSplitData *sd;
1156         
1157         /* custom data */
1158         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1159         op->customdata= sd;
1160         
1161         sd->sarea= CTX_wm_area(C);
1162         
1163         if(sd->sarea) {
1164                 int dir= RNA_enum_get(op->ptr, "direction");
1165
1166                 if(dir=='h')
1167                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1168                 else
1169                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1170         }
1171         return 1;
1172 }
1173
1174 /* generic init, no UI stuff here, assumes active area */
1175 static int area_split_init(bContext *C, wmOperator *op)
1176 {
1177         ScrArea *sa= CTX_wm_area(C);
1178         sAreaSplitData *sd;
1179         int areaminy= ED_area_headersize()+1;
1180         int dir;
1181         
1182         /* required context */
1183         if(sa==NULL) return 0;
1184         
1185         /* required properties */
1186         dir= RNA_enum_get(op->ptr, "direction");
1187         
1188         /* minimal size */
1189         if(dir=='v' && sa->winx < 2*AREAMINX) return 0;
1190         if(dir=='h' && sa->winy < 2*areaminy) return 0;
1191         
1192         /* custom data */
1193         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1194         op->customdata= sd;
1195         
1196         sd->sarea= sa;
1197         sd->origsize= dir=='v' ? sa->winx:sa->winy;
1198         sd->origmin = dir=='v' ? sa->totrct.xmin:sa->totrct.ymin;
1199         
1200         return 1;
1201 }
1202
1203 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1204 /* used with split operator */
1205 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1206 {
1207         ScrVert *sav1= sa->v1;
1208         ScrVert *sav2= sa->v2;
1209         ScrVert *sav3= sa->v3;
1210         ScrVert *sav4= sa->v4;
1211         ScrVert *sbv1= sb->v1;
1212         ScrVert *sbv2= sb->v2;
1213         ScrVert *sbv3= sb->v3;
1214         ScrVert *sbv4= sb->v4;
1215         
1216         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
1217                 return screen_findedge(screen, sav1, sav2);
1218         }
1219         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
1220                 return screen_findedge(screen, sav2, sav3);
1221         }
1222         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
1223                 return screen_findedge(screen, sav3, sav4);
1224         }
1225         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
1226                 return screen_findedge(screen, sav1, sav4);
1227         }
1228         
1229         return NULL;
1230 }
1231
1232
1233 /* do the split, return success */
1234 static int area_split_apply(bContext *C, wmOperator *op)
1235 {
1236         bScreen *sc= CTX_wm_screen(C);
1237         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1238         float fac;
1239         int dir;
1240         
1241         fac= RNA_float_get(op->ptr, "factor");
1242         dir= RNA_enum_get(op->ptr, "direction");
1243         
1244         sd->narea= area_split(sc, sd->sarea, dir, fac, 0); /* 0 = no merge */
1245         
1246         if(sd->narea) {
1247                 ScrVert *sv;
1248                 
1249                 sd->nedge= area_findsharededge(sc, sd->sarea, sd->narea);
1250                 
1251                 /* select newly created edge, prepare for moving edge */
1252                 for(sv= sc->vertbase.first; sv; sv= sv->next)
1253                         sv->flag = 0;
1254                 
1255                 sd->nedge->v1->flag= 1;
1256                 sd->nedge->v2->flag= 1;
1257                 
1258                 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
1259                 else sd->origval= sd->nedge->v1->vec.x;
1260
1261                 ED_area_tag_redraw(sd->sarea);
1262                 ED_area_tag_redraw(sd->narea);
1263
1264                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1265                 
1266                 return 1;
1267         }               
1268         
1269         return 0;
1270 }
1271
1272 static void area_split_exit(bContext *C, wmOperator *op)
1273 {
1274         if (op->customdata) {
1275                 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1276                 if(sd->sarea) ED_area_tag_redraw(sd->sarea);
1277                 if(sd->narea) ED_area_tag_redraw(sd->narea);
1278
1279                 if(sd->sarea)
1280                         sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H|AREA_FLAG_DRAWSPLIT_V);
1281                 
1282                 MEM_freeN(op->customdata);
1283                 op->customdata = NULL;
1284         }
1285         
1286         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1287         
1288         /* this makes sure aligned edges will result in aligned grabbing */
1289         removedouble_scrverts(CTX_wm_screen(C));
1290         removedouble_scredges(CTX_wm_screen(C));
1291 }
1292
1293
1294 /* UI callback, adds new handler */
1295 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
1296 {
1297         sAreaSplitData *sd;
1298         int dir;
1299         
1300         /* no full window splitting allowed */
1301         if(CTX_wm_screen(C)->full != SCREENNORMAL)
1302                 return OPERATOR_CANCELLED;
1303         
1304         if(event->type==EVT_ACTIONZONE_AREA) {
1305                 sActionzoneData *sad= event->customdata;
1306                 
1307                 if(sad->modifier>0) {
1308                         return OPERATOR_PASS_THROUGH;
1309                 }
1310                 
1311                 /* verify *sad itself */
1312                 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
1313                         return OPERATOR_PASS_THROUGH;
1314                 
1315                 /* is this our *sad? if areas not equal it should be passed on */
1316                 if(CTX_wm_area(C)!=sad->sa1 || sad->sa1!=sad->sa2)
1317                         return OPERATOR_PASS_THROUGH;
1318                 
1319                 /* prepare operator state vars */
1320                 if(sad->gesture_dir=='n' || sad->gesture_dir=='s') {
1321                         dir= 'h';
1322                         RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1323                 }
1324                 else {
1325                         dir= 'v';
1326                         RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1327                 }
1328                 RNA_enum_set(op->ptr, "direction", dir);
1329                 
1330                 /* general init, also non-UI case, adds customdata, sets area and defaults */
1331                 if(!area_split_init(C, op))
1332                         return OPERATOR_PASS_THROUGH;
1333                 
1334         }
1335         else {
1336                 ScrEdge *actedge;
1337                 int x, y;
1338                 
1339                 /* retrieve initial mouse coord, so we can find the active edge */
1340                 if(RNA_property_is_set(op->ptr, "mouse_x"))
1341                         x= RNA_int_get(op->ptr, "mouse_x");
1342                 else
1343                         x= event->x;
1344                 
1345                 if(RNA_property_is_set(op->ptr, "mouse_y"))
1346                         y= RNA_int_get(op->ptr, "mouse_y");
1347                 else
1348                         y= event->x;
1349                 
1350                 actedge= screen_find_active_scredge(CTX_wm_screen(C), x, y);
1351                 if(actedge==NULL) 
1352                         return OPERATOR_CANCELLED;
1353                 
1354                 dir= scredge_is_horizontal(actedge)?'v':'h';
1355                 
1356                 RNA_enum_set(op->ptr, "direction", dir);
1357                 
1358                 /* special case, adds customdata, sets defaults */
1359                 if(!area_split_menu_init(C, op))
1360                         return OPERATOR_CANCELLED;
1361                 
1362         }
1363         
1364         sd= (sAreaSplitData *)op->customdata;
1365         
1366         sd->x= event->x;
1367         sd->y= event->y;
1368         
1369         if(event->type==EVT_ACTIONZONE_AREA) {
1370                 
1371                 /* do the split */
1372                 if(area_split_apply(C, op)) {
1373                         area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
1374                         
1375                         /* add temp handler for edge move or cancel */
1376                         WM_event_add_modal_handler(C, op);
1377                         
1378                         return OPERATOR_RUNNING_MODAL;
1379                 }
1380         }
1381         else {
1382                 sd->previewmode= 1;
1383                 /* add temp handler for edge move or cancel */
1384                 WM_event_add_modal_handler(C, op);
1385                 
1386                 return OPERATOR_RUNNING_MODAL;
1387                 
1388         }
1389         
1390         return OPERATOR_PASS_THROUGH;
1391 }
1392
1393 /* function to be called outside UI context, or for redo */
1394 static int area_split_exec(bContext *C, wmOperator *op)
1395 {
1396         
1397         if(!area_split_init(C, op))
1398                 return OPERATOR_CANCELLED;
1399         
1400         area_split_apply(C, op);
1401         area_split_exit(C, op);
1402         
1403         return OPERATOR_FINISHED;
1404 }
1405
1406
1407 static int area_split_cancel(bContext *C, wmOperator *op)
1408 {
1409         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1410         
1411         if(sd->previewmode) {
1412         }
1413         else {
1414                 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1415                         if (CTX_wm_area(C) == sd->narea) {
1416                                 CTX_wm_area_set(C, NULL);
1417                                 CTX_wm_region_set(C, NULL);
1418                         }
1419                         sd->narea = NULL;
1420                 }
1421         }
1422         area_split_exit(C, op);
1423         
1424         return OPERATOR_CANCELLED;
1425 }
1426
1427 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
1428 {
1429         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1430         float fac;
1431         int dir;
1432         
1433         /* execute the events */
1434         switch(event->type) {
1435                 case MOUSEMOVE:
1436                         dir= RNA_enum_get(op->ptr, "direction");
1437                         
1438                         sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
1439                         if(sd->previewmode==0) 
1440                                 area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1441                         else {
1442                                 if(sd->sarea) {
1443                                         sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H|AREA_FLAG_DRAWSPLIT_V);
1444                                         ED_area_tag_redraw(sd->sarea);
1445                                 }
1446                                 sd->sarea= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);  /* area context not set */
1447                                 
1448                                 if(sd->sarea) {
1449                                         ED_area_tag_redraw(sd->sarea);
1450                                         if (dir=='v') {
1451                                                 sd->origsize= sd->sarea->winx;
1452                                                 sd->origmin= sd->sarea->totrct.xmin;
1453                                                 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1454                                         }
1455                                         else {
1456                                                 sd->origsize= sd->sarea->winy;
1457                                                 sd->origmin= sd->sarea->totrct.ymin;
1458                                                 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1459                                         }
1460                                 }
1461                                 
1462                                 CTX_wm_window(C)->screen->do_draw= 1;
1463
1464                         }
1465                         
1466                         fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
1467                         RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1468                         
1469                         break;
1470                         
1471                 case LEFTMOUSE:
1472                         if(sd->previewmode) {
1473                                 area_split_apply(C, op);
1474                                 area_split_exit(C, op);
1475                                 return OPERATOR_FINISHED;
1476                         }
1477                         else {
1478                                 if(event->val==KM_RELEASE) { /* mouse up */
1479                                         area_split_exit(C, op);
1480                                         return OPERATOR_FINISHED;
1481                                 }
1482                         }
1483                         break;
1484                 case RIGHTMOUSE: /* cancel operation */
1485                 case ESCKEY:
1486                         return area_split_cancel(C, op);
1487         }
1488         
1489         return OPERATOR_RUNNING_MODAL;
1490 }
1491
1492 static EnumPropertyItem prop_direction_items[] = {
1493 {'h', "HORIZONTAL", 0, "Horizontal", ""},
1494 {'v', "VERTICAL", 0, "Vertical", ""},
1495 {0, NULL, 0, NULL, NULL}};
1496
1497 static void SCREEN_OT_area_split(wmOperatorType *ot)
1498 {
1499         ot->name = "Split area";
1500         ot->description= "Split selected area into new windows";
1501         ot->idname = "SCREEN_OT_area_split";
1502         
1503         ot->exec= area_split_exec;
1504         ot->invoke= area_split_invoke;
1505         ot->modal= area_split_modal;
1506         ot->cancel= area_split_cancel;
1507         
1508         ot->poll= screen_active_editable;
1509         ot->flag= OPTYPE_BLOCKING;
1510         
1511         /* rna */
1512         RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1513         RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1514         RNA_def_int(ot->srna, "mouse_x", -100, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
1515         RNA_def_int(ot->srna, "mouse_y", -100, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
1516 }
1517
1518
1519
1520 /* ************** scale region edge operator *********************************** */
1521
1522 typedef struct RegionMoveData {
1523         AZone *az;
1524         ARegion *ar;
1525         ScrArea *sa;
1526         int bigger, smaller, origval;
1527         int origx, origy;
1528         int maxsize;
1529         AZEdge edge;
1530         
1531 } RegionMoveData;
1532
1533
1534 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
1535 {
1536         ARegion *ar;
1537         int dist;
1538         
1539         if(edge==AE_RIGHT_TO_TOPLEFT || edge==AE_LEFT_TO_TOPRIGHT) {
1540                 dist = sa->totrct.xmax - sa->totrct.xmin;
1541         } else {        /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
1542                 dist = sa->totrct.ymax - sa->totrct.ymin;
1543         }
1544         
1545         /* subtractwidth of regions on opposite side 
1546          * prevents dragging regions into other opposite regions */
1547         for (ar=sa->regionbase.first; ar; ar=ar->next) {
1548                 if (ar == scalear)
1549                         continue;
1550                 
1551                 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1552                         dist -= ar->winy;
1553                 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1554                         dist -= ar->winy;
1555                 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1556                         dist -= ar->winx;
1557                 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1558                         dist -= ar->winx;
1559                 
1560                 /* case of regions in regions, like operator properties panel */
1561                 /* these can sit on top of other regions such as headers, so account for this */
1562                 else if (edge == AE_BOTTOM_TO_TOPLEFT && scalear->alignment & RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_TOP && ar->regiontype == RGN_TYPE_HEADER)
1563                         dist -= ar->winy;
1564                 else if (edge == AE_TOP_TO_BOTTOMRIGHT && scalear->alignment & RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_BOTTOM && ar->regiontype == RGN_TYPE_HEADER)
1565                         dist -= ar->winy;
1566         }
1567
1568         return dist;
1569 }
1570
1571 static int region_scale_invoke(bContext *C, wmOperator *op, wmEvent *event)
1572 {
1573         sActionzoneData *sad= event->customdata;
1574         AZone *az;
1575         
1576         if(event->type!=EVT_ACTIONZONE_REGION) {
1577                 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");   
1578                 return OPERATOR_CANCELLED;
1579         }
1580         
1581         az = sad->az;
1582         
1583         if(az->ar) {
1584                 RegionMoveData *rmd= MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1585                 int maxsize;
1586                 
1587                 op->customdata= rmd;
1588                 
1589                 rmd->az = az;
1590                 rmd->ar= az->ar;
1591                 rmd->sa = sad->sa1;
1592                 rmd->edge= az->edge;
1593                 rmd->origx= event->x;
1594                 rmd->origy= event->y;
1595                 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1596                 
1597                 /* if not set we do now, otherwise it uses type */
1598                 if(rmd->ar->sizex==0) 
1599                         rmd->ar->sizex= rmd->ar->type->prefsizex;
1600                 if(rmd->ar->sizey==0) 
1601                         rmd->ar->sizey= rmd->ar->type->prefsizey;
1602                 
1603                 /* now copy to regionmovedata */
1604                 if(rmd->edge==AE_LEFT_TO_TOPRIGHT || rmd->edge==AE_RIGHT_TO_TOPLEFT) {
1605                         rmd->origval= rmd->ar->sizex;
1606                 } else {
1607                         rmd->origval= rmd->ar->sizey;
1608                 }
1609                 
1610                 /* limit headers to standard height for now */
1611                 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1612                         maxsize = rmd->ar->type->prefsizey;
1613                 else
1614                         maxsize = 1000;
1615                 
1616                 CLAMP(rmd->maxsize, 0, maxsize);
1617                 
1618                 /* add temp handler */
1619                 WM_event_add_modal_handler(C, op);
1620                 
1621                 return OPERATOR_RUNNING_MODAL;
1622         }
1623         
1624         return OPERATOR_FINISHED;
1625 }
1626
1627 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1628 {
1629         RegionMoveData *rmd= op->customdata;
1630         int delta;
1631         
1632         /* execute the events */
1633         switch(event->type) {
1634                 case MOUSEMOVE:
1635                         
1636                         if(rmd->edge==AE_LEFT_TO_TOPRIGHT || rmd->edge==AE_RIGHT_TO_TOPLEFT) {
1637                                 delta= event->x - rmd->origx;
1638                                 if(rmd->edge==AE_LEFT_TO_TOPRIGHT) delta= -delta;
1639                                 
1640                                 rmd->ar->sizex= rmd->origval + delta;
1641                                 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
1642                                 
1643                                 if(rmd->ar->sizex < UI_UNIT_X) {
1644                                         rmd->ar->sizex= rmd->origval;
1645                                         if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1646                                                 ED_region_toggle_hidden(C, rmd->ar);
1647                                 }
1648                                 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1649                                         ED_region_toggle_hidden(C, rmd->ar);
1650                         }
1651                         else {
1652                                 int maxsize=0;
1653                                 delta= event->y - rmd->origy;
1654                                 if(rmd->edge==AE_BOTTOM_TO_TOPLEFT) delta= -delta;
1655                                 
1656                                 rmd->ar->sizey= rmd->origval + delta;
1657                                 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
1658
1659                                 if(rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) {
1660                                         /* this calculation seems overly verbose
1661                                          * can someone explain why this method is necessary? - campbell */
1662                                         maxsize = rmd->maxsize - ((rmd->sa->headertype==HEADERTOP)?UI_UNIT_Y*2:UI_UNIT_Y) - (UI_UNIT_Y/4);
1663                                 }
1664
1665                                 /* note, 'UI_UNIT_Y/4' means you need to drag the header almost
1666                                  * all the way down for it to become hidden, this is done
1667                                  * otherwise its too easy to do this by accident */
1668                                 if(rmd->ar->sizey < UI_UNIT_Y/4 || (maxsize > 0 && (rmd->ar->sizey > maxsize)) ) {
1669                                         rmd->ar->sizey= rmd->origval;
1670                                         if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1671                                                 ED_region_toggle_hidden(C, rmd->ar);
1672                                 }
1673                                 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1674                                         ED_region_toggle_hidden(C, rmd->ar);
1675                         }
1676                         ED_area_tag_redraw(rmd->sa);
1677                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1678                         
1679                         break;
1680                         
1681                 case LEFTMOUSE:
1682                         if(event->val==KM_RELEASE) {
1683                                 
1684                                 if(ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1685                                         if(rmd->ar->flag & RGN_FLAG_HIDDEN) {
1686                                                 ED_region_toggle_hidden(C, rmd->ar);
1687                                                 ED_area_tag_redraw(rmd->sa);
1688                                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1689                                         }
1690                                 }
1691                                 MEM_freeN(op->customdata);
1692                                 op->customdata = NULL;
1693                                 
1694                                 return OPERATOR_FINISHED;
1695                         }
1696                         break;
1697                         
1698                 case ESCKEY:
1699                         ;
1700         }
1701         
1702         return OPERATOR_RUNNING_MODAL;
1703 }
1704
1705 static int region_scale_cancel(bContext *UNUSED(C), wmOperator *op)
1706 {
1707         MEM_freeN(op->customdata);
1708         op->customdata = NULL;
1709
1710         return OPERATOR_CANCELLED;
1711 }
1712
1713 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1714 {
1715         /* identifiers */
1716         ot->name= "Scale Region Size";
1717         ot->description= "Scale selected area";
1718         ot->idname= "SCREEN_OT_region_scale";
1719         
1720         ot->invoke= region_scale_invoke;
1721         ot->modal= region_scale_modal;
1722         ot->cancel= region_scale_cancel;
1723         
1724         ot->poll= ED_operator_areaactive;
1725         
1726         ot->flag= OPTYPE_BLOCKING;
1727 }
1728
1729
1730 /* ************** frame change operator ***************************** */
1731
1732 /* function to be called outside UI context, or for redo */
1733 static int frame_offset_exec(bContext *C, wmOperator *op)
1734 {
1735         Main *bmain= CTX_data_main(C);
1736         Scene *scene= CTX_data_scene(C);
1737         int delta;
1738         
1739         delta = RNA_int_get(op->ptr, "delta");
1740
1741         scene->r.cfra += delta;
1742         scene->r.subframe = 0.f;
1743         
1744         sound_seek_scene(bmain, scene);
1745
1746         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1747         
1748         return OPERATOR_FINISHED;
1749 }
1750
1751 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
1752 {
1753         ot->name = "Frame Offset";
1754         ot->idname = "SCREEN_OT_frame_offset";
1755         
1756         ot->exec= frame_offset_exec;
1757         
1758         ot->poll= ED_operator_screenactive_norender;
1759         ot->flag= 0;
1760         
1761         /* rna */
1762         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1763 }
1764
1765
1766 /* function to be called outside UI context, or for redo */
1767 static int frame_jump_exec(bContext *C, wmOperator *op)
1768 {
1769         Main *bmain= CTX_data_main(C);
1770         Scene *scene= CTX_data_scene(C);
1771         wmTimer *animtimer= CTX_wm_screen(C)->animtimer;
1772
1773         /* Don't change CFRA directly if animtimer is running as this can cause
1774          * first/last frame not to be actually shown (bad since for example physics
1775          * simulations aren't reset properly).
1776          */
1777         if(animtimer) {
1778                 ScreenAnimData *sad = animtimer->customdata;
1779                 
1780                 sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
1781                 
1782                 if (RNA_boolean_get(op->ptr, "end"))
1783                         sad->nextfra= PEFRA;
1784                 else
1785                         sad->nextfra= PSFRA;
1786         }
1787         else {
1788                 if (RNA_boolean_get(op->ptr, "end"))
1789                         CFRA= PEFRA;
1790                 else
1791                         CFRA= PSFRA;
1792                 
1793                 sound_seek_scene(bmain, scene);
1794
1795                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1796         }
1797         
1798         return OPERATOR_FINISHED;
1799 }
1800
1801 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1802 {
1803         ot->name = "Jump to Endpoint";
1804         ot->description= "Jump to first/last frame in frame range";
1805         ot->idname = "SCREEN_OT_frame_jump";
1806         
1807         ot->exec= frame_jump_exec;
1808         
1809         ot->poll= ED_operator_screenactive_norender;
1810         ot->flag= OPTYPE_UNDO;
1811         
1812         /* rna */
1813         RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range.");
1814 }
1815
1816
1817 /* ************** jump to keyframe operator ***************************** */
1818
1819 /* function to be called outside UI context, or for redo */
1820 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1821 {
1822         Main *bmain= CTX_data_main(C);
1823         Scene *scene= CTX_data_scene(C);
1824         Object *ob= CTX_data_active_object(C);
1825         bDopeSheet ads= {NULL};
1826         DLRBT_Tree keys;
1827         ActKeyColumn *ak;
1828         float cfra;
1829         short next= RNA_boolean_get(op->ptr, "next");
1830         short done = 0;
1831         
1832         /* sanity checks */
1833         if (scene == NULL)
1834                 return OPERATOR_CANCELLED;
1835
1836         cfra= (float)(CFRA);
1837
1838         /* init binarytree-list for getting keyframes */
1839         BLI_dlrbTree_init(&keys);
1840         
1841         /* populate tree with keyframe nodes */
1842         scene_to_keylist(&ads, scene, &keys, NULL);
1843
1844         if (ob)
1845                 ob_to_keylist(&ads, ob, &keys, NULL);
1846         
1847         /* build linked-list for searching */
1848         BLI_dlrbTree_linkedlist_sync(&keys);
1849         
1850         /* find matching keyframe in the right direction */
1851         do {
1852                 if (next)
1853                         ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1854                 else
1855                         ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1856                 
1857                 if (ak) {
1858                         if (CFRA != (int)ak->cfra) {
1859                                 /* this changes the frame, so set the frame and we're done */
1860                                 CFRA= (int)ak->cfra;
1861                                 done = 1;
1862                         }
1863                         else {
1864                                 /* make this the new starting point for the search */
1865                                 cfra = ak->cfra;
1866                         }
1867                 }
1868         } while ((ak != NULL) && (done == 0));
1869         
1870         /* any success? */
1871         if (done == 0)
1872                 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
1873         
1874         /* free temp stuff */
1875         BLI_dlrbTree_free(&keys);
1876         
1877         sound_seek_scene(bmain, scene);
1878
1879         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1880         
1881         return OPERATOR_FINISHED;
1882 }
1883
1884 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1885 {
1886         ot->name = "Jump to Keyframe";
1887         ot->description= "Jump to previous/next keyframe";
1888         ot->idname = "SCREEN_OT_keyframe_jump";
1889         
1890         ot->exec= keyframe_jump_exec;
1891         
1892         ot->poll= ED_operator_screenactive_norender;
1893         ot->flag= OPTYPE_UNDO;
1894         
1895         /* rna */
1896         RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1897 }
1898
1899 /* ************** switch screen operator ***************************** */
1900
1901
1902 /* function to be called outside UI context, or for redo */
1903 static int screen_set_exec(bContext *C, wmOperator *op)
1904 {
1905         bScreen *screen= CTX_wm_screen(C);
1906         bScreen *screen_prev= screen;
1907         
1908         ScrArea *sa= CTX_wm_area(C);
1909         int tot= BLI_countlist(&CTX_data_main(C)->screen);
1910         int delta= RNA_int_get(op->ptr, "delta");
1911         
1912         /* temp screens are for userpref or render display */
1913         if(screen->temp)
1914                 return OPERATOR_CANCELLED;
1915         
1916         if(delta==1) {
1917                 while(tot--) {
1918                         screen= screen->id.next;
1919                         if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1920                         if(screen->winid==0 && screen->full==0 && screen != screen_prev)
1921                                 break;
1922                 }
1923         }
1924         else if(delta== -1) {
1925                 while(tot--) {
1926                         screen= screen->id.prev;
1927                         if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1928                         if(screen->winid==0 && screen->full==0 && screen != screen_prev)
1929                                 break;
1930                 }
1931         }
1932         else {
1933                 screen= NULL;
1934         }
1935         
1936         if(screen && screen_prev != screen) {
1937                 /* return to previous state before switching screens */
1938                 if(sa && sa->full) {
1939                         ED_screen_full_restore(C, sa); /* may free 'screen_prev' */
1940                 }
1941                 
1942                 ED_screen_set(C, screen);
1943                 return OPERATOR_FINISHED;
1944         }
1945         return OPERATOR_CANCELLED;
1946 }
1947
1948 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1949 {
1950         ot->name = "Set Screen";
1951         ot->description= "Cycle through available screens";
1952         ot->idname = "SCREEN_OT_screen_set";
1953         
1954         ot->exec= screen_set_exec;
1955         ot->poll= ED_operator_screenactive;
1956         
1957         /* rna */
1958         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1959 }
1960
1961 /* ************** screen full-area operator ***************************** */
1962
1963
1964 /* function to be called outside UI context, or for redo */
1965 static int screen_full_area_exec(bContext *C, wmOperator *UNUSED(op))
1966 {
1967         bScreen *screen = CTX_wm_screen(C);
1968         ScrArea *sa=NULL;
1969         
1970         /* search current screen for 'fullscreen' areas */
1971         /* prevents restoring info header, when mouse is over it */
1972         for (sa=screen->areabase.first; sa; sa=sa->next) {
1973                 if (sa->full) break;
1974         }
1975         
1976         if(sa==NULL) sa= CTX_wm_area(C);
1977         
1978         ED_screen_full_toggle(C, CTX_wm_window(C), sa);
1979         return OPERATOR_FINISHED;
1980 }
1981
1982 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1983 {
1984         ot->name = "Toggle Full Screen";
1985         ot->description= "Toggle display selected area as fullscreen";
1986         ot->idname = "SCREEN_OT_screen_full_area";
1987         
1988         ot->exec= screen_full_area_exec;
1989         ot->poll= ED_operator_areaactive;
1990         ot->flag= 0;
1991         
1992 }
1993
1994
1995
1996 /* ************** join area operator ********************************************** */
1997
1998 /* operator state vars used:  
1999  x1, y1     mouse coord in first area, which will disappear
2000  x2, y2     mouse coord in 2nd area, which will become joined
2001  
2002  functions:
2003  
2004  init()   find edge based on state vars 
2005  test if the edge divides two areas, 
2006  store active and nonactive area,
2007  
2008  apply()  do the actual join
2009  
2010  exit() cleanup, send notifier
2011  
2012  callbacks:
2013  
2014  exec() calls init, apply, exit 
2015  
2016  invoke() sets mouse coords in x,y
2017  call init()
2018  add modal handler
2019  
2020  modal()        accept modal events while doing it
2021  call apply() with active window and nonactive window
2022  call exit() and remove handler when LMB confirm
2023  
2024  */
2025
2026 typedef struct sAreaJoinData
2027 {
2028         ScrArea *sa1;   /* first area to be considered */
2029         ScrArea *sa2;   /* second area to be considered */
2030         ScrArea *scr;   /* designed for removal */
2031
2032 } sAreaJoinData;
2033
2034
2035 /* validate selection inside screen, set variables OK */
2036 /* return 0: init failed */
2037 /* XXX todo: find edge based on (x,y) and set other area? */
2038 static int area_join_init(bContext *C, wmOperator *op)
2039 {
2040         ScrArea *sa1, *sa2;
2041         sAreaJoinData* jd= NULL;
2042         int x1, y1;
2043         int x2, y2;
2044         int shared= 0;
2045         
2046         /* required properties, make negative to get return 0 if not set by caller */
2047         x1= RNA_int_get(op->ptr, "min_x");
2048         y1= RNA_int_get(op->ptr, "min_y");
2049         x2= RNA_int_get(op->ptr, "max_x");
2050         y2= RNA_int_get(op->ptr, "max_y");
2051         
2052         sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
2053         sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
2054         if(sa1==NULL || sa2==NULL || sa1==sa2)
2055                 return 0;
2056         
2057         /* do areas share an edge? */
2058         if(sa1->v1==sa2->v1 || sa1->v1==sa2->v2 || sa1->v1==sa2->v3 || sa1->v1==sa2->v4) shared++; 
2059         if(sa1->v2==sa2->v1 || sa1->v2==sa2->v2 || sa1->v2==sa2->v3 || sa1->v2==sa2->v4) shared++; 
2060         if(sa1->v3==sa2->v1 || sa1->v3==sa2->v2 || sa1->v3==sa2->v3 || sa1->v3==sa2->v4) shared++; 
2061         if(sa1->v4==sa2->v1 || sa1->v4==sa2->v2 || sa1->v4==sa2->v3 || sa1->v4==sa2->v4) shared++; 
2062         if(shared!=2) {
2063                 printf("areas don't share edge\n");
2064                 return 0;
2065         }
2066         
2067         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
2068         
2069         jd->sa1 = sa1;
2070         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2071         jd->sa2 = sa2;
2072         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2073         
2074         op->customdata= jd;
2075         
2076         return 1;
2077 }
2078
2079 /* apply the join of the areas (space types) */
2080 static int area_join_apply(bContext *C, wmOperator *op)
2081 {
2082         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2083         if (!jd) return 0;
2084         
2085         if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
2086                 return 0;
2087         }
2088         if (CTX_wm_area(C) == jd->sa2) {
2089                 CTX_wm_area_set(C, NULL);
2090                 CTX_wm_region_set(C, NULL);
2091         }
2092         
2093         return 1;
2094 }
2095
2096 /* finish operation */
2097 static void area_join_exit(bContext *C, wmOperator *op)
2098 {
2099         if (op->customdata) {
2100                 MEM_freeN(op->customdata);
2101                 op->customdata = NULL;
2102         }
2103         
2104         /* this makes sure aligned edges will result in aligned grabbing */
2105         removedouble_scredges(CTX_wm_screen(C));
2106         removenotused_scredges(CTX_wm_screen(C));
2107         removenotused_scrverts(CTX_wm_screen(C));
2108 }
2109
2110 static int area_join_exec(bContext *C, wmOperator *op)
2111 {
2112         if(!area_join_init(C, op)) 
2113                 return OPERATOR_CANCELLED;
2114         
2115         area_join_apply(C, op);
2116         area_join_exit(C, op);
2117         
2118         return OPERATOR_FINISHED;
2119 }
2120
2121 /* interaction callback */
2122 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
2123 {
2124         
2125         if(event->type==EVT_ACTIONZONE_AREA) {
2126                 sActionzoneData *sad= event->customdata;
2127                 
2128                 if(sad->modifier>0) {
2129                         return OPERATOR_PASS_THROUGH;
2130                 }
2131                 
2132                 /* verify *sad itself */
2133                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
2134                         return OPERATOR_PASS_THROUGH;
2135                 
2136                 /* is this our *sad? if areas equal it should be passed on */
2137                 if(sad->sa1==sad->sa2)
2138                         return OPERATOR_PASS_THROUGH;
2139                 
2140                 /* prepare operator state vars */
2141                 RNA_int_set(op->ptr, "min_x", sad->x);
2142                 RNA_int_set(op->ptr, "min_y", sad->y);
2143                 RNA_int_set(op->ptr, "max_x", event->x);
2144                 RNA_int_set(op->ptr, "max_y", event->y);
2145         }
2146         
2147         
2148         if(!area_join_init(C, op)) 
2149                 return OPERATOR_PASS_THROUGH;
2150         
2151         /* add temp handler */
2152         WM_event_add_modal_handler(C, op);
2153         
2154         return OPERATOR_RUNNING_MODAL;
2155 }
2156
2157 static int area_join_cancel(bContext *C, wmOperator *op)
2158 {
2159         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2160         
2161         if (jd->sa1) {
2162                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2163                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
2164         }
2165         if (jd->sa2) {
2166                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
2167                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2168         }
2169         
2170         WM_event_add_notifier(C, NC_WINDOW, NULL);
2171         
2172         area_join_exit(C, op);
2173         
2174         return OPERATOR_CANCELLED;
2175 }
2176
2177 /* modal callback while selecting area (space) that will be removed */
2178 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
2179 {
2180         bScreen *sc= CTX_wm_screen(C);
2181         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2182         
2183         /* execute the events */
2184         switch(event->type) {
2185                         
2186                 case MOUSEMOVE: 
2187                 {
2188                         ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
2189                         int dir;
2190                         
2191                         if (sa) {                                       
2192                                 if (jd->sa1 != sa) {
2193                                         dir = area_getorientation(jd->sa1, sa);
2194                                         if (dir >= 0) {
2195                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2196                                                 jd->sa2 = sa;
2197                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2198                                         } 
2199                                         else {
2200                                                 /* we are not bordering on the previously selected area 
2201                                                  we check if area has common border with the one marked for removal
2202                                                  in this case we can swap areas.
2203                                                  */
2204                                                 dir = area_getorientation(sa, jd->sa2);
2205                                                 if (dir >= 0) {
2206                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2207                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2208                                                         jd->sa1 = jd->sa2;
2209                                                         jd->sa2 = sa;
2210                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2211                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2212                                                 } 
2213                                                 else {
2214                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2215                                                         jd->sa2 = NULL;
2216                                                 }
2217                                         }
2218                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
2219                                 } 
2220                                 else {
2221                                         /* we are back in the area previously selected for keeping 
2222                                          * we swap the areas if possible to allow user to choose */
2223                                         if (jd->sa2 != NULL) {
2224                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2225                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2226                                                 jd->sa1 = jd->sa2;
2227                                                 jd->sa2 = sa;
2228                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2229                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2230                                                 dir = area_getorientation(jd->sa1, jd->sa2);
2231                                                 if (dir < 0) {
2232                                                         printf("oops, didn't expect that!\n");
2233                                                 }
2234                                         } 
2235                                         else {
2236                                                 dir = area_getorientation(jd->sa1, sa);
2237                                                 if (dir >= 0) {
2238                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2239                                                         jd->sa2 = sa;
2240                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2241                                                 }
2242                                         }
2243                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
2244                                 }
2245                         }
2246                 }
2247                         break;
2248                 case LEFTMOUSE:
2249                         if(event->val==KM_RELEASE) {
2250                                 ED_area_tag_redraw(jd->sa1);
2251                                 ED_area_tag_redraw(jd->sa2);
2252
2253                                 area_join_apply(C, op);
2254                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2255                                 area_join_exit(C, op);
2256                                 return OPERATOR_FINISHED;
2257                         }
2258                         break;
2259                         
2260                 case RIGHTMOUSE:
2261                 case ESCKEY:
2262                         return area_join_cancel(C, op);
2263         }
2264         
2265         return OPERATOR_RUNNING_MODAL;
2266 }
2267
2268 /* Operator for joining two areas (space types) */
2269 static void SCREEN_OT_area_join(wmOperatorType *ot)
2270 {
2271         /* identifiers */
2272         ot->name= "Join area";
2273         ot->description= "Join selected areas into new window";
2274         ot->idname= "SCREEN_OT_area_join";
2275         
2276         /* api callbacks */
2277         ot->exec= area_join_exec;
2278         ot->invoke= area_join_invoke;
2279         ot->modal= area_join_modal;
2280         ot->poll= screen_active_editable;
2281         ot->cancel= area_join_cancel;
2282         
2283         ot->flag= OPTYPE_BLOCKING|OPTYPE_INTERNAL;
2284         
2285         /* rna */
2286         RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
2287         RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2288         RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2289         RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2290 }
2291
2292 /* ******************************* */
2293
2294 static int screen_area_options_invoke(bContext *C, wmOperator *op, wmEvent *event)
2295 {
2296         uiPopupMenu *pup;
2297         uiLayout *layout;
2298         PointerRNA ptr1, ptr2;
2299         ScrEdge *actedge= screen_find_active_scredge(CTX_wm_screen(C), event->x, event->y);
2300         
2301         if(actedge==NULL) return OPERATOR_CANCELLED;
2302         
2303         pup= uiPupMenuBegin(C, op->type->name, ICON_NONE);
2304         layout= uiPupMenuLayout(pup);
2305         
2306         WM_operator_properties_create(&ptr1, "SCREEN_OT_area_join");
2307         
2308         /* mouse cursor on edge, '4' can fail on wide edges... */
2309         RNA_int_set(&ptr1, "min_x", event->x+4);
2310         RNA_int_set(&ptr1, "min_y", event->y+4);
2311         RNA_int_set(&ptr1, "max_x", event->x-4);
2312         RNA_int_set(&ptr1, "max_y", event->y-4);
2313         
2314         WM_operator_properties_create(&ptr2, "SCREEN_OT_area_split");
2315         
2316         /* store initial mouse cursor position */
2317         RNA_int_set(&ptr2, "mouse_x", event->x);
2318         RNA_int_set(&ptr2, "mouse_y", event->y);
2319         
2320         uiItemFullO(layout, "SCREEN_OT_area_split", "Split Area", ICON_NONE, ptr2.data, WM_OP_INVOKE_DEFAULT, 0);
2321         uiItemFullO(layout, "SCREEN_OT_area_join", "Join Area", ICON_NONE, ptr1.data, WM_OP_INVOKE_DEFAULT, 0);
2322         
2323         uiPupMenuEnd(C, pup);
2324         
2325         return OPERATOR_CANCELLED;
2326 }
2327
2328 static void SCREEN_OT_area_options(wmOperatorType *ot)
2329 {
2330         /* identifiers */
2331         ot->name= "Area Options";
2332         ot->description= "Operations for splitting and merging";
2333         ot->idname= "SCREEN_OT_area_options";
2334         
2335         /* api callbacks */
2336         ot->invoke= screen_area_options_invoke;
2337         
2338         ot->poll= ED_operator_screen_mainwinactive;
2339 }
2340
2341
2342 /* ******************************* */
2343
2344
2345 static int spacedata_cleanup(bContext *C, wmOperator *op)
2346 {
2347         Main *bmain= CTX_data_main(C);
2348         bScreen *screen;
2349         ScrArea *sa;
2350         int tot= 0;
2351         
2352         for(screen= bmain->screen.first; screen; screen= screen->id.next) {
2353                 for(sa= screen->areabase.first; sa; sa= sa->next) {
2354                         if(sa->spacedata.first != sa->spacedata.last) {
2355                                 SpaceLink *sl= sa->spacedata.first;
2356
2357                                 BLI_remlink(&sa->spacedata, sl);
2358                                 tot+= BLI_countlist(&sa->spacedata);
2359                                 BKE_spacedata_freelist(&sa->spacedata);
2360                                 BLI_addtail(&sa->spacedata, sl);
2361                         }
2362                 }
2363         }
2364         BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
2365         
2366         return OPERATOR_FINISHED;
2367 }
2368
2369 static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
2370 {
2371         /* identifiers */
2372         ot->name= "Clean-up space-data";
2373         ot->description= "Remove unused settings for invisible editors";
2374         ot->idname= "SCREEN_OT_spacedata_cleanup";
2375         
2376         /* api callbacks */
2377         ot->exec= spacedata_cleanup;
2378         ot->poll= WM_operator_winactive;
2379         
2380 }
2381
2382 /* ************** repeat last operator ***************************** */
2383
2384 static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
2385 {
2386         wmOperator *lastop= CTX_wm_manager(C)->operators.last;
2387         
2388         if(lastop)
2389                 WM_operator_repeat(C, lastop);
2390         
2391         return OPERATOR_CANCELLED;
2392 }
2393
2394 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2395 {
2396         /* identifiers */
2397         ot->name= "Repeat Last";
2398         ot->description= "Repeat last action";
2399         ot->idname= "SCREEN_OT_repeat_last";
2400         
2401         /* api callbacks */
2402         ot->exec= repeat_last_exec;
2403         
2404         ot->poll= ED_operator_screenactive;
2405         
2406 }
2407
2408 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2409 {
2410         wmWindowManager *wm= CTX_wm_manager(C);
2411         wmOperator *lastop;
2412         uiPopupMenu *pup;
2413         uiLayout *layout;
2414         int items, i;
2415         
2416         items= BLI_countlist(&wm->operators);
2417         if(items==0)
2418                 return OPERATOR_CANCELLED;
2419         
2420         pup= uiPupMenuBegin(C, op->type->name, ICON_NONE);
2421         layout= uiPupMenuLayout(pup);
2422         
2423         for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
2424                 uiItemIntO(layout, lastop->type->name, ICON_NONE, op->type->idname, "index", i);
2425         
2426         uiPupMenuEnd(C, pup);
2427         
2428         return OPERATOR_CANCELLED;
2429 }
2430
2431 static int repeat_history_exec(bContext *C, wmOperator *op)
2432 {
2433         wmWindowManager *wm= CTX_wm_manager(C);
2434         
2435         op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2436         if(op) {
2437                 /* let's put it as last operator in list */
2438                 BLI_remlink(&wm->operators, op);
2439                 BLI_addtail(&wm->operators, op);
2440                 
2441                 WM_operator_repeat(C, op);
2442         }
2443         
2444         return OPERATOR_FINISHED;
2445 }
2446
2447 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2448 {
2449         /* identifiers */
2450         ot->name= "Repeat History";
2451         ot->description= "Display menu for previous actions performed";
2452         ot->idname= "SCREEN_OT_repeat_history";
2453         
2454         /* api callbacks */
2455         ot->invoke= repeat_history_invoke;
2456         ot->exec= repeat_history_exec;
2457         
2458         ot->poll= ED_operator_screenactive;
2459         
2460         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2461 }
2462
2463 /* ********************** redo operator ***************************** */
2464
2465 static int redo_last_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
2466 {
2467         wmOperator *lastop= WM_operator_last_redo(C);
2468         
2469         if(lastop)
2470                 WM_operator_redo_popup(C, lastop);
2471         
2472         return OPERATOR_CANCELLED;
2473 }
2474
2475 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2476 {
2477         /* identifiers */
2478         ot->name= "Redo Last";
2479         ot->description= "Display menu for last action performed";
2480         ot->idname= "SCREEN_OT_redo_last";
2481         
2482         /* api callbacks */
2483         ot->invoke= redo_last_invoke;
2484         
2485         ot->poll= ED_operator_screenactive;
2486 }
2487
2488 /* ************** region four-split operator ***************************** */
2489
2490 /* insert a region in the area region list */
2491 static int region_quadview_exec(bContext *C, wmOperator *op)
2492 {
2493         ARegion *ar= CTX_wm_region(C);
2494         
2495         /* some rules... */
2496         if(ar->regiontype!=RGN_TYPE_WINDOW)
2497                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2498         else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2499                 ScrArea *sa= CTX_wm_area(C);
2500                 ARegion *arn;
2501                 
2502                 /* keep current region */
2503                 ar->alignment= 0;
2504                 
2505                 if(sa->spacetype==SPACE_VIEW3D) {
2506                         RegionView3D *rv3d= ar->regiondata;
2507                         rv3d->viewlock= 0;
2508                         rv3d->rflag &= ~RV3D_CLIPPING;
2509                 }
2510                 
2511                 for(ar= sa->regionbase.first; ar; ar= arn) {
2512                         arn= ar->next;
2513                         if(ar->alignment==RGN_ALIGN_QSPLIT) {
2514                                 ED_region_exit(C, ar);
2515                                 BKE_area_region_free(sa->type, ar);
2516                                 BLI_remlink(&sa->regionbase, ar);
2517                                 MEM_freeN(ar);
2518                         }
2519                 }
2520                 ED_area_tag_redraw(sa);
2521                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2522         }
2523         else if(ar->next)
2524                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2525         else {
2526                 ScrArea *sa= CTX_wm_area(C);
2527                 ARegion *newar;
2528                 int count;
2529                 
2530                 ar->alignment= RGN_ALIGN_QSPLIT;
2531                 
2532                 for(count=0; count<3; count++) {
2533                         newar= BKE_area_region_copy(sa->type, ar);
2534                         BLI_addtail(&sa->regionbase, newar);
2535                 }
2536                 
2537                 /* lock views and set them */
2538                 if(sa->spacetype==SPACE_VIEW3D) {
2539                         /* run ED_view3d_lock() so the correct 'rv3d->viewquat' is set,
2540                          * otherwise when restoring rv3d->localvd the 'viewquat' won't
2541                          * match the 'view', set on entering localview See: [#26315],
2542                          *
2543                          * We could avoid manipulating rv3d->localvd here if exiting
2544                          * localview with a 4-split would assign these view locks */
2545                         RegionView3D *rv3d;
2546                         
2547                         rv3d= ar->regiondata;
2548                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2549                         ED_view3d_lock(rv3d);
2550                         if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2551                         
2552                         ar= ar->next;
2553                         rv3d= ar->regiondata;
2554                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2555                         ED_view3d_lock(rv3d);
2556                         if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2557                         
2558                         ar= ar->next;
2559                         rv3d= ar->regiondata;
2560                         rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2561                         ED_view3d_lock(rv3d);
2562                         if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2563                         
2564                         ar= ar->next;
2565                         rv3d= ar->regiondata;
2566                         rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2567                         ED_view3d_lock(rv3d);
2568                         if (rv3d->localvd) {rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2569                 }
2570                 ED_area_tag_redraw(sa);
2571                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2572         }
2573         
2574         
2575         return OPERATOR_FINISHED;
2576 }
2577
2578 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
2579 {
2580         /* identifiers */
2581         ot->name= "Toggle Quad View";
2582         ot->description= "Split selected area into camera, front, right & top views";
2583         ot->idname= "SCREEN_OT_region_quadview";
2584         
2585         /* api callbacks */
2586         //      ot->invoke= WM_operator_confirm;
2587         ot->exec= region_quadview_exec;
2588         ot->poll= ED_operator_region_view3d_active;
2589         ot->flag= 0;
2590 }
2591
2592
2593
2594 /* ************** region flip operator ***************************** */
2595
2596 /* flip a region alignment */
2597 static int region_flip_exec(bContext *C, wmOperator *UNUSED(op))
2598 {
2599         ARegion *ar= CTX_wm_region(C);
2600         
2601         if (!ar)
2602                 return OPERATOR_CANCELLED;
2603         
2604         if(ar->alignment==RGN_ALIGN_TOP)
2605                 ar->alignment= RGN_ALIGN_BOTTOM;
2606         else if(ar->alignment==RGN_ALIGN_BOTTOM)
2607                 ar->alignment= RGN_ALIGN_TOP;
2608         else if(ar->alignment==RGN_ALIGN_LEFT)
2609                 ar->alignment= RGN_ALIGN_RIGHT;
2610         else if(ar->alignment==RGN_ALIGN_RIGHT)
2611                 ar->alignment= RGN_ALIGN_LEFT;
2612
2613         ED_area_tag_redraw(CTX_wm_area(C));
2614         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2615         
2616         return OPERATOR_FINISHED;
2617 }
2618
2619
2620 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2621 {
2622         /* identifiers */
2623         ot->name= "Flip Region";
2624         ot->idname= "SCREEN_OT_region_flip";
2625         
2626         /* api callbacks */
2627         ot->exec= region_flip_exec;
2628         ot->poll= ED_operator_areaactive;
2629         ot->flag= 0;
2630 }
2631
2632 /* ************** header flip operator ***************************** */
2633
2634 /* flip a header region alignment */
2635 static int header_flip_exec(bContext *C, wmOperator *UNUSED(op))
2636 {
2637         ARegion *ar= CTX_wm_region(C);
2638         
2639         /* find the header region 
2640          *      - try context first, but upon failing, search all regions in area...
2641          */
2642         if((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) {
2643                 ScrArea *sa= CTX_wm_area(C);
2644                 ar= BKE_area_find_region_type(sa, RGN_TYPE_HEADER);
2645
2646                 /* don't do anything if no region */
2647                 if(ar == NULL)
2648                         return OPERATOR_CANCELLED;
2649         }       
2650         
2651         /* copied from SCREEN_OT_region_flip */
2652         if(ar->alignment==RGN_ALIGN_TOP)
2653                 ar->alignment= RGN_ALIGN_BOTTOM;
2654         else if(ar->alignment==RGN_ALIGN_BOTTOM)
2655                 ar->alignment= RGN_ALIGN_TOP;
2656         else if(ar->alignment==RGN_ALIGN_LEFT)
2657                 ar->alignment= RGN_ALIGN_RIGHT;
2658         else if(ar->alignment==RGN_ALIGN_RIGHT)
2659                 ar->alignment= RGN_ALIGN_LEFT;
2660
2661         ED_area_tag_redraw(CTX_wm_area(C));
2662
2663         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2664         
2665         return OPERATOR_FINISHED;
2666 }
2667
2668
2669 static void SCREEN_OT_header_flip(wmOperatorType *ot)
2670 {
2671         /* identifiers */
2672         ot->name= "Flip Header Region";
2673         ot->idname= "SCREEN_OT_header_flip";
2674         
2675         /* api callbacks */
2676         ot->exec= header_flip_exec;
2677         
2678         ot->poll= ED_operator_areaactive;
2679         ot->flag= 0;
2680 }
2681
2682 /* ************** header tools operator ***************************** */
2683
2684 static int header_toolbox_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
2685 {
2686         ScrArea *sa= CTX_wm_area(C);
2687         ARegion *ar= CTX_wm_region(C);
2688         uiPopupMenu *pup;
2689         uiLayout *layout;
2690         
2691         pup= uiPupMenuBegin(C, "Header", ICON_NONE);
2692         layout= uiPupMenuLayout(pup);
2693         
2694         // XXX SCREEN_OT_region_flip doesn't work - gets wrong context for active region, so added custom operator
2695         if (ar->alignment == RGN_ALIGN_TOP)
2696                 uiItemO(layout, "Flip to Bottom", ICON_NONE, "SCREEN_OT_header_flip");
2697         else
2698                 uiItemO(layout, "Flip to Top", ICON_NONE, "SCREEN_OT_header_flip");
2699         
2700         uiItemS(layout);
2701         
2702         /* file browser should be fullscreen all the time, but other regions can be maximised/restored... */
2703         if (sa->spacetype != SPACE_FILE) {
2704                 if (sa->full) 
2705                         uiItemO(layout, "Tile Area", ICON_NONE, "SCREEN_OT_screen_full_area");
2706                 else
2707                         uiItemO(layout, "Maximize Area", ICON_NONE, "SCREEN_OT_screen_full_area");
2708         }
2709         
2710         uiPupMenuEnd(C, pup);
2711         
2712         return OPERATOR_CANCELLED;
2713 }
2714
2715 static void SCREEN_OT_header_toolbox(wmOperatorType *ot)
2716 {
2717         /* identifiers */
2718         ot->name= "Header Toolbox";
2719         ot->description="Display header region toolbox";
2720         ot->idname= "SCREEN_OT_header_toolbox";
2721         
2722         /* api callbacks */
2723         ot->invoke= header_toolbox_invoke;
2724 }
2725
2726 /* ****************** anim player, with timer ***************** */
2727
2728 static int match_area_with_refresh(int spacetype, int refresh)
2729 {
2730         switch (spacetype) {
2731                 case SPACE_TIME:
2732                         if (refresh & SPACE_TIME)
2733                                 return 1;
2734                         break;
2735         }
2736         
2737         return 0;
2738 }
2739
2740 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2741 {
2742         if(regiontype==RGN_TYPE_WINDOW) {
2743                 
2744                 switch (spacetype) {
2745                         case SPACE_VIEW3D:
2746                                 if(redraws & TIME_ALL_3D_WIN)
2747                                         return 1;
2748                                 break;
2749                         case SPACE_IPO:
2750                         case SPACE_ACTION:
2751                         case SPACE_NLA:
2752                                 if(redraws & TIME_ALL_ANIM_WIN)
2753                                         return 1;
2754                                 break;
2755                         case SPACE_TIME:
2756                                 /* if only 1 window or 3d windows, we do timeline too */
2757                                 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2758                                         return 1;
2759                                 break;
2760                         case SPACE_BUTS:
2761                                 if(redraws & TIME_ALL_BUTS_WIN)
2762                                         return 1;
2763                                 break;
2764                         case SPACE_SEQ:
2765                                 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2766                                         return 1;
2767                                 break;
2768                         case SPACE_NODE:
2769                                 if(redraws & (TIME_NODES))
2770                                         return 1;
2771                                 break;
2772                         case SPACE_IMAGE:
2773                                 if(redraws & TIME_ALL_IMAGE_WIN)
2774                                         return 1;
2775                                 break;
2776                                 
2777                 }
2778         }
2779         else if(regiontype==RGN_TYPE_UI) {
2780                 if(redraws & TIME_ALL_BUTS_WIN)
2781                         return 1;
2782         }
2783         else if(regiontype==RGN_TYPE_HEADER) {
2784                 if(spacetype==SPACE_TIME)
2785                         return 1;
2786         }
2787         else if (regiontype==RGN_TYPE_PREVIEW) {
2788                 switch (spacetype) {
2789                         case SPACE_SEQ:
2790                                 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2791                                         return 1;
2792                                 break;
2793                 }
2794         }
2795         return 0;
2796 }
2797
2798 static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
2799 {
2800         bScreen *screen= CTX_wm_screen(C);
2801
2802         if(screen->animtimer && screen->animtimer==event->customdata) {
2803                 Main *bmain= CTX_data_main(C);
2804                 Scene *scene= CTX_data_scene(C);
2805                 wmTimer *wt= screen->animtimer;
2806                 ScreenAnimData *sad= wt->customdata;
2807                 ScrArea *sa;
2808                 int sync;
2809                 float time;
2810                 
2811                 /* sync, don't sync, or follow scene setting */
2812                 if (sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2813                 else if (sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2814                 else sync= (scene->flag & SCE_FRAME_DROP);
2815                 
2816                 if((scene->audio.flag & AUDIO_SYNC) && !(sad->flag & ANIMPLAY_FLAG_REVERSE) && finite(time = sound_sync_scene(scene)))
2817                         scene->r.cfra = (double)time * FPS + 0.5;
2818                 else
2819                 {
2820                         if (sync) {
2821                                 int step = floor(wt->duration * FPS);
2822                                 /* skip frames */
2823                                 if (sad->flag & ANIMPLAY_FLAG_REVERSE)
2824                                         scene->r.cfra -= step;
2825                                 else
2826                                         scene->r.cfra += step;
2827                                 wt->duration -= ((double)step)/FPS;
2828                         }
2829                         else {
2830                                 /* one frame +/- */
2831                                 if (sad->flag & ANIMPLAY_FLAG_REVERSE)
2832                                         scene->r.cfra--;
2833                                 else
2834                                         scene->r.cfra++;
2835                         }
2836                 }
2837                 
2838                 /* reset 'jumped' flag before checking if we need to jump... */
2839                 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2840                 
2841                 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2842                         /* jump back to end? */
2843                         if (PRVRANGEON) {
2844                                 if (scene->r.cfra < scene->r.psfra) {
2845                                         scene->r.cfra= scene->r.pefra;
2846                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2847                                 }
2848                         }
2849                         else {
2850                                 if (scene->r.cfra < scene->r.sfra) {
2851                                         scene->r.cfra= scene->r.efra;
2852                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2853                                 }
2854                         }
2855                 }
2856                 else {
2857                         /* jump back to start? */
2858                         if (PRVRANGEON) {
2859                                 if (scene->r.cfra > scene->r.pefra) {
2860                                         scene->r.cfra= scene->r.psfra;
2861                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2862                                 }
2863                         }
2864                         else {
2865                                 if (scene->r.cfra > scene->r.efra) {
2866                                         scene->r.cfra= scene->r.sfra;
2867                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2868                                 }
2869                         }
2870                 }
2871
2872                 /* next frame overriden by user action (pressed jump to first/last frame) */
2873                 if(sad->flag & ANIMPLAY_FLAG_USE_NEXT_FRAME) {
2874                         scene->r.cfra = sad->nextfra;
2875                         sad->flag &= ~ANIMPLAY_FLAG_USE_NEXT_FRAME;
2876                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2877                 }
2878                 
2879                 if (sad->flag & ANIMPLAY_FLAG_JUMPED)
2880                         sound_seek_scene(bmain, scene);
2881                 
2882                 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2883                 ED_update_for_newframe(CTX_data_main(C), scene, screen, 1);
2884                 
2885                 for (sa= screen->areabase.first; sa; sa= sa->next) {
2886                         ARegion *ar;
2887                         for (ar= sa->regionbase.first; ar; ar= ar->next) {
2888                                 if (ar==sad->ar)
2889                                         ED_region_tag_redraw(ar);
2890                                 else
2891                                         if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2892                                                 ED_region_tag_redraw(ar);
2893                         }
2894                         
2895                         if (match_area_with_refresh(sa->spacetype, sad->refresh))
2896                                 ED_area_tag_refresh(sa);
2897                 }
2898                 
2899                 /* update frame rate info too 
2900                  * NOTE: this may not be accurate enough, since we might need this after modifiers/etc. 
2901                  * have been calculated instead of just before updates have been done?
2902                  */
2903                 ED_refresh_viewport_fps(C);
2904                 
2905                 /* recalculate the timestep for the timer now that we've finished calculating this,
2906                  * since the frames-per-second value may have been changed
2907                  */
2908                 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2909                 wt->timestep= (1.0/FPS);
2910                 
2911                 return OPERATOR_FINISHED;
2912         }
2913         return OPERATOR_PASS_THROUGH;
2914 }
2915
2916 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2917 {
2918         /* identifiers */
2919         ot->name= "Animation Step";
2920         ot->description= "Step through animation by position";
2921         ot->idname= "SCREEN_OT_animation_step";
2922         
2923         /* api callbacks */
2924         ot->invoke= screen_animation_step;
2925         
2926         ot->poll= ED_operator_screenactive_norender;
2927         
2928 }
2929
2930 /* ****************** anim player, starts or ends timer ***************** */
2931
2932 /* toggle operator */
2933 int ED_screen_animation_play(bContext *C, int sync, int mode)
2934 {
2935         bScreen *screen= CTX_wm_screen(C);
2936         Scene *scene = CTX_data_scene(C);
2937
2938         if (screen->animtimer) {
2939                 /* stop playback now */
2940                 ED_screen_animation_timer(C, 0, 0, 0, 0);
2941                 sound_stop_scene(scene);
2942         }
2943         else {
2944                 int refresh= SPACE_TIME; /* these settings are currently only available from a menu in the TimeLine */
2945                 
2946                 if (mode == 1) // XXX only play audio forwards!?
2947                         sound_play_scene(scene);
2948                 
2949                 ED_screen_animation_timer(C, screen->redraws_flag, refresh, sync, mode);
2950                 
2951                 if (screen->animtimer) {
2952                         wmTimer *wt= screen->animtimer;
2953                         ScreenAnimData *sad= wt->customdata;
2954                         
2955                         sad->ar= CTX_wm_region(C);
2956                 }
2957         }
2958
2959         return OPERATOR_FINISHED;
2960 }
2961
2962 static int screen_animation_play_exec(bContext *C, wmOperator *op)
2963 {
2964         int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2965         int sync= -1;
2966         
2967         if (RNA_property_is_set(op->ptr, "sync"))
2968                 sync= (RNA_boolean_get(op->ptr, "sync"));
2969         
2970         return ED_screen_animation_play(C, sync, mode);
2971 }
2972
2973 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2974 {
2975         /* identifiers */
2976         ot->name= "Play Animation";
2977         ot->description= "Play animation";
2978         ot->idname= "SCREEN_OT_animation_play";
2979         
2980         /* api callbacks */
2981         ot->exec= screen_animation_play_exec;
2982         
2983         ot->poll= ED_operator_screenactive_norender;
2984         
2985         RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2986         RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate");
2987 }
2988
2989 static int screen_animation_cancel_exec(bContext *C, wmOperator *op)
2990 {
2991         bScreen *screen= CTX_wm_screen(C);
2992
2993         if (screen->animtimer) {
2994                 if(RNA_boolean_get(op->ptr, "restore_frame")) {
2995                         ScreenAnimData *sad= screen->animtimer->customdata;
2996                         Scene *scene= CTX_data_scene(C);
2997
2998                         /* reset current frame before stopping, and just send a notifier to deal with the rest
2999                          * (since playback still needs to be stopped)
3000                          */
3001                         scene->r.cfra= sad->sfra;
3002
3003                         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
3004                 }
3005
3006                 /* call the other "toggling" operator to clean up now */
3007                 ED_screen_animation_play(C, 0, 0);
3008         }
3009
3010         return OPERATOR_PASS_THROUGH;
3011 }
3012
3013 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
3014 {
3015         /* identifiers */
3016         ot->name= "Cancel Animation";
3017         ot->description= "Cancel animation, returning to the original frame";
3018         ot->idname= "SCREEN_OT_animation_cancel";
3019         
3020         /* api callbacks */
3021         ot->exec= screen_animation_cancel_exec;
3022         
3023         ot->poll= ED_operator_screenactive;
3024
3025         RNA_def_boolean(ot->srna, "restore_frame", TRUE, "Restore Frame", "Restore the frame when animation was initialized.");
3026 }
3027
3028 /* ************** border select operator (template) ***************************** */
3029
3030 /* operator state vars used: (added by default WM callbacks)   
3031  xmin, ymin     
3032  xmax, ymax     
3033  
3034  customdata: the wmGesture pointer
3035  
3036  callbacks:
3037  
3038  exec() has to be filled in by user
3039  
3040  invoke() default WM function
3041  adds modal handler
3042  
3043  modal()        default WM function 
3044  accept modal events while doing it, calls exec(), handles ESC and border drawing
3045  
3046  poll() has to be filled in by user for context
3047  */
3048 #if 0
3049 static int border_select_do(bContext *C, wmOperator *op)
3050 {
3051         int event_type= RNA_int_get(op->ptr, "event_type");
3052         
3053         if(event_type==LEFTMOUSE)
3054                 printf("border select do select\n");
3055         else if(event_type==RIGHTMOUSE)
3056                 printf("border select deselect\n");
3057         else 
3058                 printf("border select do something\n");
3059         
3060         return 1;
3061 }
3062
3063 static void SCREEN_OT_border_select(wmOperatorType *ot)
3064 {
3065         /* identifiers */
3066         ot->name= "Border select";
3067         ot->idname= "SCREEN_OT_border_select";
3068         
3069         /* api callbacks */
3070         ot->exec= border_select_do;
3071         ot->invoke= WM_border_select_invoke;
3072         ot->modal= WM_border_select_modal;
3073         ot->cancel= WM_border_select_cancel;
3074         
3075         ot->poll= ED_operator_areaactive;
3076         
3077         /* rna */
3078         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
3079         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
3080         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
3081         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
3082         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
3083         
3084 }
3085 #endif
3086
3087 /* *********************** generic fullscreen 'back' button *************** */
3088
3089
3090 static int fullscreen_back_exec(bContext *C, wmOperator *op)
3091 {
3092         bScreen *screen = CTX_wm_screen(C);
3093         ScrArea *sa=NULL;
3094         
3095         /* search current screen for 'fullscreen' areas */
3096         for (sa=screen->areabase.first; sa; sa=sa->next) {
3097                 if (sa->full) break;
3098         }
3099         if (!sa) {
3100                 BKE_report(op->reports, RPT_ERROR, "No fullscreen areas were found.");
3101                 return OPERATOR_CANCELLED;
3102         }
3103         
3104         ED_screen_full_restore(C, sa);
3105         
3106         return OPERATOR_FINISHED;
3107 }
3108
3109 static void SCREEN_OT_back_to_previous(struct wmOperatorType *ot)
3110 {
3111         /* identifiers */
3112         ot->name= "Back to Previous Screen";
3113         ot->description= "Revert back to the original screen layout, before fullscreen area overlay";
3114         ot->idname= "SCREEN_OT_back_to_previous";
3115         
3116         /* api callbacks */
3117         ot->exec= fullscreen_back_exec;
3118         ot->poll= ED_operator_screenactive;
3119 }
3120
3121 /* *********** show user pref window ****** */
3122
3123 static int userpref_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
3124 {
3125         rcti rect;
3126         int sizex, sizey;
3127         
3128         sizex= 800;
3129         sizey= 480;
3130         
3131         /* some magic to calculate postition */
3132         rect.xmin= event->x + CTX_wm_window(C)->posx - sizex/2;
3133         rect.ymin= event->y + CTX_wm_window(C)->posy - sizey/2;
3134         rect.xmax= rect.xmin + sizex;
3135         rect.ymax= rect.ymin + sizey;
3136         
3137         /* changes context! */
3138         WM_window_open_temp(C, &rect, WM_WINDOW_USERPREFS);
3139         
3140         return OPERATOR_FINISHED;
3141 }
3142
3143
3144 static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
3145 {
3146         /* identifiers */
3147         ot->name= "Show/Hide User Preferences";
3148         ot->description= "Show/hide user preferences";
3149         ot->idname= "SCREEN_OT_userpref_show";
3150         
3151         /* api callbacks */
3152         ot->invoke= userpref_show_invoke;
3153         ot->poll= ED_operator_screenactive;
3154 }
3155
3156 /********************* new screen operator *********************/
3157
3158 static int screen_new_exec(bContext *C, wmOperator *UNUSED(op))
3159 {
3160         wmWindow *win= CTX_wm_window(C);
3161         bScreen *sc= CTX_wm_screen(C);
3162         
3163         sc= ED_screen_duplicate(win, sc);
3164         WM_event_add_notifier(C, NC_SCREEN|ND_SCREENBROWSE, sc);
3165         
3166         return OPERATOR_FINISHED;
3167 }
3168
3169 static void SCREEN_OT_new(wmOperatorType *ot)
3170 {
3171         /* identifiers */
3172         ot->name= "New Screen";
3173         ot->description= "Add a new screen";
3174         ot->idname= "SCREEN_OT_new";
3175         
3176         /* api callbacks */
3177         ot->exec= screen_new_exec;
3178         ot->poll= WM_operator_winactive;
3179         
3180         /* flags */
3181         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
3182 }
3183
3184 /********************* delete screen operator *********************/
3185
3186 static int screen_delete_exec(bContext *C, wmOperator *UNUSED(op))
3187 {
3188         bScreen *sc= CTX_wm_screen(C);
3189         
3190         WM_event_add_notifier(C, NC_SCREEN|ND_SCREENDELETE, sc);
3191         
3192         return OPERATOR_FINISHED;
3193 }
3194
3195 static void SCREEN_OT_delete(wmOperatorType *ot)
3196 {
3197         /* identifiers */
3198         ot->name= "Delete Screen"; //was scene
3199         ot->description= "Delete active screen";
3200         ot->idname= "SCREEN_OT_delete";
3201         
3202         /* api callbacks */
3203         ot->exec= screen_delete_exec;
3204         
3205         /* flags */
3206         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
3207 }
3208
3209 /********************* new scene operator *********************/
3210
3211 static int scene_new_exec(bContext *C, wmOperator *op)
3212 {
3213         Scene *newscene, *scene= CTX_data_scene(C);