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