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