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