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 *****
33 #include "MEM_guardedalloc.h"
36 #include "BLI_blenlib.h"
37 #include "BLI_editVert.h"
38 #include "BLI_dlrbTree.h"
40 #include "DNA_armature_types.h"
41 #include "DNA_image_types.h"
42 #include "DNA_lattice_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_mesh_types.h"
45 #include "DNA_curve_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_meta_types.h"
48 #include "DNA_view3d_types.h"
50 #include "BKE_blender.h"
51 #include "BKE_colortools.h"
52 #include "BKE_context.h"
53 #include "BKE_customdata.h"
54 #include "BKE_global.h"
55 #include "BKE_image.h"
56 #include "BKE_idprop.h"
57 #include "BKE_library.h"
60 #include "BKE_multires.h"
61 #include "BKE_report.h"
62 #include "BKE_scene.h"
63 #include "BKE_screen.h"
64 #include "BKE_utildefines.h"
65 #include "BKE_sound.h"
66 #include "BKE_writeavi.h"
72 #include "ED_screen.h"
74 #include "ED_object.h"
75 #include "ED_screen_types.h"
76 #include "ED_keyframes_draw.h"
77 #include "ED_view3d.h"
79 #include "RE_pipeline.h"
80 #include "IMB_imbuf.h"
81 #include "IMB_imbuf_types.h"
83 #include "RNA_access.h"
84 #include "RNA_define.h"
86 #include "UI_interface.h"
87 #include "UI_resources.h"
89 #include "GPU_extensions.h"
91 #include "wm_window.h"
93 #include "screen_intern.h" /* own module include */
95 #define KM_MODAL_CANCEL 1
96 #define KM_MODAL_APPLY 2
97 #define KM_MODAL_STEP10 3
98 #define KM_MODAL_STEP10_OFF 4
100 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2)
101 /* ************** libgomp (Apple gcc 4.2.1) TLS bug workaround *************** */
103 extern pthread_key_t gomp_tls_key;
104 static void *thread_tls_data;
106 /* ************** Exported Poll tests ********************** */
108 int ED_operator_regionactive(bContext *C)
110 if(CTX_wm_window(C)==NULL) return 0;
111 if(CTX_wm_screen(C)==NULL) return 0;
112 if(CTX_wm_region(C)==NULL) return 0;
116 int ED_operator_areaactive(bContext *C)
118 if(CTX_wm_window(C)==NULL) return 0;
119 if(CTX_wm_screen(C)==NULL) return 0;
120 if(CTX_wm_area(C)==NULL) return 0;
124 int ED_operator_screenactive(bContext *C)
126 if(CTX_wm_window(C)==NULL) return 0;
127 if(CTX_wm_screen(C)==NULL) return 0;
131 /* when mouse is over area-edge */
132 int ED_operator_screen_mainwinactive(bContext *C)
134 if(CTX_wm_window(C)==NULL) return 0;
135 if(CTX_wm_screen(C)==NULL) return 0;
136 if (CTX_wm_screen(C)->subwinactive!=CTX_wm_screen(C)->mainwin) return 0;
140 int ED_operator_scene_editable(bContext *C)
142 Scene *scene= CTX_data_scene(C);
143 if(scene && scene->id.lib==NULL)
148 static int ed_spacetype_test(bContext *C, int type)
150 if(ED_operator_areaactive(C)) {
151 SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
152 return sl && (sl->spacetype == type);
157 int ED_operator_view3d_active(bContext *C)
159 return ed_spacetype_test(C, SPACE_VIEW3D);
162 int ED_operator_timeline_active(bContext *C)
164 return ed_spacetype_test(C, SPACE_TIME);
167 int ED_operator_outliner_active(bContext *C)
169 return ed_spacetype_test(C, SPACE_OUTLINER);
172 int ED_operator_file_active(bContext *C)
174 return ed_spacetype_test(C, SPACE_FILE);
177 int ED_operator_action_active(bContext *C)
179 return ed_spacetype_test(C, SPACE_ACTION);
182 int ED_operator_buttons_active(bContext *C)
184 return ed_spacetype_test(C, SPACE_BUTS);
187 int ED_operator_node_active(bContext *C)
189 SpaceNode *snode= CTX_wm_space_node(C);
191 if(snode && snode->edittree)
198 int ED_operator_ipo_active(bContext *C)
200 return ed_spacetype_test(C, SPACE_IPO);
203 int ED_operator_sequencer_active(bContext *C)
205 return ed_spacetype_test(C, SPACE_SEQ);
208 int ED_operator_image_active(bContext *C)
210 return ed_spacetype_test(C, SPACE_IMAGE);
213 int ED_operator_nla_active(bContext *C)
215 return ed_spacetype_test(C, SPACE_NLA);
218 int ED_operator_logic_active(bContext *C)
220 return ed_spacetype_test(C, SPACE_LOGIC);
223 int ED_operator_object_active(bContext *C)
225 return NULL != CTX_data_active_object(C);
228 int ED_operator_object_active_editable(bContext *C)
230 Object *ob=CTX_data_active_object(C);
231 return ((ob != NULL) && !(ob->id.lib));
234 int ED_operator_editmesh(bContext *C)
236 Object *obedit= CTX_data_edit_object(C);
237 if(obedit && obedit->type==OB_MESH)
238 return NULL != ((Mesh *)obedit->data)->edit_mesh;
242 int ED_operator_editmesh_view3d(bContext *C)
244 return ED_operator_editmesh(C) && ED_operator_view3d_active(C);
247 int ED_operator_editarmature(bContext *C)
249 Object *obedit= CTX_data_edit_object(C);
250 if(obedit && obedit->type==OB_ARMATURE)
251 return NULL != ((bArmature *)obedit->data)->edbo;
255 int ED_operator_posemode(bContext *C)
257 Object *obact= CTX_data_active_object(C);
258 Object *obedit= CTX_data_edit_object(C);
260 if ((obact != obedit) && (obact) && (obact->type==OB_ARMATURE))
261 return (obact->mode & OB_MODE_POSE)!=0;
267 int ED_operator_uvedit(bContext *C)
269 Object *obedit= CTX_data_edit_object(C);
272 if(obedit && obedit->type==OB_MESH)
273 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
275 if(em && (em->faces.first) && (CustomData_has_layer(&em->fdata, CD_MTFACE))) {
276 BKE_mesh_end_editmesh(obedit->data, em);
281 BKE_mesh_end_editmesh(obedit->data, em);
285 int ED_operator_uvmap(bContext *C)
287 Object *obedit= CTX_data_edit_object(C);
290 if(obedit && obedit->type==OB_MESH)
291 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
293 if(em && (em->faces.first)) {
294 BKE_mesh_end_editmesh(obedit->data, em);
299 BKE_mesh_end_editmesh(obedit->data, em);
303 int ED_operator_editsurfcurve(bContext *C)
305 Object *obedit= CTX_data_edit_object(C);
306 if(obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
307 return NULL != ((Curve *)obedit->data)->editnurb;
312 int ED_operator_editcurve(bContext *C)
314 Object *obedit= CTX_data_edit_object(C);
315 if(obedit && obedit->type==OB_CURVE)
316 return NULL != ((Curve *)obedit->data)->editnurb;
320 int ED_operator_editsurf(bContext *C)
322 Object *obedit= CTX_data_edit_object(C);
323 if(obedit && obedit->type==OB_SURF)
324 return NULL != ((Curve *)obedit->data)->editnurb;
328 int ED_operator_editfont(bContext *C)
330 Object *obedit= CTX_data_edit_object(C);
331 if(obedit && obedit->type==OB_FONT)
332 return NULL != ((Curve *)obedit->data)->editfont;
336 int ED_operator_editlattice(bContext *C)
338 Object *obedit= CTX_data_edit_object(C);
339 if(obedit && obedit->type==OB_LATTICE)
340 return NULL != ((Lattice *)obedit->data)->editlatt;
344 int ED_operator_editmball(bContext *C)
346 Object *obedit= CTX_data_edit_object(C);
347 if(obedit && obedit->type==OB_MBALL)
348 return NULL != ((MetaBall *)obedit->data)->editelems;
352 /* *************************** action zone operator ************************** */
354 /* operator state vars used:
359 apply() set actionzone event
361 exit() free customdata
367 invoke() check if in zone
368 add customdata, put mouseco and area in it
371 modal() accept modal events while doing it
372 call apply() with gesture info, active window, nonactive window
373 call exit() and remove handler when LMB confirm
377 typedef struct sActionzoneData {
380 int x, y, gesture_dir, modifier;
383 /* used by other operators too */
384 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
387 sa= scr->areabase.first;
389 if(BLI_in_rcti(&sa->totrct, x, y)) break;
396 /* quick poll to save operators to be created and handled */
397 static int actionzone_area_poll(bContext *C)
399 wmWindow *win= CTX_wm_window(C);
400 ScrArea *sa= CTX_wm_area(C);
404 int x= win->eventstate->x;
405 int y= win->eventstate->y;
407 for(az= sa->actionzones.first; az; az= az->next)
408 if(BLI_in_rcti(&az->rect, x, y))
414 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
418 for(az= sa->actionzones.first; az; az= az->next) {
419 if(BLI_in_rcti(&az->rect, x, y)) {
420 if(az->type == AZONE_AREA) {
421 if(isect_point_tri_v2_int(az->x1, az->y1, az->x2, az->y2, x, y))
424 else if(az->type == AZONE_REGION) {
434 static void actionzone_exit(bContext *C, wmOperator *op)
437 MEM_freeN(op->customdata);
438 op->customdata= NULL;
441 /* send EVT_ACTIONZONE event */
442 static void actionzone_apply(bContext *C, wmOperator *op, int type)
445 wmWindow *win= CTX_wm_window(C);
446 sActionzoneData *sad= op->customdata;
448 sad->modifier= RNA_int_get(op->ptr, "modifier");
450 event= *(win->eventstate); /* XXX huh huh? make api call */
452 event.type= EVT_ACTIONZONE_AREA;
454 event.type= EVT_ACTIONZONE_REGION;
455 event.customdata= op->customdata;
456 event.customdatafree= TRUE;
457 op->customdata= NULL;
459 wm_event_add(win, &event);
462 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
464 AZone *az= is_in_area_actionzone(CTX_wm_area(C), event->x, event->y);
465 sActionzoneData *sad;
469 return OPERATOR_PASS_THROUGH;
471 /* ok we do the actionzone */
472 sad= op->customdata= MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
473 sad->sa1= CTX_wm_area(C);
475 sad->x= event->x; sad->y= event->y;
477 /* region azone directly reacts on mouse clicks */
478 if(sad->az->type==AZONE_REGION) {
479 actionzone_apply(C, op, AZONE_REGION);
480 actionzone_exit(C, op);
481 return OPERATOR_FINISHED;
484 /* add modal handler */
485 WM_event_add_modal_handler(C, op);
487 return OPERATOR_RUNNING_MODAL;
492 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
494 sActionzoneData *sad= op->customdata;
496 int mindelta= sad->az->type==AZONE_REGION?1:12;
498 switch(event->type) {
500 /* calculate gesture direction */
501 deltax= (event->x - sad->x);
502 deltay= (event->y - sad->y);
504 if(deltay > ABS(deltax))
505 sad->gesture_dir= 'n';
506 else if(deltax > ABS(deltay))
507 sad->gesture_dir= 'e';
508 else if(deltay < -ABS(deltax))
509 sad->gesture_dir= 's';
511 sad->gesture_dir= 'w';
513 /* gesture is large enough? */
514 if(ABS(deltax) > mindelta || ABS(deltay) > mindelta) {
516 /* second area, for join */
517 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
518 /* apply sends event */
519 actionzone_apply(C, op, sad->az->type);
520 actionzone_exit(C, op);
522 return OPERATOR_FINISHED;
526 actionzone_exit(C, op);
527 return OPERATOR_CANCELLED;
529 actionzone_exit(C, op);
530 return OPERATOR_CANCELLED;
534 return OPERATOR_RUNNING_MODAL;
537 static void SCREEN_OT_actionzone(wmOperatorType *ot)
540 ot->name= "Handle area action zones";
541 ot->description= "Handle area action zones for mouse actions/gestures";
542 ot->idname= "SCREEN_OT_actionzone";
544 ot->invoke= actionzone_invoke;
545 ot->modal= actionzone_modal;
546 ot->poll= actionzone_area_poll;
548 ot->flag= OPTYPE_BLOCKING;
550 RNA_def_int(ot->srna, "modifier", 0, 0, 2, "modifier", "modifier state", 0, 2);
553 /* ************** swap area operator *********************************** */
555 /* operator state vars used:
557 sa2 area to swap with
561 init() set custom data for operator, based on actionzone event custom data
563 cancel() cancel the operator
565 exit() cleanup, send notifier
569 invoke() gets called on shift+lmb drag in actionzone
570 call init(), add handler
572 modal() accept modal events while doing it
576 typedef struct sAreaSwapData {
580 static int area_swap_init(bContext *C, wmOperator *op, wmEvent *event)
582 sAreaSwapData *sd= NULL;
583 sActionzoneData *sad= event->customdata;
585 if(sad==NULL || sad->sa1==NULL)
588 sd= MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
597 static void area_swap_exit(bContext *C, wmOperator *op)
600 MEM_freeN(op->customdata);
601 op->customdata= NULL;
604 static int area_swap_cancel(bContext *C, wmOperator *op)
606 area_swap_exit(C, op);
607 return OPERATOR_CANCELLED;
610 static int area_swap_invoke(bContext *C, wmOperator *op, wmEvent *event)
613 if(!area_swap_init(C, op, event))
614 return OPERATOR_PASS_THROUGH;
616 /* add modal handler */
617 WM_cursor_modal(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
618 WM_event_add_modal_handler(C, op);
620 return OPERATOR_RUNNING_MODAL;
624 static int area_swap_modal(bContext *C, wmOperator *op, wmEvent *event)
626 sActionzoneData *sad= op->customdata;
628 switch(event->type) {
630 /* second area, for join */
631 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
633 case LEFTMOUSE: /* release LMB */
634 if(event->val==KM_RELEASE) {
635 if(!sad->sa2 || sad->sa1 == sad->sa2) {
637 return area_swap_cancel(C, op);
639 ED_area_swapspace(C, sad->sa1, sad->sa2);
641 area_swap_exit(C, op);
644 ED_area_tag_redraw(sad->sa1);
645 ED_area_tag_redraw(sad->sa2);
648 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
650 return OPERATOR_FINISHED;
655 return area_swap_cancel(C, op);
657 return OPERATOR_RUNNING_MODAL;
660 static void SCREEN_OT_area_swap(wmOperatorType *ot)
662 ot->name= "Swap areas";
663 ot->description= "Swap selected areas screen positions";
664 ot->idname= "SCREEN_OT_area_swap";
666 ot->invoke= area_swap_invoke;
667 ot->modal= area_swap_modal;
668 ot->poll= ED_operator_areaactive;
670 ot->flag= OPTYPE_BLOCKING;
673 /* *********** Duplicate area as new window operator ****************** */
675 /* operator callback */
676 static int area_dupli_invoke(bContext *C, wmOperator *op, wmEvent *event)
678 wmWindow *newwin, *win;
683 win= CTX_wm_window(C);
684 sc= CTX_wm_screen(C);
688 if(event->type==EVT_ACTIONZONE_AREA) {
689 sActionzoneData *sad= event->customdata;
692 return OPERATOR_PASS_THROUGH;
697 /* poll() checks area context, but we don't accept full-area windows */
698 if(sc->full != SCREENNORMAL) {
699 if(event->type==EVT_ACTIONZONE_AREA)
700 actionzone_exit(C, op);
701 return OPERATOR_CANCELLED;
704 /* adds window to WM */
706 BLI_translate_rcti(&rect, win->posx, win->posy);
707 newwin= WM_window_open(C, &rect);
709 /* allocs new screen and adds to newly created window, using window size */
710 newsc= ED_screen_add(newwin, CTX_data_scene(C), sc->id.name+2);
711 newwin->screen= newsc;
713 /* copy area to new screen */
714 area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
717 ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
720 /* screen, areas init */
721 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
723 if(event->type==EVT_ACTIONZONE_AREA)
724 actionzone_exit(C, op);
726 return OPERATOR_FINISHED;
729 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
731 ot->name= "Duplicate Area into New Window";
732 ot->description= "Duplicate selected area into new window";
733 ot->idname= "SCREEN_OT_area_dupli";
735 ot->invoke= area_dupli_invoke;
736 ot->poll= ED_operator_areaactive;
740 /* ************** move area edge operator *********************************** */
742 /* operator state vars used:
743 x, y mouse coord near edge
744 delta movement of edge
748 init() set default property values, find edge based on mouse coords, test
749 if the edge can be moved, select edges, calculate min and max movement
751 apply() apply delta on selection
753 exit() cleanup, send notifier
755 cancel() cancel moving
759 exec() execute without any user interaction, based on properties
760 call init(), apply(), exit()
762 invoke() gets called on mouse click near edge
763 call init(), add handler
765 modal() accept modal events while doing it
766 call apply() with delta motion
767 call exit() and remove handler
771 typedef struct sAreaMoveData {
772 int bigger, smaller, origval, step;
776 /* helper call to move area-edge, sets limits */
777 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
781 /* we check all areas and test for free space with MINSIZE */
782 *bigger= *smaller= 100000;
784 for(sa= sc->areabase.first; sa; sa= sa->next) {
786 int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
788 /* if top or down edge selected, test height */
789 if(sa->v1->flag && sa->v4->flag)
790 *bigger= MIN2(*bigger, y1);
791 else if(sa->v2->flag && sa->v3->flag)
792 *smaller= MIN2(*smaller, y1);
795 int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
797 /* if left or right edge selected, test width */
798 if(sa->v1->flag && sa->v2->flag)
799 *bigger= MIN2(*bigger, x1);
800 else if(sa->v3->flag && sa->v4->flag)
801 *smaller= MIN2(*smaller, x1);
806 /* validate selection inside screen, set variables OK */
807 /* return 0: init failed */
808 static int area_move_init (bContext *C, wmOperator *op)
810 bScreen *sc= CTX_wm_screen(C);
815 /* required properties */
816 x= RNA_int_get(op->ptr, "x");
817 y= RNA_int_get(op->ptr, "y");
820 actedge= screen_find_active_scredge(sc, x, y);
821 if(actedge==NULL) return 0;
823 md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
826 md->dir= scredge_is_horizontal(actedge)?'h':'v';
827 if(md->dir=='h') md->origval= actedge->v1->vec.y;
828 else md->origval= actedge->v1->vec.x;
830 select_connected_scredge(sc, actedge);
831 /* now all vertices with 'flag==1' are the ones that can be moved. */
833 area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
838 /* moves selected screen edge amount of delta, used by split & move */
839 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
841 wmWindow *win= CTX_wm_window(C);
842 bScreen *sc= CTX_wm_screen(C);
845 delta= CLAMPIS(delta, -smaller, bigger);
847 for (v1= sc->vertbase.first; v1; v1= v1->next) {
849 /* that way a nice AREAGRID */
850 if((dir=='v') && v1->vec.x>0 && v1->vec.x<win->sizex-1) {
851 v1->vec.x= origval + delta;
852 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
854 if((dir=='h') && v1->vec.y>0 && v1->vec.y<win->sizey-1) {
855 v1->vec.y= origval + delta;
857 v1->vec.y+= AREAGRID-1;
858 v1->vec.y-= (v1->vec.y % AREAGRID);
860 /* prevent too small top header */
861 if(v1->vec.y > win->sizey-AREAMINY)
862 v1->vec.y= win->sizey-AREAMINY;
869 for(sa= sc->areabase.first; sa; sa= sa->next)
870 if(sa->v1->flag || sa->v2->flag || sa->v3->flag || sa->v4->flag)
871 ED_area_tag_redraw(sa);
875 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL); /* redraw everything */
878 static void area_move_apply(bContext *C, wmOperator *op)
880 sAreaMoveData *md= op->customdata;
883 delta= RNA_int_get(op->ptr, "delta");
884 area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
887 static void area_move_exit(bContext *C, wmOperator *op)
890 MEM_freeN(op->customdata);
891 op->customdata= NULL;
893 /* this makes sure aligned edges will result in aligned grabbing */
894 removedouble_scrverts(CTX_wm_screen(C));
895 removedouble_scredges(CTX_wm_screen(C));
898 static int area_move_exec(bContext *C, wmOperator *op)
900 if(!area_move_init(C, op))
901 return OPERATOR_CANCELLED;
903 area_move_apply(C, op);
904 area_move_exit(C, op);
906 return OPERATOR_FINISHED;
909 /* interaction callback */
910 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
912 RNA_int_set(op->ptr, "x", event->x);
913 RNA_int_set(op->ptr, "y", event->y);
915 if(!area_move_init(C, op))
916 return OPERATOR_PASS_THROUGH;
918 /* add temp handler */
919 WM_event_add_modal_handler(C, op);
921 return OPERATOR_RUNNING_MODAL;
924 static int area_move_cancel(bContext *C, wmOperator *op)
927 RNA_int_set(op->ptr, "delta", 0);
928 area_move_apply(C, op);
929 area_move_exit(C, op);
931 return OPERATOR_CANCELLED;
934 /* modal callback for while moving edges */
935 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
937 sAreaMoveData *md= op->customdata;
940 /* execute the events */
941 switch(event->type) {
944 x= RNA_int_get(op->ptr, "x");
945 y= RNA_int_get(op->ptr, "y");
947 delta= (md->dir == 'v')? event->x - x: event->y - y;
948 if(md->step) delta= delta - (delta % md->step);
949 RNA_int_set(op->ptr, "delta", delta);
951 area_move_apply(C, op);
956 switch (event->val) {
958 area_move_exit(C, op);
959 return OPERATOR_FINISHED;
961 case KM_MODAL_CANCEL:
962 return area_move_cancel(C, op);
964 case KM_MODAL_STEP10:
967 case KM_MODAL_STEP10_OFF:
973 return OPERATOR_RUNNING_MODAL;
976 static void SCREEN_OT_area_move(wmOperatorType *ot)
979 ot->name= "Move area edges";
980 ot->description= "Move selected area edges";
981 ot->idname= "SCREEN_OT_area_move";
983 ot->exec= area_move_exec;
984 ot->invoke= area_move_invoke;
985 ot->cancel= area_move_cancel;
986 ot->modal= area_move_modal;
987 ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
989 ot->flag= OPTYPE_BLOCKING;
992 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
993 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
994 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
997 /* ************** split area operator *********************************** */
1000 operator state vars:
1002 dir direction 'v' or 'h'
1004 operator customdata:
1005 area pointer to (active) area
1006 x, y last used mouse pos
1011 init() set default property values, find area based on context
1013 apply() split area based on state vars
1015 exit() cleanup, send notifier
1017 cancel() remove duplicated area
1021 exec() execute without any user interaction, based on state vars
1022 call init(), apply(), exit()
1024 invoke() gets called on mouse click in action-widget
1025 call init(), add modal handler
1026 call apply() with initial motion
1028 modal() accept modal events while doing it
1029 call move-areas code with delta motion
1030 call exit() or cancel() and remove handler
1034 #define SPLIT_STARTED 1
1035 #define SPLIT_PROGRESS 2
1037 typedef struct sAreaSplitData
1039 int x, y; /* last used mouse position */
1041 int origval; /* for move areas */
1042 int bigger, smaller; /* constraints for moving new edge */
1043 int delta; /* delta move edge */
1044 int origmin, origsize; /* to calculate fac, for property storage */
1046 ScrEdge *nedge; /* new edge */
1047 ScrArea *sarea; /* start area */
1048 ScrArea *narea; /* new area */
1051 /* generic init, no UI stuff here */
1052 static int area_split_init(bContext *C, wmOperator *op)
1054 ScrArea *sa= CTX_wm_area(C);
1058 /* required context */
1059 if(sa==NULL) return 0;
1061 /* required properties */
1062 dir= RNA_enum_get(op->ptr, "direction");
1065 if(dir=='v' && sa->winx < 2*AREAMINX) return 0;
1066 if(dir=='h' && sa->winy < 2*AREAMINY) return 0;
1069 sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1073 sd->origsize= dir=='v' ? sa->winx:sa->winy;
1074 sd->origmin = dir=='v' ? sa->totrct.xmin:sa->totrct.ymin;
1079 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1080 /* used with split operator */
1081 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1083 ScrVert *sav1= sa->v1;
1084 ScrVert *sav2= sa->v2;
1085 ScrVert *sav3= sa->v3;
1086 ScrVert *sav4= sa->v4;
1087 ScrVert *sbv1= sb->v1;
1088 ScrVert *sbv2= sb->v2;
1089 ScrVert *sbv3= sb->v3;
1090 ScrVert *sbv4= sb->v4;
1092 if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
1093 return screen_findedge(screen, sav1, sav2);
1095 else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
1096 return screen_findedge(screen, sav2, sav3);
1098 else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
1099 return screen_findedge(screen, sav3, sav4);
1101 else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
1102 return screen_findedge(screen, sav1, sav4);
1109 /* do the split, return success */
1110 static int area_split_apply(bContext *C, wmOperator *op)
1112 bScreen *sc= CTX_wm_screen(C);
1113 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1117 fac= RNA_float_get(op->ptr, "factor");
1118 dir= RNA_enum_get(op->ptr, "direction");
1120 sd->narea= area_split(CTX_wm_window(C), sc, sd->sarea, dir, fac);
1125 sd->nedge= area_findsharededge(sc, sd->sarea, sd->narea);
1127 /* select newly created edge, prepare for moving edge */
1128 for(sv= sc->vertbase.first; sv; sv= sv->next)
1131 sd->nedge->v1->flag= 1;
1132 sd->nedge->v2->flag= 1;
1134 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
1135 else sd->origval= sd->nedge->v1->vec.x;
1138 ED_area_tag_redraw(sd->sarea);
1139 ED_area_tag_redraw(sd->narea);
1141 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1149 static void area_split_exit(bContext *C, wmOperator *op)
1151 if (op->customdata) {
1153 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1154 if(sd->sarea) ED_area_tag_redraw(sd->sarea);
1155 if(sd->narea) ED_area_tag_redraw(sd->narea);
1158 MEM_freeN(op->customdata);
1159 op->customdata = NULL;
1162 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1164 /* this makes sure aligned edges will result in aligned grabbing */
1165 removedouble_scrverts(CTX_wm_screen(C));
1166 removedouble_scredges(CTX_wm_screen(C));
1170 /* UI callback, adds new handler */
1171 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
1175 if(event->type==EVT_ACTIONZONE_AREA) {
1176 sActionzoneData *sad= event->customdata;
1179 if(sad->modifier>0) {
1180 return OPERATOR_PASS_THROUGH;
1183 /* no full window splitting allowed */
1184 if(CTX_wm_area(C)->full)
1185 return OPERATOR_PASS_THROUGH;
1187 /* verify *sad itself */
1188 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
1189 return OPERATOR_PASS_THROUGH;
1191 /* is this our *sad? if areas not equal it should be passed on */
1192 if(CTX_wm_area(C)!=sad->sa1 || sad->sa1!=sad->sa2)
1193 return OPERATOR_PASS_THROUGH;
1195 /* prepare operator state vars */
1196 if(sad->gesture_dir=='n' || sad->gesture_dir=='s') {
1198 RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1202 RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1204 RNA_enum_set(op->ptr, "direction", dir);
1206 /* general init, also non-UI case, adds customdata, sets area and defaults */
1207 if(!area_split_init(C, op))
1208 return OPERATOR_PASS_THROUGH;
1210 sd= (sAreaSplitData *)op->customdata;
1216 if(area_split_apply(C, op)) {
1217 area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
1219 /* add temp handler for edge move or cancel */
1220 WM_event_add_modal_handler(C, op);
1222 return OPERATOR_RUNNING_MODAL;
1227 /* nonmodal for now */
1228 return op->type->exec(C, op);
1231 return OPERATOR_PASS_THROUGH;
1234 /* function to be called outside UI context, or for redo */
1235 static int area_split_exec(bContext *C, wmOperator *op)
1238 if(!area_split_init(C, op))
1239 return OPERATOR_CANCELLED;
1241 area_split_apply(C, op);
1242 area_split_exit(C, op);
1244 return OPERATOR_FINISHED;
1248 static int area_split_cancel(bContext *C, wmOperator *op)
1250 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1252 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1253 if (CTX_wm_area(C) == sd->narea) {
1254 CTX_wm_area_set(C, NULL);
1255 CTX_wm_region_set(C, NULL);
1259 area_split_exit(C, op);
1261 return OPERATOR_CANCELLED;
1264 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
1266 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1270 /* execute the events */
1271 switch(event->type) {
1273 dir= RNA_enum_get(op->ptr, "direction");
1275 sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
1276 area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1278 fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
1279 RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1283 if(event->val==KM_RELEASE) { /* mouse up */
1284 area_split_exit(C, op);
1285 return OPERATOR_FINISHED;
1288 case RIGHTMOUSE: /* cancel operation */
1290 return area_split_cancel(C, op);
1293 return OPERATOR_RUNNING_MODAL;
1296 static EnumPropertyItem prop_direction_items[] = {
1297 {'h', "HORIZONTAL", 0, "Horizontal", ""},
1298 {'v', "VERTICAL", 0, "Vertical", ""},
1299 {0, NULL, 0, NULL, NULL}};
1301 static void SCREEN_OT_area_split(wmOperatorType *ot)
1303 ot->name = "Split area";
1304 ot->description= "Split selected area into new windows";
1305 ot->idname = "SCREEN_OT_area_split";
1307 ot->exec= area_split_exec;
1308 ot->invoke= area_split_invoke;
1309 ot->modal= area_split_modal;
1311 ot->poll= ED_operator_areaactive;
1312 ot->flag= OPTYPE_BLOCKING;
1315 RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1316 RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1321 /* ************** scale region edge operator *********************************** */
1323 typedef struct RegionMoveData {
1327 int bigger, smaller, origval;
1335 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, char edge)
1340 if(edge=='l' || edge=='r') {
1341 dist = sa->totrct.xmax - sa->totrct.xmin;
1343 dist = sa->totrct.ymax - sa->totrct.ymin;
1346 /* subtractwidth of regions on opposite side
1347 * prevents dragging regions into other opposite regions */
1348 for (ar=sa->regionbase.first; ar; ar=ar->next)
1350 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1352 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1354 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1356 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1363 static int region_scale_invoke(bContext *C, wmOperator *op, wmEvent *event)
1365 sActionzoneData *sad= event->customdata;
1368 if(event->type!=EVT_ACTIONZONE_REGION) {
1369 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");
1370 return OPERATOR_CANCELLED;
1376 RegionMoveData *rmd= MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1379 op->customdata= rmd;
1384 rmd->edge= az->edge;
1385 rmd->origx= event->x;
1386 rmd->origy= event->y;
1387 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1389 /* if not set we do now, otherwise it uses type */
1390 if(rmd->ar->sizex==0)
1391 rmd->ar->sizex= rmd->ar->type->prefsizex;
1392 if(rmd->ar->sizey==0)
1393 rmd->ar->sizey= rmd->ar->type->prefsizey;
1395 /* now copy to regionmovedata */
1396 if(rmd->edge=='l' || rmd->edge=='r') {
1397 rmd->origval= rmd->ar->sizex;
1399 rmd->origval= rmd->ar->sizey;
1402 /* limit headers to standard height for now */
1403 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1404 maxsize = rmd->ar->type->prefsizey;
1408 CLAMP(rmd->maxsize, 0, maxsize);
1410 /* add temp handler */
1411 WM_event_add_modal_handler(C, op);
1413 return OPERATOR_RUNNING_MODAL;
1416 return OPERATOR_FINISHED;
1419 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1421 RegionMoveData *rmd= op->customdata;
1424 /* execute the events */
1425 switch(event->type) {
1428 if(rmd->edge=='l' || rmd->edge=='r') {
1429 delta= event->x - rmd->origx;
1430 if(rmd->edge=='l') delta= -delta;
1432 rmd->ar->sizex= rmd->origval + delta;
1433 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
1435 if(rmd->ar->sizex < 24) {
1436 rmd->ar->sizex= rmd->origval;
1437 if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1438 ED_region_toggle_hidden(C, rmd->ar);
1440 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1441 ED_region_toggle_hidden(C, rmd->ar);
1444 delta= event->y - rmd->origy;
1445 if(rmd->edge=='b') delta= -delta;
1447 rmd->ar->sizey= rmd->origval + delta;
1448 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
1450 if(rmd->ar->sizey < 24) {
1451 rmd->ar->sizey= rmd->origval;
1452 if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1453 ED_region_toggle_hidden(C, rmd->ar);
1455 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1456 ED_region_toggle_hidden(C, rmd->ar);
1459 ED_area_tag_redraw(rmd->sa);
1461 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1466 if(event->val==KM_RELEASE) {
1468 if(ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1469 if(rmd->ar->flag & RGN_FLAG_HIDDEN) {
1470 ED_region_toggle_hidden(C, rmd->ar);
1472 ED_area_tag_redraw(rmd->sa);
1474 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1477 MEM_freeN(op->customdata);
1478 op->customdata = NULL;
1480 return OPERATOR_FINISHED;
1488 return OPERATOR_RUNNING_MODAL;
1492 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1495 ot->name= "Scale Region Size";
1496 ot->description= "Scale selected area";
1497 ot->idname= "SCREEN_OT_region_scale";
1499 ot->invoke= region_scale_invoke;
1500 ot->modal= region_scale_modal;
1502 ot->poll= ED_operator_areaactive;
1504 ot->flag= OPTYPE_BLOCKING;
1508 /* ************** frame change operator ***************************** */
1510 /* function to be called outside UI context, or for redo */
1511 static int frame_offset_exec(bContext *C, wmOperator *op)
1515 delta = RNA_int_get(op->ptr, "delta");
1517 CTX_data_scene(C)->r.cfra += delta;
1519 sound_seek_scene(C);
1521 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1523 return OPERATOR_FINISHED;
1526 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
1528 ot->name = "Frame Offset";
1529 ot->idname = "SCREEN_OT_frame_offset";
1531 ot->exec= frame_offset_exec;
1533 ot->poll= ED_operator_screenactive;
1537 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1541 /* function to be called outside UI context, or for redo */
1542 static int frame_jump_exec(bContext *C, wmOperator *op)
1544 Scene *scene= CTX_data_scene(C);
1546 if (RNA_boolean_get(op->ptr, "end"))
1551 sound_seek_scene(C);
1553 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1555 return OPERATOR_FINISHED;
1558 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1560 ot->name = "Jump to Endpoint";
1561 ot->description= "Jump to first/last frame in frame range";
1562 ot->idname = "SCREEN_OT_frame_jump";
1564 ot->exec= frame_jump_exec;
1566 ot->poll= ED_operator_screenactive;
1570 RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range.");
1574 /* ************** jump to keyframe operator ***************************** */
1576 /* function to be called outside UI context, or for redo */
1577 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1579 Scene *scene= CTX_data_scene(C);
1580 Object *ob= CTX_data_active_object(C);
1583 float cfra= (scene)? (float)(CFRA) : 0.0f;
1584 short next= RNA_boolean_get(op->ptr, "next");
1588 return OPERATOR_CANCELLED;
1590 /* init binarytree-list for getting keyframes */
1591 BLI_dlrbTree_init(&keys);
1593 /* populate tree with keyframe nodes */
1594 if (scene && scene->adt)
1595 scene_to_keylist(NULL, scene, &keys, NULL);
1597 ob_to_keylist(NULL, ob, &keys, NULL);
1599 /* build linked-list for searching */
1600 BLI_dlrbTree_linkedlist_sync(&keys);
1602 /* find matching keyframe in the right direction */
1604 ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1606 ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1608 /* set the new frame (if keyframe found) */
1610 CFRA= (int)ak->cfra;
1612 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
1614 /* free temp stuff */
1615 BLI_dlrbTree_free(&keys);
1617 sound_seek_scene(C);
1619 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1621 return OPERATOR_FINISHED;
1624 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1626 ot->name = "Jump to Keyframe";
1627 ot->description= "Jump to previous/next keyframe";
1628 ot->idname = "SCREEN_OT_keyframe_jump";
1630 ot->exec= keyframe_jump_exec;
1632 ot->poll= ED_operator_screenactive;
1636 RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1639 /* ************** switch screen operator ***************************** */
1642 /* function to be called outside UI context, or for redo */
1643 static int screen_set_exec(bContext *C, wmOperator *op)
1645 bScreen *screen= CTX_wm_screen(C);
1646 ScrArea *sa= CTX_wm_area(C);
1647 int tot= BLI_countlist(&CTX_data_main(C)->screen);
1648 int delta= RNA_int_get(op->ptr, "delta");
1650 /* return to previous state before switching screens */
1652 ED_screen_full_restore(C, sa);
1656 screen= screen->id.next;
1657 if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1658 if(screen->winid==0 && screen->full==0)
1662 else if(delta== -1) {
1664 screen= screen->id.prev;
1665 if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1666 if(screen->winid==0 && screen->full==0)
1675 ED_screen_set(C, screen);
1676 return OPERATOR_FINISHED;
1678 return OPERATOR_CANCELLED;
1681 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1683 ot->name = "Set Screen";
1684 ot->description= "Cycle through available screens";
1685 ot->idname = "SCREEN_OT_screen_set";
1687 ot->exec= screen_set_exec;
1688 ot->poll= ED_operator_screenactive;
1691 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1694 /* ************** screen full-area operator ***************************** */
1697 /* function to be called outside UI context, or for redo */
1698 static int screen_full_area_exec(bContext *C, wmOperator *op)
1700 ed_screen_fullarea(C, CTX_wm_window(C), CTX_wm_area(C));
1701 return OPERATOR_FINISHED;
1704 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1706 ot->name = "Toggle Full Screen";
1707 ot->description= "Toggle display selected area as fullscreen";
1708 ot->idname = "SCREEN_OT_screen_full_area";
1710 ot->exec= screen_full_area_exec;
1711 ot->poll= ED_operator_areaactive;
1718 /* ************** join area operator ********************************************** */
1720 /* operator state vars used:
1721 x1, y1 mouse coord in first area, which will disappear
1722 x2, y2 mouse coord in 2nd area, which will become joined
1726 init() find edge based on state vars
1727 test if the edge divides two areas,
1728 store active and nonactive area,
1730 apply() do the actual join
1732 exit() cleanup, send notifier
1736 exec() calls init, apply, exit
1738 invoke() sets mouse coords in x,y
1742 modal() accept modal events while doing it
1743 call apply() with active window and nonactive window
1744 call exit() and remove handler when LMB confirm
1748 typedef struct sAreaJoinData
1750 ScrArea *sa1; /* first area to be considered */
1751 ScrArea *sa2; /* second area to be considered */
1752 ScrArea *scr; /* designed for removal */
1757 /* validate selection inside screen, set variables OK */
1758 /* return 0: init failed */
1759 /* XXX todo: find edge based on (x,y) and set other area? */
1760 static int area_join_init(bContext *C, wmOperator *op)
1763 sAreaJoinData* jd= NULL;
1767 /* required properties, make negative to get return 0 if not set by caller */
1768 x1= RNA_int_get(op->ptr, "x1");
1769 y1= RNA_int_get(op->ptr, "y1");
1770 x2= RNA_int_get(op->ptr, "x2");
1771 y2= RNA_int_get(op->ptr, "y2");
1773 sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1774 sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1775 if(sa1==NULL || sa2==NULL || sa1==sa2)
1778 jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1781 jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1783 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1790 /* apply the join of the areas (space types) */
1791 static int area_join_apply(bContext *C, wmOperator *op)
1793 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1796 if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1799 if (CTX_wm_area(C) == jd->sa2) {
1800 CTX_wm_area_set(C, NULL);
1801 CTX_wm_region_set(C, NULL);
1807 /* finish operation */
1808 static void area_join_exit(bContext *C, wmOperator *op)
1810 if (op->customdata) {
1811 MEM_freeN(op->customdata);
1812 op->customdata = NULL;
1815 /* this makes sure aligned edges will result in aligned grabbing */
1816 removedouble_scredges(CTX_wm_screen(C));
1817 removenotused_scredges(CTX_wm_screen(C));
1818 removenotused_scrverts(CTX_wm_screen(C));
1821 static int area_join_exec(bContext *C, wmOperator *op)
1823 if(!area_join_init(C, op))
1824 return OPERATOR_CANCELLED;
1826 area_join_apply(C, op);
1827 area_join_exit(C, op);
1829 return OPERATOR_FINISHED;
1832 /* interaction callback */
1833 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1836 if(event->type==EVT_ACTIONZONE_AREA) {
1837 sActionzoneData *sad= event->customdata;
1839 if(sad->modifier>0) {
1840 return OPERATOR_PASS_THROUGH;
1843 /* verify *sad itself */
1844 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1845 return OPERATOR_PASS_THROUGH;
1847 /* is this our *sad? if areas equal it should be passed on */
1848 if(sad->sa1==sad->sa2)
1849 return OPERATOR_PASS_THROUGH;
1851 /* prepare operator state vars */
1852 RNA_int_set(op->ptr, "x1", sad->x);
1853 RNA_int_set(op->ptr, "y1", sad->y);
1854 RNA_int_set(op->ptr, "x2", event->x);
1855 RNA_int_set(op->ptr, "y2", event->y);
1857 if(!area_join_init(C, op))
1858 return OPERATOR_PASS_THROUGH;
1860 /* add temp handler */
1861 WM_event_add_modal_handler(C, op);
1863 return OPERATOR_RUNNING_MODAL;
1866 return OPERATOR_PASS_THROUGH;
1869 static int area_join_cancel(bContext *C, wmOperator *op)
1871 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1874 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1875 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1878 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1879 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1882 WM_event_add_notifier(C, NC_WINDOW, NULL);
1884 area_join_exit(C, op);
1886 return OPERATOR_CANCELLED;
1889 /* modal callback while selecting area (space) that will be removed */
1890 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1892 bScreen *sc= CTX_wm_screen(C);
1893 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1895 /* execute the events */
1896 switch(event->type) {
1900 ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1904 if (jd->sa1 != sa) {
1905 dir = area_getorientation(sc, jd->sa1, sa);
1907 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1909 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1912 /* we are not bordering on the previously selected area
1913 we check if area has common border with the one marked for removal
1914 in this case we can swap areas.
1916 dir = area_getorientation(sc, sa, jd->sa2);
1918 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1919 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1922 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1923 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1926 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1930 WM_event_add_notifier(C, NC_WINDOW, NULL);
1933 /* we are back in the area previously selected for keeping
1934 * we swap the areas if possible to allow user to choose */
1935 if (jd->sa2 != NULL) {
1936 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1937 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1940 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1941 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1942 dir = area_getorientation(sc, jd->sa1, jd->sa2);
1944 printf("oops, didn't expect that!\n");
1948 dir = area_getorientation(sc, jd->sa1, sa);
1950 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1952 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1955 WM_event_add_notifier(C, NC_WINDOW, NULL);
1961 if(event->val==KM_RELEASE) {
1963 ED_area_tag_redraw(jd->sa1);
1964 ED_area_tag_redraw(jd->sa2);
1966 area_join_apply(C, op);
1967 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1968 area_join_exit(C, op);
1969 return OPERATOR_FINISHED;
1975 return area_join_cancel(C, op);
1978 return OPERATOR_RUNNING_MODAL;
1981 /* Operator for joining two areas (space types) */
1982 static void SCREEN_OT_area_join(wmOperatorType *ot)
1985 ot->name= "Join area";
1986 ot->description= "Join selected areas into new window";
1987 ot->idname= "SCREEN_OT_area_join";
1990 ot->exec= area_join_exec;
1991 ot->invoke= area_join_invoke;
1992 ot->modal= area_join_modal;
1993 ot->poll= ED_operator_areaactive;
1995 ot->flag= OPTYPE_BLOCKING;
1998 RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1999 RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2000 RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2001 RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2004 /* ************** repeat last operator ***************************** */
2006 static int repeat_last_exec(bContext *C, wmOperator *op)
2008 wmOperator *lastop= CTX_wm_manager(C)->operators.last;
2011 WM_operator_repeat(C, lastop);
2013 return OPERATOR_CANCELLED;
2016 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2019 ot->name= "Repeat Last";
2020 ot->description= "Repeat last action";
2021 ot->idname= "SCREEN_OT_repeat_last";
2024 ot->exec= repeat_last_exec;
2026 ot->poll= ED_operator_screenactive;
2030 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
2032 wmWindowManager *wm= CTX_wm_manager(C);
2038 items= BLI_countlist(&wm->operators);
2040 return OPERATOR_CANCELLED;
2042 pup= uiPupMenuBegin(C, op->type->name, 0);
2043 layout= uiPupMenuLayout(pup);
2045 for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
2046 uiItemIntO(layout, lastop->type->name, 0, op->type->idname, "index", i);
2048 uiPupMenuEnd(C, pup);
2050 return OPERATOR_CANCELLED;
2053 static int repeat_history_exec(bContext *C, wmOperator *op)
2055 wmWindowManager *wm= CTX_wm_manager(C);
2057 op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2059 /* let's put it as last operator in list */
2060 BLI_remlink(&wm->operators, op);
2061 BLI_addtail(&wm->operators, op);
2063 WM_operator_repeat(C, op);
2066 return OPERATOR_FINISHED;
2069 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2072 ot->name= "Repeat History";
2073 ot->description= "Display menu for previous actions performed";
2074 ot->idname= "SCREEN_OT_repeat_history";
2077 ot->invoke= repeat_history_invoke;
2078 ot->exec= repeat_history_exec;
2080 ot->poll= ED_operator_screenactive;
2082 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2085 /* ********************** redo operator ***************************** */
2087 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
2089 wmWindowManager *wm= CTX_wm_manager(C);
2092 /* only for operators that are registered and did an undo push */
2093 for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
2094 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
2098 WM_operator_redo_popup(C, lastop);
2100 return OPERATOR_CANCELLED;
2103 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2106 ot->name= "Redo Last";
2107 ot->description= "Display menu for last action performed";
2108 ot->idname= "SCREEN_OT_redo_last";
2111 ot->invoke= redo_last_invoke;
2113 ot->poll= ED_operator_screenactive;
2116 /* ************** region four-split operator ***************************** */
2118 /* insert a region in the area region list */
2119 static int region_quadview_exec(bContext *C, wmOperator *op)
2121 ARegion *ar= CTX_wm_region(C);
2124 if(ar->regiontype!=RGN_TYPE_WINDOW)
2125 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2126 else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2127 ScrArea *sa= CTX_wm_area(C);
2130 /* keep current region */
2133 if(sa->spacetype==SPACE_VIEW3D) {
2134 RegionView3D *rv3d= ar->regiondata;
2136 rv3d->rflag &= ~RV3D_CLIPPING;
2139 for(ar= sa->regionbase.first; ar; ar= arn) {
2141 if(ar->alignment==RGN_ALIGN_QSPLIT) {
2142 ED_region_exit(C, ar);
2143 BKE_area_region_free(sa->type, ar);
2144 BLI_remlink(&sa->regionbase, ar);
2149 ED_area_tag_redraw(sa);
2151 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2154 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2156 ScrArea *sa= CTX_wm_area(C);
2160 ar->alignment= RGN_ALIGN_QSPLIT;
2162 for(count=0; count<3; count++) {
2163 newar= BKE_area_region_copy(sa->type, ar);
2164 BLI_addtail(&sa->regionbase, newar);
2167 /* lock views and set them */
2168 if(sa->spacetype==SPACE_VIEW3D) {
2171 rv3d= ar->regiondata;
2172 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2175 rv3d= ar->regiondata;
2176 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2179 rv3d= ar->regiondata;
2180 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2183 rv3d= ar->regiondata;
2184 rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2188 ED_area_tag_redraw(sa);
2190 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2194 return OPERATOR_FINISHED;
2197 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
2200 ot->name= "Toggle Quad View";
2201 ot->description= "Split selected area into camera, front, right & top views";
2202 ot->idname= "SCREEN_OT_region_quadview";
2205 // ot->invoke= WM_operator_confirm;
2206 ot->exec= region_quadview_exec;
2207 ot->poll= ED_operator_areaactive;
2213 /* ************** region flip operator ***************************** */
2215 /* flip a region alignment */
2216 static int region_flip_exec(bContext *C, wmOperator *op)
2218 ARegion *ar= CTX_wm_region(C);
2221 return OPERATOR_CANCELLED;
2223 if(ar->alignment==RGN_ALIGN_TOP)
2224 ar->alignment= RGN_ALIGN_BOTTOM;
2225 else if(ar->alignment==RGN_ALIGN_BOTTOM)
2226 ar->alignment= RGN_ALIGN_TOP;
2227 else if(ar->alignment==RGN_ALIGN_LEFT)
2228 ar->alignment= RGN_ALIGN_RIGHT;
2229 else if(ar->alignment==RGN_ALIGN_RIGHT)
2230 ar->alignment= RGN_ALIGN_LEFT;
2233 ED_area_tag_redraw(CTX_wm_area(C));
2235 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2237 return OPERATOR_FINISHED;
2241 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2244 ot->name= "Flip Region";
2245 ot->idname= "SCREEN_OT_region_flip";
2248 ot->exec= region_flip_exec;
2250 ot->poll= ED_operator_areaactive;
2254 /* ************** header flip operator ***************************** */
2256 /* flip a header region alignment */
2257 static int header_flip_exec(bContext *C, wmOperator *op)
2259 ARegion *ar= CTX_wm_region(C);
2261 /* find the header region
2262 * - try context first, but upon failing, search all regions in area...
2264 if((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) {
2265 ScrArea *sa= CTX_wm_area(C);
2267 /* loop over all regions until a matching one is found */
2268 for (ar= sa->regionbase.first; ar; ar= ar->next) {
2269 if(ar->regiontype == RGN_TYPE_HEADER)
2273 /* don't do anything if no region */
2275 return OPERATOR_CANCELLED;
2278 /* copied from SCREEN_OT_region_flip */
2279 if(ar->alignment==RGN_ALIGN_TOP)
2280 ar->alignment= RGN_ALIGN_BOTTOM;
2281 else if(ar->alignment==RGN_ALIGN_BOTTOM)
2282 ar->alignment= RGN_ALIGN_TOP;
2283 else if(ar->alignment==RGN_ALIGN_LEFT)
2284 ar->alignment= RGN_ALIGN_RIGHT;
2285 else if(ar->alignment==RGN_ALIGN_RIGHT)
2286 ar->alignment= RGN_ALIGN_LEFT;
2289 ED_area_tag_redraw(CTX_wm_area(C));
2292 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2294 return OPERATOR_FINISHED;
2298 static void SCREEN_OT_header_flip(wmOperatorType *ot)
2301 ot->name= "Flip Header Region";
2302 ot->idname= "SCREEN_OT_header_flip";
2305 ot->exec= header_flip_exec;
2307 ot->poll= ED_operator_areaactive;
2311 /* ************** header tools operator ***************************** */
2313 static int header_toolbox_invoke(bContext *C, wmOperator *op, wmEvent *event)
2315 ScrArea *sa= CTX_wm_area(C);
2316 ARegion *ar= CTX_wm_region(C);
2320 pup= uiPupMenuBegin(C, "Header", 0);
2321 layout= uiPupMenuLayout(pup);
2323 // XXX SCREEN_OT_region_flip doesn't work - gets wrong context for active region, so added custom operator
2324 if (ar->alignment == RGN_ALIGN_TOP)
2325 uiItemO(layout, "Flip to Bottom", 0, "SCREEN_OT_header_flip");
2327 uiItemO(layout, "Flip to Top", 0, "SCREEN_OT_header_flip");
2331 /* file browser should be fullscreen all the time, but other regions can be maximised/restored... */
2332 if (sa->spacetype != SPACE_FILE) {
2334 uiItemO(layout, "Tile Area", 0, "SCREEN_OT_screen_full_area");
2336 uiItemO(layout, "Maximize Area", 0, "SCREEN_OT_screen_full_area");
2339 uiPupMenuEnd(C, pup);
2341 return OPERATOR_CANCELLED;
2344 void SCREEN_OT_header_toolbox(wmOperatorType *ot)
2347 ot->name= "Header Toolbox";
2348 ot->description="Display header region toolbox";
2349 ot->idname= "SCREEN_OT_header_toolbox";
2352 ot->invoke= header_toolbox_invoke;
2355 /* ****************** anim player, with timer ***************** */
2357 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2359 if(regiontype==RGN_TYPE_WINDOW) {
2361 switch (spacetype) {
2363 if(redraws & TIME_ALL_3D_WIN)
2369 if(redraws & TIME_ALL_ANIM_WIN)
2373 /* if only 1 window or 3d windows, we do timeline too */
2374 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2378 if(redraws & TIME_ALL_BUTS_WIN)
2382 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2386 if(redraws & (TIME_NODES))
2390 if(redraws & TIME_ALL_IMAGE_WIN)
2396 else if(regiontype==RGN_TYPE_UI) {
2397 if(redraws & TIME_ALL_BUTS_WIN)
2400 else if(regiontype==RGN_TYPE_HEADER) {
2401 if(spacetype==SPACE_TIME)
2404 else if (regiontype==RGN_TYPE_PREVIEW) {
2405 switch (spacetype) {
2407 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2415 static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event)
2417 bScreen *screen= CTX_wm_screen(C);
2419 if(screen->animtimer==event->customdata) {
2420 Scene *scene= CTX_data_scene(C);
2421 wmTimer *wt= screen->animtimer;
2422 ScreenAnimData *sad= wt->customdata;
2426 /* sync, don't sync, or follow scene setting */
2427 if(sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2428 else if(sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2429 else sync= (scene->flag & SCE_FRAME_DROP);
2431 if((scene->audio.flag & AUDIO_SYNC) && !(sad->flag & ANIMPLAY_FLAG_REVERSE))
2433 scene->r.cfra = floor(sound_sync_scene(scene) * FPS);
2438 int step = floor(wt->duration * FPS);
2440 if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2441 scene->r.cfra -= step;
2443 scene->r.cfra += step;
2444 wt->duration -= ((float)step)/FPS;
2448 if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2455 /* reset 'jumped' flag before checking if we need to jump... */
2456 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2458 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2459 /* jump back to end? */
2461 if (scene->r.cfra < scene->r.psfra) {
2462 scene->r.cfra= scene->r.pefra;
2463 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2467 if (scene->r.cfra < scene->r.sfra) {
2468 scene->r.cfra= scene->r.efra;
2469 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2474 /* jump back to start? */
2476 if (scene->r.cfra > scene->r.pefra) {
2477 scene->r.cfra= scene->r.psfra;
2478 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2482 if (scene->r.cfra > scene->r.efra) {
2483 scene->r.cfra= scene->r.sfra;
2484 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2489 if(sad->flag & ANIMPLAY_FLAG_JUMPED)
2490 sound_seek_scene(C);
2492 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2493 ED_update_for_newframe(C, 1);
2495 for(sa= screen->areabase.first; sa; sa= sa->next) {
2497 for(ar= sa->regionbase.first; ar; ar= ar->next) {
2499 ED_region_tag_redraw(ar);
2501 if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2502 ED_region_tag_redraw(ar);
2506 /* update frame rate info too
2507 * NOTE: this may not be accurate enough, since we might need this after modifiers/etc.
2508 * have been calculated instead of just before updates have been done?
2510 ED_refresh_viewport_fps(C);
2512 /* recalculate the timestep for the timer now that we've finished calculating this,
2513 * since the frames-per-second value may have been changed
2515 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2516 wt->timestep= (1.0/FPS);
2518 return OPERATOR_FINISHED;
2520 return OPERATOR_PASS_THROUGH;
2523 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2526 ot->name= "Animation Step";
2527 ot->description= "Step through animation by position";
2528 ot->idname= "SCREEN_OT_animation_step";
2531 ot->invoke= screen_animation_step;
2533 ot->poll= ED_operator_screenactive;
2537 /* ****************** anim player, starts or ends timer ***************** */
2539 /* toggle operator */
2540 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
2542 bScreen *screen= CTX_wm_screen(C);
2543 struct Scene* scene = CTX_data_scene(C);
2545 if(screen->animtimer) {
2546 /* stop playback now */
2547 ED_screen_animation_timer(C, 0, 0, 0);
2548 sound_stop_scene(scene);
2551 ScrArea *sa= CTX_wm_area(C);
2552 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2554 if(mode == 1) // XXX only play audio forwards!?
2555 sound_play_scene(scene);
2557 if(RNA_property_is_set(op->ptr, "sync"))
2558 sync= (RNA_boolean_get(op->ptr, "sync"));
2560 /* timeline gets special treatment since it has it's own menu for determining redraws */
2561 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2562 SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2564 ED_screen_animation_timer(C, stime->redraws, sync, mode);
2566 /* update region if TIME_REGION was set, to leftmost 3d window */
2567 ED_screen_animation_timer_update(screen, stime->redraws);
2570 int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2572 /* XXX - would like a better way to deal with this situation - Campbell */
2573 if((sa) && (sa->spacetype == SPACE_SEQ)) {
2574 redraws |= TIME_SEQ;
2577 ED_screen_animation_timer(C, redraws, sync, mode);
2579 if(screen->animtimer) {
2580 wmTimer *wt= screen->animtimer;
2581 ScreenAnimData *sad= wt->customdata;
2583 sad->ar= CTX_wm_region(C);
2588 return OPERATOR_FINISHED;
2591 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2594 ot->name= "Play Animation";
2595 ot->description= "Play animation";
2596 ot->idname= "SCREEN_OT_animation_play";
2599 ot->invoke= screen_animation_play;
2601 ot->poll= ED_operator_screenactive;
2603 RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2604 RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate and stay in sync with audio.");
2607 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2609 bScreen *screen= CTX_wm_screen(C);
2611 if(screen->animtimer) {
2612 ScreenAnimData *sad= screen->animtimer->customdata;
2613 Scene *scene= CTX_data_scene(C);
2615 /* reset current frame before stopping, and just send a notifier to deal with the rest
2616 * (since playback still needs to be stopped)
2618 scene->r.cfra= sad->sfra;
2619 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2621 /* call the other "toggling" operator to clean up now */
2622 return screen_animation_play(C, op, event);
2625 return OPERATOR_PASS_THROUGH;
2628 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2631 ot->name= "Cancel Animation";
2632 ot->description= "Cancel animation, returning to the original frame";
2633 ot->idname= "SCREEN_OT_animation_cancel";
2636 ot->invoke= screen_animation_cancel;
2638 ot->poll= ED_operator_screenactive;
2641 /* ************** border select operator (template) ***************************** */
2643 /* operator state vars used: (added by default WM callbacks)
2647 customdata: the wmGesture pointer
2651 exec() has to be filled in by user
2653 invoke() default WM function
2656 modal() default WM function
2657 accept modal events while doing it, calls exec(), handles ESC and border drawing
2659 poll() has to be filled in by user for context
2662 static int border_select_do(bContext *C, wmOperator *op)
2664 int event_type= RNA_int_get(op->ptr, "event_type");
2666 if(event_type==LEFTMOUSE)
2667 printf("border select do select\n");
2668 else if(event_type==RIGHTMOUSE)
2669 printf("border select deselect\n");
2671 printf("border select do something\n");
2676 static void SCREEN_OT_border_select(wmOperatorType *ot)
2679 ot->name= "Border select";
2680 ot->idname= "SCREEN_OT_border_select";
2683 ot->exec= border_select_do;
2684 ot->invoke= WM_border_select_invoke;
2685 ot->modal= WM_border_select_modal;
2687 ot->poll= ED_operator_areaactive;
2690 RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2691 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2692 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2693 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2694 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2699 /* ****************************** render invoking ***************** */
2701 /* set callbacks, exported to sequence render too.
2702 Only call in foreground (UI) renders. */
2704 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2705 /* window as the last possible alternative. */
2706 static ScrArea *biggest_non_image_area(bContext *C)
2708 bScreen *sc= CTX_wm_screen(C);
2709 ScrArea *sa, *big= NULL;
2710 int size, maxsize= 0, bwmaxsize= 0;
2713 for(sa= sc->areabase.first; sa; sa= sa->next) {
2714 if(sa->winx > 30 && sa->winy > 30) {
2715 size= sa->winx*sa->winy;
2716 if(sa->spacetype == SPACE_BUTS) {
2717 if(foundwin == 0 && size > bwmaxsize) {
2722 else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2733 static ScrArea *biggest_area(bContext *C)
2735 bScreen *sc= CTX_wm_screen(C);
2736 ScrArea *sa, *big= NULL;
2737 int size, maxsize= 0;
2739 for(sa= sc->areabase.first; sa; sa= sa->next) {
2740 size= sa->winx*sa->winy;
2741 if(size > maxsize) {
2750 static ScrArea *find_area_showing_r_result(bContext *C)
2752 wmWindowManager *wm= CTX_wm_manager(C);
2757 /* find an imagewindow showing render result */
2758 for(win=wm->windows.first; win; win=win->next) {
2759 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2760 if(sa->spacetype==SPACE_IMAGE) {
2761 sima= sa->spacedata.first;
2762 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2771 static ScrArea *find_area_image_empty(bContext *C)
2773 bScreen *sc= CTX_wm_screen(C);
2777 /* find an imagewindow showing render result */
2778 for(sa=sc->areabase.first; sa; sa= sa->next) {
2779 if(sa->spacetype==SPACE_IMAGE) {
2780 sima= sa->spacedata.first;
2788 #if 0 // XXX not used
2789 static ScrArea *find_empty_image_area(bContext *C)
2791 bScreen *sc= CTX_wm_screen(C);
2795 /* find an imagewindow showing render result */
2796 for(sa=sc->areabase.first; sa; sa= sa->next) {
2797 if(sa->spacetype==SPACE_IMAGE) {
2798 sima= sa->spacedata.first;
2805 #endif // XXX not used
2807 /* new window uses x,y to set position */
2808 static void screen_set_image_output(bContext *C, int mx, int my)
2810 wmWindow *win= CTX_wm_window(C);
2811 Scene *scene= CTX_data_scene(C);
2814 int area_was_image=0;
2816 if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2820 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2821 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2823 /* arbitrary... miniature image window views don't make much sense */
2824 if(sizex < 320) sizex= 320;
2825 if(sizey < 256) sizey= 256;
2827 /* XXX some magic to calculate postition */
2828 rect.xmin= mx + win->posx - sizex/2;
2829 rect.ymin= my + win->posy - sizey/2;
2830 rect.xmax= rect.xmin + sizex;
2831 rect.ymax= rect.ymin + sizey;
2833 /* changes context! */
2834 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2838 else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2839 if (CTX_wm_area(C)->spacetype == SPACE_IMAGE)
2842 /* this function returns with changed context */
2843 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2848 sa= find_area_showing_r_result(C);
2850 sa= find_area_image_empty(C);
2853 /* find largest open non-image area */
2854 sa= biggest_non_image_area(C);
2856 ED_area_newspace(C, sa, SPACE_IMAGE);
2857 sima= sa->spacedata.first;
2859 /* makes ESC go back to prev space */
2860 sima->flag |= SI_PREVSPACE;
2863 /* use any area of decent size */
2864 sa= biggest_area(C);
2865 if(sa->spacetype!=SPACE_IMAGE) {
2866 // XXX newspace(sa, SPACE_IMAGE);
2867 sima= sa->spacedata.first;
2869 /* makes ESC go back to prev space */
2870 sima->flag |= SI_PREVSPACE;
2875 sima= sa->spacedata.first;
2877 /* get the correct image, and scale it */
2878 sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2881 /* if we're rendering to full screen, set appropriate hints on image editor
2882 * so it can restore properly on pressing esc */
2884 sima->flag |= SI_FULLWINDOW;
2886 /* Tell the image editor to revert to previous space in space list on close
2887 * _only_ if it wasn't already an image editor when the render was invoked */
2888 if (area_was_image == 0)
2889 sima->flag |= SI_PREVSPACE;
2891 /* Leave it alone so the image editor will just go back from
2892 * full screen to the original tiled setup */
2900 /* executes blocking render */
2901 static int screen_render_exec(bContext *C, wmOperator *op)
2903 Scene *scene= CTX_data_scene(C);
2904 Render *re= RE_GetRender(scene->id.name, RE_SLOT_VIEW);
2907 re= RE_NewRender(scene->id.name, RE_SLOT_VIEW);
2909 RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2911 if(RNA_boolean_get(op->ptr, "animation"))
2912 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step, op->reports);
2914 RE_BlenderFrame(re, scene, NULL, scene->r.cfra);
2916 // no redraw needed, we leave state as we entered it
2917 ED_update_for_newframe(C, 1);
2919 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2921 return OPERATOR_FINISHED;
2924 typedef struct RenderJob {
2928 SceneRenderLayer *srl;
2934 ReportList *reports;
2937 static void render_freejob(void *rjv)
2944 /* str is IMA_RW_MAXTEXT in size */
2945 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2947 char info_time_str[32]; // used to be extern to header_info.c
2948 uintptr_t mem_in_use, mmap_in_use;
2949 float megs_used_memory, mmap_used_memory;
2952 mem_in_use= MEM_get_memory_in_use();
2953 mmap_in_use= MEM_get_mapped_memory_in_use();
2955 megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2956 mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2958 if(scene->lay & 0xFF000000)
2959 spos+= sprintf(spos, "Localview | ");
2960 else if(scene->r.scemode & R_SINGLE_LAYER)
2961 spos+= sprintf(spos, "Single Layer | ");
2964 spos+= sprintf(spos, "%s ", rs->statstr);
2967 spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2968 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2969 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2970 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2973 spos+= sprintf(spos, "Field %d ", rs->curfield);
2975 spos+= sprintf(spos, "Blur %d ", rs->curblur);
2978 BLI_timestr(rs->lastframetime, info_time_str);
2979 spos+= sprintf(spos, "Time:%s ", info_time_str);
2981 if(rs->infostr && rs->infostr[0])
2982 spos+= sprintf(spos, "| %s ", rs->infostr);
2984 /* very weak... but 512 characters is quite safe */
2985 if(spos >= str+IMA_RW_MAXTEXT)
2987 printf("WARNING! renderwin text beyond limit \n");
2991 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2995 /* malloc OK here, stats_draw is not in tile threads */
2996 if(rj->image->render_text==NULL)
2997 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
2999 make_renderinfo_string(rs, rj->scene, rj->image->render_text);
3001 /* make jobs timer to send notifier */
3002 *(rj->do_update)= 1;
3006 /* called inside thread! */
3007 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
3009 float x1, y1, *rectf= NULL;
3010 int ymin, ymax, xmin, xmax;
3014 /* if renrect argument, we only refresh scanlines */
3016 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
3017 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
3020 /* xmin here is first subrect x coord, xmax defines subrect width */
3021 xmin = renrect->xmin + rr->crop;
3022 xmax = renrect->xmax - xmin - rr->crop;
3025 ymin= renrect->ymin + rr->crop;
3026 ymax= renrect->ymax - ymin - rr->crop;
3029 renrect->ymin= renrect->ymax;
3033 xmin = ymin = rr->crop;
3034 xmax = rr->rectx - 2*rr->crop;
3035 ymax = rr->recty - 2*rr->crop;
3038 /* xmin ymin is in tile coords. transform to ibuf */
3039 rxmin= rr->tilerect.xmin + xmin;
3040 if(rxmin >= ibuf->x) return;
3041 rymin= rr->tilerect.ymin + ymin;
3042 if(rymin >= ibuf->y) return;
3044 if(rxmin + xmax > ibuf->x)
3045 xmax= ibuf->x - rxmin;
3046 if(rymin + ymax > ibuf->y)
3047 ymax= ibuf->y - rymin;
3049 if(xmax < 1 || ymax < 1) return;
3051 /* find current float rect for display, first case is after composit... still weak */
3058 if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
3059 rectf= rr->renlay->rectf;
3062 if(rectf==NULL) return;
3064 if(ibuf->rect==NULL)
3065 imb_addrectImBuf(ibuf);
3067 rectf+= 4*(rr->rectx*ymin + xmin);
3068 rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
3070 /* XXX make nice consistent functions for this */
3071 if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
3072 for(y1= 0; y1<ymax; y1++) {
3077 /* XXX temp. because crop offset */
3078 if( rectc >= (char *)(ibuf->rect)) {
3079 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3080 srgb[0]= linearrgb_to_srgb(rf[0]);
3081 srgb[1]= linearrgb_to_srgb(rf[1]);
3082 srgb[2]= linearrgb_to_srgb(rf[2]);
3084 rc[0]= FTOCHAR(srgb[0]);
3085 rc[1]= FTOCHAR(srgb[1]);
3086 rc[2]= FTOCHAR(srgb[2]);
3087 rc[3]= FTOCHAR(rf[3]);
3090 rectf += 4*rr->rectx;
3094 for(y1= 0; y1<ymax; y1++) {
3098 /* XXX temp. because crop offset */
3099 if( rectc >= (char *)(ibuf->rect)) {
3100 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3101 rc[0]= FTOCHAR(rf[0]);
3102 rc[1]= FTOCHAR(rf[1]);
3103 rc[2]= FTOCHAR(rf[2]);
3104 rc[3]= FTOCHAR(rf[3]);
3107 rectf += 4*rr->rectx;
3114 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
3120 ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
3122 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
3124 /* make jobs timer to send notifier */
3125 *(rj->do_update)= 1;
3127 BKE_image_release_ibuf(rj->image, lock);
3130 static void render_startjob(void *rjv, short *stop, short *do_update)
3133 // Main *mainp= BKE_undo_get_main(&rj->scene);
3136 rj->do_update= do_update;
3138 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2)
3139 // Workaround for Apple gcc 4.2.1 omp vs background thread bug
3140 pthread_setspecific (gomp_tls_key, thread_tls_data);
3144 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step, rj->reports);
3146 RE_BlenderFrame(rj->re, rj->scene, rj->srl, rj->scene->r.cfra);
3149 // free_main(mainp);
3152 /* called by render, check job 'stop' value or the global */
3153 static int render_breakjob(void *rjv)
3159 if(rj->stop && *(rj->stop))
3165 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3167 /* no running blender, remove handler and pass through */
3168 if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
3169 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
3171 /* running render */
3172 switch (event->type) {
3174 return OPERATOR_RUNNING_MODAL;
3177 return OPERATOR_PASS_THROUGH;
3180 /* using context, starts job */
3181 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3183 /* new render clears all callbacks */
3184 Scene *scene= CTX_data_scene(C);
3185 SceneRenderLayer *srl=NULL;
3191 /* only one render job at a time */
3192 if(WM_jobs_test(CTX_wm_manager(C), scene))
3193 return OPERATOR_CANCELLED;
3195 /* stop all running jobs, currently previews frustrate Render */
3196 WM_jobs_stop_all(CTX_wm_manager(C));
3198 /* handle UI stuff */
3201 /* flush multires changes (for sculpt) */
3202 multires_force_render_update(CTX_data_active_object(C));