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 *****
32 #include "MEM_guardedalloc.h"
35 #include "BLI_blenlib.h"
36 #include "BLI_editVert.h"
37 #include "BLI_dlrbTree.h"
39 #include "DNA_armature_types.h"
40 #include "DNA_image_types.h"
41 #include "DNA_lattice_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_mesh_types.h"
44 #include "DNA_curve_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_meta_types.h"
47 #include "DNA_view3d_types.h"
49 #include "BKE_blender.h"
50 #include "BKE_colortools.h"
51 #include "BKE_context.h"
52 #include "BKE_customdata.h"
53 #include "BKE_global.h"
54 #include "BKE_image.h"
55 #include "BKE_idprop.h"
56 #include "BKE_library.h"
59 #include "BKE_multires.h"
60 #include "BKE_report.h"
61 #include "BKE_scene.h"
62 #include "BKE_screen.h"
63 #include "BKE_utildefines.h"
64 #include "BKE_sound.h"
65 #include "BKE_writeavi.h"
71 #include "ED_screen.h"
73 #include "ED_object.h"
74 #include "ED_screen_types.h"
75 #include "ED_keyframes_draw.h"
76 #include "ED_view3d.h"
78 #include "RE_pipeline.h"
79 #include "IMB_imbuf.h"
80 #include "IMB_imbuf_types.h"
82 #include "RNA_access.h"
83 #include "RNA_define.h"
85 #include "UI_interface.h"
86 #include "UI_resources.h"
88 #include "GPU_extensions.h"
90 #include "wm_window.h"
92 #include "screen_intern.h" /* own module include */
94 #define KM_MODAL_CANCEL 1
95 #define KM_MODAL_APPLY 2
96 #define KM_MODAL_STEP10 3
97 #define KM_MODAL_STEP10_OFF 4
99 /* ************** Exported Poll tests ********************** */
101 int ED_operator_regionactive(bContext *C)
103 if(CTX_wm_window(C)==NULL) return 0;
104 if(CTX_wm_screen(C)==NULL) return 0;
105 if(CTX_wm_region(C)==NULL) return 0;
109 int ED_operator_areaactive(bContext *C)
111 if(CTX_wm_window(C)==NULL) return 0;
112 if(CTX_wm_screen(C)==NULL) return 0;
113 if(CTX_wm_area(C)==NULL) return 0;
117 int ED_operator_screenactive(bContext *C)
119 if(CTX_wm_window(C)==NULL) return 0;
120 if(CTX_wm_screen(C)==NULL) return 0;
124 /* when mouse is over area-edge */
125 int ED_operator_screen_mainwinactive(bContext *C)
127 if(CTX_wm_window(C)==NULL) return 0;
128 if(CTX_wm_screen(C)==NULL) return 0;
129 if (CTX_wm_screen(C)->subwinactive!=CTX_wm_screen(C)->mainwin) return 0;
133 int ED_operator_scene_editable(bContext *C)
135 Scene *scene= CTX_data_scene(C);
136 if(scene && scene->id.lib==NULL)
141 static int ed_spacetype_test(bContext *C, int type)
143 if(ED_operator_areaactive(C)) {
144 SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
145 return sl && (sl->spacetype == type);
150 int ED_operator_view3d_active(bContext *C)
152 return ed_spacetype_test(C, SPACE_VIEW3D);
155 int ED_operator_timeline_active(bContext *C)
157 return ed_spacetype_test(C, SPACE_TIME);
160 int ED_operator_outliner_active(bContext *C)
162 return ed_spacetype_test(C, SPACE_OUTLINER);
165 int ED_operator_file_active(bContext *C)
167 return ed_spacetype_test(C, SPACE_FILE);
170 int ED_operator_action_active(bContext *C)
172 return ed_spacetype_test(C, SPACE_ACTION);
175 int ED_operator_buttons_active(bContext *C)
177 return ed_spacetype_test(C, SPACE_BUTS);
180 int ED_operator_node_active(bContext *C)
182 SpaceNode *snode= CTX_wm_space_node(C);
184 if(snode && snode->edittree)
191 int ED_operator_ipo_active(bContext *C)
193 return ed_spacetype_test(C, SPACE_IPO);
196 int ED_operator_sequencer_active(bContext *C)
198 return ed_spacetype_test(C, SPACE_SEQ);
201 int ED_operator_image_active(bContext *C)
203 return ed_spacetype_test(C, SPACE_IMAGE);
206 int ED_operator_nla_active(bContext *C)
208 return ed_spacetype_test(C, SPACE_NLA);
211 int ED_operator_logic_active(bContext *C)
213 return ed_spacetype_test(C, SPACE_LOGIC);
216 int ED_operator_object_active(bContext *C)
218 return NULL != CTX_data_active_object(C);
221 int ED_operator_editmesh(bContext *C)
223 Object *obedit= CTX_data_edit_object(C);
224 if(obedit && obedit->type==OB_MESH)
225 return NULL != ((Mesh *)obedit->data)->edit_mesh;
229 int ED_operator_editarmature(bContext *C)
231 Object *obedit= CTX_data_edit_object(C);
232 if(obedit && obedit->type==OB_ARMATURE)
233 return NULL != ((bArmature *)obedit->data)->edbo;
237 int ED_operator_posemode(bContext *C)
239 Object *obact= CTX_data_active_object(C);
240 Object *obedit= CTX_data_edit_object(C);
242 if ((obact != obedit) && (obact) && (obact->type==OB_ARMATURE))
243 return (obact->mode & OB_MODE_POSE)!=0;
249 int ED_operator_uvedit(bContext *C)
251 Object *obedit= CTX_data_edit_object(C);
254 if(obedit && obedit->type==OB_MESH)
255 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
257 if(em && (em->faces.first) && (CustomData_has_layer(&em->fdata, CD_MTFACE))) {
258 BKE_mesh_end_editmesh(obedit->data, em);
263 BKE_mesh_end_editmesh(obedit->data, em);
267 int ED_operator_uvmap(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)) {
276 BKE_mesh_end_editmesh(obedit->data, em);
281 BKE_mesh_end_editmesh(obedit->data, em);
285 int ED_operator_editsurfcurve(bContext *C)
287 Object *obedit= CTX_data_edit_object(C);
288 if(obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
289 return NULL != ((Curve *)obedit->data)->editnurb;
294 int ED_operator_editcurve(bContext *C)
296 Object *obedit= CTX_data_edit_object(C);
297 if(obedit && obedit->type==OB_CURVE)
298 return NULL != ((Curve *)obedit->data)->editnurb;
302 int ED_operator_editsurf(bContext *C)
304 Object *obedit= CTX_data_edit_object(C);
305 if(obedit && obedit->type==OB_SURF)
306 return NULL != ((Curve *)obedit->data)->editnurb;
310 int ED_operator_editfont(bContext *C)
312 Object *obedit= CTX_data_edit_object(C);
313 if(obedit && obedit->type==OB_FONT)
314 return NULL != ((Curve *)obedit->data)->editfont;
318 int ED_operator_editlattice(bContext *C)
320 Object *obedit= CTX_data_edit_object(C);
321 if(obedit && obedit->type==OB_LATTICE)
322 return NULL != ((Lattice *)obedit->data)->editlatt;
326 int ED_operator_editmball(bContext *C)
328 Object *obedit= CTX_data_edit_object(C);
329 if(obedit && obedit->type==OB_MBALL)
330 return NULL != ((MetaBall *)obedit->data)->editelems;
334 /* *************************** action zone operator ************************** */
336 /* operator state vars used:
341 apply() set actionzone event
343 exit() free customdata
349 invoke() check if in zone
350 add customdata, put mouseco and area in it
353 modal() accept modal events while doing it
354 call apply() with gesture info, active window, nonactive window
355 call exit() and remove handler when LMB confirm
359 typedef struct sActionzoneData {
362 int x, y, gesture_dir, modifier;
365 /* used by other operators too */
366 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
369 sa= scr->areabase.first;
371 if(BLI_in_rcti(&sa->totrct, x, y)) break;
378 /* quick poll to save operators to be created and handled */
379 static int actionzone_area_poll(bContext *C)
381 wmWindow *win= CTX_wm_window(C);
382 ScrArea *sa= CTX_wm_area(C);
386 int x= win->eventstate->x;
387 int y= win->eventstate->y;
389 for(az= sa->actionzones.first; az; az= az->next)
390 if(BLI_in_rcti(&az->rect, x, y))
396 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
400 for(az= sa->actionzones.first; az; az= az->next) {
401 if(BLI_in_rcti(&az->rect, x, y)) {
402 if(az->type == AZONE_AREA) {
403 if(isect_point_tri_v2_int(az->x1, az->y1, az->x2, az->y2, x, y))
406 else if(az->type == AZONE_REGION) {
416 static void actionzone_exit(bContext *C, wmOperator *op)
419 MEM_freeN(op->customdata);
420 op->customdata= NULL;
423 /* send EVT_ACTIONZONE event */
424 static void actionzone_apply(bContext *C, wmOperator *op, int type)
427 wmWindow *win= CTX_wm_window(C);
428 sActionzoneData *sad= op->customdata;
430 sad->modifier= RNA_int_get(op->ptr, "modifier");
432 event= *(win->eventstate); /* XXX huh huh? make api call */
434 event.type= EVT_ACTIONZONE_AREA;
436 event.type= EVT_ACTIONZONE_REGION;
437 event.customdata= op->customdata;
438 event.customdatafree= TRUE;
439 op->customdata= NULL;
441 wm_event_add(win, &event);
444 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
446 AZone *az= is_in_area_actionzone(CTX_wm_area(C), event->x, event->y);
447 sActionzoneData *sad;
451 return OPERATOR_PASS_THROUGH;
453 /* ok we do the actionzone */
454 sad= op->customdata= MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
455 sad->sa1= CTX_wm_area(C);
457 sad->x= event->x; sad->y= event->y;
459 /* region azone directly reacts on mouse clicks */
460 if(sad->az->type==AZONE_REGION) {
461 actionzone_apply(C, op, AZONE_REGION);
462 actionzone_exit(C, op);
463 return OPERATOR_FINISHED;
466 /* add modal handler */
467 WM_event_add_modal_handler(C, op);
469 return OPERATOR_RUNNING_MODAL;
474 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
476 sActionzoneData *sad= op->customdata;
478 int mindelta= sad->az->type==AZONE_REGION?1:12;
480 switch(event->type) {
482 /* calculate gesture direction */
483 deltax= (event->x - sad->x);
484 deltay= (event->y - sad->y);
486 if(deltay > ABS(deltax))
487 sad->gesture_dir= 'n';
488 else if(deltax > ABS(deltay))
489 sad->gesture_dir= 'e';
490 else if(deltay < -ABS(deltax))
491 sad->gesture_dir= 's';
493 sad->gesture_dir= 'w';
495 /* gesture is large enough? */
496 if(ABS(deltax) > mindelta || ABS(deltay) > mindelta) {
498 /* second area, for join */
499 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
500 /* apply sends event */
501 actionzone_apply(C, op, sad->az->type);
502 actionzone_exit(C, op);
504 return OPERATOR_FINISHED;
508 actionzone_exit(C, op);
509 return OPERATOR_CANCELLED;
511 actionzone_exit(C, op);
512 return OPERATOR_CANCELLED;
516 return OPERATOR_RUNNING_MODAL;
519 static void SCREEN_OT_actionzone(wmOperatorType *ot)
522 ot->name= "Handle area action zones";
523 ot->description= "Handle area action zones for mouse actions/gestures.";
524 ot->idname= "SCREEN_OT_actionzone";
526 ot->invoke= actionzone_invoke;
527 ot->modal= actionzone_modal;
528 ot->poll= actionzone_area_poll;
530 ot->flag= OPTYPE_BLOCKING;
532 RNA_def_int(ot->srna, "modifier", 0, 0, 2, "modifier", "modifier state", 0, 2);
535 /* ************** swap area operator *********************************** */
537 /* operator state vars used:
539 sa2 area to swap with
543 init() set custom data for operator, based on actionzone event custom data
545 cancel() cancel the operator
547 exit() cleanup, send notifier
551 invoke() gets called on shift+lmb drag in actionzone
552 call init(), add handler
554 modal() accept modal events while doing it
558 typedef struct sAreaSwapData {
562 static int area_swap_init(bContext *C, wmOperator *op, wmEvent *event)
564 sAreaSwapData *sd= NULL;
565 sActionzoneData *sad= event->customdata;
567 if(sad==NULL || sad->sa1==NULL)
570 sd= MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
579 static void area_swap_exit(bContext *C, wmOperator *op)
582 MEM_freeN(op->customdata);
583 op->customdata= NULL;
586 static int area_swap_cancel(bContext *C, wmOperator *op)
588 area_swap_exit(C, op);
589 return OPERATOR_CANCELLED;
592 static int area_swap_invoke(bContext *C, wmOperator *op, wmEvent *event)
595 if(!area_swap_init(C, op, event))
596 return OPERATOR_PASS_THROUGH;
598 /* add modal handler */
599 WM_cursor_modal(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
600 WM_event_add_modal_handler(C, op);
602 return OPERATOR_RUNNING_MODAL;
606 static int area_swap_modal(bContext *C, wmOperator *op, wmEvent *event)
608 sActionzoneData *sad= op->customdata;
610 switch(event->type) {
612 /* second area, for join */
613 sad->sa2= screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
615 case LEFTMOUSE: /* release LMB */
616 if(event->val==KM_RELEASE) {
617 if(!sad->sa2 || sad->sa1 == sad->sa2) {
619 return area_swap_cancel(C, op);
621 ED_area_swapspace(C, sad->sa1, sad->sa2);
623 area_swap_exit(C, op);
625 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
627 return OPERATOR_FINISHED;
632 return area_swap_cancel(C, op);
634 return OPERATOR_RUNNING_MODAL;
637 static void SCREEN_OT_area_swap(wmOperatorType *ot)
639 ot->name= "Swap areas";
640 ot->description= "Swap selected areas screen positions.";
641 ot->idname= "SCREEN_OT_area_swap";
643 ot->invoke= area_swap_invoke;
644 ot->modal= area_swap_modal;
645 ot->poll= ED_operator_areaactive;
647 ot->flag= OPTYPE_BLOCKING;
650 /* *********** Duplicate area as new window operator ****************** */
652 /* operator callback */
653 static int area_dupli_invoke(bContext *C, wmOperator *op, wmEvent *event)
655 wmWindow *newwin, *win;
660 win= CTX_wm_window(C);
661 sc= CTX_wm_screen(C);
665 if(event->type==EVT_ACTIONZONE_AREA) {
666 sActionzoneData *sad= event->customdata;
669 return OPERATOR_PASS_THROUGH;
674 /* poll() checks area context, but we don't accept full-area windows */
675 if(sc->full != SCREENNORMAL) {
676 if(event->type==EVT_ACTIONZONE_AREA)
677 actionzone_exit(C, op);
678 return OPERATOR_CANCELLED;
681 /* adds window to WM */
683 BLI_translate_rcti(&rect, win->posx, win->posy);
684 newwin= WM_window_open(C, &rect);
686 /* allocs new screen and adds to newly created window, using window size */
687 newsc= ED_screen_add(newwin, CTX_data_scene(C), sc->id.name+2);
688 newwin->screen= newsc;
690 /* copy area to new screen */
691 area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
693 /* screen, areas init */
694 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
696 if(event->type==EVT_ACTIONZONE_AREA)
697 actionzone_exit(C, op);
699 return OPERATOR_FINISHED;
702 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
704 ot->name= "Duplicate Area into New Window";
705 ot->description= "Duplicate selected area into new window.";
706 ot->idname= "SCREEN_OT_area_dupli";
708 ot->invoke= area_dupli_invoke;
709 ot->poll= ED_operator_areaactive;
713 /* ************** move area edge operator *********************************** */
715 /* operator state vars used:
716 x, y mouse coord near edge
717 delta movement of edge
721 init() set default property values, find edge based on mouse coords, test
722 if the edge can be moved, select edges, calculate min and max movement
724 apply() apply delta on selection
726 exit() cleanup, send notifier
728 cancel() cancel moving
732 exec() execute without any user interaction, based on properties
733 call init(), apply(), exit()
735 invoke() gets called on mouse click near edge
736 call init(), add handler
738 modal() accept modal events while doing it
739 call apply() with delta motion
740 call exit() and remove handler
744 typedef struct sAreaMoveData {
745 int bigger, smaller, origval, step;
749 /* helper call to move area-edge, sets limits */
750 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
754 /* we check all areas and test for free space with MINSIZE */
755 *bigger= *smaller= 100000;
757 for(sa= sc->areabase.first; sa; sa= sa->next) {
759 int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
761 /* if top or down edge selected, test height */
762 if(sa->v1->flag && sa->v4->flag)
763 *bigger= MIN2(*bigger, y1);
764 else if(sa->v2->flag && sa->v3->flag)
765 *smaller= MIN2(*smaller, y1);
768 int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
770 /* if left or right edge selected, test width */
771 if(sa->v1->flag && sa->v2->flag)
772 *bigger= MIN2(*bigger, x1);
773 else if(sa->v3->flag && sa->v4->flag)
774 *smaller= MIN2(*smaller, x1);
779 /* validate selection inside screen, set variables OK */
780 /* return 0: init failed */
781 static int area_move_init (bContext *C, wmOperator *op)
783 bScreen *sc= CTX_wm_screen(C);
788 /* required properties */
789 x= RNA_int_get(op->ptr, "x");
790 y= RNA_int_get(op->ptr, "y");
793 actedge= screen_find_active_scredge(sc, x, y);
794 if(actedge==NULL) return 0;
796 md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
799 md->dir= scredge_is_horizontal(actedge)?'h':'v';
800 if(md->dir=='h') md->origval= actedge->v1->vec.y;
801 else md->origval= actedge->v1->vec.x;
803 select_connected_scredge(sc, actedge);
804 /* now all vertices with 'flag==1' are the ones that can be moved. */
806 area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
811 /* moves selected screen edge amount of delta, used by split & move */
812 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
814 wmWindow *win= CTX_wm_window(C);
815 bScreen *sc= CTX_wm_screen(C);
818 delta= CLAMPIS(delta, -smaller, bigger);
820 for (v1= sc->vertbase.first; v1; v1= v1->next) {
822 /* that way a nice AREAGRID */
823 if((dir=='v') && v1->vec.x>0 && v1->vec.x<win->sizex-1) {
824 v1->vec.x= origval + delta;
825 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
827 if((dir=='h') && v1->vec.y>0 && v1->vec.y<win->sizey-1) {
828 v1->vec.y= origval + delta;
830 v1->vec.y+= AREAGRID-1;
831 v1->vec.y-= (v1->vec.y % AREAGRID);
833 /* prevent too small top header */
834 if(v1->vec.y > win->sizey-AREAMINY)
835 v1->vec.y= win->sizey-AREAMINY;
840 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
843 static void area_move_apply(bContext *C, wmOperator *op)
845 sAreaMoveData *md= op->customdata;
848 delta= RNA_int_get(op->ptr, "delta");
849 area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
852 static void area_move_exit(bContext *C, wmOperator *op)
855 MEM_freeN(op->customdata);
856 op->customdata= NULL;
858 /* this makes sure aligned edges will result in aligned grabbing */
859 removedouble_scrverts(CTX_wm_screen(C));
860 removedouble_scredges(CTX_wm_screen(C));
863 static int area_move_exec(bContext *C, wmOperator *op)
865 if(!area_move_init(C, op))
866 return OPERATOR_CANCELLED;
868 area_move_apply(C, op);
869 area_move_exit(C, op);
871 return OPERATOR_FINISHED;
874 /* interaction callback */
875 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
877 RNA_int_set(op->ptr, "x", event->x);
878 RNA_int_set(op->ptr, "y", event->y);
880 if(!area_move_init(C, op))
881 return OPERATOR_PASS_THROUGH;
883 /* add temp handler */
884 WM_event_add_modal_handler(C, op);
886 return OPERATOR_RUNNING_MODAL;
889 static int area_move_cancel(bContext *C, wmOperator *op)
892 RNA_int_set(op->ptr, "delta", 0);
893 area_move_apply(C, op);
894 area_move_exit(C, op);
896 return OPERATOR_CANCELLED;
899 /* modal callback for while moving edges */
900 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
902 sAreaMoveData *md= op->customdata;
905 /* execute the events */
906 switch(event->type) {
909 x= RNA_int_get(op->ptr, "x");
910 y= RNA_int_get(op->ptr, "y");
912 delta= (md->dir == 'v')? event->x - x: event->y - y;
913 if(md->step) delta= delta - (delta % md->step);
914 RNA_int_set(op->ptr, "delta", delta);
916 area_move_apply(C, op);
921 switch (event->val) {
923 area_move_exit(C, op);
924 return OPERATOR_FINISHED;
926 case KM_MODAL_CANCEL:
927 return area_move_cancel(C, op);
929 case KM_MODAL_STEP10:
932 case KM_MODAL_STEP10_OFF:
938 return OPERATOR_RUNNING_MODAL;
941 static void SCREEN_OT_area_move(wmOperatorType *ot)
944 ot->name= "Move area edges";
945 ot->description= "Move selected area edges.";
946 ot->idname= "SCREEN_OT_area_move";
948 ot->exec= area_move_exec;
949 ot->invoke= area_move_invoke;
950 ot->cancel= area_move_cancel;
951 ot->modal= area_move_modal;
952 ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
954 ot->flag= OPTYPE_BLOCKING;
957 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
958 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
959 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
962 /* ************** split area operator *********************************** */
967 dir direction 'v' or 'h'
970 area pointer to (active) area
971 x, y last used mouse pos
976 init() set default property values, find area based on context
978 apply() split area based on state vars
980 exit() cleanup, send notifier
982 cancel() remove duplicated area
986 exec() execute without any user interaction, based on state vars
987 call init(), apply(), exit()
989 invoke() gets called on mouse click in action-widget
990 call init(), add modal handler
991 call apply() with initial motion
993 modal() accept modal events while doing it
994 call move-areas code with delta motion
995 call exit() or cancel() and remove handler
999 #define SPLIT_STARTED 1
1000 #define SPLIT_PROGRESS 2
1002 typedef struct sAreaSplitData
1004 int x, y; /* last used mouse position */
1006 int origval; /* for move areas */
1007 int bigger, smaller; /* constraints for moving new edge */
1008 int delta; /* delta move edge */
1009 int origmin, origsize; /* to calculate fac, for property storage */
1011 ScrEdge *nedge; /* new edge */
1012 ScrArea *sarea; /* start area */
1013 ScrArea *narea; /* new area */
1016 /* generic init, no UI stuff here */
1017 static int area_split_init(bContext *C, wmOperator *op)
1019 ScrArea *sa= CTX_wm_area(C);
1023 /* required context */
1024 if(sa==NULL) return 0;
1026 /* required properties */
1027 dir= RNA_enum_get(op->ptr, "direction");
1030 if(dir=='v' && sa->winx < 2*AREAMINX) return 0;
1031 if(dir=='h' && sa->winy < 2*AREAMINY) return 0;
1034 sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1038 sd->origsize= dir=='v' ? sa->winx:sa->winy;
1039 sd->origmin = dir=='v' ? sa->totrct.xmin:sa->totrct.ymin;
1044 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1045 /* used with split operator */
1046 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1048 ScrVert *sav1= sa->v1;
1049 ScrVert *sav2= sa->v2;
1050 ScrVert *sav3= sa->v3;
1051 ScrVert *sav4= sa->v4;
1052 ScrVert *sbv1= sb->v1;
1053 ScrVert *sbv2= sb->v2;
1054 ScrVert *sbv3= sb->v3;
1055 ScrVert *sbv4= sb->v4;
1057 if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
1058 return screen_findedge(screen, sav1, sav2);
1060 else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
1061 return screen_findedge(screen, sav2, sav3);
1063 else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
1064 return screen_findedge(screen, sav3, sav4);
1066 else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
1067 return screen_findedge(screen, sav1, sav4);
1074 /* do the split, return success */
1075 static int area_split_apply(bContext *C, wmOperator *op)
1077 bScreen *sc= CTX_wm_screen(C);
1078 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1082 fac= RNA_float_get(op->ptr, "factor");
1083 dir= RNA_enum_get(op->ptr, "direction");
1085 sd->narea= area_split(CTX_wm_window(C), sc, sd->sarea, dir, fac);
1090 sd->nedge= area_findsharededge(sc, sd->sarea, sd->narea);
1092 /* select newly created edge, prepare for moving edge */
1093 for(sv= sc->vertbase.first; sv; sv= sv->next)
1096 sd->nedge->v1->flag= 1;
1097 sd->nedge->v2->flag= 1;
1099 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
1100 else sd->origval= sd->nedge->v1->vec.x;
1102 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1110 static void area_split_exit(bContext *C, wmOperator *op)
1112 if (op->customdata) {
1113 MEM_freeN(op->customdata);
1114 op->customdata = NULL;
1117 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1119 /* this makes sure aligned edges will result in aligned grabbing */
1120 removedouble_scrverts(CTX_wm_screen(C));
1121 removedouble_scredges(CTX_wm_screen(C));
1125 /* UI callback, adds new handler */
1126 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
1130 if(event->type==EVT_ACTIONZONE_AREA) {
1131 sActionzoneData *sad= event->customdata;
1134 if(sad->modifier>0) {
1135 return OPERATOR_PASS_THROUGH;
1138 /* no full window splitting allowed */
1139 if(CTX_wm_area(C)->full)
1140 return OPERATOR_PASS_THROUGH;
1142 /* verify *sad itself */
1143 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
1144 return OPERATOR_PASS_THROUGH;
1146 /* is this our *sad? if areas not equal it should be passed on */
1147 if(CTX_wm_area(C)!=sad->sa1 || sad->sa1!=sad->sa2)
1148 return OPERATOR_PASS_THROUGH;
1150 /* prepare operator state vars */
1151 if(sad->gesture_dir=='n' || sad->gesture_dir=='s') {
1153 RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1157 RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1159 RNA_enum_set(op->ptr, "direction", dir);
1161 /* general init, also non-UI case, adds customdata, sets area and defaults */
1162 if(!area_split_init(C, op))
1163 return OPERATOR_PASS_THROUGH;
1165 sd= (sAreaSplitData *)op->customdata;
1171 if(area_split_apply(C, op)) {
1172 area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
1174 /* add temp handler for edge move or cancel */
1175 WM_event_add_modal_handler(C, op);
1177 return OPERATOR_RUNNING_MODAL;
1182 /* nonmodal for now */
1183 return op->type->exec(C, op);
1186 return OPERATOR_PASS_THROUGH;
1189 /* function to be called outside UI context, or for redo */
1190 static int area_split_exec(bContext *C, wmOperator *op)
1193 if(!area_split_init(C, op))
1194 return OPERATOR_CANCELLED;
1196 area_split_apply(C, op);
1197 area_split_exit(C, op);
1199 return OPERATOR_FINISHED;
1203 static int area_split_cancel(bContext *C, wmOperator *op)
1205 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1207 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1208 if (CTX_wm_area(C) == sd->narea) {
1209 CTX_wm_area_set(C, NULL);
1210 CTX_wm_region_set(C, NULL);
1214 area_split_exit(C, op);
1216 return OPERATOR_CANCELLED;
1219 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
1221 sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1225 /* execute the events */
1226 switch(event->type) {
1228 dir= RNA_enum_get(op->ptr, "direction");
1230 sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
1231 area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1233 fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
1234 RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1236 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1240 if(event->val==KM_RELEASE) { /* mouse up */
1241 area_split_exit(C, op);
1242 return OPERATOR_FINISHED;
1245 case RIGHTMOUSE: /* cancel operation */
1247 return area_split_cancel(C, op);
1250 return OPERATOR_RUNNING_MODAL;
1253 static EnumPropertyItem prop_direction_items[] = {
1254 {'h', "HORIZONTAL", 0, "Horizontal", ""},
1255 {'v', "VERTICAL", 0, "Vertical", ""},
1256 {0, NULL, 0, NULL, NULL}};
1258 static void SCREEN_OT_area_split(wmOperatorType *ot)
1260 ot->name = "Split area";
1261 ot->description= "Split selected area into new windows.";
1262 ot->idname = "SCREEN_OT_area_split";
1264 ot->exec= area_split_exec;
1265 ot->invoke= area_split_invoke;
1266 ot->modal= area_split_modal;
1268 ot->poll= ED_operator_areaactive;
1269 ot->flag= OPTYPE_BLOCKING;
1272 RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1273 RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1278 /* ************** scale region edge operator *********************************** */
1280 typedef struct RegionMoveData {
1284 int bigger, smaller, origval;
1290 static int region_scale_invoke(bContext *C, wmOperator *op, wmEvent *event)
1292 sActionzoneData *sad= event->customdata;
1296 RegionMoveData *rmd= MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1298 op->customdata= rmd;
1303 rmd->edge= az->edge;
1304 rmd->origx= event->x;
1305 rmd->origy= event->y;
1306 if(rmd->edge=='l' || rmd->edge=='r')
1307 rmd->origval= rmd->ar->type->minsizex;
1309 rmd->origval= rmd->ar->type->minsizey;
1311 /* add temp handler */
1312 WM_event_add_modal_handler(C, op);
1314 return OPERATOR_RUNNING_MODAL;
1317 return OPERATOR_FINISHED;
1320 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1322 RegionMoveData *rmd= op->customdata;
1325 /* execute the events */
1326 switch(event->type) {
1329 if(rmd->edge=='l' || rmd->edge=='r') {
1330 delta= event->x - rmd->origx;
1331 if(rmd->edge=='l') delta= -delta;
1332 rmd->ar->type->minsizex= rmd->origval + delta;
1333 CLAMP(rmd->ar->type->minsizex, 0, 1000);
1334 if(rmd->ar->type->minsizex < 24) {
1335 rmd->ar->type->minsizex= rmd->origval;
1336 if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1337 ED_region_toggle_hidden(C, rmd->ar);
1339 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1340 ED_region_toggle_hidden(C, rmd->ar);
1343 delta= event->y - rmd->origy;
1344 if(rmd->edge=='b') delta= -delta;
1345 rmd->ar->type->minsizey= rmd->origval + delta;
1346 CLAMP(rmd->ar->type->minsizey, 0, 1000);
1347 if(rmd->ar->type->minsizey < 24) {
1348 rmd->ar->type->minsizey= rmd->origval;
1349 if(!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1350 ED_region_toggle_hidden(C, rmd->ar);
1352 else if(rmd->ar->flag & RGN_FLAG_HIDDEN)
1353 ED_region_toggle_hidden(C, rmd->ar);
1356 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1361 if(event->val==KM_RELEASE) {
1363 if(ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1364 if(rmd->ar->flag & RGN_FLAG_HIDDEN) {
1365 ED_region_toggle_hidden(C, rmd->ar);
1366 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1369 MEM_freeN(op->customdata);
1370 op->customdata = NULL;
1372 return OPERATOR_FINISHED;
1380 return OPERATOR_RUNNING_MODAL;
1384 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1387 ot->name= "Scale Region Size";
1388 ot->description= "Scale selected area.";
1389 ot->idname= "SCREEN_OT_region_scale";
1391 ot->invoke= region_scale_invoke;
1392 ot->modal= region_scale_modal;
1394 ot->poll= ED_operator_areaactive;
1396 ot->flag= OPTYPE_BLOCKING;
1400 /* ************** frame change operator ***************************** */
1402 /* function to be called outside UI context, or for redo */
1403 static int frame_offset_exec(bContext *C, wmOperator *op)
1407 delta = RNA_int_get(op->ptr, "delta");
1409 CTX_data_scene(C)->r.cfra += delta;
1411 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1413 return OPERATOR_FINISHED;
1416 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
1418 ot->name = "Frame Offset";
1419 ot->idname = "SCREEN_OT_frame_offset";
1421 ot->exec= frame_offset_exec;
1423 ot->poll= ED_operator_screenactive;
1427 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1431 /* function to be called outside UI context, or for redo */
1432 static int frame_jump_exec(bContext *C, wmOperator *op)
1434 Scene *scene= CTX_data_scene(C);
1436 if (RNA_boolean_get(op->ptr, "end"))
1441 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1443 return OPERATOR_FINISHED;
1446 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1448 ot->name = "Jump to Endpoint";
1449 ot->description= "Jump to first/last frame in frame range.";
1450 ot->idname = "SCREEN_OT_frame_jump";
1452 ot->exec= frame_jump_exec;
1454 ot->poll= ED_operator_screenactive;
1458 RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range.");
1462 /* ************** jump to keyframe operator ***************************** */
1464 /* function to be called outside UI context, or for redo */
1465 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1467 Scene *scene= CTX_data_scene(C);
1468 Object *ob= CTX_data_active_object(C);
1471 float cfra= (scene)? (float)(CFRA) : 0.0f;
1472 short next= RNA_boolean_get(op->ptr, "next");
1476 return OPERATOR_CANCELLED;
1478 /* init binarytree-list for getting keyframes */
1479 BLI_dlrbTree_init(&keys);
1481 /* populate tree with keyframe nodes */
1482 if (scene && scene->adt)
1483 scene_to_keylist(NULL, scene, &keys, NULL);
1485 ob_to_keylist(NULL, ob, &keys, NULL);
1487 /* build linked-list for searching */
1488 BLI_dlrbTree_linkedlist_sync(&keys);
1490 /* find matching keyframe in the right direction */
1492 ak= (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1494 ak= (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1496 /* set the new frame (if keyframe found) */
1498 CFRA= (int)ak->cfra;
1500 BKE_report(op->reports, RPT_ERROR, "No more keyframes to jump to in this direction");
1502 /* free temp stuff */
1503 BLI_dlrbTree_free(&keys);
1505 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, CTX_data_scene(C));
1507 return OPERATOR_FINISHED;
1510 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1512 ot->name = "Jump to Keyframe";
1513 ot->description= "Jump to previous/next keyframe.";
1514 ot->idname = "SCREEN_OT_keyframe_jump";
1516 ot->exec= keyframe_jump_exec;
1518 ot->poll= ED_operator_screenactive;
1522 RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1525 /* ************** switch screen operator ***************************** */
1528 /* function to be called outside UI context, or for redo */
1529 static int screen_set_exec(bContext *C, wmOperator *op)
1531 bScreen *screen= CTX_wm_screen(C);
1532 ScrArea *sa= CTX_wm_area(C);
1533 int tot= BLI_countlist(&CTX_data_main(C)->screen);
1534 int delta= RNA_int_get(op->ptr, "delta");
1536 /* this screen is 'fake', solve later XXX */
1538 return OPERATOR_CANCELLED;
1542 screen= screen->id.next;
1543 if(screen==NULL) screen= CTX_data_main(C)->screen.first;
1544 if(screen->winid==0 && screen->full==0)
1548 else if(delta== -1) {
1550 screen= screen->id.prev;
1551 if(screen==NULL) screen= CTX_data_main(C)->screen.last;
1552 if(screen->winid==0 && screen->full==0)
1561 ED_screen_set(C, screen);
1562 return OPERATOR_FINISHED;
1564 return OPERATOR_CANCELLED;
1567 static void SCREEN_OT_screen_set(wmOperatorType *ot)
1569 ot->name = "Set Screen";
1570 ot->description= "Cycle through available screens.";
1571 ot->idname = "SCREEN_OT_screen_set";
1573 ot->exec= screen_set_exec;
1574 ot->poll= ED_operator_screenactive;
1577 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1580 /* ************** screen full-area operator ***************************** */
1583 /* function to be called outside UI context, or for redo */
1584 static int screen_full_area_exec(bContext *C, wmOperator *op)
1586 ed_screen_fullarea(C, CTX_wm_window(C), CTX_wm_area(C));
1587 return OPERATOR_FINISHED;
1590 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
1592 ot->name = "Toggle Full Screen";
1593 ot->description= "Toggle display selected area as fullscreen.";
1594 ot->idname = "SCREEN_OT_screen_full_area";
1596 ot->exec= screen_full_area_exec;
1597 ot->poll= ED_operator_areaactive;
1604 /* ************** join area operator ********************************************** */
1606 /* operator state vars used:
1607 x1, y1 mouse coord in first area, which will disappear
1608 x2, y2 mouse coord in 2nd area, which will become joined
1612 init() find edge based on state vars
1613 test if the edge divides two areas,
1614 store active and nonactive area,
1616 apply() do the actual join
1618 exit() cleanup, send notifier
1622 exec() calls init, apply, exit
1624 invoke() sets mouse coords in x,y
1628 modal() accept modal events while doing it
1629 call apply() with active window and nonactive window
1630 call exit() and remove handler when LMB confirm
1634 typedef struct sAreaJoinData
1636 ScrArea *sa1; /* first area to be considered */
1637 ScrArea *sa2; /* second area to be considered */
1638 ScrArea *scr; /* designed for removal */
1643 /* validate selection inside screen, set variables OK */
1644 /* return 0: init failed */
1645 /* XXX todo: find edge based on (x,y) and set other area? */
1646 static int area_join_init(bContext *C, wmOperator *op)
1649 sAreaJoinData* jd= NULL;
1653 /* required properties, make negative to get return 0 if not set by caller */
1654 x1= RNA_int_get(op->ptr, "x1");
1655 y1= RNA_int_get(op->ptr, "y1");
1656 x2= RNA_int_get(op->ptr, "x2");
1657 y2= RNA_int_get(op->ptr, "y2");
1659 sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
1660 sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
1661 if(sa1==NULL || sa2==NULL || sa1==sa2)
1664 jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
1667 jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1669 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1676 /* apply the join of the areas (space types) */
1677 static int area_join_apply(bContext *C, wmOperator *op)
1679 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1682 if(!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)){
1685 if (CTX_wm_area(C) == jd->sa2) {
1686 CTX_wm_area_set(C, NULL);
1687 CTX_wm_region_set(C, NULL);
1693 /* finish operation */
1694 static void area_join_exit(bContext *C, wmOperator *op)
1696 if (op->customdata) {
1697 MEM_freeN(op->customdata);
1698 op->customdata = NULL;
1701 /* this makes sure aligned edges will result in aligned grabbing */
1702 removedouble_scredges(CTX_wm_screen(C));
1703 removenotused_scredges(CTX_wm_screen(C));
1704 removenotused_scrverts(CTX_wm_screen(C));
1707 static int area_join_exec(bContext *C, wmOperator *op)
1709 if(!area_join_init(C, op))
1710 return OPERATOR_CANCELLED;
1712 area_join_apply(C, op);
1713 area_join_exit(C, op);
1715 return OPERATOR_FINISHED;
1718 /* interaction callback */
1719 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1722 if(event->type==EVT_ACTIONZONE_AREA) {
1723 sActionzoneData *sad= event->customdata;
1725 if(sad->modifier>0) {
1726 return OPERATOR_PASS_THROUGH;
1729 /* verify *sad itself */
1730 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1731 return OPERATOR_PASS_THROUGH;
1733 /* is this our *sad? if areas equal it should be passed on */
1734 if(sad->sa1==sad->sa2)
1735 return OPERATOR_PASS_THROUGH;
1737 /* prepare operator state vars */
1738 RNA_int_set(op->ptr, "x1", sad->x);
1739 RNA_int_set(op->ptr, "y1", sad->y);
1740 RNA_int_set(op->ptr, "x2", event->x);
1741 RNA_int_set(op->ptr, "y2", event->y);
1743 if(!area_join_init(C, op))
1744 return OPERATOR_PASS_THROUGH;
1746 /* add temp handler */
1747 WM_event_add_modal_handler(C, op);
1749 return OPERATOR_RUNNING_MODAL;
1752 return OPERATOR_PASS_THROUGH;
1755 static int area_join_cancel(bContext *C, wmOperator *op)
1757 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1760 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1761 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1764 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1765 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1768 WM_event_add_notifier(C, NC_WINDOW, NULL);
1770 area_join_exit(C, op);
1772 return OPERATOR_CANCELLED;
1775 /* modal callback while selecting area (space) that will be removed */
1776 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1778 bScreen *sc= CTX_wm_screen(C);
1779 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1781 /* execute the events */
1782 switch(event->type) {
1786 ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
1790 if (jd->sa1 != sa) {
1791 dir = area_getorientation(sc, jd->sa1, sa);
1793 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1795 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1798 /* we are not bordering on the previously selected area
1799 we check if area has common border with the one marked for removal
1800 in this case we can swap areas.
1802 dir = area_getorientation(sc, sa, jd->sa2);
1804 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1805 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1808 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1809 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1812 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1816 WM_event_add_notifier(C, NC_WINDOW, NULL);
1819 /* we are back in the area previously selected for keeping
1820 * we swap the areas if possible to allow user to choose */
1821 if (jd->sa2 != NULL) {
1822 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1823 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1826 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1827 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1828 dir = area_getorientation(sc, jd->sa1, jd->sa2);
1830 printf("oops, didn't expect that!\n");
1834 dir = area_getorientation(sc, jd->sa1, sa);
1836 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1838 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1841 WM_event_add_notifier(C, NC_WINDOW, NULL);
1847 if(event->val==KM_RELEASE) {
1848 area_join_apply(C, op);
1849 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
1850 area_join_exit(C, op);
1851 return OPERATOR_FINISHED;
1856 return area_join_cancel(C, op);
1859 return OPERATOR_RUNNING_MODAL;
1862 /* Operator for joining two areas (space types) */
1863 static void SCREEN_OT_area_join(wmOperatorType *ot)
1866 ot->name= "Join area";
1867 ot->description= "Join selected areas into new window.";
1868 ot->idname= "SCREEN_OT_area_join";
1871 ot->exec= area_join_exec;
1872 ot->invoke= area_join_invoke;
1873 ot->modal= area_join_modal;
1874 ot->poll= ED_operator_areaactive;
1876 ot->flag= OPTYPE_BLOCKING;
1879 RNA_def_int(ot->srna, "x1", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
1880 RNA_def_int(ot->srna, "y1", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
1881 RNA_def_int(ot->srna, "x2", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
1882 RNA_def_int(ot->srna, "y2", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
1885 /* ************** repeat last operator ***************************** */
1887 static int repeat_last_exec(bContext *C, wmOperator *op)
1889 wmOperator *lastop= CTX_wm_manager(C)->operators.last;
1892 WM_operator_repeat(C, lastop);
1894 return OPERATOR_CANCELLED;
1897 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
1900 ot->name= "Repeat Last";
1901 ot->description= "Repeat last action.";
1902 ot->idname= "SCREEN_OT_repeat_last";
1905 ot->exec= repeat_last_exec;
1907 ot->poll= ED_operator_screenactive;
1911 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *event)
1913 wmWindowManager *wm= CTX_wm_manager(C);
1919 items= BLI_countlist(&wm->operators);
1921 return OPERATOR_CANCELLED;
1923 pup= uiPupMenuBegin(C, op->type->name, 0);
1924 layout= uiPupMenuLayout(pup);
1926 for (i=items-1, lastop= wm->operators.last; lastop; lastop= lastop->prev, i--)
1927 uiItemIntO(layout, lastop->type->name, 0, op->type->idname, "index", i);
1929 uiPupMenuEnd(C, pup);
1931 return OPERATOR_CANCELLED;
1934 static int repeat_history_exec(bContext *C, wmOperator *op)
1936 wmWindowManager *wm= CTX_wm_manager(C);
1938 op= BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
1940 /* let's put it as last operator in list */
1941 BLI_remlink(&wm->operators, op);
1942 BLI_addtail(&wm->operators, op);
1944 WM_operator_repeat(C, op);
1947 return OPERATOR_FINISHED;
1950 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
1953 ot->name= "Repeat History";
1954 ot->description= "Display menu for previous actions performed.";
1955 ot->idname= "SCREEN_OT_repeat_history";
1958 ot->invoke= repeat_history_invoke;
1959 ot->exec= repeat_history_exec;
1961 ot->poll= ED_operator_screenactive;
1963 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
1966 /* ********************** redo operator ***************************** */
1968 static int redo_last_invoke(bContext *C, wmOperator *op, wmEvent *event)
1970 wmWindowManager *wm= CTX_wm_manager(C);
1973 /* only for operators that are registered and did an undo push */
1974 for(lastop= wm->operators.last; lastop; lastop= lastop->prev)
1975 if((lastop->type->flag & OPTYPE_REGISTER) && (lastop->type->flag & OPTYPE_UNDO))
1979 WM_operator_redo_popup(C, lastop);
1981 return OPERATOR_CANCELLED;
1984 static void SCREEN_OT_redo_last(wmOperatorType *ot)
1987 ot->name= "Redo Last";
1988 ot->description= "Display menu for last action performed.";
1989 ot->idname= "SCREEN_OT_redo_last";
1992 ot->invoke= redo_last_invoke;
1994 ot->poll= ED_operator_screenactive;
1997 /* ************** region four-split operator ***************************** */
1999 /* insert a region in the area region list */
2000 static int region_foursplit_exec(bContext *C, wmOperator *op)
2002 ARegion *ar= CTX_wm_region(C);
2005 if(ar->regiontype!=RGN_TYPE_WINDOW)
2006 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2007 else if(ar->alignment==RGN_ALIGN_QSPLIT) {
2008 ScrArea *sa= CTX_wm_area(C);
2011 /* keep current region */
2014 if(sa->spacetype==SPACE_VIEW3D) {
2015 RegionView3D *rv3d= ar->regiondata;
2017 rv3d->rflag &= ~RV3D_CLIPPING;
2020 for(ar= sa->regionbase.first; ar; ar= arn) {
2022 if(ar->alignment==RGN_ALIGN_QSPLIT) {
2023 ED_region_exit(C, ar);
2024 BKE_area_region_free(sa->type, ar);
2025 BLI_remlink(&sa->regionbase, ar);
2029 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2032 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2034 ScrArea *sa= CTX_wm_area(C);
2038 ar->alignment= RGN_ALIGN_QSPLIT;
2040 for(count=0; count<3; count++) {
2041 newar= BKE_area_region_copy(sa->type, ar);
2042 BLI_addtail(&sa->regionbase, newar);
2045 /* lock views and set them */
2046 if(sa->spacetype==SPACE_VIEW3D) {
2049 rv3d= ar->regiondata;
2050 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_FRONT; rv3d->persp= RV3D_ORTHO;
2053 rv3d= ar->regiondata;
2054 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_TOP; rv3d->persp= RV3D_ORTHO;
2057 rv3d= ar->regiondata;
2058 rv3d->viewlock= RV3D_LOCKED; rv3d->view= RV3D_VIEW_RIGHT; rv3d->persp= RV3D_ORTHO;
2061 rv3d= ar->regiondata;
2062 rv3d->view= RV3D_VIEW_CAMERA; rv3d->persp= RV3D_CAMOB;
2065 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2069 return OPERATOR_FINISHED;
2072 static void SCREEN_OT_region_foursplit(wmOperatorType *ot)
2075 ot->name= "Toggle Quad View";
2076 ot->description= "Split selected area into camera, front, right & top views.";
2077 ot->idname= "SCREEN_OT_region_foursplit";
2080 // ot->invoke= WM_operator_confirm;
2081 ot->exec= region_foursplit_exec;
2082 ot->poll= ED_operator_areaactive;
2088 /* ************** region flip operator ***************************** */
2090 /* flip a region alignment */
2091 static int region_flip_exec(bContext *C, wmOperator *op)
2093 ARegion *ar= CTX_wm_region(C);
2095 if(ar->alignment==RGN_ALIGN_TOP)
2096 ar->alignment= RGN_ALIGN_BOTTOM;
2097 else if(ar->alignment==RGN_ALIGN_BOTTOM)
2098 ar->alignment= RGN_ALIGN_TOP;
2099 else if(ar->alignment==RGN_ALIGN_LEFT)
2100 ar->alignment= RGN_ALIGN_RIGHT;
2101 else if(ar->alignment==RGN_ALIGN_RIGHT)
2102 ar->alignment= RGN_ALIGN_LEFT;
2104 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
2105 printf("executed region flip\n");
2107 return OPERATOR_FINISHED;
2111 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2114 ot->name= "Flip Region";
2115 ot->idname= "SCREEN_OT_region_flip";
2118 ot->exec= region_flip_exec;
2120 ot->poll= ED_operator_areaactive;
2125 /* ****************** anim player, with timer ***************** */
2127 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2129 if(regiontype==RGN_TYPE_WINDOW) {
2131 switch (spacetype) {
2133 if(redraws & TIME_ALL_3D_WIN)
2139 if(redraws & TIME_ALL_ANIM_WIN)
2143 /* if only 1 window or 3d windows, we do timeline too */
2144 if(redraws & (TIME_ALL_ANIM_WIN|TIME_REGION|TIME_ALL_3D_WIN))
2148 if(redraws & TIME_ALL_BUTS_WIN)
2152 if(redraws & (TIME_SEQ|TIME_ALL_ANIM_WIN))
2156 if(redraws & (TIME_NODES))
2160 if(redraws & TIME_ALL_IMAGE_WIN)
2166 else if(regiontype==RGN_TYPE_UI) {
2167 if(redraws & TIME_ALL_BUTS_WIN)
2170 else if(regiontype==RGN_TYPE_HEADER) {
2171 if(spacetype==SPACE_TIME)
2177 static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event)
2179 bScreen *screen= CTX_wm_screen(C);
2181 if(screen->animtimer==event->customdata) {
2182 Scene *scene= CTX_data_scene(C);
2183 wmTimer *wt= screen->animtimer;
2184 ScreenAnimData *sad= wt->customdata;
2188 /* sync, don't sync, or follow scene setting */
2189 if(sad->flag & ANIMPLAY_FLAG_SYNC) sync= 1;
2190 else if(sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync= 0;
2191 else sync= (scene->audio.flag & AUDIO_SYNC);
2195 int step = floor(wt->duration * FPS);
2196 if(sad->flag & ANIMPLAY_FLAG_REVERSE) // XXX does this option work with audio?
2197 scene->r.cfra -= step;
2199 scene->r.cfra += step;
2200 wt->duration -= ((float)step)/FPS;
2204 if(sad->flag & ANIMPLAY_FLAG_REVERSE)
2210 /* reset 'jumped' flag before checking if we need to jump... */
2211 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2213 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2214 /* jump back to end? */
2215 if (scene->r.psfra) {
2216 if (scene->r.cfra < scene->r.psfra) {
2217 scene->r.cfra= scene->r.pefra;
2218 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2222 if (scene->r.cfra < scene->r.sfra) {
2223 scene->r.cfra= scene->r.efra;
2224 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2229 /* jump back to start? */
2230 if (scene->r.psfra) {
2231 if (scene->r.cfra > scene->r.pefra) {
2232 scene->r.cfra= scene->r.psfra;
2233 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2237 if (scene->r.cfra > scene->r.efra) {
2238 scene->r.cfra= scene->r.sfra;
2239 sad->flag |= ANIMPLAY_FLAG_JUMPED;
2244 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2245 ED_update_for_newframe(C, 1);
2247 sound_update_playing(C);
2249 for(sa= screen->areabase.first; sa; sa= sa->next) {
2251 for(ar= sa->regionbase.first; ar; ar= ar->next) {
2253 ED_region_tag_redraw(ar);
2255 if(match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2256 ED_region_tag_redraw(ar);
2260 /* recalculate the timestep for the timer now that we've finished calculating this,
2261 * since the frames-per-second value may have been changed
2263 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
2264 wt->timestep= (1.0/FPS);
2266 //WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2268 return OPERATOR_FINISHED;
2270 return OPERATOR_PASS_THROUGH;
2273 static void SCREEN_OT_animation_step(wmOperatorType *ot)
2276 ot->name= "Animation Step";
2277 ot->description= "Step through animation by position.";
2278 ot->idname= "SCREEN_OT_animation_step";
2281 ot->invoke= screen_animation_step;
2283 ot->poll= ED_operator_screenactive;
2287 /* ****************** anim player, starts or ends timer ***************** */
2289 /* toggle operator */
2290 static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event)
2292 bScreen *screen= CTX_wm_screen(C);
2294 if(screen->animtimer) {
2295 ED_screen_animation_timer(C, 0, 0, 0);
2299 ScrArea *sa= CTX_wm_area(C);
2300 int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
2303 if(RNA_property_is_set(op->ptr, "sync"))
2304 sync= (RNA_boolean_get(op->ptr, "sync"));
2306 /* timeline gets special treatment since it has it's own menu for determining redraws */
2307 if ((sa) && (sa->spacetype == SPACE_TIME)) {
2308 SpaceTime *stime= (SpaceTime *)sa->spacedata.first;
2310 ED_screen_animation_timer(C, stime->redraws, sync, mode);
2312 /* update region if TIME_REGION was set, to leftmost 3d window */
2313 ED_screen_animation_timer_update(C, stime->redraws);
2316 int redraws = TIME_REGION|TIME_ALL_3D_WIN;
2318 /* XXX - would like a better way to deal with this situation - Campbell */
2319 if((sa) && (sa->spacetype == SPACE_SEQ)) {
2320 redraws |= TIME_SEQ;
2323 ED_screen_animation_timer(C, redraws, sync, mode);
2325 if(screen->animtimer) {
2326 wmTimer *wt= screen->animtimer;
2327 ScreenAnimData *sad= wt->customdata;
2329 sad->ar= CTX_wm_region(C);
2334 return OPERATOR_FINISHED;
2337 static void SCREEN_OT_animation_play(wmOperatorType *ot)
2340 ot->name= "Play Animation";
2341 ot->description= "Play animation.";
2342 ot->idname= "SCREEN_OT_animation_play";
2345 ot->invoke= screen_animation_play;
2347 ot->poll= ED_operator_screenactive;
2349 RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
2350 RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate and stay in sync with audio.");
2353 static int screen_animation_cancel(bContext *C, wmOperator *op, wmEvent *event)
2355 bScreen *screen= CTX_wm_screen(C);
2357 if(screen->animtimer)
2358 return screen_animation_play(C, op, event);
2360 return OPERATOR_PASS_THROUGH;
2363 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
2366 ot->name= "Cancel Animation";
2367 ot->description= "Cancel animation.";
2368 ot->idname= "SCREEN_OT_animation_cancel";
2371 ot->invoke= screen_animation_cancel;
2373 ot->poll= ED_operator_screenactive;
2376 /* ************** border select operator (template) ***************************** */
2378 /* operator state vars used: (added by default WM callbacks)
2382 customdata: the wmGesture pointer
2386 exec() has to be filled in by user
2388 invoke() default WM function
2391 modal() default WM function
2392 accept modal events while doing it, calls exec(), handles ESC and border drawing
2394 poll() has to be filled in by user for context
2397 static int border_select_do(bContext *C, wmOperator *op)
2399 int event_type= RNA_int_get(op->ptr, "event_type");
2401 if(event_type==LEFTMOUSE)
2402 printf("border select do select\n");
2403 else if(event_type==RIGHTMOUSE)
2404 printf("border select deselect\n");
2406 printf("border select do something\n");
2411 static void SCREEN_OT_border_select(wmOperatorType *ot)
2414 ot->name= "Border select";
2415 ot->idname= "SCREEN_OT_border_select";
2418 ot->exec= border_select_do;
2419 ot->invoke= WM_border_select_invoke;
2420 ot->modal= WM_border_select_modal;
2422 ot->poll= ED_operator_areaactive;
2425 RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
2426 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2427 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2428 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2429 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2434 /* ****************************** render invoking ***************** */
2436 /* set callbacks, exported to sequence render too.
2437 Only call in foreground (UI) renders. */
2439 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
2440 /* window as the last possible alternative. */
2441 static ScrArea *biggest_non_image_area(bContext *C)
2443 bScreen *sc= CTX_wm_screen(C);
2444 ScrArea *sa, *big= NULL;
2445 int size, maxsize= 0, bwmaxsize= 0;
2448 for(sa= sc->areabase.first; sa; sa= sa->next) {
2449 if(sa->winx > 30 && sa->winy > 30) {
2450 size= sa->winx*sa->winy;
2451 if(sa->spacetype == SPACE_BUTS) {
2452 if(foundwin == 0 && size > bwmaxsize) {
2457 else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
2468 static ScrArea *biggest_area(bContext *C)
2470 bScreen *sc= CTX_wm_screen(C);
2471 ScrArea *sa, *big= NULL;
2472 int size, maxsize= 0;
2474 for(sa= sc->areabase.first; sa; sa= sa->next) {
2475 size= sa->winx*sa->winy;
2476 if(size > maxsize) {
2485 static ScrArea *find_area_showing_r_result(bContext *C)
2487 wmWindowManager *wm= CTX_wm_manager(C);
2492 /* find an imagewindow showing render result */
2493 for(win=wm->windows.first; win; win=win->next) {
2494 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
2495 if(sa->spacetype==SPACE_IMAGE) {
2496 sima= sa->spacedata.first;
2497 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
2506 static ScrArea *find_area_image_empty(bContext *C)
2508 bScreen *sc= CTX_wm_screen(C);
2512 /* find an imagewindow showing render result */
2513 for(sa=sc->areabase.first; sa; sa= sa->next) {
2514 if(sa->spacetype==SPACE_IMAGE) {
2515 sima= sa->spacedata.first;
2523 #if 0 // XXX not used
2524 static ScrArea *find_empty_image_area(bContext *C)
2526 bScreen *sc= CTX_wm_screen(C);
2530 /* find an imagewindow showing render result */
2531 for(sa=sc->areabase.first; sa; sa= sa->next) {
2532 if(sa->spacetype==SPACE_IMAGE) {
2533 sima= sa->spacedata.first;
2540 #endif // XXX not used
2542 /* new window uses x,y to set position */
2543 static void screen_set_image_output(bContext *C, int mx, int my)
2545 wmWindow *win= CTX_wm_window(C);
2546 Scene *scene= CTX_data_scene(C);
2550 if(scene->r.displaymode==R_OUTPUT_WINDOW) {
2554 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
2555 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
2557 /* arbitrary... miniature image window views don't make much sense */
2558 if(sizex < 320) sizex= 320;
2559 if(sizey < 256) sizey= 256;
2561 /* XXX some magic to calculate postition */
2562 rect.xmin= mx + win->posx - sizex/2;
2563 rect.ymin= my + win->posy - sizey/2;
2564 rect.xmax= rect.xmin + sizex;
2565 rect.ymax= rect.ymin + sizey;
2567 /* changes context! */
2568 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
2572 else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
2573 /* this function returns with changed context */
2574 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
2579 sa= find_area_showing_r_result(C);
2581 sa= find_area_image_empty(C);
2584 /* find largest open non-image area */
2585 sa= biggest_non_image_area(C);
2587 ED_area_newspace(C, sa, SPACE_IMAGE);
2588 sima= sa->spacedata.first;
2590 /* makes ESC go back to prev space */
2591 sima->flag |= SI_PREVSPACE;
2594 /* use any area of decent size */
2595 sa= biggest_area(C);
2596 if(sa->spacetype!=SPACE_IMAGE) {
2597 // XXX newspace(sa, SPACE_IMAGE);
2598 sima= sa->spacedata.first;
2600 /* makes ESC go back to prev space */
2601 sima->flag |= SI_PREVSPACE;
2606 sima= sa->spacedata.first;
2608 /* get the correct image, and scale it */
2609 sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2611 // if(G.displaymode==2) { // XXX
2613 sima->flag |= SI_FULLWINDOW|SI_PREVSPACE;
2615 // ed_screen_fullarea(C, win, sa);
2621 /* executes blocking render */
2622 static int screen_render_exec(bContext *C, wmOperator *op)
2624 Scene *scene= CTX_data_scene(C);
2625 Render *re= RE_GetRender(scene->id.name);
2628 re= RE_NewRender(scene->id.name);
2630 RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
2632 if(RNA_boolean_get(op->ptr, "animation"))
2633 RE_BlenderAnim(re, scene, scene->r.sfra, scene->r.efra, scene->r.frame_step);
2635 RE_BlenderFrame(re, scene, scene->r.cfra);
2637 // no redraw needed, we leave state as we entered it
2638 ED_update_for_newframe(C, 1);
2640 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2642 return OPERATOR_FINISHED;
2645 typedef struct RenderJob {
2656 static void render_freejob(void *rjv)
2663 /* str is IMA_RW_MAXTEXT in size */
2664 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
2666 char info_time_str[32]; // used to be extern to header_info.c
2667 uintptr_t mem_in_use, mmap_in_use;
2668 float megs_used_memory, mmap_used_memory;
2671 mem_in_use= MEM_get_memory_in_use();
2672 mmap_in_use= MEM_get_mapped_memory_in_use();
2674 megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
2675 mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
2677 if(scene->lay & 0xFF000000)
2678 spos+= sprintf(spos, "Localview | ");
2679 else if(scene->r.scemode & R_SINGLE_LAYER)
2680 spos+= sprintf(spos, "Single Layer | ");
2683 spos+= sprintf(spos, "%s ", rs->statstr);
2686 spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
2687 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
2688 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
2689 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
2692 spos+= sprintf(spos, "Field %d ", rs->curfield);
2694 spos+= sprintf(spos, "Blur %d ", rs->curblur);
2697 BLI_timestr(rs->lastframetime, info_time_str);
2698 spos+= sprintf(spos, "Time:%s ", info_time_str);
2700 if(rs->infostr && rs->infostr[0])
2701 spos+= sprintf(spos, "| %s ", rs->infostr);
2703 /* very weak... but 512 characters is quite safe */
2704 if(spos >= str+IMA_RW_MAXTEXT)
2705 printf("WARNING! renderwin text beyond limit \n");
2709 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
2713 /* malloc OK here, stats_draw is not in tile threads */
2714 if(rj->image->render_text==NULL)
2715 rj->image->render_text= MEM_callocN(IMA_RW_MAXTEXT, "rendertext");
2717 make_renderinfo_string(rs, rj->scene, rj->image->render_text);
2719 /* make jobs timer to send notifier */
2720 *(rj->do_update)= 1;
2724 /* called inside thread! */
2725 static void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
2727 float x1, y1, *rectf= NULL;
2728 int ymin, ymax, xmin, xmax;
2732 /* if renrect argument, we only refresh scanlines */
2734 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
2735 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
2738 /* xmin here is first subrect x coord, xmax defines subrect width */
2739 xmin = renrect->xmin + rr->crop;
2740 xmax = renrect->xmax - xmin - rr->crop;
2743 ymin= renrect->ymin + rr->crop;
2744 ymax= renrect->ymax - ymin - rr->crop;
2747 renrect->ymin= renrect->ymax;
2751 xmin = ymin = rr->crop;
2752 xmax = rr->rectx - 2*rr->crop;
2753 ymax = rr->recty - 2*rr->crop;
2756 /* xmin ymin is in tile coords. transform to ibuf */
2757 rxmin= rr->tilerect.xmin + xmin;
2758 if(rxmin >= ibuf->x) return;
2759 rymin= rr->tilerect.ymin + ymin;
2760 if(rymin >= ibuf->y) return;
2762 if(rxmin + xmax > ibuf->x)
2763 xmax= ibuf->x - rxmin;
2764 if(rymin + ymax > ibuf->y)
2765 ymax= ibuf->y - rymin;
2767 if(xmax < 1 || ymax < 1) return;
2769 /* find current float rect for display, first case is after composit... still weak */
2776 if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
2777 rectf= rr->renlay->rectf;
2780 if(rectf==NULL) return;
2782 rectf+= 4*(rr->rectx*ymin + xmin);
2783 rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
2785 /* XXX make nice consistent functions for this */
2786 if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
2787 for(y1= 0; y1<ymax; y1++) {
2792 /* XXX temp. because crop offset */
2793 if( rectc >= (char *)(ibuf->rect)) {
2794 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2795 srgb[0]= linearrgb_to_srgb(rf[0]);
2796 srgb[1]= linearrgb_to_srgb(rf[1]);
2797 srgb[2]= linearrgb_to_srgb(rf[2]);
2799 rc[0]= FTOCHAR(srgb[0]);
2800 rc[1]= FTOCHAR(srgb[1]);
2801 rc[2]= FTOCHAR(srgb[2]);
2802 rc[3]= FTOCHAR(rf[3]);
2805 rectf += 4*rr->rectx;
2809 for(y1= 0; y1<ymax; y1++) {
2813 /* XXX temp. because crop offset */
2814 if( rectc >= (char *)(ibuf->rect)) {
2815 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
2816 rc[0]= FTOCHAR(rf[0]);
2817 rc[1]= FTOCHAR(rf[1]);
2818 rc[2]= FTOCHAR(rf[2]);
2819 rc[3]= FTOCHAR(rf[3]);
2822 rectf += 4*rr->rectx;
2829 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
2835 ibuf= BKE_image_acquire_ibuf(rj->image, &rj->iuser, &lock);
2837 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
2839 /* make jobs timer to send notifier */
2840 *(rj->do_update)= 1;
2842 BKE_image_release_ibuf(rj->image, lock);
2845 static void render_startjob(void *rjv, short *stop, short *do_update)
2850 rj->do_update= do_update;
2853 RE_BlenderAnim(rj->re, rj->scene, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step);
2855 RE_BlenderFrame(rj->re, rj->scene, rj->scene->r.cfra);
2858 /* called by render, check job 'stop' value or the global */
2859 static int render_breakjob(void *rjv)
2865 if(rj->stop && *(rj->stop))
2871 static int screen_render_modal(bContext *C, wmOperator *op, wmEvent *event)
2873 /* no running blender, remove handler and pass through */
2874 if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
2875 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
2877 /* running render */
2878 switch (event->type) {
2880 return OPERATOR_RUNNING_MODAL;
2883 return OPERATOR_PASS_THROUGH;
2886 /* using context, starts job */
2887 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
2889 /* new render clears all callbacks */
2890 Scene *scene= CTX_data_scene(C);
2896 /* only one render job at a time */
2897 if(WM_jobs_test(CTX_wm_manager(C), scene))
2898 return OPERATOR_CANCELLED;
2900 /* stop all running jobs, currently previews frustrate Render */
2901 WM_jobs_stop_all(CTX_wm_manager(C));
2903 /* handle UI stuff */
2906 /* flush multires changes (for sculpt) */
2907 multires_force_update(CTX_data_active_object(C));
2909 /* get editmode results */
2910 ED_object_exit_editmode(C, EM_DO_UNDO); /* 0 = does not exit editmode */
2913 // get view3d layer, local layer, make this nice api call to render
2916 /* ensure at least 1 area shows result */
2917 screen_set_image_output(C, event->x, event->y);
2919 /* job custom data */
2920 rj= MEM_callocN(sizeof(RenderJob), "render job");
2922 rj->win= CTX_wm_window(C);
2923 rj->anim= RNA_boolean_get(op->ptr, "animation");
2924 rj->iuser.scene= scene;
2928 steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY);
2929 WM_jobs_customdata(steve, rj, render_freejob);
2930 WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
2931 WM_jobs_callbacks(steve, render_startjob, NULL, NULL);
2933 /* get a render result image, and make sure it is empty */
2934 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
2935 BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
2938 /* setup new render */
2939 re= RE_NewRender(scene->id.name);
2940 RE_test_break_cb(re, rj, render_breakjob);
2941 RE_display_draw_cb(re, rj, image_rect_update);
2942 RE_stats_draw_cb(re, rj, image_renderinfo_cb);
2947 // BKE_report in render!
2948 // RE_error_cb(re, error_cb);
2950 WM_jobs_start(CTX_wm_manager(C), steve);
2955 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
2957 /* add modal handler for ESC */
2958 WM_event_add_modal_handler(C, op);
2960 return OPERATOR_RUNNING_MODAL;
2964 /* contextual render, using current scene, view3d? */
2965 static void SCREEN_OT_render(wmOperatorType *ot)
2969 ot->description= "Render active scene.";
2970 ot->idname= "SCREEN_OT_render";
2973 ot->invoke= screen_render_invoke;
2974 ot->modal= screen_render_modal;
2975 ot->exec= screen_render_exec;
2977 ot->poll= ED_operator_screenactive;
2979 RNA_def_boolean(ot->srna, "animation", 0, "Animation", "");
2982 /* ****************************** opengl render *************************** */
2984 typedef struct OGLRender {
3004 static void screen_opengl_render_apply(OGLRender *oglrender)
3006 Scene *scene= oglrender->scene;
3007 ARegion *ar= oglrender->ar;
3008 View3D *v3d= oglrender->v3d;
3009 RegionView3D *rv3d= oglrender->rv3d;
3014 int sizex= oglrender->sizex;
3015 int sizey= oglrender->sizey;
3018 GPU_offscreen_bind(oglrender->ofs);
3020 /* render 3d view */
3021 if(rv3d->persp==RV3D_CAMOB && v3d->camera) {
3022 RE_GetCameraWindow(oglrender->re, v3d->camera, scene->r.cfra, winmat);
3023 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat);
3026 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, NULL);
3028 /* read in pixels & stamp */
3029 rr= RE_AcquireResultRead(oglrender->re);
3030 glReadPixels(0, 0, sizex, sizey, GL_RGBA, GL_FLOAT, rr->rectf);
3031 if((scene->r.scemode & R_STAMP_INFO) && (scene->r.stamp & R_STAMP_DRAW))
3032 BKE_stamp_buf(scene, (unsigned char *)rr->rect32, rr->rectf, rr->rectx, rr->recty, 3);
3033 RE_ReleaseResult(oglrender->re);
3035 /* update byte from float buffer */
3036 ibuf= BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
3037 if(ibuf) image_buffer_rect_update(NULL, rr, ibuf, NULL);
3038 BKE_image_release_ibuf(oglrender->ima, lock);
3041 GPU_offscreen_unbind(oglrender->ofs);
3044 static int screen_opengl_render_init(bContext *C, wmOperator *op)
3046 /* new render clears all callbacks */
3047 Scene *scene= CTX_data_scene(C);
3050 OGLRender *oglrender;
3053 /* ensure we have a 3d view */
3054 if(!ED_view3d_context_activate(C))
3057 /* only one render job at a time */
3058 if(WM_jobs_test(CTX_wm_manager(C), scene))
3061 /* stop all running jobs, currently previews frustrate Render */
3062 WM_jobs_stop_all(CTX_wm_manager(C));
3064 /* handle UI stuff */
3067 /* create offscreen buffer */
3068 sizex= (scene->r.size*scene->r.xsch)/100;
3069 sizey= (scene->r.size*scene->r.ysch)/100;
3071 view3d_operator_needs_opengl(C);
3072 ofs= GPU_offscreen_create(sizex, sizey);
3075 BKE_report(op->reports, RPT_ERROR, "Failed to create OpenGL offscreen buffer.");
3079 /* allocate opengl render */
3080 oglrender= MEM_callocN(sizeof(OGLRender), "OGLRender");
3081 op->customdata= oglrender;
3083 oglrender->ofs= ofs;
3084 oglrender->sizex= sizex;
3085 oglrender->sizey= sizey;
3086 oglrender->scene= scene;
3088 oglrender->v3d= CTX_wm_view3d(C);
3089 oglrender->ar= CTX_wm_region(C);
3090 oglrender->rv3d= CTX_wm_region_view3d(C);
3092 /* create image and image user */
3093 oglrender->ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
3094 BKE_image_signal(oglrender->ima, NULL, IMA_SIGNAL_FREE);
3096 oglrender->iuser.scene= scene;
3097 oglrender->iuser.ok= 1;
3099 /* create render and render result */
3100 oglrender->re= RE_NewRender(scene->id.name);
3101 RE_InitState(oglrender->re, NULL, &scene->r, sizex, sizey, NULL);
3103 rr= RE_AcquireResultWrite(oglrender->re);
3105 rr->rectf= MEM_mallocN(sizeof(float)*4*sizex*sizex, "32 bits rects");
3106 RE_ReleaseResult(oglrender->re);
3111 static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
3113 Scene *scene= oglrender->scene;
3116 if(BKE_imtype_is_movie(scene->r.imtype))
3117 oglrender->mh->end_movie();
3120 if(oglrender->timer) {
3121 scene->r.cfra= oglrender->cfrao;
3122 scene_update_for_newframe(scene, scene->lay);
3124 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), oglrender->timer);
3128 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, oglrender->scene);
3130 GPU_offscreen_free(oglrender->ofs);
3132 MEM_freeN(oglrender);
3135 static int screen_opengl_render_cancel(bContext *C, wmOperator *op)
3137 screen_opengl_render_end(C, op->customdata);
3139 return OPERATOR_CANCELLED;
3142 static int screen_opengl_render_modal(bContext *C, wmOperator *op, wmEvent *event)
3144 OGLRender *oglrender= op->customdata;
3145 Scene *scene= oglrender->scene;
3148 char name[FILE_MAXDIR+FILE_MAXFILE];
3152 switch(event->type) {
3155 screen_opengl_render_end(C, op->customdata);
3156 return OPERATOR_FINISHED;
3159 if(oglrender->timer == event->customdata)
3163 return OPERATOR_RUNNING_MODAL;
3166 /* go to next frame */
3167 while(CFRA<oglrender->nfra) {
3168 if(scene->lay & 0xFF000000)
3169 lay= scene->lay & 0xFF000000;
3173 scene_update_for_newframe(scene, lay);
3177 scene_update_for_newframe(scene, scene->lay);
3179 /* render into offscreen buffer */
3180 screen_opengl_render_apply(oglrender);
3183 ibuf= BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
3186 if(BKE_imtype_is_movie(scene->r.imtype)) {
3187 oglrender->mh->append_movie(&scene->r, CFRA, (int*)ibuf->rect, oglrender->sizex, oglrender->sizey);
3188 printf("Append frame %d", scene->r.cfra);
3192 BKE_makepicstring(scene, name, scene->r.pic, scene->r.cfra, scene->r.imtype);
3193 ok= BKE_write_ibuf(scene, ibuf, name, scene->r.imtype, scene->r.subimtype, scene->r.quality);
3195 if(ok==0) printf("write error: cannot save %s\n", name);
3196 else printf("saved: %s", name);
3200 BKE_image_release_ibuf(oglrender->ima, lock);
3202 /* movie stats prints have no line break */
3205 /* go to next frame */
3206 oglrender->nfra += scene->r.frame_step;
3209 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, oglrender->scene);
3211 /* stop at the end or on error */
3212 if(scene->r.cfra > EFRA || !ok) {
3213 screen_opengl_render_end(C, op->customdata);
3214 return OPERATOR_FINISHED;
3217 return OPERATOR_RUNNING_MODAL;
3220 static int screen_opengl_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
3222 int anim= RNA_boolean_get(op->ptr, "animation");
3224 if(!screen_opengl_render_init(C, op))
3225 return OPERATOR_CANCELLED;
3229 screen_opengl_render_apply(op->customdata);
3230 screen_opengl_render_end(C, op->customdata);
3231 screen_set_image_output(C, event->x, event->y);
3233 return OPERATOR_FINISHED;
3236 /* initialize animation */
3237 OGLRender *oglrender;
3240 oglrender= op->customdata;
3241 scene= oglrender->scene;
3243 oglrender->mh= BKE_get_movie_handle(scene->r.imtype);
3244 if(BKE_imtype_is_movie(scene->r.imtype))
3245 oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey);
3247 oglrender->cfrao= scene->r.cfra;
3248 oglrender->nfra= SFRA;
3249 scene->r.cfra= SFRA;
3251 WM_event_add_modal_handler(C, op);
3252 oglrender->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
3254 screen_set_image_output(C, event->x, event->y);
3256 return OPERATOR_RUNNING_MODAL;
3260 static void SCREEN_OT_opengl_render(wmOperatorType *ot)
3263 ot->name= "OpenGL Render";
3264 ot->description= "OpenGL render active viewport.";
3265 ot->idname= "SCREEN_OT_opengl_render";
3268 ot->invoke= screen_opengl_render_invoke;
3269 ot->modal= screen_opengl_render_modal;
3270 ot->cancel= screen_opengl_render_cancel;
3272 ot->poll= ED_operator_screenactive;
3274 RNA_def_boolean(ot->srna, "animation", 0, "Animation", "");
3277 /* *********************** cancel render viewer *************** */
3279 static int render_view_cancel_exec(bContext *C, wmOperator *unused)
3281 wmWindow *win= CTX_wm_window(C);
3282 ScrArea *sa= CTX_wm_area(C);
3283 SpaceImage *sima= sa->spacedata.first;
3285 /* test if we have a temp screen in front */
3286 if(CTX_wm_window(C)->screen->full==SCREENTEMP) {
3287 wm_window_lower(CTX_wm_window(C));
3288 return OPERATOR_FINISHED;
3290 /* determine if render already shows */
3291 else if(sima->flag & SI_PREVSPACE) {
3292 sima->flag &= ~SI_PREVSPACE;
3294 if(sima->flag & SI_FULLWINDOW) {
3295 sima->flag &= ~SI_FULLWINDOW;
3296 ED_screen_full_prevspace(C);
3299 ED_area_prevspace(C);
3301 return OPERATOR_FINISHED;
3303 else if(sima->flag & SI_FULLWINDOW) {
3304 sima->flag &= ~SI_FULLWINDOW;
3305 ed_screen_fullarea(C, win, sa);
3306 return OPERATOR_FINISHED;