diff options
Diffstat (limited to 'source/blender/editors/screen')
-rw-r--r-- | source/blender/editors/screen/CMakeLists.txt | 3 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 11 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_context.c | 8 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_draw.c | 9 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_edit.c | 427 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_intern.h | 11 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_ops.c | 229 | ||||
-rw-r--r-- | source/blender/editors/screen/workspace_edit.c | 426 | ||||
-rw-r--r-- | source/blender/editors/screen/workspace_layout_edit.c | 198 |
9 files changed, 866 insertions, 456 deletions
diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index 43e044b613a..02584a4611b 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../blenloader ../../blentranslation ../../bmesh ../../gpu @@ -46,6 +47,8 @@ set(SRC screen_edit.c screen_ops.c screendump.c + workspace_edit.c + workspace_layout_edit.c screen_intern.h ) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 3b933b0182a..9d3dde06925 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -581,9 +581,11 @@ void ED_region_do_draw(bContext *C, ARegion *ar) UI_blocklist_free_inactive(C, &ar->uiblocks); if (sa) { + const bScreen *screen = WM_window_get_active_screen(win); + /* disable emboss when the area is full, * unless we need to see division between regions (quad-split for eg) */ - if (((win->screen->state == SCREENFULL) && (ar->alignment == RGN_ALIGN_NONE)) == 0) { + if (((screen->state == SCREENFULL) && (ar->alignment == RGN_ALIGN_NONE)) == 0) { region_draw_emboss(ar, &ar->winrct); } } @@ -1353,7 +1355,9 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti * must be minimum '4' */ } else { - if (ELEM(win->screen->state, SCREENNORMAL, SCREENMAXIMIZED)) { + const bScreen *screen = WM_window_get_active_screen(win); + + if (ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) { region_azone_add(sa, ar, alignment, false); } else { @@ -1477,6 +1481,7 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand /* called in screen_refresh, or screens_init, also area size changes */ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) { + const bScreen *screen = WM_window_get_active_screen(win); ARegion *ar; rcti rect; @@ -1495,7 +1500,7 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) area_calc_totrct(sa, WM_window_pixels_x(win), WM_window_pixels_y(win)); /* clear all azones, add the area triange widgets */ - area_azone_initialize(win, win->screen, sa); + area_azone_initialize(win, screen, sa); /* region rect sizes */ rect = sa->totrct; diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 0448fba78e6..d4e9609904e 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -50,6 +50,7 @@ #include "BKE_layer.h" #include "BKE_screen.h" #include "BKE_sequencer.h" +#include "BKE_workspace.h" #include "RNA_access.h" @@ -81,10 +82,13 @@ const char *screen_context_dir[] = { int ed_screen_context(const bContext *C, const char *member, bContextDataResult *result) { + wmWindow *win = CTX_wm_window(C); bScreen *sc = CTX_wm_screen(C); ScrArea *sa = CTX_wm_area(C); - Scene *scene = sc->scene; - SceneLayer *sl = CTX_data_scene_layer(C); + Scene *scene = WM_window_get_active_scene(win); + /* can't call BKE_scene_layer_context_active here, it uses G.main->wm which might be NULL on file read. */ + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + SceneLayer *sl = BKE_workspace_render_layer_get(workspace); Object *obedit = scene->obedit; Object *obact = sl->basact ? sl->basact->object : NULL; diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 4e43a7f96e7..52c13871925 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -293,6 +293,7 @@ static void drawscredge_area(ScrArea *sa, int sizex, int sizey, unsigned int pos */ void ED_screen_draw(wmWindow *win) { + bScreen *screen = WM_window_get_active_screen(win); const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); @@ -301,7 +302,7 @@ void ED_screen_draw(wmWindow *win) ScrArea *sa2 = NULL; ScrArea *sa3 = NULL; - wmSubWindowSet(win, win->screen->mainwin); + wmSubWindowSet(win, screen->mainwin); unsigned int pos = VertexFormat_add_attrib(immVertexFormat(), "pos", COMP_F32, 2, KEEP_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -312,7 +313,7 @@ void ED_screen_draw(wmWindow *win) glLineWidth((2.0f * U.pixelsize) - 1); immUniformColor3ub(0x50, 0x50, 0x50); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { drawscredge_area(sa, winsize_x, winsize_y, pos); } } @@ -320,7 +321,7 @@ void ED_screen_draw(wmWindow *win) glLineWidth(1); immUniformColor3ub(0, 0, 0); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { drawscredge_area(sa, winsize_x, winsize_y, pos); /* gather area split/join info */ @@ -403,7 +404,7 @@ void ED_screen_draw(wmWindow *win) immUnbindProgram(); - win->screen->do_draw = false; + screen->do_draw = false; } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index d59109a5baa..65b783463a9 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -51,6 +51,7 @@ #include "BKE_node.h" #include "BKE_screen.h" #include "BKE_scene.h" +#include "BKE_workspace.h" #include "WM_api.h" #include "WM_types.h" @@ -264,7 +265,7 @@ bool scredge_is_horizontal(ScrEdge *se) } /* need win size to make sure not to include edges along screen edge */ -ScrEdge *screen_find_active_scredge(bScreen *sc, +ScrEdge *screen_find_active_scredge(const bScreen *sc, const int winsize_x, const int winsize_y, const int mx, const int my) { @@ -455,39 +456,35 @@ ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge) return newa; } -/* empty screen, with 1 dummy area without spacedata */ -/* uses window size */ -bScreen *ED_screen_add(wmWindow *win, Scene *scene, const char *name) +/** + * Empty screen, with 1 dummy area without spacedata. Uses window size. + */ +bScreen *screen_add(const char *name, const int winsize_x, const int winsize_y) { - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); - bScreen *sc; ScrVert *sv1, *sv2, *sv3, *sv4; - + sc = BKE_libblock_alloc(G.main, ID_SCR, name); - sc->scene = scene; sc->do_refresh = true; sc->redraws_flag = TIME_ALL_3D_WIN | TIME_ALL_ANIM_WIN; - sc->winid = win->winid; sv1 = screen_addvert(sc, 0, 0); sv2 = screen_addvert(sc, 0, winsize_y - 1); sv3 = screen_addvert(sc, winsize_x - 1, winsize_y - 1); sv4 = screen_addvert(sc, winsize_x - 1, 0); - + screen_addedge(sc, sv1, sv2); screen_addedge(sc, sv2, sv3); screen_addedge(sc, sv3, sv4); screen_addedge(sc, sv4, sv1); - + /* dummy type, no spacedata */ screen_addarea(sc, sv1, sv2, sv3, sv4, HEADERDOWN, SPACE_EMPTY); - + return sc; } -static void screen_copy(bScreen *to, bScreen *from) +void screen_data_copy(bScreen *to, bScreen *from) { ScrVert *s1, *s2; ScrEdge *se; @@ -530,7 +527,16 @@ static void screen_copy(bScreen *to, bScreen *from) /* put at zero (needed?) */ for (s1 = from->vertbase.first; s1; s1 = s1->next) s1->newv = NULL; +} +/** + * Prepare a newly created screen for initializing it as active screen. + */ +void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new) +{ + screen_new->winid = win->winid; + screen_new->do_refresh = true; + screen_new->do_draw = true; } @@ -841,24 +847,12 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) /* ****************** EXPORTED API TO OTHER MODULES *************************** */ -bScreen *ED_screen_duplicate(wmWindow *win, bScreen *sc) -{ - bScreen *newsc; - - if (sc->state != SCREENNORMAL) return NULL; /* XXX handle this case! */ - - /* make new empty screen: */ - newsc = ED_screen_add(win, sc->scene, sc->id.name + 2); - /* copy all data */ - screen_copy(newsc, sc); - - return newsc; -} - /* screen sets cursor based on swinid */ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) { - for (ScrArea *sa = win->screen->areabase.first; sa; sa = sa->next) { + bScreen *screen = WM_window_get_active_screen(win); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid == swinid) { if (swin_changed || (ar->type && ar->type->event_cursor)) { @@ -876,19 +870,20 @@ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) void ED_screen_do_listen(bContext *C, wmNotifier *note) { wmWindow *win = CTX_wm_window(C); - + bScreen *screen = CTX_wm_screen(C); + /* generic notes */ switch (note->category) { case NC_WM: if (note->data == ND_FILEREAD) - win->screen->do_draw = true; + screen->do_draw = true; break; case NC_WINDOW: - win->screen->do_draw = true; + screen->do_draw = true; break; case NC_SCREEN: if (note->action == NA_EDITED) - win->screen->do_draw = win->screen->do_refresh = true; + screen->do_draw = screen->do_refresh = true; break; case NC_SCENE: if (note->data == ND_MODE) @@ -912,7 +907,9 @@ static void screen_refresh_headersizes(void) /* make this screen usable */ /* for file read and first use, for scaling window, area moves */ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) -{ +{ + bScreen *screen = WM_window_get_active_screen(win); + /* exception for bg mode, we only need the screen context */ if (!G.background) { const int winsize_x = WM_window_pixels_x(win); @@ -928,32 +925,34 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) /* header size depends on DPI, let's verify */ screen_refresh_headersizes(); - screen_test_scale(win->screen, winsize_x, winsize_y); + screen_test_scale(screen, winsize_x, winsize_y); - if (win->screen->mainwin == 0) { - win->screen->mainwin = wm_subwindow_open(win, &winrct, false); + if (screen->mainwin == 0) { + screen->mainwin = wm_subwindow_open(win, &winrct, false); } else { - wm_subwindow_position(win, win->screen->mainwin, &winrct, false); + wm_subwindow_position(win, screen->mainwin, &winrct, false); } - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { /* set spacetype and region callbacks, calls init() */ /* sets subwindows for regions, adds handlers */ ED_area_initialize(wm, win, sa); } /* wake up animtimer */ - if (win->screen->animtimer) - WM_event_timer_sleep(wm, win, win->screen->animtimer, false); + if (screen->animtimer) + WM_event_timer_sleep(wm, win, screen->animtimer, false); } if (G.debug & G_DEBUG_EVENTS) { printf("%s: set screen\n", __func__); } - win->screen->do_refresh = false; + screen->do_refresh = false; + /* prevent multiwin errors */ + screen->winid = win->winid; - win->screen->context = ed_screen_context; + screen->context = ed_screen_context; } /* file read, set all screens, ... */ @@ -962,10 +961,10 @@ void ED_screens_initialize(wmWindowManager *wm) wmWindow *win; for (win = wm->windows.first; win; win = win->next) { - - if (win->screen == NULL) - win->screen = G.main->screen.first; - + if (WM_window_get_active_workspace(win) == NULL) { + WM_window_set_active_workspace(win, G.main->workspaces.first); + } + ED_screen_refresh(wm, win); } } @@ -1053,7 +1052,7 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) /* mark it available for use for other windows */ screen->winid = 0; - if (prevwin->screen->temp == 0) { + if (!WM_window_is_temp_screen(prevwin)) { /* use previous window if possible */ CTX_wm_window_set(C, prevwin); } @@ -1069,13 +1068,14 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) /* case when on area-edge or in azones, or outside window */ static void screen_cursor_set(wmWindow *win, wmEvent *event) { + const bScreen *screen = WM_window_get_active_screen(win); const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); AZone *az = NULL; ScrArea *sa; - for (sa = win->screen->areabase.first; sa; sa = sa->next) + for (sa = screen->areabase.first; sa; sa = sa->next) if ((az = is_in_area_actionzone(sa, &event->x))) break; @@ -1090,7 +1090,7 @@ static void screen_cursor_set(wmWindow *win, wmEvent *event) } } else { - ScrEdge *actedge = screen_find_active_scredge(win->screen, winsize_x, winsize_y, event->x, event->y); + ScrEdge *actedge = screen_find_active_scredge(screen, winsize_x, winsize_y, event->x, event->y); if (actedge) { if (scredge_is_horizontal(actedge)) @@ -1109,9 +1109,9 @@ static void screen_cursor_set(wmWindow *win, wmEvent *event) void ED_screen_set_subwinactive(bContext *C, wmEvent *event) { wmWindow *win = CTX_wm_window(C); - - if (win->screen) { - bScreen *scr = win->screen; + bScreen *scr = WM_window_get_active_screen(win); + + if (scr) { ScrArea *sa; ARegion *ar; int oldswin = scr->subwinactive; @@ -1164,7 +1164,7 @@ void ED_screen_set_subwinactive(bContext *C, wmEvent *event) /* this used to be a notifier, but needs to be done immediate * because it can undo setting the right button as active due * to delayed notifier handling */ - UI_screen_free_active_but(C, win->screen); + UI_screen_free_active_but(C, scr); } else region_cursor_set(win, scr->subwinactive, false); @@ -1192,177 +1192,120 @@ int ED_screen_area_active(const bContext *C) return 0; } + +/* -------------------------------------------------------------------- */ +/* Screen changing */ + +static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen) +{ + for (bScreen *screen_iter = bmain->screen.first; screen_iter; screen_iter = screen_iter->id.next) { + ScrArea *sa = screen_iter->areabase.first; + if (sa->full == screen) { + return screen_iter; + } + } + + return screen; +} + /** - * operator call, WM + Window + screen already existed before - * - * \warning Do NOT call in area/region queues! - * \returns success. + * \return the screen to activate. + * \warning The returned screen may not always equal \a screen_new! */ -bool ED_screen_set(bContext *C, bScreen *sc) +bScreen *screen_change_prepare(bScreen *screen_old, bScreen *screen_new, Main *bmain, bContext *C, wmWindow *win) { - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - bScreen *oldscreen = CTX_wm_screen(C); - /* validate screen, it's called with notifier reference */ - if (BLI_findindex(&bmain->screen, sc) == -1) { - return true; + if (BLI_findindex(&bmain->screen, screen_new) == -1) { + return NULL; } - if (ELEM(sc->state, SCREENMAXIMIZED, SCREENFULL)) { - /* find associated full */ - bScreen *sc1; - for (sc1 = bmain->screen.first; sc1; sc1 = sc1->id.next) { - ScrArea *sa = sc1->areabase.first; - if (sa->full == sc) { - sc = sc1; - break; - } - } + if (ELEM(screen_new->state, SCREENMAXIMIZED, SCREENFULL)) { + screen_new = screen_fullscreen_find_associated_normal_screen(bmain, screen_new); } /* check for valid winid */ - if (sc->winid != 0 && sc->winid != win->winid) { - return false; + if (!(screen_new->winid == 0 || screen_new->winid == win->winid)) { + return NULL; } - - if (oldscreen != sc) { - wmTimer *wt = oldscreen->animtimer; - ScrArea *sa; - Scene *oldscene = oldscreen->scene; + + if (screen_old != screen_new) { + wmTimer *wt = screen_old->animtimer; /* remove handlers referencing areas in old screen */ - for (sa = oldscreen->areabase.first; sa; sa = sa->next) { + for (ScrArea *sa = screen_old->areabase.first; sa; sa = sa->next) { WM_event_remove_area_handler(&win->modalhandlers, sa); } /* we put timer to sleep, so screen_exit has to think there's no timer */ - oldscreen->animtimer = NULL; + screen_old->animtimer = NULL; if (wt) { - WM_event_timer_sleep(wm, win, wt, true); + WM_event_timer_sleep(CTX_wm_manager(C), win, wt, true); } - - ED_screen_exit(C, win, oldscreen); + ED_screen_exit(C, win, screen_old); /* Same scene, "transfer" playback to new screen. */ if (wt) { - if (oldscene == sc->scene) { - sc->animtimer = wt; - } - /* Else, stop playback. */ - else { - oldscreen->animtimer = wt; - ED_screen_animation_play(C, 0, 0); - } + screen_new->animtimer = wt; } - win->screen = sc; - CTX_wm_window_set(C, win); // stores C->wm.screen... hrmf - - /* prevent multiwin errors */ - sc->winid = win->winid; - - ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C)); - WM_event_add_notifier(C, NC_WINDOW, NULL); - WM_event_add_notifier(C, NC_SCREEN | ND_SCREENSET, sc); - - /* makes button hilites work */ - WM_event_add_mousemove(C); - - /* Needed to make sure all the derivedMeshes are - * up-to-date before viewport starts acquiring this. - * - * This is needed in cases when, for example, boolean - * modifier uses operant from invisible layer. - * Without this trick boolean wouldn't apply correct. - * - * Quite the same happens when setting screen's scene, - * so perhaps this is in fact correct thing to do. - */ - if (oldscene != sc->scene) { - BKE_scene_set_background(bmain, sc->scene); - } - - /* Always do visible update since it's possible new screen will - * have different layers visible in 3D view-ports. - * This is possible because of view3d.lock_camera_and_layers option. - */ - DAG_on_visible_update(bmain, false); + return screen_new; } - return true; + return NULL; } -static bool ed_screen_used(wmWindowManager *wm, bScreen *sc) +void screen_changed_update(bContext *C, wmWindow *win, bScreen *sc) { - wmWindow *win; + Scene *scene = WM_window_get_active_scene(win); + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, sc); - for (win = wm->windows.first; win; win = win->next) { - if (win->screen == sc) { - return true; - } + CTX_wm_window_set(C, win); /* stores C->wm.screen... hrmf */ - if (ELEM(win->screen->state, SCREENMAXIMIZED, SCREENFULL)) { - ScrArea *sa = win->screen->areabase.first; - if (sa->full == sc) { - return true; - } - } - } + ED_screen_refresh(CTX_wm_manager(C), win); - return false; + BKE_screen_view3d_scene_sync(sc, scene); /* sync new screen with scene data */ + WM_event_add_notifier(C, NC_WINDOW, NULL); + WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTSET, layout); + + /* makes button hilites work */ + WM_event_add_mousemove(C); } -/* only call outside of area/region loops */ -bool ED_screen_delete(bContext *C, bScreen *sc) + +/** + * \brief Change the active screen. + * + * Operator call, WM + Window + screen already existed before + * + * \warning Do NOT call in area/region queues! + * \returns if screen changing was successful. + */ +bool ED_screen_change(bContext *C, bScreen *sc) { Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); - bScreen *newsc; - - /* don't allow deleting temp fullscreens for now */ - if (ELEM(sc->state, SCREENMAXIMIZED, SCREENFULL)) { - return false; - } + bScreen *screen_old = CTX_wm_screen(C); + bScreen *screen_new = screen_change_prepare(screen_old, sc, bmain, C, win); - /* screen can only be in use by one window at a time, so as - * long as we are able to find a screen that is unused, we - * can safely assume ours is not in use anywhere an delete it */ + if (screen_new) { + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WM_window_set_active_screen(win, workspace, sc); + screen_changed_update(C, win, screen_new); - for (newsc = sc->id.prev; newsc; newsc = newsc->id.prev) - if (!ed_screen_used(wm, newsc) && !newsc->temp) - break; - - if (!newsc) { - for (newsc = sc->id.next; newsc; newsc = newsc->id.next) - if (!ed_screen_used(wm, newsc) && !newsc->temp) - break; - } - - if (!newsc) { - return false; - } - - ED_screen_set(C, newsc); - - if (win->screen != sc) { - BKE_libblock_free(bmain, sc); return true; } - else { - return false; - } + + return false; } -static void ed_screen_set_3dview_camera(Scene *scene, bScreen *sc, ScrArea *sa, View3D *v3d) +static void screen_set_3dview_camera(Scene *scene, ScrArea *sa, View3D *v3d) { /* fix any cameras that are used in the 3d view but not in the scene */ BKE_screen_view3d_sync(v3d, scene); if (!v3d->camera || !BKE_scene_base_find(scene, v3d->camera)) { - v3d->camera = BKE_scene_camera_find(sc->scene); + v3d->camera = BKE_scene_camera_find(scene); // XXX if (sc == curscreen) handle_view3d_lock(); if (!v3d->camera) { ARegion *ar; @@ -1386,90 +1329,16 @@ static void ed_screen_set_3dview_camera(Scene *scene, bScreen *sc, ScrArea *sa, } } -/* only call outside of area/region loops */ -void ED_screen_set_scene(bContext *C, bScreen *screen, Scene *scene) +void ED_screen_update_after_scene_change(const bScreen *screen, Scene *scene_new) { - Main *bmain = CTX_data_main(C); - bScreen *sc; - - if (screen == NULL) - return; - - if (ed_screen_used(CTX_wm_manager(C), screen)) - ED_object_editmode_exit(C, EM_FREEDATA | EM_DO_UNDO); - - for (sc = bmain->screen.first; sc; sc = sc->id.next) { - if ((U.flag & USER_SCENEGLOBAL) || sc == screen) { - - if (scene != sc->scene) { - /* all areas endlocalview */ - // XXX ScrArea *sa = sc->areabase.first; - // while (sa) { - // endlocalview(sa); - // sa = sa->next; - // } - sc->scene = scene; - } - - } - } - - // copy_view3d_lock(0); /* space.c */ - - /* are there cameras in the views that are not in the scene? */ - for (sc = bmain->screen.first; sc; sc = sc->id.next) { - if ((U.flag & USER_SCENEGLOBAL) || sc == screen) { - ScrArea *sa = sc->areabase.first; - while (sa) { - SpaceLink *sl = sa->spacedata.first; - while (sl) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *) sl; - ed_screen_set_3dview_camera(scene, sc, sa, v3d); - - } - sl = sl->next; - } - sa = sa->next; + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + screen_set_3dview_camera(scene_new, sa, v3d); } } } - - CTX_data_scene_set(C, scene); - BKE_scene_set_background(bmain, scene); - DAG_on_visible_update(bmain, false); - - ED_render_engine_changed(bmain); - ED_update_for_newframe(bmain, scene, 1); - - /* complete redraw */ - WM_event_add_notifier(C, NC_WINDOW, NULL); - -} - -/** - * \note Only call outside of area/region loops - * \return true if successful - */ -bool ED_screen_delete_scene(bContext *C, Scene *scene) -{ - Main *bmain = CTX_data_main(C); - Scene *newscene; - - if (scene->id.prev) - newscene = scene->id.prev; - else if (scene->id.next) - newscene = scene->id.next; - else - return false; - - ED_screen_set_scene(C, CTX_wm_screen(C), newscene); - - BKE_libblock_remap(bmain, scene, newscene, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); - - BKE_libblock_free(bmain, scene); - - return true; } ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) @@ -1576,6 +1445,7 @@ void ED_screen_full_restore(bContext *C, ScrArea *sa) ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const short state) { wmWindowManager *wm = CTX_wm_manager(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); bScreen *sc, *oldscreen; ARegion *ar; @@ -1597,11 +1467,12 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s } if (sa && sa->full) { + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); /* restoring back to SCREENNORMAL */ ScrArea *old; sc = sa->full; /* the old screen to restore */ - oldscreen = win->screen; /* the one disappearing */ + oldscreen = WM_window_get_active_screen(win); /* the one disappearing */ sc->state = SCREENNORMAL; @@ -1629,10 +1500,9 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s sc->animtimer = oldscreen->animtimer; oldscreen->animtimer = NULL; - ED_screen_set(C, sc); + ED_screen_change(C, sc); - BKE_screen_free(oldscreen); - BKE_libblock_free(CTX_data_main(C), oldscreen); + BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old); /* After we've restored back to SCREENNORMAL, we have to wait with * screen handling as it uses the area coords which aren't updated yet. @@ -1642,14 +1512,18 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s } else { /* change from SCREENNORMAL to new state */ + WorkSpaceLayout *layout_new; ScrArea *newa; char newname[MAX_ID_NAME - 2]; - oldscreen = win->screen; + oldscreen = WM_window_get_active_screen(win); oldscreen->state = state; BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal"); - sc = ED_screen_add(win, oldscreen->scene, newname); + + layout_new = ED_workspace_layout_add(workspace, win, newname); + + sc = BKE_workspace_layout_screen_get(layout_new); sc->state = state; sc->redraws_flag = oldscreen->redraws_flag; sc->temp = oldscreen->temp; @@ -1704,7 +1578,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s BLI_assert(false); } - ED_screen_set(C, sc); + ED_screen_change(C, sc); } /* XXX bad code: setscreen() ends with first area active. fullscreen render assumes this too */ @@ -1850,7 +1724,7 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) scene->camera = camera; /* are there cameras in the views that are not in the scene? */ for (sc = bmain->screen.first; sc; sc = sc->id.next) { - BKE_screen_view3d_scene_sync(sc); + BKE_screen_view3d_scene_sync(sc, scene); } } #endif @@ -1879,11 +1753,10 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) /* * return true if any active area requires to see in 3D */ -bool ED_screen_stereo3d_required(bScreen *screen) +bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene) { ScrArea *sa; - Scene *sce = screen->scene; - const bool is_multiview = (sce->r.scemode & R_MULTIVIEW) != 0; + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; for (sa = screen->areabase.first; sa; sa = sa->next) { switch (sa->spacetype) { @@ -1958,3 +1831,19 @@ bool ED_screen_stereo3d_required(bScreen *screen) return false; } + +/** + * Find the scene displayed in \a screen. + * \note Assumes \a screen to be visible/active! + */ +Scene *ED_screen_scene_find(const bScreen *screen, const wmWindowManager *wm) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_screen(win) == screen) { + return WM_window_get_active_scene(win); + } + } + + BLI_assert(0); + return NULL; +} diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 49c0869fcfb..4eb68d01df7 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -32,6 +32,7 @@ #define __SCREEN_INTERN_H__ struct bContextDataResult; +struct Main; /* internal exports only */ @@ -45,6 +46,11 @@ void ED_area_data_swap(ScrArea *sa1, ScrArea *sa2); void region_toggle_hidden(struct bContext *C, ARegion *ar, const bool do_fade); /* screen_edit.c */ +bScreen *screen_add(const char *name, const int winsize_x, const int winsize_y); +void screen_data_copy(bScreen *to, bScreen *from); +void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new); +void screen_changed_update(struct bContext *C, wmWindow *win, bScreen *sc); +bScreen *screen_change_prepare(bScreen *screen_old, bScreen *screen_new, struct Main *bmain, struct bContext *C, wmWindow *win); ScrEdge *screen_findedge(bScreen *sc, ScrVert *v1, ScrVert *v2); ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge); int screen_area_join(struct bContext *C, bScreen *scr, ScrArea *sa1, ScrArea *sa2); @@ -56,7 +62,7 @@ void removedouble_scrverts(bScreen *sc); void removedouble_scredges(bScreen *sc); void removenotused_scredges(bScreen *sc); bool scredge_is_horizontal(ScrEdge *se); -ScrEdge *screen_find_active_scredge(bScreen *sc, +ScrEdge *screen_find_active_scredge(const bScreen *sc, const int winsize_x, const int winsize_y, const int mx, const int my); @@ -74,5 +80,8 @@ void SCREEN_OT_screencast(struct wmOperatorType *ot); /* screen_ops.c */ void region_blend_start(struct bContext *C, struct ScrArea *sa, struct ARegion *ar); +/* workspace_layout_edit.c */ +bool workspace_layout_set_poll(const struct WorkSpaceLayout *layout); + #endif /* __SCREEN_INTERN_H__ */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 69d13beee48..5cd68f5121c 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -62,6 +62,7 @@ #include "BKE_editmesh.h" #include "BKE_sound.h" #include "BKE_mask.h" +#include "BKE_workspace.h" #include "WM_api.h" #include "WM_types.h" @@ -978,13 +979,17 @@ static void SCREEN_OT_area_swap(wmOperatorType *ot) /* operator callback */ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - wmWindow *newwin, *win; - bScreen *newsc, *sc; + wmWindow *newwin, *win = CTX_wm_window(C); + Scene *scene; + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); + WorkSpaceLayout *layout_new; + bScreen *newsc; ScrArea *sa; rcti rect; win = CTX_wm_window(C); - sc = CTX_wm_screen(C); + scene = CTX_data_scene(C); sa = CTX_wm_area(C); /* XXX hrmf! */ @@ -1010,11 +1015,15 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) } *newwin->stereo3d_format = *win->stereo3d_format; - + + newwin->scene = scene; + + WM_window_set_active_workspace(newwin, workspace); /* allocs new screen and adds to newly created window, using window size */ - newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2); - newwin->screen = newsc; - + layout_new = ED_workspace_layout_add(workspace, newwin, BKE_workspace_layout_name_get(layout_old)); + newsc = BKE_workspace_layout_screen_get(layout_new); + WM_window_set_active_layout(newwin, workspace, layout_new); + /* copy area to new screen */ ED_area_data_copy((ScrArea *)newsc->areabase.first, sa, true); @@ -1714,7 +1723,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) } } - CTX_wm_window(C)->screen->do_draw = true; + CTX_wm_screen(C)->do_draw = true; } @@ -2084,12 +2093,11 @@ static void areas_do_frame_follow(bContext *C, bool middle) bScreen *scr = CTX_wm_screen(C); Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *window; - for (window = wm->windows.first; window; window = window->next) { - ScrArea *sa; - for (sa = window->screen->areabase.first; sa; sa = sa->next) { - ARegion *ar; - for (ar = sa->regionbase.first; ar; ar = ar->next) { + for (wmWindow *window = wm->windows.first; window; window = window->next) { + const bScreen *screen = WM_window_get_active_screen(window); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { /* do follow here if editor type supports it */ if ((scr->redraws_flag & TIME_FOLLOW)) { if ((ar->regiontype == RGN_TYPE_WINDOW && @@ -2385,64 +2393,16 @@ static void SCREEN_OT_marker_jump(wmOperatorType *ot) /* ************** switch screen operator ***************************** */ -static bool screen_set_is_ok(bScreen *screen, bScreen *screen_prev) -{ - return ((screen->winid == 0) && - /* in typical usage these should have a nonzero winid - * (all temp screens should be used, or closed & freed). */ - (screen->temp == false) && - (screen->state == SCREENNORMAL) && - (screen != screen_prev) && - (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT))); -} - /* function to be called outside UI context, or for redo */ static int screen_set_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - bScreen *screen = CTX_wm_screen(C); - bScreen *screen_prev = screen; - - ScrArea *sa = CTX_wm_area(C); - int tot = BLI_listbase_count(&bmain->screen); + WorkSpace *workspace = CTX_wm_workspace(C); int delta = RNA_int_get(op->ptr, "delta"); - - /* temp screens are for userpref or render display */ - if (screen->temp || (sa && sa->full && sa->full->temp)) { - return OPERATOR_CANCELLED; - } - - if (delta == 1) { - while (tot--) { - screen = screen->id.next; - if (screen == NULL) screen = bmain->screen.first; - if (screen_set_is_ok(screen, screen_prev)) { - break; - } - } - } - else if (delta == -1) { - while (tot--) { - screen = screen->id.prev; - if (screen == NULL) screen = bmain->screen.last; - if (screen_set_is_ok(screen, screen_prev)) { - break; - } - } - } - else { - screen = NULL; - } - - if (screen && screen_prev != screen) { - /* return to previous state before switching screens */ - if (sa && sa->full) { - ED_screen_full_restore(C, sa); /* may free 'screen_prev' */ - } - - ED_screen_set(C, screen); + + if (ED_workspace_layout_cycle(workspace, delta, C)) { return OPERATOR_FINISHED; } + return OPERATOR_CANCELLED; } @@ -3297,7 +3257,7 @@ static int header_toggle_menus_exec(bContext *C, wmOperator *UNUSED(op)) sa->flag = sa->flag ^ HEADER_NO_PULLDOWN; ED_area_tag_redraw(sa); - WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -3594,7 +3554,9 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv ED_update_for_newframe(bmain, scene, 1); for (window = wm->windows.first; window; window = window->next) { - for (sa = window->screen->areabase.first; sa; sa = sa->next) { + const bScreen *win_screen = WM_window_get_active_screen(window); + + for (sa = win_screen->areabase.first; sa; sa = sa->next) { ARegion *ar; for (ar = sa->regionbase.first; ar; ar = ar->next) { bool redraw = false; @@ -3668,11 +3630,11 @@ static void SCREEN_OT_animation_step(wmOperatorType *ot) /* find window that owns the animation timer */ bScreen *ED_screen_animation_playing(const wmWindowManager *wm) { - wmWindow *win; + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); - for (win = wm->windows.first; win; win = win->next) { - if (win->screen->animtimer || win->screen->scrubbing) { - return win->screen; + if (screen->animtimer || screen->scrubbing) { + return screen; } } @@ -3681,11 +3643,11 @@ bScreen *ED_screen_animation_playing(const wmWindowManager *wm) bScreen *ED_screen_animation_no_scrub(const wmWindowManager *wm) { - wmWindow *win; + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); - for (win = wm->windows.first; win; win = win->next) { - if (win->screen->animtimer) { - return win->screen; + if (screen->animtimer) { + return screen; } } @@ -3919,11 +3881,13 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) static int screen_new_exec(bContext *C, wmOperator *UNUSED(op)) { wmWindow *win = CTX_wm_window(C); - bScreen *sc = CTX_wm_screen(C); - - sc = ED_screen_duplicate(win, sc); - WM_event_add_notifier(C, NC_SCREEN | ND_SCREENBROWSE, sc); - + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WorkSpaceLayout *layout_old = BKE_workspace_active_layout_get(win->workspace_hook); + WorkSpaceLayout *layout_new; + + layout_new = ED_workspace_layout_duplicate(workspace, layout_old, win); + WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTBROWSE, layout_new); + return OPERATOR_FINISHED; } @@ -3944,9 +3908,11 @@ static void SCREEN_OT_new(wmOperatorType *ot) static int screen_delete_exec(bContext *C, wmOperator *UNUSED(op)) { bScreen *sc = CTX_wm_screen(C); - - WM_event_add_notifier(C, NC_SCREEN | ND_SCREENDELETE, sc); - + WorkSpace *workspace = CTX_wm_workspace(C); + WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, sc); + + WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTDELETE, layout); + return OPERATOR_FINISHED; } @@ -3961,95 +3927,6 @@ static void SCREEN_OT_delete(wmOperatorType *ot) ot->exec = screen_delete_exec; } -/********************* new scene operator *********************/ - -static int scene_new_exec(bContext *C, wmOperator *op) -{ - Scene *newscene, *scene = CTX_data_scene(C); - Main *bmain = CTX_data_main(C); - int type = RNA_enum_get(op->ptr, "type"); - - if (type == SCE_COPY_NEW) { - newscene = BKE_scene_add(bmain, DATA_("Scene")); - } - else { /* different kinds of copying */ - newscene = BKE_scene_copy(bmain, scene, type); - - /* these can't be handled in blenkernel currently, so do them here */ - if (type == SCE_COPY_LINK_DATA) { - ED_object_single_users(bmain, newscene, false, true); - } - else if (type == SCE_COPY_FULL) { - ED_editors_flush_edits(C, false); - ED_object_single_users(bmain, newscene, true, true); - } - } - - ED_screen_set_scene(C, CTX_wm_screen(C), newscene); - - WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, newscene); - - return OPERATOR_FINISHED; -} - -static void SCENE_OT_new(wmOperatorType *ot) -{ - static EnumPropertyItem type_items[] = { - {SCE_COPY_NEW, "NEW", 0, "New", "Add new scene"}, - {SCE_COPY_EMPTY, "EMPTY", 0, "Copy Settings", "Make a copy without any objects"}, - {SCE_COPY_LINK_OB, "LINK_OBJECTS", 0, "Link Objects", "Link to the objects from the current scene"}, - {SCE_COPY_LINK_DATA, "LINK_OBJECT_DATA", 0, "Link Object Data", "Copy objects linked to data from the current scene"}, - {SCE_COPY_FULL, "FULL_COPY", 0, "Full Copy", "Make a full copy of the current scene"}, - {0, NULL, 0, NULL, NULL}}; - - /* identifiers */ - ot->name = "New Scene"; - ot->description = "Add new scene by type"; - ot->idname = "SCENE_OT_new"; - - /* api callbacks */ - ot->exec = scene_new_exec; - ot->invoke = WM_menu_invoke; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", ""); -} - -/********************* delete scene operator *********************/ - -static int scene_delete_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *scene = CTX_data_scene(C); - - if (ED_screen_delete_scene(C, scene) == false) { - return OPERATOR_CANCELLED; - } - - if (G.debug & G_DEBUG) - printf("scene delete %p\n", scene); - - WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene); - - return OPERATOR_FINISHED; -} - -static void SCENE_OT_delete(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Delete Scene"; - ot->description = "Delete active scene"; - ot->idname = "SCENE_OT_delete"; - - /* api callbacks */ - ot->exec = scene_delete_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /* ***************** region alpha blending ***************** */ /* implementation note: a disappearing region needs at least 1 last draw with 100% backbuffer @@ -4321,13 +4198,11 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_animation_step); WM_operatortype_append(SCREEN_OT_animation_play); WM_operatortype_append(SCREEN_OT_animation_cancel); - + /* new/delete */ WM_operatortype_append(SCREEN_OT_new); WM_operatortype_append(SCREEN_OT_delete); - WM_operatortype_append(SCENE_OT_new); - WM_operatortype_append(SCENE_OT_delete); - + /* tools shared by more space types */ WM_operatortype_append(ED_OT_undo); WM_operatortype_append(ED_OT_undo_push); diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c new file mode 100644 index 00000000000..3782d583003 --- /dev/null +++ b/source/blender/editors/screen/workspace_edit.c @@ -0,0 +1,426 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/screen/workspace_edit.c + * \ingroup edscr + */ + +#include <stdlib.h> +#include <string.h> + +#include "BLI_utildefines.h" +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_appdir.h" +#include "BKE_blendfile.h" +#include "BKE_context.h" +#include "BKE_idcode.h" +#include "BKE_main.h" +#include "BKE_library.h" +#include "BKE_report.h" +#include "BKE_screen.h" +#include "BKE_workspace.h" + +#include "BLO_readfile.h" + +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" + +#include "ED_object.h" +#include "ED_screen.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "screen_intern.h" + + +/** \name Workspace API + * + * \brief API for managing workspaces and their data. + * \{ */ + +WorkSpace *ED_workspace_add( + Main *bmain, const char *name, SceneLayer *act_render_layer) +{ + WorkSpace *workspace = BKE_workspace_add(bmain, name); + +#ifdef USE_WORKSPACE_MODE + BKE_workspace_object_mode_set(workspace, OB_MODE_OBJECT); +#endif + + BKE_workspace_render_layer_set(workspace, act_render_layer); + + return workspace; +} + +#ifdef USE_WORKSPACE_MODE +/** + * Changes the object mode (if needed) to the one set in \a workspace_new. + * Object mode is still stored on object level. In future it should all be workspace level instead. + */ +static void workspace_change_update_mode( + const WorkSpace *workspace_old, const WorkSpace *workspace_new, + bContext *C, Object *ob_act, ReportList *reports) +{ + ObjectMode mode_old = BKE_workspace_object_mode_get(workspace_old); + ObjectMode mode_new = BKE_workspace_object_mode_get(workspace_new); + + if (mode_old != mode_new) { + ED_object_mode_compat_set(C, ob_act, mode_new, reports); + ED_object_toggle_modes(C, mode_new); + } +} +#endif + +static void workspace_change_update_render_layer( + WorkSpace *workspace_new, const WorkSpace *workspace_old) +{ + if (!BKE_workspace_render_layer_get(workspace_new)) { + BKE_workspace_render_layer_set(workspace_new, BKE_workspace_render_layer_get(workspace_old)); + } +} + +static void workspace_change_update( + WorkSpace *workspace_new, const WorkSpace *workspace_old, + bContext *C, wmWindowManager *wm) +{ + /* needs to be done before changing mode! (to ensure right context) */ + workspace_change_update_render_layer(workspace_new, workspace_old); +#ifdef USE_WORKSPACE_MODE + workspace_change_update_mode(workspace_old, workspace_new, C, CTX_data_active_object(C), &wm->reports); +#else + UNUSED_VARS(C, wm); +#endif +} + +static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) +{ + /* return false to stop the iterator if we've found a layout that can be activated */ + return workspace_layout_set_poll(layout) ? false : true; +} + +static WorkSpaceLayout *workspace_change_get_new_layout( + WorkSpace *workspace_new, wmWindow *win) +{ + /* ED_workspace_duplicate may have stored a layout to activate once the workspace gets activated. */ + WorkSpaceLayout *layout_new; + bScreen *screen_new; + + if (win->workspace_hook->temp_workspace_store) { + layout_new = win->workspace_hook->temp_layout_store; + } + else { + layout_new = BKE_workspace_hook_layout_for_workspace_get(win->workspace_hook, workspace_new); + if (!layout_new) { + layout_new = BKE_workspace_layouts_get(workspace_new)->first; + } + } + screen_new = BKE_workspace_layout_screen_get(layout_new); + + if (screen_new->winid) { + /* screen is already used, try to find a free one */ + WorkSpaceLayout *layout_temp = BKE_workspace_layout_iter_circular( + workspace_new, layout_new, workspace_change_find_new_layout_cb, + NULL, false); + if (!layout_temp) { + /* fallback solution: duplicate layout */ + layout_temp = ED_workspace_layout_duplicate(workspace_new, layout_new, win); + } + layout_new = layout_temp; + } + + return layout_new; +} + +/** + * \brief Change the active workspace. + * + * Operator call, WM + Window + screen already existed before + * Pretty similar to #ED_screen_change since changing workspace also changes screen. + * + * \warning Do NOT call in area/region queues! + * \returns if workspace changing was successful. + */ +bool ED_workspace_change( + WorkSpace *workspace_new, bContext *C, wmWindowManager *wm, wmWindow *win) +{ + Main *bmain = CTX_data_main(C); + WorkSpace *workspace_old = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout_new = workspace_change_get_new_layout(workspace_new, win); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); + bScreen *screen_old = BKE_workspace_active_screen_get(win->workspace_hook); + + win->workspace_hook->temp_layout_store = NULL; + if (workspace_old == workspace_new) { + /* Could also return true, everything that needs to be done was done (nothing :P), but nothing changed */ + return false; + } + + screen_new = screen_change_prepare(screen_old, screen_new, bmain, C, win); + BLI_assert(BKE_workspace_layout_screen_get(layout_new) == screen_new); + + if (screen_new) { + WM_window_set_active_layout(win, workspace_new, layout_new); + WM_window_set_active_workspace(win, workspace_new); + + /* update screen *after* changing workspace - which also causes the actual screen change */ + screen_changed_update(C, win, screen_new); + workspace_change_update(workspace_new, workspace_old, C, wm); + + BLI_assert(BKE_workspace_render_layer_get(workspace_new) != NULL); + BLI_assert(CTX_wm_workspace(C) == workspace_new); + + return true; + } + + return false; +} + +/** + * Duplicate a workspace including its layouts. Does not activate the workspace, but + * it stores the screen-layout to be activated (BKE_workspace_temp_layout_store) + */ +WorkSpace *ED_workspace_duplicate( + WorkSpace *workspace_old, Main *bmain, wmWindow *win) +{ + WorkSpaceLayout *layout_active_old = BKE_workspace_active_layout_get(win->workspace_hook); + ListBase *layouts_old = BKE_workspace_layouts_get(workspace_old); + WorkSpace *workspace_new = ED_workspace_add( + bmain, workspace_old->id.name + 2, + BKE_workspace_render_layer_get(workspace_old)); + +#ifdef USE_WORKSPACE_MODE + BKE_workspace_object_mode_set(workspace_new, BKE_workspace_object_mode_get(workspace_old)); +#endif + + for (WorkSpaceLayout *layout_old = layouts_old->first; layout_old; layout_old = layout_old->next) { + WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(workspace_new, layout_old, win); + + if (layout_active_old == layout_old) { + win->workspace_hook->temp_layout_store = layout_new; + } + } + + return workspace_new; +} + +/** + * \return if succeeded. + */ +bool ED_workspace_delete( + WorkSpace *workspace, Main *bmain, bContext *C, wmWindowManager *wm, wmWindow *win) +{ + ID *workspace_id = (ID *)workspace; + + if (BLI_listbase_is_single(&bmain->workspaces)) { + return false; + } + + if (WM_window_get_active_workspace(win) == workspace) { + WorkSpace *prev = workspace_id->prev; + WorkSpace *next = workspace_id->next; + + ED_workspace_change((prev != NULL) ? prev : next, C, wm, win); + } + BKE_libblock_free(bmain, workspace_id); + + return true; +} + +/** + * Some editor data may need to be synced with scene data (3D View camera and layers). + * This function ensures data is synced for editors in active layout of \a workspace. + */ +void ED_workspace_scene_data_sync( + WorkSpaceInstanceHook *hook, Scene *scene) +{ + bScreen *screen = BKE_workspace_active_screen_get(hook); + BKE_screen_view3d_scene_sync(screen, scene); +} + +/** \} Workspace API */ + + +/** \name Workspace Operators + * + * \{ */ + +static int workspace_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = ED_workspace_duplicate(WM_window_get_active_workspace(win), bmain, win); + + WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_SET, workspace); + + return OPERATOR_FINISHED; +} + +static void WORKSPACE_OT_workspace_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Workspace"; + ot->description = "Add a new workspace"; + ot->idname = "WORKSPACE_OT_workspace_duplicate"; + + /* api callbacks */ + ot->exec = workspace_new_exec; + ot->poll = WM_operator_winactive; +} + +static int workspace_delete_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + + ED_workspace_delete(WM_window_get_active_workspace(win), bmain, C, wm, win); + + return OPERATOR_FINISHED; +} + +static void WORKSPACE_OT_workspace_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Workspace"; + ot->description = "Delete the active workspace"; + ot->idname = "WORKSPACE_OT_workspace_delete"; + + /* api callbacks */ + ot->exec = workspace_delete_exec; +} + +static void workspace_config_file_path_from_folder_id( + const Main *bmain, int folder_id, char *r_path) +{ + const char *app_template = U.app_template[0] ? U.app_template : NULL; + const char * const cfgdir = BKE_appdir_folder_id(folder_id, app_template); + + if (cfgdir) { + BLI_make_file_string(bmain->name, r_path, cfgdir, BLENDER_WORKSPACES_FILE); + } + else { + r_path[0] = '\0'; + } +} + +ATTR_NONNULL(1) +static WorkspaceConfigFileData *workspace_config_file_read( + const Main *bmain, ReportList *reports) +{ + char workspace_config_path[FILE_MAX]; + bool has_path = false; + + workspace_config_file_path_from_folder_id(bmain, BLENDER_USER_CONFIG, workspace_config_path); + if (BLI_exists(workspace_config_path)) { + has_path = true; + } + else { + workspace_config_file_path_from_folder_id(bmain, BLENDER_DATAFILES, workspace_config_path); + if (BLI_exists(workspace_config_path)) { + has_path = true; + } + } + + return has_path ? BKE_blendfile_workspace_config_read(workspace_config_path, reports) : NULL; +} + +static void workspace_append_button( + uiLayout *layout, wmOperatorType *ot_append, const WorkSpace *workspace, const Main *from_main) +{ + const ID *id = (ID *)workspace; + PointerRNA opptr; + char lib_path[FILE_MAX_LIBEXTRA]; + + BLI_path_join( + lib_path, sizeof(lib_path), from_main->name, BKE_idcode_to_name(GS(id->name)), NULL); + + BLI_assert(STREQ(ot_append->idname, "WM_OT_append")); + opptr = uiItemFullO_ptr( + layout, ot_append, workspace->id.name + 2, ICON_NONE, NULL, + WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); + RNA_string_set(&opptr, "directory", lib_path); + RNA_string_set(&opptr, "filename", id->name + 2); +} + +ATTR_NONNULL(1, 2) +static void workspace_config_file_append_buttons( + uiLayout *layout, const Main *bmain, ReportList *reports) +{ + WorkspaceConfigFileData *workspace_config = workspace_config_file_read(bmain, reports); + + if (workspace_config) { + wmOperatorType *ot_append = WM_operatortype_find("WM_OT_append", true); + + for (WorkSpace *workspace = workspace_config->workspaces.first; workspace; workspace = workspace->id.next) { + workspace_append_button(layout, ot_append, workspace, workspace_config->main); + } + + BKE_blendfile_workspace_config_data_free(workspace_config); + } +} + +static int workspace_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + const Main *bmain = CTX_data_main(C); + + uiPopupMenu *pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + + uiItemO(layout, "Duplicate Current", ICON_NONE, "WORKSPACE_OT_workspace_duplicate"); + uiItemS(layout); + workspace_config_file_append_buttons(layout, bmain, op->reports); + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static void WORKSPACE_OT_workspace_add_menu(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Workspace"; + ot->description = "Add a new workspace by duplicating the current one or appending one " + "from the user configuration"; + ot->idname = "WORKSPACE_OT_workspace_add_menu"; + + /* api callbacks */ + ot->invoke = workspace_add_invoke; +} + +void ED_operatortypes_workspace(void) +{ + WM_operatortype_append(WORKSPACE_OT_workspace_duplicate); + WM_operatortype_append(WORKSPACE_OT_workspace_delete); + WM_operatortype_append(WORKSPACE_OT_workspace_add_menu); +} + +/** \} Workspace Operators */ diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c new file mode 100644 index 00000000000..e4dd841f833 --- /dev/null +++ b/source/blender/editors/screen/workspace_layout_edit.c @@ -0,0 +1,198 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/screen/workspace_layout_edit.c + * \ingroup edscr + */ + +#include <stdlib.h> + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_screen.h" +#include "BKE_workspace.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" + +#include "DNA_screen_types.h" +#include "DNA_workspace_types.h" + +#include "ED_screen.h" + +#include "WM_api.h" + +#include "screen_intern.h" + + +/** + * Empty screen, with 1 dummy area without spacedata. Uses window size. + */ +WorkSpaceLayout *ED_workspace_layout_add( + WorkSpace *workspace, + wmWindow *win, + const char *name) +{ + const int winsize_x = WM_window_pixels_x(win); + const int winsize_y = WM_window_pixels_y(win); + + bScreen *screen = screen_add(name, winsize_x, winsize_y); + WorkSpaceLayout *layout = BKE_workspace_layout_add(workspace, screen, name); + + return layout; +} + +WorkSpaceLayout *ED_workspace_layout_duplicate( + WorkSpace *workspace, const WorkSpaceLayout *layout_old, + wmWindow *win) +{ + bScreen *screen_old = BKE_workspace_layout_screen_get(layout_old); + const char *name = BKE_workspace_layout_name_get(layout_old); + bScreen *screen_new; + WorkSpaceLayout *layout_new; + + if (BKE_screen_is_fullscreen_area(screen_old)) { + return NULL; /* XXX handle this case! */ + } + + layout_new = ED_workspace_layout_add(workspace, win, name); + screen_new = BKE_workspace_layout_screen_get(layout_new); + screen_data_copy(screen_new, screen_old); + + return layout_new; +} + +static bool workspace_layout_delete_doit( + WorkSpace *workspace, WorkSpaceLayout *layout_old, WorkSpaceLayout *layout_new, + bContext *C) +{ + Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); + + ED_screen_change(C, screen_new); + + if (BKE_workspace_active_layout_get(win->workspace_hook) != layout_old) { + BKE_workspace_layout_remove(bmain, workspace, layout_old); + return true; + } + + return false; +} + +bool workspace_layout_set_poll(const WorkSpaceLayout *layout) +{ + const bScreen *screen = BKE_workspace_layout_screen_get(layout); + + return ((BKE_screen_is_used(screen) == false) && + /* in typical usage temp screens should have a nonzero winid + * (all temp screens should be used, or closed & freed). */ + (screen->temp == false) && + (BKE_screen_is_fullscreen_area(screen) == false) && + (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT))); +} + +static WorkSpaceLayout *workspace_layout_delete_find_new(const WorkSpaceLayout *layout_old) +{ + for (WorkSpaceLayout *layout_new = layout_old->prev; layout_new; layout_new = layout_new->next) { + if (workspace_layout_set_poll(layout_new)) { + return layout_new; + } + } + + for (WorkSpaceLayout *layout_new = layout_old->next; layout_new; layout_new = layout_new->next) { + if (workspace_layout_set_poll(layout_new)) { + return layout_new; + } + } + + return NULL; +} + +/** + * \warning Only call outside of area/region loops! + * \return true if succeeded. + */ +bool ED_workspace_layout_delete( + WorkSpace *workspace, WorkSpaceLayout *layout_old, + bContext *C) +{ + const bScreen *screen_old = BKE_workspace_layout_screen_get(layout_old); + WorkSpaceLayout *layout_new; + + BLI_assert(BLI_findindex(BKE_workspace_layouts_get(workspace), layout_old) != -1); + + /* don't allow deleting temp fullscreens for now */ + if (BKE_screen_is_fullscreen_area(screen_old)) { + return false; + } + + /* A layout/screen can only be in use by one window at a time, so as + * long as we are able to find a layout/screen that is unused, we + * can safely assume ours is not in use anywhere an delete it. */ + + layout_new = workspace_layout_delete_find_new(layout_old); + + if (layout_new) { + return workspace_layout_delete_doit(workspace, layout_old, layout_new, C); + } + + return false; +} + +static bool workspace_layout_cycle_iter_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) +{ + /* return false to stop iterator when we have found a layout to activate */ + return !workspace_layout_set_poll(layout); +} + +bool ED_workspace_layout_cycle( + WorkSpace *workspace, const short direction, bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + WorkSpaceLayout *old_layout = BKE_workspace_active_layout_get(win->workspace_hook); + WorkSpaceLayout *new_layout; + const bScreen *old_screen = BKE_workspace_layout_screen_get(old_layout); + ScrArea *sa = CTX_wm_area(C); + + if (old_screen->temp || (sa && sa->full && sa->full->temp)) { + return false; + } + + BLI_assert(ELEM(direction, 1, -1)); + new_layout = BKE_workspace_layout_iter_circular(workspace, old_layout, workspace_layout_cycle_iter_cb, + NULL, (direction == -1) ? true : false); + + if (new_layout && (old_layout != new_layout)) { + bScreen *new_screen = BKE_workspace_layout_screen_get(new_layout); + + if (sa && sa->full) { + /* return to previous state before switching screens */ + ED_screen_full_restore(C, sa); /* may free screen of old_layout */ + } + + ED_screen_change(C, new_screen); + + return true; + } + + return false; +} |