4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2008 Blender Foundation.
21 * All rights reserved.
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/screen/screen_ops.c
35 #include "MEM_guardedalloc.h"
38 #include "BLI_blenlib.h"
39 #include "BLI_editVert.h"
40 #include "BLI_dlrbTree.h"
41 #include "BLI_utildefines.h"
43 #include "DNA_armature_types.h"
44 #include "DNA_lattice_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_curve_types.h"
47 #include "DNA_scene_types.h"
48 #include "DNA_meta_types.h"
50 #include "BKE_context.h"
51 #include "BKE_customdata.h"
52 #include "BKE_global.h"
55 #include "BKE_report.h"
56 #include "BKE_scene.h"
57 #include "BKE_screen.h"
58 #include "BKE_tessmesh.h"
59 #include "BKE_sound.h"
66 #include "ED_screen.h"
67 #include "ED_object.h"
68 #include "ED_armature.h"
69 #include "ED_screen_types.h"
70 #include "ED_keyframes_draw.h"
71 #include "ED_view3d.h"
73 #include "RNA_access.h"
74 #include "RNA_define.h"
76 #include "UI_interface.h"
77 #include "UI_resources.h"
79 #include "wm_window.h"
81 #include "screen_intern.h" /* own module include */
83 #define KM_MODAL_CANCEL 1
84 #define KM_MODAL_APPLY 2
85 #define KM_MODAL_STEP10 3
86 #define KM_MODAL_STEP10_OFF 4
88 /* ************** Exported Poll tests ********************** */
90 int ED_operator_regionactive(bContext *C)
92 if(CTX_wm_window(C)==NULL) return 0;
93 if(CTX_wm_screen(C)==NULL) return 0;
94 if(CTX_wm_region(C)==NULL) return 0;
98 int ED_operator_areaactive(bContext *C)
100 if(CTX_wm_window(C)==NULL) return 0;
101 if(CTX_wm_screen(C)==NULL) return 0;
102 if(CTX_wm_area(C)==NULL) return 0;
106 int ED_operator_screenactive(bContext *C)
108 if(CTX_wm_window(C)==NULL) return 0;
109 if(CTX_wm_screen(C)==NULL) return 0;
113 /* XXX added this to prevent anim state to change during renders */
114 static int ED_operator_screenactive_norender(bContext *C)
116 if(G.rendering) return 0;
117 if(CTX_wm_window(C)==NULL) return 0;
118 if(CTX_wm_screen(C)==NULL) return 0;
123 static int screen_active_editable(bContext *C)
125 if(ED_operator_screenactive(C)) {
126 /* no full window splitting allowed */
127 if(CTX_wm_screen(C)->full != SCREENNORMAL)
134 /* when mouse is over area-edge */
135 int ED_operator_screen_mainwinactive(bContext *C)
137 if(CTX_wm_window(C)==NULL) return 0;
138 if(CTX_wm_screen(C)==NULL) return 0;
139 if (CTX_wm_screen(C)->subwinactive!=CTX_wm_screen(C)->mainwin) return 0;
143 int ED_operator_scene_editable(bContext *C)
145 Scene *scene= CTX_data_scene(C);
146 if(scene && scene->id.lib==NULL)
151 int ED_operator_objectmode(bContext *C)
153 Scene *scene= CTX_data_scene(C);
154 Object *obact= CTX_data_active_object(C);
156 if(scene==NULL || scene->id.lib)
158 if( CTX_data_edit_object(C) )
161 /* add a check for ob->mode too? */
162 if(obact && obact->mode)
169 static int ed_spacetype_test(bContext *C, int type)
171 if(ED_operator_areaactive(C)) {
172 SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
173 return sl && (sl->spacetype == type);
178 int ED_operator_view3d_active(bContext *C)
180 return ed_spacetype_test(C, SPACE_VIEW3D);
183 int ED_operator_region_view3d_active(bContext *C)
185 if(CTX_wm_region_view3d(C))
188 CTX_wm_operator_poll_msg_set(C, "expected a view3d region");
192 /* generic for any view2d which uses anim_ops */
193 int ED_operator_animview_active(bContext *C)
195 if(ED_operator_areaactive(C)) {
196 SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
197 if (sl && (ELEM6(sl->spacetype, SPACE_SEQ, SPACE_SOUND, SPACE_ACTION, SPACE_NLA, SPACE_IPO, SPACE_TIME)))
201 CTX_wm_operator_poll_msg_set(C, "expected an timeline/animation area to be active");
205 int ED_operator_timeline_active(bContext *C)
207 return ed_spacetype_test(C, SPACE_TIME);
210 int ED_operator_outliner_active(bContext *C)
212 return ed_spacetype_test(C, SPACE_OUTLINER);
215 int ED_operator_outliner_active_no_editobject(bContext *C)
217 if(ed_spacetype_test(C, SPACE_OUTLINER)) {
218 Object *ob = ED_object_active_context(C);
219 Object *obedit= CTX_data_edit_object(C);
220 if(ob && ob == obedit)
228 int ED_operator_file_active(bContext *C)
230 return ed_spacetype_test(C, SPACE_FILE);
233 int ED_operator_action_active(bContext *C)
235 return ed_spacetype_test(C, SPACE_ACTION);
238 int ED_operator_buttons_active(bContext *C)
240 return ed_spacetype_test(C, SPACE_BUTS);
243 int ED_operator_node_active(bContext *C)
245 SpaceNode *snode= CTX_wm_space_node(C);
247 if(snode && snode->edittree)
254 int ED_operator_graphedit_active(bContext *C)
256 return ed_spacetype_test(C, SPACE_IPO);
259 int ED_operator_sequencer_active(bContext *C)
261 return ed_spacetype_test(C, SPACE_SEQ);
264 int ED_operator_image_active(bContext *C)
266 return ed_spacetype_test(C, SPACE_IMAGE);
269 int ED_operator_nla_active(bContext *C)
271 return ed_spacetype_test(C, SPACE_NLA);
274 int ED_operator_logic_active(bContext *C)
276 return ed_spacetype_test(C, SPACE_LOGIC);
279 int ED_operator_info_active(bContext *C)
281 return ed_spacetype_test(C, SPACE_INFO);
285 int ED_operator_console_active(bContext *C)
287 return ed_spacetype_test(C, SPACE_CONSOLE);
290 int ED_operator_object_active(bContext *C)
292 Object *ob = ED_object_active_context(C);
293 return ((ob != NULL) && !(ob->restrictflag & OB_RESTRICT_VIEW));
296 int ED_operator_object_active_editable(bContext *C)
298 Object *ob = ED_object_active_context(C);
299 return ((ob != NULL) && !(ob->id.lib) && !(ob->restrictflag & OB_RESTRICT_VIEW));
302 int ED_operator_object_active_editable_mesh(bContext *C)
304 Object *ob = ED_object_active_context(C);
305 return ((ob != NULL) && !(ob->id.lib) && !(ob->restrictflag & OB_RESTRICT_VIEW) && ob->type == OB_MESH && !(((ID *)ob->data)->lib));
308 int ED_operator_object_active_editable_font(bContext *C)
310 Object *ob = ED_object_active_context(C);
311 return ((ob != NULL) && !(ob->id.lib) && !(ob->restrictflag & OB_RESTRICT_VIEW) && ob->type == OB_FONT);
314 int ED_operator_editmesh(bContext *C)
316 Object *obedit= CTX_data_edit_object(C);
317 if(obedit && obedit->type==OB_MESH)
318 return NULL != ((Mesh *)obedit->data)->edit_btmesh;
322 int ED_operator_editmesh_view3d(bContext *C)
324 return ED_operator_editmesh(C) && ED_operator_view3d_active(C);
327 int ED_operator_editmesh_region_view3d(bContext *C)
329 if(ED_operator_editmesh(C) && CTX_wm_region_view3d(C))
332 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editmesh");
336 int ED_operator_editarmature(bContext *C)
338 Object *obedit= CTX_data_edit_object(C);
339 if(obedit && obedit->type==OB_ARMATURE)
340 return NULL != ((bArmature *)obedit->data)->edbo;
344 int ED_operator_posemode(bContext *C)
346 Object *obact= CTX_data_active_object(C);
348 if (obact && !(obact->mode & OB_MODE_EDIT)) {
350 if((obpose= ED_object_pose_armature(obact))) {
351 if((obact == obpose) || (obact->mode & OB_MODE_WEIGHT_PAINT)) {
360 /* wrapper for ED_space_image_show_uvedit */
361 int ED_operator_uvedit(bContext *C)
363 SpaceImage *sima= CTX_wm_space_image(C);
364 Object *obedit= CTX_data_edit_object(C);
366 return ED_space_image_show_uvedit(sima, obedit);
369 int ED_operator_uvmap(bContext *C)
371 Object *obedit= CTX_data_edit_object(C);
372 BMEditMesh *em= NULL;
374 if(obedit && obedit->type==OB_MESH)
375 em= ((Mesh *)obedit->data)->edit_btmesh;
377 if(em && (em->bm->totface))
383 int ED_operator_editsurfcurve(bContext *C)
385 Object *obedit= CTX_data_edit_object(C);
386 if(obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
387 return NULL != ((Curve *)obedit->data)->editnurb;
391 int ED_operator_editsurfcurve_region_view3d(bContext *C)
393 if(ED_operator_editsurfcurve(C) && CTX_wm_region_view3d(C))
396 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editcurve");
400 int ED_operator_editcurve(bContext *C)
402 Object *obedit= CTX_data_edit_object(C);
403 if(obedit && obedit->type==OB_CURVE)
404 return NULL != ((Curve *)obedit->data)->editnurb;
408 int ED_operator_editsurf(bContext *C)
410 Object *obedit= CTX_data_edit_object(C);
411 if(obedit && obedit->type==OB_SURF)
412 return NULL != ((Curve *)obedit->data)->editnurb;
416 int ED_operator_editfont(bContext *C)
418 Object *obedit= CTX_data_edit_object(C);
419 if(obedit && obedit->type==OB_FONT)
420 return NULL != ((Curve *)obedit->data)->editfont;
424 int ED_operator_editlattice(bContext *C)
426 Object *obedit= CTX_data_edit_object(C);
427 if(obedit && obedit->type==OB_LATTICE)
428 return NULL != ((Lattice *)obedit->data)->editlatt;
432 int ED_operator_editmball(bContext *C)
434 Object *obedit= CTX_data_edit_object(C);
435 if(obedit && obedit->type==OB_MBALL)
436 return NULL != ((MetaBall *)obedit->data)->editelems;
440 /* *************************** action zone operator ************************** */
442 /* operator state vars used:
447 apply() set actionzone event
449 exit() free customdata
455 invoke() check if in zone
456 add customdata, put mouseco and area in it
459 modal() accept modal events while doing it
460 call apply() with gesture info, active window, nonactive window
461 call exit() and remove handler when LMB confirm
465 typedef struct sActionzoneData {
468 int x, y, gesture_dir, modifier;
471 /* used by other operators too */
472 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
475 sa= scr->areabase.first;
477 if(BLI_in_rcti(&sa->totrct, x, y)) break;
484 /* quick poll to save operators to be created and handled */
485 static int actionzone_area_poll(bContext *C)
487 wmWindow *win= CTX_wm_window(C);
488 ScrArea *sa= CTX_wm_area(C);
492 int x= win->eventstate->x;
493 int y= win->eventstate->y;
495 for(az= sa->actionzones.first; az; az= az->next)
496 if(BLI_in_rcti(&az->rect, x, y))
502 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
506 for(az= sa->actionzones.first; az; az= az->next) {
507 if(BLI_in_rcti(&az->rect, x, y)) {
508 if(az->type == AZONE_AREA) {
509 /* no triangle intersect but a hotspot circle based on corner */
510 int radius= (x-az->x1)*(x-az->x1) + (y-az->y1)*(y-az->y1);
512 if(radius <= AZONESPOT*AZONESPOT)
515 else if(az->type == AZONE_REGION) {
525 static void actionzone_exit(wmOperator *op)
528 MEM_freeN(op->customdata);
529 op->customdata= NULL;
532 /* send EVT_ACTIONZONE event */
533 static void actionzone_apply(bContext *C, wmOperator *op, int type)
536 wmWindow *win= CTX_wm_window(C);
537 sActionzoneData *sad= op->customdata;
539 sad->modifier= RNA_int_get(op->ptr, "modifier");
541 event= *(win->eventstate); /* XXX huh huh? make api call */
543 event.type= EVT_ACTIONZONE_AREA;
545 event.type= EVT_ACTIONZONE_REGION;
546 event.customdata= op->customdata;
547 event.customdatafree= TRUE;
548 op->customdata= NULL;
550 wm_event_add(win, &event);
553 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
555 AZone *az= is_in_area_actionzone(CTX_wm_area(C), event->x, event->y);
556 sActionzoneData *sad;
560 return OPERATOR_PASS_THROUGH;
562 /* ok we do the actionzone */
563 sad= op->customdata= MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
564 sad->sa1= CTX_wm_area(C);
566 sad->x= event->x; sad->y= event->y;
568 /* region azone directly reacts on mouse clicks */
569 if(sad->az->type==AZONE_REGION) {
570 actionzone_apply(C, op, AZONE_REGION);
572 return OPERATOR_FINISHED;
575 /* add modal handler */
576 WM_event_add_modal_handler(C, op);
578 return OPERATOR_RUNNING_MODAL;
583 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
585 sActionzoneData *sad= op->customdata;
587 int mindelta= sad->az->type==AZONE_REGION?1:12;
589 switch(event->type) {
591 /* calculate gesture direction */
592 deltax= (event->x - sad->x);
593 deltay= (event->y - sad->y);
595 if(deltay > ABS(deltax))
596 sad->gesture_dir= 'n';
597 else if(deltax > ABS(deltay))
598 sad->gesture_dir= 'e';
599 else if(deltay < -ABS(deltax))
600 sad->gesture_dir= 's';
602 sad->gesture_dir= 'w';
604 /* gesture is large enough? */
605 if(ABS(deltax) > mindelta || ABS(deltay) > mindelta) {
607 /* second area, for join */
608 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
609 /* apply sends event */
610 actionzone_apply(C, op, sad->az->type);
613 return OPERATOR_FINISHED;
618 return OPERATOR_CANCELLED;
621 return OPERATOR_CANCELLED;
625 return OPERATOR_RUNNING_MODAL;
628 static int actionzone_cancel(bContext *UNUSED(C), wmOperator *op)
632 return OPERATOR_CANCELLED;
635 static void SCREEN_OT_actionzone(wmOperatorType *ot)
638 ot->name= "Handle area action zones";
639 ot->description= "Handle area action zones for mouse actions/gestures";
640 ot->idname= "SCREEN_OT_actionzone";
642 ot->invoke= actionzone_invoke;
643 ot->modal= actionzone_modal;
644 ot->poll= actionzone_area_poll;
645 ot->cancel= actionzone_cancel;
647 ot->flag= OPTYPE_BLOCKING;
649 RNA_def_int(ot->srna, "modifier", 0, 0, 2, "modifier", "modifier state", 0, 2);
652 /* ************** swap area operator *********************************** */
654 /* operator state vars used:
656 sa2 area to swap with
660 init() set custom data for operator, based on actionzone event custom data
662 cancel() cancel the operator
664 exit() cleanup, send notifier
668 invoke() gets called on shift+lmb drag in actionzone
669 call init(), add handler
671 modal() accept modal events while doing it
675 typedef struct sAreaSwapData {
679 static int area_swap_init(wmOperator *op, wmEvent *event)
681 sAreaSwapData *sd= NULL;
682 sActionzoneData *sad= event->customdata;
684 if(sad==NULL || sad->sa1==NULL)
687 sd= MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
696 static void area_swap_exit(bContext *C, wmOperator *op)
698 WM_cursor_restore(CTX_wm_window(C));
700 MEM_freeN(op->customdata);
701 op->customdata= NULL;
704 static int area_swap_cancel(bContext *C, wmOperator *op)
706 area_swap_exit(C, op);
707 return OPERATOR_CANCELLED;
710 static int area_swap_invoke(bContext *C, wmOperator *op, wmEvent *event)
713 if(!area_swap_init(op, event))
714 return OPERATOR_PASS_THROUGH;
716 /* add modal handler */
717 WM_cursor_modal(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
718 WM_event_add_modal_handler(C, op);
720 return OPERATOR_RUNNING_MODAL;
724 static int area_swap_modal(bContext *C, wmOperator *op, wmEvent *event)
726 sActionzoneData *sad= op->customdata;
728 switch(event->type) {
730 /* second area, for join */
731 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
733 case LEFTMOUSE: /* release LMB */
734 if(event->val==KM_RELEASE) {
735 if(!sad->sa2 || sad->sa1 == sad->sa2) {
737 return area_swap_cancel(C, op);
740 ED_area_tag_redraw(sad->sa1);
741 ED_area_tag_redraw(sad->sa2);
743 ED_area_swapspace(C, sad->sa1, sad->sa2);
745 area_swap_exit(C, op);
747 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
749 return OPERATOR_FINISHED;
754 return area_swap_cancel(C, op);
756 return OPERATOR_RUNNING_MODAL;
759 static void SCREEN_OT_area_swap(wmOperatorType *ot)
761 ot->name= "Swap areas";
762 ot->description= "Swap selected areas screen positions";
763 ot->idname= "SCREEN_OT_area_swap";
765 ot->invoke= area_swap_invoke;
766 ot->modal= area_swap_modal;
767 ot->poll= ED_operator_areaactive;
768 ot->cancel= area_swap_cancel;
770 ot->flag= OPTYPE_BLOCKING;
773 /* *********** Duplicate area as new window operator ****************** */
775 /* operator callback */
776 static int area_dupli_invoke(bContext *C, wmOperator *op, wmEvent *event)
778 wmWindow *newwin, *win;
783 win= CTX_wm_window(C);
784 sc= CTX_wm_screen(C);
788 if(event->type==EVT_ACTIONZONE_AREA) {
789 sActionzoneData *sad= event->customdata;
792 return OPERATOR_PASS_THROUGH;
797 /* poll() checks area context, but we don't accept full-area windows */
798 if(sc->full != SCREENNORMAL) {
799 if(event->type==EVT_ACTIONZONE_AREA)
801 return OPERATOR_CANCELLED;
804 /* adds window to WM */
806 BLI_translate_rcti(&rect, win->posx, win->posy);
807 newwin= WM_window_open(C, &rect);
809 /* allocs new screen and adds to newly created window, using window size */
810 newsc= ED_screen_add(newwin, CTX_data_scene(C), sc->id.name+2);
811 newwin->screen= newsc;
813 /* copy area to new screen */
814 area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
816 ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
818 /* screen, areas init */
819 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
821 if(event->type==EVT_ACTIONZONE_AREA)
824 return OPERATOR_FINISHED;
827 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
829 ot->name= "Duplicate Area into New Window";
830 ot->description= "Duplicate selected area into new window";
831 ot->idname= "SCREEN_OT_area_dupli";
833 ot->invoke= area_dupli_invoke;
834 ot->poll= ED_operator_areaactive;
838 /* ************** move area edge operator *********************************** */
840 /* operator state vars used:
841 x, y mouse coord near edge
842 delta movement of edge
846 init() set default property values, find edge based on mouse coords, test
847 if the edge can be moved, select edges, calculate min and max movement
849 apply() apply delta on selection
851 exit() cleanup, send notifier
853 cancel() cancel moving
857 exec() execute without any user interaction, based on properties
858 call init(), apply(), exit()
860 invoke() gets called on mouse click near edge
861 call init(), add handler
863 modal() accept modal events while doing it
864 call apply() with delta motion
865 call exit() and remove handler
869 typedef struct sAreaMoveData {
870 int bigger, smaller, origval, step;
874 /* helper call to move area-edge, sets limits */
875 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
878 int areaminy= ED_area_headersize()+1;
880 /* we check all areas and test for free space with MINSIZE */
881 *bigger= *smaller= 100000;
883 for(sa= sc->areabase.first; sa; sa= sa->next) {
885 int y1= sa->v2->vec.y - sa->v1->vec.y-areaminy;
887 /* if top or down edge selected, test height */
888 if(sa->v1->flag && sa->v4->flag)
889 *bigger= MIN2(*bigger, y1);
890 else if(sa->v2->flag && sa->v3->flag)
891 *smaller= MIN2(*smaller, y1);
894 int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
896 /* if left or right edge selected, test width */
897 if(sa->v1->flag && sa->v2->flag)
898 *bigger= MIN2(*bigger, x1);
899 else if(sa->v3->flag && sa->v4->flag)
900 *smaller= MIN2(*smaller, x1);
905 /* validate selection inside screen, set variables OK */
906 /* return 0: init failed */
907 static int area_move_init (bContext *C, wmOperator *op)
909 bScreen *sc= CTX_wm_screen(C);
914 /* required properties */
915 x= RNA_int_get(op->ptr, "x");
916 y= RNA_int_get(op->ptr, "y");
919 actedge= screen_find_active_scredge(sc, x, y);
920 if(actedge==NULL) return 0;
922 md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
925 md->dir= scredge_is_horizontal(actedge)?'h':'v';
926 if(md->dir=='h') md->origval= actedge->v1->vec.y;
927 else md->origval= actedge->v1->vec.x;
929 select_connected_scredge(sc, actedge);
930 /* now all vertices with 'flag==1' are the ones that can be moved. */
932 area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
937 /* moves selected screen edge amount of delta, used by split & move */
938 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
940 wmWindow *win= CTX_wm_window(C);
941 bScreen *sc= CTX_wm_screen(C);
944 int areaminy= ED_area_headersize()+1;
946 delta= CLAMPIS(delta, -smaller, bigger);
948 for (v1= sc->vertbase.first; v1; v1= v1->next) {
950 /* that way a nice AREAGRID */
951 if((dir=='v') && v1->vec.x>0 && v1->vec.x<win->sizex-1) {
952 v1->vec.x= origval + delta;
953 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
955 if((dir=='h') && v1->vec.y>0 && v1->vec.y<win->sizey-1) {
956 v1->vec.y= origval + delta;
958 v1->vec.y+= AREAGRID-1;
959 v1->vec.y-= (v1->vec.y % AREAGRID);
961 /* prevent too small top header */
962 if(v1->vec.y > win->sizey-areaminy)
963 v1->vec.y= win->sizey-areaminy;
968 for(sa= sc->areabase.first; sa; sa= sa->next) {
969 if(sa->v1->flag || sa->v2->flag || sa->v3->flag || sa->v4->flag)
970 ED_area_tag_redraw(sa);
973 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL); /* redraw everything */
976 static void area_move_apply(bContext *C, wmOperator *op)
978 sAreaMoveData *md= op->customdata;
981 delta= RNA_int_get(op->ptr, "delta");
982 area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
985 static void area_move_exit(bContext *C, wmOperator *op)
988 MEM_freeN(op->customdata);
989 op->customdata= NULL;
991 /* this makes sure aligned edges will result in aligned grabbing */
992 removedouble_scrverts(CTX_wm_screen(C));
993 removedouble_scredges(CTX_wm_screen(C));
996 static int area_move_exec(bContext *C, wmOperator *op)
998 if(!area_move_init(C, op))
999 return OPERATOR_CANCELLED;
1001 area_move_apply(C, op);
1002 area_move_exit(C, op);
1004 return OPERATOR_FINISHED;
1007 /* interaction callback */
1008 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
1010 RNA_int_set(op->ptr, "x", event->x);
1011 RNA_int_set(op->ptr, "y", event->y);
1013 if(!area_move_init(C, op))
1014 return OPERATOR_PASS_THROUGH;
1016 /* add temp handler */
1017 WM_event_add_modal_handler(C, op);
1019 return OPERATOR_RUNNING_MODAL;
1022 static int area_move_cancel(bContext *C, wmOperator *op)
1025 RNA_int_set(op->ptr, "delta", 0);
1026 area_move_apply(C, op);
1027 area_move_exit(C, op);
1029 return OPERATOR_CANCELLED;
1032 /* modal callback for while moving edges */
1033 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
1035 sAreaMoveData *md= op->customdata;
1038 /* execute the events */
1039 switch(event->type) {
1042 x= RNA_int_get(op->ptr, "x");
1043 y= RNA_int_get(op->ptr, "y");
1045 delta= (md->dir == 'v')? event->x - x: event->y - y;
1046 if(md->step) delta= delta - (delta % md->step);
1047 RNA_int_set(op->ptr, "delta", delta);
1049 area_move_apply(C, op);
1054 switch (event->val) {
1055 case KM_MODAL_APPLY:
1056 area_move_exit(C, op);
1057 return OPERATOR_FINISHED;
1059 case KM_MODAL_CANCEL:
1060 return area_move_cancel(C, op);
1062 case KM_MODAL_STEP10:
1065 case KM_MODAL_STEP10_OFF:
1071 return OPERATOR_RUNNING_MODAL;
1074 static void SCREEN_OT_area_move(wmOperatorType *ot)
1077 ot->name= "Move area edges";
1078 ot->description= "Move selected area edges";
1079 ot->idname= "SCREEN_OT_area_move";
1081 ot->exec= area_move_exec;
1082 ot->invoke= area_move_invoke;
1083 ot->cancel= area_move_cancel;
1084 ot->modal= area_move_modal;
1085 ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
1087 ot->flag= OPTYPE_BLOCKING;
1090 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1091 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1092 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1095 /* ************** split area operator *********************************** */
1098 operator state vars:
1100 dir direction 'v' or 'h'
1102 operator customdata:
1103 area pointer to (active) area
1104 x, y last used mouse pos
1109 init() set default property values, find area based on context
1111 apply() split area based on state vars
1113 exit() cleanup, send notifier
1115 cancel() remove duplicated area
1119 exec() execute without any user interaction, based on state vars
1120 call init(), apply(), exit()
1122 invoke() gets called on mouse click in action-widget
1123 call init(), add modal handler
1124 call apply() with initial motion
1126 modal() accept modal events while doing it
1127 call move-areas code with delta motion
1128 call exit() or cancel() and remove handler
1132 #define SPLIT_STARTED 1
1133 #define SPLIT_PROGRESS 2
1135 typedef struct sAreaSplitData {
1136 int x, y; /* last used mouse position */
1138 int origval; /* for move areas */
1139 int bigger, smaller; /* constraints for moving new edge */
1140 int delta; /* delta move edge */
1141 int origmin, origsize; /* to calculate fac, for property storage */
1142 int previewmode; /* draw previewline, then split */
1144 ScrEdge *nedge; /* new edge */
1145 ScrArea *sarea; /* start area */
1146 ScrArea *narea; /* new area */
1150 /* generic init, menu case, doesn't need active area */
1151 static int area_split_menu_init(bContext *C, wmOperator *op)
1156 sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1159 sd->sarea= CTX_wm_area(C);
1162 int dir= RNA_enum_get(op->ptr, "direction");
1165 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1167 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1172 /* generic init, no UI stuff here, assumes active area */
1173 static int area_split_init(bContext *C, wmOperator *op)
1175 ScrArea *sa= CTX_wm_area(C);
1177 int areaminy= ED_area_headersize()+1;
1180 /* required context */
1181 if(sa==NULL) return 0;
1183 /* required properties */
1184 dir= RNA_enum_get(op->ptr, "direction");
1187 if(dir=='v' && sa->winx < 2*AREAMINX) return 0;
1188 if(dir=='h' && sa->winy < 2*areaminy) return 0;
1191 sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1195 sd->origsize= dir=='v' ? sa->winx:sa->winy;
1196 sd->origmin = dir=='v' ? sa->totrct.xmin:sa->totrct.ymin;
1201 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1202 /* used with split operator */
1203 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1205 ScrVert *sav1= sa->v1;
1206 ScrVert *sav2= sa->v2;
1207 ScrVert *sav3= sa->v3;
1208 ScrVert *sav4= sa->v4;
1209 ScrVert *sbv1= sb->v1;
1210 ScrVert *sbv2= sb->v2;
1211 ScrVert *sbv3= sb->v3;
1212 ScrVert *sbv4= sb->v4;
1214 if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
1215 return screen_findedge(screen, sav1, sav2);
1217 else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
1218 return screen_findedge(screen, sav2, sav3);
1220 else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
1221 return screen_findedge(screen, sav3, sav4);
1223 else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
1224 return screen_findedge(screen, sav1, sav4);
1231 /* do the split, return success */
1232 static int area_split_apply(bContext *C, wmOperator *op)
1234 bScreen *sc= CTX_wm_screen(C);
1235 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1239 fac= RNA_float_get(op->ptr, "factor");
1240 dir= RNA_enum_get(op->ptr, "direction");
1242 sd->narea= area_split(sc, sd->sarea, dir, fac, 0); /* 0 = no merge */
1247 sd->nedge= area_findsharededge(sc, sd->sarea, sd->narea);
1249 /* select newly created edge, prepare for moving edge */
1250 for(sv= sc->vertbase.first; sv; sv= sv->next)
1253 sd->nedge->v1->flag= 1;
1254 sd->nedge->v2->flag= 1;
1256 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
1257 else sd->origval= sd->nedge->v1->vec.x;
1259 ED_area_tag_redraw(sd->sarea);
1260 ED_area_tag_redraw(sd->narea);
1262 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1270 static void area_split_exit(bContext *C, wmOperator *op)
1272 if (op->customdata) {
1273 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1274 if(sd->sarea) ED_area_tag_redraw(sd->sarea);
1275 if(sd->narea) ED_area_tag_redraw(sd->narea);
1278 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H|AREA_FLAG_DRAWSPLIT_V);
1280 MEM_freeN(op->customdata);
1281 op->customdata = NULL;
1284 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1286 /* this makes sure aligned edges will result in aligned grabbing */
1287 removedouble_scrverts(CTX_wm_screen(C));
1288 removedouble_scredges(CTX_wm_screen(C));
1292 /* UI callback, adds new handler */
1293 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
1298 /* no full window splitting allowed */
1299 if(CTX_wm_screen(C)->full != SCREENNORMAL)
1300 return OPERATOR_CANCELLED;
1302 if(event->type==EVT_ACTIONZONE_AREA) {
1303 sActionzoneData *sad= event->customdata;
1305 if(sad->modifier>0) {
1306 return OPERATOR_PASS_THROUGH;
1309 /* verify *sad itself */
1310 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
1311 return OPERATOR_PASS_THROUGH;
1313 /* is this our *sad? if areas not equal it should be passed on */
1314 if(CTX_wm_area(C)!=sad->sa1 || sad->sa1!=sad->sa2)
1315 return OPERATOR_PASS_THROUGH;
1317 /* prepare operator state vars */
1318 if(sad->gesture_dir=='n' || sad->gesture_dir=='s') {
1320 RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1324 RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1326 RNA_enum_set(op->ptr, "direction", dir);
1328 /* general init, also non-UI case, adds customdata, sets area and defaults */
1329 if(!area_split_init(C, op))
1330 return OPERATOR_PASS_THROUGH;
1337 /* retrieve initial mouse coord, so we can find the active edge */
1338 if(RNA_property_is_set(op->ptr, "mouse_x"))
1339 x= RNA_int_get(op->ptr, "mouse_x");
1343 if(RNA_property_is_set(op->ptr, "mouse_y"))
1344 y= RNA_int_get(op->ptr, "mouse_y");
1348 actedge= screen_find_active_scredge(CTX_wm_screen(C), x, y);
1350 return OPERATOR_CANCELLED;
1352 dir= scredge_is_horizontal(actedge)?'v':'h';
1354 RNA_enum_set(op->ptr, "direction", dir);
1356 /* special case, adds customdata, sets defaults */
1357 if(!area_split_menu_init(C, op))
1358 return OPERATOR_CANCELLED;
1362 sd= (sAreaSplitData *)op->customdata;
1367 if(event->type==EVT_ACTIONZONE_AREA) {
1370 if(area_split_apply(C, op)) {
1371 area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
1373 /* add temp handler for edge move or cancel */
1374 WM_event_add_modal_handler(C, op);
1376 return OPERATOR_RUNNING_MODAL;
1381 /* add temp handler for edge move or cancel */
1382 WM_event_add_modal_handler(C, op);
1384 return OPERATOR_RUNNING_MODAL;
1388 return OPERATOR_PASS_THROUGH;
1391 /* function to be called outside UI context, or for redo */
1392 static int area_split_exec(bContext *C, wmOperator *op)
1395 if(!area_split_init(C, op))
1396 return OPERATOR_CANCELLED;
1398 area_split_apply(C, op);
1399 area_split_exit(C, op);
1401 return OPERATOR_FINISHED;
1405 static int area_split_cancel(bContext *C, wmOperator *op)
1407 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1409 if(sd->previewmode) {
1412 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1413 if (CTX_wm_area(C) == sd->narea) {
1414 CTX_wm_area_set(C, NULL);
1415 CTX_wm_region_set(C, NULL);
1420 area_split_exit(C, op);
1422 return OPERATOR_CANCELLED;
1425 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
1427 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1431 /* execute the events */
1432 switch(event->type) {
1434 dir= RNA_enum_get(op->ptr, "direction");
1436 sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
1437 if(sd->previewmode==0)
1438 area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1441 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H|AREA_FLAG_DRAWSPLIT_V);
1442 ED_area_tag_redraw(sd->sarea);
1444 sd->sarea= screen_areahascursor(CTX_wm_screen(C), event->x, event->y); /* area context not set */
1447 ED_area_tag_redraw(sd->sarea);
1449 sd->origsize= sd->sarea->winx;
1450 sd->origmin= sd->sarea->totrct.xmin;
1451 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1454 sd->origsize= sd->sarea->winy;
1455 sd->origmin= sd->sarea->totrct.ymin;
1456 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1460 CTX_wm_window(C)->screen->do_draw= 1;
1464 fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
1465 RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1470 if(sd->previewmode) {
1471 area_split_apply(C, op);
1472 area_split_exit(C, op);
1473 return OPERATOR_FINISHED;
1476 if(event->val==KM_RELEASE) { /* mouse up */
1477 area_split_exit(C, op);
1478 return OPERATOR_FINISHED;
1482 case RIGHTMOUSE: /* cancel operation */
1484 return area_split_cancel(C, op);
1487 return OPERATOR_RUNNING_MODAL;
1490 static EnumPropertyItem prop_direction_items[] = {
1491 {'h', "HORIZONTAL", 0, "Horizontal", ""},
1492 {'v', "VERTICAL", 0, "Vertical", ""},
1493 {0, NULL, 0, NULL, NULL}};
1495 static void SCREEN_OT_area_split(wmOperatorType *ot)
1497 ot->name = "Split area";
1498 ot->description= "Split selected area into new windows";
1499 ot->idname = "SCREEN_OT_area_split";
1501 ot->exec= area_split_exec;
1502 ot->invoke= area_split_invoke;
1503 ot->modal= area_split_modal;
1504 ot->cancel= area_split_cancel;
1506 ot->poll= screen_active_editable;
1507 ot->flag= OPTYPE_BLOCKING;
1510 RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1511 RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1512 RNA_def_int(ot->srna, "mouse_x", -100, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
1513 RNA_def_int(ot->srna, "mouse_y", -100, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
1518 /* ************** scale region edge operator *********************************** */
1520 typedef struct RegionMoveData {
1524 int bigger, smaller, origval;
1532 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
1537 if(edge==AE_RIGHT_TO_TOPLEFT || edge==AE_LEFT_TO_TOPRIGHT) {
1538 dist = sa->totrct.xmax - sa->totrct.xmin;
1539 } else { /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
1540 dist = sa->totrct.ymax - sa->totrct.ymin;
1543 /* subtractwidth of regions on opposite side
1544 * prevents dragging regions into other opposite regions */
1545 for (ar=sa->regionbase.first; ar; ar=ar->next) {
1549 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1551 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1553 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1555 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1558 /* case of regions in regions, like operator properties panel */
1559 /* these can sit on top of other regions such as headers, so account for this */
1560 else if (edge == AE_BOTTOM_TO_TOPLEFT && scalear->alignment & RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_TOP && ar->regiontype == RGN_TYPE_HEADER)
1562 else if (edge == AE_TOP_TO_BOTTOMRIGHT && scalear->alignment & RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_BOTTOM && ar->regiontype == RGN_TYPE_HEADER)
1569 static int region_scale_invoke(bContext *C, wmOperator *op, wmEvent *event)
1571 sActionzoneData *sad= event->customdata;
1574 if(event->type!=EVT_ACTIONZONE_REGION) {
1575 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");
1576 return OPERATOR_CANCELLED;
1582 RegionMoveData *rmd= MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1585 op->customdata= rmd;
1590 rmd->edge= az->edge;
1591 rmd->origx= event->x;
1592 rmd->origy= event->y;
1593 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1595 /* if not set we do now, otherwise it uses type */
1596 if(rmd->ar->sizex==0)
1597 rmd->ar->sizex= rmd->ar->type->prefsizex;
1598 if(rmd->ar->sizey==0)
1599 rmd->ar->sizey= rmd->ar->type->prefsizey;
1601 /* now copy to regionmovedata */
1602 if(rmd->edge==AE_LEFT_TO_TOPRIGHT || rmd->edge==AE_RIGHT_TO_TOPLEFT) {
1603 rmd->origval= rmd->ar->sizex;
1605 rmd->origval= rmd->ar->sizey;
1608 /* limit headers to standard height for now */
1609 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1610 maxsize = rmd->ar->type->prefsizey;
1614 CLAMP(rmd->maxsize, 0, maxsize);
1616 /* add temp handler */
1617 WM_event_add_modal_handler(C, op);
1619 return OPERATOR_RUNNING_MODAL;
1622 return OPERATOR_FINISHED;
1625 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1627 RegionMoveData *rmd= op->customdata;
1630 /* execute the events */
1631 switch(event->type) {
1634 if(rmd->edge==AE_LEFT_TO_TOPRIGHT || rmd->edge==AE_RIGHT_TO_TOPLEFT) {
1635 delta= event->x - rmd->origx;
1636 if(rmd->edge==AE_LEFT_TO_TOPRIGHT) delta= -delta;
1638 rmd->ar->sizex= rmd->origval + delta;
1639 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
1641 if(rmd->ar->sizex < UI_UNIT_X) {
1642 rmd->ar->sizex= rmd->origval;
1643 if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1644 ED_region_toggle_hidden(C, rmd->ar);
1646 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1647 ED_region_toggle_hidden(C, rmd->ar);
1651 delta= event->y - rmd->origy;
1652 if(rmd->edge==AE_BOTTOM_TO_TOPLEFT) delta= -delta;
1654 rmd->ar->sizey= rmd->origval + delta;
1655 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
1657 if(rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) {
1658 /* this calculation seems overly verbose
1659 * can someone explain why this method is necessary? - campbell */
1660 maxsize = rmd->maxsize - ((rmd->sa->headertype==HEADERTOP)?UI_UNIT_Y*2:UI_UNIT_Y) - (UI_UNIT_Y/4);
1663 /* note, 'UI_UNIT_Y/4' means you need to drag the header almost
1664 * all the way down for it to become hidden, this is done
1665 * otherwise its too easy to do this by accident */
1666 if(rmd->ar->sizey < UI_UNIT_Y/4 || (maxsize > 0 && (rmd->ar->sizey > maxsize)) ) {
1667 rmd->ar->sizey= rmd->origval;
1668 if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1669 ED_region_toggle_hidden(C, rmd->ar);
1671 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1672 ED_region_toggle_hidden(C, rmd->ar);
1674 ED_area_tag_redraw(rmd->sa);
1675 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1680 if(event->val==KM_RELEASE) {
1682 if(ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1683 if(rmd->ar->flag & RGN_FLAG_HIDDEN) {
1684 ED_region_toggle_hidden(C, rmd->ar);
1685 ED_area_tag_redraw(rmd->sa);
1686 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1689 MEM_freeN(op->customdata);
1690 op->customdata = NULL;
1692 return OPERATOR_FINISHED;
1700 return OPERATOR_RUNNING_MODAL;
1703 static int region_scale_cancel(bContext *UNUSED(C), wmOperator *op)
1705 MEM_freeN(op->customdata);
1706 op->customdata = NULL;
1708 return OPERATOR_CANCELLED;
1711 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1714 ot->name= "Scale Region Size";
1715 ot->description= "Scale selected area";
1716 ot->idname= "SCREEN_OT_region_scale";
1718 ot->invoke= region_scale_invoke;
1719 ot->modal= region_scale_modal;
1720 ot->cancel= region_scale_cancel;
1722 ot->poll= ED_operator_areaactive;
1724 ot->flag= OPTYPE_BLOCKING;
1728 /* ************** frame change operator ***************************** */
1730 /* function to be called outside UI context, or for redo */
1731 static int frame_offset_exec(bContext *C, wmOperator *op)
1733 Main *bmain= CTX_data_main(C);
1734 Scene *scene= CTX_data_scene(C);
1737 delta = RNA_int_get(op->ptr, "delta");
1739 scene->r.cfra += delta;
1740 scene->r.subframe = 0.f;
1742 sound_seek_scene(bmain, scene);
1744 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1746 return OPERATOR_FINISHED;
1749 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
1751 ot->name = "Frame Offset";
1752 ot->idname = "SCREEN_OT_frame_offset";
1754 ot->exec= frame_offset_exec;
1756 ot->poll= ED_operator_screenactive_norender;
1760 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1764 /* function to be called outside UI context, or for redo */
1765 static int frame_jump_exec(bContext *C, wmOperator *op)
1767 Main *bmain= CTX_data_main(C);
1768 Scene *scene= CTX_data_scene(C);
1769 wmTimer *animtimer= CTX_wm_screen(C)->animtimer;
1771 /* Don't change CFRA directly if animtimer is running as this can cause
1772 * first/last frame not to be actually shown (bad since for example physics
1773 * simulations aren't reset properly).
1776 ScreenAnimData *sad = animtimer->customdata;
1778 sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
1780 if (RNA_boolean_get(op->ptr, "end"))
1781 sad->nextfra= PEFRA;
1783 sad->nextfra= PSFRA;
1786 if (RNA_boolean_get(op->ptr, "end"))
1791 sound_seek_scene(bmain, scene);
1793 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1796 return OPERATOR_FINISHED;
1799 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1801 ot->name = "Jump to Endpoint";
1802 ot->description= "Jump to first/last frame in frame range";
1803 ot->idname = "SCREEN_OT_frame_jump";
1805 ot->exec= frame_jump_exec;
1807 ot->poll= ED_operator_screenactive_norender;
1808 ot->flag= OPTYPE_UNDO;
1811 RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range.");
1815 /* ************** jump to keyframe operator ***************************** */
1817 /* function to be called outside UI context, or for redo */
1818 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1820 Main *bmain= CTX_data_main(C);
1821 Scene *scene= CTX_data_scene(C);
1822 Object *ob= CTX_data_active_object(C);
1823 bDopeSheet ads= {NULL};
1827 short next= RNA_boolean_get(op->ptr, "next");
1832 return OPERATOR_CANCELLED;
1834 cfra= (float)(CFRA);
1836 /* init binarytree-list for getting keyframes */
1837 BLI_dlrbTree_init(&keys);
1839 /* populate tree with keyframe nodes */
1840 scene_to_keylist(&ads, scene, &keys, NULL);
1843 ob_to_keylist(&ads, ob, &keys, NULL);
1845 /* build linked-list for searching */
1846 BLI_dlrbTree_linkedlist_sync(&keys);
1848 /* find matching keyframe in the right direction */
1851 ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1853 ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1856 if (CFRA != (int)ak->cfra) {
1857 /* this changes the frame, so set the frame and we're done */
1858 CFRA= (int)ak->cfra;
1862 /* make this the new starting point for the search */
1866 } while ((ak != NULL) && (done == 0));
1870 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
1872 /* free temp stuff */
1873 BLI_dlrbTree_free(&keys);
1875 sound_seek_scene(bmain, scene);
1877 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1879 return OPERATOR_FINISHED;
1882 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1884 ot->name = "Jump to Keyframe";
1885 ot->description= "Jump to previous/next keyframe";
1886 ot->idname = "SCREEN_OT_keyframe_jump";
1888 ot->exec= keyframe_jump_exec;
1890 ot->poll= ED_operator_screenactive_norender;
1891 ot->flag= OPTYPE_UNDO;
1894 RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1897 /* ************** switch screen operator ***************************** */
1900 /* function to be called outside UI context, or for redo */
1901 static int screen_set_exec(bContext *C, wmOperator *op)
1903 bScreen *screen= CTX_wm_screen(C);
1904 bScreen *screen_prev= screen;
1906 ScrArea *sa= CTX_wm_area(C);
1907 int tot= BLI_countlist(&CTX_data_main(C)->screen);
1908 int delta= RNA_int_get(op->ptr, "delta");
1910 /* temp screens are for userpref or render display */
1912 return OPERATOR_CANCELLED;
1916 screen= screen->id.next;
1917 if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1918 if(screen->winid==0 && screen->full==0 && screen != screen_prev)
1922 else if(delta== -1) {
1924 screen= screen->id.prev;
1925 if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1926 if(screen->winid==0 && screen->full==0 && screen != screen_prev)
1934 if(screen && screen_prev != screen) {
1935 /* return to previous state before switching screens */
1936 if(sa && sa->full) {
1937 ED_screen_full_restore(C, sa); /* may free 'screen_prev' */
1940 ED_screen_set(C, screen);
1941 return OPERATOR_FINISHED;
1943 return OPERATOR_CANCELLED;
1946 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1948 ot->name = "Set Screen";
1949 ot->description= "Cycle through available screens";
1950 ot->idname = "SCREEN_OT_screen_set";
1952 ot->exec= screen_set_exec;
1953 ot->poll= ED_operator_screenactive;
1956 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1959 /* ************** screen full-area operator ***************************** */
1962 /* function to be called outside UI context, or for redo */
1963 static int screen_full_area_exec(bContext *C, wmOperator *UNUSED(op))
1965 bScreen *screen = CTX_wm_screen(C);
1968 /* search current screen for 'fullscreen' areas */
1969 /* prevents restoring info header, when mouse is over it */
1970 for (sa=screen->areabase.first; sa; sa=sa->next) {
1971 if (sa->full) break;
1974 if(sa==NULL) sa= CTX_wm_area(C);
1976 ED_screen_full_toggle(C, CTX_wm_window(C), sa);
1977 return OPERATOR_FINISHED;
1980 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1982 ot->name = "Toggle Full Screen";
1983 ot->description= "Toggle display selected area as fullscreen";
1984 ot->idname = "SCREEN_OT_screen_full_area";
1986 ot->exec= screen_full_area_exec;
1987 ot->poll= ED_operator_areaactive;
1994 /* ************** join area operator ********************************************** */
1996 /* operator state vars used:
1997 x1, y1 mouse coord in first area, which will disappear
1998 x2, y2 mouse coord in 2nd area, which will become joined
2002 init() find edge based on state vars
2003 test if the edge divides two areas,
2004 store active and nonactive area,
2006 apply() do the actual join
2008 exit() cleanup, send notifier
2012 exec() calls init, apply, exit
2014 invoke() sets mouse coords in x,y
2018 modal() accept modal events while doing it
2019 call apply() with active window and nonactive window
2020 call exit() and remove handler when LMB confirm
2024 typedef struct sAreaJoinData
2026 ScrArea *sa1; /* first area to be considered */
2027 ScrArea *sa2; /* second area to be considered */
2028 ScrArea *scr; /* designed for removal */
2033 /* validate selection inside screen, set variables OK */
2034 /* return 0: init failed */
2035 /* XXX todo: find edge based on (x,y) and set other area? */
2036 static int area_join_init(bContext *C, wmOperator *op)
2039 sAreaJoinData* jd= NULL;
2044 /* required properties, make negative to get return 0 if not set by caller */
2045 x1= RNA_int_get(op->ptr, "min_x");
2046 y1= RNA_int_get(op->ptr, "min_y");
2047 x2= RNA_int_get(op->ptr, "max_x");
2048 y2= RNA_int_get(op->ptr, "max_y");
2050 sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
2051 sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
2052 if(sa1==NULL || sa2==NULL || sa1==sa2)
2055 /* do areas share an edge? */
2056 if(sa1->v1==sa2->v1 || sa1->v1==sa2->v2 || sa1->v1==sa2->v3 || sa1->v1==sa2->v4) shared++;
2057 if(sa1->v2==sa2->v1 || sa1->v2==sa2->v2 || sa1->v2==sa2->v3 || sa1->v2==sa2->v4) shared++;
2058 if(sa1->v3==sa2->v1 || sa1->v3==sa2->v2 || sa1->v3==sa2->v3 || sa1->v3==sa2->v4) shared++;
2059 if(sa1->v4==sa2->v1 || sa1->v4==sa2->v2 || sa1->v4==sa2->v3 || sa1->v4==sa2->v4) shared++;
2061 printf("areas don't share edge\n");
2065 jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
2068 jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2070 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2077 /* apply the join of the areas (space types) */
2078 static int area_join_apply(bContext *C, wmOperator *op)
2080 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2083 if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
2086 if (CTX_wm_area(C) == jd->sa2) {
2087 CTX_wm_area_set(C, NULL);
2088 CTX_wm_region_set(C, NULL);
2094 /* finish operation */
2095 static void area_join_exit(bContext *C, wmOperator *op)
2097 if (op->customdata) {
2098 MEM_freeN(op->customdata);
2099 op->customdata = NULL;
2102 /* this makes sure aligned edges will result in aligned grabbing */
2103 removedouble_scredges(CTX_wm_screen(C));
2104 removenotused_scredges(CTX_wm_screen(C));
2105 removenotused_scrverts(CTX_wm_screen(C));
2108 static int area_join_exec(bContext *C, wmOperator *op)
2110 if(!area_join_init(C, op))
2111 return OPERATOR_CANCELLED;
2113 area_join_apply(C, op);
2114 area_join_exit(C, op);
2116 return OPERATOR_FINISHED;
2119 /* interaction callback */
2120 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
2123 if(event->type==EVT_ACTIONZONE_AREA) {
2124 sActionzoneData *sad= event->customdata;
2126 if(sad->modifier>0) {
2127 return OPERATOR_PASS_THROUGH;
2130 /* verify *sad itself */
2131 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
2132 return OPERATOR_PASS_THROUGH;
2134 /* is this our *sad? if areas equal it should be passed on */
2135 if(sad->sa1==sad->sa2)
2136 return OPERATOR_PASS_THROUGH;
2138 /* prepare operator state vars */
2139 RNA_int_set(op->ptr, "min_x", sad->x);
2140 RNA_int_set(op->ptr, "min_y", sad->y);
2141 RNA_int_set(op->ptr, "max_x", event->x);
2142 RNA_int_set(op->ptr, "max_y", event->y);
2146 if(!area_join_init(C, op))
2147 return OPERATOR_PASS_THROUGH;
2149 /* add temp handler */
2150 WM_event_add_modal_handler(C, op);
2152 return OPERATOR_RUNNING_MODAL;
2155 static int area_join_cancel(bContext *C, wmOperator *op)
2157 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2160 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2161 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
2164 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
2165 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2168 WM_event_add_notifier(C, NC_WINDOW, NULL);
2170 area_join_exit(C, op);
2172 return OPERATOR_CANCELLED;
2175 /* modal callback while selecting area (space) that will be removed */
2176 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
2178 bScreen *sc= CTX_wm_screen(C);
2179 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2181 /* execute the events */
2182 switch(event->type) {
2186 ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
2190 if (jd->sa1 != sa) {
2191 dir = area_getorientation(jd->sa1, sa);
2193 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2195 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2198 /* we are not bordering on the previously selected area
2199 we check if area has common border with the one marked for removal
2200 in this case we can swap areas.
2202 dir = area_getorientation(sa, jd->sa2);
2204 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2205 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2208 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2209 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2212 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2216 WM_event_add_notifier(C, NC_WINDOW, NULL);
2219 /* we are back in the area previously selected for keeping
2220 * we swap the areas if possible to allow user to choose */
2221 if (jd->sa2 != NULL) {
2222 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2223 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2226 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2227 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2228 dir = area_getorientation(jd->sa1, jd->sa2);
2230 printf("oops, didn't expect that!\n");
2234 dir = area_getorientation(jd->sa1, sa);
2236 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2238 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2241 WM_event_add_notifier(C, NC_WINDOW, NULL);
2247 if(event->val==KM_RELEASE) {
2248 ED_area_tag_redraw(jd->sa1);
2249 ED_area_tag_redraw(jd->sa2);
2251 area_join_apply(C, op);
2252 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2253 area_join_exit(C, op);
2254 return OPERATOR_FINISHED;
2260 return area_join_cancel(C, op);
2263 return OPERATOR_RUNNING_MODAL;
2266 /* Operator for joining two areas (space types) */
2267 static void SCREEN_OT_area_join(wmOperatorType *ot)
2270 ot->name= "Join area";
2271 ot->description= "Join selected areas into new window";
2272 ot->idname= "SCREEN_OT_area_join";
2275 ot->exec= area_join_exec;
2276 ot->invoke= area_join_invoke;
2277 ot->modal= area_join_modal;
2278 ot->poll= screen_active_editable;
2279 ot->cancel= area_join_cancel;
2281 ot->flag= OPTYPE_BLOCKING|OPTYPE_INTERNAL;
2284 RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
2285 RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2286 RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2287 RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2290 /* ******************************* */
2292 static int screen_area_options_invoke(bContext *C, wmOperator *op, wmEvent *event)
2296 PointerRNA ptr1, ptr2;
2297 ScrEdge *actedge= screen_find_active_scredge(CTX_wm_screen(C), event->x, event->y);
2299 if(actedge==NULL) return OPERATOR_CANCELLED;
2301 pup= uiPupMenuBegin(C, op->type->name, ICON_NONE);
2302 layout= uiPupMenuLayout(pup);
2304 WM_operator_properties_create(&ptr1, "SCREEN_OT_area_join");
2306 /* mouse cursor on edge, '4' can fail on wide edges... */
2307 RNA_int_set(&ptr1, "min_x", event->x+4);
2308 RNA_int_set(&ptr1, "min_y", event->y+4);
2309 RNA_int_set(&ptr1, "max_x", event->x-4);
2310 RNA_int_set(&ptr1, "max_y", event->y-4);
2312 WM_operator_properties_create(&ptr2, "SCREEN_OT_area_split");
2314 /* store initial mouse cursor position */
2315 RNA_int_set(&ptr2, "mouse_x", event->x);
2316 RNA_int_set(&ptr2, "mouse_y", event->y);
2318 uiItemFullO(layout, "SCREEN_OT_area_split", "Split Area", ICON_NONE, ptr2.data, WM_OP_INVOKE_DEFAULT, 0);
2319 uiItemFullO(layout, "SCREEN_OT_area_join", "Join Area", ICON_NONE, ptr1.data, WM_OP_INVOKE_DEFAULT, 0);
2321 uiPupMenuEnd(C, pup);
2323 return OPERATOR_CANCELLED;
2326 static void SCREEN_OT_area_options(wmOperatorType *ot)
2329 ot->name= "Area Options";
2330 ot->description= "Operations for splitting and merging";
2331 ot->idname= "SCREEN_OT_area_options";
2334 ot->invoke= screen_area_options_invoke;
2336 ot->poll= ED_operator_screen_mainwinactive;
2340 /* ******************************* */
2343 static int spacedata_cleanup(bContext *C, wmOperator *op)
2345 Main *bmain= CTX_data_main(C);
2350 for(screen= bmain->screen.first; screen; screen= screen->id.next) {
2351 for(sa= screen->areabase.first; sa; sa= sa->next) {
2352 if(sa->spacedata.first != sa->spacedata.last) {
2353 SpaceLink *sl= sa->spacedata.first;
2355 BLI_remlink(&sa->spacedata, sl);
2356 tot+= BLI_countlist(&sa->spacedata);
2357 BKE_spacedata_freelist(&sa->spacedata);
2358 BLI_addtail(&sa->spacedata, sl);
2362 BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
2364 return OPERATOR_FINISHED;
2367 static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
2370 ot->name= "Clean-up space-data";
2371 ot->description= "Remove unused settings for invisible editors";
2372 ot->idname= "SCREEN_OT_spacedata_cleanup";
2375 ot->exec= spacedata_cleanup;
2376 ot->poll= WM_operator_winactive;
2380 /* ************** repeat last operator ***************************** */
2382 static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
2384 wmOperator *lastop= CTX_wm_manager(C)->operators.last;
2387 WM_operator_repeat(C, lastop);
2389 return OPERATOR_CANCELLED;
2392 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2395 ot->name= "Repeat Last";
2396 ot->description= "Repeat last action";
2397 ot->idname= "SCREEN_OT_repeat_last";
2400 ot->exec= repeat_last_exec;
2402 ot->poll= ED_operator_screenactive;
2406 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2408 wmWindowManager *wm= CTX_wm_manager(C);
2414 items= BLI_countlist(&wm->operators);
2416 return OPERATOR_CANCELLED;
2418 pup= uiPupMenuBegin(C, op->type->name, ICON_NONE);
2419 layout= uiPupMenuLayout(pup);
2421 for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
2422 uiItemIntO(layout, lastop->type->name, ICON_NONE, op->type->idname, "index", i);
2424 uiPupMenuEnd(C, pup);
2426 return OPERATOR_CANCELLED;
2429 static int repeat_history_exec(bContext *C, wmOperator *op)
2431 wmWindowManager *wm= CTX_wm_manager(C);
2433 op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2435 /* let's put it as last operator in list */
2436 BLI_remlink(&wm->operators, op);
2437 BLI_addtail(&wm->operators, op);
2439 WM_operator_repeat(C, op);
2442 return OPERATOR_FINISHED;
2445 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2448 ot->name= "Repeat History";
2449 ot->description= "Display menu for previous actions performed";
2450 ot->idname= "SCREEN_OT_repeat_history";
2453 ot->invoke= repeat_history_invoke;
2454 ot->exec= repeat_history_exec;
2456 ot->poll= ED_operator_screenactive;
2458 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2461 /* ********************** redo operator ***************************** */
2463 static int redo_last_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
2465 wmOperator *lastop= WM_operator_last_redo(C);
2468 WM_operator_redo_popup(C, lastop);
2470 return OPERATOR_CANCELLED;
2473 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2476 ot->name= "Redo Last";
2477 ot->description= "Display menu for last action performed";
2478 ot->idname= "SCREEN_OT_redo_last";
2481 ot->invoke= redo_last_invoke;
2483 ot->poll= ED_operator_screenactive;
2486 /* ************** region four-split operator ***************************** */
2488 /* insert a region in the area region list */
2489 static int region_quadview_exec(bContext *C, wmOperator *op)
2491 ARegion *ar= CTX_wm_region(C);
2494 if(ar->regiontype!=RGN_TYPE_WINDOW)
2495 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2496 else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2497 ScrArea *sa= CTX_wm_area(C);
2500 /* keep current region */
2503 if(sa->spacetype==SPACE_VIEW3D) {
2504 RegionView3D *rv3d= ar->regiondata;
2506 rv3d->rflag &= ~RV3D_CLIPPING;
2509 for(ar= sa->regionbase.first; ar; ar= arn) {
2511 if(ar->alignment==RGN_ALIGN_QSPLIT) {
2512 ED_region_exit(C, ar);
2513 BKE_area_region_free(sa->type, ar);
2514 BLI_remlink(&sa->regionbase, ar);
2518 ED_area_tag_redraw(sa);
2519 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2522 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2524 ScrArea *sa= CTX_wm_area(C);
2528 ar->alignment= RGN_ALIGN_QSPLIT;
2530 for(count=0; count<3; count++) {
2531 newar= BKE_area_region_copy(sa->type, ar);
2532 BLI_addtail(&sa->regionbase, newar);
2535 /* lock views and set them */
2536 if(sa->spacetype==SPACE_VIEW3D) {
2537 /* run ED_view3d_lock() so the correct 'rv3d->viewquat' is set,
2538 * otherwise when restoring rv3d->localvd the 'viewquat' won't
2539 * match the 'view', set on entering localview See: [#26315],
2541 * We could avoid manipulating rv3d->localvd here if exiting
2542 * localview with a 4-split would assign these view locks */
2545 rv3d= ar->regiondata;
2546 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2547 ED_view3d_lock(rv3d);
2548 if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2551 rv3d= ar->regiondata;
2552 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2553 ED_view3d_lock(rv3d);
2554 if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2557 rv3d= ar->regiondata;
2558 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2559 ED_view3d_lock(rv3d);
2560 if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2563 rv3d= ar->regiondata;
2564 rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2565 ED_view3d_lock(rv3d);
2566 if (rv3d->localvd) {rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);}
2568 ED_area_tag_redraw(sa);
2569 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2573 return OPERATOR_FINISHED;
2576 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
2579 ot->name= "Toggle Quad View";
2580 ot->description= "Split selected area into camera, front, right & top views";
2581 ot->idname= "SCREEN_OT_region_quadview";
2584 // ot->invoke= WM_operator_confirm;
2585 ot->exec= region_quadview_exec;
2586 ot->poll= ED_operator_region_view3d_active;
2592 /* ************** region flip operator ***************************** */
2594 /* flip a region alignment */
2595 static int region_flip_exec(bContext *C, wmOperator *UNUSED(op))
2597 ARegion *ar= CTX_wm_region(C);
2600 return OPERATOR_CANCELLED;
2602 if(ar->alignment==RGN_ALIGN_TOP)
2603 ar->alignment= RGN_ALIGN_BOTTOM;
2604 else if(ar->alignment==RGN_ALIGN_BOTTOM)
2605 ar->alignment= RGN_ALIGN_TOP;
2606 else if(ar->alignment==RGN_ALIGN_LEFT)
2607 ar->alignment= RGN_ALIGN_RIGHT;
2608 else if(ar->alignment==RGN_ALIGN_RIGHT)
2609 ar->alignment= RGN_ALIGN_LEFT;
2611 ED_area_tag_redraw(CTX_wm_area(C));
2612 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2614 return OPERATOR_FINISHED;
2618 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2621 ot->name= "Flip Region";
2622 ot->idname= "SCREEN_OT_region_flip";
2625 ot->exec= region_flip_exec;
2626 ot->poll= ED_operator_areaactive;
2630 /* ************** header flip operator ***************************** */
2632 /* flip a header region alignment */
2633 static int header_flip_exec(bContext *C, wmOperator *UNUSED(op))
2635 ARegion *ar= CTX_wm_region(C);
2637 /* find the header region
2638 * - try context first, but upon failing, search all regions in area...
2640 if((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) {
2641 ScrArea *sa= CTX_wm_area(C);
2642 ar= BKE_area_find_region_type(sa, RGN_TYPE_HEADER);
2644 /* don't do anything if no region */
2646 return OPERATOR_CANCELLED;
2649 /* copied from SCREEN_OT_region_flip */
2650 if(ar->alignment==RGN_ALIGN_TOP)
2651 ar->alignment= RGN_ALIGN_BOTTOM;
2652 else if(ar->alignment==RGN_ALIGN_BOTTOM)
2653 ar->alignment= RGN_ALIGN_TOP;
2654 else if(ar->alignment==RGN_ALIGN_LEFT)
2655 ar->alignment= RGN_ALIGN_RIGHT;
2656 else if(ar->alignment==RGN_ALIGN_RIGHT)
2657 ar->alignment= RGN_ALIGN_LEFT;
2659 ED_area_tag_redraw(CTX_wm_area(C));
2661 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2663 return OPERATOR_FINISHED;
2667 static void SCREEN_OT_header_flip(wmOperatorType *ot)
2670 ot->name= "Flip Header Region";
2671 ot->idname= "SCREEN_OT_header_flip";
2674 ot->exec= header_flip_exec;
2676 ot->poll= ED_operator_areaactive;
2680 /* ************** header tools operator ***************************** */
2682 static int header_toolbox_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
2684 ScrArea *sa= CTX_wm_area(C);
2685 ARegion *ar= CTX_wm_region(C);
2689 pup= uiPupMenuBegin(C, "Header", ICON_NONE);
2690 layout= uiPupMenuLayout(pup);
2692 // XXX SCREEN_OT_region_flip doesn't work - gets wrong context for active region, so added custom operator
2693 if (ar->alignment == RGN_ALIGN_TOP)
2694 uiItemO(layout, "Flip to Bottom", ICON_NONE, "SCREEN_OT_header_flip");
2696 uiItemO(layout, "Flip to Top", ICON_NONE, "SCREEN_OT_header_flip");
2700 /* file browser should be fullscreen all the time, but other regions can be maximised/restored... */
2701 if (sa->spacetype != SPACE_FILE) {
2703 uiItemO(layout, "Tile Area", ICON_NONE, "SCREEN_OT_screen_full_area");
2705 uiItemO(layout, "Maximize Area", ICON_NONE, "SCREEN_OT_screen_full_area");
2708 uiPupMenuEnd(C, pup);
2710 return OPERATOR_CANCELLED;
2713 static void SCREEN_OT_header_toolbox(wmOperatorType *ot)
2716 ot->name= "Header Toolbox";
2717 ot->description="Display header region toolbox";
2718 ot->idname= "SCREEN_OT_header_toolbox";
2721 ot->invoke= header_toolbox_invoke;
2724 /* ****************** anim player, with timer ***************** */
2726 static int match_area_with_refresh(int spacetype, int refresh)
2728 switch (spacetype) {
2730 if (refresh & SPACE_TIME)
2738 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2740 if(regiontype==RGN_TYPE_WINDOW) {
2742 switch (spacetype) {
2744 if(redraws & TIME_ALL_3D_WIN)
2750 if(redraws & TIME_ALL_ANIM_WIN)
2754 /* if only 1 window or 3d windows, we do timeline too */
2755 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2759 if(redraws & TIME_ALL_BUTS_WIN)
2763 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2767 if(redraws & (TIME_NODES))
2771 if(redraws & TIME_ALL_IMAGE_WIN)
2777 else if(regiontype==RGN_TYPE_UI) {
2778 if(redraws & TIME_ALL_BUTS_WIN)
2781 else if(regiontype==RGN_TYPE_HEADER) {
2782 if(spacetype==SPACE_TIME)
2785 else if (regiontype==RGN_TYPE_PREVIEW) {
2786 switch (spacetype) {
2788 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2796 static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
2798 bScreen *screen= CTX_wm_screen(C);
2800 if(screen->animtimer && screen->animtimer==event->customdata) {
2801 Main *bmain= CTX_data_main(C);
2802 Scene *scene= CTX_data_scene(C);
2803 wmTimer *wt= screen->animtimer;
2804 ScreenAnimData *sad= wt->customdata;
2809 /* sync, don't sync, or follow scene setting */
2810 if (sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2811 else if (sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2812 else sync= (scene->flag & SCE_FRAME_DROP);
2814 if((scene->audio.flag & AUDIO_SYNC) && !(sad->flag & ANIMPLAY_FLAG_REVERSE) && finite(time = sound_sync_scene(scene)))
2815 scene->r.cfra = (double)time * FPS + 0.5;
2819 int step = floor(wt->duration * FPS);
2821 if (sad->flag & ANIMPLAY_FLAG_REVERSE)
2822 scene->r.cfra -= step;
2824 scene->r.cfra += step;
2825 wt->duration -= ((double)step)/FPS;
2829 if (sad->flag & ANIMPLAY_FLAG_REVERSE)
2836 /* reset 'jumped' flag before checking if we need to jump... */
2837 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2839 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2840 /* jump back to end? */
2842 if (scene->r.cfra < scene->r.psfra) {
2843 scene->r.cfra= scene->r.pefra;
2844 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2848 if (scene->r.cfra < scene->r.sfra) {
2849 scene->r.cfra= scene->r.efra;
2850 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2855 /* jump back to start? */
2857 if (scene->r.cfra > scene->r.pefra) {
2858 scene->r.cfra= scene->r.psfra;
2859 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2863 if (scene->r.cfra > scene->r.efra) {
2864 scene->r.cfra= scene->r.sfra;
2865 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2870 /* next frame overriden by user action (pressed jump to first/last frame) */
2871 if(sad->flag & ANIMPLAY_FLAG_USE_NEXT_FRAME) {
2872 scene->r.cfra = sad->nextfra;
2873 sad->flag &= ~ANIMPLAY_FLAG_USE_NEXT_FRAME;
2874 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2877 if (sad->flag & ANIMPLAY_FLAG_JUMPED)
2878 sound_seek_scene(bmain, scene);
2880 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2881 ED_update_for_newframe(CTX_data_main(C), scene, screen, 1);
2883 for (sa= screen->areabase.first; sa; sa= sa->next) {
2885 for (ar= sa->regionbase.first; ar; ar= ar->next) {
2887 ED_region_tag_redraw(ar);
2889 if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2890 ED_region_tag_redraw(ar);
2893 if (match_area_with_refresh(sa->spacetype, sad->refresh))
2894 ED_area_tag_refresh(sa);
2897 /* update frame rate info too
2898 * NOTE: this may not be accurate enough, since we might need this after modifiers/etc.
2899 * have been calculated instead of just before updates have been done?
2901 ED_refresh_viewport_fps(C);
2903 /* recalculate the timestep for the timer now that we've finished calculating this,
2904 * since the frames-per-second value may have been changed
2906 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2907 wt->timestep= (1.0/FPS);
2909 return OPERATOR_FINISHED;
2911 return OPERATOR_PASS_THROUGH;
2914 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2917 ot->name= "Animation Step";
2918 ot->description= "Step through animation by position";
2919 ot->idname= "SCREEN_OT_animation_step";
2922 ot->invoke= screen_animation_step;
2924 ot->poll= ED_operator_screenactive_norender;
2928 /* ****************** anim player, starts or ends timer ***************** */
2930 /* toggle operator */
2931 int ED_screen_animation_play(bContext *C, int sync, int mode)
2933 bScreen *screen= CTX_wm_screen(C);
2934 Scene *scene = CTX_data_scene(C);
2936 if (screen->animtimer) {
2937 /* stop playback now */
2938 ED_screen_animation_timer(C, 0, 0, 0, 0);
2939 sound_stop_scene(scene);
2942 int refresh= SPACE_TIME; /* these settings are currently only available from a menu in the TimeLine */
2944 if (mode == 1) // XXX only play audio forwards!?
2945 sound_play_scene(scene);
2947 ED_screen_animation_timer(C, screen->redraws_flag, refresh, sync, mode);
2949 if (screen->animtimer) {
2950 wmTimer *wt= screen->animtimer;
2951 ScreenAnimData *sad= wt->customdata;
2953 sad->ar= CTX_wm_region(C);
2957 return OPERATOR_FINISHED;
2960 static int screen_animation_play_exec(bContext *C, wmOperator *op)
2962 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2965 if (RNA_property_is_set(op->ptr, "sync"))
2966 sync= (RNA_boolean_get(op->ptr, "sync"));
2968 return ED_screen_animation_play(C, sync, mode);
2971 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2974 ot->name= "Play Animation";
2975 ot->description= "Play animation";
2976 ot->idname= "SCREEN_OT_animation_play";
2979 ot->exec= screen_animation_play_exec;
2981 ot->poll= ED_operator_screenactive_norender;
2983 RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2984 RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate");
2987 static int screen_animation_cancel_exec(bContext *C, wmOperator *op)
2989 bScreen *screen= CTX_wm_screen(C);
2991 if (screen->animtimer) {
2992 if(RNA_boolean_get(op->ptr, "restore_frame")) {
2993 ScreenAnimData *sad= screen->animtimer->customdata;
2994 Scene *scene= CTX_data_scene(C);
2996 /* reset current frame before stopping, and just send a notifier to deal with the rest
2997 * (since playback still needs to be stopped)
2999 scene->r.cfra= sad->sfra;
3001 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
3004 /* call the other "toggling" operator to clean up now */
3005 ED_screen_animation_play(C, 0, 0);
3008 return OPERATOR_PASS_THROUGH;
3011 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
3014 ot->name= "Cancel Animation";
3015 ot->description= "Cancel animation, returning to the original frame";
3016 ot->idname= "SCREEN_OT_animation_cancel";
3019 ot->exec= screen_animation_cancel_exec;
3021 ot->poll= ED_operator_screenactive;
3023 RNA_def_boolean(ot->srna, "restore_frame", TRUE, "Restore Frame", "Restore the frame when animation was initialized.");
3026 /* ************** border select operator (template) ***************************** */
3028 /* operator state vars used: (added by default WM callbacks)
3032 customdata: the wmGesture pointer
3036 exec() has to be filled in by user
3038 invoke() default WM function
3041 modal() default WM function
3042 accept modal events while doing it, calls exec(), handles ESC and border drawing
3044 poll() has to be filled in by user for context
3047 static int border_select_do(bContext *C, wmOperator *op)
3049 int event_type= RNA_int_get(op->ptr, "event_type");
3051 if(event_type==LEFTMOUSE)
3052 printf("border select do select\n");
3053 else if(event_type==RIGHTMOUSE)
3054 printf("border select deselect\n");
3056 printf("border select do something\n");
3061 static void SCREEN_OT_border_select(wmOperatorType *ot)
3064 ot->name= "Border select";
3065 ot->idname= "SCREEN_OT_border_select";
3068 ot->exec= border_select_do;
3069 ot->invoke= WM_border_select_invoke;
3070 ot->modal= WM_border_select_modal;
3071 ot->cancel= WM_border_select_cancel;
3073 ot->poll= ED_operator_areaactive;
3076 RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
3077 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
3078 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
3079 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
3080 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
3085 /* *********************** generic fullscreen 'back' button *************** */
3088 static int fullscreen_back_exec(bContext *C, wmOperator *op)
3090 bScreen *screen = CTX_wm_screen(C);
3093 /* search current screen for 'fullscreen' areas */
3094 for (sa=screen->areabase.first; sa; sa=sa->next) {
3095 if (sa->full) break;
3098 BKE_report(op->reports, RPT_ERROR, "No fullscreen areas were found.");
3099 return OPERATOR_CANCELLED;
3102 ED_screen_full_restore(C, sa);
3104 return OPERATOR_FINISHED;
3107 static void SCREEN_OT_back_to_previous(struct wmOperatorType *ot)
3110 ot->name= "Back to Previous Screen";
3111 ot->description= "Revert back to the original screen layout, before fullscreen area overlay";
3112 ot->idname= "SCREEN_OT_back_to_previous";
3115 ot->exec= fullscreen_back_exec;
3116 ot->poll= ED_operator_screenactive;
3119 /* *********** show user pref window ****** */
3121 static int userpref_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
3129 /* some magic to calculate postition */
3130 rect.xmin= event->x + CTX_wm_window(C)->posx - sizex/2;
3131 rect.ymin= event->y + CTX_wm_window(C)->posy - sizey/2;
3132 rect.xmax= rect.xmin + sizex;
3133 rect.ymax= rect.ymin + sizey;
3135 /* changes context! */
3136 WM_window_open_temp(C, &rect, WM_WINDOW_USERPREFS);
3138 return OPERATOR_FINISHED;
3142 static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
3145 ot->name= "Show/Hide User Preferences";
3146 ot->description= "Show/hide user preferences";
3147 ot->idname= "SCREEN_OT_userpref_show";
3150 ot->invoke= userpref_show_invoke;
3151 ot->poll= ED_operator_screenactive;
3154 /********************* new screen operator *********************/
3156 static int screen_new_exec(bContext *C, wmOperator *UNUSED(op))
3158 wmWindow *win= CTX_wm_window(C);
3159 bScreen *sc= CTX_wm_screen(C);
3161 sc= ED_screen_duplicate(win, sc);
3162 WM_event_add_notifier(C, NC_SCREEN|ND_SCREENBROWSE, sc);
3164 return OPERATOR_FINISHED;
3167 static void SCREEN_OT_new(wmOperatorType *ot)
3170 ot->name= "New Screen";
3171 ot->description= "Add a new screen";
3172 ot->idname= "SCREEN_OT_new";
3175 ot->exec= screen_new_exec;
3176 ot->poll= WM_operator_winactive;
3179 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
3182 /********************* delete screen operator *********************/
3184 static int screen_delete_exec(bContext *C, wmOperator *UNUSED(op))
3186 bScreen *sc= CTX_wm_screen(C);
3188 WM_event_add_notifier(C, NC_SCREEN|ND_SCREENDELETE, sc);
3190 return OPERATOR_FINISHED;
3193 static void SCREEN_OT_delete(wmOperatorType *ot)
3196 ot->name= "Delete Screen"; //was scene
3197 ot->description= "Delete active screen";
3198 ot->idname= "SCREEN_OT_delete";
3201 ot->exec= screen_delete_exec;
3204 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
3207 /********************* new scene operator *********************/
3209 static int scene_new_exec(bContext *C, wmOperator *op)
3211 Scene *newscene, *scene= CTX_data_scene(C);
3212 bScreen *screen= CTX_wm_screen(C);
3213 Main *bmain= CTX_data_main(C);
3214 int type= RNA_enum_get(op->ptr, "type");
3216 if(type == SCE_COPY_NEW) {
3217 newscene= add_scene("Scene");
3219 else { /* different kinds of copying */