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