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