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