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 "BLF_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"
85 #include "screen_intern.h" /* own module include */
87 #define KM_MODAL_CANCEL 1
88 #define KM_MODAL_APPLY 2
89 #define KM_MODAL_STEP10 3
90 #define KM_MODAL_STEP10_OFF 4
92 /* ************** Exported Poll tests ********************** */
94 int ED_operator_regionactive(bContext *C)
96 if (CTX_wm_window(C) == NULL) return 0;
97 if (CTX_wm_screen(C) == NULL) return 0;
98 if (CTX_wm_region(C) == NULL) return 0;
102 int ED_operator_areaactive(bContext *C)
104 if (CTX_wm_window(C) == NULL) return 0;
105 if (CTX_wm_screen(C) == NULL) return 0;
106 if (CTX_wm_area(C) == NULL) return 0;
110 int ED_operator_screenactive(bContext *C)
112 if (CTX_wm_window(C) == NULL) return 0;
113 if (CTX_wm_screen(C) == NULL) return 0;
117 /* XXX added this to prevent anim state to change during renders */
118 static int ED_operator_screenactive_norender(bContext *C)
120 if (G.is_rendering) return 0;
121 if (CTX_wm_window(C) == NULL) return 0;
122 if (CTX_wm_screen(C) == NULL) return 0;
127 static int screen_active_editable(bContext *C)
129 if (ED_operator_screenactive(C)) {
130 /* no full window splitting allowed */
131 if (CTX_wm_screen(C)->state != SCREENNORMAL)
138 static ARegion *screen_find_region_type(bContext *C, int type)
140 ARegion *ar = CTX_wm_region(C);
142 /* find the header region
143 * - try context first, but upon failing, search all regions in area...
145 if ((ar == NULL) || (ar->regiontype != type)) {
146 ScrArea *sa = CTX_wm_area(C);
147 ar = BKE_area_find_region_type(sa, type);
156 /* when mouse is over area-edge */
157 int ED_operator_screen_mainwinactive(bContext *C)
160 if (CTX_wm_window(C) == NULL) return 0;
161 screen = CTX_wm_screen(C);
162 if (screen == NULL) return 0;
163 if (screen->subwinactive != screen->mainwin) return 0;
167 int ED_operator_scene_editable(bContext *C)
169 Scene *scene = CTX_data_scene(C);
170 if (scene && scene->id.lib == NULL)
175 int ED_operator_objectmode(bContext *C)
177 Scene *scene = CTX_data_scene(C);
178 Object *obact = CTX_data_active_object(C);
180 if (scene == NULL || scene->id.lib)
182 if (CTX_data_edit_object(C))
185 /* add a check for ob->mode too? */
186 if (obact && (obact->mode != OB_MODE_OBJECT))
193 static bool ed_spacetype_test(bContext *C, int type)
195 if (ED_operator_areaactive(C)) {
196 SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);
197 return sl && (sl->spacetype == type);
202 int ED_operator_view3d_active(bContext *C)
204 return ed_spacetype_test(C, SPACE_VIEW3D);
207 int ED_operator_region_view3d_active(bContext *C)
209 if (CTX_wm_region_view3d(C))
212 CTX_wm_operator_poll_msg_set(C, "expected a view3d region");
216 /* generic for any view2d which uses anim_ops */
217 int ED_operator_animview_active(bContext *C)
219 if (ED_operator_areaactive(C)) {
220 SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);
221 if (sl && (ELEM(sl->spacetype, SPACE_SEQ, SPACE_ACTION, SPACE_NLA, SPACE_IPO, SPACE_TIME)))
225 CTX_wm_operator_poll_msg_set(C, "expected a timeline/animation area to be active");
229 int ED_operator_timeline_active(bContext *C)
231 return ed_spacetype_test(C, SPACE_TIME);
234 int ED_operator_outliner_active(bContext *C)
236 return ed_spacetype_test(C, SPACE_OUTLINER);
239 int ED_operator_outliner_active_no_editobject(bContext *C)
241 if (ed_spacetype_test(C, SPACE_OUTLINER)) {
242 Object *ob = ED_object_active_context(C);
243 Object *obedit = CTX_data_edit_object(C);
244 if (ob && ob == obedit)
252 int ED_operator_file_active(bContext *C)
254 return ed_spacetype_test(C, SPACE_FILE);
257 int ED_operator_action_active(bContext *C)
259 return ed_spacetype_test(C, SPACE_ACTION);
262 int ED_operator_buttons_active(bContext *C)
264 return ed_spacetype_test(C, SPACE_BUTS);
267 int ED_operator_node_active(bContext *C)
269 SpaceNode *snode = CTX_wm_space_node(C);
271 if (snode && snode->edittree)
277 int ED_operator_node_editable(bContext *C)
279 SpaceNode *snode = CTX_wm_space_node(C);
281 if (snode && snode->edittree && snode->edittree->id.lib == NULL)
287 int ED_operator_graphedit_active(bContext *C)
289 return ed_spacetype_test(C, SPACE_IPO);
292 int ED_operator_sequencer_active(bContext *C)
294 return ed_spacetype_test(C, SPACE_SEQ);
297 int ED_operator_sequencer_active_editable(bContext *C)
299 return ed_spacetype_test(C, SPACE_SEQ) && ED_operator_scene_editable(C);
302 int ED_operator_image_active(bContext *C)
304 return ed_spacetype_test(C, SPACE_IMAGE);
307 int ED_operator_nla_active(bContext *C)
309 return ed_spacetype_test(C, SPACE_NLA);
312 int ED_operator_logic_active(bContext *C)
314 return ed_spacetype_test(C, SPACE_LOGIC);
317 int ED_operator_info_active(bContext *C)
319 return ed_spacetype_test(C, SPACE_INFO);
323 int ED_operator_console_active(bContext *C)
325 return ed_spacetype_test(C, SPACE_CONSOLE);
328 static int ed_object_hidden(Object *ob)
330 /* if hidden but in edit mode, we still display, can happen with animation */
331 return ((ob->restrictflag & OB_RESTRICT_VIEW) && !(ob->mode & OB_MODE_EDIT));
334 int ED_operator_object_active(bContext *C)
336 Object *ob = ED_object_active_context(C);
337 return ((ob != NULL) && !ed_object_hidden(ob));
340 int ED_operator_object_active_editable(bContext *C)
342 Object *ob = ED_object_active_context(C);
343 return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob));
346 int ED_operator_object_active_editable_mesh(bContext *C)
348 Object *ob = ED_object_active_context(C);
349 return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) &&
350 (ob->type == OB_MESH) && !(((ID *)ob->data)->lib));
353 int ED_operator_object_active_editable_font(bContext *C)
355 Object *ob = ED_object_active_context(C);
356 return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) &&
357 (ob->type == OB_FONT));
360 int ED_operator_editmesh(bContext *C)
362 Object *obedit = CTX_data_edit_object(C);
363 if (obedit && obedit->type == OB_MESH)
364 return NULL != BKE_editmesh_from_object(obedit);
368 int ED_operator_editmesh_view3d(bContext *C)
370 return ED_operator_editmesh(C) && ED_operator_view3d_active(C);
373 int ED_operator_editmesh_region_view3d(bContext *C)
375 if (ED_operator_editmesh(C) && CTX_wm_region_view3d(C))
378 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editmesh");
382 int ED_operator_editarmature(bContext *C)
384 Object *obedit = CTX_data_edit_object(C);
385 if (obedit && obedit->type == OB_ARMATURE)
386 return NULL != ((bArmature *)obedit->data)->edbo;
391 * \brief check for pose mode (no mixed modes)
393 * We want to enable most pose operations in weight paint mode,
394 * when it comes to transforming bones, but managing bomes layers/groups
395 * can be left for pose mode only. (not weight paint mode)
397 int ED_operator_posemode_exclusive(bContext *C)
399 Object *obact = CTX_data_active_object(C);
401 if (obact && !(obact->mode & OB_MODE_EDIT)) {
403 if ((obpose = BKE_object_pose_armature_get(obact))) {
404 if (obact == obpose) {
413 /* allows for pinned pose objects to be used in the object buttons
414 * and the non-active pose object to be used in the 3D view */
415 int ED_operator_posemode_context(bContext *C)
417 Object *obpose = ED_pose_object_from_context(C);
419 if (obpose && !(obpose->mode & OB_MODE_EDIT)) {
420 if (BKE_object_pose_context_check(obpose)) {
428 int ED_operator_posemode(bContext *C)
430 Object *obact = CTX_data_active_object(C);
432 if (obact && !(obact->mode & OB_MODE_EDIT)) {
434 if ((obpose = BKE_object_pose_armature_get(obact))) {
435 if ((obact == obpose) || (obact->mode & OB_MODE_WEIGHT_PAINT)) {
444 /* wrapper for ED_space_image_show_uvedit */
445 int ED_operator_uvedit(bContext *C)
447 SpaceImage *sima = CTX_wm_space_image(C);
448 Object *obedit = CTX_data_edit_object(C);
449 return ED_space_image_show_uvedit(sima, obedit);
452 int ED_operator_uvedit_space_image(bContext *C)
454 SpaceImage *sima = CTX_wm_space_image(C);
455 Object *obedit = CTX_data_edit_object(C);
456 return sima && ED_space_image_show_uvedit(sima, obedit);
459 int ED_operator_uvmap(bContext *C)
461 Object *obedit = CTX_data_edit_object(C);
462 BMEditMesh *em = NULL;
464 if (obedit && obedit->type == OB_MESH) {
465 em = BKE_editmesh_from_object(obedit);
468 if (em && (em->bm->totface)) {
475 int ED_operator_editsurfcurve(bContext *C)
477 Object *obedit = CTX_data_edit_object(C);
478 if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
479 return NULL != ((Curve *)obedit->data)->editnurb;
483 int ED_operator_editsurfcurve_region_view3d(bContext *C)
485 if (ED_operator_editsurfcurve(C) && CTX_wm_region_view3d(C))
488 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editcurve");
492 int ED_operator_editcurve(bContext *C)
494 Object *obedit = CTX_data_edit_object(C);
495 if (obedit && obedit->type == OB_CURVE)
496 return NULL != ((Curve *)obedit->data)->editnurb;
500 int ED_operator_editcurve_3d(bContext *C)
502 Object *obedit = CTX_data_edit_object(C);
503 if (obedit && obedit->type == OB_CURVE) {
504 Curve *cu = (Curve *)obedit->data;
506 return (cu->flag & CU_3D) && (NULL != cu->editnurb);
511 int ED_operator_editsurf(bContext *C)
513 Object *obedit = CTX_data_edit_object(C);
514 if (obedit && obedit->type == OB_SURF)
515 return NULL != ((Curve *)obedit->data)->editnurb;
519 int ED_operator_editfont(bContext *C)
521 Object *obedit = CTX_data_edit_object(C);
522 if (obedit && obedit->type == OB_FONT)
523 return NULL != ((Curve *)obedit->data)->editfont;
527 int ED_operator_editlattice(bContext *C)
529 Object *obedit = CTX_data_edit_object(C);
530 if (obedit && obedit->type == OB_LATTICE)
531 return NULL != ((Lattice *)obedit->data)->editlatt;
535 int ED_operator_editmball(bContext *C)
537 Object *obedit = CTX_data_edit_object(C);
538 if (obedit && obedit->type == OB_MBALL)
539 return NULL != ((MetaBall *)obedit->data)->editelems;
543 int ED_operator_mask(bContext *C)
545 ScrArea *sa = CTX_wm_area(C);
546 if (sa && sa->spacedata.first) {
547 switch (sa->spacetype) {
550 SpaceClip *sc = sa->spacedata.first;
551 return ED_space_clip_check_show_maskedit(sc);
555 SpaceSeq *sseq = sa->spacedata.first;
556 Scene *scene = CTX_data_scene(C);
557 return ED_space_sequencer_check_show_maskedit(sseq, scene);
561 SpaceImage *sima = sa->spacedata.first;
562 Scene *scene = CTX_data_scene(C);
563 return ED_space_image_check_show_maskedit(scene, sima);
571 /* *************************** action zone operator ************************** */
573 /* operator state vars used:
578 * apply() set actionzone event
580 * exit() free customdata
586 * invoke() check if in zone
587 * add customdata, put mouseco and area in it
590 * modal() accept modal events while doing it
591 * call apply() with gesture info, active window, nonactive window
592 * call exit() and remove handler when LMB confirm
595 typedef struct sActionzoneData {
598 int x, y, gesture_dir, modifier;
601 /* quick poll to save operators to be created and handled */
602 static int actionzone_area_poll(bContext *C)
604 wmWindow *win = CTX_wm_window(C);
605 ScrArea *sa = CTX_wm_area(C);
607 if (sa && win && win->eventstate) {
608 const int *xy = &win->eventstate->x;
611 for (az = sa->actionzones.first; az; az = az->next)
612 if (BLI_rcti_isect_pt_v(&az->rect, xy))
618 /* the debug drawing of the click_rect is in area_draw_azone_fullscreen, keep both in sync */
619 static void fullscreen_click_rcti_init(rcti *rect, const short x1, const short y1, const short x2, const short y2)
621 int x = x2 - ((float) x2 - x1) * 0.5f / UI_DPI_FAC;
622 int y = y2 - ((float) y2 - y1) * 0.5f / UI_DPI_FAC;
623 float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC;
625 /* adjust the icon distance from the corner */
626 x += 36.0f / UI_DPI_FAC;
627 y += 36.0f / UI_DPI_FAC;
629 /* draws from the left bottom corner of the icon */
630 x -= UI_DPI_ICON_SIZE;
631 y -= UI_DPI_ICON_SIZE;
633 BLI_rcti_init(rect, x, x + icon_size, y, y + icon_size);
636 AZone *is_in_area_actionzone(ScrArea *sa, const int xy[2])
640 for (az = sa->actionzones.first; az; az = az->next) {
641 if (BLI_rcti_isect_pt_v(&az->rect, xy)) {
642 if (az->type == AZONE_AREA) {
643 /* no triangle intersect but a hotspot circle based on corner */
644 int radius = (xy[0] - az->x1) * (xy[0] - az->x1) + (xy[1] - az->y1) * (xy[1] - az->y1);
646 if (radius <= AZONESPOT * AZONESPOT)
649 else if (az->type == AZONE_REGION) {
652 else if (az->type == AZONE_FULLSCREEN) {
653 int mouse_radius, spot_radius, fadein_radius, fadeout_radius;
656 fullscreen_click_rcti_init(&click_rect, az->x1, az->y1, az->x2, az->y2);
658 if (BLI_rcti_isect_pt_v(&click_rect, xy)) {
662 mouse_radius = (xy[0] - az->x2) * (xy[0] - az->x2) + (xy[1] - az->y2) * (xy[1] - az->y2);
663 spot_radius = AZONESPOT * AZONESPOT;
664 fadein_radius = AZONEFADEIN * AZONEFADEIN;
665 fadeout_radius = AZONEFADEOUT * AZONEFADEOUT;
667 if (mouse_radius < spot_radius) {
670 else if (mouse_radius < fadein_radius) {
673 else if (mouse_radius < fadeout_radius) {
674 az->alpha = 1.0f - ((float)(mouse_radius - fadein_radius)) / ((float)(fadeout_radius - fadein_radius));
680 /* fade in/out but no click */
684 /* XXX force redraw to show/hide the action zone */
685 ED_area_tag_redraw(sa);
695 static void actionzone_exit(wmOperator *op)
698 MEM_freeN(op->customdata);
699 op->customdata = NULL;
702 /* send EVT_ACTIONZONE event */
703 static void actionzone_apply(bContext *C, wmOperator *op, int type)
706 wmWindow *win = CTX_wm_window(C);
707 sActionzoneData *sad = op->customdata;
709 sad->modifier = RNA_int_get(op->ptr, "modifier");
711 wm_event_init_from_window(win, &event);
713 if (type == AZONE_AREA)
714 event.type = EVT_ACTIONZONE_AREA;
715 else if (type == AZONE_FULLSCREEN)
716 event.type = EVT_ACTIONZONE_FULLSCREEN;
718 event.type = EVT_ACTIONZONE_REGION;
721 event.customdata = op->customdata;
722 event.customdatafree = true;
723 op->customdata = NULL;
725 wm_event_add(win, &event);
728 static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
730 AZone *az = is_in_area_actionzone(CTX_wm_area(C), &event->x);
731 sActionzoneData *sad;
735 return OPERATOR_PASS_THROUGH;
737 /* ok we do the actionzone */
738 sad = op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
739 sad->sa1 = CTX_wm_area(C);
741 sad->x = event->x; sad->y = event->y;
743 /* region azone directly reacts on mouse clicks */
744 if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) {
745 actionzone_apply(C, op, sad->az->type);
747 return OPERATOR_FINISHED;
750 /* add modal handler */
751 WM_event_add_modal_handler(C, op);
753 return OPERATOR_RUNNING_MODAL;
758 static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
760 wmWindow *win = CTX_wm_window(C);
761 bScreen *sc = CTX_wm_screen(C);
762 sActionzoneData *sad = op->customdata;
763 const int winsize_x = WM_window_pixels_x(win);
764 const int winsize_y = WM_window_pixels_y(win);
766 switch (event->type) {
771 const int delta_x = (event->x - sad->x);
772 const int delta_y = (event->y - sad->y);
774 /* calculate gesture direction */
775 if (delta_y > ABS(delta_x))
776 sad->gesture_dir = 'n';
777 else if (delta_x > ABS(delta_y))
778 sad->gesture_dir = 'e';
779 else if (delta_y < -ABS(delta_x))
780 sad->gesture_dir = 's';
782 sad->gesture_dir = 'w';
784 if (sad->az->type == AZONE_AREA) {
785 /* once we drag outside the actionzone, register a gesture
786 * check we're not on an edge so join finds the other area */
787 is_gesture = ((is_in_area_actionzone(sad->sa1, &event->x) != sad->az) &&
788 (screen_find_active_scredge(sc, winsize_x, winsize_y, event->x, event->y) == NULL));
791 const int delta_min = 1;
792 is_gesture = (ABS(delta_x) > delta_min || ABS(delta_y) > delta_min);
795 /* gesture is large enough? */
797 /* second area, for join when (sa1 != sa2) */
798 sad->sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y);
799 /* apply sends event */
800 actionzone_apply(C, op, sad->az->type);
803 return OPERATOR_FINISHED;
809 return OPERATOR_CANCELLED;
812 return OPERATOR_CANCELLED;
816 return OPERATOR_RUNNING_MODAL;
819 static void actionzone_cancel(bContext *UNUSED(C), wmOperator *op)
824 static void SCREEN_OT_actionzone(wmOperatorType *ot)
827 ot->name = "Handle Area Action Zones";
828 ot->description = "Handle area action zones for mouse actions/gestures";
829 ot->idname = "SCREEN_OT_actionzone";
831 ot->invoke = actionzone_invoke;
832 ot->modal = actionzone_modal;
833 ot->poll = actionzone_area_poll;
834 ot->cancel = actionzone_cancel;
837 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
839 RNA_def_int(ot->srna, "modifier", 0, 0, 2, "Modifier", "Modifier state", 0, 2);
842 /* ************** swap area operator *********************************** */
844 /* operator state vars used:
846 * sa2 area to swap with
850 * init() set custom data for operator, based on actionzone event custom data
852 * cancel() cancel the operator
854 * exit() cleanup, send notifier
858 * invoke() gets called on shift+lmb drag in actionzone
859 * call init(), add handler
861 * modal() accept modal events while doing it
864 typedef struct sAreaSwapData {
868 static int area_swap_init(wmOperator *op, const wmEvent *event)
870 sAreaSwapData *sd = NULL;
871 sActionzoneData *sad = event->customdata;
873 if (sad == NULL || sad->sa1 == NULL)
876 sd = MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
885 static void area_swap_exit(bContext *C, wmOperator *op)
887 WM_cursor_modal_restore(CTX_wm_window(C));
889 MEM_freeN(op->customdata);
890 op->customdata = NULL;
893 static void area_swap_cancel(bContext *C, wmOperator *op)
895 area_swap_exit(C, op);
898 static int area_swap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
901 if (!area_swap_init(op, event))
902 return OPERATOR_PASS_THROUGH;
904 /* add modal handler */
905 WM_cursor_modal_set(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
906 WM_event_add_modal_handler(C, op);
908 return OPERATOR_RUNNING_MODAL;
912 static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event)
914 sActionzoneData *sad = op->customdata;
916 switch (event->type) {
918 /* second area, for join */
919 sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y);
921 case LEFTMOUSE: /* release LMB */
922 if (event->val == KM_RELEASE) {
923 if (!sad->sa2 || sad->sa1 == sad->sa2) {
924 area_swap_cancel(C, op);
925 return OPERATOR_CANCELLED;
928 ED_area_tag_redraw(sad->sa1);
929 ED_area_tag_redraw(sad->sa2);
931 ED_area_swapspace(C, sad->sa1, sad->sa2);
933 area_swap_exit(C, op);
935 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
937 return OPERATOR_FINISHED;
942 area_swap_cancel(C, op);
943 return OPERATOR_CANCELLED;
945 return OPERATOR_RUNNING_MODAL;
948 static void SCREEN_OT_area_swap(wmOperatorType *ot)
950 ot->name = "Swap Areas";
951 ot->description = "Swap selected areas screen positions";
952 ot->idname = "SCREEN_OT_area_swap";
954 ot->invoke = area_swap_invoke;
955 ot->modal = area_swap_modal;
956 ot->poll = ED_operator_areaactive;
957 ot->cancel = area_swap_cancel;
959 ot->flag = OPTYPE_BLOCKING;
962 /* *********** Duplicate area as new window operator ****************** */
964 /* operator callback */
965 static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
967 wmWindow *newwin, *win;
972 win = CTX_wm_window(C);
973 sc = CTX_wm_screen(C);
977 if (event->type == EVT_ACTIONZONE_AREA) {
978 sActionzoneData *sad = event->customdata;
981 return OPERATOR_PASS_THROUGH;
986 /* adds window to WM */
988 BLI_rcti_translate(&rect, win->posx, win->posy);
989 rect.xmax = rect.xmin + BLI_rcti_size_x(&rect) / U.pixelsize;
990 rect.ymax = rect.ymin + BLI_rcti_size_y(&rect) / U.pixelsize;
992 newwin = WM_window_open(C, &rect);
994 /* allocs new screen and adds to newly created window, using window size */
995 newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2);
996 newwin->screen = newsc;
998 /* copy area to new screen */
999 ED_area_data_copy((ScrArea *)newsc->areabase.first, sa, true);
1001 ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
1003 /* screen, areas init */
1004 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1006 if (event->type == EVT_ACTIONZONE_AREA)
1007 actionzone_exit(op);
1009 return OPERATOR_FINISHED;
1012 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
1014 ot->name = "Duplicate Area into New Window";
1015 ot->description = "Duplicate selected area into new window";
1016 ot->idname = "SCREEN_OT_area_dupli";
1018 ot->invoke = area_dupli_invoke;
1019 ot->poll = ED_operator_areaactive;
1023 /* ************** move area edge operator *********************************** */
1025 /* operator state vars used:
1026 * x, y mouse coord near edge
1027 * delta movement of edge
1031 * init() set default property values, find edge based on mouse coords, test
1032 * if the edge can be moved, select edges, calculate min and max movement
1034 * apply() apply delta on selection
1036 * exit() cleanup, send notifier
1038 * cancel() cancel moving
1042 * exec() execute without any user interaction, based on properties
1043 * call init(), apply(), exit()
1045 * invoke() gets called on mouse click near edge
1046 * call init(), add handler
1048 * modal() accept modal events while doing it
1049 * call apply() with delta motion
1050 * call exit() and remove handler
1053 typedef struct sAreaMoveData {
1054 int bigger, smaller, origval, step;
1058 /* helper call to move area-edge, sets limits
1059 * need window size in order to get correct limits */
1060 static void area_move_set_limits(bScreen *sc, int dir,
1061 const int winsize_x, const int winsize_y,
1062 int *bigger, int *smaller)
1065 int areaminy = ED_area_headersize();
1068 /* we check all areas and test for free space with MINSIZE */
1069 *bigger = *smaller = 100000;
1071 for (sa = sc->areabase.first; sa; sa = sa->next) {
1076 if (sa->v1->vec.y > 0)
1077 areamin += U.pixelsize;
1078 if (sa->v2->vec.y < winsize_y - 1)
1079 areamin += U.pixelsize;
1081 y1 = sa->v2->vec.y - sa->v1->vec.y + 1 - areamin;
1083 /* if top or down edge selected, test height */
1084 if (sa->v1->editflag && sa->v4->editflag)
1085 *bigger = min_ii(*bigger, y1);
1086 else if (sa->v2->editflag && sa->v3->editflag)
1087 *smaller = min_ii(*smaller, y1);
1093 if (sa->v1->vec.x > 0)
1094 areamin += U.pixelsize;
1095 if (sa->v4->vec.x < winsize_x - 1)
1096 areamin += U.pixelsize;
1098 x1 = sa->v4->vec.x - sa->v1->vec.x + 1 - areamin;
1100 /* if left or right edge selected, test width */
1101 if (sa->v1->editflag && sa->v2->editflag)
1102 *bigger = min_ii(*bigger, x1);
1103 else if (sa->v3->editflag && sa->v4->editflag)
1104 *smaller = min_ii(*smaller, x1);
1109 /* validate selection inside screen, set variables OK */
1110 /* return 0: init failed */
1111 static int area_move_init(bContext *C, wmOperator *op)
1113 bScreen *sc = CTX_wm_screen(C);
1114 wmWindow *win = CTX_wm_window(C);
1118 const int winsize_x = WM_window_pixels_x(win);
1119 const int winsize_y = WM_window_pixels_y(win);
1122 /* required properties */
1123 x = RNA_int_get(op->ptr, "x");
1124 y = RNA_int_get(op->ptr, "y");
1127 actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, x, y);
1128 if (actedge == NULL) return 0;
1130 md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
1131 op->customdata = md;
1133 md->dir = scredge_is_horizontal(actedge) ? 'h' : 'v';
1134 if (md->dir == 'h') md->origval = actedge->v1->vec.y;
1135 else md->origval = actedge->v1->vec.x;
1137 select_connected_scredge(sc, actedge);
1138 /* now all vertices with 'flag==1' are the ones that can be moved. Move this to editflag */
1139 for (v1 = sc->vertbase.first; v1; v1 = v1->next)
1140 v1->editflag = v1->flag;
1142 area_move_set_limits(sc, md->dir, winsize_x, winsize_y, &md->bigger, &md->smaller);
1147 /* moves selected screen edge amount of delta, used by split & move */
1148 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
1150 wmWindow *win = CTX_wm_window(C);
1151 const int winsize_x = WM_window_pixels_x(win);
1152 const int winsize_y = WM_window_pixels_y(win);
1153 bScreen *sc = CTX_wm_screen(C);
1159 delta = CLAMPIS(delta, -smaller, bigger);
1161 for (v1 = sc->vertbase.first; v1; v1 = v1->next) {
1163 /* that way a nice AREAGRID */
1164 if ((dir == 'v') && v1->vec.x > 0 && v1->vec.x < winsize_x - 1) {
1166 v1->vec.x = origval + delta;
1168 if (delta != bigger && delta != -smaller) {
1169 v1->vec.x -= (v1->vec.x % AREAGRID);
1170 v1->vec.x = CLAMPIS(v1->vec.x, origval - smaller, origval + bigger);
1172 if (oldval != v1->vec.x)
1175 if ((dir == 'h') && v1->vec.y > 0 && v1->vec.y < winsize_y - 1) {
1177 v1->vec.y = origval + delta;
1179 if (delta != bigger && delta != smaller) {
1180 v1->vec.y -= (v1->vec.y % AREAGRID);
1181 v1->vec.y = CLAMPIS(v1->vec.y, origval - smaller, origval + bigger);
1183 if (oldval != v1->vec.y)
1189 /* only redraw if we actually moved a screen vert, for AREAGRID */
1191 for (sa = sc->areabase.first; sa; sa = sa->next) {
1192 if (sa->v1->editflag || sa->v2->editflag || sa->v3->editflag || sa->v4->editflag)
1193 ED_area_tag_redraw(sa);
1196 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */
1200 static void area_move_apply(bContext *C, wmOperator *op)
1202 sAreaMoveData *md = op->customdata;
1205 delta = RNA_int_get(op->ptr, "delta");
1206 area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
1209 static void area_move_exit(bContext *C, wmOperator *op)
1212 MEM_freeN(op->customdata);
1213 op->customdata = NULL;
1215 /* this makes sure aligned edges will result in aligned grabbing */
1216 removedouble_scrverts(CTX_wm_screen(C));
1217 removedouble_scredges(CTX_wm_screen(C));
1220 static int area_move_exec(bContext *C, wmOperator *op)
1222 if (!area_move_init(C, op))
1223 return OPERATOR_CANCELLED;
1225 area_move_apply(C, op);
1226 area_move_exit(C, op);
1228 return OPERATOR_FINISHED;
1231 /* interaction callback */
1232 static int area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1234 RNA_int_set(op->ptr, "x", event->x);
1235 RNA_int_set(op->ptr, "y", event->y);
1237 if (!area_move_init(C, op))
1238 return OPERATOR_PASS_THROUGH;
1240 /* add temp handler */
1241 WM_event_add_modal_handler(C, op);
1243 return OPERATOR_RUNNING_MODAL;
1246 static void area_move_cancel(bContext *C, wmOperator *op)
1249 RNA_int_set(op->ptr, "delta", 0);
1250 area_move_apply(C, op);
1251 area_move_exit(C, op);
1254 /* modal callback for while moving edges */
1255 static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
1257 sAreaMoveData *md = op->customdata;
1260 /* execute the events */
1261 switch (event->type) {
1264 x = RNA_int_get(op->ptr, "x");
1265 y = RNA_int_get(op->ptr, "y");
1267 delta = (md->dir == 'v') ? event->x - x : event->y - y;
1268 if (md->step) delta = delta - (delta % md->step);
1269 RNA_int_set(op->ptr, "delta", delta);
1271 area_move_apply(C, op);
1276 switch (event->val) {
1277 case KM_MODAL_APPLY:
1278 area_move_exit(C, op);
1279 return OPERATOR_FINISHED;
1281 case KM_MODAL_CANCEL:
1282 area_move_cancel(C, op);
1283 return OPERATOR_CANCELLED;
1285 case KM_MODAL_STEP10:
1288 case KM_MODAL_STEP10_OFF:
1296 return OPERATOR_RUNNING_MODAL;
1299 static void SCREEN_OT_area_move(wmOperatorType *ot)
1302 ot->name = "Move Area Edges";
1303 ot->description = "Move selected area edges";
1304 ot->idname = "SCREEN_OT_area_move";
1306 ot->exec = area_move_exec;
1307 ot->invoke = area_move_invoke;
1308 ot->cancel = area_move_cancel;
1309 ot->modal = area_move_modal;
1310 ot->poll = ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
1313 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1316 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1317 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1318 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1321 /* ************** split area operator *********************************** */
1324 * operator state vars:
1326 * dir direction 'v' or 'h'
1328 * operator customdata:
1329 * area pointer to (active) area
1330 * x, y last used mouse pos
1335 * init() set default property values, find area based on context
1337 * apply() split area based on state vars
1339 * exit() cleanup, send notifier
1341 * cancel() remove duplicated area
1345 * exec() execute without any user interaction, based on state vars
1346 * call init(), apply(), exit()
1348 * invoke() gets called on mouse click in action-widget
1349 * call init(), add modal handler
1350 * call apply() with initial motion
1352 * modal() accept modal events while doing it
1353 * call move-areas code with delta motion
1354 * call exit() or cancel() and remove handler
1357 typedef struct sAreaSplitData {
1358 int x, y; /* last used mouse position */
1360 int origval; /* for move areas */
1361 int bigger, smaller; /* constraints for moving new edge */
1362 int delta; /* delta move edge */
1363 int origmin, origsize; /* to calculate fac, for property storage */
1364 int previewmode; /* draw previewline, then split */
1366 ScrEdge *nedge; /* new edge */
1367 ScrArea *sarea; /* start area */
1368 ScrArea *narea; /* new area */
1372 /* generic init, menu case, doesn't need active area */
1373 static int area_split_menu_init(bContext *C, wmOperator *op)
1378 sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1379 op->customdata = sd;
1381 sd->sarea = CTX_wm_area(C);
1384 int dir = RNA_enum_get(op->ptr, "direction");
1387 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1389 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1394 /* generic init, no UI stuff here, assumes active area */
1395 static int area_split_init(bContext *C, wmOperator *op)
1397 ScrArea *sa = CTX_wm_area(C);
1399 int areaminy = ED_area_headersize() + 1;
1402 /* required context */
1403 if (sa == NULL) return 0;
1405 /* required properties */
1406 dir = RNA_enum_get(op->ptr, "direction");
1409 if (dir == 'v' && sa->winx < 2 * AREAMINX) return 0;
1410 if (dir == 'h' && sa->winy < 2 * areaminy) return 0;
1413 sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1414 op->customdata = sd;
1417 sd->origsize = dir == 'v' ? sa->winx : sa->winy;
1418 sd->origmin = dir == 'v' ? sa->totrct.xmin : sa->totrct.ymin;
1423 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1424 /* used with split operator */
1425 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1427 ScrVert *sav1 = sa->v1;
1428 ScrVert *sav2 = sa->v2;
1429 ScrVert *sav3 = sa->v3;
1430 ScrVert *sav4 = sa->v4;
1431 ScrVert *sbv1 = sb->v1;
1432 ScrVert *sbv2 = sb->v2;
1433 ScrVert *sbv3 = sb->v3;
1434 ScrVert *sbv4 = sb->v4;
1436 if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */
1437 return screen_findedge(screen, sav1, sav2);
1439 else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */
1440 return screen_findedge(screen, sav2, sav3);
1442 else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */
1443 return screen_findedge(screen, sav3, sav4);
1445 else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/
1446 return screen_findedge(screen, sav1, sav4);
1453 /* do the split, return success */
1454 static int area_split_apply(bContext *C, wmOperator *op)
1456 bScreen *sc = CTX_wm_screen(C);
1457 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1461 fac = RNA_float_get(op->ptr, "factor");
1462 dir = RNA_enum_get(op->ptr, "direction");
1464 sd->narea = area_split(sc, sd->sarea, dir, fac, 0); /* 0 = no merge */
1469 sd->nedge = area_findsharededge(sc, sd->sarea, sd->narea);
1471 /* select newly created edge, prepare for moving edge */
1472 for (sv = sc->vertbase.first; sv; sv = sv->next)
1475 sd->nedge->v1->editflag = 1;
1476 sd->nedge->v2->editflag = 1;
1478 if (dir == 'h') sd->origval = sd->nedge->v1->vec.y;
1479 else sd->origval = sd->nedge->v1->vec.x;
1481 ED_area_tag_redraw(sd->sarea);
1482 ED_area_tag_redraw(sd->narea);
1484 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1492 static void area_split_exit(bContext *C, wmOperator *op)
1494 if (op->customdata) {
1495 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1496 if (sd->sarea) ED_area_tag_redraw(sd->sarea);
1497 if (sd->narea) ED_area_tag_redraw(sd->narea);
1500 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1502 MEM_freeN(op->customdata);
1503 op->customdata = NULL;
1506 WM_cursor_modal_restore(CTX_wm_window(C));
1507 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1509 /* this makes sure aligned edges will result in aligned grabbing */
1510 removedouble_scrverts(CTX_wm_screen(C));
1511 removedouble_scredges(CTX_wm_screen(C));
1515 /* UI callback, adds new handler */
1516 static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1518 wmWindow *win = CTX_wm_window(C);
1519 bScreen *sc = CTX_wm_screen(C);
1521 const int winsize_x = WM_window_pixels_x(win);
1522 const int winsize_y = WM_window_pixels_y(win);
1525 /* no full window splitting allowed */
1526 if (sc->state != SCREENNORMAL)
1527 return OPERATOR_CANCELLED;
1529 if (event->type == EVT_ACTIONZONE_AREA) {
1530 sActionzoneData *sad = event->customdata;
1532 if (sad == NULL || sad->modifier > 0) {
1533 return OPERATOR_PASS_THROUGH;
1536 /* verify *sad itself */
1537 if (sad->sa1 == NULL || sad->az == NULL)
1538 return OPERATOR_PASS_THROUGH;
1540 /* is this our *sad? if areas not equal it should be passed on */
1541 if (CTX_wm_area(C) != sad->sa1 || sad->sa1 != sad->sa2)
1542 return OPERATOR_PASS_THROUGH;
1544 /* prepare operator state vars */
1545 if (sad->gesture_dir == 'n' || sad->gesture_dir == 's') {
1547 RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1551 RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1553 RNA_enum_set(op->ptr, "direction", dir);
1555 /* general init, also non-UI case, adds customdata, sets area and defaults */
1556 if (!area_split_init(C, op))
1557 return OPERATOR_PASS_THROUGH;
1564 /* retrieve initial mouse coord, so we can find the active edge */
1565 if (RNA_struct_property_is_set(op->ptr, "mouse_x"))
1566 x = RNA_int_get(op->ptr, "mouse_x");
1570 if (RNA_struct_property_is_set(op->ptr, "mouse_y"))
1571 y = RNA_int_get(op->ptr, "mouse_y");
1575 actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, x, y);
1576 if (actedge == NULL)
1577 return OPERATOR_CANCELLED;
1579 dir = scredge_is_horizontal(actedge) ? 'v' : 'h';
1581 RNA_enum_set(op->ptr, "direction", dir);
1583 /* special case, adds customdata, sets defaults */
1584 if (!area_split_menu_init(C, op))
1585 return OPERATOR_CANCELLED;
1589 sd = (sAreaSplitData *)op->customdata;
1594 if (event->type == EVT_ACTIONZONE_AREA) {
1597 if (area_split_apply(C, op)) {
1598 area_move_set_limits(sc, dir, winsize_x, winsize_y, &sd->bigger, &sd->smaller);
1600 /* add temp handler for edge move or cancel */
1601 WM_event_add_modal_handler(C, op);
1603 return OPERATOR_RUNNING_MODAL;
1607 sd->previewmode = 1;
1608 /* add temp handler for edge move or cancel */
1609 WM_event_add_modal_handler(C, op);
1611 return OPERATOR_RUNNING_MODAL;
1615 return OPERATOR_PASS_THROUGH;
1618 /* function to be called outside UI context, or for redo */
1619 static int area_split_exec(bContext *C, wmOperator *op)
1622 if (!area_split_init(C, op))
1623 return OPERATOR_CANCELLED;
1625 area_split_apply(C, op);
1626 area_split_exit(C, op);
1628 return OPERATOR_FINISHED;
1632 static void area_split_cancel(bContext *C, wmOperator *op)
1634 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1636 if (sd->previewmode) {
1639 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1640 if (CTX_wm_area(C) == sd->narea) {
1641 CTX_wm_area_set(C, NULL);
1642 CTX_wm_region_set(C, NULL);
1647 area_split_exit(C, op);
1650 static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
1652 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1656 /* execute the events */
1657 switch (event->type) {
1659 dir = RNA_enum_get(op->ptr, "direction");
1661 sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
1662 if (sd->previewmode == 0)
1663 area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1666 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1667 ED_area_tag_redraw(sd->sarea);
1669 /* area context not set */
1670 sd->sarea = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y);
1673 ED_area_tag_redraw(sd->sarea);
1675 sd->origsize = sd->sarea->winx;
1676 sd->origmin = sd->sarea->totrct.xmin;
1677 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1680 sd->origsize = sd->sarea->winy;
1681 sd->origmin = sd->sarea->totrct.ymin;
1682 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1686 CTX_wm_window(C)->screen->do_draw = true;
1690 fac = (dir == 'v') ? event->x - sd->origmin : event->y - sd->origmin;
1691 RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1696 if (sd->previewmode) {
1697 area_split_apply(C, op);
1698 area_split_exit(C, op);
1699 return OPERATOR_FINISHED;
1702 if (event->val == KM_RELEASE) { /* mouse up */
1703 area_split_exit(C, op);
1704 return OPERATOR_FINISHED;
1711 if (sd->previewmode == 0) {
1714 dir = RNA_enum_get(op->ptr, "direction");
1716 if (event->val == KM_PRESS) {
1718 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1719 ED_area_tag_redraw(sd->sarea);
1722 RNA_enum_set(op->ptr, "direction", 'h');
1723 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1725 WM_cursor_set(CTX_wm_window(C), CURSOR_X_MOVE);
1728 RNA_enum_set(op->ptr, "direction", 'v');
1729 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1731 WM_cursor_set(CTX_wm_window(C), CURSOR_Y_MOVE);
1739 case RIGHTMOUSE: /* cancel operation */
1741 area_split_cancel(C, op);
1742 return OPERATOR_CANCELLED;
1745 return OPERATOR_RUNNING_MODAL;
1748 static EnumPropertyItem prop_direction_items[] = {
1749 {'h', "HORIZONTAL", 0, "Horizontal", ""},
1750 {'v', "VERTICAL", 0, "Vertical", ""},
1751 {0, NULL, 0, NULL, NULL}
1754 static void SCREEN_OT_area_split(wmOperatorType *ot)
1756 ot->name = "Split Area";
1757 ot->description = "Split selected area into new windows";
1758 ot->idname = "SCREEN_OT_area_split";
1760 ot->exec = area_split_exec;
1761 ot->invoke = area_split_invoke;
1762 ot->modal = area_split_modal;
1763 ot->cancel = area_split_cancel;
1765 ot->poll = screen_active_editable;
1768 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1771 RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1772 RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1773 RNA_def_int(ot->srna, "mouse_x", -100, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
1774 RNA_def_int(ot->srna, "mouse_y", -100, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
1779 /* ************** scale region edge operator *********************************** */
1781 typedef struct RegionMoveData {
1785 int bigger, smaller, origval;
1793 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
1798 if (edge == AE_RIGHT_TO_TOPLEFT || edge == AE_LEFT_TO_TOPRIGHT) {
1799 dist = BLI_rcti_size_x(&sa->totrct);
1801 else { /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
1802 dist = BLI_rcti_size_y(&sa->totrct);
1805 /* subtractwidth of regions on opposite side
1806 * prevents dragging regions into other opposite regions */
1807 for (ar = sa->regionbase.first; ar; ar = ar->next) {
1811 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1813 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1815 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1817 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1820 /* case of regions in regions, like operator properties panel */
1821 /* these can sit on top of other regions such as headers, so account for this */
1822 else if (edge == AE_BOTTOM_TO_TOPLEFT && scalear->alignment & RGN_ALIGN_TOP &&
1823 ar->alignment == RGN_ALIGN_TOP && ar->regiontype == RGN_TYPE_HEADER)
1827 else if (edge == AE_TOP_TO_BOTTOMRIGHT && scalear->alignment & RGN_ALIGN_BOTTOM &&
1828 ar->alignment == RGN_ALIGN_BOTTOM && ar->regiontype == RGN_TYPE_HEADER)
1837 static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1839 sActionzoneData *sad = event->customdata;
1842 if (event->type != EVT_ACTIONZONE_REGION) {
1843 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");
1844 return OPERATOR_CANCELLED;
1850 RegionMoveData *rmd = MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1853 op->customdata = rmd;
1858 rmd->edge = az->edge;
1859 rmd->origx = event->x;
1860 rmd->origy = event->y;
1861 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1863 /* if not set we do now, otherwise it uses type */
1864 if (rmd->ar->sizex == 0)
1865 rmd->ar->sizex = rmd->ar->winx;
1866 if (rmd->ar->sizey == 0)
1867 rmd->ar->sizey = rmd->ar->winy;
1869 /* now copy to regionmovedata */
1870 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1871 rmd->origval = rmd->ar->sizex;
1874 rmd->origval = rmd->ar->sizey;
1877 /* limit headers to standard height for now */
1878 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1879 maxsize = ED_area_headersize();
1883 CLAMP(rmd->maxsize, 0, maxsize);
1885 /* add temp handler */
1886 WM_event_add_modal_handler(C, op);
1888 return OPERATOR_RUNNING_MODAL;
1891 return OPERATOR_FINISHED;
1894 static int region_scale_get_maxsize(RegionMoveData *rmd)
1898 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1899 return (int) ( (rmd->sa->winx / UI_DPI_FAC) - UI_UNIT_X);
1902 if (rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) {
1903 /* this calculation seems overly verbose
1904 * can someone explain why this method is necessary? - campbell */
1905 maxsize = rmd->maxsize - ((rmd->sa->headertype == HEADERTOP) ? UI_UNIT_Y * 2 : UI_UNIT_Y) - (UI_UNIT_Y / 4);
1911 static void region_scale_validate_size(RegionMoveData *rmd)
1913 if ((rmd->ar->flag & RGN_FLAG_HIDDEN) == 0) {
1914 short *size, maxsize = -1;
1917 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT)
1918 size = &rmd->ar->sizex;
1920 size = &rmd->ar->sizey;
1922 maxsize = region_scale_get_maxsize(rmd);
1924 if (*size > maxsize && maxsize > 0)
1929 static void region_scale_toggle_hidden(bContext *C, RegionMoveData *rmd)
1931 region_toggle_hidden(C, rmd->ar, 0);
1932 region_scale_validate_size(rmd);
1935 static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
1937 RegionMoveData *rmd = op->customdata;
1940 /* execute the events */
1941 switch (event->type) {
1944 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1945 delta = event->x - rmd->origx;
1946 if (rmd->edge == AE_LEFT_TO_TOPRIGHT) delta = -delta;
1948 /* region sizes now get multiplied */
1949 delta /= UI_DPI_FAC;
1951 rmd->ar->sizex = rmd->origval + delta;
1952 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
1954 if (rmd->ar->sizex < UI_UNIT_X) {
1955 rmd->ar->sizex = rmd->origval;
1956 if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1957 region_scale_toggle_hidden(C, rmd);
1959 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
1960 region_scale_toggle_hidden(C, rmd);
1963 int maxsize = region_scale_get_maxsize(rmd);
1964 delta = event->y - rmd->origy;
1965 if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) delta = -delta;
1967 /* region sizes now get multiplied */
1968 delta /= UI_DPI_FAC;
1970 rmd->ar->sizey = rmd->origval + delta;
1971 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
1973 /* note, 'UI_UNIT_Y/4' means you need to drag the header almost
1974 * all the way down for it to become hidden, this is done
1975 * otherwise its too easy to do this by accident */
1976 if (rmd->ar->sizey < UI_UNIT_Y / 4) {
1977 rmd->ar->sizey = rmd->origval;
1978 if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1979 region_scale_toggle_hidden(C, rmd);
1981 else if (maxsize > 0 && (rmd->ar->sizey > maxsize))
1982 rmd->ar->sizey = maxsize;
1983 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
1984 region_scale_toggle_hidden(C, rmd);
1986 ED_area_tag_redraw(rmd->sa);
1987 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1992 if (event->val == KM_RELEASE) {
1994 if (ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1995 if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
1996 region_scale_toggle_hidden(C, rmd);
1998 else if (rmd->ar->flag & RGN_FLAG_TOO_SMALL) {
1999 region_scale_validate_size(rmd);
2002 ED_area_tag_redraw(rmd->sa);
2003 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2005 MEM_freeN(op->customdata);
2006 op->customdata = NULL;
2008 return OPERATOR_FINISHED;
2016 return OPERATOR_RUNNING_MODAL;
2019 static void region_scale_cancel(bContext *UNUSED(C), wmOperator *op)
2021 MEM_freeN(op->customdata);
2022 op->customdata = NULL;
2025 static void SCREEN_OT_region_scale(wmOperatorType *ot)
2028 ot->name = "Scale Region Size";
2029 ot->description = "Scale selected area";
2030 ot->idname = "SCREEN_OT_region_scale";
2032 ot->invoke = region_scale_invoke;
2033 ot->modal = region_scale_modal;
2034 ot->cancel = region_scale_cancel;
2036 ot->poll = ED_operator_areaactive;
2039 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2043 /* ************** frame change operator ***************************** */
2045 /* function to be called outside UI context, or for redo */
2046 static int frame_offset_exec(bContext *C, wmOperator *op)
2048 Main *bmain = CTX_data_main(C);
2049 Scene *scene = CTX_data_scene(C);
2052 delta = RNA_int_get(op->ptr, "delta");
2055 FRAMENUMBER_MIN_CLAMP(CFRA);
2058 sound_seek_scene(bmain, scene);
2060 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2062 return OPERATOR_FINISHED;
2065 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
2067 ot->name = "Frame Offset";
2068 ot->idname = "SCREEN_OT_frame_offset";
2069 ot->description = "Move current frame forward/backward by a given number";
2071 ot->exec = frame_offset_exec;
2073 ot->poll = ED_operator_screenactive_norender;
2077 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2081 /* function to be called outside UI context, or for redo */
2082 static int frame_jump_exec(bContext *C, wmOperator *op)
2084 Main *bmain = CTX_data_main(C);
2085 Scene *scene = CTX_data_scene(C);
2086 wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
2088 /* Don't change CFRA directly if animtimer is running as this can cause
2089 * first/last frame not to be actually shown (bad since for example physics
2090 * simulations aren't reset properly).
2093 ScreenAnimData *sad = animtimer->customdata;
2095 sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
2097 if (RNA_boolean_get(op->ptr, "end"))
2098 sad->nextfra = PEFRA;
2100 sad->nextfra = PSFRA;
2103 if (RNA_boolean_get(op->ptr, "end"))
2108 sound_seek_scene(bmain, scene);
2110 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2113 return OPERATOR_FINISHED;
2116 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
2118 ot->name = "Jump to Endpoint";
2119 ot->description = "Jump to first/last frame in frame range";
2120 ot->idname = "SCREEN_OT_frame_jump";
2122 ot->exec = frame_jump_exec;
2124 ot->poll = ED_operator_screenactive_norender;
2125 ot->flag = OPTYPE_UNDO;
2128 RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range");
2132 /* ************** jump to keyframe operator ***************************** */
2134 /* function to be called outside UI context, or for redo */
2135 static int keyframe_jump_exec(bContext *C, wmOperator *op)
2137 Main *bmain = CTX_data_main(C);
2138 Scene *scene = CTX_data_scene(C);
2139 Object *ob = CTX_data_active_object(C);
2140 bGPdata *gpd = CTX_data_gpencil_data(C);
2141 bDopeSheet ads = {NULL};
2145 const bool next = RNA_boolean_get(op->ptr, "next");
2150 return OPERATOR_CANCELLED;
2152 cfra = (float)(CFRA);
2154 /* init binarytree-list for getting keyframes */
2155 BLI_dlrbTree_init(&keys);
2157 /* seed up dummy dopesheet context with flags to perform necessary filtering */
2158 if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
2159 /* only selected channels are included */
2160 ads.filterflag |= ADS_FILTER_ONLYSEL;
2163 /* populate tree with keyframe nodes */
2164 scene_to_keylist(&ads, scene, &keys, NULL);
2167 ob_to_keylist(&ads, ob, &keys, NULL);
2169 gpencil_to_keylist(&ads, gpd, &keys);
2172 Mask *mask = CTX_data_edit_mask(C);
2174 MaskLayer *masklay = BKE_mask_layer_active(mask);
2175 mask_to_keylist(&ads, masklay, &keys);
2179 /* build linked-list for searching */
2180 BLI_dlrbTree_linkedlist_sync(&keys);
2182 /* find matching keyframe in the right direction */
2185 ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
2187 ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
2190 if (CFRA != (int)ak->cfra) {
2191 /* this changes the frame, so set the frame and we're done */
2192 CFRA = (int)ak->cfra;
2196 /* make this the new starting point for the search */
2200 } while ((ak != NULL) && (done == false));
2202 /* free temp stuff */
2203 BLI_dlrbTree_free(&keys);
2206 if (done == false) {
2207 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
2209 return OPERATOR_CANCELLED;
2212 sound_seek_scene(bmain, scene);
2214 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2216 return OPERATOR_FINISHED;
2220 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
2222 ot->name = "Jump to Keyframe";
2223 ot->description = "Jump to previous/next keyframe";
2224 ot->idname = "SCREEN_OT_keyframe_jump";
2226 ot->exec = keyframe_jump_exec;
2228 ot->poll = ED_operator_screenactive_norender;
2229 ot->flag = OPTYPE_UNDO;
2232 RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
2235 /* ************** jump to marker operator ***************************** */
2237 /* function to be called outside UI context, or for redo */
2238 static int marker_jump_exec(bContext *C, wmOperator *op)
2240 Main *bmain = CTX_data_main(C);
2241 Scene *scene = CTX_data_scene(C);
2244 const bool next = RNA_boolean_get(op->ptr, "next");
2247 /* find matching marker in the right direction */
2248 for (marker = scene->markers.first; marker; marker = marker->next) {
2250 if ((marker->frame > CFRA) && (!found || closest > marker->frame)) {
2251 closest = marker->frame;
2256 if ((marker->frame < CFRA) && (!found || closest < marker->frame)) {
2257 closest = marker->frame;
2265 BKE_report(op->reports, RPT_INFO, "No more markers to jump to in this direction");
2267 return OPERATOR_CANCELLED;
2272 sound_seek_scene(bmain, scene);
2274 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2276 return OPERATOR_FINISHED;
2280 static void SCREEN_OT_marker_jump(wmOperatorType *ot)
2282 ot->name = "Jump to Marker";
2283 ot->description = "Jump to previous/next marker";
2284 ot->idname = "SCREEN_OT_marker_jump";
2286 ot->exec = marker_jump_exec;
2288 ot->poll = ED_operator_screenactive_norender;
2289 ot->flag = OPTYPE_UNDO;
2292 RNA_def_boolean(ot->srna, "next", true, "Next Marker", "");
2295 /* ************** switch screen operator ***************************** */
2297 static bool screen_set_is_ok(bScreen *screen, bScreen *screen_prev)
2299 return ((screen->winid == 0) &&
2300 (screen->state == SCREENNORMAL) &&
2301 (screen != screen_prev) &&
2302 (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT)));
2305 /* function to be called outside UI context, or for redo */
2306 static int screen_set_exec(bContext *C, wmOperator *op)
2308 Main *bmain = CTX_data_main(C);
2309 bScreen *screen = CTX_wm_screen(C);
2310 bScreen *screen_prev = screen;
2312 ScrArea *sa = CTX_wm_area(C);
2313 int tot = BLI_listbase_count(&bmain->screen);
2314 int delta = RNA_int_get(op->ptr, "delta");
2316 /* temp screens are for userpref or render display */
2318 return OPERATOR_CANCELLED;
2322 screen = screen->id.next;
2323 if (screen == NULL) screen = bmain->screen.first;
2324 if (screen_set_is_ok(screen, screen_prev)) {
2329 else if (delta == -1) {
2331 screen = screen->id.prev;
2332 if (screen == NULL) screen = bmain->screen.last;
2333 if (screen_set_is_ok(screen, screen_prev)) {
2342 if (screen && screen_prev != screen) {
2343 /* return to previous state before switching screens */
2344 if (sa && sa->full) {
2345 ED_screen_full_restore(C, sa); /* may free 'screen_prev' */
2348 ED_screen_set(C, screen);
2349 return OPERATOR_FINISHED;
2351 return OPERATOR_CANCELLED;
2354 static void SCREEN_OT_screen_set(wmOperatorType *ot)
2356 ot->name = "Set Screen";
2357 ot->description = "Cycle through available screens";
2358 ot->idname = "SCREEN_OT_screen_set";
2360 ot->exec = screen_set_exec;
2361 ot->poll = ED_operator_screenactive;
2364 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2367 /* ************** screen full-area operator ***************************** */
2370 /* function to be called outside UI context, or for redo */
2371 static int screen_maximize_area_exec(bContext *C, wmOperator *op)
2373 bScreen *screen = CTX_wm_screen(C);
2375 const bool hide_panels = RNA_boolean_get(op->ptr, "use_hide_panels");
2377 /* search current screen for 'fullscreen' areas */
2378 /* prevents restoring info header, when mouse is over it */
2379 for (sa = screen->areabase.first; sa; sa = sa->next) {
2380 if (sa->full) break;
2384 sa = CTX_wm_area(C);
2388 if (!ELEM(screen->state, SCREENNORMAL, SCREENFULL)) {
2389 return OPERATOR_CANCELLED;
2391 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENFULL);
2394 if (!ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) {
2395 return OPERATOR_CANCELLED;
2397 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED);
2400 return OPERATOR_FINISHED;
2403 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
2407 ot->name = "Toggle Fullscreen Area";
2408 ot->description = "Toggle display selected area as fullscreen/maximized";
2409 ot->idname = "SCREEN_OT_screen_full_area";
2411 ot->exec = screen_maximize_area_exec;
2412 ot->poll = ED_operator_areaactive;
2415 prop = RNA_def_boolean(ot->srna, "use_hide_panels", false, "Hide Panels", "Hide all the panels");
2416 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2419 /* ************** join area operator ********************************************** */
2421 /* operator state vars used:
2422 * x1, y1 mouse coord in first area, which will disappear
2423 * x2, y2 mouse coord in 2nd area, which will become joined
2427 * init() find edge based on state vars
2428 * test if the edge divides two areas,
2429 * store active and nonactive area,
2431 * apply() do the actual join
2433 * exit() cleanup, send notifier
2437 * exec() calls init, apply, exit
2439 * invoke() sets mouse coords in x,y
2443 * modal() accept modal events while doing it
2444 * call apply() with active window and nonactive window
2445 * call exit() and remove handler when LMB confirm
2448 typedef struct sAreaJoinData {
2449 ScrArea *sa1; /* first area to be considered */
2450 ScrArea *sa2; /* second area to be considered */
2451 ScrArea *scr; /* designed for removal */
2456 /* validate selection inside screen, set variables OK */
2457 /* return 0: init failed */
2458 /* XXX todo: find edge based on (x,y) and set other area? */
2459 static int area_join_init(bContext *C, wmOperator *op)
2462 sAreaJoinData *jd = NULL;
2467 /* required properties, make negative to get return 0 if not set by caller */
2468 x1 = RNA_int_get(op->ptr, "min_x");
2469 y1 = RNA_int_get(op->ptr, "min_y");
2470 x2 = RNA_int_get(op->ptr, "max_x");
2471 y2 = RNA_int_get(op->ptr, "max_y");
2473 sa1 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x1, y1);
2474 sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x2, y2);
2475 if (sa1 == NULL || sa2 == NULL || sa1 == sa2)
2478 /* do areas share an edge? */
2479 if (sa1->v1 == sa2->v1 || sa1->v1 == sa2->v2 || sa1->v1 == sa2->v3 || sa1->v1 == sa2->v4) shared++;
2480 if (sa1->v2 == sa2->v1 || sa1->v2 == sa2->v2 || sa1->v2 == sa2->v3 || sa1->v2 == sa2->v4) shared++;
2481 if (sa1->v3 == sa2->v1 || sa1->v3 == sa2->v2 || sa1->v3 == sa2->v3 || sa1->v3 == sa2->v4) shared++;
2482 if (sa1->v4 == sa2->v1 || sa1->v4 == sa2->v2 || sa1->v4 == sa2->v3 || sa1->v4 == sa2->v4) shared++;
2484 printf("areas don't share edge\n");
2488 jd = (sAreaJoinData *)MEM_callocN(sizeof(sAreaJoinData), "op_area_join");
2491 jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2493 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2495 op->customdata = jd;
2500 /* apply the join of the areas (space types) */
2501 static int area_join_apply(bContext *C, wmOperator *op)
2503 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2506 if (!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)) {
2509 if (CTX_wm_area(C) == jd->sa2) {
2510 CTX_wm_area_set(C, NULL);
2511 CTX_wm_region_set(C, NULL);
2517 /* finish operation */
2518 static void area_join_exit(bContext *C, wmOperator *op)
2520 if (op->customdata) {
2521 MEM_freeN(op->customdata);
2522 op->customdata = NULL;
2525 /* this makes sure aligned edges will result in aligned grabbing */
2526 removedouble_scredges(CTX_wm_screen(C));
2527 removenotused_scredges(CTX_wm_screen(C));
2528 removenotused_scrverts(CTX_wm_screen(C));
2531 static int area_join_exec(bContext *C, wmOperator *op)
2533 if (!area_join_init(C, op))
2534 return OPERATOR_CANCELLED;
2536 area_join_apply(C, op);
2537 area_join_exit(C, op);
2539 return OPERATOR_FINISHED;
2542 /* interaction callback */
2543 static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2546 if (event->type == EVT_ACTIONZONE_AREA) {
2547 sActionzoneData *sad = event->customdata;
2549 if (sad == NULL || sad->modifier > 0) {
2550 return OPERATOR_PASS_THROUGH;
2553 /* verify *sad itself */
2554 if (sad->sa1 == NULL || sad->sa2 == NULL)
2555 return OPERATOR_PASS_THROUGH;
2557 /* is this our *sad? if areas equal it should be passed on */
2558 if (sad->sa1 == sad->sa2)
2559 return OPERATOR_PASS_THROUGH;
2561 /* prepare operator state vars */
2562 RNA_int_set(op->ptr, "min_x", sad->x);
2563 RNA_int_set(op->ptr, "min_y", sad->y);
2564 RNA_int_set(op->ptr, "max_x", event->x);
2565 RNA_int_set(op->ptr, "max_y", event->y);
2569 if (!area_join_init(C, op))
2570 return OPERATOR_PASS_THROUGH;
2572 /* add temp handler */
2573 WM_event_add_modal_handler(C, op);
2575 return OPERATOR_RUNNING_MODAL;
2578 static void area_join_cancel(bContext *C, wmOperator *op)
2580 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2583 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2584 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
2587 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
2588 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2591 WM_event_add_notifier(C, NC_WINDOW, NULL);
2593 area_join_exit(C, op);
2596 /* modal callback while selecting area (space) that will be removed */
2597 static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
2599 bScreen *sc = CTX_wm_screen(C);
2600 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2602 /* execute the events */
2603 switch (event->type) {
2607 ScrArea *sa = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y);
2611 if (jd->sa1 != sa) {
2612 dir = area_getorientation(jd->sa1, sa);
2614 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2616 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2619 /* we are not bordering on the previously selected area
2620 * we check if area has common border with the one marked for removal
2621 * in this case we can swap areas.
2623 dir = area_getorientation(sa, jd->sa2);
2625 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2626 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2629 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2630 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2633 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2637 WM_event_add_notifier(C, NC_WINDOW, NULL);
2640 /* we are back in the area previously selected for keeping
2641 * we swap the areas if possible to allow user to choose */
2642 if (jd->sa2 != NULL) {
2643 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2644 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2647 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2648 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2649 dir = area_getorientation(jd->sa1, jd->sa2);
2651 printf("oops, didn't expect that!\n");
2655 dir = area_getorientation(jd->sa1, sa);
2657 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2659 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2662 WM_event_add_notifier(C, NC_WINDOW, NULL);
2668 if (event->val == KM_RELEASE) {
2669 ED_area_tag_redraw(jd->sa1);
2670 ED_area_tag_redraw(jd->sa2);
2672 area_join_apply(C, op);
2673 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2674 area_join_exit(C, op);
2675 return OPERATOR_FINISHED;
2681 area_join_cancel(C, op);
2682 return OPERATOR_CANCELLED;
2685 return OPERATOR_RUNNING_MODAL;
2688 /* Operator for joining two areas (space types) */
2689 static void SCREEN_OT_area_join(wmOperatorType *ot)
2692 ot->name = "Join Area";
2693 ot->description = "Join selected areas into new window";
2694 ot->idname = "SCREEN_OT_area_join";
2697 ot->exec = area_join_exec;
2698 ot->invoke = area_join_invoke;
2699 ot->modal = area_join_modal;
2700 ot->poll = screen_active_editable;
2701 ot->cancel = area_join_cancel;
2704 ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2707 RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
2708 RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2709 RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2710 RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2713 /* ******************************* */
2715 static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2717 wmWindow *win = CTX_wm_window(C);
2718 bScreen *sc = CTX_wm_screen(C);
2721 PointerRNA ptr1, ptr2;
2723 const int winsize_x = WM_window_pixels_x(win);
2724 const int winsize_y = WM_window_pixels_y(win);
2726 actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, event->x, event->y);
2728 if (actedge == NULL) return OPERATOR_CANCELLED;
2730 pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
2731 layout = UI_popup_menu_layout(pup);
2733 WM_operator_properties_create(&ptr1, "SCREEN_OT_area_join");
2735 /* mouse cursor on edge, '4' can fail on wide edges... */
2736 RNA_int_set(&ptr1, "min_x", event->x + 4);
2737 RNA_int_set(&ptr1, "min_y", event->y + 4);
2738 RNA_int_set(&ptr1, "max_x", event->x - 4);
2739 RNA_int_set(&ptr1, "max_y", event->y - 4);
2741 WM_operator_properties_create(&ptr2, "SCREEN_OT_area_split");
2743 /* store initial mouse cursor position */
2744 RNA_int_set(&ptr2, "mouse_x", event->x);
2745 RNA_int_set(&ptr2, "mouse_y", event->y);
2747 uiItemFullO(layout, "SCREEN_OT_area_split", NULL, ICON_NONE, ptr2.data, WM_OP_INVOKE_DEFAULT, 0);
2748 uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, ptr1.data, WM_OP_INVOKE_DEFAULT, 0);
2750 UI_popup_menu_end(C, pup);
2752 return OPERATOR_INTERFACE;
2755 static void SCREEN_OT_area_options(wmOperatorType *ot)
2758 ot->name = "Area Options";
2759 ot->description = "Operations for splitting and merging";
2760 ot->idname = "SCREEN_OT_area_options";
2763 ot->invoke = screen_area_options_invoke;
2765 ot->poll = ED_operator_screen_mainwinactive;
2768 ot->flag = OPTYPE_INTERNAL;
2772 /* ******************************* */
2775 static int spacedata_cleanup_exec(bContext *C, wmOperator *op)
2777 Main *bmain = CTX_data_main(C);
2782 for (screen = bmain->screen.first; screen; screen = screen->id.next) {
2783 for (sa = screen->areabase.first; sa; sa = sa->next) {
2784 if (sa->spacedata.first != sa->spacedata.last) {
2785 SpaceLink *sl = sa->spacedata.first;
2787 BLI_remlink(&sa->spacedata, sl);
2788 tot += BLI_listbase_count(&sa->spacedata);
2789 BKE_spacedata_freelist(&sa->spacedata);
2790 BLI_addtail(&sa->spacedata, sl);
2794 BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
2796 return OPERATOR_FINISHED;
2799 static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
2802 ot->name = "Clean-up Space-data";
2803 ot->description = "Remove unused settings for invisible editors";
2804 ot->idname = "SCREEN_OT_spacedata_cleanup";
2807 ot->exec = spacedata_cleanup_exec;
2808 ot->poll = WM_operator_winactive;
2812 /* ************** repeat last operator ***************************** */
2814 static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
2816 wmOperator *lastop = CTX_wm_manager(C)->operators.last;
2819 WM_operator_repeat(C, lastop);
2821 return OPERATOR_CANCELLED;
2824 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2827 ot->name = "Repeat Last";
2828 ot->description = "Repeat last action";
2829 ot->idname = "SCREEN_OT_repeat_last";
2832 ot->exec = repeat_last_exec;
2834 ot->poll = ED_operator_screenactive;
2838 static int repeat_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2840 wmWindowManager *wm = CTX_wm_manager(C);
2846 items = BLI_listbase_count(&wm->operators);
2848 return OPERATOR_CANCELLED;
2850 pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
2851 layout = UI_popup_menu_layout(pup);
2853 for (i = items - 1, lastop = wm->operators.last; lastop; lastop = lastop->prev, i--)
2854 if (WM_operator_repeat_check(C, lastop))
2855 uiItemIntO(layout, RNA_struct_ui_name(lastop->type->srna), ICON_NONE, op->type->idname, "index", i);
2857 UI_popup_menu_end(C, pup);
2859 return OPERATOR_INTERFACE;
2862 static int repeat_history_exec(bContext *C, wmOperator *op)
2864 wmWindowManager *wm = CTX_wm_manager(C);
2866 op = BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2868 /* let's put it as last operator in list */
2869 BLI_remlink(&wm->operators, op);
2870 BLI_addtail(&wm->operators, op);
2872 WM_operator_repeat(C, op);
2875 return OPERATOR_FINISHED;
2878 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2881 ot->name = "Repeat History";
2882 ot->description = "Display menu for previous actions performed";
2883 ot->idname = "SCREEN_OT_repeat_history";
2886 ot->invoke = repeat_history_invoke;
2887 ot->exec = repeat_history_exec;
2889 ot->poll = ED_operator_screenactive;
2891 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2894 /* ********************** redo operator ***************************** */
2896 static int redo_last_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
2898 wmOperator *lastop = WM_operator_last_redo(C);
2901 WM_operator_redo_popup(C, lastop);
2903 return OPERATOR_CANCELLED;
2906 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2909 ot->name = "Redo Last";
2910 ot->description = "Display menu for last action performed";
2911 ot->idname = "SCREEN_OT_redo_last";
2914 ot->invoke = redo_last_invoke;
2916 ot->poll = ED_operator_screenactive;
2919 /* ************** region four-split operator ***************************** */
2921 static void view3d_localview_update_rv3d(struct RegionView3D *rv3d)
2923 if (rv3d->localvd) {
2924 rv3d->localvd->view = rv3d->view;
2925 rv3d->localvd->persp = rv3d->persp;
2926 copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);
2930 static void region_quadview_init_rv3d(ScrArea *sa, ARegion *ar,
2931 const char viewlock, const char view, const char persp)
2933 RegionView3D *rv3d = ar->regiondata;
2935 if (persp == RV3D_CAMOB) {
2936 ED_view3d_lastview_store(rv3d);
2939 rv3d->viewlock = viewlock;
2941 rv3d->persp = persp;
2943 ED_view3d_lock(rv3d);
2944 view3d_localview_update_rv3d(rv3d);
2945 if ((viewlock & RV3D_BOXCLIP) && (persp == RV3D_ORTHO)) {
2946 ED_view3d_quadview_update(sa, ar, true);
2950 /* insert a region in the area region list */
2951 static int region_quadview_exec(bContext *C, wmOperator *op)
2953 ARegion *ar = CTX_wm_region(C);
2956 if (ar->regiontype != RGN_TYPE_WINDOW) {
2957 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2959 else if (ar->alignment == RGN_ALIGN_QSPLIT) {
2960 /* Exit quad-view */
2961 ScrArea *sa = CTX_wm_area(C);
2964 /* keep current region */
2967 if (sa->spacetype == SPACE_VIEW3D) {
2969 RegionView3D *rv3d = ar->regiondata;
2971 /* if this is a locked view, use settings from 'User' view */
2972 if (rv3d->viewlock) {
2976 if (ED_view3d_context_user_region(C, &v3d_user, &ar_user)) {
2977 if (ar != ar_user) {
2978 SWAP(void *, ar->regiondata, ar_user->regiondata);
2979 rv3d = ar->regiondata;
2984 rv3d->viewlock_quad = RV3D_VIEWLOCK_INIT;
2986 rv3d->rflag &= ~RV3D_CLIPPING;
2988 /* accumulate locks, incase they're mixed */
2989 for (ar_iter = sa->regionbase.first; ar_iter; ar_iter = ar_iter->next) {
2990 if (ar_iter->regiontype == RGN_TYPE_WINDOW) {
2991 RegionView3D *rv3d_iter = ar_iter->regiondata;
2992 rv3d->viewlock_quad |= rv3d_iter->viewlock;
2997 for (ar = sa->regionbase.first; ar; ar = arn) {
2999 if (ar->alignment == RGN_ALIGN_QSPLIT) {
3000 ED_region_exit(C, ar);
3001 BKE_area_region_free(sa->type, ar);
3002 BLI_remlink(&sa->regionbase, ar);
3006 ED_area_tag_redraw(sa);
3007 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3009 else if (ar->next) {
3010 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
3013 /* Enter quad-view */
3014 ScrArea *sa = CTX_wm_area(C);
3018 ar->alignment = RGN_ALIGN_QSPLIT;
3020 for (count = 0; count < 3; count++) {
3021 newar = BKE_area_region_copy(sa->type, ar);
3022 BLI_addtail(&sa->regionbase, newar);
3025 /* lock views and set them */
3026 if (sa->spacetype == SPACE_VIEW3D) {
3027 View3D *v3d = sa->spacedata.first;
3028 int index_qsplit = 0;
3030 /* run ED_view3d_lock() so the correct 'rv3d->viewquat' is set,
3031 * otherwise when restoring rv3d->localvd the 'viewquat' won't
3032 * match the 'view', set on entering localview See: [#26315],
3034 * We could avoid manipulating rv3d->localvd here if exiting
3035 * localview with a 4-split would assign these view locks */
3036 RegionView3D *rv3d = ar->regiondata;
3037 const char viewlock = (rv3d->viewlock_quad & RV3D_VIEWLOCK_INIT) ?
3038 (rv3d->viewlock_quad & ~RV3D_VIEWLOCK_INIT) : RV3D_LOCKED;
3040 region_quadview_init_rv3d(sa, ar, viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3041 region_quadview_init_rv3d(sa, (ar = ar->next), viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3042 region_quadview_init_rv3d(sa, (ar = ar->next), viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3043 /* forcing camera is distracting */
3045 if (v3d->camera) region_quadview_init_rv3d(sa, (ar = ar->next), 0, RV3D_VIEW_CAMERA, RV3D_CAMOB);
3046 else region_quadview_init_rv3d(sa, (ar = ar->next), 0, RV3D_VIEW_USER, RV3D_PERSP);
3051 ED_area_tag_redraw(sa);
3052 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3056 return OPERATOR_FINISHED;
3059 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
3062 ot->name = "Toggle Quad View";
3063 ot->description = "Split selected area into camera, front, right & top views";
3064 ot->idname = "SCREEN_OT_region_quadview";
3067 ot->exec = region_quadview_exec;
3068 ot->poll = ED_operator_region_view3d_active;
3073 /* ************** region flip operator ***************************** */
3075 /* flip a region alignment */
3076 static int region_flip_exec(bContext *C, wmOperator *UNUSED(op))
3078 ARegion *ar = CTX_wm_region(C);
3081 return OPERATOR_CANCELLED;
3083 if (ar->alignment == RGN_ALIGN_TOP)
3084 ar->alignment = RGN_ALIGN_BOTTOM;
3085 else if (ar->alignment == RGN_ALIGN_BOTTOM)
3086 ar->alignment = RGN_ALIGN_TOP;
3087 else if (ar->alignment == RGN_ALIGN_LEFT)
3088 ar->alignment = RGN_ALIGN_RIGHT;
3089 else if (ar->alignment == RGN_ALIGN_RIGHT)
3090 ar->alignment = RGN_ALIGN_LEFT;
3092 ED_area_tag_redraw(CTX_wm_area(C));
3093 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3095 return OPERATOR_FINISHED;
3099 static void SCREEN_OT_region_flip(wmOperatorType *ot)
3102 ot->name = "Flip Region";
3103 ot->idname = "SCREEN_OT_region_flip";
3104 ot->description = "Toggle the region's alignment (left/right or top/bottom)";
3107 ot->exec = region_flip_exec;
3108 ot->poll = ED_operator_areaactive;
3112 /* ************** header operator ***************************** */
3113 static int header_exec(bContext *C, wmOperator *UNUSED(op))
3115 ARegion *ar = screen_find_region_type(C, RGN_TYPE_HEADER);
3118 return OPERATOR_CANCELLED;
3121 ar->flag ^= RGN_FLAG_HIDDEN;
3123 ED_area_tag_redraw(CTX_wm_area(C));
3125 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3127 return OPERATOR_FINISHED;
3130 static void SCREEN_OT_header(wmOperatorType *ot)
3133 ot->name = "Header";
3134 ot->description = "Display header";
3135 ot->idname = "SCREEN_OT_header";
3138 ot->exec = header_exec;
3141 /* ************** header flip operator ***************************** */
3143 /* flip a header region alignment */
3144 static int header_flip_exec(bContext *C, wmOperator *UNUSED(op))
3146 ARegion *ar = screen_find_region_type(C, RGN_TYPE_HEADER);
3149 return OPERATOR_CANCELLED;
3152 /* copied from SCREEN_OT_region_flip */
3153 if (ar->alignment == RGN_ALIGN_TOP)
3154 ar->alignment = RGN_ALIGN_BOTTOM;
3155 else if (ar->alignment == RGN_ALIGN_BOTTOM)
3156 ar->alignment = RGN_ALIGN_TOP;
3157 else if (ar->alignment == RGN_ALIGN_LEFT)
3158 ar->alignment = RGN_ALIGN_RIGHT;
3159 else if (ar->alignment == RGN_ALIGN_RIGHT)
3160 ar->alignment = RGN_ALIGN_LEFT;
3162 ED_area_tag_redraw(CTX_wm_area(C));
3164 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3166 return OPERATOR_FINISHED;
3170 static void SCREEN_OT_header_flip(wmOperatorType *ot)
3173 ot->name = "Flip Header Region";
3174 ot->idname = "SCREEN_OT_header_flip";
3175 ot->description = "Toggle the header over/below the main window area";