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