2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
22 * ***** END GPL LICENSE BLOCK *****
25 /** \file blender/editors/screen/screen_ops.c
33 #include "MEM_guardedalloc.h"
36 #include "BLI_blenlib.h"
37 #include "BLI_dlrbTree.h"
38 #include "BLI_utildefines.h"
40 #include "BLT_translation.h"
42 #include "DNA_armature_types.h"
43 #include "DNA_lattice_types.h"
44 #include "DNA_object_types.h"
45 #include "DNA_curve_types.h"
46 #include "DNA_gpencil_types.h"
47 #include "DNA_scene_types.h"
48 #include "DNA_meta_types.h"
49 #include "DNA_mask_types.h"
50 #include "DNA_node_types.h"
51 #include "DNA_userdef_types.h"
53 #include "BKE_context.h"
54 #include "BKE_customdata.h"
55 #include "BKE_global.h"
57 #include "BKE_object.h"
58 #include "BKE_report.h"
59 #include "BKE_scene.h"
60 #include "BKE_screen.h"
61 #include "BKE_editmesh.h"
62 #include "BKE_sound.h"
68 #include "ED_armature.h"
71 #include "ED_keyframes_draw.h"
72 #include "ED_object.h"
73 #include "ED_screen.h"
74 #include "ED_screen_types.h"
75 #include "ED_sequencer.h"
77 #include "ED_view3d.h"
79 #include "RNA_access.h"
80 #include "RNA_define.h"
82 #include "UI_interface.h"
83 #include "UI_resources.h"
84 #include "UI_view2d.h"
86 #include "screen_intern.h" /* own module include */
88 #define KM_MODAL_CANCEL 1
89 #define KM_MODAL_APPLY 2
90 #define KM_MODAL_STEP10 3
91 #define KM_MODAL_STEP10_OFF 4
93 /* ************** Exported Poll tests ********************** */
95 int ED_operator_regionactive(bContext *C)
97 if (CTX_wm_window(C) == NULL) return 0;
98 if (CTX_wm_screen(C) == NULL) return 0;
99 if (CTX_wm_region(C) == NULL) return 0;
103 int ED_operator_areaactive(bContext *C)
105 if (CTX_wm_window(C) == NULL) return 0;
106 if (CTX_wm_screen(C) == NULL) return 0;
107 if (CTX_wm_area(C) == NULL) return 0;
111 int ED_operator_screenactive(bContext *C)
113 if (CTX_wm_window(C) == NULL) return 0;
114 if (CTX_wm_screen(C) == NULL) return 0;
118 /* XXX added this to prevent anim state to change during renders */
119 static int ED_operator_screenactive_norender(bContext *C)
121 if (G.is_rendering) return 0;
122 if (CTX_wm_window(C) == NULL) return 0;
123 if (CTX_wm_screen(C) == NULL) return 0;
128 static int screen_active_editable(bContext *C)
130 if (ED_operator_screenactive(C)) {
131 /* no full window splitting allowed */
132 if (CTX_wm_screen(C)->state != SCREENNORMAL)
139 static ARegion *screen_find_region_type(bContext *C, int type)
141 ARegion *ar = CTX_wm_region(C);
143 /* find the header region
144 * - try context first, but upon failing, search all regions in area...
146 if ((ar == NULL) || (ar->regiontype != type)) {
147 ScrArea *sa = CTX_wm_area(C);
148 ar = BKE_area_find_region_type(sa, type);
157 /* when mouse is over area-edge */
158 int ED_operator_screen_mainwinactive(bContext *C)
161 if (CTX_wm_window(C) == NULL) return 0;
162 screen = CTX_wm_screen(C);
163 if (screen == NULL) return 0;
164 if (screen->subwinactive != screen->mainwin) return 0;
168 int ED_operator_scene_editable(bContext *C)
170 Scene *scene = CTX_data_scene(C);
171 if (scene && scene->id.lib == NULL)
176 int ED_operator_objectmode(bContext *C)
178 Scene *scene = CTX_data_scene(C);
179 Object *obact = CTX_data_active_object(C);
181 if (scene == NULL || scene->id.lib)
183 if (CTX_data_edit_object(C))
186 /* add a check for ob->mode too? */
187 if (obact && (obact->mode != OB_MODE_OBJECT))
194 static bool ed_spacetype_test(bContext *C, int type)
196 if (ED_operator_areaactive(C)) {
197 SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);
198 return sl && (sl->spacetype == type);
203 int ED_operator_view3d_active(bContext *C)
205 return ed_spacetype_test(C, SPACE_VIEW3D);
208 int ED_operator_region_view3d_active(bContext *C)
210 if (CTX_wm_region_view3d(C))
213 CTX_wm_operator_poll_msg_set(C, "expected a view3d region");
217 /* generic for any view2d which uses anim_ops */
218 int ED_operator_animview_active(bContext *C)
220 if (ED_operator_areaactive(C)) {
221 SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);
222 if (sl && (ELEM(sl->spacetype, SPACE_SEQ, SPACE_ACTION, SPACE_NLA, SPACE_IPO, SPACE_TIME)))
226 CTX_wm_operator_poll_msg_set(C, "expected a timeline/animation area to be active");
230 int ED_operator_timeline_active(bContext *C)
232 return ed_spacetype_test(C, SPACE_TIME);
235 int ED_operator_outliner_active(bContext *C)
237 return ed_spacetype_test(C, SPACE_OUTLINER);
240 int ED_operator_outliner_active_no_editobject(bContext *C)
242 if (ed_spacetype_test(C, SPACE_OUTLINER)) {
243 Object *ob = ED_object_active_context(C);
244 Object *obedit = CTX_data_edit_object(C);
245 if (ob && ob == obedit)
253 int ED_operator_file_active(bContext *C)
255 return ed_spacetype_test(C, SPACE_FILE);
258 int ED_operator_action_active(bContext *C)
260 return ed_spacetype_test(C, SPACE_ACTION);
263 int ED_operator_buttons_active(bContext *C)
265 return ed_spacetype_test(C, SPACE_BUTS);
268 int ED_operator_node_active(bContext *C)
270 SpaceNode *snode = CTX_wm_space_node(C);
272 if (snode && snode->edittree)
278 int ED_operator_node_editable(bContext *C)
280 SpaceNode *snode = CTX_wm_space_node(C);
282 if (snode && snode->edittree && snode->edittree->id.lib == NULL)
288 int ED_operator_graphedit_active(bContext *C)
290 return ed_spacetype_test(C, SPACE_IPO);
293 int ED_operator_sequencer_active(bContext *C)
295 return ed_spacetype_test(C, SPACE_SEQ);
298 int ED_operator_sequencer_active_editable(bContext *C)
300 return ed_spacetype_test(C, SPACE_SEQ) && ED_operator_scene_editable(C);
303 int ED_operator_image_active(bContext *C)
305 return ed_spacetype_test(C, SPACE_IMAGE);
308 int ED_operator_nla_active(bContext *C)
310 return ed_spacetype_test(C, SPACE_NLA);
313 int ED_operator_logic_active(bContext *C)
315 return ed_spacetype_test(C, SPACE_LOGIC);
318 int ED_operator_info_active(bContext *C)
320 return ed_spacetype_test(C, SPACE_INFO);
324 int ED_operator_console_active(bContext *C)
326 return ed_spacetype_test(C, SPACE_CONSOLE);
329 static int ed_object_hidden(Object *ob)
331 /* if hidden but in edit mode, we still display, can happen with animation */
332 return ((ob->restrictflag & OB_RESTRICT_VIEW) && !(ob->mode & OB_MODE_EDIT));
335 int ED_operator_object_active(bContext *C)
337 Object *ob = ED_object_active_context(C);
338 return ((ob != NULL) && !ed_object_hidden(ob));
341 int ED_operator_object_active_editable(bContext *C)
343 Object *ob = ED_object_active_context(C);
344 return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob));
347 int ED_operator_object_active_editable_mesh(bContext *C)
349 Object *ob = ED_object_active_context(C);
350 return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) &&
351 (ob->type == OB_MESH) && !(((ID *)ob->data)->lib));
354 int ED_operator_object_active_editable_font(bContext *C)
356 Object *ob = ED_object_active_context(C);
357 return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) &&
358 (ob->type == OB_FONT));
361 int ED_operator_editmesh(bContext *C)
363 Object *obedit = CTX_data_edit_object(C);
364 if (obedit && obedit->type == OB_MESH)
365 return NULL != BKE_editmesh_from_object(obedit);
369 int ED_operator_editmesh_view3d(bContext *C)
371 return ED_operator_editmesh(C) && ED_operator_view3d_active(C);
374 int ED_operator_editmesh_region_view3d(bContext *C)
376 if (ED_operator_editmesh(C) && CTX_wm_region_view3d(C))
379 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editmesh");
383 int ED_operator_editarmature(bContext *C)
385 Object *obedit = CTX_data_edit_object(C);
386 if (obedit && obedit->type == OB_ARMATURE)
387 return NULL != ((bArmature *)obedit->data)->edbo;
392 * \brief check for pose mode (no mixed modes)
394 * We want to enable most pose operations in weight paint mode,
395 * when it comes to transforming bones, but managing bones layers/groups
396 * can be left for pose mode only. (not weight paint mode)
398 int ED_operator_posemode_exclusive(bContext *C)
400 Object *obact = CTX_data_active_object(C);
402 if (obact && !(obact->mode & OB_MODE_EDIT)) {
404 if ((obpose = BKE_object_pose_armature_get(obact))) {
405 if (obact == obpose) {
414 /* allows for pinned pose objects to be used in the object buttons
415 * and the non-active pose object to be used in the 3D view */
416 int ED_operator_posemode_context(bContext *C)
418 Object *obpose = ED_pose_object_from_context(C);
420 if (obpose && !(obpose->mode & OB_MODE_EDIT)) {
421 if (BKE_object_pose_context_check(obpose)) {
429 int ED_operator_posemode(bContext *C)
431 Object *obact = CTX_data_active_object(C);
433 if (obact && !(obact->mode & OB_MODE_EDIT)) {
435 if ((obpose = BKE_object_pose_armature_get(obact))) {
436 if ((obact == obpose) || (obact->mode & OB_MODE_WEIGHT_PAINT)) {
445 /* wrapper for ED_space_image_show_uvedit */
446 int ED_operator_uvedit(bContext *C)
448 SpaceImage *sima = CTX_wm_space_image(C);
449 Object *obedit = CTX_data_edit_object(C);
450 return ED_space_image_show_uvedit(sima, obedit);
453 int ED_operator_uvedit_space_image(bContext *C)
455 SpaceImage *sima = CTX_wm_space_image(C);
456 Object *obedit = CTX_data_edit_object(C);
457 return sima && ED_space_image_show_uvedit(sima, obedit);
460 int ED_operator_uvmap(bContext *C)
462 Object *obedit = CTX_data_edit_object(C);
463 BMEditMesh *em = NULL;
465 if (obedit && obedit->type == OB_MESH) {
466 em = BKE_editmesh_from_object(obedit);
469 if (em && (em->bm->totface)) {
476 int ED_operator_editsurfcurve(bContext *C)
478 Object *obedit = CTX_data_edit_object(C);
479 if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
480 return NULL != ((Curve *)obedit->data)->editnurb;
484 int ED_operator_editsurfcurve_region_view3d(bContext *C)
486 if (ED_operator_editsurfcurve(C) && CTX_wm_region_view3d(C))
489 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editcurve");
493 int ED_operator_editcurve(bContext *C)
495 Object *obedit = CTX_data_edit_object(C);
496 if (obedit && obedit->type == OB_CURVE)
497 return NULL != ((Curve *)obedit->data)->editnurb;
501 int ED_operator_editcurve_3d(bContext *C)
503 Object *obedit = CTX_data_edit_object(C);
504 if (obedit && obedit->type == OB_CURVE) {
505 Curve *cu = (Curve *)obedit->data;
507 return (cu->flag & CU_3D) && (NULL != cu->editnurb);
512 int ED_operator_editsurf(bContext *C)
514 Object *obedit = CTX_data_edit_object(C);
515 if (obedit && obedit->type == OB_SURF)
516 return NULL != ((Curve *)obedit->data)->editnurb;
520 int ED_operator_editfont(bContext *C)
522 Object *obedit = CTX_data_edit_object(C);
523 if (obedit && obedit->type == OB_FONT)
524 return NULL != ((Curve *)obedit->data)->editfont;
528 int ED_operator_editlattice(bContext *C)
530 Object *obedit = CTX_data_edit_object(C);
531 if (obedit && obedit->type == OB_LATTICE)
532 return NULL != ((Lattice *)obedit->data)->editlatt;
536 int ED_operator_editmball(bContext *C)
538 Object *obedit = CTX_data_edit_object(C);
539 if (obedit && obedit->type == OB_MBALL)
540 return NULL != ((MetaBall *)obedit->data)->editelems;
544 int ED_operator_mask(bContext *C)
546 ScrArea *sa = CTX_wm_area(C);
547 if (sa && sa->spacedata.first) {
548 switch (sa->spacetype) {
551 SpaceClip *sc = sa->spacedata.first;
552 return ED_space_clip_check_show_maskedit(sc);
556 SpaceSeq *sseq = sa->spacedata.first;
557 Scene *scene = CTX_data_scene(C);
558 return ED_space_sequencer_check_show_maskedit(sseq, scene);
562 SpaceImage *sima = sa->spacedata.first;
563 Scene *scene = CTX_data_scene(C);
564 return ED_space_image_check_show_maskedit(scene, sima);
572 /* *************************** action zone operator ************************** */
574 /* operator state vars used:
579 * apply() set actionzone event
581 * exit() free customdata
587 * invoke() check if in zone
588 * add customdata, put mouseco and area in it
591 * modal() accept modal events while doing it
592 * call apply() with gesture info, active window, nonactive window
593 * call exit() and remove handler when LMB confirm
596 typedef struct sActionzoneData {
599 int x, y, gesture_dir, modifier;
602 /* quick poll to save operators to be created and handled */
603 static int actionzone_area_poll(bContext *C)
605 wmWindow *win = CTX_wm_window(C);
606 ScrArea *sa = CTX_wm_area(C);
608 if (sa && win && win->eventstate) {
609 const int *xy = &win->eventstate->x;
612 for (az = sa->actionzones.first; az; az = az->next)
613 if (BLI_rcti_isect_pt_v(&az->rect, xy))
619 /* the debug drawing of the click_rect is in area_draw_azone_fullscreen, keep both in sync */
620 static void fullscreen_click_rcti_init(rcti *rect, const short x1, const short y1, const short x2, const short y2)
622 int x = x2 - ((float) x2 - x1) * 0.5f / UI_DPI_FAC;
623 int y = y2 - ((float) y2 - y1) * 0.5f / UI_DPI_FAC;
624 float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC;
626 /* adjust the icon distance from the corner */
627 x += 36.0f / UI_DPI_FAC;
628 y += 36.0f / UI_DPI_FAC;
630 /* draws from the left bottom corner of the icon */
631 x -= UI_DPI_ICON_SIZE;
632 y -= UI_DPI_ICON_SIZE;
634 BLI_rcti_init(rect, x, x + icon_size, y, y + icon_size);
637 AZone *is_in_area_actionzone(ScrArea *sa, const int xy[2])
641 for (az = sa->actionzones.first; az; az = az->next) {
642 if (BLI_rcti_isect_pt_v(&az->rect, xy)) {
643 if (az->type == AZONE_AREA) {
644 /* no triangle intersect but a hotspot circle based on corner */
645 int radius = (xy[0] - az->x1) * (xy[0] - az->x1) + (xy[1] - az->y1) * (xy[1] - az->y1);
647 if (radius <= AZONESPOT * AZONESPOT)
650 else if (az->type == AZONE_REGION) {
653 else if (az->type == AZONE_FULLSCREEN) {
654 int mouse_radius, spot_radius, fadein_radius, fadeout_radius;
657 fullscreen_click_rcti_init(&click_rect, az->x1, az->y1, az->x2, az->y2);
659 if (BLI_rcti_isect_pt_v(&click_rect, xy)) {
663 mouse_radius = (xy[0] - az->x2) * (xy[0] - az->x2) + (xy[1] - az->y2) * (xy[1] - az->y2);
664 spot_radius = AZONESPOT * AZONESPOT;
665 fadein_radius = AZONEFADEIN * AZONEFADEIN;
666 fadeout_radius = AZONEFADEOUT * AZONEFADEOUT;
668 if (mouse_radius < spot_radius) {
671 else if (mouse_radius < fadein_radius) {
674 else if (mouse_radius < fadeout_radius) {
675 az->alpha = 1.0f - ((float)(mouse_radius - fadein_radius)) / ((float)(fadeout_radius - fadein_radius));
681 /* fade in/out but no click */
685 /* XXX force redraw to show/hide the action zone */
686 ED_area_tag_redraw(sa);
696 static void actionzone_exit(wmOperator *op)
699 MEM_freeN(op->customdata);
700 op->customdata = NULL;
703 /* send EVT_ACTIONZONE event */
704 static void actionzone_apply(bContext *C, wmOperator *op, int type)
707 wmWindow *win = CTX_wm_window(C);
708 sActionzoneData *sad = op->customdata;
710 sad->modifier = RNA_int_get(op->ptr, "modifier");
712 wm_event_init_from_window(win, &event);
714 if (type == AZONE_AREA)
715 event.type = EVT_ACTIONZONE_AREA;
716 else if (type == AZONE_FULLSCREEN)
717 event.type = EVT_ACTIONZONE_FULLSCREEN;
719 event.type = EVT_ACTIONZONE_REGION;
721 event.val = KM_NOTHING;
722 event.customdata = op->customdata;
723 event.customdatafree = true;
724 op->customdata = NULL;
726 wm_event_add(win, &event);
729 static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
731 ScrArea *sa = CTX_wm_area(C);
732 AZone *az = is_in_area_actionzone(sa, &event->x);
733 sActionzoneData *sad;
737 return OPERATOR_PASS_THROUGH;
739 /* ok we do the actionzone */
740 sad = op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
743 sad->x = event->x; sad->y = event->y;
745 /* region azone directly reacts on mouse clicks */
746 if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) {
747 actionzone_apply(C, op, sad->az->type);
749 return OPERATOR_FINISHED;
752 /* add modal handler */
753 WM_event_add_modal_handler(C, op);
755 return OPERATOR_RUNNING_MODAL;
760 static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
762 wmWindow *win = CTX_wm_window(C);
763 bScreen *sc = CTX_wm_screen(C);
764 sActionzoneData *sad = op->customdata;
765 const int winsize_x = WM_window_pixels_x(win);
766 const int winsize_y = WM_window_pixels_y(win);
768 switch (event->type) {
773 const int delta_x = (event->x - sad->x);
774 const int delta_y = (event->y - sad->y);
776 /* calculate gesture direction */
777 if (delta_y > ABS(delta_x))
778 sad->gesture_dir = 'n';
779 else if (delta_x > ABS(delta_y))
780 sad->gesture_dir = 'e';
781 else if (delta_y < -ABS(delta_x))
782 sad->gesture_dir = 's';
784 sad->gesture_dir = 'w';
786 if (sad->az->type == AZONE_AREA) {
787 /* once we drag outside the actionzone, register a gesture
788 * check we're not on an edge so join finds the other area */
789 is_gesture = ((is_in_area_actionzone(sad->sa1, &event->x) != sad->az) &&
790 (screen_find_active_scredge(sc, winsize_x, winsize_y, event->x, event->y) == NULL));
793 const int delta_min = 1;
794 is_gesture = (ABS(delta_x) > delta_min || ABS(delta_y) > delta_min);
797 /* gesture is large enough? */
799 /* second area, for join when (sa1 != sa2) */
800 sad->sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y);
801 /* apply sends event */
802 actionzone_apply(C, op, sad->az->type);
805 return OPERATOR_FINISHED;
811 return OPERATOR_CANCELLED;
814 return OPERATOR_CANCELLED;
818 return OPERATOR_RUNNING_MODAL;
821 static void actionzone_cancel(bContext *UNUSED(C), wmOperator *op)
826 static void SCREEN_OT_actionzone(wmOperatorType *ot)
829 ot->name = "Handle Area Action Zones";
830 ot->description = "Handle area action zones for mouse actions/gestures";
831 ot->idname = "SCREEN_OT_actionzone";
833 ot->invoke = actionzone_invoke;
834 ot->modal = actionzone_modal;
835 ot->poll = actionzone_area_poll;
836 ot->cancel = actionzone_cancel;
839 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
841 RNA_def_int(ot->srna, "modifier", 0, 0, 2, "Modifier", "Modifier state", 0, 2);
844 /* ************** swap area operator *********************************** */
846 /* operator state vars used:
848 * sa2 area to swap with
852 * init() set custom data for operator, based on actionzone event custom data
854 * cancel() cancel the operator
856 * exit() cleanup, send notifier
860 * invoke() gets called on shift+lmb drag in actionzone
861 * call init(), add handler
863 * modal() accept modal events while doing it
866 typedef struct sAreaSwapData {
870 static int area_swap_init(wmOperator *op, const wmEvent *event)
872 sAreaSwapData *sd = NULL;
873 sActionzoneData *sad = event->customdata;
875 if (sad == NULL || sad->sa1 == NULL)
878 sd = MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
887 static void area_swap_exit(bContext *C, wmOperator *op)
889 WM_cursor_modal_restore(CTX_wm_window(C));
891 MEM_freeN(op->customdata);
892 op->customdata = NULL;
895 static void area_swap_cancel(bContext *C, wmOperator *op)
897 area_swap_exit(C, op);
900 static int area_swap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
903 if (!area_swap_init(op, event))
904 return OPERATOR_PASS_THROUGH;
906 /* add modal handler */
907 WM_cursor_modal_set(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
908 WM_event_add_modal_handler(C, op);
910 return OPERATOR_RUNNING_MODAL;
914 static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event)
916 sActionzoneData *sad = op->customdata;
918 switch (event->type) {
920 /* second area, for join */
921 sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y);
923 case LEFTMOUSE: /* release LMB */
924 if (event->val == KM_RELEASE) {
925 if (!sad->sa2 || sad->sa1 == sad->sa2) {
926 area_swap_cancel(C, op);
927 return OPERATOR_CANCELLED;
930 ED_area_tag_redraw(sad->sa1);
931 ED_area_tag_redraw(sad->sa2);
933 ED_area_swapspace(C, sad->sa1, sad->sa2);
935 area_swap_exit(C, op);
937 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
939 return OPERATOR_FINISHED;
944 area_swap_cancel(C, op);
945 return OPERATOR_CANCELLED;
947 return OPERATOR_RUNNING_MODAL;
950 static void SCREEN_OT_area_swap(wmOperatorType *ot)
952 ot->name = "Swap Areas";
953 ot->description = "Swap selected areas screen positions";
954 ot->idname = "SCREEN_OT_area_swap";
956 ot->invoke = area_swap_invoke;
957 ot->modal = area_swap_modal;
958 ot->poll = ED_operator_areaactive;
959 ot->cancel = area_swap_cancel;
961 ot->flag = OPTYPE_BLOCKING;
964 /* *********** Duplicate area as new window operator ****************** */
966 /* operator callback */
967 static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
969 wmWindow *newwin, *win;
974 win = CTX_wm_window(C);
975 sc = CTX_wm_screen(C);
979 if (event->type == EVT_ACTIONZONE_AREA) {
980 sActionzoneData *sad = event->customdata;
983 return OPERATOR_PASS_THROUGH;
988 /* adds window to WM */
990 BLI_rcti_translate(&rect, win->posx, win->posy);
991 rect.xmax = rect.xmin + BLI_rcti_size_x(&rect) / U.pixelsize;
992 rect.ymax = rect.ymin + BLI_rcti_size_y(&rect) / U.pixelsize;
994 newwin = WM_window_open(C, &rect);
995 if (newwin == NULL) {
996 BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
1000 *newwin->stereo3d_format = *win->stereo3d_format;
1002 /* allocs new screen and adds to newly created window, using window size */
1003 newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2);
1004 newwin->screen = newsc;
1006 /* copy area to new screen */
1007 ED_area_data_copy((ScrArea *)newsc->areabase.first, sa, true);
1009 ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
1011 /* screen, areas init */
1012 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1016 if (event->type == EVT_ACTIONZONE_AREA)
1017 actionzone_exit(op);
1020 return OPERATOR_FINISHED;
1023 return OPERATOR_CANCELLED;
1027 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
1029 ot->name = "Duplicate Area into New Window";
1030 ot->description = "Duplicate selected area into new window";
1031 ot->idname = "SCREEN_OT_area_dupli";
1033 ot->invoke = area_dupli_invoke;
1034 ot->poll = ED_operator_areaactive;
1038 /* ************** move area edge operator *********************************** */
1040 /* operator state vars used:
1041 * x, y mouse coord near edge
1042 * delta movement of edge
1046 * init() set default property values, find edge based on mouse coords, test
1047 * if the edge can be moved, select edges, calculate min and max movement
1049 * apply() apply delta on selection
1051 * exit() cleanup, send notifier
1053 * cancel() cancel moving
1057 * exec() execute without any user interaction, based on properties
1058 * call init(), apply(), exit()
1060 * invoke() gets called on mouse click near edge
1061 * call init(), add handler
1063 * modal() accept modal events while doing it
1064 * call apply() with delta motion
1065 * call exit() and remove handler
1068 typedef struct sAreaMoveData {
1069 int bigger, smaller, origval, step;
1073 /* helper call to move area-edge, sets limits
1074 * need window size in order to get correct limits */
1075 static void area_move_set_limits(bScreen *sc, int dir,
1076 const int winsize_x, const int winsize_y,
1077 int *bigger, int *smaller)
1080 int areaminy = ED_area_headersize();
1083 /* we check all areas and test for free space with MINSIZE */
1084 *bigger = *smaller = 100000;
1086 for (sa = sc->areabase.first; sa; sa = sa->next) {
1091 if (sa->v1->vec.y > 0)
1092 areamin += U.pixelsize;
1093 if (sa->v2->vec.y < winsize_y - 1)
1094 areamin += U.pixelsize;
1096 y1 = sa->v2->vec.y - sa->v1->vec.y + 1 - areamin;
1098 /* if top or down edge selected, test height */
1099 if (sa->v1->editflag && sa->v4->editflag)
1100 *bigger = min_ii(*bigger, y1);
1101 else if (sa->v2->editflag && sa->v3->editflag)
1102 *smaller = min_ii(*smaller, y1);
1108 if (sa->v1->vec.x > 0)
1109 areamin += U.pixelsize;
1110 if (sa->v4->vec.x < winsize_x - 1)
1111 areamin += U.pixelsize;
1113 x1 = sa->v4->vec.x - sa->v1->vec.x + 1 - areamin;
1115 /* if left or right edge selected, test width */
1116 if (sa->v1->editflag && sa->v2->editflag)
1117 *bigger = min_ii(*bigger, x1);
1118 else if (sa->v3->editflag && sa->v4->editflag)
1119 *smaller = min_ii(*smaller, x1);
1124 /* validate selection inside screen, set variables OK */
1125 /* return 0: init failed */
1126 static int area_move_init(bContext *C, wmOperator *op)
1128 bScreen *sc = CTX_wm_screen(C);
1129 wmWindow *win = CTX_wm_window(C);
1133 const int winsize_x = WM_window_pixels_x(win);
1134 const int winsize_y = WM_window_pixels_y(win);
1137 /* required properties */
1138 x = RNA_int_get(op->ptr, "x");
1139 y = RNA_int_get(op->ptr, "y");
1142 actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, x, y);
1143 if (actedge == NULL) return 0;
1145 md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
1146 op->customdata = md;
1148 md->dir = scredge_is_horizontal(actedge) ? 'h' : 'v';
1149 if (md->dir == 'h') md->origval = actedge->v1->vec.y;
1150 else md->origval = actedge->v1->vec.x;
1152 select_connected_scredge(sc, actedge);
1153 /* now all vertices with 'flag==1' are the ones that can be moved. Move this to editflag */
1154 for (v1 = sc->vertbase.first; v1; v1 = v1->next)
1155 v1->editflag = v1->flag;
1157 area_move_set_limits(sc, md->dir, winsize_x, winsize_y, &md->bigger, &md->smaller);
1162 /* moves selected screen edge amount of delta, used by split & move */
1163 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
1165 wmWindow *win = CTX_wm_window(C);
1166 const int winsize_x = WM_window_pixels_x(win);
1167 const int winsize_y = WM_window_pixels_y(win);
1168 bScreen *sc = CTX_wm_screen(C);
1174 delta = CLAMPIS(delta, -smaller, bigger);
1176 for (v1 = sc->vertbase.first; v1; v1 = v1->next) {
1178 /* that way a nice AREAGRID */
1179 if ((dir == 'v') && v1->vec.x > 0 && v1->vec.x < winsize_x - 1) {
1181 v1->vec.x = origval + delta;
1183 if (delta != bigger && delta != -smaller) {
1184 v1->vec.x -= (v1->vec.x % AREAGRID);
1185 v1->vec.x = CLAMPIS(v1->vec.x, origval - smaller, origval + bigger);
1187 if (oldval != v1->vec.x)
1190 if ((dir == 'h') && v1->vec.y > 0 && v1->vec.y < winsize_y - 1) {
1192 v1->vec.y = origval + delta;
1194 if (delta != bigger && delta != smaller) {
1195 v1->vec.y -= (v1->vec.y % AREAGRID);
1196 v1->vec.y = CLAMPIS(v1->vec.y, origval - smaller, origval + bigger);
1198 if (oldval != v1->vec.y)
1204 /* only redraw if we actually moved a screen vert, for AREAGRID */
1206 for (sa = sc->areabase.first; sa; sa = sa->next) {
1207 if (sa->v1->editflag || sa->v2->editflag || sa->v3->editflag || sa->v4->editflag)
1208 ED_area_tag_redraw(sa);
1211 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */
1215 static void area_move_apply(bContext *C, wmOperator *op)
1217 sAreaMoveData *md = op->customdata;
1220 delta = RNA_int_get(op->ptr, "delta");
1221 area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
1224 static void area_move_exit(bContext *C, wmOperator *op)
1227 MEM_freeN(op->customdata);
1228 op->customdata = NULL;
1230 /* this makes sure aligned edges will result in aligned grabbing */
1231 removedouble_scrverts(CTX_wm_screen(C));
1232 removedouble_scredges(CTX_wm_screen(C));
1235 static int area_move_exec(bContext *C, wmOperator *op)
1237 if (!area_move_init(C, op))
1238 return OPERATOR_CANCELLED;
1240 area_move_apply(C, op);
1241 area_move_exit(C, op);
1243 return OPERATOR_FINISHED;
1246 /* interaction callback */
1247 static int area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1249 RNA_int_set(op->ptr, "x", event->x);
1250 RNA_int_set(op->ptr, "y", event->y);
1252 if (!area_move_init(C, op))
1253 return OPERATOR_PASS_THROUGH;
1255 /* add temp handler */
1256 WM_event_add_modal_handler(C, op);
1258 return OPERATOR_RUNNING_MODAL;
1261 static void area_move_cancel(bContext *C, wmOperator *op)
1264 RNA_int_set(op->ptr, "delta", 0);
1265 area_move_apply(C, op);
1266 area_move_exit(C, op);
1269 /* modal callback for while moving edges */
1270 static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
1272 sAreaMoveData *md = op->customdata;
1275 /* execute the events */
1276 switch (event->type) {
1279 x = RNA_int_get(op->ptr, "x");
1280 y = RNA_int_get(op->ptr, "y");
1282 delta = (md->dir == 'v') ? event->x - x : event->y - y;
1283 if (md->step) delta = delta - (delta % md->step);
1284 RNA_int_set(op->ptr, "delta", delta);
1286 area_move_apply(C, op);
1291 switch (event->val) {
1292 case KM_MODAL_APPLY:
1293 area_move_exit(C, op);
1294 return OPERATOR_FINISHED;
1296 case KM_MODAL_CANCEL:
1297 area_move_cancel(C, op);
1298 return OPERATOR_CANCELLED;
1300 case KM_MODAL_STEP10:
1303 case KM_MODAL_STEP10_OFF:
1311 return OPERATOR_RUNNING_MODAL;
1314 static void SCREEN_OT_area_move(wmOperatorType *ot)
1317 ot->name = "Move Area Edges";
1318 ot->description = "Move selected area edges";
1319 ot->idname = "SCREEN_OT_area_move";
1321 ot->exec = area_move_exec;
1322 ot->invoke = area_move_invoke;
1323 ot->cancel = area_move_cancel;
1324 ot->modal = area_move_modal;
1325 ot->poll = ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
1328 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1331 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1332 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1333 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1336 /* ************** split area operator *********************************** */
1339 * operator state vars:
1341 * dir direction 'v' or 'h'
1343 * operator customdata:
1344 * area pointer to (active) area
1345 * x, y last used mouse pos
1350 * init() set default property values, find area based on context
1352 * apply() split area based on state vars
1354 * exit() cleanup, send notifier
1356 * cancel() remove duplicated area
1360 * exec() execute without any user interaction, based on state vars
1361 * call init(), apply(), exit()
1363 * invoke() gets called on mouse click in action-widget
1364 * call init(), add modal handler
1365 * call apply() with initial motion
1367 * modal() accept modal events while doing it
1368 * call move-areas code with delta motion
1369 * call exit() or cancel() and remove handler
1372 typedef struct sAreaSplitData {
1373 int x, y; /* last used mouse position */
1375 int origval; /* for move areas */
1376 int bigger, smaller; /* constraints for moving new edge */
1377 int delta; /* delta move edge */
1378 int origmin, origsize; /* to calculate fac, for property storage */
1379 int previewmode; /* draw previewline, then split */
1381 ScrEdge *nedge; /* new edge */
1382 ScrArea *sarea; /* start area */
1383 ScrArea *narea; /* new area */
1387 /* generic init, menu case, doesn't need active area */
1388 static int area_split_menu_init(bContext *C, wmOperator *op)
1393 sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1394 op->customdata = sd;
1396 sd->sarea = CTX_wm_area(C);
1399 int dir = RNA_enum_get(op->ptr, "direction");
1402 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1404 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1409 /* generic init, no UI stuff here, assumes active area */
1410 static int area_split_init(bContext *C, wmOperator *op)
1412 ScrArea *sa = CTX_wm_area(C);
1414 int areaminy = ED_area_headersize() + 1;
1417 /* required context */
1418 if (sa == NULL) return 0;
1420 /* required properties */
1421 dir = RNA_enum_get(op->ptr, "direction");
1424 if (dir == 'v' && sa->winx < 2 * AREAMINX) return 0;
1425 if (dir == 'h' && sa->winy < 2 * areaminy) return 0;
1428 sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1429 op->customdata = sd;
1432 sd->origsize = dir == 'v' ? sa->winx : sa->winy;
1433 sd->origmin = dir == 'v' ? sa->totrct.xmin : sa->totrct.ymin;
1438 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1439 /* used with split operator */
1440 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1442 ScrVert *sav1 = sa->v1;
1443 ScrVert *sav2 = sa->v2;
1444 ScrVert *sav3 = sa->v3;
1445 ScrVert *sav4 = sa->v4;
1446 ScrVert *sbv1 = sb->v1;
1447 ScrVert *sbv2 = sb->v2;
1448 ScrVert *sbv3 = sb->v3;
1449 ScrVert *sbv4 = sb->v4;
1451 if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */
1452 return screen_findedge(screen, sav1, sav2);
1454 else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */
1455 return screen_findedge(screen, sav2, sav3);
1457 else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */
1458 return screen_findedge(screen, sav3, sav4);
1460 else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/
1461 return screen_findedge(screen, sav1, sav4);
1468 /* do the split, return success */
1469 static int area_split_apply(bContext *C, wmOperator *op)
1471 bScreen *sc = CTX_wm_screen(C);
1472 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1476 fac = RNA_float_get(op->ptr, "factor");
1477 dir = RNA_enum_get(op->ptr, "direction");
1479 sd->narea = area_split(sc, sd->sarea, dir, fac, 0); /* 0 = no merge */
1484 sd->nedge = area_findsharededge(sc, sd->sarea, sd->narea);
1486 /* select newly created edge, prepare for moving edge */
1487 for (sv = sc->vertbase.first; sv; sv = sv->next)
1490 sd->nedge->v1->editflag = 1;
1491 sd->nedge->v2->editflag = 1;
1493 if (dir == 'h') sd->origval = sd->nedge->v1->vec.y;
1494 else sd->origval = sd->nedge->v1->vec.x;
1496 ED_area_tag_redraw(sd->sarea);
1497 ED_area_tag_redraw(sd->narea);
1499 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1507 static void area_split_exit(bContext *C, wmOperator *op)
1509 if (op->customdata) {
1510 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1511 if (sd->sarea) ED_area_tag_redraw(sd->sarea);
1512 if (sd->narea) ED_area_tag_redraw(sd->narea);
1515 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1517 MEM_freeN(op->customdata);
1518 op->customdata = NULL;
1521 WM_cursor_modal_restore(CTX_wm_window(C));
1522 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1524 /* this makes sure aligned edges will result in aligned grabbing */
1525 removedouble_scrverts(CTX_wm_screen(C));
1526 removedouble_scredges(CTX_wm_screen(C));
1530 /* UI callback, adds new handler */
1531 static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1533 wmWindow *win = CTX_wm_window(C);
1534 bScreen *sc = CTX_wm_screen(C);
1536 const int winsize_x = WM_window_pixels_x(win);
1537 const int winsize_y = WM_window_pixels_y(win);
1540 /* no full window splitting allowed */
1541 if (sc->state != SCREENNORMAL)
1542 return OPERATOR_CANCELLED;
1544 if (event->type == EVT_ACTIONZONE_AREA) {
1545 sActionzoneData *sad = event->customdata;
1547 if (sad == NULL || sad->modifier > 0) {
1548 return OPERATOR_PASS_THROUGH;
1551 /* verify *sad itself */
1552 if (sad->sa1 == NULL || sad->az == NULL)
1553 return OPERATOR_PASS_THROUGH;
1555 /* is this our *sad? if areas not equal it should be passed on */
1556 if (CTX_wm_area(C) != sad->sa1 || sad->sa1 != sad->sa2)
1557 return OPERATOR_PASS_THROUGH;
1559 /* prepare operator state vars */
1560 if (sad->gesture_dir == 'n' || sad->gesture_dir == 's') {
1562 RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1566 RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1568 RNA_enum_set(op->ptr, "direction", dir);
1570 /* general init, also non-UI case, adds customdata, sets area and defaults */
1571 if (!area_split_init(C, op))
1572 return OPERATOR_PASS_THROUGH;
1579 /* retrieve initial mouse coord, so we can find the active edge */
1580 if (RNA_struct_property_is_set(op->ptr, "mouse_x"))
1581 x = RNA_int_get(op->ptr, "mouse_x");
1585 if (RNA_struct_property_is_set(op->ptr, "mouse_y"))
1586 y = RNA_int_get(op->ptr, "mouse_y");
1590 actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, x, y);
1591 if (actedge == NULL)
1592 return OPERATOR_CANCELLED;
1594 dir = scredge_is_horizontal(actedge) ? 'v' : 'h';
1596 RNA_enum_set(op->ptr, "direction", dir);
1598 /* special case, adds customdata, sets defaults */
1599 if (!area_split_menu_init(C, op))
1600 return OPERATOR_CANCELLED;
1604 sd = (sAreaSplitData *)op->customdata;
1609 if (event->type == EVT_ACTIONZONE_AREA) {
1612 if (area_split_apply(C, op)) {
1613 area_move_set_limits(sc, dir, winsize_x, winsize_y, &sd->bigger, &sd->smaller);
1615 /* add temp handler for edge move or cancel */
1616 WM_event_add_modal_handler(C, op);
1618 return OPERATOR_RUNNING_MODAL;
1622 sd->previewmode = 1;
1623 /* add temp handler for edge move or cancel */
1624 WM_event_add_modal_handler(C, op);
1626 return OPERATOR_RUNNING_MODAL;
1630 return OPERATOR_PASS_THROUGH;
1633 /* function to be called outside UI context, or for redo */
1634 static int area_split_exec(bContext *C, wmOperator *op)
1637 if (!area_split_init(C, op))
1638 return OPERATOR_CANCELLED;
1640 area_split_apply(C, op);
1641 area_split_exit(C, op);
1643 return OPERATOR_FINISHED;
1647 static void area_split_cancel(bContext *C, wmOperator *op)
1649 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1651 if (sd->previewmode) {
1654 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1655 if (CTX_wm_area(C) == sd->narea) {
1656 CTX_wm_area_set(C, NULL);
1657 CTX_wm_region_set(C, NULL);
1662 area_split_exit(C, op);
1665 static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
1667 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1671 /* execute the events */
1672 switch (event->type) {
1674 dir = RNA_enum_get(op->ptr, "direction");
1676 sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
1677 if (sd->previewmode == 0)
1678 area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1681 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1682 ED_area_tag_redraw(sd->sarea);
1684 /* area context not set */
1685 sd->sarea = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y);
1688 ED_area_tag_redraw(sd->sarea);
1690 sd->origsize = sd->sarea->winx;
1691 sd->origmin = sd->sarea->totrct.xmin;
1692 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1695 sd->origsize = sd->sarea->winy;
1696 sd->origmin = sd->sarea->totrct.ymin;
1697 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1701 CTX_wm_window(C)->screen->do_draw = true;
1705 fac = (dir == 'v') ? event->x - sd->origmin : event->y - sd->origmin;
1706 RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1711 if (sd->previewmode) {
1712 area_split_apply(C, op);
1713 area_split_exit(C, op);
1714 return OPERATOR_FINISHED;
1717 if (event->val == KM_RELEASE) { /* mouse up */
1718 area_split_exit(C, op);
1719 return OPERATOR_FINISHED;
1726 if (sd->previewmode == 0) {
1729 dir = RNA_enum_get(op->ptr, "direction");
1731 if (event->val == KM_PRESS) {
1733 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1734 ED_area_tag_redraw(sd->sarea);
1737 RNA_enum_set(op->ptr, "direction", 'h');
1738 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1740 WM_cursor_set(CTX_wm_window(C), CURSOR_X_MOVE);
1743 RNA_enum_set(op->ptr, "direction", 'v');
1744 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1746 WM_cursor_set(CTX_wm_window(C), CURSOR_Y_MOVE);
1754 case RIGHTMOUSE: /* cancel operation */
1756 area_split_cancel(C, op);
1757 return OPERATOR_CANCELLED;
1760 return OPERATOR_RUNNING_MODAL;
1763 static EnumPropertyItem prop_direction_items[] = {
1764 {'h', "HORIZONTAL", 0, "Horizontal", ""},
1765 {'v', "VERTICAL", 0, "Vertical", ""},
1766 {0, NULL, 0, NULL, NULL}
1769 static void SCREEN_OT_area_split(wmOperatorType *ot)
1771 ot->name = "Split Area";
1772 ot->description = "Split selected area into new windows";
1773 ot->idname = "SCREEN_OT_area_split";
1775 ot->exec = area_split_exec;
1776 ot->invoke = area_split_invoke;
1777 ot->modal = area_split_modal;
1778 ot->cancel = area_split_cancel;
1780 ot->poll = screen_active_editable;
1783 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1786 RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1787 RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1788 RNA_def_int(ot->srna, "mouse_x", -100, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
1789 RNA_def_int(ot->srna, "mouse_y", -100, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
1794 /* ************** scale region edge operator *********************************** */
1796 typedef struct RegionMoveData {
1800 int bigger, smaller, origval;
1808 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
1813 if (edge == AE_RIGHT_TO_TOPLEFT || edge == AE_LEFT_TO_TOPRIGHT) {
1814 dist = BLI_rcti_size_x(&sa->totrct);
1816 else { /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
1817 dist = BLI_rcti_size_y(&sa->totrct);
1820 /* subtractwidth of regions on opposite side
1821 * prevents dragging regions into other opposite regions */
1822 for (ar = sa->regionbase.first; ar; ar = ar->next) {
1826 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1828 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1830 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1832 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1835 /* case of regions in regions, like operator properties panel */
1836 /* these can sit on top of other regions such as headers, so account for this */
1837 else if (edge == AE_BOTTOM_TO_TOPLEFT && scalear->alignment & RGN_ALIGN_TOP &&
1838 ar->alignment == RGN_ALIGN_TOP && ar->regiontype == RGN_TYPE_HEADER)
1842 else if (edge == AE_TOP_TO_BOTTOMRIGHT && scalear->alignment & RGN_ALIGN_BOTTOM &&
1843 ar->alignment == RGN_ALIGN_BOTTOM && ar->regiontype == RGN_TYPE_HEADER)
1852 static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1854 sActionzoneData *sad = event->customdata;
1857 if (event->type != EVT_ACTIONZONE_REGION) {
1858 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");
1859 return OPERATOR_CANCELLED;
1865 RegionMoveData *rmd = MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1868 op->customdata = rmd;
1873 rmd->edge = az->edge;
1874 rmd->origx = event->x;
1875 rmd->origy = event->y;
1876 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1878 /* if not set we do now, otherwise it uses type */
1879 if (rmd->ar->sizex == 0)
1880 rmd->ar->sizex = rmd->ar->winx;
1881 if (rmd->ar->sizey == 0)
1882 rmd->ar->sizey = rmd->ar->winy;
1884 /* now copy to regionmovedata */
1885 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1886 rmd->origval = rmd->ar->sizex;
1889 rmd->origval = rmd->ar->sizey;
1892 /* limit headers to standard height for now */
1893 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1894 maxsize = ED_area_headersize();
1898 CLAMP(rmd->maxsize, 0, maxsize);
1900 /* add temp handler */
1901 WM_event_add_modal_handler(C, op);
1903 return OPERATOR_RUNNING_MODAL;
1906 return OPERATOR_FINISHED;
1909 static int region_scale_get_maxsize(RegionMoveData *rmd)
1913 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1914 return (int) ( (rmd->sa->winx / UI_DPI_FAC) - UI_UNIT_X);
1917 if (rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) {
1918 /* this calculation seems overly verbose
1919 * can someone explain why this method is necessary? - campbell */
1920 maxsize = rmd->maxsize - ((rmd->sa->headertype == HEADERTOP) ? UI_UNIT_Y * 2 : UI_UNIT_Y) - (UI_UNIT_Y / 4);
1926 static void region_scale_validate_size(RegionMoveData *rmd)
1928 if ((rmd->ar->flag & RGN_FLAG_HIDDEN) == 0) {
1929 short *size, maxsize = -1;
1932 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT)
1933 size = &rmd->ar->sizex;
1935 size = &rmd->ar->sizey;
1937 maxsize = region_scale_get_maxsize(rmd);
1939 if (*size > maxsize && maxsize > 0)
1944 static void region_scale_toggle_hidden(bContext *C, RegionMoveData *rmd)
1946 /* hidden areas may have bad 'View2D.cur' value,
1947 * correct before displaying. see T45156 */
1948 if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
1949 UI_view2d_curRect_validate(&rmd->ar->v2d);
1952 region_toggle_hidden(C, rmd->ar, 0);
1953 region_scale_validate_size(rmd);
1956 static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
1958 RegionMoveData *rmd = op->customdata;
1961 /* execute the events */
1962 switch (event->type) {
1965 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1966 delta = event->x - rmd->origx;
1967 if (rmd->edge == AE_LEFT_TO_TOPRIGHT) delta = -delta;
1969 /* region sizes now get multiplied */
1970 delta /= UI_DPI_FAC;
1972 rmd->ar->sizex = rmd->origval + delta;
1973 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
1975 if (rmd->ar->sizex < UI_UNIT_X) {
1976 rmd->ar->sizex = rmd->origval;
1977 if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1978 region_scale_toggle_hidden(C, rmd);
1980 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
1981 region_scale_toggle_hidden(C, rmd);
1984 int maxsize = region_scale_get_maxsize(rmd);
1985 delta = event->y - rmd->origy;
1986 if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) delta = -delta;
1988 /* region sizes now get multiplied */
1989 delta /= UI_DPI_FAC;
1991 rmd->ar->sizey = rmd->origval + delta;
1992 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
1994 /* note, 'UI_UNIT_Y/4' means you need to drag the header almost
1995 * all the way down for it to become hidden, this is done
1996 * otherwise its too easy to do this by accident */
1997 if (rmd->ar->sizey < UI_UNIT_Y / 4) {
1998 rmd->ar->sizey = rmd->origval;
1999 if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
2000 region_scale_toggle_hidden(C, rmd);
2002 else if (maxsize > 0 && (rmd->ar->sizey > maxsize))
2003 rmd->ar->sizey = maxsize;
2004 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
2005 region_scale_toggle_hidden(C, rmd);
2007 ED_area_tag_redraw(rmd->sa);
2008 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2013 if (event->val == KM_RELEASE) {
2015 if (ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
2016 if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
2017 region_scale_toggle_hidden(C, rmd);
2019 else if (rmd->ar->flag & RGN_FLAG_TOO_SMALL) {
2020 region_scale_validate_size(rmd);
2023 ED_area_tag_redraw(rmd->sa);
2024 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2026 MEM_freeN(op->customdata);
2027 op->customdata = NULL;
2029 return OPERATOR_FINISHED;
2037 return OPERATOR_RUNNING_MODAL;
2040 static void region_scale_cancel(bContext *UNUSED(C), wmOperator *op)
2042 MEM_freeN(op->customdata);
2043 op->customdata = NULL;
2046 static void SCREEN_OT_region_scale(wmOperatorType *ot)
2049 ot->name = "Scale Region Size";
2050 ot->description = "Scale selected area";
2051 ot->idname = "SCREEN_OT_region_scale";
2053 ot->invoke = region_scale_invoke;
2054 ot->modal = region_scale_modal;
2055 ot->cancel = region_scale_cancel;
2057 ot->poll = ED_operator_areaactive;
2060 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2064 /* ************** frame change operator ***************************** */
2066 static void areas_do_frame_follow(bContext *C, bool middle)
2068 bScreen *scr = CTX_wm_screen(C);
2069 Scene *scene = CTX_data_scene(C);
2070 wmWindowManager *wm = CTX_wm_manager(C);
2072 for (window = wm->windows.first; window; window = window->next) {
2074 for (sa = window->screen->areabase.first; sa; sa = sa->next) {
2076 for (ar = sa->regionbase.first; ar; ar = ar->next) {
2077 /* do follow here if editor type supports it */
2078 if ((scr->redraws_flag & TIME_FOLLOW)) {
2079 if ((ar->regiontype == RGN_TYPE_WINDOW &&
2080 ELEM(sa->spacetype, SPACE_SEQ, SPACE_TIME, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) ||
2081 (sa->spacetype == SPACE_CLIP && ar->regiontype == RGN_TYPE_PREVIEW))
2083 float w = BLI_rctf_size_x(&ar->v2d.cur);
2086 if ((scene->r.cfra < ar->v2d.cur.xmin) || (scene->r.cfra > ar->v2d.cur.xmax)) {
2087 ar->v2d.cur.xmax = scene->r.cfra + (w / 2);
2088 ar->v2d.cur.xmin = scene->r.cfra - (w / 2);
2092 if (scene->r.cfra < ar->v2d.cur.xmin) {
2093 ar->v2d.cur.xmax = scene->r.cfra;
2094 ar->v2d.cur.xmin = ar->v2d.cur.xmax - w;
2096 else if (scene->r.cfra > ar->v2d.cur.xmax) {
2097 ar->v2d.cur.xmin = scene->r.cfra;
2098 ar->v2d.cur.xmax = ar->v2d.cur.xmin + w;
2108 /* function to be called outside UI context, or for redo */
2109 static int frame_offset_exec(bContext *C, wmOperator *op)
2111 Main *bmain = CTX_data_main(C);
2112 Scene *scene = CTX_data_scene(C);
2115 delta = RNA_int_get(op->ptr, "delta");
2118 FRAMENUMBER_MIN_CLAMP(CFRA);
2121 areas_do_frame_follow(C, false);
2123 BKE_sound_seek_scene(bmain, scene);
2125 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2127 return OPERATOR_FINISHED;
2130 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
2132 ot->name = "Frame Offset";
2133 ot->idname = "SCREEN_OT_frame_offset";
2134 ot->description = "Move current frame forward/backward by a given number";
2136 ot->exec = frame_offset_exec;
2138 ot->poll = ED_operator_screenactive_norender;
2142 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2146 /* function to be called outside UI context, or for redo */
2147 static int frame_jump_exec(bContext *C, wmOperator *op)
2149 Main *bmain = CTX_data_main(C);
2150 Scene *scene = CTX_data_scene(C);
2151 wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
2153 /* Don't change CFRA directly if animtimer is running as this can cause
2154 * first/last frame not to be actually shown (bad since for example physics
2155 * simulations aren't reset properly).
2158 ScreenAnimData *sad = animtimer->customdata;
2160 sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
2162 if (RNA_boolean_get(op->ptr, "end"))
2163 sad->nextfra = PEFRA;
2165 sad->nextfra = PSFRA;
2168 if (RNA_boolean_get(op->ptr, "end"))
2173 areas_do_frame_follow(C, true);
2175 BKE_sound_seek_scene(bmain, scene);
2177 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2180 return OPERATOR_FINISHED;
2183 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
2185 ot->name = "Jump to Endpoint";
2186 ot->description = "Jump to first/last frame in frame range";
2187 ot->idname = "SCREEN_OT_frame_jump";
2189 ot->exec = frame_jump_exec;
2191 ot->poll = ED_operator_screenactive_norender;
2192 ot->flag = OPTYPE_UNDO;
2195 RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range");
2199 /* ************** jump to keyframe operator ***************************** */
2201 /* function to be called outside UI context, or for redo */
2202 static int keyframe_jump_exec(bContext *C, wmOperator *op)
2204 Main *bmain = CTX_data_main(C);
2205 Scene *scene = CTX_data_scene(C);
2206 Object *ob = CTX_data_active_object(C);
2207 bDopeSheet ads = {NULL};
2211 const bool next = RNA_boolean_get(op->ptr, "next");
2216 return OPERATOR_CANCELLED;
2218 cfra = (float)(CFRA);
2220 /* init binarytree-list for getting keyframes */
2221 BLI_dlrbTree_init(&keys);
2223 /* seed up dummy dopesheet context with flags to perform necessary filtering */
2224 if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
2225 /* only selected channels are included */
2226 ads.filterflag |= ADS_FILTER_ONLYSEL;
2229 /* populate tree with keyframe nodes */
2230 scene_to_keylist(&ads, scene, &keys, NULL);
2231 gpencil_to_keylist(&ads, scene->gpd, &keys);
2234 ob_to_keylist(&ads, ob, &keys, NULL);
2235 gpencil_to_keylist(&ads, ob->gpd, &keys);
2239 Mask *mask = CTX_data_edit_mask(C);
2241 MaskLayer *masklay = BKE_mask_layer_active(mask);
2242 mask_to_keylist(&ads, masklay, &keys);
2246 /* build linked-list for searching */
2247 BLI_dlrbTree_linkedlist_sync(&keys);
2249 /* find matching keyframe in the right direction */
2252 ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
2254 ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
2257 if (CFRA != (int)ak->cfra) {
2258 /* this changes the frame, so set the frame and we're done */
2259 CFRA = (int)ak->cfra;
2263 /* make this the new starting point for the search */
2267 } while ((ak != NULL) && (done == false));
2269 /* free temp stuff */
2270 BLI_dlrbTree_free(&keys);
2273 if (done == false) {
2274 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
2276 return OPERATOR_CANCELLED;
2279 areas_do_frame_follow(C, true);
2281 BKE_sound_seek_scene(bmain, scene);
2283 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2285 return OPERATOR_FINISHED;
2289 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
2291 ot->name = "Jump to Keyframe";
2292 ot->description = "Jump to previous/next keyframe";
2293 ot->idname = "SCREEN_OT_keyframe_jump";
2295 ot->exec = keyframe_jump_exec;
2297 ot->poll = ED_operator_screenactive_norender;
2298 ot->flag = OPTYPE_UNDO;
2301 RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
2304 /* ************** jump to marker operator ***************************** */
2306 /* function to be called outside UI context, or for redo */
2307 static int marker_jump_exec(bContext *C, wmOperator *op)
2309 Main *bmain = CTX_data_main(C);
2310 Scene *scene = CTX_data_scene(C);
2313 const bool next = RNA_boolean_get(op->ptr, "next");
2316 /* find matching marker in the right direction */
2317 for (marker = scene->markers.first; marker; marker = marker->next) {
2319 if ((marker->frame > CFRA) && (!found || closest > marker->frame)) {
2320 closest = marker->frame;
2325 if ((marker->frame < CFRA) && (!found || closest < marker->frame)) {
2326 closest = marker->frame;
2334 BKE_report(op->reports, RPT_INFO, "No more markers to jump to in this direction");
2336 return OPERATOR_CANCELLED;
2341 areas_do_frame_follow(C, true);
2343 BKE_sound_seek_scene(bmain, scene);
2345 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2347 return OPERATOR_FINISHED;
2351 static void SCREEN_OT_marker_jump(wmOperatorType *ot)
2353 ot->name = "Jump to Marker";
2354 ot->description = "Jump to previous/next marker";
2355 ot->idname = "SCREEN_OT_marker_jump";
2357 ot->exec = marker_jump_exec;
2359 ot->poll = ED_operator_screenactive_norender;
2360 ot->flag = OPTYPE_UNDO;
2363 RNA_def_boolean(ot->srna, "next", true, "Next Marker", "");
2366 /* ************** switch screen operator ***************************** */
2368 static bool screen_set_is_ok(bScreen *screen, bScreen *screen_prev)
2370 return ((screen->winid == 0) &&
2371 /* in typical usage these should have a nonzero winid
2372 * (all temp screens should be used, or closed & freed). */
2373 (screen->temp == false) &&
2374 (screen->state == SCREENNORMAL) &&
2375 (screen != screen_prev) &&
2376 (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT)));
2379 /* function to be called outside UI context, or for redo */
2380 static int screen_set_exec(bContext *C, wmOperator *op)
2382 Main *bmain = CTX_data_main(C);
2383 bScreen *screen = CTX_wm_screen(C);
2384 bScreen *screen_prev = screen;
2386 ScrArea *sa = CTX_wm_area(C);
2387 int tot = BLI_listbase_count(&bmain->screen);
2388 int delta = RNA_int_get(op->ptr, "delta");
2390 /* temp screens are for userpref or render display */
2391 if (screen->temp || (sa && sa->full && sa->full->temp)) {
2392 return OPERATOR_CANCELLED;
2397 screen = screen->id.next;
2398 if (screen == NULL) screen = bmain->screen.first;
2399 if (screen_set_is_ok(screen, screen_prev)) {
2404 else if (delta == -1) {
2406 screen = screen->id.prev;
2407 if (screen == NULL) screen = bmain->screen.last;
2408 if (screen_set_is_ok(screen, screen_prev)) {
2417 if (screen && screen_prev != screen) {
2418 /* return to previous state before switching screens */
2419 if (sa && sa->full) {
2420 ED_screen_full_restore(C, sa); /* may free 'screen_prev' */
2423 ED_screen_set(C, screen);
2424 return OPERATOR_FINISHED;
2426 return OPERATOR_CANCELLED;
2429 static void SCREEN_OT_screen_set(wmOperatorType *ot)
2431 ot->name = "Set Screen";
2432 ot->description = "Cycle through available screens";
2433 ot->idname = "SCREEN_OT_screen_set";
2435 ot->exec = screen_set_exec;
2436 ot->poll = ED_operator_screenactive;
2439 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2442 /* ************** screen full-area operator ***************************** */
2445 /* function to be called outside UI context, or for redo */
2446 static int screen_maximize_area_exec(bContext *C, wmOperator *op)
2448 bScreen *screen = CTX_wm_screen(C);
2450 const bool hide_panels = RNA_boolean_get(op->ptr, "use_hide_panels");
2452 /* search current screen for 'fullscreen' areas */
2453 /* prevents restoring info header, when mouse is over it */
2454 for (sa = screen->areabase.first; sa; sa = sa->next) {
2455 if (sa->full) break;
2459 sa = CTX_wm_area(C);
2463 if (!ELEM(screen->state, SCREENNORMAL, SCREENFULL)) {
2464 return OPERATOR_CANCELLED;
2466 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENFULL);
2469 if (!ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) {
2470 return OPERATOR_CANCELLED;
2472 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED);
2475 return OPERATOR_FINISHED;
2478 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
2482 ot->name = "Toggle Fullscreen Area";
2483 ot->description = "Toggle display selected area as fullscreen/maximized";
2484 ot->idname = "SCREEN_OT_screen_full_area";
2486 ot->exec = screen_maximize_area_exec;
2487 ot->poll = ED_operator_areaactive;
2490 prop = RNA_def_boolean(ot->srna, "use_hide_panels", false, "Hide Panels", "Hide all the panels");
2491 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2494 /* ************** join area operator ********************************************** */
2496 /* operator state vars used:
2497 * x1, y1 mouse coord in first area, which will disappear
2498 * x2, y2 mouse coord in 2nd area, which will become joined
2502 * init() find edge based on state vars
2503 * test if the edge divides two areas,
2504 * store active and nonactive area,
2506 * apply() do the actual join
2508 * exit() cleanup, send notifier
2512 * exec() calls init, apply, exit
2514 * invoke() sets mouse coords in x,y
2518 * modal() accept modal events while doing it
2519 * call apply() with active window and nonactive window
2520 * call exit() and remove handler when LMB confirm
2523 typedef struct sAreaJoinData {
2524 ScrArea *sa1; /* first area to be considered */
2525 ScrArea *sa2; /* second area to be considered */
2526 ScrArea *scr; /* designed for removal */
2531 /* validate selection inside screen, set variables OK */
2532 /* return 0: init failed */
2533 /* XXX todo: find edge based on (x,y) and set other area? */
2534 static int area_join_init(bContext *C, wmOperator *op)
2537 sAreaJoinData *jd = NULL;
2542 /* required properties, make negative to get return 0 if not set by caller */
2543 x1 = RNA_int_get(op->ptr, "min_x");
2544 y1 = RNA_int_get(op->ptr, "min_y");
2545 x2 = RNA_int_get(op->ptr, "max_x");
2546 y2 = RNA_int_get(op->ptr, "max_y");
2548 sa1 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x1, y1);
2549 sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x2, y2);
2550 if (sa1 == NULL || sa2 == NULL || sa1 == sa2)
2553 /* do areas share an edge? */
2554 if (sa1->v1 == sa2->v1 || sa1->v1 == sa2->v2 || sa1->v1 == sa2->v3 || sa1->v1 == sa2->v4) shared++;
2555 if (sa1->v2 == sa2->v1 || sa1->v2 == sa2->v2 || sa1->v2 == sa2->v3 || sa1->v2 == sa2->v4) shared++;
2556 if (sa1->v3 == sa2->v1 || sa1->v3 == sa2->v2 || sa1->v3 == sa2->v3 || sa1->v3 == sa2->v4) shared++;
2557 if (sa1->v4 == sa2->v1 || sa1->v4 == sa2->v2 || sa1->v4 == sa2->v3 || sa1->v4 == sa2->v4) shared++;
2559 printf("areas don't share edge\n");
2563 jd = (sAreaJoinData *)MEM_callocN(sizeof(sAreaJoinData), "op_area_join");
2566 jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2568 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2570 op->customdata = jd;
2575 /* apply the join of the areas (space types) */
2576 static int area_join_apply(bContext *C, wmOperator *op)
2578 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2581 if (!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)) {
2584 if (CTX_wm_area(C) == jd->sa2) {
2585 CTX_wm_area_set(C, NULL);
2586 CTX_wm_region_set(C, NULL);
2592 /* finish operation */
2593 static void area_join_exit(bContext *C, wmOperator *op)
2595 if (op->customdata) {
2596 MEM_freeN(op->customdata);
2597 op->customdata = NULL;
2600 /* this makes sure aligned edges will result in aligned grabbing */
2601 removedouble_scredges(CTX_wm_screen(C));
2602 removenotused_scredges(CTX_wm_screen(C));
2603 removenotused_scrverts(CTX_wm_screen(C));
2606 static int area_join_exec(bContext *C, wmOperator *op)
2608 if (!area_join_init(C, op))
2609 return OPERATOR_CANCELLED;
2611 area_join_apply(C, op);
2612 area_join_exit(C, op);
2614 return OPERATOR_FINISHED;
2617 /* interaction callback */
2618 static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2621 if (event->type == EVT_ACTIONZONE_AREA) {
2622 sActionzoneData *sad = event->customdata;
2624 if (sad == NULL || sad->modifier > 0) {
2625 return OPERATOR_PASS_THROUGH;
2628 /* verify *sad itself */
2629 if (sad->sa1 == NULL || sad->sa2 == NULL)
2630 return OPERATOR_PASS_THROUGH;
2632 /* is this our *sad? if areas equal it should be passed on */
2633 if (sad->sa1 == sad->sa2)
2634 return OPERATOR_PASS_THROUGH;
2636 /* prepare operator state vars */
2637 RNA_int_set(op->ptr, "min_x", sad->x);
2638 RNA_int_set(op->ptr, "min_y", sad->y);
2639 RNA_int_set(op->ptr, "max_x", event->x);
2640 RNA_int_set(op->ptr, "max_y", event->y);
2644 if (!area_join_init(C, op))
2645 return OPERATOR_PASS_THROUGH;
2647 /* add temp handler */
2648 WM_event_add_modal_handler(C, op);
2650 return OPERATOR_RUNNING_MODAL;
2653 static void area_join_cancel(bContext *C, wmOperator *op)
2655 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2658 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2659 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
2662 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
2663 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2666 WM_event_add_notifier(C, NC_WINDOW, NULL);
2668 area_join_exit(C, op);
2671 /* modal callback while selecting area (space) that will be removed */
2672 static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
2674 bScreen *sc = CTX_wm_screen(C);
2675 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2677 /* execute the events */
2678 switch (event->type) {
2682 ScrArea *sa = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y);
2686 if (jd->sa1 != sa) {
2687 dir = area_getorientation(jd->sa1, sa);
2689 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2691 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2694 /* we are not bordering on the previously selected area
2695 * we check if area has common border with the one marked for removal
2696 * in this case we can swap areas.
2698 dir = area_getorientation(sa, jd->sa2);
2700 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2701 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2704 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2705 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2708 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2712 WM_event_add_notifier(C, NC_WINDOW, NULL);
2715 /* we are back in the area previously selected for keeping
2716 * we swap the areas if possible to allow user to choose */
2717 if (jd->sa2 != NULL) {
2718 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2719 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2722 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2723 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2724 dir = area_getorientation(jd->sa1, jd->sa2);
2726 printf("oops, didn't expect that!\n");
2730 dir = area_getorientation(jd->sa1, sa);
2732 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2734 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2737 WM_event_add_notifier(C, NC_WINDOW, NULL);
2743 if (event->val == KM_RELEASE) {
2744 ED_area_tag_redraw(jd->sa1);
2745 ED_area_tag_redraw(jd->sa2);
2747 area_join_apply(C, op);
2748 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2749 area_join_exit(C, op);
2750 return OPERATOR_FINISHED;
2756 area_join_cancel(C, op);
2757 return OPERATOR_CANCELLED;
2760 return OPERATOR_RUNNING_MODAL;
2763 /* Operator for joining two areas (space types) */
2764 static void SCREEN_OT_area_join(wmOperatorType *ot)
2767 ot->name = "Join Area";
2768 ot->description = "Join selected areas into new window";
2769 ot->idname = "SCREEN_OT_area_join";
2772 ot->exec = area_join_exec;
2773 ot->invoke = area_join_invoke;
2774 ot->modal = area_join_modal;
2775 ot->poll = screen_active_editable;
2776 ot->cancel = area_join_cancel;
2779 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2782 RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
2783 RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2784 RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2785 RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2788 /* ******************************* */
2790 static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2792 wmWindow *win = CTX_wm_window(C);
2793 bScreen *sc = CTX_wm_screen(C);
2796 PointerRNA ptr1, ptr2;
2798 const int winsize_x = WM_window_pixels_x(win);
2799 const int winsize_y = WM_window_pixels_y(win);
2801 actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, event->x, event->y);
2803 if (actedge == NULL) return OPERATOR_CANCELLED;
2805 pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
2806 layout = UI_popup_menu_layout(pup);
2808 WM_operator_properties_create(&ptr1, "SCREEN_OT_area_join");
2810 /* mouse cursor on edge, '4' can fail on wide edges... */
2811 RNA_int_set(&ptr1, "min_x", event->x + 4);
2812 RNA_int_set(&ptr1, "min_y", event->y + 4);
2813 RNA_int_set(&ptr1, "max_x", event->x - 4);
2814 RNA_int_set(&ptr1, "max_y", event->y - 4);
2816 WM_operator_properties_create(&ptr2, "SCREEN_OT_area_split");
2818 /* store initial mouse cursor position */
2819 RNA_int_set(&ptr2, "mouse_x", event->x);
2820 RNA_int_set(&ptr2, "mouse_y", event->y);
2822 uiItemFullO(layout, "SCREEN_OT_area_split", NULL, ICON_NONE, ptr2.data, WM_OP_INVOKE_DEFAULT, 0);
2823 uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, ptr1.data, WM_OP_INVOKE_DEFAULT, 0);
2825 UI_popup_menu_end(C, pup);
2827 return OPERATOR_INTERFACE;
2830 static void SCREEN_OT_area_options(wmOperatorType *ot)
2833 ot->name = "Area Options";
2834 ot->description = "Operations for splitting and merging";
2835 ot->idname = "SCREEN_OT_area_options";
2838 ot->invoke = screen_area_options_invoke;
2840 ot->poll = ED_operator_screen_mainwinactive;
2843 ot->flag = OPTYPE_INTERNAL;
2847 /* ******************************* */
2850 static int spacedata_cleanup_exec(bContext *C, wmOperator *op)
2852 Main *bmain = CTX_data_main(C);
2857 for (screen = bmain->screen.first; screen; screen = screen->id.next) {
2858 for (sa = screen->areabase.first; sa; sa = sa->next) {
2859 if (sa->spacedata.first != sa->spacedata.last) {
2860 SpaceLink *sl = sa->spacedata.first;
2862 BLI_remlink(&sa->spacedata, sl);
2863 tot += BLI_listbase_count(&sa->spacedata);
2864 BKE_spacedata_freelist(&sa->spacedata);
2865 BLI_addtail(&sa->spacedata, sl);
2869 BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
2871 return OPERATOR_FINISHED;
2874 static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
2877 ot->name = "Clean-up Space-data";
2878 ot->description = "Remove unused settings for invisible editors";
2879 ot->idname = "SCREEN_OT_spacedata_cleanup";
2882 ot->exec = spacedata_cleanup_exec;
2883 ot->poll = WM_operator_winactive;
2887 /* ************** repeat last operator ***************************** */
2889 static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
2891 wmOperator *lastop = CTX_wm_manager(C)->operators.last;
2894 WM_operator_repeat(C, lastop);
2896 return OPERATOR_CANCELLED;
2899 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2902 ot->name = "Repeat Last";
2903 ot->description = "Repeat last action";
2904 ot->idname = "SCREEN_OT_repeat_last";
2907 ot->exec = repeat_last_exec;
2909 ot->poll = ED_operator_screenactive;
2913 static int repeat_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2915 wmWindowManager *wm = CTX_wm_manager(C);
2921 items = BLI_listbase_count(&wm->operators);
2923 return OPERATOR_CANCELLED;
2925 pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
2926 layout = UI_popup_menu_layout(pup);
2928 for (i = items - 1, lastop = wm->operators.last; lastop; lastop = lastop->prev, i--)
2929 if (WM_operator_repeat_check(C, lastop))
2930 uiItemIntO(layout, RNA_struct_ui_name(lastop->type->srna), ICON_NONE, op->type->idname, "index", i);
2932 UI_popup_menu_end(C, pup);
2934 return OPERATOR_INTERFACE;
2937 static int repeat_history_exec(bContext *C, wmOperator *op)
2939 wmWindowManager *wm = CTX_wm_manager(C);
2941 op = BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2943 /* let's put it as last operator in list */
2944 BLI_remlink(&wm->operators, op);
2945 BLI_addtail(&wm->operators, op);
2947 WM_operator_repeat(C, op);
2950 return OPERATOR_FINISHED;
2953 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2956 ot->name = "Repeat History";
2957 ot->description = "Display menu for previous actions performed";
2958 ot->idname = "SCREEN_OT_repeat_history";
2961 ot->invoke = repeat_history_invoke;
2962 ot->exec = repeat_history_exec;
2964 ot->poll = ED_operator_screenactive;
2966 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2969 /* ********************** redo operator ***************************** */
2971 static int redo_last_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
2973 wmOperator *lastop = WM_operator_last_redo(C);
2976 WM_operator_redo_popup(C, lastop);
2978 return OPERATOR_CANCELLED;
2981 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2984 ot->name = "Redo Last";
2985 ot->description = "Display menu for last action performed";
2986 ot->idname = "SCREEN_OT_redo_last";
2989 ot->invoke = redo_last_invoke;
2991 ot->poll = ED_operator_screenactive;
2994 /* ************** region four-split operator ***************************** */
2996 static void view3d_localview_update_rv3d(struct RegionView3D *rv3d)
2998 if (rv3d->localvd) {
2999 rv3d->localvd->view = rv3d->view;
3000 rv3d->localvd->persp = rv3d->persp;
3001 copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);
3005 static void region_quadview_init_rv3d(ScrArea *sa, ARegion *ar,
3006 const char viewlock, const char view, const char persp)
3008 RegionView3D *rv3d = ar->regiondata;
3010 if (persp == RV3D_CAMOB) {
3011 ED_view3d_lastview_store(rv3d);
3014 rv3d->viewlock = viewlock;
3016 rv3d->persp = persp;
3018 ED_view3d_lock(rv3d);
3019 view3d_localview_update_rv3d(rv3d);
3020 if ((viewlock & RV3D_BOXCLIP) && (persp == RV3D_ORTHO)) {
3021 ED_view3d_quadview_update(sa, ar, true);
3025 /* insert a region in the area region list */
3026 static int region_quadview_exec(bContext *C, wmOperator *op)
3028 ARegion *ar = CTX_wm_region(C);
3031 if (ar->regiontype != RGN_TYPE_WINDOW) {
3032 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
3034 else if (ar->alignment == RGN_ALIGN_QSPLIT) {
3035 /* Exit quad-view */
3036 ScrArea *sa = CTX_wm_area(C);
3039 /* keep current region */
3042 if (sa->spacetype == SPACE_VIEW3D) {
3044 RegionView3D *rv3d = ar->regiondata;
3046 /* if this is a locked view, use settings from 'User' view */
3047 if (rv3d->viewlock) {
3051 if (ED_view3d_context_user_region(C, &v3d_user, &ar_user)) {
3052 if (ar != ar_user) {
3053 SWAP(void *, ar->regiondata, ar_user->regiondata);
3054 rv3d = ar->regiondata;
3059 rv3d->viewlock_quad = RV3D_VIEWLOCK_INIT;
3061 rv3d->rflag &= ~RV3D_CLIPPING;
3063 /* accumulate locks, incase they're mixed */
3064 for (ar_iter = sa->regionbase.first; ar_iter; ar_iter = ar_iter->next) {
3065 if (ar_iter->regiontype == RGN_TYPE_WINDOW) {
3066 RegionView3D *rv3d_iter = ar_iter->regiondata;
3067 rv3d->viewlock_quad |= rv3d_iter->viewlock;
3072 for (ar = sa->regionbase.first; ar; ar = arn) {
3074 if (ar->alignment == RGN_ALIGN_QSPLIT) {
3075 ED_region_exit(C, ar);
3076 BKE_area_region_free(sa->type, ar);
3077 BLI_remlink(&sa->regionbase, ar);
3081 ED_area_tag_redraw(sa);
3082 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3084 else if (ar->next) {
3085 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
3088 /* Enter quad-view */
3089 ScrArea *sa = CTX_wm_area(C);
3093 ar->alignment = RGN_ALIGN_QSPLIT;
3095 for (count = 0; count < 3; count++) {
3096 newar = BKE_area_region_copy(sa->type, ar);
3097 BLI_addtail(&sa->regionbase, newar);
3100 /* lock views and set them */
3101 if (sa->spacetype == SPACE_VIEW3D) {
3102 View3D *v3d = sa->spacedata.first;
3103 int index_qsplit = 0;
3105 /* run ED_view3d_lock() so the correct 'rv3d->viewquat' is set,
3106 * otherwise when restoring rv3d->localvd the 'viewquat' won't
3107 * match the 'view', set on entering localview See: [#26315],
3109 * We could avoid manipulating rv3d->localvd here if exiting
3110 * localview with a 4-split would assign these view locks */
3111 RegionView3D *rv3d = ar->regiondata;
3112 const char viewlock = (rv3d->viewlock_quad & RV3D_VIEWLOCK_INIT) ?
3113 (rv3d->viewlock_quad & ~RV3D_VIEWLOCK_INIT) : RV3D_LOCKED;
3115 region_quadview_init_rv3d(sa, ar, viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3116 region_quadview_init_rv3d(sa, (ar = ar->next), viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3117 region_quadview_init_rv3d(sa, (ar = ar->next), viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3118 /* forcing camera is distracting */
3120 if (v3d->camera) region_quadview_init_rv3d(sa, (ar = ar->next), 0, RV3D_VIEW_CAMERA, RV3D_CAMOB);
3121 else region_quadview_init_rv3d(sa, (ar = ar->next), 0, RV3D_VIEW_USER, RV3D_PERSP);
3126 ED_area_tag_redraw(sa);
3127 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3131 return OPERATOR_FINISHED;
3134 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
3137 ot->name = "Toggle Quad View";
3138 ot->description = "Split selected area into camera, front, right & top views";
3139 ot->idname = "SCREEN_OT_region_quadview";
3142 ot->exec = region_quadview_exec;
3143 ot->poll = ED_operator_region_view3d_active;
3148 /* ************** region flip operator ***************************** */
3150 /* flip a region alignment */
3151 static int region_flip_exec(bContext *C, wmOperator *UNUSED(op))
3153 ARegion *ar = CTX_wm_region(C);
3156 return OPERATOR_CANCELLED;
3158 if (ar->alignment == RGN_ALIGN_TOP)
3159 ar->alignment = RGN_ALIGN_BOTTOM;
3160 else if (ar->alignment == RGN_ALIGN_BOTTOM)
3161 ar->alignment = RGN_ALIGN_TOP;
3162 else if (ar->alignment == RGN_ALIGN_LEFT)
3163 ar->alignment = RGN_ALIGN_RIGHT;
3164 else if (ar->alignment == RGN_ALIGN_RIGHT)
3165 ar->alignment = RGN_ALIGN_LEFT;
3167 ED_area_tag_redraw(CTX_wm_area(C));
3168 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3170 return OPERATOR_FINISHED;
3174 static void SCREEN_OT_region_flip(wmOperatorType *ot)
3177 ot->name = "Flip Region";
3178 ot->idname = "SCREEN_OT_region_flip";
3179 ot->description = "Toggle the region's alignment (left/right or top/bottom)";
3182 ot->exec = region_flip_exec;
3183 ot->poll = ED_operator_areaactive;
3187 /* ************** header operator ***************************** */
3188 static int header_exec(bContext *C, wmOperator *UNUSED(op))
3190 ARegion *ar = screen_find_region_type(C, RGN_TYPE_HEADER);
3193 return OPERATOR_CANCELLED;
3196 ar->flag ^= RGN_FLAG_HIDDEN;
3198 ED_area_tag_redraw(CTX_wm_area(C));
3200 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3202 return OPERATOR_FINISHED;
3205 static void SCREEN_OT_header(wmOperatorType *ot)
3208 ot->name = "Toggle Header";
3209 ot->description = "Toggle header display";
3210 ot->idname = "SCREEN_OT_header";
3213 ot->exec = header_exec;
3216 /* ************** header flip operator ***************************** */
3218 /* flip a header region alignment */
3219 static int header_flip_exec(bContext *C, wmOperator *UNUSED(op))
3221 ARegion *ar = screen_find_region_type(C, RGN_TYPE_HEADER);
3224 return OPERATOR_CANCELLED;
3227 /* copied from SCREEN_OT_region_flip */
3228 if (ar->alignment == RGN_ALIGN_TOP)
3229 ar->alignment = RGN_ALIGN_BOTTOM;
3230 else if (ar->alignment == RGN_ALIGN_BOTTOM)
3231 ar->alignment = RGN_ALIGN_TOP;