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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1553 return OPERATOR_FINISHED;
1556 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1558 ot->name = "Jump to Endpoint";
1559 ot->description= "Jump to first/last frame in frame range.";
1560 ot->idname = "SCREEN_OT_frame_jump";
1562 ot->exec= frame_jump_exec;
1564 ot->poll= ED_operator_screenactive;
1568 RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range.");
1572 /* ************** jump to keyframe operator ***************************** */
1574 /* function to be called outside UI context, or for redo */
1575 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1577 Scene *scene= CTX_data_scene(C);
1578 Object *ob= CTX_data_active_object(C);
1581 float cfra= (scene)? (float)(CFRA) : 0.0f;
1582 short next= RNA_boolean_get(op->ptr, "next");
1586 return OPERATOR_CANCELLED;
1588 /* init binarytree-list for getting keyframes */
1589 BLI_dlrbTree_init(&keys);
1591 /* populate tree with keyframe nodes */
1592 if (scene && scene->adt)
1593 scene_to_keylist(NULL, scene, &keys, NULL);
1595 ob_to_keylist(NULL, ob, &keys, NULL);
1597 /* build linked-list for searching */
1598 BLI_dlrbTree_linkedlist_sync(&keys);
1600 /* find matching keyframe in the right direction */
1602 ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1604 ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1606 /* set the new frame (if keyframe found) */
1608 CFRA= (int)ak->cfra;
1610 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
1612 /* free temp stuff */
1613 BLI_dlrbTree_free(&keys);
1615 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1617 return OPERATOR_FINISHED;
1620 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1622 ot->name = "Jump to Keyframe";
1623 ot->description= "Jump to previous/next keyframe.";
1624 ot->idname = "SCREEN_OT_keyframe_jump";
1626 ot->exec= keyframe_jump_exec;
1628 ot->poll= ED_operator_screenactive;
1632 RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1635 /* ************** switch screen operator ***************************** */
1638 /* function to be called outside UI context, or for redo */
1639 static int screen_set_exec(bContext *C, wmOperator *op)
1641 bScreen *screen= CTX_wm_screen(C);
1642 ScrArea *sa= CTX_wm_area(C);
1643 int tot= BLI_countlist(&CTX_data_main(C)->screen);
1644 int delta= RNA_int_get(op->ptr, "delta");
1646 /* return to previous state before switching screens */
1648 ED_screen_full_restore(C, sa);
1652 screen= screen->id.next;
1653 if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1654 if(screen->winid==0 && screen->full==0)
1658 else if(delta== -1) {
1660 screen= screen->id.prev;
1661 if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1662 if(screen->winid==0 && screen->full==0)
1671 ED_screen_set(C, screen);
1672 return OPERATOR_FINISHED;
1674 return OPERATOR_CANCELLED;
1677 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1679 ot->name = "Set Screen";
1680 ot->description= "Cycle through available screens.";
1681 ot->idname = "SCREEN_OT_screen_set";
1683 ot->exec= screen_set_exec;
1684 ot->poll= ED_operator_screenactive;
1687 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1690 /* ************** screen full-area operator ***************************** */
1693 /* function to be called outside UI context, or for redo */
1694 static int screen_full_area_exec(bContext *C, wmOperator *op)
1696 ed_screen_fullarea(C, CTX_wm_window(C), CTX_wm_area(C));
1697 return OPERATOR_FINISHED;
1700 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1702 ot->name = "Toggle Full Screen";
1703 ot->description= "Toggle display selected area as fullscreen.";
1704 ot->idname = "SCREEN_OT_screen_full_area";
1706 ot->exec= screen_full_area_exec;
1707 ot->poll= ED_operator_areaactive;
1714 /* ************** join area operator ********************************************** */
1716 /* operator state vars used:
1717 x1, y1 mouse coord in first area, which will disappear
1718 x2, y2 mouse coord in 2nd area, which will become joined
1722 init() find edge based on state vars
1723 test if the edge divides two areas,
1724 store active and nonactive area,
1726 apply() do the actual join
1728 exit() cleanup, send notifier
1732 exec() calls init, apply, exit
1734 invoke() sets mouse coords in x,y
1738 modal() accept modal events while doing it
1739 call apply() with active window and nonactive window
1740 call exit() and remove handler when LMB confirm
1744 typedef struct sAreaJoinData
1746 ScrArea *sa1; /* first area to be considered */
1747 ScrArea *sa2; /* second area to be considered */
1748 ScrArea *scr; /* designed for removal */
1753 /* validate selection inside screen, set variables OK */
1754 /* return 0: init failed */
1755 /* XXX todo: find edge based on (x,y) and set other area? */
1756 static int area_join_init(bContext *C, wmOperator *op)
1759 sAreaJoinData* jd= NULL;
1763 /* required properties, make negative to get return 0 if not set by caller */
1764 x1= RNA_int_get(op->ptr, "x1");
1765 y1= RNA_int_get(op->ptr, "y1");
1766 x2= RNA_int_get(op->ptr, "x2");
1767 y2= RNA_int_get(op->ptr, "y2");
1769 sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1770 sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1771 if(sa1==NULL || sa2==NULL || sa1==sa2)
1774 jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1777 jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1779 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1786 /* apply the join of the areas (space types) */
1787 static int area_join_apply(bContext *C, wmOperator *op)
1789 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1792 if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1795 if (CTX_wm_area(C) == jd->sa2) {
1796 CTX_wm_area_set(C, NULL);
1797 CTX_wm_region_set(C, NULL);
1803 /* finish operation */
1804 static void area_join_exit(bContext *C, wmOperator *op)
1806 if (op->customdata) {
1807 MEM_freeN(op->customdata);
1808 op->customdata = NULL;
1811 /* this makes sure aligned edges will result in aligned grabbing */
1812 removedouble_scredges(CTX_wm_screen(C));
1813 removenotused_scredges(CTX_wm_screen(C));
1814 removenotused_scrverts(CTX_wm_screen(C));
1817 static int area_join_exec(bContext *C, wmOperator *op)
1819 if(!area_join_init(C, op))
1820 return OPERATOR_CANCELLED;
1822 area_join_apply(C, op);
1823 area_join_exit(C, op);
1825 return OPERATOR_FINISHED;
1828 /* interaction callback */
1829 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1832 if(event->type==EVT_ACTIONZONE_AREA) {
1833 sActionzoneData *sad= event->customdata;
1835 if(sad->modifier>0) {
1836 return OPERATOR_PASS_THROUGH;
1839 /* verify *sad itself */
1840 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1841 return OPERATOR_PASS_THROUGH;
1843 /* is this our *sad? if areas equal it should be passed on */
1844 if(sad->sa1==sad->sa2)
1845 return OPERATOR_PASS_THROUGH;
1847 /* prepare operator state vars */
1848 RNA_int_set(op->ptr, "x1", sad->x);
1849 RNA_int_set(op->ptr, "y1", sad->y);
1850 RNA_int_set(op->ptr, "x2", event->x);
1851 RNA_int_set(op->ptr, "y2", event->y);
1853 if(!area_join_init(C, op))
1854 return OPERATOR_PASS_THROUGH;
1856 /* add temp handler */
1857 WM_event_add_modal_handler(C, op);
1859 return OPERATOR_RUNNING_MODAL;
1862 return OPERATOR_PASS_THROUGH;
1865 static int area_join_cancel(bContext *C, wmOperator *op)
1867 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1870 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1871 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1874 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1875 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1878 WM_event_add_notifier(C, NC_WINDOW, NULL);
1880 area_join_exit(C, op);
1882 return OPERATOR_CANCELLED;
1885 /* modal callback while selecting area (space) that will be removed */
1886 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1888 bScreen *sc= CTX_wm_screen(C);
1889 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1891 /* execute the events */
1892 switch(event->type) {
1896 ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1900 if (jd->sa1 != sa) {
1901 dir = area_getorientation(sc, jd->sa1, sa);
1903 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1905 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1908 /* we are not bordering on the previously selected area
1909 we check if area has common border with the one marked for removal
1910 in this case we can swap areas.
1912 dir = area_getorientation(sc, sa, jd->sa2);
1914 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1915 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1918 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1919 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1922 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1926 WM_event_add_notifier(C, NC_WINDOW, NULL);
1929 /* we are back in the area previously selected for keeping
1930 * we swap the areas if possible to allow user to choose */
1931 if (jd->sa2 != NULL) {
1932 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1933 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1936 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1937 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1938 dir = area_getorientation(sc, jd->sa1, jd->sa2);
1940 printf("oops, didn't expect that!\n");
1944 dir = area_getorientation(sc, jd->sa1, sa);
1946 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1948 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1951 WM_event_add_notifier(C, NC_WINDOW, NULL);
1957 if(event->val==KM_RELEASE) {
1959 ED_area_tag_redraw(jd->sa1);
1960 ED_area_tag_redraw(jd->sa2);
1962 area_join_apply(C, op);
1963 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1964 area_join_exit(C, op);
1965 return OPERATOR_FINISHED;
1971 return area_join_cancel(C, op);
1974 return OPERATOR_RUNNING_MODAL;
1977 /* Operator for joining two areas (space types) */
1978 static void SCREEN_OT_area_join(wmOperatorType *ot)
1981 ot->name= "Join area";
1982 ot->description= "Join selected areas into new window.";
1983 ot->idname= "SCREEN_OT_area_join";
1986 ot->exec= area_join_exec;
1987 ot->invoke= area_join_invoke;
1988 ot->modal= area_join_modal;
1989 ot->poll= ED_operator_areaactive;
1991 ot->flag= OPTYPE_BLOCKING;
1994 RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1995 RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
1996 RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
1997 RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2000 /* ************** repeat last operator ***************************** */
2002 static int repeat_last_exec(bContext *C, wmOperator *op)
2004 wmOperator *lastop= CTX_wm_manager(C)->operators.last;
2007 WM_operator_repeat(C, lastop);
2009 return OPERATOR_CANCELLED;
2012 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2015 ot->name= "Repeat Last";
2016 ot->description= "Repeat last action.";
2017 ot->idname= "SCREEN_OT_repeat_last";
2020 ot->exec= repeat_last_exec;
2022 ot->poll= ED_operator_screenactive;
2026 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
2028 wmWindowManager *wm= CTX_wm_manager(C);
2034 items= BLI_countlist(&wm->operators);
2036 return OPERATOR_CANCELLED;
2038 pup= uiPupMenuBegin(C, op->type->name, 0);
2039 layout= uiPupMenuLayout(pup);
2041 for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
2042 uiItemIntO(layout, lastop->type->name, 0, op->type->idname, "index", i);
2044 uiPupMenuEnd(C, pup);
2046 return OPERATOR_CANCELLED;
2049 static int repeat_history_exec(bContext *C, wmOperator *op)
2051 wmWindowManager *wm= CTX_wm_manager(C);
2053 op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2055 /* let's put it as last operator in list */
2056 BLI_remlink(&wm->operators, op);
2057 BLI_addtail(&wm->operators, op);
2059 WM_operator_repeat(C, op);
2062 return OPERATOR_FINISHED;
2065 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2068 ot->name= "Repeat History";
2069 ot->description= "Display menu for previous actions performed.";
2070 ot->idname= "SCREEN_OT_repeat_history";
2073 ot->invoke= repeat_history_invoke;
2074 ot->exec= repeat_history_exec;
2076 ot->poll= ED_operator_screenactive;
2078 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2081 /* ********************** redo operator ***************************** */
2083 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
2085 wmWindowManager *wm= CTX_wm_manager(C);
2088 /* only for operators that are registered and did an undo push */
2089 for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
2090 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
2094 WM_operator_redo_popup(C, lastop);
2096 return OPERATOR_CANCELLED;
2099 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2102 ot->name= "Redo Last";
2103 ot->description= "Display menu for last action performed.";
2104 ot->idname= "SCREEN_OT_redo_last";
2107 ot->invoke= redo_last_invoke;
2109 ot->poll= ED_operator_screenactive;
2112 /* ************** region four-split operator ***************************** */
2114 /* insert a region in the area region list */
2115 static int region_quadview_exec(bContext *C, wmOperator *op)
2117 ARegion *ar= CTX_wm_region(C);
2120 if(ar->regiontype!=RGN_TYPE_WINDOW)
2121 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2122 else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2123 ScrArea *sa= CTX_wm_area(C);
2126 /* keep current region */
2129 if(sa->spacetype==SPACE_VIEW3D) {
2130 RegionView3D *rv3d= ar->regiondata;
2132 rv3d->rflag &= ~RV3D_CLIPPING;
2135 for(ar= sa->regionbase.first; ar; ar= arn) {
2137 if(ar->alignment==RGN_ALIGN_QSPLIT) {
2138 ED_region_exit(C, ar);
2139 BKE_area_region_free(sa->type, ar);
2140 BLI_remlink(&sa->regionbase, ar);
2145 ED_area_tag_redraw(sa);
2147 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2150 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2152 ScrArea *sa= CTX_wm_area(C);
2156 ar->alignment= RGN_ALIGN_QSPLIT;
2158 for(count=0; count<3; count++) {
2159 newar= BKE_area_region_copy(sa->type, ar);
2160 BLI_addtail(&sa->regionbase, newar);
2163 /* lock views and set them */
2164 if(sa->spacetype==SPACE_VIEW3D) {
2167 rv3d= ar->regiondata;
2168 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2171 rv3d= ar->regiondata;
2172 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2175 rv3d= ar->regiondata;
2176 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2179 rv3d= ar->regiondata;
2180 rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2184 ED_area_tag_redraw(sa);
2186 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2190 return OPERATOR_FINISHED;
2193 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
2196 ot->name= "Toggle Quad View";
2197 ot->description= "Split selected area into camera, front, right & top views.";
2198 ot->idname= "SCREEN_OT_region_quadview";
2201 // ot->invoke= WM_operator_confirm;
2202 ot->exec= region_quadview_exec;
2203 ot->poll= ED_operator_areaactive;
2209 /* ************** region flip operator ***************************** */
2211 /* flip a region alignment */
2212 static int region_flip_exec(bContext *C, wmOperator *op)
2214 ARegion *ar= CTX_wm_region(C);
2217 return OPERATOR_CANCELLED;
2219 if(ar->alignment==RGN_ALIGN_TOP)
2220 ar->alignment= RGN_ALIGN_BOTTOM;
2221 else if(ar->alignment==RGN_ALIGN_BOTTOM)
2222 ar->alignment= RGN_ALIGN_TOP;
2223 else if(ar->alignment==RGN_ALIGN_LEFT)
2224 ar->alignment= RGN_ALIGN_RIGHT;
2225 else if(ar->alignment==RGN_ALIGN_RIGHT)
2226 ar->alignment= RGN_ALIGN_LEFT;
2229 ED_area_tag_redraw(CTX_wm_area(C));
2231 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2233 return OPERATOR_FINISHED;
2237 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2240 ot->name= "Flip Region";
2241 ot->idname= "SCREEN_OT_region_flip";
2244 ot->exec= region_flip_exec;
2246 ot->poll= ED_operator_areaactive;
2250 /* ************** header flip operator ***************************** */
2252 /* flip a header region alignment */
2253 static int header_flip_exec(bContext *C, wmOperator *op)
2255 ARegion *ar= CTX_wm_region(C);
2257 /* find the header region
2258 * - try context first, but upon failing, search all regions in area...
2260 if((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) {
2261 ScrArea *sa= CTX_wm_area(C);
2263 /* loop over all regions until a matching one is found */
2264 for (ar= sa->regionbase.first; ar; ar= ar->next) {
2265 if(ar->regiontype == RGN_TYPE_HEADER)
2269 /* don't do anything if no region */
2271 return OPERATOR_CANCELLED;
2274 /* copied from SCREEN_OT_region_flip */
2275 if(ar->alignment==RGN_ALIGN_TOP)
2276 ar->alignment= RGN_ALIGN_BOTTOM;
2277 else if(ar->alignment==RGN_ALIGN_BOTTOM)
2278 ar->alignment= RGN_ALIGN_TOP;
2279 else if(ar->alignment==RGN_ALIGN_LEFT)
2280 ar->alignment= RGN_ALIGN_RIGHT;
2281 else if(ar->alignment==RGN_ALIGN_RIGHT)
2282 ar->alignment= RGN_ALIGN_LEFT;
2285 ED_area_tag_redraw(CTX_wm_area(C));
2288 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2290 return OPERATOR_FINISHED;
2294 static void SCREEN_OT_header_flip(wmOperatorType *ot)
2297 ot->name= "Flip Header Region";
2298 ot->idname= "SCREEN_OT_header_flip";
2301 ot->exec= header_flip_exec;
2303 ot->poll= ED_operator_areaactive;
2307 /* ************** header tools operator ***************************** */
2309 static int header_toolbox_invoke(bContext *C, wmOperator *op, wmEvent *event)
2311 ScrArea *sa= CTX_wm_area(C);
2312 ARegion *ar= CTX_wm_region(C);
2316 pup= uiPupMenuBegin(C, "Header", 0);
2317 layout= uiPupMenuLayout(pup);
2319 // XXX SCREEN_OT_region_flip doesn't work - gets wrong context for active region, so added custom operator
2320 if (ar->alignment == RGN_ALIGN_TOP)
2321 uiItemO(layout, "Flip to Bottom", 0, "SCREEN_OT_header_flip");
2323 uiItemO(layout, "Flip to Top", 0, "SCREEN_OT_header_flip");
2327 /* file browser should be fullscreen all the time, but other regions can be maximised/restored... */
2328 if (sa->spacetype != SPACE_FILE) {
2330 uiItemO(layout, "Tile Area", 0, "SCREEN_OT_screen_full_area");
2332 uiItemO(layout, "Maximize Area", 0, "SCREEN_OT_screen_full_area");
2335 uiPupMenuEnd(C, pup);
2337 return OPERATOR_CANCELLED;
2340 void SCREEN_OT_header_toolbox(wmOperatorType *ot)
2343 ot->name= "Header Toolbox";
2344 ot->description="Display header region toolbox";
2345 ot->idname= "SCREEN_OT_header_toolbox";
2348 ot->invoke= header_toolbox_invoke;
2351 /* ****************** anim player, with timer ***************** */
2353 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2355 if(regiontype==RGN_TYPE_WINDOW) {
2357 switch (spacetype) {
2359 if(redraws & TIME_ALL_3D_WIN)
2365 if(redraws & TIME_ALL_ANIM_WIN)
2369 /* if only 1 window or 3d windows, we do timeline too */
2370 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2374 if(redraws & TIME_ALL_BUTS_WIN)
2378 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2382 if(redraws & (TIME_NODES))
2386 if(redraws & TIME_ALL_IMAGE_WIN)
2392 else if(regiontype==RGN_TYPE_UI) {
2393 if(redraws & TIME_ALL_BUTS_WIN)
2396 else if(regiontype==RGN_TYPE_HEADER) {
2397 if(spacetype==SPACE_TIME)
2400 else if (regiontype==RGN_TYPE_PREVIEW) {
2401 switch (spacetype) {
2403 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2411 static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event)
2413 bScreen *screen= CTX_wm_screen(C);
2415 if(screen->animtimer==event->customdata) {
2416 Scene *scene= CTX_data_scene(C);
2417 wmTimer *wt= screen->animtimer;
2418 ScreenAnimData *sad= wt->customdata;
2422 /* sync, don't sync, or follow scene setting */
2423 if(sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2424 else if(sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2425 else sync= (scene->audio.flag & AUDIO_SYNC);
2429 int step = floor(wt->duration * FPS);
2430 if(sad->flag & ANIMPLAY_FLAG_REVERSE) // XXX does this option work with audio?
2431 scene->r.cfra -= step;
2433 scene->r.cfra += step;
2434 wt->duration -= ((float)step)/FPS;
2438 if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2444 /* reset 'jumped' flag before checking if we need to jump... */
2445 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2447 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2448 /* jump back to end? */
2450 if (scene->r.cfra < scene->r.psfra) {
2451 scene->r.cfra= scene->r.pefra;
2452 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2456 if (scene->r.cfra < scene->r.sfra) {
2457 scene->r.cfra= scene->r.efra;
2458 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2463 /* jump back to start? */
2465 if (scene->r.cfra > scene->r.pefra) {
2466 scene->r.cfra= scene->r.psfra;
2467 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2471 if (scene->r.cfra > scene->r.efra) {
2472 scene->r.cfra= scene->r.sfra;
2473 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2478 if(sad->flag & ANIMPLAY_FLAG_JUMPED)
2479 sound_seek_scene(C);
2481 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2483 ED_update_for_newframe(C, 1);
2485 for(sa= screen->areabase.first; sa; sa= sa->next) {
2487 for(ar= sa->regionbase.first; ar; ar= ar->next) {
2489 ED_region_tag_redraw(ar);
2491 if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2492 ED_region_tag_redraw(ar);
2496 /* recalculate the timestep for the timer now that we've finished calculating this,
2497 * since the frames-per-second value may have been changed
2499 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2500 wt->timestep= (1.0/FPS);
2502 return OPERATOR_FINISHED;
2504 return OPERATOR_PASS_THROUGH;
2507 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2510 ot->name= "Animation Step";
2511 ot->description= "Step through animation by position.";
2512 ot->idname= "SCREEN_OT_animation_step";
2515 ot->invoke= screen_animation_step;
2517 ot->poll= ED_operator_screenactive;
2521 /* ****************** anim player, starts or ends timer ***************** */
2523 /* toggle operator */
2524 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
2526 bScreen *screen= CTX_wm_screen(C);
2527 struct Scene* scene = CTX_data_scene(C);
2529 if(screen->animtimer) {
2530 /* stop playback now */
2531 ED_screen_animation_timer(C, 0, 0, 0);
2532 sound_stop_scene(scene);
2535 ScrArea *sa= CTX_wm_area(C);
2536 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2538 if(mode == 1) // XXX only play audio forwards!?
2539 sound_play_scene(scene);
2541 if(RNA_property_is_set(op->ptr, "sync"))
2542 sync= (RNA_boolean_get(op->ptr, "sync"));
2544 /* timeline gets special treatment since it has it's own menu for determining redraws */
2545 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2546 SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2548 ED_screen_animation_timer(C, stime->redraws, sync, mode);
2550 /* update region if TIME_REGION was set, to leftmost 3d window */
2551 ED_screen_animation_timer_update(screen, stime->redraws);
2554 int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2556 /* XXX - would like a better way to deal with this situation - Campbell */
2557 if((sa) && (sa->spacetype == SPACE_SEQ)) {
2558 redraws |= TIME_SEQ;
2561 ED_screen_animation_timer(C, redraws, sync, mode);
2563 if(screen->animtimer) {
2564 wmTimer *wt= screen->animtimer;
2565 ScreenAnimData *sad= wt->customdata;
2567 sad->ar= CTX_wm_region(C);
2572 return OPERATOR_FINISHED;
2575 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2578 ot->name= "Play Animation";
2579 ot->description= "Play animation.";
2580 ot->idname= "SCREEN_OT_animation_play";
2583 ot->invoke= screen_animation_play;
2585 ot->poll= ED_operator_screenactive;
2587 RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2588 RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate and stay in sync with audio.");
2591 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2593 bScreen *screen= CTX_wm_screen(C);
2595 if(screen->animtimer) {
2596 ScreenAnimData *sad= screen->animtimer->customdata;
2597 Scene *scene= CTX_data_scene(C);
2599 /* reset current frame before stopping, and just send a notifier to deal with the rest
2600 * (since playback still needs to be stopped)
2602 scene->r.cfra= sad->sfra;
2603 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2605 /* call the other "toggling" operator to clean up now */
2606 return screen_animation_play(C, op, event);
2609 return OPERATOR_PASS_THROUGH;
2612 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2615 ot->name= "Cancel Animation";
2616 ot->description= "Cancel animation, returning to the original frame.";
2617 ot->idname= "SCREEN_OT_animation_cancel";
2620 ot->invoke= screen_animation_cancel;
2622 ot->poll= ED_operator_screenactive;
2625 /* ************** border select operator (template) ***************************** */
2627 /* operator state vars used: (added by default WM callbacks)
2631 customdata: the wmGesture pointer
2635 exec() has to be filled in by user
2637 invoke() default WM function
2640 modal() default WM function
2641 accept modal events while doing it, calls exec(), handles ESC and border drawing
2643 poll() has to be filled in by user for context
2646 static int border_select_do(bContext *C, wmOperator *op)
2648 int event_type= RNA_int_get(op->ptr, "event_type");
2650 if(event_type==LEFTMOUSE)
2651 printf("border select do select\n");
2652 else if(event_type==RIGHTMOUSE)
2653 printf("border select deselect\n");
2655 printf("border select do something\n");
2660 static void SCREEN_OT_border_select(wmOperatorType *ot)
2663 ot->name= "Border select";
2664 ot->idname= "SCREEN_OT_border_select";
2667 ot->exec= border_select_do;
2668 ot->invoke= WM_border_select_invoke;
2669 ot->modal= WM_border_select_modal;
2671 ot->poll= ED_operator_areaactive;
2674 RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2675 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2676 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2677 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2678 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2683 /* ****************************** render invoking ***************** */
2685 /* set callbacks, exported to sequence render too.
2686 Only call in foreground (UI) renders. */
2688 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2689 /* window as the last possible alternative. */
2690 static ScrArea *biggest_non_image_area(bContext *C)
2692 bScreen *sc= CTX_wm_screen(C);
2693 ScrArea *sa, *big= NULL;
2694 int size, maxsize= 0, bwmaxsize= 0;
2697 for(sa= sc->areabase.first; sa; sa= sa->next) {
2698 if(sa->winx > 30 && sa->winy > 30) {
2699 size= sa->winx*sa->winy;
2700 if(sa->spacetype == SPACE_BUTS) {
2701 if(foundwin == 0 && size > bwmaxsize) {
2706 else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2717 static ScrArea *biggest_area(bContext *C)
2719 bScreen *sc= CTX_wm_screen(C);
2720 ScrArea *sa, *big= NULL;
2721 int size, maxsize= 0;
2723 for(sa= sc->areabase.first; sa; sa= sa->next) {
2724 size= sa->winx*sa->winy;
2725 if(size > maxsize) {
2734 static ScrArea *find_area_showing_r_result(bContext *C)
2736 wmWindowManager *wm= CTX_wm_manager(C);
2741 /* find an imagewindow showing render result */
2742 for(win=wm->windows.first; win; win=win->next) {
2743 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2744 if(sa->spacetype==SPACE_IMAGE) {
2745 sima= sa->spacedata.first;
2746 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2755 static ScrArea *find_area_image_empty(bContext *C)
2757 bScreen *sc= CTX_wm_screen(C);
2761 /* find an imagewindow showing render result */
2762 for(sa=sc->areabase.first; sa; sa= sa->next) {
2763 if(sa->spacetype==SPACE_IMAGE) {
2764 sima= sa->spacedata.first;
2772 #if 0 // XXX not used
2773 static ScrArea *find_empty_image_area(bContext *C)
2775 bScreen *sc= CTX_wm_screen(C);
2779 /* find an imagewindow showing render result */
2780 for(sa=sc->areabase.first; sa; sa= sa->next) {
2781 if(sa->spacetype==SPACE_IMAGE) {
2782 sima= sa->spacedata.first;
2789 #endif // XXX not used
2791 /* new window uses x,y to set position */
2792 static void screen_set_image_output(bContext *C, int mx, int my)
2794 wmWindow *win= CTX_wm_window(C);
2795 Scene *scene= CTX_data_scene(C);
2798 int area_was_image=0;
2800 if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2804 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2805 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2807 /* arbitrary... miniature image window views don't make much sense */
2808 if(sizex < 320) sizex= 320;
2809 if(sizey < 256) sizey= 256;
2811 /* XXX some magic to calculate postition */
2812 rect.xmin= mx + win->posx - sizex/2;
2813 rect.ymin= my + win->posy - sizey/2;
2814 rect.xmax= rect.xmin + sizex;
2815 rect.ymax= rect.ymin + sizey;
2817 /* changes context! */
2818 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2822 else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2823 if (CTX_wm_area(C)->spacetype == SPACE_IMAGE)
2826 /* this function returns with changed context */
2827 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2832 sa= find_area_showing_r_result(C);
2834 sa= find_area_image_empty(C);
2837 /* find largest open non-image area */
2838 sa= biggest_non_image_area(C);
2840 ED_area_newspace(C, sa, SPACE_IMAGE);
2841 sima= sa->spacedata.first;
2843 /* makes ESC go back to prev space */
2844 sima->flag |= SI_PREVSPACE;
2847 /* use any area of decent size */
2848 sa= biggest_area(C);
2849 if(sa->spacetype!=SPACE_IMAGE) {
2850 // XXX newspace(sa, SPACE_IMAGE);
2851 sima= sa->spacedata.first;
2853 /* makes ESC go back to prev space */
2854 sima->flag |= SI_PREVSPACE;
2859 sima= sa->spacedata.first;
2861 /* get the correct image, and scale it */
2862 sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2865 /* if we're rendering to full screen, set appropriate hints on image editor
2866 * so it can restore properly on pressing esc */
2868 sima->flag |= SI_FULLWINDOW;
2870 /* Tell the image editor to revert to previous space in space list on close
2871 * _only_ if it wasn't already an image editor when the render was invoked */
2872 if (area_was_image == 0)
2873 sima->flag |= SI_PREVSPACE;
2875 /* Leave it alone so the image editor will just go back from
2876 * full screen to the original tiled setup */
2884 /* executes blocking render */
2885 static int screen_render_exec(bContext *C, wmOperator *op)
2887 Scene *scene= CTX_data_scene(C);
2888 Render *re= RE_GetRender(scene->id.name);
2891 re= RE_NewRender(scene->id.name);
2893 RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2895 /* inform Freestyle of the context */
2898 if(RNA_boolean_get(op->ptr, "animation"))
2899 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step, op->reports);
2901 RE_BlenderFrame(re, scene, NULL, scene->r.cfra);
2903 // no redraw needed, we leave state as we entered it
2904 ED_update_for_newframe(C, 1);
2906 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2908 return OPERATOR_FINISHED;
2911 typedef struct RenderJob {
2915 SceneRenderLayer *srl;
2921 ReportList *reports;
2924 static void render_freejob(void *rjv)
2931 /* str is IMA_RW_MAXTEXT in size */
2932 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2934 char info_time_str[32]; // used to be extern to header_info.c
2935 uintptr_t mem_in_use, mmap_in_use;
2936 float megs_used_memory, mmap_used_memory;
2939 mem_in_use= MEM_get_memory_in_use();
2940 mmap_in_use= MEM_get_mapped_memory_in_use();
2942 megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2943 mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2945 if(scene->lay & 0xFF000000)
2946 spos+= sprintf(spos, "Localview | ");
2947 else if(scene->r.scemode & R_SINGLE_LAYER)
2948 spos+= sprintf(spos, "Single Layer | ");
2951 spos+= sprintf(spos, "%s ", rs->statstr);
2954 spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2955 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2956 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2957 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2960 spos+= sprintf(spos, "Field %d ", rs->curfield);
2962 spos+= sprintf(spos, "Blur %d ", rs->curblur);
2965 BLI_timestr(rs->lastframetime, info_time_str);
2966 spos+= sprintf(spos, "Time:%s ", info_time_str);
2968 if(rs->infostr && rs->infostr[0])
2969 spos+= sprintf(spos, "| %s ", rs->infostr);
2971 /* very weak... but 512 characters is quite safe */
2972 if(spos >= str+IMA_RW_MAXTEXT)
2974 printf("WARNING! renderwin text beyond limit \n");
2978 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2982 /* malloc OK here, stats_draw is not in tile threads */
2983 if(rj->image->render_text==NULL)
2984 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
2986 make_renderinfo_string(rs, rj->scene, rj->image->render_text);
2988 /* make jobs timer to send notifier */
2989 *(rj->do_update)= 1;
2993 /* called inside thread! */
2994 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
2996 float x1, y1, *rectf= NULL;
2997 int ymin, ymax, xmin, xmax;
3001 /* if renrect argument, we only refresh scanlines */
3003 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
3004 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
3007 /* xmin here is first subrect x coord, xmax defines subrect width */
3008 xmin = renrect->xmin + rr->crop;
3009 xmax = renrect->xmax - xmin - rr->crop;
3012 ymin= renrect->ymin + rr->crop;
3013 ymax= renrect->ymax - ymin - rr->crop;
3016 renrect->ymin= renrect->ymax;
3020 xmin = ymin = rr->crop;
3021 xmax = rr->rectx - 2*rr->crop;
3022 ymax = rr->recty - 2*rr->crop;
3025 /* xmin ymin is in tile coords. transform to ibuf */
3026 rxmin= rr->tilerect.xmin + xmin;
3027 if(rxmin >= ibuf->x) return;
3028 rymin= rr->tilerect.ymin + ymin;
3029 if(rymin >= ibuf->y) return;
3031 if(rxmin + xmax > ibuf->x)
3032 xmax= ibuf->x - rxmin;
3033 if(rymin + ymax > ibuf->y)
3034 ymax= ibuf->y - rymin;
3036 if(xmax < 1 || ymax < 1) return;
3038 /* find current float rect for display, first case is after composit... still weak */
3045 if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
3046 rectf= rr->renlay->rectf;
3049 if(rectf==NULL) return;
3051 if(ibuf->rect==NULL)
3052 imb_addrectImBuf(ibuf);
3054 rectf+= 4*(rr->rectx*ymin + xmin);
3055 rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
3057 /* XXX make nice consistent functions for this */
3058 if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
3059 for(y1= 0; y1<ymax; y1++) {
3064 /* XXX temp. because crop offset */
3065 if( rectc >= (char *)(ibuf->rect)) {
3066 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3067 srgb[0]= linearrgb_to_srgb(rf[0]);
3068 srgb[1]= linearrgb_to_srgb(rf[1]);
3069 srgb[2]= linearrgb_to_srgb(rf[2]);
3071 rc[0]= FTOCHAR(srgb[0]);
3072 rc[1]= FTOCHAR(srgb[1]);
3073 rc[2]= FTOCHAR(srgb[2]);
3074 rc[3]= FTOCHAR(rf[3]);
3077 rectf += 4*rr->rectx;
3081 for(y1= 0; y1<ymax; y1++) {
3085 /* XXX temp. because crop offset */
3086 if( rectc >= (char *)(ibuf->rect)) {
3087 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
3088 rc[0]= FTOCHAR(rf[0]);
3089 rc[1]= FTOCHAR(rf[1]);
3090 rc[2]= FTOCHAR(rf[2]);
3091 rc[3]= FTOCHAR(rf[3]);
3094 rectf += 4*rr->rectx;
3101 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
3107 ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
3109 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
3111 /* make jobs timer to send notifier */
3112 *(rj->do_update)= 1;
3114 BKE_image_release_ibuf(rj->image, lock);
3117 static void render_startjob(void *rjv, short *stop, short *do_update)
3122 rj->do_update= do_update;
3124 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2)
3125 // Workaround for Apple gcc 4.2.1 omp vs background thread bug
3126 pthread_setspecific (gomp_tls_key, thread_tls_data);
3130 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step, rj->reports);
3132 RE_BlenderFrame(rj->re, rj->scene, rj->srl, rj->scene->r.cfra);
3135 /* called by render, check job 'stop' value or the global */
3136 static int render_breakjob(void *rjv)
3142 if(rj->stop && *(rj->stop))
3148 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3150 /* no running blender, remove handler and pass through */
3151 if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
3152 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
3154 /* running render */
3155 switch (event->type) {
3157 return OPERATOR_RUNNING_MODAL;
3160 return OPERATOR_PASS_THROUGH;
3163 /* using context, starts job */
3164 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3166 /* new render clears all callbacks */
3167 Scene *scene= CTX_data_scene(C);
3168 SceneRenderLayer *srl=NULL;
3174 /* only one render job at a time */
3175 if(WM_jobs_test(CTX_wm_manager(C), scene))
3176 return OPERATOR_CANCELLED;
3178 /* stop all running jobs, currently previews frustrate Render */
3179 WM_jobs_stop_all(CTX_wm_manager(C));
3181 /* handle UI stuff */
3184 /* inform Freestyle of the context */
3187 /* flush multires changes (for sculpt) */
3188 multires_force_render_update(CTX_data_active_object(C));
3190 /* get editmode results */
3191 ED_object_exit_editmode(C, EM_FREEDATA|EM_DO_UNDO); /* 0 = does not exit editmode */
3194 // get view3d layer, local layer, make this nice api call to render
3197 /* ensure at least 1 area shows result */
3198 screen_set_image_output(C, event->x, event->y);
3200 /* single layer re-render */
3201 if(RNA_property_is_set(op->ptr, "layer")) {
3202 SceneRenderLayer *rl;
3204 char scene_name[19], rl_name[RE_MAXNAME];
3206 RNA_string_get(op->ptr, "layer", rl_name);
3207 RNA_string_get(op->ptr, "scene", scene_name);
3209 scn = (Scene *)BLI_findstring(&CTX_data_main(C)->scene, scene_name, offsetof(ID, name) + 2);
3210 rl = (SceneRenderLayer *)BLI_findstring(&scene->r.layers, rl_name, offsetof(SceneRenderLayer,