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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool ED_operator_view3d_active(bContext *C)
194 {
195         return ed_spacetype_test(C, SPACE_VIEW3D);
196 }
197
198 bool 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 bool 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 bool ED_operator_outliner_active(bContext *C)
221 {
222         return ed_spacetype_test(C, SPACE_OUTLINER);
223 }
224
225 bool 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 bool ED_operator_file_active(bContext *C)
239 {
240         return ed_spacetype_test(C, SPACE_FILE);
241 }
242
243 bool ED_operator_action_active(bContext *C)
244 {
245         return ed_spacetype_test(C, SPACE_ACTION);
246 }
247
248 bool ED_operator_buttons_active(bContext *C)
249 {
250         return ed_spacetype_test(C, SPACE_BUTS);
251 }
252
253 bool 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 bool 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 bool ED_operator_graphedit_active(bContext *C)
274 {
275         return ed_spacetype_test(C, SPACE_IPO);
276 }
277
278 bool ED_operator_sequencer_active(bContext *C)
279 {
280         return ed_spacetype_test(C, SPACE_SEQ);
281 }
282
283 bool ED_operator_sequencer_active_editable(bContext *C)
284 {
285         return ed_spacetype_test(C, SPACE_SEQ) && ED_operator_scene_editable(C);
286 }
287
288 bool ED_operator_image_active(bContext *C)
289 {
290         return ed_spacetype_test(C, SPACE_IMAGE);
291 }
292
293 bool ED_operator_nla_active(bContext *C)
294 {
295         return ed_spacetype_test(C, SPACE_NLA);
296 }
297
298 bool ED_operator_info_active(bContext *C)
299 {
300         return ed_spacetype_test(C, SPACE_INFO);
301 }
302
303
304 bool ED_operator_console_active(bContext *C)
305 {
306         return ed_spacetype_test(C, SPACE_CONSOLE);
307 }
308
309 static bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool ED_operator_editmesh_view3d(bContext *C)
350 {
351         return ED_operator_editmesh(C) && ED_operator_view3d_active(C);
352 }
353
354 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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 static AZone *area_actionzone_refresh_xy(ScrArea *sa, const int xy[2], const bool test_only)
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_sq = SQUARE(xy[0] - az->x1) + SQUARE(xy[1] - az->y1);
691                                 if (radius_sq <= SQUARE(AZONESPOT)) {
692                                         break;
693                                 }
694                         }
695                         else if (az->type == AZONE_REGION) {
696                                 break;
697                         }
698                         else if (az->type == AZONE_FULLSCREEN) {
699                                 rcti click_rect;
700                                 fullscreen_click_rcti_init(&click_rect, az->x1, az->y1, az->x2, az->y2);
701                                 const bool click_isect = BLI_rcti_isect_pt_v(&click_rect, xy);
702
703                                 if (test_only) {
704                                         if (click_isect) {
705                                                 break;
706                                         }
707                                 }
708                                 else {
709                                         if (click_isect) {
710                                                 az->alpha = 1.0f;
711                                         }
712                                         else {
713                                                 const int mouse_sq = SQUARE(xy[0] - az->x2) + SQUARE(xy[1] - az->y2);
714                                                 const int spot_sq = SQUARE(AZONESPOT);
715                                                 const int fadein_sq = SQUARE(AZONEFADEIN);
716                                                 const int fadeout_sq = SQUARE(AZONEFADEOUT);
717
718                                                 if (mouse_sq < spot_sq) {
719                                                         az->alpha = 1.0f;
720                                                 }
721                                                 else if (mouse_sq < fadein_sq) {
722                                                         az->alpha = 1.0f;
723                                                 }
724                                                 else if (mouse_sq < fadeout_sq) {
725                                                         az->alpha = 1.0f - ((float)(mouse_sq - fadein_sq)) / ((float)(fadeout_sq - fadein_sq));
726                                                 }
727                                                 else {
728                                                         az->alpha = 0.0f;
729                                                 }
730
731                                                 /* fade in/out but no click */
732                                                 az = NULL;
733                                         }
734
735                                         /* XXX force redraw to show/hide the action zone */
736                                         ED_area_tag_redraw(sa);
737                                         break;
738                                 }
739                         }
740                         else if (az->type == AZONE_REGION_SCROLL) {
741                                 ARegion *ar = az->ar;
742                                 View2D *v2d = &ar->v2d;
743                                 const short isect_value = UI_view2d_mouse_in_scrollers(ar, v2d, xy[0], xy[1]);
744                                 if (test_only) {
745                                         if (isect_value != 0) {
746                                                 break;
747                                         }
748                                 }
749                                 else {
750                                         bool redraw = false;
751
752                                         if (isect_value == 'h') {
753                                                 if (az->direction == AZ_SCROLL_HOR) {
754                                                         az->alpha = 1.0f;
755                                                         v2d->alpha_hor = 255;
756                                                         redraw = true;
757                                                 }
758                                         }
759                                         else if (isect_value == 'v') {
760                                                 if (az->direction == AZ_SCROLL_VERT) {
761                                                         az->alpha = 1.0f;
762                                                         v2d->alpha_vert = 255;
763                                                         redraw = true;
764                                                 }
765                                         }
766                                         else {
767                                                 const int local_xy[2] = {xy[0] - ar->winrct.xmin, xy[1] - ar->winrct.ymin};
768                                                 float dist_fac = 0.0f, alpha = 0.0f;
769
770                                                 if (az->direction == AZ_SCROLL_HOR) {
771                                                         dist_fac = BLI_rcti_length_y(&v2d->hor, local_xy[1]) / AZONEFADEIN;
772                                                         CLAMP(dist_fac, 0.0f, 1.0f);
773                                                         alpha = 1.0f - dist_fac;
774
775                                                         v2d->alpha_hor = alpha * 255;
776                                                 }
777                                                 else if (az->direction == AZ_SCROLL_VERT) {
778                                                         dist_fac = BLI_rcti_length_x(&v2d->vert, local_xy[0]) / AZONEFADEIN;
779                                                         CLAMP(dist_fac, 0.0f, 1.0f);
780                                                         alpha = 1.0f - dist_fac;
781
782                                                         v2d->alpha_vert = alpha * 255;
783                                                 }
784                                                 az->alpha = alpha;
785                                                 redraw = true;
786                                         }
787
788                                         if (redraw) {
789                                                 ED_area_tag_redraw_no_rebuild(sa);
790                                         }
791                                         /* Don't return! */
792                                 }
793                         }
794                 }
795         }
796
797         return az;
798 }
799
800 AZone *ED_area_actionzone_find_xy(ScrArea *sa, const int xy[2])
801 {
802         return area_actionzone_refresh_xy(sa, xy, true);
803 }
804
805 AZone *ED_area_actionzone_refresh_xy(ScrArea *sa, const int xy[2])
806 {
807         return area_actionzone_refresh_xy(sa, xy, false);
808 }
809
810 static void actionzone_exit(wmOperator *op)
811 {
812         if (op->customdata)
813                 MEM_freeN(op->customdata);
814         op->customdata = NULL;
815 }
816
817 /* send EVT_ACTIONZONE event */
818 static void actionzone_apply(bContext *C, wmOperator *op, int type)
819 {
820         wmEvent event;
821         wmWindow *win = CTX_wm_window(C);
822         sActionzoneData *sad = op->customdata;
823
824         sad->modifier = RNA_int_get(op->ptr, "modifier");
825
826         wm_event_init_from_window(win, &event);
827
828         if (type == AZONE_AREA)
829                 event.type = EVT_ACTIONZONE_AREA;
830         else if (type == AZONE_FULLSCREEN)
831                 event.type = EVT_ACTIONZONE_FULLSCREEN;
832         else
833                 event.type = EVT_ACTIONZONE_REGION;
834
835         event.val = KM_NOTHING;
836         event.customdata = op->customdata;
837         event.customdatafree = true;
838         op->customdata = NULL;
839
840         wm_event_add(win, &event);
841 }
842
843 static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
844 {
845         ScrArea *sa = CTX_wm_area(C);
846         AZone *az = ED_area_actionzone_find_xy(sa, &event->x);
847         sActionzoneData *sad;
848
849         /* quick escape - Scroll azones only hide/unhide the scroll-bars, they have their own handling. */
850         if (az == NULL || ELEM(az->type, AZONE_REGION_SCROLL))
851                 return OPERATOR_PASS_THROUGH;
852
853         /* ok we do the actionzone */
854         sad = op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
855         sad->sa1 = sa;
856         sad->az = az;
857         sad->x = event->x; sad->y = event->y;
858
859         /* region azone directly reacts on mouse clicks */
860         if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) {
861                 actionzone_apply(C, op, sad->az->type);
862                 actionzone_exit(op);
863                 return OPERATOR_FINISHED;
864         }
865         else {
866                 /* add modal handler */
867                 WM_event_add_modal_handler(C, op);
868
869                 return OPERATOR_RUNNING_MODAL;
870         }
871 }
872
873
874 static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
875 {
876         bScreen *sc = CTX_wm_screen(C);
877         sActionzoneData *sad = op->customdata;
878
879         switch (event->type) {
880                 case MOUSEMOVE:
881                 {
882                         bool is_gesture;
883
884                         const int delta_x = (event->x - sad->x);
885                         const int delta_y = (event->y - sad->y);
886
887                         /* calculate gesture direction */
888                         if (delta_y > ABS(delta_x))
889                                 sad->gesture_dir = 'n';
890                         else if (delta_x > ABS(delta_y))
891                                 sad->gesture_dir = 'e';
892                         else if (delta_y < -ABS(delta_x))
893                                 sad->gesture_dir = 's';
894                         else
895                                 sad->gesture_dir = 'w';
896
897                         if (sad->az->type == AZONE_AREA) {
898                                 const wmWindow *win = CTX_wm_window(C);
899                                 rcti screen_rect;
900
901                                 WM_window_screen_rect_calc(win, &screen_rect);
902                                 /* once we drag outside the actionzone, register a gesture
903                                  * check we're not on an edge so join finds the other area */
904                                 is_gesture = ((ED_area_actionzone_find_xy(sad->sa1, &event->x) != sad->az) &&
905                                               (screen_geom_area_map_find_active_scredge(
906                                                    AREAMAP_FROM_SCREEN(sc), &screen_rect, event->x, event->y) == NULL));
907                         }
908                         else {
909                                 const int delta_min = 1;
910                                 is_gesture = (ABS(delta_x) > delta_min || ABS(delta_y) > delta_min);
911                         }
912
913                         /* gesture is large enough? */
914                         if (is_gesture) {
915                                 /* second area, for join when (sa1 != sa2) */
916                                 sad->sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y);
917                                 /* apply sends event */
918                                 actionzone_apply(C, op, sad->az->type);
919                                 actionzone_exit(op);
920
921                                 return OPERATOR_FINISHED;
922                         }
923                         break;
924                 }
925                 case ESCKEY:
926                         actionzone_exit(op);
927                         return OPERATOR_CANCELLED;
928                 case LEFTMOUSE:
929                         actionzone_exit(op);
930                         return OPERATOR_CANCELLED;
931
932         }
933
934         return OPERATOR_RUNNING_MODAL;
935 }
936
937 static void actionzone_cancel(bContext *UNUSED(C), wmOperator *op)
938 {
939         actionzone_exit(op);
940 }
941
942 static void SCREEN_OT_actionzone(wmOperatorType *ot)
943 {
944         /* identifiers */
945         ot->name = "Handle Area Action Zones";
946         ot->description = "Handle area action zones for mouse actions/gestures";
947         ot->idname = "SCREEN_OT_actionzone";
948
949         ot->invoke = actionzone_invoke;
950         ot->modal = actionzone_modal;
951         ot->poll = actionzone_area_poll;
952         ot->cancel = actionzone_cancel;
953
954         /* flags */
955         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
956
957         RNA_def_int(ot->srna, "modifier", 0, 0, 2, "Modifier", "Modifier state", 0, 2);
958 }
959
960 /** \} */
961
962 /* -------------------------------------------------------------------- */
963 /** \name Swap Area Operator
964  * \{ */
965
966 /* operator state vars used:
967  * sa1      start area
968  * sa2      area to swap with
969  *
970  * functions:
971  *
972  * init()   set custom data for operator, based on actionzone event custom data
973  *
974  * cancel() cancel the operator
975  *
976  * exit()   cleanup, send notifier
977  *
978  * callbacks:
979  *
980  * invoke() gets called on shift+lmb drag in actionzone
981  * call init(), add handler
982  *
983  * modal()  accept modal events while doing it
984  */
985
986 typedef struct sAreaSwapData {
987         ScrArea *sa1, *sa2;
988 } sAreaSwapData;
989
990 static int area_swap_init(wmOperator *op, const wmEvent *event)
991 {
992         sAreaSwapData *sd = NULL;
993         sActionzoneData *sad = event->customdata;
994
995         if (sad == NULL || sad->sa1 == NULL)
996                 return 0;
997
998         sd = MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
999         sd->sa1 = sad->sa1;
1000         sd->sa2 = sad->sa2;
1001         op->customdata = sd;
1002
1003         return 1;
1004 }
1005
1006
1007 static void area_swap_exit(bContext *C, wmOperator *op)
1008 {
1009         WM_cursor_modal_restore(CTX_wm_window(C));
1010         if (op->customdata)
1011                 MEM_freeN(op->customdata);
1012         op->customdata = NULL;
1013 }
1014
1015 static void area_swap_cancel(bContext *C, wmOperator *op)
1016 {
1017         area_swap_exit(C, op);
1018 }
1019
1020 static int area_swap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1021 {
1022
1023         if (!area_swap_init(op, event))
1024                 return OPERATOR_PASS_THROUGH;
1025
1026         /* add modal handler */
1027         WM_cursor_modal_set(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
1028         WM_event_add_modal_handler(C, op);
1029
1030         return OPERATOR_RUNNING_MODAL;
1031
1032 }
1033
1034 static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event)
1035 {
1036         sActionzoneData *sad = op->customdata;
1037
1038         switch (event->type) {
1039                 case MOUSEMOVE:
1040                         /* second area, for join */
1041                         sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y);
1042                         break;
1043                 case LEFTMOUSE: /* release LMB */
1044                         if (event->val == KM_RELEASE) {
1045                                 if (!sad->sa2 || sad->sa1 == sad->sa2) {
1046                                         area_swap_cancel(C, op);
1047                                         return OPERATOR_CANCELLED;
1048                                 }
1049
1050                                 ED_area_tag_redraw(sad->sa1);
1051                                 ED_area_tag_redraw(sad->sa2);
1052
1053                                 ED_area_swapspace(C, sad->sa1, sad->sa2);
1054
1055                                 area_swap_exit(C, op);
1056
1057                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1058
1059                                 return OPERATOR_FINISHED;
1060                         }
1061                         break;
1062
1063                 case ESCKEY:
1064                         area_swap_cancel(C, op);
1065                         return OPERATOR_CANCELLED;
1066         }
1067         return OPERATOR_RUNNING_MODAL;
1068 }
1069
1070 static void SCREEN_OT_area_swap(wmOperatorType *ot)
1071 {
1072         ot->name = "Swap Areas";
1073         ot->description = "Swap selected areas screen positions";
1074         ot->idname = "SCREEN_OT_area_swap";
1075
1076         ot->invoke = area_swap_invoke;
1077         ot->modal = area_swap_modal;
1078         ot->poll = ED_operator_areaactive;
1079         ot->cancel = area_swap_cancel;
1080
1081         ot->flag = OPTYPE_BLOCKING;
1082 }
1083
1084 /** \} */
1085
1086 /* -------------------------------------------------------------------- */
1087 /** \name Area Duplicate Operator
1088  *
1089  * Create new window from area.
1090  * \{ */
1091
1092 /* operator callback */
1093 static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1094 {
1095         Main *bmain = CTX_data_main(C);
1096         wmWindow *newwin, *win = CTX_wm_window(C);
1097         Scene *scene;
1098         WorkSpace *workspace = WM_window_get_active_workspace(win);
1099         WorkSpaceLayout *layout_old = WM_window_get_active_layout(win);
1100         WorkSpaceLayout *layout_new;
1101         bScreen *newsc;
1102         ScrArea *sa;
1103         rcti rect;
1104
1105         win = CTX_wm_window(C);
1106         scene = CTX_data_scene(C);
1107         sa = CTX_wm_area(C);
1108
1109         /* XXX hrmf! */
1110         if (event->type == EVT_ACTIONZONE_AREA) {
1111                 sActionzoneData *sad = event->customdata;
1112
1113                 if (sad == NULL)
1114                         return OPERATOR_PASS_THROUGH;
1115
1116                 sa = sad->sa1;
1117         }
1118
1119         /* adds window to WM */
1120         rect = sa->totrct;
1121         BLI_rcti_translate(&rect, win->posx, win->posy);
1122         rect.xmax = rect.xmin + BLI_rcti_size_x(&rect) / U.pixelsize;
1123         rect.ymax = rect.ymin + BLI_rcti_size_y(&rect) / U.pixelsize;
1124
1125         newwin = WM_window_open(C, &rect);
1126         if (newwin == NULL) {
1127                 BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
1128                 goto finally;
1129         }
1130
1131         *newwin->stereo3d_format = *win->stereo3d_format;
1132
1133         newwin->scene = scene;
1134
1135         BKE_workspace_active_set(newwin->workspace_hook, workspace);
1136         /* allocs new screen and adds to newly created window, using window size */
1137         layout_new = ED_workspace_layout_add(bmain, workspace, newwin, BKE_workspace_layout_name_get(layout_old));
1138         newsc = BKE_workspace_layout_screen_get(layout_new);
1139         WM_window_set_active_layout(newwin, workspace, layout_new);
1140
1141         /* copy area to new screen */
1142         ED_area_data_copy((ScrArea *)newsc->areabase.first, sa, true);
1143
1144         ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
1145
1146         /* screen, areas init */
1147         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1148
1149
1150 finally:
1151         if (event->type == EVT_ACTIONZONE_AREA)
1152                 actionzone_exit(op);
1153
1154         if (newwin) {
1155                 return OPERATOR_FINISHED;
1156         }
1157         else {
1158                 return OPERATOR_CANCELLED;
1159         }
1160 }
1161
1162 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
1163 {
1164         ot->name = "Duplicate Area into New Window";
1165         ot->description = "Duplicate selected area into new window";
1166         ot->idname = "SCREEN_OT_area_dupli";
1167
1168         ot->invoke = area_dupli_invoke;
1169         ot->poll = ED_operator_areaactive;
1170 }
1171
1172 /** \} */
1173
1174 /* -------------------------------------------------------------------- */
1175 /** \name Move Area Edge Operator
1176  * \{ */
1177
1178 /* operator state vars used:
1179  * x, y             mouse coord near edge
1180  * delta            movement of edge
1181  *
1182  * functions:
1183  *
1184  * init()   set default property values, find edge based on mouse coords, test
1185  * if the edge can be moved, select edges, calculate min and max movement
1186  *
1187  * apply()      apply delta on selection
1188  *
1189  * exit()       cleanup, send notifier
1190  *
1191  * cancel() cancel moving
1192  *
1193  * callbacks:
1194  *
1195  * exec()   execute without any user interaction, based on properties
1196  * call init(), apply(), exit()
1197  *
1198  * invoke() gets called on mouse click near edge
1199  * call init(), add handler
1200  *
1201  * modal()  accept modal events while doing it
1202  * call apply() with delta motion
1203  * call exit() and remove handler
1204  */
1205
1206 typedef struct sAreaMoveData {
1207         int bigger, smaller, origval, step;
1208         char dir;
1209         enum AreaMoveSnapType {
1210                 /* Snapping disabled */
1211                 SNAP_NONE = 0,
1212                 /* Snap to an invisible grid with a unit defined in AREAGRID */
1213                 SNAP_AREAGRID,
1214                 /* Snap to fraction (half, third.. etc) and adjacent edges. */
1215                 SNAP_FRACTION_AND_ADJACENT,
1216                 /* Snap to either bigger or smaller, nothing in-between (used for
1217                  * global areas). This has priority over other snap types, if it is
1218                  * used, toggling SNAP_FRACTION_AND_ADJACENT doesn't work. */
1219                 SNAP_BIGGER_SMALLER_ONLY,
1220         } snap_type;
1221 } sAreaMoveData;
1222
1223 /* helper call to move area-edge, sets limits
1224  * need window bounds in order to get correct limits */
1225 static void area_move_set_limits(
1226         wmWindow *win, bScreen *sc, int dir,
1227         int *bigger, int *smaller,
1228         bool *use_bigger_smaller_snap)
1229 {
1230         rcti window_rect;
1231         int areaminy = ED_area_headersize();
1232         int areamin;
1233
1234         /* we check all areas and test for free space with MINSIZE */
1235         *bigger = *smaller = 100000;
1236
1237         if (use_bigger_smaller_snap != NULL) {
1238                 *use_bigger_smaller_snap = false;
1239                 for (ScrArea *area = win->global_areas.areabase.first; area; area = area->next) {
1240                         int size_min = ED_area_global_min_size_y(area) - 1;
1241                         int size_max = ED_area_global_max_size_y(area) - 1;
1242
1243                         size_min = MAX2(size_min, 0);
1244                         BLI_assert(size_min < size_max);
1245
1246                         /* logic here is only tested for lower edge :) */
1247                         /* left edge */
1248                         if ((area->v1->editflag && area->v2->editflag)) {
1249                                 *smaller = area->v4->vec.x - size_max;
1250                                 *bigger  = area->v4->vec.x - size_min;
1251                                 *use_bigger_smaller_snap = true;
1252                                 return;
1253                         }
1254                         /* top edge */
1255                         else if ((area->v2->editflag && area->v3->editflag)) {
1256                                 *smaller = area->v1->vec.y + size_min;
1257                                 *bigger  = area->v1->vec.y + size_max;
1258                                 *use_bigger_smaller_snap = true;
1259                                 return;
1260                         }
1261                         /* right edge */
1262                         else if ((area->v3->editflag && area->v4->editflag)) {
1263                                 *smaller = area->v1->vec.x + size_min;
1264                                 *bigger  = area->v1->vec.x + size_max;
1265                                 *use_bigger_smaller_snap = true;
1266                                 return;
1267                         }
1268                         /* lower edge */
1269                         else if ((area->v4->editflag && area->v1->editflag)) {
1270                                 *smaller = area->v2->vec.y - size_max;
1271                                 *bigger  = area->v2->vec.y - size_min;
1272                                 *use_bigger_smaller_snap = true;
1273                                 return;
1274                         }
1275                 }
1276         }
1277
1278         WM_window_rect_calc(win, &window_rect);
1279
1280         for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
1281                 if (dir == 'h') {
1282                         int y1;
1283                         areamin = areaminy;
1284
1285                         if (sa->v1->vec.y > window_rect.ymin)
1286                                 areamin += U.pixelsize;
1287                         if (sa->v2->vec.y < (window_rect.ymax - 1))
1288                                 areamin += U.pixelsize;
1289
1290                         y1 = screen_geom_area_height(sa) - areamin;
1291
1292                         /* if top or down edge selected, test height */
1293                         if (sa->v1->editflag && sa->v4->editflag)
1294                                 *bigger = min_ii(*bigger, y1);
1295                         else if (sa->v2->editflag && sa->v3->editflag)
1296                                 *smaller = min_ii(*smaller, y1);
1297                 }
1298                 else {
1299                         int x1;
1300                         areamin = AREAMINX;
1301
1302                         if (sa->v1->vec.x > window_rect.xmin)
1303                                 areamin += U.pixelsize;
1304                         if (sa->v4->vec.x < (window_rect.xmax - 1))
1305                                 areamin += U.pixelsize;
1306
1307                         x1 = screen_geom_area_width(sa) - areamin;
1308
1309                         /* if left or right edge selected, test width */
1310                         if (sa->v1->editflag && sa->v2->editflag)
1311                                 *bigger = min_ii(*bigger, x1);
1312                         else if (sa->v3->editflag && sa->v4->editflag)
1313                                 *smaller = min_ii(*smaller, x1);
1314                 }
1315         }
1316 }
1317
1318 /* validate selection inside screen, set variables OK */
1319 /* return 0: init failed */
1320 static int area_move_init(bContext *C, wmOperator *op)
1321 {
1322         bScreen *sc = CTX_wm_screen(C);
1323         wmWindow *win = CTX_wm_window(C);
1324         ScrEdge *actedge;
1325         sAreaMoveData *md;
1326         int x, y;
1327
1328         /* required properties */
1329         x = RNA_int_get(op->ptr, "x");
1330         y = RNA_int_get(op->ptr, "y");
1331
1332         /* setup */
1333         actedge = screen_geom_find_active_scredge(win, sc, x, y);
1334         if (actedge == NULL) return 0;
1335
1336         md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
1337         op->customdata = md;
1338
1339         md->dir = screen_geom_edge_is_horizontal(actedge) ? 'h' : 'v';
1340         if (md->dir == 'h') md->origval = actedge->v1->vec.y;
1341         else md->origval = actedge->v1->vec.x;
1342
1343         screen_geom_select_connected_edge(win, actedge);
1344         /* now all vertices with 'flag == 1' are the ones that can be moved. Move this to editflag */
1345         ED_screen_verts_iter(win, sc, v1) {
1346                 v1->editflag = v1->flag;
1347         }
1348
1349         bool use_bigger_smaller_snap = false;
1350         area_move_set_limits(win, sc, md->dir, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
1351
1352         md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID;
1353
1354         return 1;
1355 }
1356
1357 static int area_snap_calc_location(
1358         const bScreen *sc, const enum AreaMoveSnapType snap_type,
1359         const int delta, const int origval, const int dir,
1360         const int bigger, const int smaller)
1361 {
1362         BLI_assert(snap_type != SNAP_NONE);
1363         int m_cursor_final = -1;
1364         const int m_cursor = origval + delta;
1365         const int m_span = (float)(bigger + smaller);
1366         const int m_min = origval - smaller;
1367         // const int axis_max = axis_min + m_span;
1368
1369         switch (snap_type) {
1370                 case SNAP_AREAGRID:
1371                         m_cursor_final = m_cursor;
1372                         if (delta != bigger && delta != -smaller) {
1373                                 m_cursor_final -= (m_cursor % AREAGRID);
1374                                 CLAMP(m_cursor_final, origval - smaller, origval + bigger);
1375                         }
1376                         break;
1377
1378                 case SNAP_BIGGER_SMALLER_ONLY:
1379                         m_cursor_final = (m_cursor >= bigger) ? bigger : smaller;
1380                         break;
1381
1382                 case SNAP_FRACTION_AND_ADJACENT:
1383                 {
1384                         const int axis = (dir == 'v') ? 0 : 1;
1385                         int snap_dist_best = INT_MAX;
1386                         {
1387                                 const float div_array[] = {
1388                                         /* Middle. */
1389                                         1.0f / 2.0f,
1390                                         /* Thirds. */
1391                                         1.0f / 3.0f, 2.0f / 3.0f,
1392                                         /* Quaters. */
1393                                         1.0f / 4.0f, 3.0f / 4.0f,
1394                                         /* Eighth. */
1395                                         1.0f / 8.0f, 3.0f / 8.0f,
1396                                         5.0f / 8.0f, 7.0f / 8.0f,
1397                                 };
1398                                 /* Test the snap to the best division. */
1399                                 for (int i = 0; i < ARRAY_SIZE(div_array); i++) {
1400                                         const int m_cursor_test = m_min + round_fl_to_int(m_span * div_array[i]);
1401                                         const int snap_dist_test = abs(m_cursor - m_cursor_test);
1402                                         if (snap_dist_best >= snap_dist_test) {
1403                                                 snap_dist_best = snap_dist_test;
1404                                                 m_cursor_final = m_cursor_test;
1405                                         }
1406                                 }
1407                         }
1408
1409                         for (const ScrVert *v1 = sc->vertbase.first; v1; v1 = v1->next) {
1410                                 if (!v1->editflag) {
1411                                         continue;
1412                                 }
1413                                 const int v_loc = (&v1->vec.x)[!axis];
1414
1415                                 for (const ScrVert *v2 = sc->vertbase.first; v2; v2 = v2->next) {
1416                                         if (v2->editflag) {
1417                                                 continue;
1418                                         }
1419                                         if (v_loc == (&v2->vec.x)[!axis]) {
1420                                                 const int v_loc2 = (&v2->vec.x)[axis];
1421                                                 /* Do not snap to the vertices at the ends. */
1422                                                 if ((origval - smaller) < v_loc2 && v_loc2 < (origval + bigger)) {
1423                                                         const int snap_dist_test = abs(m_cursor - v_loc2);
1424                                                         if (snap_dist_best >= snap_dist_test) {
1425                                                                 snap_dist_best = snap_dist_test;
1426                                                                 m_cursor_final = v_loc2;
1427                                                         }
1428                                                 }
1429                                         }
1430                                 }
1431                         }
1432                         break;
1433                 }
1434                 case SNAP_NONE:
1435                         break;
1436         }
1437
1438         BLI_assert(ELEM(snap_type, SNAP_BIGGER_SMALLER_ONLY) ||
1439                    IN_RANGE_INCL(m_cursor_final, origval - smaller, origval + bigger));
1440
1441         return m_cursor_final;
1442 }
1443
1444 /* moves selected screen edge amount of delta, used by split & move */
1445 static void area_move_apply_do(
1446         const bContext *C, int delta,
1447         const int origval, const int dir,
1448         const int bigger, const int smaller,
1449         const enum AreaMoveSnapType snap_type)
1450 {
1451         wmWindow *win = CTX_wm_window(C);
1452         bScreen *sc = CTX_wm_screen(C);
1453         short final_loc = -1;
1454         bool doredraw = false;
1455
1456         if (snap_type != SNAP_BIGGER_SMALLER_ONLY) {
1457                 CLAMP(delta, -smaller, bigger);
1458         }
1459
1460         if (snap_type == SNAP_NONE) {
1461                 final_loc = origval + delta;
1462         }
1463         else {
1464                 final_loc = area_snap_calc_location(sc, snap_type, delta, origval, dir, bigger, smaller);
1465         }
1466
1467         BLI_assert(final_loc != -1);
1468         short axis = (dir == 'v') ? 0 : 1;
1469
1470         ED_screen_verts_iter(win, sc, v1) {
1471                 if (v1->editflag) {
1472                         short oldval = (&v1->vec.x)[axis];
1473                         (&v1->vec.x)[axis] = final_loc;
1474
1475                         if (oldval == final_loc) {
1476                                 /* nothing will change to the other vertices either. */
1477                                 break;
1478                         }
1479                         doredraw = true;
1480                 }
1481         }
1482
1483         /* only redraw if we actually moved a screen vert, for AREAGRID */
1484         if (doredraw) {
1485                 bool redraw_all = false;
1486                 ED_screen_areas_iter(win, sc, sa) {
1487                         if (sa->v1->editflag || sa->v2->editflag || sa->v3->editflag || sa->v4->editflag) {
1488                                 if (ED_area_is_global(sa)) {
1489                                         sa->global->cur_fixed_height = round_fl_to_int(screen_geom_area_height(sa) / UI_DPI_FAC);
1490                                         sc->do_refresh = true;
1491                                         redraw_all = true;
1492                                 }
1493                                 ED_area_tag_redraw(sa);
1494                         }
1495                 }
1496                 if (redraw_all) {
1497                         ED_screen_areas_iter(win, sc, sa) {
1498                                 ED_area_tag_redraw(sa);
1499                         }
1500                 }
1501
1502                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */
1503                 /* Update preview thumbnail */
1504                 BKE_icon_changed(sc->id.icon_id);
1505         }
1506 }
1507
1508 static void area_move_apply(bContext *C, wmOperator *op)
1509 {
1510         sAreaMoveData *md = op->customdata;
1511         int delta = RNA_int_get(op->ptr, "delta");
1512
1513         area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->snap_type);
1514 }
1515
1516 static void area_move_exit(bContext *C, wmOperator *op)
1517 {
1518         if (op->customdata)
1519                 MEM_freeN(op->customdata);
1520         op->customdata = NULL;
1521
1522         /* this makes sure aligned edges will result in aligned grabbing */
1523         BKE_screen_remove_double_scrverts(CTX_wm_screen(C));
1524         BKE_screen_remove_double_scredges(CTX_wm_screen(C));
1525 }
1526
1527 static int area_move_exec(bContext *C, wmOperator *op)
1528 {
1529         if (!area_move_init(C, op))
1530                 return OPERATOR_CANCELLED;
1531
1532         area_move_apply(C, op);
1533         area_move_exit(C, op);
1534
1535         return OPERATOR_FINISHED;
1536 }
1537
1538 /* interaction callback */
1539 static int area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1540 {
1541         RNA_int_set(op->ptr, "x", event->x);
1542         RNA_int_set(op->ptr, "y", event->y);
1543
1544         if (!area_move_init(C, op))
1545                 return OPERATOR_PASS_THROUGH;
1546
1547         /* add temp handler */
1548         WM_event_add_modal_handler(C, op);
1549
1550         return OPERATOR_RUNNING_MODAL;
1551 }
1552
1553 static void area_move_cancel(bContext *C, wmOperator *op)
1554 {
1555
1556         RNA_int_set(op->ptr, "delta", 0);
1557         area_move_apply(C, op);
1558         area_move_exit(C, op);
1559 }
1560
1561 /* modal callback for while moving edges */
1562 static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
1563 {
1564         sAreaMoveData *md = op->customdata;
1565         int delta, x, y;
1566
1567         /* execute the events */
1568         switch (event->type) {
1569                 case MOUSEMOVE:
1570                 {
1571                         x = RNA_int_get(op->ptr, "x");
1572                         y = RNA_int_get(op->ptr, "y");
1573
1574                         delta = (md->dir == 'v') ? event->x - x : event->y - y;
1575                         RNA_int_set(op->ptr, "delta", delta);
1576
1577                         area_move_apply(C, op);
1578                         break;
1579                 }
1580                 case EVT_MODAL_MAP:
1581                 {
1582                         switch (event->val) {
1583                                 case KM_MODAL_APPLY:
1584                                         area_move_exit(C, op);
1585                                         return OPERATOR_FINISHED;
1586
1587                                 case KM_MODAL_CANCEL:
1588                                         area_move_cancel(C, op);
1589                                         return OPERATOR_CANCELLED;
1590
1591                                 case KM_MODAL_SNAP_ON:
1592                                         if (md->snap_type != SNAP_BIGGER_SMALLER_ONLY) {
1593                                                 md->snap_type = SNAP_FRACTION_AND_ADJACENT;
1594                                         }
1595                                         break;
1596
1597                                 case KM_MODAL_SNAP_OFF:
1598                                         if (md->snap_type != SNAP_BIGGER_SMALLER_ONLY) {
1599                                                 md->snap_type = SNAP_AREAGRID;
1600                                         }
1601                                         break;
1602                         }
1603                         break;
1604                 }
1605         }
1606
1607         return OPERATOR_RUNNING_MODAL;
1608 }
1609
1610 static void SCREEN_OT_area_move(wmOperatorType *ot)
1611 {
1612         /* identifiers */
1613         ot->name = "Move Area Edges";
1614         ot->description = "Move selected area edges";
1615         ot->idname = "SCREEN_OT_area_move";
1616
1617         ot->exec = area_move_exec;
1618         ot->invoke = area_move_invoke;
1619         ot->cancel = area_move_cancel;
1620         ot->modal = area_move_modal;
1621         ot->poll = ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
1622
1623         /* flags */
1624         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1625
1626         /* rna */
1627         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1628         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1629         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1630 }
1631
1632 /** \} */
1633
1634 /* -------------------------------------------------------------------- */
1635 /** \name Split Area Operator
1636  * \{ */
1637
1638 /*
1639  * operator state vars:
1640  * fac              spit point
1641  * dir              direction 'v' or 'h'
1642  *
1643  * operator customdata:
1644  * area             pointer to (active) area
1645  * x, y             last used mouse pos
1646  * (more, see below)
1647  *
1648  * functions:
1649  *
1650  * init()   set default property values, find area based on context
1651  *
1652  * apply()  split area based on state vars
1653  *
1654  * exit()   cleanup, send notifier
1655  *
1656  * cancel() remove duplicated area
1657  *
1658  * callbacks:
1659  *
1660  * exec()   execute without any user interaction, based on state vars
1661  * call init(), apply(), exit()
1662  *
1663  * invoke() gets called on mouse click in action-widget
1664  * call init(), add modal handler
1665  * call apply() with initial motion
1666  *
1667  * modal()  accept modal events while doing it
1668  * call move-areas code with delta motion
1669  * call exit() or cancel() and remove handler
1670  */
1671
1672 typedef struct sAreaSplitData {
1673         int origval;            /* for move areas */
1674         int bigger, smaller;    /* constraints for moving new edge */
1675         int delta;              /* delta move edge */
1676         int origmin, origsize;  /* to calculate fac, for property storage */
1677         int previewmode;        /* draw previewline, then split */
1678         void *draw_callback;    /* call `ED_screen_draw_split_preview` */
1679         bool do_snap;
1680
1681         ScrEdge *nedge;         /* new edge */
1682         ScrArea *sarea;         /* start area */
1683         ScrArea *narea;         /* new area */
1684
1685 } sAreaSplitData;
1686
1687 static void area_split_draw_cb(const struct wmWindow *UNUSED(win), void *userdata)
1688 {
1689         const wmOperator *op = userdata;
1690
1691         sAreaSplitData *sd = op->customdata;
1692         if (sd->sarea) {
1693                 int dir = RNA_enum_get(op->ptr, "direction");
1694                 float fac = RNA_float_get(op->ptr, "factor");
1695
1696                 ED_screen_draw_split_preview(sd->sarea, dir, fac);
1697         }
1698 }
1699
1700 /* generic init, menu case, doesn't need active area */
1701 static int area_split_menu_init(bContext *C, wmOperator *op)
1702 {
1703         sAreaSplitData *sd;
1704
1705         /* custom data */
1706         sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1707         op->customdata = sd;
1708
1709         sd->sarea = CTX_wm_area(C);
1710
1711         return 1;
1712 }
1713
1714 /* generic init, no UI stuff here, assumes active area */
1715 static int area_split_init(bContext *C, wmOperator *op)
1716 {
1717         ScrArea *sa = CTX_wm_area(C);
1718         sAreaSplitData *sd;
1719         int areaminy = ED_area_headersize();
1720         int dir;
1721
1722         /* required context */
1723         if (sa == NULL) return 0;
1724
1725         /* required properties */
1726         dir = RNA_enum_get(op->ptr, "direction");
1727
1728         /* minimal size */
1729         if (dir == 'v' && sa->winx < 2 * AREAMINX) return 0;
1730         if (dir == 'h' && sa->winy < 2 * areaminy) return 0;
1731
1732         /* custom data */
1733         sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1734         op->customdata = sd;
1735
1736         sd->sarea = sa;
1737         if (dir == 'v') {
1738                 sd->origmin = sa->v1->vec.x;
1739                 sd->origsize = sa->v4->vec.x - sd->origmin;
1740         }
1741         else {
1742                 sd->origmin = sa->v1->vec.y;
1743                 sd->origsize = sa->v2->vec.y - sd->origmin;
1744         }
1745
1746         return 1;
1747 }
1748
1749 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1750 /* used with split operator */
1751 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1752 {
1753         ScrVert *sav1 = sa->v1;
1754         ScrVert *sav2 = sa->v2;
1755         ScrVert *sav3 = sa->v3;
1756         ScrVert *sav4 = sa->v4;
1757         ScrVert *sbv1 = sb->v1;
1758         ScrVert *sbv2 = sb->v2;
1759         ScrVert *sbv3 = sb->v3;
1760         ScrVert *sbv4 = sb->v4;
1761
1762         if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */
1763                 return BKE_screen_find_edge(screen, sav1, sav2);
1764         }
1765         else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */
1766                 return BKE_screen_find_edge(screen, sav2, sav3);
1767         }
1768         else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */
1769                 return BKE_screen_find_edge(screen, sav3, sav4);
1770         }
1771         else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/
1772                 return BKE_screen_find_edge(screen, sav1, sav4);
1773         }
1774
1775         return NULL;
1776 }
1777
1778
1779 /* do the split, return success */
1780 static int area_split_apply(bContext *C, wmOperator *op)
1781 {
1782         const wmWindow *win = CTX_wm_window(C);
1783         bScreen *sc = CTX_wm_screen(C);
1784         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1785         float fac;
1786         int dir;
1787
1788         fac = RNA_float_get(op->ptr, "factor");
1789         dir = RNA_enum_get(op->ptr, "direction");
1790
1791         sd->narea = area_split(win, sc, sd->sarea, dir, fac, 0); /* 0 = no merge */
1792
1793         if (sd->narea) {
1794                 sd->nedge = area_findsharededge(sc, sd->sarea, sd->narea);
1795
1796                 /* select newly created edge, prepare for moving edge */
1797                 ED_screen_verts_iter(win, sc, sv) {
1798                         sv->editflag = 0;
1799                 }
1800
1801                 sd->nedge->v1->editflag = 1;
1802                 sd->nedge->v2->editflag = 1;
1803
1804                 if (dir == 'h') sd->origval = sd->nedge->v1->vec.y;
1805                 else sd->origval = sd->nedge->v1->vec.x;
1806
1807                 ED_area_tag_redraw(sd->sarea);
1808                 ED_area_tag_redraw(sd->narea);
1809
1810                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1811                 /* Update preview thumbnail */
1812                 BKE_icon_changed(sc->id.icon_id);
1813
1814                 return 1;
1815         }
1816
1817         return 0;
1818 }
1819
1820 static void area_split_exit(bContext *C, wmOperator *op)
1821 {
1822         if (op->customdata) {
1823                 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1824                 if (sd->sarea) ED_area_tag_redraw(sd->sarea);
1825                 if (sd->narea) ED_area_tag_redraw(sd->narea);
1826
1827                 if (sd->draw_callback)
1828                         WM_draw_cb_exit(CTX_wm_window(C), sd->draw_callback);
1829
1830                 MEM_freeN(op->customdata);
1831                 op->customdata = NULL;
1832         }
1833
1834         WM_cursor_modal_restore(CTX_wm_window(C));
1835         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1836
1837         /* this makes sure aligned edges will result in aligned grabbing */
1838         BKE_screen_remove_double_scrverts(CTX_wm_screen(C));
1839         BKE_screen_remove_double_scredges(CTX_wm_screen(C));
1840 }
1841
1842 static void area_split_preview_update_cursor(bContext *C, wmOperator *op)
1843 {
1844         wmWindow *win = CTX_wm_window(C);
1845         int dir = RNA_enum_get(op->ptr, "direction");
1846         WM_cursor_set(win, (dir == 'v') ? CURSOR_X_MOVE : CURSOR_Y_MOVE);
1847 }
1848
1849 /* UI callback, adds new handler */
1850 static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1851 {
1852         wmWindow *win = CTX_wm_window(C);
1853         bScreen *sc = CTX_wm_screen(C);
1854         sAreaSplitData *sd;
1855         int dir;
1856
1857         /* no full window splitting allowed */
1858         BLI_assert(sc->state == SCREENNORMAL);
1859
1860         PropertyRNA *prop_dir = RNA_struct_find_property(op->ptr, "direction");
1861         PropertyRNA *prop_factor = RNA_struct_find_property(op->ptr, "factor");
1862         PropertyRNA *prop_cursor = RNA_struct_find_property(op->ptr, "cursor");
1863
1864         if (event->type == EVT_ACTIONZONE_AREA) {
1865                 sActionzoneData *sad = event->customdata;
1866
1867                 if (sad == NULL || sad->modifier > 0) {
1868                         return OPERATOR_PASS_THROUGH;
1869                 }
1870
1871                 /* verify *sad itself */
1872                 if (sad->sa1 == NULL || sad->az == NULL)
1873                         return OPERATOR_PASS_THROUGH;
1874
1875                 /* is this our *sad? if areas not equal it should be passed on */
1876                 if (CTX_wm_area(C) != sad->sa1 || sad->sa1 != sad->sa2)
1877                         return OPERATOR_PASS_THROUGH;
1878
1879                 /* prepare operator state vars */
1880                 if (sad->gesture_dir == 'n' || sad->gesture_dir == 's') {
1881                         dir = 'h';
1882                         RNA_property_float_set(
1883                                 op->ptr, prop_factor,
1884                                 ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1885                 }
1886                 else {
1887                         dir = 'v';
1888                         RNA_property_float_set(
1889                                 op->ptr, prop_factor,
1890                                 ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1891                 }
1892                 RNA_property_enum_set(op->ptr, prop_dir, dir);
1893
1894                 /* general init, also non-UI case, adds customdata, sets area and defaults */
1895                 if (!area_split_init(C, op)) {
1896                         return OPERATOR_PASS_THROUGH;
1897                 }
1898         }
1899         else if (RNA_property_is_set(op->ptr, prop_dir)) {
1900                 ScrArea *sa = CTX_wm_area(C);
1901                 if (sa == NULL) {
1902                         return OPERATOR_CANCELLED;
1903                 }
1904                 dir = RNA_property_enum_get(op->ptr, prop_dir);
1905                 if (dir == 'h') {
1906                         RNA_property_float_set(
1907                                 op->ptr, prop_factor,
1908                                 ((float)(event->x - sa->v1->vec.x)) / (float)sa->winx);
1909                 }
1910                 else {
1911                         RNA_property_float_set(
1912                                 op->ptr, prop_factor,
1913                                 ((float)(event->y - sa->v1->vec.y)) / (float)sa->winy);
1914                 }
1915
1916                 if (!area_split_init(C, op)) {
1917                         return OPERATOR_CANCELLED;
1918                 }
1919         }
1920         else {
1921                 ScrEdge *actedge;
1922                 rcti window_rect;
1923                 int event_co[2];
1924
1925                 /* retrieve initial mouse coord, so we can find the active edge */
1926                 if (RNA_property_is_set(op->ptr, prop_cursor)) {
1927                         RNA_property_int_get_array(op->ptr, prop_cursor, event_co);
1928                 }
1929                 else {
1930                         copy_v2_v2_int(event_co, &event->x);
1931                 }
1932
1933                 WM_window_rect_calc(win, &window_rect);
1934
1935                 actedge = screen_geom_area_map_find_active_scredge(
1936                         AREAMAP_FROM_SCREEN(sc), &window_rect, event_co[0], event_co[1]);
1937                 if (actedge == NULL) {
1938                         return OPERATOR_CANCELLED;
1939                 }
1940
1941                 dir = screen_geom_edge_is_horizontal(actedge) ? 'v' : 'h';
1942
1943                 RNA_property_enum_set(op->ptr, prop_dir, dir);
1944
1945                 /* special case, adds customdata, sets defaults */
1946                 if (!area_split_menu_init(C, op)) {
1947                         return OPERATOR_CANCELLED;
1948                 }
1949         }
1950
1951         sd = (sAreaSplitData *)op->customdata;
1952
1953         if (event->type == EVT_ACTIONZONE_AREA) {
1954
1955                 /* do the split */
1956                 if (area_split_apply(C, op)) {
1957                         area_move_set_limits(win, sc, dir, &sd->bigger, &sd->smaller, NULL);
1958
1959                         /* add temp handler for edge move or cancel */
1960                         WM_event_add_modal_handler(C, op);
1961
1962                         return OPERATOR_RUNNING_MODAL;
1963                 }
1964         }
1965         else {
1966                 sd->previewmode = 1;
1967                 sd->draw_callback = WM_draw_cb_activate(win, area_split_draw_cb, op);
1968                 /* add temp handler for edge move or cancel */
1969                 WM_event_add_modal_handler(C, op);
1970                 area_split_preview_update_cursor(C, op);
1971
1972                 return OPERATOR_RUNNING_MODAL;
1973
1974         }
1975
1976         return OPERATOR_PASS_THROUGH;
1977 }
1978
1979 /* function to be called outside UI context, or for redo */
1980 static int area_split_exec(bContext *C, wmOperator *op)
1981 {
1982
1983         if (!area_split_init(C, op))
1984                 return OPERATOR_CANCELLED;
1985
1986         area_split_apply(C, op);
1987         area_split_exit(C, op);
1988
1989         return OPERATOR_FINISHED;
1990 }
1991
1992
1993 static void area_split_cancel(bContext *C, wmOperator *op)
1994 {
1995         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1996
1997         if (sd->previewmode) {
1998                 /* pass */
1999         }
2000         else {
2001                 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
2002                         if (CTX_wm_area(C) == sd->narea) {
2003                                 CTX_wm_area_set(C, NULL);
2004                                 CTX_wm_region_set(C, NULL);
2005                         }
2006                         sd->narea = NULL;
2007                 }
2008         }
2009         area_split_exit(C, op);
2010 }
2011
2012 static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
2013 {
2014         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
2015         PropertyRNA *prop_dir = RNA_struct_find_property(op->ptr, "direction");
2016         bool update_factor = false;
2017
2018         /* execute the events */
2019         switch (event->type) {
2020                 case MOUSEMOVE:
2021                         update_factor = true;
2022                         break;
2023
2024                 case LEFTMOUSE:
2025                         if (sd->previewmode) {
2026                                 area_split_apply(C, op);
2027                                 area_split_exit(C, op);
2028                                 return OPERATOR_FINISHED;
2029                         }
2030                         else {
2031                                 if (event->val == KM_RELEASE) { /* mouse up */
2032                                         area_split_exit(C, op);
2033                                         return OPERATOR_FINISHED;
2034                                 }
2035                         }
2036                         break;
2037
2038                 case MIDDLEMOUSE:
2039                 case TABKEY:
2040                         if (sd->previewmode == 0) {
2041                                 /* pass */
2042                         }
2043                         else {
2044                                 if (event->val == KM_PRESS) {
2045                                         if (sd->sarea) {
2046                                                 int dir = RNA_property_enum_get(op->ptr, prop_dir);
2047                                                 RNA_property_enum_set(op->ptr, prop_dir, (dir == 'v') ? 'h' : 'v');
2048                                                 area_split_preview_update_cursor(C, op);
2049                                                 update_factor = true;
2050                                         }
2051                                 }
2052                         }
2053
2054                         break;
2055
2056                 case RIGHTMOUSE: /* cancel operation */
2057                 case ESCKEY:
2058                         area_split_cancel(C, op);
2059                         return OPERATOR_CANCELLED;
2060
2061                 case LEFTCTRLKEY:
2062                         sd->do_snap = event->val == KM_PRESS;
2063                         update_factor = true;
2064                         break;
2065         }
2066
2067         if (update_factor) {
2068                 const int dir = RNA_property_enum_get(op->ptr, prop_dir);
2069
2070                 sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
2071
2072                 if (sd->previewmode == 0) {
2073                         if (sd->do_snap) {
2074                                 const int snap_loc = area_snap_calc_location(
2075                                         CTX_wm_screen(C), SNAP_FRACTION_AND_ADJACENT, sd->delta, sd->origval, dir,
2076                                         sd->bigger, sd->smaller);
2077                                 sd->delta = snap_loc - sd->origval;
2078                         }
2079                         area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, SNAP_NONE);
2080                 }
2081                 else {
2082                         if (sd->sarea) {
2083                                 ED_area_tag_redraw(sd->sarea);
2084                         }
2085                         /* area context not set */
2086                         sd->sarea = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y);
2087
2088                         if (sd->sarea) {
2089                                 ScrArea *sa = sd->sarea;
2090                                 if (dir == 'v') {
2091                                         sd->origmin = sa->v1->vec.x;
2092                                         sd->origsize = sa->v4->vec.x - sd->origmin;
2093                                 }
2094                                 else {
2095                                         sd->origmin = sa->v1->vec.y;
2096                                         sd->origsize = sa->v2->vec.y - sd->origmin;
2097                                 }
2098
2099                                 if (sd->do_snap) {
2100                                         sa->v1->editflag = sa->v2->editflag = sa->v3->editflag = sa->v4->editflag = 1;
2101
2102                                         const int snap_loc = area_snap_calc_location(
2103                                                 CTX_wm_screen(C), SNAP_FRACTION_AND_ADJACENT, sd->delta, sd->origval, dir,
2104                                                 sd->origmin + sd->origsize, -sd->origmin);
2105
2106                                         sa->v1->editflag = sa->v2->editflag = sa->v3->editflag = sa->v4->editflag = 0;
2107                                         sd->delta = snap_loc - sd->origval;
2108                                 }
2109
2110                                 ED_area_tag_redraw(sd->sarea);
2111                         }
2112
2113                         CTX_wm_screen(C)->do_draw = true;
2114                 }
2115
2116                 float fac = (float)(sd->delta + sd->origval - sd->origmin) / sd->origsize;
2117                 RNA_float_set(op->ptr, "factor", fac);
2118         }
2119
2120         return OPERATOR_RUNNING_MODAL;
2121 }
2122
2123 static const EnumPropertyItem prop_direction_items[] = {
2124         {'h', "HORIZONTAL", 0, "Horizontal", ""},
2125         {'v', "VERTICAL", 0, "Vertical", ""},
2126         {0, NULL, 0, NULL, NULL}
2127 };
2128
2129 static void SCREEN_OT_area_split(wmOperatorType *ot)
2130 {
2131         ot->name = "Split Area";
2132         ot->description = "Split selected area into new windows";
2133         ot->idname = "SCREEN_OT_area_split";
2134
2135         ot->exec = area_split_exec;
2136         ot->invoke = area_split_invoke;
2137         ot->modal = area_split_modal;
2138         ot->cancel = area_split_cancel;
2139
2140         ot->poll = screen_active_editable;
2141
2142         /* flags */
2143         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2144
2145         /* rna */
2146         RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
2147         RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
2148         RNA_def_int_vector(ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
2149 }
2150
2151 /** \} */
2152
2153 /* -------------------------------------------------------------------- */
2154 /** \name Scale Region Edge Operator
2155  * \{ */
2156
2157 typedef struct RegionMoveData {
2158         AZone *az;
2159         ARegion *ar;
2160         ScrArea *sa;
2161         int bigger, smaller, origval;
2162         int origx, origy;
2163         int maxsize;
2164         AZEdge edge;
2165
2166 } RegionMoveData;
2167
2168
2169 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
2170 {
2171         ARegion *ar;
2172         int dist;
2173
2174         if (edge == AE_RIGHT_TO_TOPLEFT || edge == AE_LEFT_TO_TOPRIGHT) {
2175                 dist = BLI_rcti_size_x(&sa->totrct);
2176         }
2177         else {  /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
2178                 dist = BLI_rcti_size_y(&sa->totrct);
2179         }
2180
2181         /* subtractwidth of regions on opposite side
2182          * prevents dragging regions into other opposite regions */
2183         for (ar = sa->regionbase.first; ar; ar = ar->next) {
2184                 if (ar == scalear)
2185                         continue;
2186
2187                 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
2188                         dist -= ar->winy;
2189                 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
2190                         dist -= ar->winy;
2191                 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
2192                         dist -= ar->winx;
2193                 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
2194                         dist -= ar->winx;
2195
2196                 /* case of regions in regions, like operator properties panel */
2197                 /* these can sit on top of other regions such as headers, so account for this */
2198                 else if (edge == AE_BOTTOM_TO_TOPLEFT && scalear->alignment & RGN_ALIGN_TOP &&
2199                          ar->alignment == RGN_ALIGN_TOP && ar->regiontype == RGN_TYPE_HEADER)
2200                 {
2201                         dist -= ar->winy;
2202                 }
2203                 else if (edge == AE_TOP_TO_BOTTOMRIGHT && scalear->alignment & RGN_ALIGN_BOTTOM &&
2204                          ar->alignment == RGN_ALIGN_BOTTOM && ar->regiontype == RGN_TYPE_HEADER)
2205                 {
2206                         dist -= ar->winy;
2207                 }
2208         }
2209
2210         return dist;
2211 }
2212
2213 static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2214 {
2215         sActionzoneData *sad = event->customdata;
2216         AZone *az;
2217
2218         if (event->type != EVT_ACTIONZONE_REGION) {
2219                 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");
2220                 return OPERATOR_CANCELLED;
2221         }
2222
2223         az = sad->az;
2224
2225         if (az->ar) {
2226                 RegionMoveData *rmd = MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
2227                 int maxsize;
2228
2229                 op->customdata = rmd;
2230
2231                 rmd->az = az;
2232                 rmd->ar = az->ar;
2233                 rmd->sa = sad->sa1;
2234                 rmd->edge = az->edge;
2235                 rmd->origx = event->x;
2236                 rmd->origy = event->y;
2237                 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
2238
2239                 /* if not set we do now, otherwise it uses type */
2240                 if (rmd->ar->sizex == 0)
2241                         rmd->ar->sizex = rmd->ar->winx;
2242                 if (rmd->ar->sizey == 0)
2243                         rmd->ar->sizey = rmd->ar->winy;
2244
2245                 /* now copy to regionmovedata */
2246                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
2247                         rmd->origval = rmd->ar->sizex;
2248                 }
2249                 else {
2250                         rmd->origval = rmd->ar->sizey;
2251                 }
2252
2253                 /* limit headers to standard height for now */
2254                 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
2255                         maxsize = ED_area_headersize();
2256                 else
2257                         maxsize = 1000;
2258
2259                 CLAMP(rmd->maxsize, 0, maxsize);
2260
2261                 /* add temp handler */
2262                 WM_event_add_modal_handler(C, op);
2263
2264                 return OPERATOR_RUNNING_MODAL;
2265         }
2266
2267         return OPERATOR_FINISHED;
2268 }
2269
2270 static int region_scale_get_maxsize(RegionMoveData *rmd)
2271 {
2272         int maxsize = 0;
2273
2274         if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
2275                 return  (int) ( (rmd->sa->winx / UI_DPI_FAC) - UI_UNIT_X);
2276         }
2277
2278         if (rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) {
2279                 /* this calculation seems overly verbose
2280                  * can someone explain why this method is necessary? - campbell */
2281                 const bool top_header = ED_area_header_alignment(rmd->sa) == RGN_ALIGN_TOP;
2282                 maxsize = rmd->maxsize - ((top_header) ? UI_UNIT_Y * 2 : UI_UNIT_Y) - (UI_UNIT_Y / 4);
2283         }
2284
2285         return maxsize;
2286 }
2287
2288 static void region_scale_validate_size(RegionMoveData *rmd)
2289 {
2290         if ((rmd->ar->flag & RGN_FLAG_HIDDEN) == 0) {
2291                 short *size, maxsize = -1;
2292
2293
2294                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT)
2295                         size = &rmd->ar->sizex;
2296                 else
2297                         size = &rmd->ar->sizey;
2298
2299                 maxsize = region_scale_get_maxsize(rmd);
2300
2301                 if (*size > maxsize && maxsize > 0)
2302                         *size = maxsize;
2303         }
2304 }
2305
2306 static void region_scale_toggle_hidden(bContext *C, RegionMoveData *rmd)
2307 {
2308         /* hidden areas may have bad 'View2D.cur' value,
2309          * correct before displaying. see T45156 */
2310         if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
2311                 UI_view2d_curRect_validate(&rmd->ar->v2d);
2312         }
2313
2314         region_toggle_hidden(C, rmd->ar, 0);
2315         region_scale_validate_size(rmd);
2316 }
2317
2318 static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
2319 {
2320         RegionMoveData *rmd = op->customdata;
2321         int delta;
2322
2323         /* execute the events */
2324         switch (event->type) {
2325                 case MOUSEMOVE:
2326                 {
2327                         const float aspect = BLI_rctf_size_x(&rmd->ar->v2d.cur) / (BLI_rcti_size_x(&rmd->ar->v2d.mask) + 1);
2328                         const int snap_size_threshold = (U.widget_unit * 2) / aspect;
2329                         if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
2330                                 delta = event->x - rmd->origx;
2331                                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT) delta = -delta;
2332
2333                                 /* region sizes now get multiplied */
2334                                 delta /= UI_DPI_FAC;
2335
2336                                 rmd->ar->sizex = rmd->origval + delta;
2337
2338                                 if (rmd->ar->type->snap_size) {
2339                                         short sizex_test = rmd->ar->type->snap_size(rmd->ar, rmd->ar->sizex, 0);
2340                                         if (ABS(rmd->ar->sizex - sizex_test) < snap_size_threshold) {
2341                                                 rmd->ar->sizex = sizex_test;
2342                                         }
2343                                 }
2344                                 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
2345
2346                                 if (rmd->ar->sizex < UI_UNIT_X) {
2347                                         rmd->ar->sizex = rmd->origval;
2348                                         if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
2349                                                 region_scale_toggle_hidden(C, rmd);
2350                                 }
2351                                 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
2352                                         region_scale_toggle_hidden(C, rmd);
2353                         }
2354                         else {
2355                                 int maxsize = region_scale_get_maxsize(rmd);
2356                                 delta = event->y - rmd->origy;
2357                                 if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) delta = -delta;
2358
2359                                 /* region sizes now get multiplied */
2360                                 delta /= UI_DPI_FAC;
2361
2362                                 rmd->ar->sizey = rmd->origval + delta;
2363
2364                                 if (rmd->ar->type->snap_size) {
2365                                         short sizey_test = rmd->ar->type->snap_size(rmd->ar, rmd->ar->sizey, 1);
2366                                         if (ABS(rmd->ar->sizey - sizey_test) < snap_size_threshold) {
2367                                                 rmd->ar->sizey = sizey_test;
2368                                         }
2369                                 }
2370                                 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
2371
2372                                 /* note, 'UI_UNIT_Y/4' means you need to drag the header almost
2373                                  * all the way down for it to become hidden, this is done
2374                                  * otherwise its too easy to do this by accident */
2375                                 if (rmd->ar->sizey < UI_UNIT_Y / 4) {
2376                                         rmd->ar->sizey = rmd->origval;
2377                                         if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
2378                                                 region_scale_toggle_hidden(C, rmd);
2379                                 }
2380                                 else if (maxsize > 0 && (rmd->ar->sizey > maxsize))
2381                                         rmd->ar->sizey = maxsize;
2382                                 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
2383                                         region_scale_toggle_hidden(C, rmd);
2384                         }
2385                         ED_area_tag_redraw(rmd->sa);
2386                         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2387
2388                         break;
2389                 }
2390                 case LEFTMOUSE:
2391                         if (event->val == KM_RELEASE) {
2392
2393                                 if (ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
2394                                         if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
2395                                                 region_scale_toggle_hidden(C, rmd);
2396                                         }
2397                                         else if (rmd->ar->flag & RGN_FLAG_TOO_SMALL) {
2398                                                 region_scale_validate_size(rmd);
2399                                         }
2400
2401                                         ED_area_tag_redraw(rmd->sa);
2402                                         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2403                                 }
2404                                 MEM_freeN(op->customdata);
2405                                 op->customdata = NULL;
2406
2407                                 return OPERATOR_FINISHED;
2408                         }
2409                         break;
2410
2411                 case ESCKEY:
2412                         break;
2413         }
2414
2415         return OPERATOR_RUNNING_MODAL;
2416 }
2417
2418 static void region_scale_cancel(bContext *UNUSED(C), wmOperator *op)
2419 {
2420         MEM_freeN(op->customdata);
2421         op->customdata = NULL;
2422 }
2423
2424 static void SCREEN_OT_region_scale(wmOperatorType *ot)
2425 {
2426         /* identifiers */
2427         ot->name = "Scale Region Size";
2428         ot->description = "Scale selected area";
2429         ot->idname = "SCREEN_OT_region_scale";
2430
2431         ot->invoke = region_scale_invoke;
2432         ot->modal = region_scale_modal;
2433         ot->cancel = region_scale_cancel;
2434
2435         ot->poll = ED_operator_areaactive;
2436
2437         /* flags */
2438         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2439 }
2440
2441 /** \} */
2442
2443 /* -------------------------------------------------------------------- */
2444 /** \name Frame Change Operator
2445  * \{ */
2446
2447 static void areas_do_frame_follow(bContext *C, bool middle)
2448 {
2449         bScreen *scr = CTX_wm_screen(C);
2450         Scene *scene = CTX_data_scene(C);
2451         wmWindowManager *wm = CTX_wm_manager(C);
2452         for (wmWindow *window = wm->windows.first; window; window = window->next) {
2453                 const bScreen *screen = WM_window_get_active_screen(window);
2454
2455                 for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
2456                         for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
2457                                 /* do follow here if editor type supports it */
2458                                 if ((scr->redraws_flag & TIME_FOLLOW)) {
2459                                         if ((ar->regiontype == RGN_TYPE_WINDOW &&
2460                                              ELEM(sa->spacetype, SPACE_SEQ, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) ||
2461                                             (sa->spacetype == SPACE_CLIP && ar->regiontype == RGN_TYPE_PREVIEW))
2462                                         {
2463                                                 float w = BLI_rctf_size_x(&ar->v2d.cur);
2464
2465                                                 if (middle) {
2466                                                         if ((scene->r.cfra < ar->v2d.cur.xmin) || (scene->r.cfra > ar->v2d.cur.xmax)) {
2467                                                                 ar->v2d.cur.xmax = scene->r.cfra + (w / 2);
2468                                                                 ar->v2d.cur.xmin = scene->r.cfra - (w / 2);
2469                                                         }
2470                                                 }
2471                                                 else {
2472                                                         if (scene->r.cfra < ar->v2d.cur.xmin) {
2473                                                                 ar->v2d.cur.xmax = scene->r.cfra;
2474                                                                 ar->v2d.cur.xmin = ar->v2d.cur.xmax - w;
2475                                                         }
2476                                                         else if (scene->r.cfra > ar->v2d.cur.xmax) {
2477                                                                 ar->v2d.cur.xmin = scene->r.cfra;
2478                                                                 ar->v2d.cur.xmax = ar->v2d.cur.xmin + w;
2479                                                         }
2480                                                 }
2481                                         }
2482                                 }
2483                         }
2484                 }
2485         }
2486 }
2487
2488 /* function to be called outside UI context, or for redo */
2489 static int frame_offset_exec(bContext *C, wmOperator *op)
2490 {
2491         Main *bmain = CTX_data_main(C);
2492         Scene *scene = CTX_data_scene(C);
2493         int delta;
2494
2495         delta = RNA_int_get(op->ptr, "delta");
2496
2497         CFRA += delta;
2498         FRAMENUMBER_MIN_CLAMP(CFRA);
2499         SUBFRA = 0.f;
2500
2501         areas_do_frame_follow(C, false);
2502
2503         BKE_sound_seek_scene(bmain, scene);
2504
2505         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2506
2507         return OPERATOR_FINISHED;
2508 }
2509
2510 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
2511 {
2512         ot->name = "Frame Offset";
2513         ot->idname = "SCREEN_OT_frame_offset";
2514         ot->description = "Move current frame forward/backward by a given number";
2515
2516         ot->exec = frame_offset_exec;
2517
2518         ot->poll = ED_operator_screenactive_norender;
2519         ot->flag = OPTYPE_UNDO_GROUPED;
2520         ot->undo_group = "Frame Change";
2521
2522         /* rna */
2523         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2524 }
2525
2526 /** \} */
2527
2528 /* -------------------------------------------------------------------- */
2529 /** \name Frame Jump Operator
2530  * \{ */
2531
2532 /* function to be called outside UI context, or for redo */
2533 static int frame_jump_exec(bContext *C, wmOperator *op)
2534 {
2535         Main *bmain = CTX_data_main(C);
2536         Scene *scene = CTX_data_scene(C);
2537         wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
2538
2539         /* Don't change CFRA directly if animtimer is running as this can cause
2540          * first/last frame not to be actually shown (bad since for example physics
2541          * simulations aren't reset properly).
2542          */
2543         if (animtimer) {
2544                 ScreenAnimData *sad = animtimer->customdata;
2545
2546                 sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
2547
2548                 if (RNA_boolean_get(op->ptr, "end"))
2549                         sad->nextfra = PEFRA;
2550                 else
2551                         sad->nextfra = PSFRA;
2552         }
2553         else {
2554                 if (RNA_boolean_get(op->ptr, "end"))
2555                         CFRA = PEFRA;
2556                 else
2557                         CFRA = PSFRA;
2558
2559                 areas_do_frame_follow(C, true);
2560
2561                 BKE_sound_seek_scene(bmain, scene);
2562
2563                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2564         }
2565
2566         return OPERATOR_FINISHED;
2567 }
2568
2569 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
2570 {
2571         ot->name = "Jump to Endpoint";
2572         ot->description = "Jump to first/last frame in frame range";
2573         ot->idname = "SCREEN_OT_frame_jump";
2574
2575         ot->exec = frame_jump_exec;
2576
2577         ot->poll = ED_operator_screenactive_norender;
2578         ot->flag = OPTYPE_UNDO_GROUPED;
2579         ot->undo_group = "Frame Change";
2580
2581         /* rna */
2582         RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range");
2583 }
2584
2585 /** \} */
2586
2587 /* -------------------------------------------------------------------- */
2588 /** \name Jump to Key-Frame Operator
2589  * \{ */
2590
2591 /* function to be called outside UI context, or for redo */
2592 static int keyframe_jump_exec(bContext *C, wmOperator *op)
2593 {
2594         Main *bmain = CTX_data_main(C);
2595         Scene *scene = CTX_data_scene(C);
2596         Object *ob = CTX_data_active_object(C);
2597         bDopeSheet ads = {NULL};
2598         DLRBT_Tree keys;
2599         ActKeyColumn *ak;
2600         float cfra;
2601         const bool next = RNA_boolean_get(op->ptr, "next");
2602         bool done = false;
2603
2604         /* sanity checks */
2605         if (scene == NULL)
2606                 return OPERATOR_CANCELLED;
2607
2608         cfra = (float)(CFRA);
2609
2610         /* init binarytree-list for getting keyframes */
2611         BLI_dlrbTree_init(&keys);
2612
2613         /* seed up dummy dopesheet context with flags to perform necessary filtering */
2614         if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
2615                 /* only selected channels are included */
2616                 ads.filterflag |= ADS_FILTER_ONLYSEL;
2617         }
2618
2619         /* populate tree with keyframe nodes */
2620         scene_to_keylist(&ads, scene, &keys, NULL);
2621         gpencil_to_keylist(&ads, scene->gpd, &keys);
2622
2623         if (ob) {
2624                 ob_to_keylist(&ads, ob, &keys, NULL);
2625                 gpencil_to_keylist(&ads, ob->gpd, &keys);
2626         }
2627
2628         {
2629                 Mask *mask = CTX_data_edit_mask(C);
2630                 if (mask) {
2631                         MaskLayer *masklay = BKE_mask_layer_active(mask);
2632                         mask_to_keylist(&ads, masklay, &keys);
2633                 }
2634         }
2635
2636         /* build linked-list for searching */
2637         BLI_dlrbTree_linkedlist_sync(&keys);
2638
2639         /* find matching keyframe in the right direction */
2640         if (next)
2641                 ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
2642         else
2643                 ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
2644
2645         while ((ak != NULL) && (done == false)) {
2646                 if (CFRA != (int)ak->cfra) {
2647                         /* this changes the frame, so set the frame and we're done */
2648                         CFRA = (int)ak->cfra;
2649                         done = true;
2650                 }
2651                 else {
2652                         /* take another step... */
2653                         if (next) {
2654                                 ak = ak->next;
2655                         }
2656                         else {
2657                                 ak = ak->prev;
2658                         }
2659                 }
2660         }
2661
2662         /* free temp stuff */
2663         BLI_dlrbTree_free(&keys);
2664
2665         /* any success? */
2666         if (done == false) {
2667                 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
2668
2669                 return OPERATOR_CANCELLED;
2670         }
2671         else {
2672                 areas_do_frame_follow(C, true);
2673
2674                 BKE_sound_seek_scene(bmain, scene);
2675
2676                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2677
2678                 return OPERATOR_FINISHED;
2679         }
2680 }
2681
2682 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
2683 {
2684         ot->name = "Jump to Keyframe";
2685         ot->description = "Jump to previous/next keyframe";
2686         ot->idname = "SCREEN_OT_keyframe_jump";
2687
2688         ot->exec = keyframe_jump_exec;
2689
2690         ot->poll = ED_operator_screenactive_norender;
2691         ot->flag = OPTYPE_UNDO_GROUPED;
2692         ot->undo_group = "Frame Change";
2693
2694         /* properties */
2695         RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
2696 }
2697
2698 /** \} */
2699
2700 /* -------------------------------------------------------------------- */
2701 /** \name Jump to Marker Operator
2702  * \{ */
2703
2704 /* function to be called outside UI context, or for redo */
2705 static int marker_jump_exec(bContext *C, wmOperator *op)
2706 {
2707         Main *bmain = CTX_data_main(C);
2708         Scene *scene = CTX_data_scene(C);
2709         TimeMarker *marker;
2710         int closest = CFRA;
2711         const bool next = RNA_boolean_get(op->ptr, "next");
2712         bool found = false;
2713
2714         /* find matching marker in the right direction */
2715         for (marker = scene->markers.first; marker; marker = marker->next) {
2716                 if (next) {
2717                         if ((marker->frame > CFRA) && (!found || closest > marker->frame)) {
2718                                 closest = marker->frame;
2719                                 found = true;
2720                         }
2721                 }
2722                 else {
2723                         if ((marker->frame < CFRA) && (!found || closest < marker->frame)) {
2724                                 closest = marker->frame;
2725                                 found = true;
2726                         }
2727                 }
2728         }
2729
2730         /* any success? */
2731         if (!found) {
2732                 BKE_report(op->reports, RPT_INFO, "No more markers to jump to in this direction");
2733
2734                 return OPERATOR_CANCELLED;
2735         }
2736         else {
2737                 CFRA = closest;
2738
2739                 areas_do_frame_follow(C, true);
2740
2741                 BKE_sound_seek_scene(bmain, scene);
2742
2743                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2744
2745                 return OPERATOR_FINISHED;
2746         }
2747 }
2748
2749 static void SCREEN_OT_marker_jump(wmOperatorType *ot)
2750 {
2751         ot->name = "Jump to Marker";
2752         ot->description = "Jump to previous/next marker";
2753         ot->idname = "SCREEN_OT_marker_jump";
2754
2755         ot->exec = marker_jump_exec;
2756
2757         ot->poll = ED_operator_screenactive_norender;
2758         ot->flag = OPTYPE_UNDO_GROUPED;
2759         ot->undo_group = "Frame Change";
2760
2761         /* properties */
2762         RNA_def_boolean(ot->srna, "next", true, "Next Marker", "");
2763 }
2764
2765 /** \} */
2766
2767 /* -------------------------------------------------------------------- */
2768 /** \name Set Screen Operator
2769  * \{ */
2770
2771 /* function to be called outside UI context, or for redo */
2772 static int screen_set_exec(bContext *C, wmOperator *op)
2773 {
2774         WorkSpace *workspace = CTX_wm_workspace(C);
2775         int delta = RNA_int_get(op->ptr, "delta");
2776
2777         if (ED_workspace_layout_cycle(workspace, delta, C)) {
2778                 return OPERATOR_FINISHED;
2779         }
2780
2781         return OPERATOR_CANCELLED;
2782 }
2783
2784 static void SCREEN_OT_screen_set(wmOperatorType *ot)
2785 {
2786         ot->name = "Set Screen";
2787         ot->description = "Cycle through available screens";
2788         ot->idname = "SCREEN_OT_screen_set";
2789
2790         ot->exec = screen_set_exec;
2791         ot->poll = ED_operator_screenactive;
2792
2793         /* rna */
2794         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2795 }
2796
2797 /** \} */
2798
2799 /* -------------------------------------------------------------------- */
2800 /** \name Screen Full-Area Operator
2801  * \{ */
2802
2803 /* function to be called outside UI context, or for redo */
2804 static int screen_maximize_area_exec(bContext *C, wmOperator *op)
2805 {
2806         bScreen *screen = CTX_wm_screen(C);
2807         ScrArea *sa = NULL;
2808         const bool hide_panels = RNA_boolean_get(op->ptr, "use_hide_panels");
2809
2810         /* search current screen for 'fullscreen' areas */
2811         /* prevents restoring info header, when mouse is over it */
2812         for (sa = screen->areabase.first; sa; sa = sa->next) {
2813                 if (sa->full) break;
2814         }
2815
2816         if (sa == NULL) {
2817                 sa = CTX_wm_area(C);
2818         }
2819
2820         if (hide_panels) {
2821                 if (!ELEM(screen->state, SCREENNORMAL, SCREENFULL)) {
2822                         return OPERATOR_CANCELLED;
2823                 }
2824                 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENFULL);
2825         }
2826         else {
2827                 if (!ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) {
2828                         return OPERATOR_CANCELLED;
2829                 }
2830                 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED);
2831         }
2832
2833         return OPERATOR_FINISHED;
2834 }
2835
2836 static bool screen_maximize_area_poll(bContext *C)
2837 {
2838         const bScreen *screen = CTX_wm_screen(C);
2839         const ScrArea *area = CTX_wm_area(C);
2840         return ED_operator_areaactive(C) &&
2841                 /* Don't allow maximizing global areas but allow minimizing from them. */
2842                ((screen->state != SCREENNORMAL) || !ED_area_is_global(area));
2843 }
2844
2845 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
2846 {
2847         PropertyRNA *prop;
2848
2849         ot->name = "Toggle Maximize Area";
2850         ot->description = "Toggle display selected area as fullscreen/maximized";
2851         ot->idname = "SCREEN_OT_screen_full_area";
2852
2853         ot->exec = screen_maximize_area_exec;
2854         ot->poll = screen_maximize_area_poll;
2855         ot->flag = 0;
2856
2857         prop = RNA_def_boolean(ot->srna, "use_hide_panels", false, "Hide Panels", "Hide all the panels");
2858         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2859 }
2860
2861 /** \} */
2862
2863 /* -------------------------------------------------------------------- */
2864 /** \name Screen Join-Area Operator
2865  * \{ */
2866
2867 /* operator state vars used:
2868  * x1, y1     mouse coord in first area, which will disappear
2869  * x2, y2     mouse coord in 2nd area, which will become joined
2870  *
2871  * functions:
2872  *
2873  * init()   find edge based on state vars
2874  * test if the edge divides two areas,
2875  * store active and nonactive area,
2876  *
2877  * apply()  do the actual join
2878  *
2879  * exit()       cleanup, send notifier
2880  *
2881  * callbacks:
2882  *
2883  * exec()       calls init, apply, exit
2884  *
2885  * invoke() sets mouse coords in x,y
2886  * call init()
2887  * add modal handler
2888  *
2889  * modal()      accept modal events while doing it
2890  * call apply() with active window and nonactive window
2891  * call exit() and remove handler when LMB confirm
2892  */
2893
2894 typedef struct sAreaJoinData {
2895         ScrArea *sa1;        /* first area to be considered */
2896         ScrArea *sa2;        /* second area to be considered */
2897         void *draw_callback; /* call `ED_screen_draw_join_shape` */
2898
2899 } sAreaJoinData;
2900
2901
2902 static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata)
2903 {
2904         const wmOperator *op = userdata;
2905
2906         sAreaJoinData *sd = op->customdata;
2907         if (sd->sa1 && sd->sa2) {
2908                 ED_screen_draw_join_shape(sd->sa1, sd->sa2);
2909         }
2910 }
2911
2912 /* validate selection inside screen, set variables OK */
2913 /* return 0: init failed */
2914 /* XXX todo: find edge based on (x,y) and set other area? */
2915 static int area_join_init(bContext *C, wmOperator *op)
2916 {
2917         const wmWindow *win = CTX_wm_window(C);
2918         bScreen *screen = CTX_wm_screen(C);
2919         ScrArea *sa1, *sa2;
2920         sAreaJoinData *jd = NULL;
2921         int x1, y1;
2922         int x2, y2;
2923         int shared = 0;
2924
2925         /* required properties, make negative to get return 0 if not set by caller */
2926         x1 = RNA_int_get(op->ptr, "min_x");
2927         y1 = RNA_int_get(op->ptr, "min_y");
2928         x2 = RNA_int_get(op->ptr, "max_x");
2929         y2 = RNA_int_get(op->ptr, "max_y");
2930
2931         sa1 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, x1, y1);
2932         if (sa1 == NULL) {
2933                 sa1 = BKE_screen_area_map_find_area_xy(&win->global_areas, SPACE_TYPE_ANY, x1, y1);
2934         }
2935         sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, x2, y2);
2936         if (sa2 == NULL) {
2937                 sa2 = BKE_screen_area_map_find_area_xy(&win->global_areas, SPACE_TYPE_ANY, x2, y2);
2938         }
2939         if ((sa1 && ED_area_is_global(sa1)) || (sa2 && ED_area_is_global(sa2))) {
2940                 BKE_report(op->reports, RPT_ERROR, "Global areas (Top Bar, Status Bar) do not support joining");
2941                 return 0;
2942         }
2943         else if (sa1 == NULL || sa2 == NULL || sa1 == sa2) {
2944                 return 0;
2945         }
2946
2947         /* do areas share an edge? */
2948         if (sa1->v1 == sa2->v1 || sa1->v1 == sa2->v2 || sa1->v1 == sa2->v3 || sa1->v1 == sa2->v4) shared++;
2949         if (sa1->v2 == sa2->v1 || sa1->v2 == sa2->v2 || sa1->v2 == sa2->v3 || sa1->v2 == sa2->v4) shared++;
2950         if (sa1->v3 == sa2->v1 || sa1->v3 == sa2->v2 || sa1->v3 == sa2->v3 || sa1->v3 == sa2->v4) shared++;
2951         if (sa1->v4 == sa2->v1 || sa1->v4 == sa2->v2 || sa1->v4 == sa2->v3 || sa1->v4 == sa2->v4) shared++;
2952         if (shared != 2) {
2953                 printf("areas don't share edge\n");
2954                 return 0;
2955         }
2956
2957         jd = (sAreaJoinData *)MEM_callocN(sizeof(sAreaJoinData), "op_area_join");
2958
2959         jd->sa1 = sa1;
2960         jd->sa2 = sa2;
2961
2962         op->customdata = jd;
2963
2964         jd->draw_callback = WM_draw_cb_activate(CTX_wm_window(C), area_join_draw_cb, op);
2965
2966         return 1;
2967 }
2968
2969 /* apply the join of the areas (space types) */
2970 static int area_join_apply(bContext *C, wmOperator *op)
2971 {
2972         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2973         if (!jd) return 0;
2974
2975         if (!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)) {
2976                 return 0;
2977         }
2978         if (CTX_wm_area(C) == jd->sa2) {
2979                 CTX_wm_area_set(C, NULL);
2980                 CTX_wm_region_set(C, NULL);
2981         }
2982
2983         return 1;
2984 }
2985
2986 /* finish operation */
2987 static void area_join_exit(bContext *C, wmOperator *op)
2988 {
2989         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2990
2991         if (jd) {
2992                 if (jd->draw_callback)
2993                         WM_draw_cb_exit(CTX_wm_window(C), jd->draw_callback);
2994
2995                 MEM_freeN(jd);
2996                 op->customdata = NULL;
2997         }
2998
2999         /* this makes sure aligned edges will result in aligned grabbing */
3000         BKE_screen_remove_double_scredges(CTX_wm_screen(C));
3001         BKE_screen_remove_unused_scredges(CTX_wm_screen(C));
3002         BKE_screen_remove_unused_scrverts(CTX_wm_screen(C));
3003 }
3004
3005 static int area_join_exec(bContext *C, wmOperator *op)
3006 {
3007         if (!area_join_init(C, op))
3008                 return OPERATOR_CANCELLED;
3009
3010         area_join_apply(C, op);
3011         area_join_exit(C, op);
3012
3013         return OPERATOR_FINISHED;
3014 }
3015
3016 /* interaction callback */
3017 static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3018 {
3019
3020         if (event->type == EVT_ACTIONZONE_AREA) {
3021                 sActionzoneData *sad = event->customdata;
3022
3023                 if (sad == NULL || sad->modifier > 0) {
3024                         return OPERATOR_PASS_THROUGH;
3025                 }
3026
3027                 /* verify *sad itself */
3028                 if (sad->sa1 == NULL || sad->sa2 == NULL)
3029                         return OPERATOR_PASS_THROUGH;
3030
3031                 /* is this our *sad? if areas equal it should be passed on */
3032                 if (sad->sa1 == sad->sa2)
3033                         return OPERATOR_PASS_THROUGH;
3034
3035                 /* prepare operator state vars */
3036                 RNA_int_set(op->ptr, "min_x", sad->x);
3037                 RNA_int_set(op->ptr, "min_y", sad->y);
3038                 RNA_int_set(op->ptr, "max_x", event->x);
3039                 RNA_int_set(op->ptr, "max_y", event->y);
3040         }
3041
3042
3043         if (!area_join_init(C, op))
3044                 return OPERATOR_CANCELLED;
3045
3046         /* add temp handler */
3047         WM_event_add_modal_handler(C, op);
3048
3049         return OPERATOR_RUNNING_MODAL;
3050 }
3051
3052 static void area_join_cancel(bContext *C, wmOperator *op)
3053 {
3054         WM_event_add_notifier(C, NC_WINDOW, NULL);
3055
3056         area_join_exit(C, op);
3057 }
3058
3059 /* modal callback while selecting area (space) that will be removed */
3060 static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
3061 {
3062         bScreen *sc = CTX_wm_screen(C);
3063         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
3064
3065         /* execute the events */
3066         switch (event->type) {
3067
3068                 case MOUSEMOVE:
3069                 {
3070                         ScrArea *sa = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y);
3071                         int dir;
3072
3073                         if (sa) {
3074                                 if (jd->sa1 != sa) {
3075                                         dir = area_getorientation(jd->sa1, sa);
3076                                         if (dir != -1) {
3077                                                 jd->sa2 = sa;
3078                                         }
3079                                         else {
3080                                                 /* we are not bordering on the previously selected area
3081                                                  * we check if area has common border with the one marked for removal
3082                                                  * in this case we can swap areas.
3083                                                  */
3084                                                 dir = area_getorientation(sa, jd->sa2);
3085                                                 if (dir != -1) {
3086                                                         jd->sa1 = jd->sa2;
3087                                                         jd->sa2 = sa;
3088                                                 }
3089                                                 else {
3090                                                         jd->sa2 = NULL;
3091                                                 }
3092                                         }
3093                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
3094                                 }
3095                                 else {
3096                                         /* we are back in the area previously selected for keeping
3097                                          * we swap the areas if possible to allow user to choose */
3098                                         if (jd->sa2 != NULL) {
3099                                                 jd->sa1 = jd->sa2;
3100                                                 jd->sa2 = sa;
3101                                                 dir = area_getorientation(jd->sa1, jd->sa2);
3102                                                 if (dir == -1) {
3103                                                         printf("oops, didn't expect that!\n");
3104                                                 }
3105                                         }
3106                                         else {
3107                                                 dir = area_getorientation(jd->sa1, sa);
3108                                                 if (dir != -1) {
3109                                                         jd->sa2 = sa;
3110                                                 }
3111                                         }
3112                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
3113                                 }
3114                         }
3115                         break;
3116                 }
3117                 case LEFTMOUSE:
3118                         if (event->val == KM_RELEASE) {
3119                                 ED_area_tag_redraw(jd->sa1);
3120                                 ED_area_tag_redraw(jd->sa2);
3121
3122                                 area_join_apply(C, op);
3123                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3124                                 area_join_exit(C, op);
3125                                 return OPERATOR_FINISHED;
3126                         }
3127                         break;
3128
3129                 case RIGHTMOUSE:
3130                 case ESCKEY:
3131                         area_join_cancel(C, op);
3132                         return OPERATOR_CANCELLED;
3133         }
3134
3135         return OPERATOR_RUNNING_MODAL;
3136 }
3137
3138 /* Operator for joining two areas (space types) */
3139 static void SCREEN_OT_area_join(wmOperatorType *ot)
3140 {
3141         /* identifiers */
3142         ot->name = "Join Area";
3143         ot->description = "Join selected areas into new window";
3144         ot->idname = "SCREEN_OT_area_join";
3145
3146         /* api callbacks */
3147         ot->exec = area_join_exec;
3148         ot->invoke = area_join_invoke;
3149         ot->modal = area_join_modal;
3150         ot->poll = screen_active_editable;
3151         ot->cancel = area_join_cancel;
3152
3153         /* flags */
3154         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
3155
3156         /* rna */
3157         RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
3158         RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
3159         RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
3160         RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
3161 }
3162
3163 /** \} */
3164
3165 /* -------------------------------------------------------------------- */
3166 /** \name Screen Area Options Operator
3167  * \{ */
3168
3169 static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3170 {
3171         const wmWindow *win = CTX_wm_window(C);
3172         const bScreen *sc = CTX_wm_screen(C);
3173         uiPopupMenu *pup;
3174         uiLayout *layout;
3175         PointerRNA ptr;
3176         ScrEdge *actedge;
3177         rcti window_rect;
3178
3179         WM_window_rect_calc(win, &window_rect);
3180         actedge = screen_geom_area_map_find_active_scredge(AREAMAP_FROM_SCREEN(sc), &window_rect, event->x, event->y);
3181
3182         if (actedge == NULL) return OPERATOR_CANCELLED;
3183
3184         pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
3185         layout = UI_popup_menu_layout(pup);
3186
3187         uiItemFullO(layout, "SCREEN_OT_area_split", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr);
3188         /* store initial mouse cursor position */
3189         RNA_int_set_array(&ptr, "cursor", &event->x);
3190
3191         uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr);
3192         /* mouse cursor on edge, '4' can fail on wide edges... */
3193         RNA_int_set(&ptr, "min_x", event->x + 4);
3194         RNA_int_set(&ptr, "min_y", event->y + 4);
3195         RNA_int_set(&ptr, "max_x", event->x - 4);
3196         RNA_int_set(&ptr, "max_y", event->y - 4);
3197
3198         UI_popup_menu_end(C, pup);
3199
3200         return OPERATOR_INTERFACE;
3201 }
3202
3203 static void SCREEN_OT_area_options(wmOperatorType *ot)
3204 {
3205         /* identifiers */
3206         ot->name = "Area Options";
3207         ot->description = "Operations for splitting and merging";
3208         ot->idname = "SCREEN_OT_area_options";
3209
3210         /* api callbacks */
3211         ot->invoke = screen_area_options_invoke;
3212
3213         ot->poll = ED_operator_screen_mainwinactive;
3214
3215         /* flags */
3216         ot->flag = OPTYPE_INTERNAL;
3217 }
3218
3219 /** \} */
3220
3221 /* -------------------------------------------------------------------- */
3222 /** \name Space Data Cleanup Operator
3223  * \{ */
3224
3225 static int spacedata_cleanup_exec(bContext *C, wmOperator *op)
3226 {
3227         Main *bmain = CTX_data_main(C);
3228         bScreen *screen;
3229         ScrArea *sa;
3230         int tot = 0;
3231
3232         for (screen = bmain->screen.first; screen; screen = screen->id.next) {
3233                 for (sa = screen->areabase.first; sa; sa = sa->next) {
3234                         if (sa->spacedata.first != sa->spacedata.last) {
3235                                 SpaceLink *sl = sa->spacedata.first;
3236
3237                                 BLI_remlink(&sa->spacedata, sl);
3238                                 tot += BLI_listbase_count(&sa->spacedata);
3239                                 BKE_spacedata_freelist(&sa->spacedata);
3240                                 BLI_addtail(&sa->spacedata, sl);
3241                         }
3242                 }
3243         }
3244         BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
3245
3246         return OPERATOR_FINISHED;
3247 }
3248
3249 static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
3250 {
3251         /* identifiers */
3252         ot->name = "Clean-up Space-data";
3253         ot->description = "Remove unused settings for invisible editors";