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