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