diff options
131 files changed, 3636 insertions, 1065 deletions
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index bb356f51ab9..92342d580c3 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -585,6 +585,7 @@ function(SETUP_BLENDER_SORTED_LIBS) bf_editor_armature bf_editor_physics bf_editor_render + bf_editor_scene bf_editor_screen bf_editor_sculpt_paint bf_editor_sound diff --git a/doc/doxygen/doxygen.source.h b/doc/doxygen/doxygen.source.h index 3ef20466664..6f12991fb44 100644 --- a/doc/doxygen/doxygen.source.h +++ b/doc/doxygen/doxygen.source.h @@ -223,6 +223,10 @@ * \ingroup editors */ +/** \defgroup edscene scene + * \ingroup editors + */ + /** \defgroup edsculpt sculpt and paint * \ingroup editors */ diff --git a/release/datafiles/workspaces.blend b/release/datafiles/workspaces.blend Binary files differnew file mode 100644 index 00000000000..4e5e9646830 --- /dev/null +++ b/release/datafiles/workspaces.blend diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index 90168429724..f05808c7a2c 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -28,7 +28,10 @@ class INFO_HT_header(Header): layout = self.layout window = context.window + workspace = context.workspace + screen = context.screen scene = context.scene + layer = context.render_layer rd = scene.render row = layout.row(align=True) @@ -36,15 +39,27 @@ class INFO_HT_header(Header): INFO_MT_editor_menus.draw_collapsible(context, layout) - if window.screen.show_fullscreen: + layout.separator() + + if screen.show_fullscreen: layout.operator("screen.back_to_previous", icon='SCREEN_BACK', text="Back to Previous") layout.separator() else: - layout.template_ID_preview(context.window, "screen", new="screen.new", unlink="screen.delete", rows=2, cols=6) - layout.template_ID(context.screen, "scene", new="scene.new", unlink="scene.delete") + layout.template_ID(window, "workspace", new="workspace.workspace_add_menu", unlink="workspace.workspace_delete") + layout.template_search_preview(window, "screen", workspace, "screens", new="screen.new", unlink="screen.delete", rows=2, cols=6) + + if hasattr(workspace, 'object_mode'): + act_mode_item = bpy.types.Object.bl_rna.properties['mode'].enum_items[workspace.object_mode] + else: + act_mode_item = bpy.types.Object.bl_rna.properties['mode'].enum_items[layer.objects.active.mode] + layout.operator_menu_enum("object.mode_set", "mode", text=act_mode_item.name, icon=act_mode_item.icon) + + layout.template_search(workspace, "render_layer", scene, "render_layers") layout.separator() + layout.template_ID(window, "scene", new="scene.new", unlink="scene.delete") + if rd.has_multiple_engines: layout.prop(rd, "engine", text="") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index dad8ca73dce..0250f4a8ce8 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -57,7 +57,9 @@ class USERPREF_HT_header(Header): layout.operator_context = 'INVOKE_DEFAULT' - if userpref.active_section == 'INPUT': + if userpref.active_section == 'INTERFACE': + layout.operator("wm.save_workspace_file") + elif userpref.active_section == 'INPUT': layout.operator("wm.keyconfig_import") layout.operator("wm.keyconfig_export") elif userpref.active_section == 'ADDONS': @@ -224,7 +226,6 @@ class USERPREF_PT_interface(Panel): col.prop(view, "show_large_cursors") col.prop(view, "show_view_name", text="View Name") col.prop(view, "show_playback_fps", text="Playback FPS") - col.prop(view, "use_global_scene") col.prop(view, "object_origin_size") col.separator() diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 448e66ec540..1788c889719 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -90,6 +90,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_workspace_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_movieclip_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_tracking_types.h diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index ac8f861fa56..ffbdc6972f4 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -82,6 +82,7 @@ enum { #define BLENDER_STARTUP_FILE "startup.blend" #define BLENDER_USERPREF_FILE "userpref.blend" +#define BLENDER_WORKSPACES_FILE "workspaces.blend" #define BLENDER_QUIT_FILE "quit.blend" #define BLENDER_BOOKMARK_FILE "bookmarks.txt" #define BLENDER_HISTORY_FILE "recent-files.txt" diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index bc3db11f34b..ae87f1097d1 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -28,7 +28,7 @@ * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 280 -#define BLENDER_SUBVERSION 0 +#define BLENDER_SUBVERSION 1 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 6 diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h index ac58451e412..d9fff5343b0 100644 --- a/source/blender/blenkernel/BKE_blendfile.h +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -60,6 +60,10 @@ struct UserDef *BKE_blendfile_userdef_read_from_memory( int BKE_blendfile_userdef_write(const char *filepath, struct ReportList *reports); +struct WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepath, struct ReportList *reports); +bool BKE_blendfile_workspace_config_write(struct Main *bmain, const char *filepath, struct ReportList *reports); +void BKE_blendfile_workspace_config_data_free(struct WorkspaceConfigFileData *workspace_config); + /* partial blend file writing */ void BKE_blendfile_write_partial_tag_ID(struct ID *id, bool set); diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index c911d9ec952..b71f5a4aa8e 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -145,6 +145,7 @@ void CTX_py_dict_set(bContext *C, void *value); struct wmWindowManager *CTX_wm_manager(const bContext *C); struct wmWindow *CTX_wm_window(const bContext *C); +struct WorkSpace *CTX_wm_workspace(const bContext *C); struct bScreen *CTX_wm_screen(const bContext *C); struct ScrArea *CTX_wm_area(const bContext *C); struct SpaceLink *CTX_wm_space_data(const bContext *C); diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index c6ec7e9df25..17ab0bf0d2b 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -60,6 +60,7 @@ struct SceneLayer; void BKE_layer_exit(void); struct SceneLayer *BKE_scene_layer_render_active(const struct Scene *scene); +struct SceneLayer *BKE_scene_layer_context_active_ex(const struct Main *bmain, const struct Scene *scene); struct SceneLayer *BKE_scene_layer_context_active(const struct Scene *scene); struct SceneLayer *BKE_scene_layer_add(struct Scene *scene, const char *name); diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 6649cfbb585..dcc15c66c3e 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -95,7 +95,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma struct ListBase *which_libbase(struct Main *mainlib, short type); -#define MAX_LIBARRAY 35 +#define MAX_LIBARRAY 36 int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]); /* Main API */ diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 387045878f3..a616429fa95 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -123,6 +123,7 @@ typedef struct Main { ListBase mask; ListBase linestyle; ListBase cachefiles; + ListBase workspaces; char id_tag_update[MAX_LIBARRAY]; diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 454fba0126e..cd59ef98903 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -52,11 +52,13 @@ struct SceneLayer; struct UnitSettings; struct Main; -#define SCE_COPY_NEW 0 -#define SCE_COPY_EMPTY 1 -#define SCE_COPY_LINK_OB 2 -#define SCE_COPY_LINK_DATA 3 -#define SCE_COPY_FULL 4 +typedef enum eSceneCopyMethod { + SCE_COPY_NEW = 0, + SCE_COPY_EMPTY = 1, + SCE_COPY_LINK_OB = 2, + SCE_COPY_LINK_DATA = 3, + SCE_COPY_FULL = 4, +} eSceneCopyMethod; /* Use as the contents of a 'for' loop: for (SETLOOPER(...)) { ... */ #define SETLOOPER(_sce_basis, _sce_iter, _base) \ diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 7def41d0540..645a99d592f 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -308,11 +308,12 @@ unsigned int BKE_screen_view3d_layer_active( unsigned int BKE_screen_view3d_layer_all(const struct bScreen *sc) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); void BKE_screen_view3d_sync(struct View3D *v3d, struct Scene *scene); -void BKE_screen_view3d_scene_sync(struct bScreen *sc); -void BKE_screen_view3d_main_sync(ListBase *screen_lb, struct Scene *scene); +void BKE_screen_view3d_scene_sync(struct bScreen *sc, struct Scene *scene); void BKE_screen_view3d_twmode_remove(struct View3D *v3d, const int i); void BKE_screen_view3d_main_twmode_remove(ListBase *screen_lb, struct Scene *scene, const int i); void BKE_screen_gpu_fx_validate(struct GPUFXSettings *fx_settings); +bool BKE_screen_is_fullscreen_area(const struct bScreen *screen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BKE_screen_is_used(const struct bScreen *screen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /* zoom factor conversion */ float BKE_screen_view3d_zoom_to_fac(float camzoom); diff --git a/source/blender/blenkernel/BKE_workspace.h b/source/blender/blenkernel/BKE_workspace.h new file mode 100644 index 00000000000..e7eaf72dd15 --- /dev/null +++ b/source/blender/blenkernel/BKE_workspace.h @@ -0,0 +1,116 @@ +/* + * ***** 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 BKE_workspace.h + * \ingroup bke + */ + +#ifndef __BKE_WORKSPACE_H__ +#define __BKE_WORKSPACE_H__ + +#include "BLI_compiler_attrs.h" + +struct bScreen; + +typedef struct WorkSpace WorkSpace; +typedef struct WorkSpaceInstanceHook WorkSpaceInstanceHook; +typedef struct WorkSpaceLayout WorkSpaceLayout; + +/** + * Plan is to store the object-mode per workspace, not per object anymore. + * However, there's quite some work to be done for that, so for now, there is just a basic + * implementation of an object <-> workspace object-mode syncing for testing, with some known + * problems. Main problem being that the modes can get out of sync when changing object selection. + * Would require a pile of temporary changes to always sync modes when changing selection. So just + * leaving this here for some testing until object-mode is really a workspace level setting. + */ +#define USE_WORKSPACE_MODE + + +/* -------------------------------------------------------------------- */ +/* Create, delete, init */ + +WorkSpace *BKE_workspace_add(struct Main *bmain, const char *name); +void BKE_workspace_free(WorkSpace *workspace); +void BKE_workspace_remove(struct Main *bmain, WorkSpace *workspace); + +WorkSpaceInstanceHook *BKE_workspace_instance_hook_create(const struct Main *bmain); +void BKE_workspace_instance_hook_free(const struct Main *bmain, WorkSpaceInstanceHook *hook); + +struct WorkSpaceLayout *BKE_workspace_layout_add( + WorkSpace *workspace, + struct bScreen *screen, + const char *name) ATTR_NONNULL(); +void BKE_workspace_layout_remove( + struct Main *bmain, + WorkSpace *workspace, WorkSpaceLayout *layout) ATTR_NONNULL(); + + +/* -------------------------------------------------------------------- */ +/* General Utils */ + +WorkSpaceLayout *BKE_workspace_layout_find( + const WorkSpace *workspace, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +WorkSpaceLayout *BKE_workspace_layout_find_global( + const struct Main *bmain, const struct bScreen *screen, + WorkSpace **r_workspace) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; + +WorkSpaceLayout *BKE_workspace_layout_iter_circular( + const WorkSpace *workspace, WorkSpaceLayout *start, + bool (*callback)(const WorkSpaceLayout *layout, void *arg), + void *arg, const bool iter_backward); + + +/* -------------------------------------------------------------------- */ +/* Getters/Setters */ + +#define GETTER_ATTRS ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT +#define SETTER_ATTRS ATTR_NONNULL(1) + +WorkSpace *BKE_workspace_active_get(WorkSpaceInstanceHook *hook) GETTER_ATTRS; +void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace) SETTER_ATTRS; +WorkSpaceLayout *BKE_workspace_active_layout_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS; +void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook, WorkSpaceLayout *layout) SETTER_ATTRS; +struct bScreen *BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS; +void BKE_workspace_active_screen_set( + WorkSpaceInstanceHook *hook, struct WorkSpace *workspace, struct bScreen *screen) SETTER_ATTRS; +enum ObjectMode BKE_workspace_object_mode_get(const WorkSpace *workspace) GETTER_ATTRS; +#ifdef USE_WORKSPACE_MODE +void BKE_workspace_object_mode_set(WorkSpace *workspace, const enum ObjectMode mode) SETTER_ATTRS; +#endif +struct SceneLayer *BKE_workspace_render_layer_get(const WorkSpace *workspace) GETTER_ATTRS; +void BKE_workspace_render_layer_set(WorkSpace *workspace, struct SceneLayer *layer) SETTER_ATTRS; +struct ListBase *BKE_workspace_layouts_get(WorkSpace *workspace) GETTER_ATTRS; + +const char *BKE_workspace_layout_name_get(const WorkSpaceLayout *layout) GETTER_ATTRS; +void BKE_workspace_layout_name_set( + WorkSpace *workspace, WorkSpaceLayout *layout, const char *new_name) ATTR_NONNULL(); +struct bScreen *BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout) GETTER_ATTRS; +void BKE_workspace_layout_screen_set(WorkSpaceLayout *layout, struct bScreen *screen) SETTER_ATTRS; + +WorkSpaceLayout *BKE_workspace_hook_layout_for_workspace_get( + const WorkSpaceInstanceHook *hook, const WorkSpace *workspace) GETTER_ATTRS; +void BKE_workspace_hook_layout_for_workspace_set( + WorkSpaceInstanceHook *hook, WorkSpace *workspace, WorkSpaceLayout *layout) ATTR_NONNULL(); + +#undef GETTER_ATTRS +#undef SETTER_ATTRS + +#endif /* __BKE_WORKSPACE_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 1ddc6fce818..71aeb393c97 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -190,6 +190,7 @@ set(SRC intern/tracking_stabilize.c intern/tracking_util.c intern/unit.c + intern/workspace.c intern/world.c intern/writeavi.c intern/writeframeserver.c @@ -302,6 +303,7 @@ set(SRC BKE_tracking.h BKE_unit.h BKE_utildefines.h + BKE_workspace.h BKE_world.h BKE_writeavi.h BKE_writeframeserver.h diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index a521d671ea4..9e244246e16 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -32,6 +32,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_workspace_types.h" #include "BLI_listbase.h" #include "BLI_string.h" @@ -48,11 +49,13 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_ipo.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "BLO_readfile.h" #include "BLO_writefile.h" @@ -93,7 +96,7 @@ static bool wm_scene_is_visible(wmWindowManager *wm, Scene *scene) { wmWindow *win; for (win = wm->windows.first; win; win = win->next) { - if (win->screen->scene == scene) { + if (win->scene == scene) { return true; } } @@ -162,17 +165,22 @@ static void setup_app_data( * (otherwise we'd be undoing on an off-screen scene which isn't acceptable). * see: T43424 */ + wmWindow *win; bScreen *curscreen = NULL; + SceneLayer *cur_render_layer; bool track_undo_scene; /* comes from readfile.c */ SWAP(ListBase, G.main->wm, bfd->main->wm); + SWAP(ListBase, G.main->workspaces, bfd->main->workspaces); SWAP(ListBase, G.main->screen, bfd->main->screen); - /* we re-use current screen */ + /* we re-use current window and screen */ + win = CTX_wm_window(C); curscreen = CTX_wm_screen(C); - /* but use new Scene pointer */ + /* but use Scene pointer from new file */ curscene = bfd->curscene; + cur_render_layer = bfd->cur_render_layer; track_undo_scene = (mode == LOAD_UNDO && curscreen && curscene && bfd->main->wm.first); @@ -183,32 +191,31 @@ static void setup_app_data( if (curscene == NULL) { curscene = BKE_scene_add(bfd->main, "Empty"); } + if (cur_render_layer == NULL) { + /* fallback to scene layer */ + cur_render_layer = BKE_scene_layer_render_active(curscene); + } if (track_undo_scene) { /* keep the old (free'd) scene, let 'blo_lib_link_screen_restore' * replace it with 'curscene' if its needed */ } - else { - /* and we enforce curscene to be in current screen */ - if (curscreen) { - /* can run in bgmode */ - curscreen->scene = curscene; - } + /* and we enforce curscene to be in current screen */ + else if (win) { /* can run in bgmode */ + win->scene = curscene; } /* BKE_blender_globals_clear will free G.main, here we can still restore pointers */ - blo_lib_link_screen_restore(bfd->main, curscreen, curscene); - /* curscreen might not be set when loading without ui (see T44217) so only re-assign if available */ - if (curscreen) { - curscene = curscreen->scene; + blo_lib_link_restore(bfd->main, CTX_wm_manager(C), curscene, cur_render_layer); + if (win) { + curscene = win->scene; } if (track_undo_scene) { wmWindowManager *wm = bfd->main->wm.first; if (wm_scene_is_visible(wm, bfd->curscene) == false) { curscene = bfd->curscene; - curscreen->scene = curscene; - BKE_screen_view3d_scene_sync(curscreen); + BKE_screen_view3d_scene_sync(curscreen, curscene); } } } @@ -262,12 +269,14 @@ static void setup_app_data( /* this can happen when active scene was lib-linked, and doesn't exist anymore */ if (CTX_data_scene(C) == NULL) { + wmWindow *win = CTX_wm_window(C); + /* in case we don't even have a local scene, add one */ if (!G.main->scene.first) BKE_scene_add(G.main, "Empty"); CTX_data_scene_set(C, G.main->scene.first); - CTX_wm_screen(C)->scene = CTX_data_scene(C); + win->scene = CTX_data_scene(C); curscene = CTX_data_scene(C); } @@ -316,12 +325,10 @@ static void setup_app_data( wmWindowManager *wm = G.main->wm.first; if (wm) { - wmWindow *win; - - for (win = wm->windows.first; win; win = win->next) { - if (win->screen && win->screen->scene) /* zealous check... */ - if (win->screen->scene != curscene) - BKE_scene_set_background(G.main, win->screen->scene); + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (win->scene && win->scene != curscene) { + BKE_scene_set_background(G.main, win->scene); + } } } } @@ -509,6 +516,49 @@ int BKE_blendfile_userdef_write(const char *filepath, ReportList *reports) return retval; } +WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepath, ReportList *reports) +{ + BlendFileData *bfd; + WorkspaceConfigFileData *workspace_config = NULL; + + bfd = BLO_read_from_file(filepath, reports, BLO_READ_SKIP_USERDEF); + if (bfd) { + workspace_config = MEM_mallocN(sizeof(*workspace_config), __func__); + workspace_config->main = bfd->main; + workspace_config->workspaces = bfd->main->workspaces; + + MEM_freeN(bfd); + } + + return workspace_config; +} + +bool BKE_blendfile_workspace_config_write(Main *bmain, const char *filepath, ReportList *reports) +{ + int fileflags = G.fileflags & ~(G_FILE_NO_UI | G_FILE_AUTOPLAY | G_FILE_HISTORY); + bool retval = false; + + BKE_blendfile_write_partial_begin(bmain); + + for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + BKE_blendfile_write_partial_tag_ID(&workspace->id, true); + } + + if (BKE_blendfile_write_partial(bmain, filepath, fileflags, reports)) { + retval = true; + } + + BKE_blendfile_write_partial_end(bmain); + + return retval; +} + +void BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData *workspace_config) +{ + BKE_main_free(workspace_config->main); + MEM_freeN(workspace_config); +} + /** \} */ diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index ec0e14d814f..7f24f1c2e5e 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -53,6 +53,7 @@ #include "BKE_main.h" #include "BKE_screen.h" #include "BKE_sound.h" +#include "BKE_workspace.h" #include "RNA_access.h" @@ -69,6 +70,7 @@ struct bContext { struct { struct wmWindowManager *manager; struct wmWindow *window; + struct WorkSpace *workspace; struct bScreen *screen; struct ScrArea *area; struct ARegion *region; @@ -630,6 +632,11 @@ wmWindow *CTX_wm_window(const bContext *C) return ctx_wm_python_context_get(C, "window", &RNA_Window, C->wm.window); } +WorkSpace *CTX_wm_workspace(const bContext *C) +{ + return ctx_wm_python_context_get(C, "workspace", &RNA_WorkSpace, C->wm.workspace); +} + bScreen *CTX_wm_screen(const bContext *C) { return ctx_wm_python_context_get(C, "screen", &RNA_Screen, C->wm.screen); @@ -829,9 +836,11 @@ void CTX_wm_manager_set(bContext *C, wmWindowManager *wm) void CTX_wm_window_set(bContext *C, wmWindow *win) { C->wm.window = win; - C->wm.screen = (win) ? win->screen : NULL; - if (C->wm.screen) - C->data.scene = C->wm.screen->scene; + if (win) { + C->data.scene = win->scene; + } + C->wm.workspace = (win) ? BKE_workspace_active_get(win->workspace_hook) : NULL; + C->wm.screen = (win) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL; C->wm.area = NULL; C->wm.region = NULL; } @@ -839,9 +848,6 @@ void CTX_wm_window_set(bContext *C, wmWindow *win) void CTX_wm_screen_set(bContext *C, bScreen *screen) { C->wm.screen = screen; - if (C->wm.screen) { - CTX_data_scene_set(C, C->wm.screen->scene); - } C->wm.area = NULL; C->wm.region = NULL; } diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index 70d037d85f3..e905eaeed76 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -82,7 +82,7 @@ static IDType idtypes[] = { { ID_PAL, "Palettes", "palettes", BLT_I18NCONTEXT_ID_PALETTE, IDTYPE_FLAGS_ISLINKABLE }, { ID_PC, "PaintCurve", "paint_curves", BLT_I18NCONTEXT_ID_PAINTCURVE, IDTYPE_FLAGS_ISLINKABLE }, { ID_SCE, "Scene", "scenes", BLT_I18NCONTEXT_ID_SCENE, IDTYPE_FLAGS_ISLINKABLE }, - { ID_SCR, "Screen", "screens", BLT_I18NCONTEXT_ID_SCREEN, 0 }, + { ID_SCR, "Screen", "screens", BLT_I18NCONTEXT_ID_SCREEN, IDTYPE_FLAGS_ISLINKABLE }, { ID_SEQ, "Sequence", "sequences", BLT_I18NCONTEXT_ID_SEQUENCE, 0 }, /* not actually ID data */ { ID_SPK, "Speaker", "speakers", BLT_I18NCONTEXT_ID_SPEAKER, IDTYPE_FLAGS_ISLINKABLE }, { ID_SO, "Sound", "sounds", BLT_I18NCONTEXT_ID_SOUND, IDTYPE_FLAGS_ISLINKABLE }, @@ -91,6 +91,7 @@ static IDType idtypes[] = { { ID_VF, "VFont", "fonts", BLT_I18NCONTEXT_ID_VFONT, IDTYPE_FLAGS_ISLINKABLE }, { ID_WO, "World", "worlds", BLT_I18NCONTEXT_ID_WORLD, IDTYPE_FLAGS_ISLINKABLE }, { ID_WM, "WindowManager", "window_managers", BLT_I18NCONTEXT_ID_WINDOWMANAGER, 0 }, + { ID_WS, "WorkSpace", "workspaces", BLT_I18NCONTEXT_ID_WORKSPACE, IDTYPE_FLAGS_ISLINKABLE }, /** Keep last, not an ID exactly, only include for completeness */ { ID_ID, "ID", "ids", BLT_I18NCONTEXT_ID_ID, 0 }, /* plural is fake */ @@ -210,6 +211,7 @@ int BKE_idcode_to_idfilter(const short idcode) CASE_IDFILTER(TXT); CASE_IDFILTER(VF); CASE_IDFILTER(WO); + CASE_IDFILTER(WS); default: return 0; } @@ -303,6 +305,7 @@ int BKE_idcode_to_index(const short idcode) CASE_IDINDEX(VF); CASE_IDINDEX(WM); CASE_IDINDEX(WO); + CASE_IDINDEX(WS); } BLI_assert(0); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9b28d9732e5..7ee4afd052d 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -81,6 +81,7 @@ #include "BKE_scene.h" #include "BKE_node.h" #include "BKE_sequencer.h" /* seq_foreground_frame_get() */ +#include "BKE_workspace.h" #include "BLF_api.h" @@ -2544,8 +2545,9 @@ void BKE_image_walk_all_users(const Main *mainp, void *customdata, /* image window, compo node users */ for (wm = mainp->wm.first; wm; wm = wm->id.next) { /* only 1 wm */ for (win = wm->windows.first; win; win = win->next) { - ScrArea *sa; - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_VIEW3D) { View3D *v3d = sa->spacedata.first; BGpic *bgpic; diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index d2fa09fa9e9..b088c7b4745 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -38,12 +38,14 @@ #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_workspace.h" #include "DNA_ID.h" #include "DNA_layer_types.h" #include "DNA_object_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" #include "DRW_engine.h" @@ -76,14 +78,25 @@ SceneLayer *BKE_scene_layer_render_active(const Scene *scene) } /** - * Returns the SceneLayer to be used for drawing, outliner, and - * other context related areas. + * Returns the SceneLayer to be used for drawing, outliner, and other context related areas. */ +SceneLayer *BKE_scene_layer_context_active_ex(const Main *bmain, const Scene *UNUSED(scene)) +{ + /* XXX We should really pass the workspace as argument, but would require + * some bigger changes since it's often not available where we call this. + * Just working around this by getting active window from WM for now */ + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + /* Called on startup, so 'winactive' may not be set, in that case fall back to first window. */ + wmWindow *win = wm->winactive ? wm->winactive : wm->windows.first; + const WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + return BKE_workspace_render_layer_get(workspace); + } + + return NULL; +} SceneLayer *BKE_scene_layer_context_active(const Scene *scene) { - /* waiting for workspace to get the layer from context*/ - TODO_LAYER_CONTEXT; - return BKE_scene_layer_render_active(scene); + return BKE_scene_layer_context_active_ex(G.main, scene); } /** diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 4da2108b9d0..b81465a1b1d 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -70,6 +70,7 @@ #include "DNA_vfont_types.h" #include "DNA_windowmanager_types.h" #include "DNA_world_types.h" +#include "DNA_workspace_types.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" @@ -471,7 +472,11 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) case ID_CF: if (!test) BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local); return true; + case ID_WS: case ID_SCR: + /* A bit special: can be appended but not linked. Return false + * since supporting make-local doesn't make much sense. */ + return false; case ID_LI: case ID_KE: case ID_WM: @@ -578,6 +583,7 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) case ID_CF: if (!test) *newid = (ID *)BKE_cachefile_copy(bmain, (CacheFile *)id); return true; + case ID_WS: case ID_SCE: case ID_LI: case ID_SCR: @@ -693,6 +699,8 @@ ListBase *which_libbase(Main *mainlib, short type) return &(mainlib->paintcurves); case ID_CF: return &(mainlib->cachefiles); + case ID_WS: + return &(mainlib->workspaces); } return NULL; } @@ -838,6 +846,7 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[INDEX_ID_OB] = &(main->object); lb[INDEX_ID_LS] = &(main->linestyle); /* referenced by scenes */ lb[INDEX_ID_SCE] = &(main->scene); + lb[INDEX_ID_WS] = &(main->workspaces); /* before wm, so it's freed after it! */ lb[INDEX_ID_WM] = &(main->wm); lb[INDEX_ID_MSK] = &(main->mask); @@ -967,6 +976,9 @@ void *BKE_libblock_alloc_notest(short type) case ID_CF: id = MEM_callocN(sizeof(CacheFile), "Cache File"); break; + case ID_WS: + id = MEM_callocN(sizeof(WorkSpace), "Workspace"); + break; } return id; } diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 0c89a5bea42..d2ee11cc397 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -61,6 +61,8 @@ #include "DNA_sound_types.h" #include "DNA_text_types.h" #include "DNA_vfont_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "DNA_world_types.h" #include "BLI_utildefines.h" @@ -83,6 +85,7 @@ #include "BKE_sca.h" #include "BKE_sequencer.h" #include "BKE_tracking.h" +#include "BKE_workspace.h" #define FOREACH_FINALIZE _finalize @@ -746,13 +749,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call break; } - case ID_SCR: - { - bScreen *screen = (bScreen *) id; - CALLBACK_INVOKE(screen->scene, IDWALK_CB_USER_ONE); - break; - } - case ID_WO: { World *world = (World *) id; @@ -963,6 +959,38 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call } break; } + + case ID_WM: + { + wmWindowManager *wm = (wmWindowManager *)id; + + for (wmWindow *win = wm->windows.first; win; win = win->next) { + ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook); + + CALLBACK_INVOKE(win->scene, IDWALK_CB_USER_ONE); + + CALLBACK_INVOKE_ID(workspace, IDWALK_CB_NOP); + /* allow callback to set a different workspace */ + BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace); + } + break; + } + + case ID_WS: + { + WorkSpace *workspace = (WorkSpace *)id; + ListBase *layouts = BKE_workspace_layouts_get(workspace); + + for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) { + bScreen *screen = BKE_workspace_layout_screen_get(layout); + + CALLBACK_INVOKE(screen, IDWALK_CB_NOP); + /* allow callback to set a different screen */ + BKE_workspace_layout_screen_set(layout, screen); + } + + break; + } case ID_GD: { bGPdata *gpencil = (bGPdata *) id; @@ -974,11 +1002,11 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call } /* Nothing needed for those... */ + case ID_SCR: case ID_IM: case ID_VF: case ID_TXT: case ID_SO: - case ID_WM: case ID_PAL: case ID_PC: case ID_CF: @@ -1106,6 +1134,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */ case ID_LS: return (ELEM(id_type_used, ID_TE, ID_OB)); + case ID_WS: case ID_IM: case ID_VF: case ID_TXT: diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index 02128ae9c3a..235b10dd1da 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -106,6 +106,7 @@ #include "BKE_scene.h" #include "BKE_text.h" #include "BKE_texture.h" +#include "BKE_workspace.h" #include "BKE_world.h" #include "DEG_depsgraph.h" @@ -883,6 +884,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const b case ID_CF: BKE_cachefile_free((CacheFile *)id); break; + case ID_WS: + BKE_workspace_free((WorkSpace *)id); + break; } /* avoid notifying on removed data */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 9de3ab5a93b..92f5dade0a5 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -93,6 +93,7 @@ #include "BKE_sequencer.h" #include "BKE_sound.h" #include "BKE_unit.h" +#include "BKE_workspace.h" #include "BKE_world.h" #include "DEG_depsgraph.h" @@ -1004,7 +1005,7 @@ BaseLegacy *BKE_scene_base_find(Scene *scene, Object *ob) /** * Sets the active scene, mainly used when running in background mode (``--scene`` command line argument). * This is also called to set the scene directly, bypassing windowing code. - * Otherwise #ED_screen_set_scene is used when changing scenes by the user. + * Otherwise #WM_window_change_active_scene is used when changing scenes by the user. */ void BKE_scene_set_background(Main *bmain, Scene *scene) { @@ -1442,8 +1443,8 @@ static bool check_rendered_viewport_visible(Main *bmain) wmWindowManager *wm = bmain->wm.first; wmWindow *window; for (window = wm->windows.first; window != NULL; window = window->next) { - bScreen *screen = window->screen; - Scene *scene = screen->scene; + const bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + Scene *scene = window->scene; ScrArea *area; RenderEngineType *type = RE_engines_find(scene->r.engine); if ((type->draw_engine != NULL) || (type->render_to_view == NULL)) { @@ -1755,6 +1756,9 @@ Base *_setlooper_base_step(Scene **sce_iter, Base *base) /* for the first loop we should get the layer from context */ SceneLayer *sl = BKE_scene_layer_context_active((*sce_iter)); + /* TODO For first scene (non-background set), we should pass the render layer as argument. + * In some cases we want it to be the workspace one, in other the scene one. */ + TODO_LAYER; if (sl->object_bases.first) { return (Base *)sl->object_bases.first; diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index c5d00d63b6d..b1f8f574b7e 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -596,7 +596,7 @@ void BKE_screen_view3d_sync(View3D *v3d, struct Scene *scene) } } -void BKE_screen_view3d_scene_sync(bScreen *sc) +void BKE_screen_view3d_scene_sync(bScreen *sc, Scene *scene) { /* are there cameras in the views that are not in the scene? */ ScrArea *sa; @@ -605,41 +605,25 @@ void BKE_screen_view3d_scene_sync(bScreen *sc) for (sl = sa->spacedata.first; sl; sl = sl->next) { if (sl->spacetype == SPACE_VIEW3D) { View3D *v3d = (View3D *) sl; - BKE_screen_view3d_sync(v3d, sc->scene); + BKE_screen_view3d_sync(v3d, scene); } } } } -void BKE_screen_view3d_main_sync(ListBase *screen_lb, Scene *scene) -{ - bScreen *sc; - ScrArea *sa; - SpaceLink *sl; - - /* from scene copy to the other views */ - for (sc = screen_lb->first; sc; sc = sc->id.next) { - if (sc->scene != scene) - continue; - - for (sa = sc->areabase.first; sa; sa = sa->next) - for (sl = sa->spacedata.first; sl; sl = sl->next) - if (sl->spacetype == SPACE_VIEW3D) - BKE_screen_view3d_sync((View3D *)sl, scene); - } -} - +/* XXX apply D2687 */ void BKE_screen_view3d_twmode_remove(View3D *v3d, const int i) { const int selected_index = (v3d->twmode - V3D_MANIP_CUSTOM); if (selected_index == i) { - v3d->twmode = V3D_MANIP_GLOBAL; /* fallback to global */ + v3d->twmode = V3D_MANIP_GLOBAL; } else if (selected_index > i) { v3d->twmode--; } } +/* XXX apply D2687 */ void BKE_screen_view3d_main_twmode_remove(ListBase *screen_lb, Scene *scene, const int i) { bScreen *sc; @@ -700,3 +684,13 @@ void BKE_screen_gpu_fx_validate(GPUFXSettings *fx_settings) GPU_fx_compositor_init_ssao_settings(fx_ssao); } } + +bool BKE_screen_is_fullscreen_area(const bScreen *screen) +{ + return ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL); +} + +bool BKE_screen_is_used(const bScreen *screen) +{ + return (screen->winid != 0); +} diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c new file mode 100644 index 00000000000..8afad317dce --- /dev/null +++ b/source/blender/blenkernel/intern/workspace.c @@ -0,0 +1,399 @@ +/* + * ***** 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/blenkernel/intern/workspace.c + * \ingroup bke + */ + +/* allow accessing private members of DNA_workspace_types.h */ +#define DNA_PRIVATE_WORKSPACE_ALLOW + +#include <stdlib.h> + +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" +#include "BLI_listbase.h" + +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_workspace.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_workspace_types.h" + +#include "MEM_guardedalloc.h" + + +/* -------------------------------------------------------------------- */ +/* Internal utils */ + +static void workspace_layout_name_set( + WorkSpace *workspace, WorkSpaceLayout *layout, const char *new_name) +{ + BLI_strncpy(layout->name, new_name, sizeof(layout->name)); + BLI_uniquename(&workspace->layouts, layout, "Layout", '.', offsetof(WorkSpaceLayout, name), sizeof(layout->name)); +} + +/** + * This should only be used directly when it is to be expected that there isn't + * a layout within \a workspace that wraps \a screen. Usually - especially outside + * of BKE_workspace - #BKE_workspace_layout_find should be used! + */ +static WorkSpaceLayout *workspace_layout_find_exec( + const WorkSpace *workspace, const bScreen *screen) +{ + return BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen)); +} + +static void workspace_relation_add( + ListBase *relation_list, void *parent, void *data) +{ + WorkSpaceDataRelation *relation = MEM_callocN(sizeof(*relation), __func__); + relation->parent = parent; + relation->value = data; + /* add to head, if we switch back to it soon we find it faster. */ + BLI_addhead(relation_list, relation); +} +static void workspace_relation_remove( + ListBase *relation_list, WorkSpaceDataRelation *relation) +{ + BLI_remlink(relation_list, relation); + MEM_freeN(relation); +} + +static void workspace_relation_ensure_updated( + ListBase *relation_list, void *parent, void *data) +{ + WorkSpaceDataRelation *relation = BLI_findptr(relation_list, parent, offsetof(WorkSpaceDataRelation, parent)); + if (relation != NULL) { + relation->value = data; + /* reinsert at the head of the list, so that more commonly used relations are found faster. */ + BLI_remlink(relation_list, relation); + BLI_addhead(relation_list, relation); + } + else { + /* no matching relation found, add new one */ + workspace_relation_add(relation_list, parent, data); + } +} + +static void *workspace_relation_get_data_matching_parent( + const ListBase *relation_list, const void *parent) +{ + WorkSpaceDataRelation *relation = BLI_findptr(relation_list, parent, offsetof(WorkSpaceDataRelation, parent)); + if (relation != NULL) { + return relation->value; + } + else { + return NULL; + } +} + +/** + * Checks if \a screen is already used within any workspace. A screen should never be assigned to multiple + * WorkSpaceLayouts, but that should be ensured outside of the BKE_workspace module and without such checks. + * Hence, this should only be used as assert check before assigining a screen to a workspace. + */ +#ifndef NDEBUG +static bool workspaces_is_screen_used( +#else +static bool UNUSED_FUNCTION(workspaces_is_screen_used)( +#endif + const Main *bmain, bScreen *screen) +{ + for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + if (workspace_layout_find_exec(workspace, screen)) { + return true; + } + } + + return false; +} + +/* -------------------------------------------------------------------- */ +/* Create, delete, init */ + +WorkSpace *BKE_workspace_add(Main *bmain, const char *name) +{ + WorkSpace *new_workspace = BKE_libblock_alloc(bmain, ID_WS, name); + return new_workspace; +} + +void BKE_workspace_free(WorkSpace *workspace) +{ + for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first, *relation_next; + relation; + relation = relation_next) + { + relation_next = relation->next; + workspace_relation_remove(&workspace->hook_layout_relations, relation); + } + BLI_freelistN(&workspace->layouts); +} + +void BKE_workspace_remove(Main *bmain, WorkSpace *workspace) +{ + for (WorkSpaceLayout *layout = workspace->layouts.first, *layout_next; layout; layout = layout_next) { + layout_next = layout->next; + BKE_workspace_layout_remove(bmain, workspace, layout); + } + + BKE_libblock_free(bmain, workspace); +} + +WorkSpaceInstanceHook *BKE_workspace_instance_hook_create(const Main *bmain) +{ + WorkSpaceInstanceHook *hook = MEM_callocN(sizeof(WorkSpaceInstanceHook), __func__); + + /* set an active screen-layout for each possible window/workspace combination */ + for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + BKE_workspace_hook_layout_for_workspace_set(hook, workspace, workspace->layouts.first); + } + + return hook; +} +void BKE_workspace_instance_hook_free(const Main *bmain, WorkSpaceInstanceHook *hook) +{ + /* workspaces should never be freed before wm (during which we call this function) */ + BLI_assert(!BLI_listbase_is_empty(&bmain->workspaces)); + + /* Free relations for this hook */ + for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first, *relation_next; + relation; + relation = relation_next) + { + relation_next = relation->next; + if (relation->parent == hook) { + workspace_relation_remove(&workspace->hook_layout_relations, relation); + } + } + } + + MEM_freeN(hook); +} + +/** + * Add a new layout to \a workspace for \a screen. + */ +WorkSpaceLayout *BKE_workspace_layout_add( + WorkSpace *workspace, + bScreen *screen, + const char *name) +{ + WorkSpaceLayout *layout = MEM_callocN(sizeof(*layout), __func__); + + BLI_assert(!workspaces_is_screen_used(G.main, screen)); + layout->screen = screen; + workspace_layout_name_set(workspace, layout, name); + BLI_addtail(&workspace->layouts, layout); + + return layout; +} + +void BKE_workspace_layout_remove( + Main *bmain, + WorkSpace *workspace, WorkSpaceLayout *layout) +{ + BKE_libblock_free(bmain, BKE_workspace_layout_screen_get(layout)); + BLI_freelinkN(&workspace->layouts, layout); +} + +/* -------------------------------------------------------------------- */ +/* General Utils */ + +WorkSpaceLayout *BKE_workspace_layout_find( + const WorkSpace *workspace, const bScreen *screen) +{ + WorkSpaceLayout *layout = workspace_layout_find_exec(workspace, screen); + if (layout) { + return layout; + } + + printf("%s: Couldn't find layout in this workspace: '%s' screen: '%s'. " + "This should not happen!\n", + __func__, workspace->id.name + 2, screen->id.name + 2); + + return NULL; +} + +/** + * Find the layout for \a screen without knowing which workspace to look in. + * + * \param r_workspace: Optionally return the workspace that contains the looked up layout (if found). + */ +WorkSpaceLayout *BKE_workspace_layout_find_global( + const Main *bmain, const bScreen *screen, + WorkSpace **r_workspace) +{ + WorkSpaceLayout *layout; + + if (r_workspace) { + *r_workspace = NULL; + } + + for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + if ((layout = workspace_layout_find_exec(workspace, screen))) { + if (r_workspace) { + *r_workspace = workspace; + } + + return layout; + } + } + + return NULL; +} + +/** + * Circular workspace layout iterator. + * + * \param callback: Custom function which gets executed for each layout. Can return false to stop iterating. + * \param arg: Custom data passed to each \a callback call. + * + * \return the layout at which \a callback returned false. + */ +WorkSpaceLayout *BKE_workspace_layout_iter_circular( + const WorkSpace *workspace, WorkSpaceLayout *start, + bool (*callback)(const WorkSpaceLayout *layout, void *arg), + void *arg, const bool iter_backward) +{ + WorkSpaceLayout *iter_layout; + + if (iter_backward) { + BLI_LISTBASE_CIRCULAR_BACKWARD_BEGIN(&workspace->layouts, iter_layout, start) + { + if (!callback(iter_layout, arg)) { + return iter_layout; + } + } + BLI_LISTBASE_CIRCULAR_BACKWARD_END(&workspace->layouts, iter_layout, start); + } + else { + BLI_LISTBASE_CIRCULAR_FORWARD_BEGIN(&workspace->layouts, iter_layout, start) + { + if (!callback(iter_layout, arg)) { + return iter_layout; + } + } + BLI_LISTBASE_CIRCULAR_FORWARD_END(&workspace->layouts, iter_layout, start) + } + + return NULL; +} + + +/* -------------------------------------------------------------------- */ +/* Getters/Setters */ + +WorkSpace *BKE_workspace_active_get(WorkSpaceInstanceHook *hook) +{ + return hook->active; +} +void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace) +{ + hook->active = workspace; + if (workspace) { + WorkSpaceLayout *layout = workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook); + if (layout) { + hook->act_layout = layout; + } + } +} + +WorkSpaceLayout *BKE_workspace_active_layout_get(const WorkSpaceInstanceHook *hook) +{ + return hook->act_layout; +} +void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook, WorkSpaceLayout *layout) +{ + hook->act_layout = layout; +} + +bScreen *BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) +{ + return hook->act_layout->screen; +} +void BKE_workspace_active_screen_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace, bScreen *screen) +{ + /* we need to find the WorkspaceLayout that wraps this screen */ + WorkSpaceLayout *layout = BKE_workspace_layout_find(hook->active, screen); + BKE_workspace_hook_layout_for_workspace_set(hook, workspace, layout); +} + +#ifdef USE_WORKSPACE_MODE +ObjectMode BKE_workspace_object_mode_get(const WorkSpace *workspace) +{ + return workspace->object_mode; +} +void BKE_workspace_object_mode_set(WorkSpace *workspace, const ObjectMode mode) +{ + workspace->object_mode = mode; +} +#endif + +SceneLayer *BKE_workspace_render_layer_get(const WorkSpace *workspace) +{ + return workspace->render_layer; +} +void BKE_workspace_render_layer_set(WorkSpace *workspace, SceneLayer *layer) +{ + workspace->render_layer = layer; +} + +ListBase *BKE_workspace_layouts_get(WorkSpace *workspace) +{ + return &workspace->layouts; +} + + +const char *BKE_workspace_layout_name_get(const WorkSpaceLayout *layout) +{ + return layout->name; +} +void BKE_workspace_layout_name_set(WorkSpace *workspace, WorkSpaceLayout *layout, const char *new_name) +{ + workspace_layout_name_set(workspace, layout, new_name); +} + +bScreen *BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout) +{ + return layout->screen; +} +void BKE_workspace_layout_screen_set(WorkSpaceLayout *layout, bScreen *screen) +{ + layout->screen = screen; +} + +WorkSpaceLayout *BKE_workspace_hook_layout_for_workspace_get( + const WorkSpaceInstanceHook *hook, const WorkSpace *workspace) +{ + return workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook); +} +void BKE_workspace_hook_layout_for_workspace_set( + WorkSpaceInstanceHook *hook, WorkSpace *workspace, WorkSpaceLayout *layout) +{ + hook->act_layout = layout; + workspace_relation_ensure_updated(&workspace->hook_layout_relations, hook, layout); +} diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 0213a4ee997..fe38fc86c84 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -39,6 +39,7 @@ extern "C" { struct BlendThumbnail; struct bScreen; struct LinkNode; +struct ListBase; struct Main; struct MemFile; struct ReportList; @@ -49,6 +50,7 @@ struct View3D; struct bContext; struct BHead; struct FileData; +struct wmWindowManager; typedef struct BlendHandle BlendHandle; @@ -65,13 +67,20 @@ typedef struct BlendFileData { int fileflags; int globalf; char filename[1024]; /* 1024 = FILE_MAX */ - - struct bScreen *curscreen; + + struct bScreen *curscreen; /* TODO think this isn't needed anymore? */ struct Scene *curscene; - + struct SceneLayer *cur_render_layer; /* layer to activate in workspaces when reading without UI */ + BlenFileType type; } BlendFileData; +typedef struct WorkspaceConfigFileData { + struct Main *main; /* has to be freed when done reading file data */ + + struct ListBase workspaces; +} WorkspaceConfigFileData; + /* skip reading some data-block types (may want to skip screen data too). */ typedef enum eBLOReadSkip { @@ -100,6 +109,7 @@ BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize); struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, int *tot_names); struct LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *tot_prev); struct LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh); +struct LinkNode *BLO_blendhandle_get_appendable_groups(BlendHandle *bh); void BLO_blendhandle_close(BlendHandle *bh); @@ -126,7 +136,9 @@ void *BLO_library_read_struct(struct FileData *fd, struct BHead *bh, const char BlendFileData *blo_read_blendafterruntime(int file, const char *name, int actualsize, struct ReportList *reports); /* internal function but we need to expose it */ -void blo_lib_link_screen_restore(struct Main *newmain, struct bScreen *curscreen, struct Scene *curscene); +void blo_lib_link_restore( + struct Main *newmain, struct wmWindowManager *curwm, + struct Scene *curscene, struct SceneLayer *cur_render_layer); typedef void (*BLOExpandDoitCallback) (void *fdhandle, struct Main *mainvar, void *idv); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index d22af275469..966cbce62db 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -53,6 +53,10 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +/* Allow using DNA struct members that are marked as private for read/write. + * Note: Each header that uses this needs to define its own way of handling + * it. There's no generic implementation, direct use does nothing. */ +#define DNA_PRIVATE_READ_WRITE_ALLOW #include "DNA_anim_types.h" #include "DNA_armature_types.h" @@ -99,10 +103,13 @@ #include "DNA_sound_types.h" #include "DNA_space_types.h" #include "DNA_vfont_types.h" +#include "DNA_workspace_types.h" #include "DNA_world_types.h" #include "DNA_movieclip_types.h" #include "DNA_mask_types.h" +#include "RNA_access.h" + #include "MEM_guardedalloc.h" #include "BLI_endian_switch.h" @@ -149,6 +156,7 @@ #include "BKE_outliner_treehash.h" #include "BKE_sound.h" #include "BKE_colortools.h" +#include "BKE_workspace.h" #include "DEG_depsgraph.h" @@ -2774,6 +2782,78 @@ static void direct_link_cachefile(FileData *fd, CacheFile *cache_file) direct_link_animdata(fd, cache_file->adt); } +/* ************ READ WORKSPACES *************** */ + +static void lib_link_workspaces(FileData *fd, Main *bmain) +{ + for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + ListBase *layouts = BKE_workspace_layouts_get(workspace); + ID *id = (ID *)workspace; + + if ((id->tag & LIB_TAG_NEED_LINK) == 0) { + continue; + } + IDP_LibLinkProperty(id->properties, fd); + id_us_ensure_real(id); + + for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) { + bScreen *screen = newlibadr(fd, id->lib, BKE_workspace_layout_screen_get(layout)); + + if (screen) { + BKE_workspace_layout_screen_set(layout, screen); + + if (ID_IS_LINKED_DATABLOCK(id)) { + screen->winid = 0; + if (screen->temp) { + /* delete temp layouts when appending */ + BKE_workspace_layout_remove(bmain, workspace, layout); + } + } + } + } + + id->tag &= ~LIB_TAG_NEED_LINK; + } +} + +static void direct_link_workspace(FileData *fd, WorkSpace *workspace, const Main *main) +{ + link_list(fd, BKE_workspace_layouts_get(workspace)); + link_list(fd, &workspace->hook_layout_relations); + + for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first; + relation; + relation = relation->next) + { + relation->parent = newglobadr(fd, relation->parent); /* data from window - need to access through global oldnew-map */ + relation->value = newdataadr(fd, relation->value); + } + + if (ID_IS_LINKED_DATABLOCK(&workspace->id)) { + /* Appending workspace so render layer is likely from a different scene. Unset + * now, when activating workspace later we set a valid one from current scene. */ + BKE_workspace_render_layer_set(workspace, NULL); + } + + /* Same issue/fix as in direct_link_scene_update_screen_data: Can't read workspace data + * when reading windows, so have to update windows after/when reading workspaces. */ + for (wmWindowManager *wm = main->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + WorkSpaceLayout *act_layout = newdataadr(fd, BKE_workspace_active_layout_get(win->workspace_hook)); + if (act_layout) { + BKE_workspace_active_layout_set(win->workspace_hook, act_layout); + } + } + } +} + +static void lib_link_workspace_instance_hook(FileData *fd, WorkSpaceInstanceHook *hook, ID *id) +{ + WorkSpace *workspace = BKE_workspace_active_get(hook); + BKE_workspace_active_set(hook, newlibadr(fd, id->lib, workspace)); +} + + /* ************ READ MOTION PATHS *************** */ /* direct data for cache */ @@ -5977,7 +6057,19 @@ static void direct_link_layer_collections(FileData *fd, ListBase *lb) } } -static void direct_link_scene(FileData *fd, Scene *sce) +static void direct_link_scene_update_screen_data( + FileData *fd, const Scene *scene, const ListBase *workspaces) +{ + for (WorkSpace *workspace = workspaces->first; workspace; workspace = workspace->id.next) { + SceneLayer *layer = newdataadr(fd, BKE_workspace_render_layer_get(workspace)); + /* only set when layer is from the scene we read */ + if (layer && (BLI_findindex(&scene->render_layers, layer) != -1)) { + BKE_workspace_render_layer_set(workspace, layer); + } + } +} + +static void direct_link_scene(FileData *fd, Scene *sce, Main *bmain) { Editing *ed; Sequence *seq; @@ -6250,7 +6342,8 @@ static void direct_link_scene(FileData *fd, Scene *sce) direct_link_scene_collection(fd, sce->collection); } - link_list(fd, &sce->render_layers); + /* insert into global old-new map for reading without UI (link_global accesses it again) */ + link_glob_list(fd, &sce->render_layers); for (sl = sce->render_layers.first; sl; sl = sl->next) { sl->stats = NULL; link_list(fd, &sl->object_bases); @@ -6277,6 +6370,8 @@ static void direct_link_scene(FileData *fd, Scene *sce) BKE_layer_collection_engine_settings_validate_scene(sce); BKE_scene_layer_engine_settings_validate_scene(sce); + + direct_link_scene_update_screen_data(fd, sce, &bmain->workspaces); } /* ************ READ WM ***************** */ @@ -6289,6 +6384,12 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) link_list(fd, &wm->windows); for (win = wm->windows.first; win; win = win->next) { + WorkSpaceInstanceHook *hook = win->workspace_hook; + + win->workspace_hook = newdataadr(fd, hook); + /* we need to restore a pointer to this later when reading workspaces, so store in global oldnew-map */ + oldnewmap_insert(fd->globmap, hook, win->workspace_hook, 0); + win->ghostwin = NULL; win->eventstate = NULL; win->curswin = NULL; @@ -6353,6 +6454,11 @@ static void lib_link_windowmanager(FileData *fd, Main *main) if (wm->id.tag & LIB_TAG_NEED_LINK) { /* Note: WM IDProperties are never written to file, hence no need to read/link them here. */ for (win = wm->windows.first; win; win = win->next) { + if (win->workspace_hook) { /* NULL for old files */ + lib_link_workspace_instance_hook(fd, win->workspace_hook, &wm->id); + } + win->scene = newlibadr(fd, wm->id.lib, win->scene); + /* deprecated, but needed for versioning (will be NULL'ed then) */ win->screen = newlibadr(fd, NULL, win->screen); } @@ -6438,13 +6544,9 @@ static void lib_link_screen(FileData *fd, Main *main) IDP_LibLinkProperty(sc->id.properties, fd); id_us_ensure_real(&sc->id); + /* deprecated, but needed for versioning (will be NULL'ed then) */ sc->scene = newlibadr(fd, sc->id.lib, sc->scene); - /* this should not happen, but apparently it does somehow. Until we figure out the cause, - * just assign first available scene */ - if (!sc->scene) - sc->scene = main->scene.first; - sc->animtimer = NULL; /* saved in rare cases */ sc->scrubbing = false; @@ -6752,56 +6854,59 @@ static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map) BKE_sequencer_base_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map); } -/* called from kernel/blender.c */ -/* used to link a file (without UI) to the current UI */ -/* note that it assumes the old pointers in UI are still valid, so old Main is not freed */ -void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *curscene) +static void lib_link_workspace_scene_data_restore(wmWindow *win, Scene *scene) { - wmWindow *win; - wmWindowManager *wm; - bScreen *sc; - ScrArea *sa; + bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); - struct IDNameLib_Map *id_map = BKE_main_idmap_create(newmain); + for (ScrArea *area = screen->areabase.first; area; area = area->next) { + for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; - /* first windowmanager */ - for (wm = newmain->wm.first; wm; wm = wm->id.next) { - for (win= wm->windows.first; win; win= win->next) { - win->screen = restore_pointer_by_name(id_map, (ID *)win->screen, USER_REAL); - - if (win->screen == NULL) - win->screen = curscreen; - - win->screen->winid = win->winid; + if (v3d->camera == NULL || v3d->scenelock) { + v3d->camera = scene->camera; + } + + if (v3d->localvd) { + /*Base *base;*/ + + v3d->localvd->camera = scene->camera; + + /* localview can become invalid during undo/redo steps, so we exit it when no could be found */ +#if 0 /* XXX regionlocalview ? */ + for (base= sc->scene->base.first; base; base= base->next) { + if (base->lay & v3d->lay) break; + } + if (base==NULL) { + v3d->lay= v3d->localvd->lay; + v3d->layact= v3d->localvd->layact; + MEM_freeN(v3d->localvd); + v3d->localvd= NULL; + } +#endif + } + else if (v3d->scenelock) { + v3d->lay = scene->lay; + } + } } } - - - for (sc = newmain->screen.first; sc; sc = sc->id.next) { - Scene *oldscene = sc->scene; - - sc->scene= restore_pointer_by_name(id_map, (ID *)sc->scene, USER_REAL); - if (sc->scene == NULL) - sc->scene = curscene; - - /* keep cursor location through undo */ - copy_v3_v3(sc->scene->cursor, oldscene->cursor); - - for (sa = sc->areabase.first; sa; sa = sa->next) { - SpaceLink *sl; - - for (sl = sa->spacedata.first; sl; sl = sl->next) { +} + +static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, Main *newmain, WorkSpaceLayout *layout) +{ + bScreen *screen = BKE_workspace_layout_screen_get(layout); + + /* avoid conflicts with 2.8x branch */ + { + 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; BGpic *bgpic; ARegion *ar; - if (v3d->scenelock) - v3d->camera = NULL; /* always get from scene */ - else - v3d->camera = restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL); - if (v3d->camera == NULL) - v3d->camera = sc->scene->camera; + v3d->camera = restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL); v3d->ob_centre = restore_pointer_by_name(id_map, (ID *)v3d->ob_centre, USER_REAL); for (bgpic= v3d->bgpicbase.first; bgpic; bgpic= bgpic->next) { @@ -6812,27 +6917,6 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc id_us_plus((ID *)bgpic->clip); } } - if (v3d->localvd) { - /*Base *base;*/ - - v3d->localvd->camera = sc->scene->camera; - - /* localview can become invalid during undo/redo steps, so we exit it when no could be found */ -#if 0 /* XXX regionlocalview ? */ - for (base= sc->scene->base.first; base; base= base->next) { - if (base->lay & v3d->lay) break; - } - if (base==NULL) { - v3d->lay= v3d->localvd->lay; - v3d->layact= v3d->localvd->layact; - MEM_freeN(v3d->localvd); - v3d->localvd= NULL; - } -#endif - } - else if (v3d->scenelock) { - v3d->lay = sc->scene->lay; - } /* not very nice, but could help */ if ((v3d->layact & v3d->lay) == 0) v3d->layact = v3d->lay; @@ -7035,6 +7119,44 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc } } } +} + +/** + * Used to link a file (without UI) to the current UI. + * Note that it assumes the old pointers in UI are still valid, so old Main is not freed. + */ +void blo_lib_link_restore(Main *newmain, wmWindowManager *curwm, Scene *curscene, SceneLayer *cur_render_layer) +{ + struct IDNameLib_Map *id_map = BKE_main_idmap_create(newmain); + + for (WorkSpace *workspace = newmain->workspaces.first; workspace; workspace = workspace->id.next) { + ListBase *layouts = BKE_workspace_layouts_get(workspace); + + for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) { + lib_link_workspace_layout_restore(id_map, newmain, layout); + } + BKE_workspace_render_layer_set(workspace, cur_render_layer); + } + + for (wmWindow *win = curwm->windows.first; win; win = win->next) { + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + ID *workspace_id = (ID *)workspace; + Scene *oldscene = win->scene; + + workspace = restore_pointer_by_name(id_map, workspace_id, USER_REAL); + BKE_workspace_active_set(win->workspace_hook, workspace); + win->scene = restore_pointer_by_name(id_map, (ID *)win->scene, USER_REAL); + if (win->scene == NULL) { + win->scene = curscene; + } + BKE_workspace_active_set(win->workspace_hook, workspace); + + /* keep cursor location through undo */ + copy_v3_v3(win->scene->cursor, oldscene->cursor); + lib_link_workspace_scene_data_restore(win, win->scene); + + BLI_assert(win->screen == NULL); + } /* update IDs stored in all possible clipboards */ lib_link_clipboard_restore(id_map); @@ -8146,6 +8268,7 @@ static const char *dataname(short id_code) case ID_MSK: return "Data from MSK"; case ID_LS: return "Data from LS"; case ID_CF: return "Data from CF"; + case ID_WS: return "Data from WS"; } return "Data from Lib Block"; @@ -8309,7 +8432,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short wrong_id = direct_link_screen(fd, (bScreen *)id); break; case ID_SCE: - direct_link_scene(fd, (Scene *)id); + direct_link_scene(fd, (Scene *)id, main); break; case ID_OB: direct_link_object(fd, (Object *)id); @@ -8404,6 +8527,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short case ID_CF: direct_link_cachefile(fd, (CacheFile *)id); break; + case ID_WS: + direct_link_workspace(fd, (WorkSpace *)id, main); + break; } oldnewmap_free_unused(fd->datamap); @@ -8449,7 +8575,8 @@ static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead) bfd->curscreen = fg->curscreen; bfd->curscene = fg->curscene; - + bfd->cur_render_layer = fg->cur_render_layer; + MEM_freeN(fg); fd->globalf = bfd->globalf; @@ -8461,6 +8588,7 @@ static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead) /* note, this has to be kept for reading older files... */ static void link_global(FileData *fd, BlendFileData *bfd) { + bfd->cur_render_layer = newglobadr(fd, bfd->cur_render_layer); bfd->curscreen = newlibadr(fd, NULL, bfd->curscreen); bfd->curscene = newlibadr(fd, NULL, bfd->curscene); // this happens in files older than 2.35 @@ -8580,6 +8708,7 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_linestyle(fd, main); lib_link_gpencil(fd, main); lib_link_cachefiles(fd, main); + lib_link_workspaces(fd, main); lib_link_library(fd, main); /* only init users */ } @@ -8966,6 +9095,11 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) } } else { + /* in 2.50+ file identifier for screens is patched, forward compatibility */ + if (bhead->code == ID_SCRN) { + bhead->code = ID_SCR; + } + id = is_yet_read(fd, mainvar, bhead); if (id == NULL) { read_libblock(fd, mainvar, bhead, LIB_TAG_TESTIND, NULL); @@ -9817,6 +9951,15 @@ static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd) expand_animdata(fd, mainvar, gpd->adt); } +static void expand_workspace(FileData *fd, Main *mainvar, WorkSpace *workspace) +{ + ListBase *layouts = BKE_workspace_layouts_get(workspace); + + for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) { + expand_doit(fd, mainvar, BKE_workspace_layout_screen_get(layout)); + } +} + /** * Set the callback func used over all ID data found by \a BLO_expand_main func. * @@ -9931,6 +10074,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) case ID_CF: expand_cachefile(fd, mainvar, (CacheFile *)id); break; + case ID_WS: + expand_workspace(fd, mainvar, (WorkSpace *)id); + break; } do_it = true; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 3eeb0fc78e4..2d14238eb6f 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -45,6 +45,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_scene.h" +#include "BKE_workspace.h" #include "BLI_listbase.h" #include "BLI_mempool.h" @@ -55,6 +56,83 @@ #include "MEM_guardedalloc.h" + +static bScreen *screen_parent_find(const bScreen *screen) +{ + /* can avoid lookup if screen state isn't maximized/full (parent and child store the same state) */ + if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) { + for (const ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + if (sa->full && sa->full != screen) { + BLI_assert(sa->full->state == screen->state); + return sa->full; + } + } + } + + return NULL; +} + +static void do_version_workspaces_create_from_screens(Main *bmain) +{ + for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { + const bScreen *screen_parent = screen_parent_find(screen); + WorkSpace *workspace; + + if (screen_parent) { + /* fullscreen with "Back to Previous" option, don't create + * a new workspace, add layout workspace containing parent */ + workspace = BLI_findstring( + &bmain->workspaces, screen_parent->id.name + 2, offsetof(ID, name) + 2); + } + else { + workspace = BKE_workspace_add(bmain, screen->id.name + 2); + } + BKE_workspace_layout_add(workspace, screen, screen->id.name + 2); + BKE_workspace_render_layer_set(workspace, screen->scene->render_layers.first); + } +} + +/** + * \brief After lib-link versioning for new workspace design. + * + * * Adds a workspace for (almost) each screen of the old file + * and adds the needed workspace-layout to wrap the screen. + * * Active screen isn't stored directly in window anymore, but in the active workspace. + * * Active scene isn't stored in screen anymore, but in window. + * * Create workspace instance hook for each window. + * + * \note Some of the created workspaces might be deleted again in case of reading the default startup.blend. + */ +static void do_version_workspaces_after_lib_link(Main *bmain) +{ + BLI_assert(BLI_listbase_is_empty(&bmain->workspaces)); + + do_version_workspaces_create_from_screens(bmain); + + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen_parent = screen_parent_find(win->screen); + bScreen *screen = screen_parent ? screen_parent : win->screen; + WorkSpace *workspace = BLI_findstring(&bmain->workspaces, screen->id.name + 2, offsetof(ID, name) + 2); + ListBase *layouts = BKE_workspace_layouts_get(workspace); + + win->workspace_hook = BKE_workspace_instance_hook_create(bmain); + + BKE_workspace_active_set(win->workspace_hook, workspace); + BKE_workspace_active_layout_set(win->workspace_hook, layouts->first); + + win->scene = screen->scene; + /* Deprecated from now on! */ + win->screen = NULL; + } + } + + for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { + /* Deprecated from now on! */ + screen->scene = NULL; + } +} + void do_versions_after_linking_280(Main *main) { if (!MAIN_VERSION_ATLEAST(main, 280, 0)) { @@ -183,13 +261,18 @@ void do_versions_after_linking_280(Main *main) scene->basact = NULL; } } + } + if (!MAIN_VERSION_ATLEAST(main, 280, 0)) { for (bScreen *screen = main->screen.first; screen; screen = screen->id.next) { + /* same render-layer as do_version_workspaces_after_lib_link will activate, + * so same layer as BKE_scene_layer_context_active would return */ + SceneLayer *layer = screen->scene->render_layers.first; + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { if (sl->spacetype == SPACE_OUTLINER) { SpaceOops *soutliner = (SpaceOops *)sl; - SceneLayer *layer = BKE_scene_layer_context_active(screen->scene); soutliner->outlinevis = SO_ACT_LAYER; @@ -213,6 +296,11 @@ void do_versions_after_linking_280(Main *main) } } } + + /* New workspace design */ + if (!MAIN_VERSION_ATLEAST(main, 280, 1)) { + do_version_workspaces_after_lib_link(main); + } } static void do_version_layer_collections_idproperties(ListBase *lb) @@ -254,34 +342,35 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *main) } } } - } - if (!DNA_struct_elem_find(fd->filesdna, "GPUDOFSettings", "float", "ratio")) { - for (Camera *ca = main->camera.first; ca; ca = ca->id.next) { - ca->gpu_dof.ratio = 1.0f; + if (!MAIN_VERSION_ATLEAST(main, 280, 1)) { + if (!DNA_struct_elem_find(fd->filesdna, "GPUDOFSettings", "float", "ratio")) { + for (Camera *ca = main->camera.first; ca; ca = ca->id.next) { + ca->gpu_dof.ratio = 1.0f; + } } - } - if (!DNA_struct_elem_find(fd->filesdna, "SceneLayer", "IDProperty", "*properties")) { - for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { - for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { - IDPropertyTemplate val = {0}; - sl->properties = IDP_New(IDP_GROUP, &val, ROOT_PROP); - BKE_scene_layer_engine_settings_create(sl->properties); + if (!DNA_struct_elem_find(fd->filesdna, "SceneLayer", "IDProperty", "*properties")) { + for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { + for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { + IDPropertyTemplate val = {0}; + sl->properties = IDP_New(IDP_GROUP, &val, ROOT_PROP); + BKE_scene_layer_engine_settings_create(sl->properties); + } } } - } - /* MTexPoly now removed. */ - if (DNA_struct_find(fd->filesdna, "MTexPoly")) { - const int cd_mtexpoly = 15; /* CD_MTEXPOLY, deprecated */ - for (Mesh *me = main->mesh.first; me; me = me->id.next) { - /* If we have UV's, so this file will have MTexPoly layers too! */ - if (me->mloopuv != NULL) { - CustomData_update_typemap(&me->pdata); - CustomData_free_layers(&me->pdata, cd_mtexpoly, me->totpoly); - BKE_mesh_update_customdata_pointers(me, false); + /* MTexPoly now removed. */ + if (DNA_struct_find(fd->filesdna, "MTexPoly")) { + const int cd_mtexpoly = 15; /* CD_MTEXPOLY, deprecated */ + for (Mesh *me = main->mesh.first; me; me = me->id.next) { + /* If we have UV's, so this file will have MTexPoly layers too! */ + if (me->mloopuv != NULL) { + CustomData_update_typemap(&me->pdata); + CustomData_free_layers(&me->pdata, cd_mtexpoly, me->totpoly); + BKE_mesh_update_customdata_pointers(me, false); + } } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index a38f122ec56..002af20bc3a 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -40,10 +40,12 @@ #include "DNA_mesh_types.h" #include "DNA_material_types.h" #include "DNA_object_types.h" +#include "DNA_workspace_types.h" #include "BKE_brush.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_workspace.h" #include "BLO_readfile.h" @@ -84,6 +86,32 @@ void BLO_update_defaults_userpref_blend(void) } /** + * New workspace design: Remove all screens/workspaces except of "Default" one and rename the workspace to "General". + * For compatibility, a new workspace has been created for each screen of old files, + * we only want one workspace and one screen in the default startup file however. + */ +static void update_defaults_startup_workspaces(Main *bmain) +{ + WorkSpace *workspace_default = NULL; + + for (WorkSpace *workspace = bmain->workspaces.first, *workspace_next; workspace; workspace = workspace_next) { + workspace_next = workspace->id.next; + + if (STREQ(workspace->id.name + 2, "Default")) { + /* don't rename within iterator, renaming causes listbase to be re-sorted */ + workspace_default = workspace; + } + else { + BKE_workspace_remove(bmain, workspace); + } + } + + /* rename "Default" workspace to "General" */ + BKE_libblock_rename(bmain, (ID *)workspace_default, "General"); + BLI_assert(BLI_listbase_count(BKE_workspace_layouts_get(workspace_default)) == 1); +} + +/** * Update defaults in startup.blend, without having to save and embed the file. * This function can be emptied each time the startup.blend is updated. */ void BLO_update_defaults_startup_blend(Main *bmain) @@ -181,20 +209,18 @@ void BLO_update_defaults_startup_blend(Main *bmain) linestyle->chain_count = 10; } - for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *space_link; - ARegion *ar; + update_defaults_startup_workspaces(bmain); - for (space_link = area->spacedata.first; space_link; space_link = space_link->next) { + for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { + for (ScrArea *area = screen->areabase.first; area; area = area->next) { + for (SpaceLink *space_link = area->spacedata.first; space_link; space_link = space_link->next) { if (space_link->spacetype == SPACE_CLIP) { SpaceClip *space_clip = (SpaceClip *) space_link; space_clip->flag &= ~SC_MANUAL_CALIBRATION; } } - for (ar = area->regionbase.first; ar; ar = ar->next) { + for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) { /* Remove all stored panels, we want to use defaults (order, open/closed) as defined by UI code here! */ BLI_freelistN(&ar->panels); diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 19f6b9f370d..82d2e37dce0 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -102,6 +102,10 @@ /* allow writefile to use deprecated functionality (for forward compatibility code) */ #define DNA_DEPRECATED_ALLOW +/* Allow using DNA struct members that are marked as private for read/write. + * Note: Each header that uses this needs to define its own way of handling + * it. There's no generic implementation, direct use does nothing. */ +#define DNA_PRIVATE_READ_WRITE_ALLOW #include "DNA_anim_types.h" #include "DNA_armature_types.h" @@ -147,6 +151,7 @@ #include "DNA_vfont_types.h" #include "DNA_world_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "DNA_movieclip_types.h" #include "DNA_mask_types.h" @@ -173,6 +178,7 @@ #include "BKE_fcurve.h" #include "BKE_pointcache.h" #include "BKE_mesh.h" +#include "BKE_workspace.h" #ifdef USE_NODE_COMPAT_CUSTOMNODES #include "NOD_socket.h" /* for sock->default_value data */ @@ -1077,10 +1083,13 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree) * Take care using 'use_active_win', since we wont want the currently active window * to change which scene renders (currently only used for undo). */ -static void current_screen_compat(Main *mainvar, bScreen **r_screen, bool use_active_win) +static void current_screen_compat( + Main *mainvar, bool use_active_win, + bScreen **r_screen, Scene **r_scene, SceneLayer **r_render_layer) { wmWindowManager *wm; wmWindow *window = NULL; + WorkSpace *workspace; /* find a global current screen in the first open window, to have * a reasonable default for reading in older versions */ @@ -1104,8 +1113,11 @@ static void current_screen_compat(Main *mainvar, bScreen **r_screen, bool use_ac window = wm->windows.first; } } + workspace = (window) ? BKE_workspace_active_get(window->workspace_hook) : NULL; - *r_screen = (window) ? window->screen : NULL; + *r_screen = (window) ? BKE_workspace_active_screen_get(window->workspace_hook) : NULL; + *r_scene = (window) ? window->scene : NULL; + *r_render_layer = (window) ? BKE_workspace_render_layer_get(workspace) : NULL; } typedef struct RenderInfo { @@ -1121,13 +1133,11 @@ static void write_renderinfo(WriteData *wd, Main *mainvar) { bScreen *curscreen; Scene *sce, *curscene = NULL; + SceneLayer *render_layer; RenderInfo data; /* XXX in future, handle multiple windows with multiple screens? */ - current_screen_compat(mainvar, &curscreen, false); - if (curscreen) { - curscene = curscreen->scene; - } + current_screen_compat(mainvar, false, &curscreen, &curscene, &render_layer); for (sce = mainvar->scene.first; sce; sce = sce->id.next) { if (sce->id.lib == NULL && (sce == curscene || (sce->r.scemode & R_BG_RENDER))) { @@ -2842,8 +2852,19 @@ static void write_windowmanager(WriteData *wd, wmWindowManager *wm) write_iddata(wd, &wm->id); for (wmWindow *win = wm->windows.first; win; win = win->next) { + + /* update deprecated screen member (for so loading in 2.7x uses the correct screen) */ + win->screen = BKE_workspace_active_screen_get(win->workspace_hook); + if (win->screen) { + BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname)); + } + writestruct(wd, DATA, wmWindow, 1, win); + writestruct(wd, DATA, WorkSpaceInstanceHook, 1, win->workspace_hook); writestruct(wd, DATA, Stereo3dFormat, 1, win->stereo3d_format); + + /* data is written, clear deprecated data again */ + win->screen = NULL; } } @@ -2984,6 +3005,7 @@ static void write_screen(WriteData *wd, bScreen *sc) View3D *v3d = (View3D *)sl; BGpic *bgpic; writestruct(wd, DATA, View3D, 1, v3d); + for (bgpic = v3d->bgpicbase.first; bgpic; bgpic = bgpic->next) { writestruct(wd, DATA, BGpic, 1, bgpic); } @@ -3729,6 +3751,15 @@ static void write_cachefile(WriteData *wd, CacheFile *cache_file) } } +static void write_workspace(WriteData *wd, WorkSpace *workspace) +{ + ListBase *layouts = BKE_workspace_layouts_get(workspace); + + writestruct(wd, ID_WS, WorkSpace, 1, workspace); + writelist(wd, DATA, WorkSpaceLayout, layouts); + writelist(wd, DATA, WorkSpaceDataRelation, &workspace->hook_layout_relations); +} + /* Keep it last of write_foodata functions. */ static void write_libraries(WriteData *wd, Main *main) { @@ -3798,6 +3829,8 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar) const bool is_undo = (wd->current != NULL); FileGlobal fg; bScreen *screen; + Scene *scene; + SceneLayer *render_layer; char subvstr[8]; /* prevent mem checkers from complaining */ @@ -3805,11 +3838,12 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar) memset(fg.filename, 0, sizeof(fg.filename)); memset(fg.build_hash, 0, sizeof(fg.build_hash)); - current_screen_compat(mainvar, &screen, is_undo); + current_screen_compat(mainvar, is_undo, &screen, &scene, &render_layer); /* XXX still remap G */ fg.curscreen = screen; - fg.curscene = screen ? screen->scene : NULL; + fg.curscene = scene; + fg.cur_render_layer = render_layer; /* prevent to save this, is not good convention, and feature with concerns... */ fg.fileflags = (fileflags & ~G_FILE_FLAGS_RUNTIME); @@ -3905,6 +3939,9 @@ static bool write_file_handle( case ID_WM: write_windowmanager(wd, (wmWindowManager *)id); break; + case ID_WS: + write_workspace(wd, (WorkSpace *)id); + break; case ID_SCR: write_screen(wd, (bScreen *)id); break; diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index 1d76077c9f1..9a00df9a0d5 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -149,6 +149,7 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_TEXT "Text" #define BLT_I18NCONTEXT_ID_VFONT "VFont" #define BLT_I18NCONTEXT_ID_WORLD "World" +#define BLT_I18NCONTEXT_ID_WORKSPACE "WorkSpace" #define BLT_I18NCONTEXT_ID_WINDOWMANAGER "WindowManager" #define BLT_I18NCONTEXT_ID_MOVIECLIP "MovieClip" #define BLT_I18NCONTEXT_ID_MASK "Mask" @@ -203,6 +204,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_TEXT, "id_text"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_VFONT, "id_vfont"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORLD, "id_world"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORKSPACE, "id_workspace"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \ {NULL, NULL, NULL} \ } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index c3d64dc05bd..16d1f39f47f 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -49,6 +49,7 @@ extern "C" { #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_workspace.h" #define new new_ #include "BKE_screen.h" diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt index 1559512d713..f62db3c1ddb 100644 --- a/source/blender/editors/CMakeLists.txt +++ b/source/blender/editors/CMakeLists.txt @@ -35,7 +35,7 @@ if(WITH_BLENDER) add_subdirectory(object) add_subdirectory(physics) add_subdirectory(render) - add_subdirectory(screen) + add_subdirectory(scene) add_subdirectory(sculpt_paint) add_subdirectory(sound) add_subdirectory(space_action) @@ -60,6 +60,7 @@ if(WITH_BLENDER) add_subdirectory(transform) add_subdirectory(util) add_subdirectory(uvedit) + add_subdirectory(screen) endif() add_subdirectory(datafiles) diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 7ec19d12fb6..ef67411a48d 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -859,7 +859,7 @@ static void ed_marker_move_apply(bContext *C, wmOperator *op) BKE_scene_camera_switch_update(scene); if (camera != scene->camera) { - BKE_screen_view3d_scene_sync(sc); + BKE_screen_view3d_scene_sync(sc, scene); WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); } #endif @@ -1550,7 +1550,7 @@ static int ed_marker_camera_bind_exec(bContext *C, wmOperator *UNUSED(op)) /* camera may have changes */ BKE_scene_camera_switch_update(scene); - BKE_screen_view3d_scene_sync(sc); + BKE_screen_view3d_scene_sync(sc, scene); WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL); WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL); diff --git a/source/blender/editors/include/ED_scene.h b/source/blender/editors/include/ED_scene.h new file mode 100644 index 00000000000..2f67f7befae --- /dev/null +++ b/source/blender/editors/include/ED_scene.h @@ -0,0 +1,40 @@ +/* + * ***** 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 ED_scene.h + * \ingroup editors + */ + +#ifndef __ED_SCENE_H__ +#define __ED_SCENE_H__ + +#include "BLI_compiler_attrs.h" + +enum eSceneCopyMethod; + +struct Scene *ED_scene_add(struct Main *bmain, bContext *C, struct wmWindow *win, enum eSceneCopyMethod method) ATTR_NONNULL(); +bool ED_scene_delete(bContext *C, struct Main *bmain, struct wmWindow *win, struct Scene *scene) ATTR_NONNULL(); +void ED_scene_exit(bContext *C) ATTR_NONNULL(); +void ED_scene_changed_update(struct Main *bmain, bContext *C, struct Scene *scene_new, + const struct bScreen *active_screen) ATTR_NONNULL(); + +void ED_operatortypes_scene(void); + +#endif /* __ED_SCENE_H__ */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 1208b1f31b6..dec8bf7d615 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -43,6 +43,8 @@ struct wmWindow; struct wmNotifier; struct wmEvent; struct wmKeyConfig; +struct WorkSpace; +struct WorkSpaceInstanceHook; struct bContext; struct Scene; struct bScreen; @@ -107,12 +109,8 @@ void ED_screens_initialize(struct wmWindowManager *wm); void ED_screen_draw(struct wmWindow *win); void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win); void ED_screen_do_listen(struct bContext *C, struct wmNotifier *note); -bScreen *ED_screen_duplicate(struct wmWindow *win, struct bScreen *sc); -bScreen *ED_screen_add(struct wmWindow *win, struct Scene *scene, const char *name); -bool ED_screen_set(struct bContext *C, struct bScreen *sc); -bool ED_screen_delete(struct bContext *C, struct bScreen *sc); -void ED_screen_set_scene(struct bContext *C, struct bScreen *screen, struct Scene *scene); -bool ED_screen_delete_scene(struct bContext *C, struct Scene *scene); +bool ED_screen_change(struct bContext *C, struct bScreen *sc); +void ED_screen_update_after_scene_change(const struct bScreen *screen, struct Scene *scene_new); void ED_screen_set_subwinactive(struct bContext *C, struct wmEvent *event); void ED_screen_exit(struct bContext *C, struct wmWindow *window, struct bScreen *screen); void ED_screen_animation_timer(struct bContext *C, int redraws, int refresh, int sync, int enable); @@ -123,9 +121,42 @@ void ED_screen_full_prevspace(struct bContext *C, ScrArea *sa); void ED_screen_full_restore(struct bContext *C, ScrArea *sa); struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *sa, const short state); void ED_screens_header_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg); -bool ED_screen_stereo3d_required(struct bScreen *screen); +bool ED_screen_stereo3d_required(const struct bScreen *screen, const struct Scene *scene); +Scene *ED_screen_scene_find(const struct bScreen *screen, const struct wmWindowManager *wm); void ED_screen_preview_render(const struct bScreen *screen, int size_x, int size_y, unsigned int *r_rect) ATTR_NONNULL(); +/* workspaces */ +struct WorkSpace *ED_workspace_add( + struct Main *bmain, + const char *name, + SceneLayer *act_render_layer) ATTR_NONNULL(); +bool ED_workspace_change( + struct WorkSpace *workspace_new, + struct bContext *C, + struct wmWindowManager *wm, struct wmWindow *win) ATTR_NONNULL(); +struct WorkSpace *ED_workspace_duplicate( + struct WorkSpace *workspace_old, + struct Main *bmain, struct wmWindow *win); +bool ED_workspace_delete( + struct WorkSpace *workspace, + struct Main *bmain, struct bContext *C, + struct wmWindowManager *wm, struct wmWindow *win) ATTR_NONNULL(); +void ED_workspace_scene_data_sync( + struct WorkSpaceInstanceHook *hook, Scene *scene) ATTR_NONNULL(); +struct WorkSpaceLayout *ED_workspace_layout_add( + struct WorkSpace *workspace, + struct wmWindow *win, + const char *name) ATTR_NONNULL(); +struct WorkSpaceLayout *ED_workspace_layout_duplicate( + struct WorkSpace *workspace, const struct WorkSpaceLayout *layout_old, + struct wmWindow *win) ATTR_NONNULL(); +bool ED_workspace_layout_delete( + struct WorkSpace *workspace, struct WorkSpaceLayout *layout_old, + struct bContext *C) ATTR_NONNULL(); +bool ED_workspace_layout_cycle( + struct WorkSpace *workspace, const short direction, + struct bContext *C) ATTR_NONNULL(); + /* anim */ void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, int mute); @@ -137,6 +168,8 @@ bScreen *ED_screen_animation_no_scrub(const struct wmWindowManager *wm); /* screen keymaps */ void ED_operatortypes_screen(void); void ED_keymap_screen(struct wmKeyConfig *keyconf); +/* workspace keymaps */ +void ED_operatortypes_workspace(void); /* operators; context poll callbacks */ int ED_operator_screenactive(struct bContext *C); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 02243738d6c..667ca099305 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -262,8 +262,9 @@ void ED_view3d_calc_camera_border_size( const struct Scene *scene, const struct ARegion *ar, const struct View3D *v3d, const struct RegionView3D *rv3d, float r_size[2]); -bool ED_view3d_calc_render_border(struct Scene *scene, struct View3D *v3d, - struct ARegion *ar, struct rcti *rect); +bool ED_view3d_calc_render_border( + const struct Scene *scene, struct View3D *v3d, + struct ARegion *ar, struct rcti *rect); void ED_view3d_clipping_calc_from_boundbox(float clip[6][4], const struct BoundBox *clipbb, const bool is_flip); void ED_view3d_clipping_calc(struct BoundBox *bb, float planes[4][4], @@ -388,7 +389,7 @@ char ED_view3d_axis_view_opposite(char view); bool ED_view3d_lock(struct RegionView3D *rv3d); uint64_t ED_view3d_datamask(const struct Scene *scene, const struct View3D *v3d); -uint64_t ED_view3d_screen_datamask(const struct bScreen *screen); +uint64_t ED_view3d_screen_datamask(const struct Scene *scene, const struct bScreen *screen); bool ED_view3d_offset_lock_check(const struct View3D *v3d, const struct RegionView3D *rv3d); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 562a2f6e32c..be37ba46f7f 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2790,11 +2790,13 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]); } else { + const bScreen *screen = WM_window_get_active_screen(window); + /* no subwindow created yet, for menus for example, so we * use the main window instead, since buttons are created * there anyway */ - wm_subwindow_matrix_get(window, window->screen->mainwin, block->winmat); - wm_subwindow_size_get(window, window->screen->mainwin, &getsizex, &getsizey); + wm_subwindow_matrix_get(window, screen->mainwin, block->winmat); + wm_subwindow_size_get(window, screen->mainwin, &getsizex, &getsizey); block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]); block->auto_open = true; @@ -4628,7 +4630,10 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) if (ptr && prop) { if (!item) { int i; - + + /* so the context is passed to itemf functions */ + WM_operator_properties_sanitize(ptr, false); + RNA_property_enum_items_gettexted(C, ptr, prop, &items, &totitems, &free_items); for (i = 0, item = items; i < totitems; i++, item++) { if (item->identifier[0] && item->value == value) diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index bad47512d12..858c21dfdda 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -169,8 +169,8 @@ static void eyedropper_draw_cursor_text(const struct bContext *C, ARegion *ar, c */ static uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) { - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, event->x, event->y); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_ANY, event->x, event->y); uiBut *but = ui_but_find_mouse_over(ar, event); @@ -262,10 +262,9 @@ static void eyedropper_exit(bContext *C, wmOperator *op) */ static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int mx, int my, float r_col[3]) { - /* we could use some clever */ - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mx, my); const char *display_device = CTX_data_scene(C)->display_settings.display_device; struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); @@ -586,8 +585,8 @@ static void datadropper_exit(bContext *C, wmOperator *op) static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id) { /* we could use some clever */ - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, -1, mx, my); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, -1, mx, my); ScrArea *area_prev = CTX_wm_area(C); ARegion *ar_prev = CTX_wm_region(C); @@ -887,9 +886,9 @@ static void depthdropper_exit(bContext *C, wmOperator *op) static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, int my, float *r_depth) { /* we could use some clever */ - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my); - Scene *scene = win->screen->scene; + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mx, my); + Scene *scene = CTX_data_scene(C); UnitSettings *unit = &scene->unit; const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0; diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index ed8c3a50875..fd6d056f7d4 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -117,6 +117,9 @@ static ARegion *ui_region_temp_add(bScreen *sc) static void ui_region_temp_remove(bContext *C, bScreen *sc, ARegion *ar) { wmWindow *win = CTX_wm_window(C); + + BLI_assert(ar->regiontype == RGN_TYPE_TEMPORARY); + BLI_assert(BLI_findindex(&sc->regionbase, ar) != -1); if (win) wm_draw_region_clear(win, ar); @@ -3361,11 +3364,13 @@ void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block) /* if loading new .blend while popup is open, window will be NULL */ if (block->handle) { if (win) { + const bScreen *screen = WM_window_get_active_screen(win); + UI_popup_handlers_remove(&win->modalhandlers, block->handle); ui_popup_block_free(C, block->handle); /* In the case we have nested popups, closing one may need to redraw another, see: T48874 */ - for (ARegion *ar = win->screen->regionbase.first; ar; ar = ar->next) { + for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { ED_region_tag_refresh_ui(ar); } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 266d8aacae3..dbef5b68a03 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -448,6 +448,7 @@ static const char *template_id_browse_tip(const StructRNA *type) case ID_PAL: return N_("Browse Palette Data to be linked"); case ID_PC: return N_("Browse Paint Curve Data to be linked"); case ID_CF: return N_("Browse Cache Files to be linked"); + case ID_WS: return N_("Browse Workspace to be linked"); } } return N_("Browse ID data to be linked"); @@ -551,7 +552,7 @@ static void template_ID( if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT); - if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB))) { + if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) { uiDefButR(block, UI_BTYPE_TOGGLE, 0, "F", 0, 0, UI_UNIT_X, UI_UNIT_Y, &idptr, "use_fake_user", -1, 0, 0, -1, -1, NULL); } } @@ -585,6 +586,7 @@ static void template_ID( BLT_I18NCONTEXT_ID_PARTICLESETTINGS, BLT_I18NCONTEXT_ID_GPENCIL, BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, + BLT_I18NCONTEXT_ID_WORKSPACE, ); if (newop) { diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index f383719a747..fd1569ae42e 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -298,8 +298,7 @@ void ui_rna_collection_search_cb(const struct bContext *C, void *arg, const char } -/***************************** ID Utilities *******************************/ - +/***************************** ID Utilities *******************************/ int UI_icon_from_id(ID *id) { Object *ob; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index e8eda567a4a..09ec08fe265 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -2899,13 +2899,7 @@ void init_userdef_do_versions(void) btheme->ttime.time_keyframe[3] = btheme->ttime.time_gp_keyframe[3] = 255; } } - - /** - * Include next version bump. - * - * (keep this block even if it becomes empty). - */ - { + if (!USER_VERSION_ATLEAST(280, 1)) { /* interface_widgets.c */ struct uiWidgetColors wcol_tab = { {255, 255, 255, 255}, @@ -2925,6 +2919,15 @@ void init_userdef_do_versions(void) } } + /** + * Include next version bump. + * + * (keep this block even if it becomes empty). + */ + { + + } + if (U.pixelsize == 0.0f) U.pixelsize = 1.0f; diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 177168aa3c3..068caee4e1a 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1210,8 +1210,8 @@ static int object_delete_exec(bContext *C, wmOperator *op) /* delete has to handle all open scenes */ BKE_main_id_tag_listbase(&bmain->scene, LIB_TAG_DOIT, true); for (win = wm->windows.first; win; win = win->next) { - scene = win->screen->scene; - + scene = WM_window_get_active_scene(win); + if (scene->id.tag & LIB_TAG_DOIT) { scene->id.tag &= ~LIB_TAG_DOIT; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index abdbf3afd5a..fe232e37886 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -83,6 +83,7 @@ #include "BKE_modifier.h" #include "BKE_editmesh.h" #include "BKE_report.h" +#include "BKE_workspace.h" #include "ED_armature.h" #include "ED_curve.h" @@ -1499,8 +1500,15 @@ bool ED_object_mode_compat_set(bContext *C, Object *ob, int mode, ReportList *re { bool ok; if (!ELEM(ob->mode, mode, OB_MODE_OBJECT)) { + WorkSpace *workspace = CTX_wm_workspace(C); const char *opstring = object_mode_op_string(ob->mode); + WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, NULL); +#ifdef USE_WORKSPACE_MODE + BKE_workspace_object_mode_set(workspace, ob->mode); +#else + UNUSED_VARS(workspace); +#endif ok = ELEM(ob->mode, mode, OB_MODE_OBJECT); if (!ok) { wmOperatorType *ot = WM_operatortype_find(opstring, false); @@ -1610,13 +1618,23 @@ void OBJECT_OT_mode_set(wmOperatorType *ot) } - void ED_object_toggle_modes(bContext *C, int mode) { if (mode != OB_MODE_OBJECT) { const char *opstring = object_mode_op_string(mode); + if (opstring) { +#ifdef USE_WORKSPACE_MODE + WorkSpace *workspace = CTX_wm_workspace(C); +#endif WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, NULL); + +#ifdef USE_WORKSPACE_MODE + Object *ob = CTX_data_active_object(C); + if (ob) { + BKE_workspace_object_mode_set(workspace, ob->mode); + } +#endif } } } diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index 4bdd524f110..60c8fa6f28d 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -59,6 +59,7 @@ #include "BKE_property.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_workspace.h" #include "BKE_library.h" #include "BKE_deform.h" @@ -110,7 +111,12 @@ void ED_base_object_activate(bContext *C, BaseLegacy *base) BASACT = base; if (base) { - +#ifdef USE_WORKSPACE_MODE + WorkSpace *workspace = CTX_wm_workspace(C); + + BKE_workspace_object_mode_set(workspace, base->object->mode); +#endif + /* XXX old signals, remember to handle notifiers now! */ // select_actionchannel_by_name(base->object->action, "Object", 1); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index d4e9956b189..bdfbac3dba7 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -491,8 +491,9 @@ static void render_image_update_pass_and_layer(RenderJob *rj, RenderResult *rr, for (wm = rj->main->wm.first; wm && matched_sa == NULL; wm = wm->id.next) { /* only 1 wm */ wmWindow *win; for (win = wm->windows.first; win && matched_sa == NULL; win = win->next) { - ScrArea *sa; - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + const bScreen *screen = WM_window_get_active_screen(win); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_IMAGE) { SpaceImage *sima = sa->spacedata.first; // sa->spacedata might be empty when toggling fullscreen mode. @@ -618,8 +619,9 @@ static void render_image_restore_layer(RenderJob *rj) for (wm = rj->main->wm.first; wm; wm = wm->id.next) { /* only 1 wm */ wmWindow *win; for (win = wm->windows.first; win; win = win->next) { - ScrArea *sa; - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + const bScreen *screen = WM_window_get_active_screen(win); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { if (sa == rj->sa) { if (sa->spacetype == SPACE_IMAGE) { SpaceImage *sima = sa->spacedata.first; diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index e019bc9f697..ce52b9af4b2 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -66,6 +66,8 @@ #include "ED_render.h" #include "ED_view3d.h" +#include "WM_api.h" + #include "render_intern.h" // own include extern Material defmaterial; @@ -104,7 +106,7 @@ void ED_render_scene_update(Main *bmain, Scene *scene, int updated) wm = bmain->wm.first; for (win = wm->windows.first; win; win = win->next) { - bScreen *sc = win->screen; + bScreen *sc = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index c4a9af79ec2..e4bae9d78ea 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -90,8 +90,10 @@ static ScrArea *find_area_showing_r_result(bContext *C, Scene *scene, wmWindow * /* find an imagewindow showing render result */ for (*win = wm->windows.first; *win; *win = (*win)->next) { - if ((*win)->screen->scene == scene) { - for (sa = (*win)->screen->areabase.first; sa; sa = sa->next) { + if (WM_window_get_active_scene(*win) == scene) { + const bScreen *screen = WM_window_get_active_screen(*win); + + for (sa = screen->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_IMAGE) { sima = sa->spacedata.first; if (sima->image && sima->image->type == IMA_TYPE_R_RESULT) @@ -246,7 +248,7 @@ static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op)) } /* test if we have a temp screen in front */ - if (win->screen->temp) { + if (WM_window_is_temp_screen(win)) { wm_window_lower(win); return OPERATOR_FINISHED; } @@ -292,7 +294,7 @@ static int render_view_show_invoke(bContext *C, wmOperator *op, const wmEvent *e wmWindow *wincur = CTX_wm_window(C); /* test if we have currently a temp screen active */ - if (wincur->screen->temp) { + if (WM_window_is_temp_screen(wincur)) { wm_window_lower(wincur); } else { @@ -301,8 +303,9 @@ static int render_view_show_invoke(bContext *C, wmOperator *op, const wmEvent *e /* is there another window on current scene showing result? */ for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) { - bScreen *sc = win->screen; - if ((sc->temp && ((ScrArea *)sc->areabase.first)->spacetype == SPACE_IMAGE) || + const bScreen *sc = WM_window_get_active_screen(win); + + if ((WM_window_is_temp_screen(win) && ((ScrArea *)sc->areabase.first)->spacetype == SPACE_IMAGE) || (win == winshow && winshow != wincur)) { wm_window_raise(win); diff --git a/source/blender/editors/scene/CMakeLists.txt b/source/blender/editors/scene/CMakeLists.txt new file mode 100644 index 00000000000..72199ca5c1f --- /dev/null +++ b/source/blender/editors/scene/CMakeLists.txt @@ -0,0 +1,43 @@ +# ***** 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. +# +# Contributor(s): Jacques Beaurain. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + ../include + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../makesdna + ../../makesrna + ../../windowmanager +) + +set(INC_SYS + +) + +set(SRC + scene_edit.c +) + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +blender_add_lib(bf_editor_scene "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c new file mode 100644 index 00000000000..6956793435c --- /dev/null +++ b/source/blender/editors/scene/scene_edit.c @@ -0,0 +1,202 @@ +/* + * ***** 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/scene/scene_edit.c + * \ingroup edscene + */ + +#include <stdio.h> + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_library_remap.h" +#include "BKE_main.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" + +#include "BLI_compiler_attrs.h" +#include "BLI_listbase.h" + +#include "BLT_translation.h" + +#include "ED_object.h" +#include "ED_render.h" +#include "ED_scene.h" +#include "ED_screen.h" +#include "ED_util.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + + +Scene *ED_scene_add(Main *bmain, bContext *C, wmWindow *win, eSceneCopyMethod method) +{ + Scene *scene_new; + + if (method == SCE_COPY_NEW) { + scene_new = BKE_scene_add(bmain, DATA_("Scene")); + } + else { /* different kinds of copying */ + Scene *scene_old = WM_window_get_active_scene(win); + + scene_new = BKE_scene_copy(bmain, scene_old, method); + + /* these can't be handled in blenkernel currently, so do them here */ + if (method == SCE_COPY_LINK_DATA) { + ED_object_single_users(bmain, scene_new, false, true); + } + else if (method == SCE_COPY_FULL) { + ED_editors_flush_edits(C, false); + ED_object_single_users(bmain, scene_new, true, true); + } + } + + WM_window_change_active_scene(bmain, C, win, scene_new); + + WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, scene_new); + + return scene_new; +} + +/** + * \note Only call outside of area/region loops + * \return true if successful + */ +bool ED_scene_delete(bContext *C, Main *bmain, wmWindow *win, Scene *scene) +{ + Scene *scene_new; + + if (scene->id.prev) + scene_new = scene->id.prev; + else if (scene->id.next) + scene_new = scene->id.next; + else + return false; + + WM_window_change_active_scene(bmain, C, win, scene_new); + + BKE_libblock_remap(bmain, scene, scene_new, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); + BKE_libblock_free(bmain, scene); + + return true; +} + +void ED_scene_exit(bContext *C) +{ + ED_object_editmode_exit(C, EM_FREEDATA | EM_DO_UNDO); +} + +void ED_scene_changed_update(Main *bmain, bContext *C, Scene *scene_new, const bScreen *active_screen) +{ + /* XXX Just using active scene render-layer for workspace when switching, + * but workspace should remember the last one set. Could store render-layer + * per window-workspace combination (using WorkSpaceDataRelation) */ + SceneLayer *layer_new = BLI_findlink(&scene_new->render_layers, scene_new->active_layer); + + CTX_data_scene_set(C, scene_new); + BKE_workspace_render_layer_set(CTX_wm_workspace(C), layer_new); + BKE_scene_set_background(bmain, scene_new); + DAG_on_visible_update(bmain, false); + + ED_screen_update_after_scene_change(active_screen, scene_new); + ED_render_engine_changed(bmain); + ED_update_for_newframe(bmain, scene_new, 1); + + /* complete redraw */ + WM_event_add_notifier(C, NC_WINDOW, NULL); +} + +static int scene_new_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + int type = RNA_enum_get(op->ptr, "type"); + + ED_scene_add(bmain, C, win, type); + + 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", ""); +} + +static int scene_delete_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + + if (ED_scene_delete(C, CTX_data_main(C), CTX_wm_window(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; +} + +void ED_operatortypes_scene(void) +{ + WM_operatortype_append(SCENE_OT_new); + WM_operatortype_append(SCENE_OT_delete); +} 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; +} diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 1b18dd4e41c..64bd866c0db 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -1056,16 +1056,20 @@ static void toggle_paint_cursor(bContext *C, int enable) void ED_space_image_paint_update(wmWindowManager *wm, Scene *scene) { ToolSettings *settings = scene->toolsettings; - wmWindow *win; - ScrArea *sa; ImagePaintSettings *imapaint = &settings->imapaint; bool enabled = false; - for (win = wm->windows.first; win; win = win->next) - for (sa = win->screen->areabase.first; sa; sa = sa->next) - if (sa->spacetype == SPACE_IMAGE) - if (((SpaceImage *)sa->spacedata.first)->mode == SI_MODE_PAINT) + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + if (sa->spacetype == SPACE_IMAGE) { + if (((SpaceImage *)sa->spacedata.first)->mode == SI_MODE_PAINT) { enabled = true; + } + } + } + } if (enabled) { BKE_paint_init(scene, ePaintTexture2D, PAINT_CURSOR_TEXTURE_PAINT); diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index e76d83715a2..fca54b282f8 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -395,7 +395,11 @@ static void action_main_region_listener( if (wmn->action == NA_RENAME) ED_region_tag_redraw(ar); break; - + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; default: if (wmn->data == ND_KEYS) ED_region_tag_redraw(ar); diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 4d601a31251..3a431eb82df 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -55,6 +55,7 @@ #include "ED_paint.h" #include "ED_physics.h" #include "ED_render.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_space_api.h" @@ -99,6 +100,8 @@ void ED_spacetypes_init(void) // ... /* register operator types for screen and all spaces */ + ED_operatortypes_workspace(); + ED_operatortypes_scene(); ED_operatortypes_screen(); ED_operatortypes_anim(); ED_operatortypes_animchannels(); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 47297e84242..299ab7171d6 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -178,6 +178,20 @@ static void buttons_main_region_draw(const bContext *C, ARegion *ar) sbuts->mainbo = sbuts->mainb; } +static void buttons_main_region_listener( + bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn, + const Scene *UNUSED(scene)) +{ + /* context changes */ + switch (wmn->category) { + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; + } +} + static void buttons_operatortypes(void) { WM_operatortype_append(BUTTONS_OT_toolbox); @@ -471,6 +485,7 @@ void ED_spacetype_buttons(void) art->regionid = RGN_TYPE_WINDOW; art->init = buttons_main_region_init; art->draw = buttons_main_region_draw; + art->listener = buttons_main_region_listener; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 59dd755173f..942bc2661c3 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -305,15 +305,12 @@ bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *ar, int mval[2], float r void ED_clip_update_frame(const Main *mainp, int cfra) { - wmWindowManager *wm; - wmWindow *win; - /* image window, compo node users */ - for (wm = mainp->wm.first; wm; wm = wm->id.next) { /* only 1 wm */ - for (win = wm->windows.first; win; win = win->next) { - ScrArea *sa; + for (wmWindowManager *wm = mainp->wm.first; wm; wm = wm->id.next) { /* only 1 wm */ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_CLIP) { SpaceClip *sc = sa->spacedata.first; diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c index c1caf5ae8ac..fc870399696 100644 --- a/source/blender/editors/space_file/file_utils.c +++ b/source/blender/editors/space_file/file_utils.c @@ -26,6 +26,7 @@ #include "BLI_rect.h" #include "BLI_fileops.h" +#include "BLI_listbase.h" #include "BLO_readfile.h" diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index c2700233cf5..c03be5782be 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -215,7 +215,7 @@ short ED_fileselect_set_params(SpaceFile *sfile) FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | - FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF; + FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | FILTER_ID_WS; if (U.uiflag & USER_HIDE_DOT) { params->flag |= FILE_HIDE_DOT; diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 980ee5aeffe..c17b60badae 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -461,6 +461,11 @@ static void graph_region_listener( if (wmn->action == NA_RENAME) ED_region_tag_redraw(ar); break; + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; default: if (wmn->data == ND_KEYS) ED_region_tag_redraw(ar); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index baa1dcdffd9..56afdce7bb1 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -443,7 +443,6 @@ static void image_refresh(const bContext *C, ScrArea *sa) static void image_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn, const Scene *scene) { - SceneLayer *sl = BKE_scene_layer_context_active(scene); SpaceImage *sima = (SpaceImage *)sa->spacedata.first; /* context changes */ @@ -536,6 +535,7 @@ static void image_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn, co case ND_TRANSFORM: case ND_MODIFIER: { + SceneLayer *sl = BKE_scene_layer_context_active(scene); Object *ob = OBACT_NEW; if (ob && (ob == wmn->reference) && (ob->mode & OB_MODE_EDIT)) { if (sima->lock && (sima->flag & SI_DRAWSHADOW)) { @@ -828,6 +828,11 @@ static void image_main_region_listener( ED_region_tag_redraw(ar); } break; + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; } } diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index ab08b288470..4f042364c63 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -260,8 +260,9 @@ static void info_header_listener( /* context changes */ switch (wmn->category) { case NC_SCREEN: - if (ELEM(wmn->data, ND_SCREENCAST, ND_ANIMPLAY)) + if (ELEM(wmn->data, ND_LAYER, ND_SCREENCAST, ND_ANIMPLAY)) { ED_region_tag_redraw(ar); + } break; case NC_WM: if (wmn->data == ND_JOB) diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index a71f2f20a3f..ab7a913ce62 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -428,6 +428,11 @@ static void nla_main_region_listener( if (wmn->action == NA_RENAME) ED_region_tag_redraw(ar); break; + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; default: if (wmn->data == ND_KEYS) ED_region_tag_redraw(ar); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 3cb5fd94ea9..8568b0a676d 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -123,7 +123,7 @@ static int compo_get_recalc_flags(const bContext *C) int recalc_flags = 0; for (win = wm->windows.first; win; win = win->next) { - bScreen *sc = win->screen; + const bScreen *sc = WM_window_get_active_screen(win); ScrArea *sa; for (sa = sc->areabase.first; sa; sa = sa->next) { diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 4eeb4d02d8c..665808c500f 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -766,12 +766,13 @@ static void node_region_listener( } break; case NC_SCREEN: - if (wmn->data == ND_SCREENSET || wmn->action == NA_EDITED) { + if (wmn->data == ND_LAYOUTSET || wmn->action == NA_EDITED) { WM_manipulatormap_tag_refresh(mmap); } switch (wmn->data) { case ND_SCREENCAST: case ND_ANIMPLAY: + case ND_LAYER: ED_region_tag_redraw(ar); break; } diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 06b37377535..722bec1fa46 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -109,7 +109,7 @@ typedef struct TreeElement { #define TREESTORE_ID_TYPE(_id) \ (ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \ ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \ - ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL)) /* Only in 'blendfile' mode ... :/ */ + ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL, ID_WS)) /* Only in 'blendfile' mode ... :/ */ /* TreeElement->flag */ enum { diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 931af08c717..400a01a2908 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -160,7 +160,7 @@ static eOLDrawState tree_element_set_active_object( sce = (Scene *)outliner_search_back(soops, te, ID_SCE); if (sce && scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce); scene = sce; } @@ -392,7 +392,7 @@ static eOLDrawState tree_element_active_world( if (set != OL_SETSEL_NONE) { /* make new scene active */ if (sce && scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce); } } @@ -891,7 +891,7 @@ static void outliner_item_activate( /* editmode? */ if (te->idcode == ID_SCE) { if (scene != (Scene *)tselem->id) { - ED_screen_set_scene(C, CTX_wm_screen(C), (Scene *)tselem->id); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), (Scene *)tselem->id); } } else if (te->idcode == ID_GR) { diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 0591a8674d3..159a00ae0ed 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -67,6 +67,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" #include "ED_util.h" @@ -310,7 +311,7 @@ static bool scene_cb(bContext *C, eOutliner_PropSceneOps event, TreeElement *UNU Scene *scene = (Scene *)tselem->id; if (event == OL_SCENE_OP_DELETE) { - if (ED_screen_delete_scene(C, scene)) { + if (ED_scene_delete(C, CTX_data_main(C), CTX_wm_window(C), scene)) { WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene); } else { @@ -571,7 +572,7 @@ void outliner_do_object_operation_ex( // when objects selected in other scenes... dunno if that should be allowed Scene *scene_owner = (Scene *)outliner_search_back(soops, te, ID_SCE); if (scene_owner && scene_act != scene_owner) { - ED_screen_set_scene(C, CTX_wm_screen(C), scene_owner); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), scene_owner); } /* important to use 'scene_owner' not scene_act else deleting objects can crash. * only use 'scene_act' when 'scene_owner' is NULL, which can happen when the @@ -994,6 +995,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + wmWindow *win = CTX_wm_window(C); SpaceOops *soops = CTX_wm_space_outliner(C); int event; const char *str = NULL; @@ -1008,7 +1010,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) Scene *sce = scene; // to be able to delete, scenes are set... outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_select_cb); if (scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); + WM_window_change_active_scene(bmain, C, win, sce); } str = "Select Objects"; @@ -1018,8 +1020,8 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) Scene *sce = scene; // to be able to delete, scenes are set... outliner_do_object_operation_ex(C, op->reports, scene, soops, &soops->tree, object_select_hierarchy_cb, false); if (scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); - } + WM_window_change_active_scene(bmain, C, win, sce); + } str = "Select Object Hierarchy"; WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index b632db48caa..346e567dd8d 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -403,6 +403,11 @@ static void outliner_main_region_listener( if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) ED_region_tag_redraw(ar); break; + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; } } diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index 9a3ae2dcd70..283cd6c0e24 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -640,6 +640,11 @@ static void time_main_region_listener( if (wmn->data == ND_DATA) ED_region_tag_redraw(ar); break; + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; } } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 9c55e99b151..1dc9b169714 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -53,6 +53,7 @@ #include "BKE_object.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "ED_space_api.h" #include "ED_screen.h" @@ -804,7 +805,6 @@ static void *view3d_main_region_duplicate(void *poin) static void view3d_recalc_used_layers(ARegion *ar, wmNotifier *wmn, const Scene *scene) { wmWindow *win = wmn->wm->winactive; - ScrArea *sa; unsigned int lay_used = 0; BaseLegacy *base; @@ -820,7 +820,8 @@ static void view3d_recalc_used_layers(ARegion *ar, wmNotifier *wmn, const Scene base = base->next; } - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + const bScreen *screen = WM_window_get_active_screen(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_VIEW3D) { if (BLI_findindex(&sa->regionbase, ar) != -1) { View3D *v3d = sa->spacedata.first; @@ -865,18 +866,24 @@ static void view3d_main_region_listener( break; case NC_SCENE: switch (wmn->data) { + case ND_SCENEBROWSE: case ND_LAYER_CONTENT: if (wmn->reference) view3d_recalc_used_layers(ar, wmn, wmn->reference); ED_region_tag_redraw(ar); WM_manipulatormap_tag_refresh(mmap); break; + case ND_LAYER: + if (wmn->reference) { + BKE_screen_view3d_sync(v3d, wmn->reference); + } + ED_region_tag_redraw(ar); + break; case ND_FRAME: case ND_TRANSFORM: case ND_OB_ACTIVE: case ND_OB_SELECT: case ND_OB_VISIBLE: - case ND_LAYER: case ND_RENDER_OPTIONS: case ND_MARKERS: case ND_MODE: @@ -1056,18 +1063,15 @@ static void view3d_main_region_listener( case ND_SKETCH: ED_region_tag_redraw(ar); break; - case ND_SCREENBROWSE: - case ND_SCREENDELETE: - case ND_SCREENSET: - /* screen was changed, need to update used layers due to NC_SCENE|ND_LAYER_CONTENT */ - /* updates used layers only for View3D in active screen */ - if (wmn->reference) { - bScreen *sc_ref = wmn->reference; - view3d_recalc_used_layers(ar, wmn, sc_ref->scene); - } + case ND_LAYOUTBROWSE: + case ND_LAYOUTDELETE: + case ND_LAYOUTSET: WM_manipulatormap_tag_refresh(mmap); ED_region_tag_redraw(ar); break; + case ND_LAYER: + ED_region_tag_redraw(ar); + break; } break; @@ -1082,7 +1086,7 @@ static void view3d_main_region_listener( /* concept is to retrieve cursor type context-less */ static void view3d_main_region_cursor(wmWindow *win, ScrArea *UNUSED(sa), ARegion *UNUSED(ar)) { - Scene *scene = win->screen->scene; + const Scene *scene = WM_window_get_active_scene(win); if (scene->obedit) { WM_cursor_set(win, CURSOR_EDIT); diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c index bc74475e8cc..a27c5df9128 100644 --- a/source/blender/editors/space_view3d/view3d_draw_legacy.c +++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c @@ -1473,9 +1473,8 @@ CustomDataMask ED_view3d_datamask(const Scene *scene, const View3D *v3d) } /* goes over all modes and view3d settings */ -CustomDataMask ED_view3d_screen_datamask(const bScreen *screen) +CustomDataMask ED_view3d_screen_datamask(const Scene *scene, const bScreen *screen) { - const Scene *scene = screen->scene; CustomDataMask mask = CD_MASK_BAREMESH; /* check if we need tfaces & mcols due to view mode */ @@ -1767,14 +1766,14 @@ void ED_scene_draw_fps(Scene *scene, const rcti *rect) #endif } -static bool view3d_main_region_do_render_draw(Scene *scene) +static bool view3d_main_region_do_render_draw(const Scene *scene) { RenderEngineType *type = RE_engines_find(scene->r.engine); return (type && type->view_update && type->render_to_view); } -bool ED_view3d_calc_render_border(Scene *scene, View3D *v3d, ARegion *ar, rcti *rect) +bool ED_view3d_calc_render_border(const Scene *scene, View3D *v3d, ARegion *ar, rcti *rect) { RegionView3D *rv3d = ar->regiondata; bool use_border; diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 6d1393a2905..31677e08207 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -71,11 +71,10 @@ static void do_view3d_header_buttons(bContext *C, void *arg, int event); /* XXX quickly ported across */ static void handle_view3d_lock(bContext *C) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ScrArea *sa = CTX_wm_area(C); View3D *v3d = CTX_wm_view3d(C); - + if (v3d != NULL && sa != NULL) { if (v3d->localvd == NULL && v3d->scenelock && sa->spacetype == SPACE_VIEW3D) { /* copy to scene */ @@ -83,10 +82,6 @@ static void handle_view3d_lock(bContext *C) scene->layact = v3d->layact; scene->camera = v3d->camera; - /* not through notifier, listener don't have context - * and non-open screens or spaces need to be updated too */ - BKE_screen_view3d_main_sync(&bmain->screen, scene); - /* notifiers for scene update */ WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); } @@ -292,8 +287,8 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) bGPdata *gpd = CTX_data_gpencil_data(C); uiBlock *block; uiLayout *row; - bool is_paint = false; - int modeselect; + bool is_paint = ob && !(gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)) && + ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT); RNA_pointer_create(&screen->id, &RNA_SpaceView3D, v3d, &v3dptr); RNA_pointer_create(&scene->id, &RNA_ToolSettings, ts, &toolsptr); @@ -304,36 +299,6 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) /* other buttons: */ UI_block_emboss_set(block, UI_EMBOSS); - - /* mode */ - if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - modeselect = OB_MODE_GPENCIL; - } - else if (ob) { - modeselect = ob->mode; - is_paint = ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT); - } - else { - modeselect = OB_MODE_OBJECT; - } - - row = uiLayoutRow(layout, false); - { - EnumPropertyItem *item = rna_enum_object_mode_items; - const char *name = ""; - int icon = ICON_OBJECT_DATAMODE; - - while (item->identifier) { - if (item->value == modeselect && item->identifier[0]) { - name = IFACE_(item->name); - icon = item->icon; - break; - } - item++; - } - - uiItemMenuEnumO(row, C, "OBJECT_OT_mode_set", "mode", name, icon); - } row = uiLayoutRow(layout, true); uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 93554c374d9..53f343ed071 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1358,19 +1358,21 @@ static void game_set_commmandline_options(GameData *gm) static int game_engine_poll(bContext *C) { - bScreen *screen; + const wmWindow *win = CTX_wm_window(C); + const Scene *scene = WM_window_get_active_scene(win); + /* we need a context and area to launch BGE * it's a temporary solution to avoid crash at load time * if we try to auto run the BGE. Ideally we want the * context to be set as soon as we load the file. */ - if (CTX_wm_window(C) == NULL) return 0; - if ((screen = CTX_wm_screen(C)) == NULL) return 0; + if (win == NULL) return 0; + if (CTX_wm_screen(C) == NULL) return 0; if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) return 0; - if (!BKE_scene_uses_blender_game(screen->scene)) + if (!BKE_scene_uses_blender_game(scene)) return 0; return 1; diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 2a97384cf7d..a2a5307c707 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -229,11 +229,11 @@ static int delete_orientation_poll(bContext *C) if (ED_operator_areaactive(C) == 0) return 0; - + if (v3d) { selected_index = (v3d->twmode - V3D_MANIP_CUSTOM); } - + return selected_index >= 0; } diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index c9e40865164..2ba640500c7 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -381,8 +381,10 @@ void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target) void BIF_selectTransformOrientationValue(bContext *C, int orientation) { View3D *v3d = CTX_wm_view3d(C); - if (v3d) /* currently using generic poll */ + + if (v3d) { /* currently using generic poll */ v3d->twmode = orientation; + } } int BIF_countTransformOrientation(const bContext *C) diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index c0b30f93939..766f70d025c 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -75,6 +75,7 @@ set(SRC ../include/ED_particle.h ../include/ED_physics.h ../include/ED_render.h + ../include/ED_scene.h ../include/ED_screen.h ../include/ED_screen_types.h ../include/ED_sculpt.h diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c index b5c6deb6b01..00fb1a44dff 100644 --- a/source/blender/imbuf/intern/thumbs_blend.c +++ b/source/blender/imbuf/intern/thumbs_blend.c @@ -32,6 +32,7 @@ #include "BLI_endian_switch.h" #include "BLI_fileops.h" #include "BLI_linklist.h" +#include "BLI_listbase.h" /* Needed due to import of BLO_readfile.h */ #include "BLO_blend_defs.h" #include "BLO_readfile.h" diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 4f22d873a30..9a89bf859c7 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -265,6 +265,7 @@ typedef enum ID_Type { ID_PAL = MAKE_ID2('P', 'L'), /* Palette */ ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */ ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ + ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked datablocks. */ @@ -288,7 +289,7 @@ typedef enum ID_Type { #define ID_REAL_USERS(id) (((ID *)id)->us - ID_FAKE_USERS(id)) #define ID_EXTRA_USERS(id) (((ID *)id)->tag & LIB_TAG_EXTRAUSER ? 1 : 0) -#define ID_CHECK_UNDO(id) ((GS((id)->name) != ID_SCR) && (GS((id)->name) != ID_WM)) +#define ID_CHECK_UNDO(id) ((GS((id)->name) != ID_SCR) && (GS((id)->name) != ID_WM) && (GS((id)->name) != ID_WS)) #define ID_BLEND_PATH(_bmain, _id) ((_id)->lib ? (_id)->lib->filepath : (_bmain)->name) @@ -398,6 +399,7 @@ enum { FILTER_ID_WO = (1 << 26), FILTER_ID_PA = (1 << 27), FILTER_ID_CF = (1 << 28), + FILTER_ID_WS = (1 << 29), }; /* IMPORTANT: this enum matches the order currently use in set_lisbasepointers, @@ -435,6 +437,7 @@ enum { INDEX_ID_OB, INDEX_ID_LS, INDEX_ID_SCE, + INDEX_ID_WS, INDEX_ID_WM, INDEX_ID_MSK, INDEX_ID_NULL, diff --git a/source/blender/makesdna/DNA_defs.h b/source/blender/makesdna/DNA_defs.h index fc9dd8f06a9..bfa8da02707 100644 --- a/source/blender/makesdna/DNA_defs.h +++ b/source/blender/makesdna/DNA_defs.h @@ -44,6 +44,12 @@ # endif #endif +#ifdef __GNUC__ +# define DNA_PRIVATE_ATTR __attribute__ ((deprecated)) +#else +# define DNA_PRIVATE_ATTR +#endif + /* poison pragma */ #ifdef DNA_DEPRECATED_ALLOW diff --git a/source/blender/makesdna/DNA_fileglobal_types.h b/source/blender/makesdna/DNA_fileglobal_types.h index b2ab0d2a08d..6a01878a9ef 100644 --- a/source/blender/makesdna/DNA_fileglobal_types.h +++ b/source/blender/makesdna/DNA_fileglobal_types.h @@ -32,9 +32,6 @@ #ifndef __DNA_FILEGLOBAL_TYPES_H__ #define __DNA_FILEGLOBAL_TYPES_H__ -struct bScreen; -struct Scene; - /** * FileGlobal stores a part of the current user-interface settings at * the moment of saving, and the file-specific settings. @@ -46,6 +43,9 @@ typedef struct FileGlobal { char pad[6]; struct bScreen *curscreen; struct Scene *curscene; + struct SceneLayer *cur_render_layer; + void *pad1; + int fileflags; int globalf; uint64_t build_commit_timestamp; /* commit timestamp from buildinfo */ diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index ce1f8d1a7d4..2524bad7c0f 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -30,6 +30,7 @@ #ifndef __DNA_SCREEN_TYPES_H__ #define __DNA_SCREEN_TYPES_H__ +#include "DNA_defs.h" #include "DNA_listBase.h" #include "DNA_view2d_types.h" #include "DNA_vec_types.h" @@ -52,10 +53,9 @@ typedef struct bScreen { ListBase edgebase; ListBase areabase; ListBase regionbase; /* screen level regions (menus), runtime only */ - - struct Scene *scene; - struct Scene *newscene; /* temporary when switching */ - + + struct Scene *scene DNA_DEPRECATED; + short winid; /* winid from WM, starts with 1 */ short redraws_flag; /* user-setting for which editors get redrawn during anim playback (used to be time->redraws) */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 256d53eed0b..9f5d33531f6 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -601,7 +601,7 @@ typedef enum eUserPref_Flag { /* USER_AUTOGRABGRID = (1 << 1), deprecated */ /* USER_AUTOROTGRID = (1 << 2), deprecated */ /* USER_AUTOSIZEGRID = (1 << 3), deprecated */ - USER_SCENEGLOBAL = (1 << 4), +/* USER_SCENEGLOBAL = (1 << 4), deprecated */ USER_TRACKBALL = (1 << 5), /* USER_DUPLILINK = (1 << 6), deprecated */ /* USER_FSCOLLUM = (1 << 7), deprecated */ diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 14400c84b69..8558e3b5a08 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -177,9 +177,13 @@ typedef struct wmWindow { void *ghostwin; /* don't want to include ghost.h stuff */ - struct bScreen *screen; /* active screen */ - struct bScreen *newscreen; /* temporary when switching */ - char screenname[64]; /* MAX_ID_NAME for matching window with active screen after file read */ + struct Scene *scene; /* The scene displayed in this window. */ + struct Scene *new_scene; /* temporary when switching */ + + struct WorkSpaceInstanceHook *workspace_hook; + + struct bScreen *screen DNA_DEPRECATED; + char screenname[64]; /* MAX_ID_NAME for matching window with active screen after file read */ short posx, posy, sizex, sizey; /* window coords */ short windowstate; /* borderless, full */ diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h new file mode 100644 index 00000000000..56221242fbf --- /dev/null +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -0,0 +1,132 @@ +/* + * ***** 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 DNA_workspace_types.h + * \ingroup DNA + * + * Use API in BKE_workspace.h! + * Struct members marked with DNA_PRIVATE_WORKSPACE will throw a + * warning saying it's deprecated when used outside of workspace.c. + */ + +#ifndef __DNA_WORKSPACE_TYPES_H__ +#define __DNA_WORKSPACE_TYPES_H__ + + +/* Same logic as DNA_DEPRECATED_ALLOW, but throws 'deprecated' + * warnings if DNA_PRIVATE_WORKSPACE_ALLOW is not defined */ +#ifdef DNA_PRIVATE_WORKSPACE_ALLOW + /* allow use of private items */ +# define DNA_PRIVATE_WORKSPACE +#else +# ifndef DNA_PRIVATE_WORKSPACE +# define DNA_PRIVATE_WORKSPACE DNA_PRIVATE_ATTR +# endif +#endif + +#ifdef DNA_PRIVATE_READ_WRITE_ALLOW +# define DNA_PRIVATE_WORKSPACE_READ_WRITE +#else +# ifndef DNA_PRIVATE_WORKSPACE_READ_WRITE +# define DNA_PRIVATE_WORKSPACE_READ_WRITE DNA_PRIVATE_WORKSPACE +# endif +#endif + + +/** + * \brief Wrapper for bScreen. + * + * bScreens are IDs and thus stored in a main list-base. We also want to store a list-base of them within the + * workspace (so each workspace can have its own set of screen-layouts) which would mess with the next/prev pointers. + * So we use this struct to wrap a bScreen pointer with another pair of next/prev pointers. + */ +typedef struct WorkSpaceLayout { + struct WorkSpaceLayout *next, *prev; + + struct bScreen *screen DNA_PRIVATE_WORKSPACE; + /* The name of this layout, we override the RNA name of the screen with this (but not ID name itself) */ + char name[64] DNA_PRIVATE_WORKSPACE; /* MAX_NAME */ +} WorkSpaceLayout; + +typedef struct WorkSpace { + ID id; + + ListBase layouts DNA_PRIVATE_WORKSPACE; /* WorkSpaceLayout */ + /* Store for each hook (so for each window) which layout has + * been activated the last time this workspace was visible. */ + ListBase hook_layout_relations DNA_PRIVATE_WORKSPACE_READ_WRITE; /* WorkSpaceDataRelation */ + + int object_mode DNA_PRIVATE_WORKSPACE; /* enum ObjectMode */ + int pad; + + struct SceneLayer *render_layer DNA_PRIVATE_WORKSPACE; +} WorkSpace; + +/* internal struct, but exported for read/write */ +#if defined(DNA_PRIVATE_READ_WRITE_ALLOW) || defined(DNA_PRIVATE_WORKSPACE_ALLOW) + +/** + * Generic (and simple/primitive) struct for storing a history of assignments/relations + * of workspace data to non-workspace data in a listbase inside the workspace. + * + * Using this we can restore the old state of a workspace if the user switches back to it. + * + * Usage + * ===== + * When activating a workspace, it should activate the screen-layout that was active in that + * workspace before *in this window*. + * More concretely: + * * There are two windows, win1 and win2. + * * Both show workspace ws1, but both also had workspace ws2 activated at some point before. + * * Last time ws2 was active in win1, screen-layout sl1 was activated. + * * Last time ws2 was active in win2, screen-layout sl2 was activated. + * * When changing from ws1 to ws2 in win1, screen-layout sl1 should be activated again. + * * When changing from ws1 to ws2 in win2, screen-layout sl2 should be activated again. + * So that means we have to store the active screen-layout in a per workspace, per window + * relation. This struct is used to store an active screen-layout for each window within the + * workspace. + * To find the screen-layout to activate for this window-workspace combination, simply lookup + * the WorkSpaceDataRelation with the workspace-hook of the window set as parent. + */ +typedef struct WorkSpaceDataRelation { + struct WorkSpaceDataRelation *next, *prev; + + /* the data used to identify the relation (e.g. to find screen-layout (= value) from/for a hook) */ + void *parent; + /* The value for this parent-data/workspace relation */ + void *value; +} WorkSpaceDataRelation; + +#endif /* DNA_PRIVATE_WORKSPACE_READ_WRITE */ + +/** + * Little wrapper to store data that is going to be per window, but comming from the workspace. + * It allows us to keep workspace and window data completely separate. + */ +typedef struct WorkSpaceInstanceHook { + WorkSpace *active DNA_PRIVATE_WORKSPACE; + struct WorkSpaceLayout *act_layout DNA_PRIVATE_WORKSPACE; + + /* Needed because we can't change workspaces/layouts in running handler loop, it would break context. */ + WorkSpace *temp_workspace_store; + struct WorkSpaceLayout *temp_layout_store; +} WorkSpaceInstanceHook; + +#endif /* __DNA_WORKSPACE_TYPES_H__ */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index e761d68ca64..ad9a03703b4 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -56,6 +56,9 @@ #include "../blenlib/BLI_sys_types.h" // for intptr_t support +/* Allow includinsg DNA files for specially guarded namespaces */ +#define DNA_NAMESPACE + #define SDNA_MAX_FILENAME_LENGTH 255 @@ -132,6 +135,8 @@ static const char *includefiles[] = { "DNA_linestyle_types.h", "DNA_cachefile_types.h", "DNA_layer_types.h", + "DNA_workspace_types.h", + /* see comment above before editing! */ /* empty string to indicate end of includefiles */ @@ -506,6 +511,17 @@ static int preprocess_include(char *maindata, int len) a -= 13; cp += 13; } + else if (strncmp("DNA_PRIVATE_WORKSPACE", cp, 21) == 0) { + /* Check for DNA_PRIVATE_WORKSPACE_READ_WRITE */ + if (strncmp("_READ_WRITE", cp + 21, 11) == 0) { + a -= 31; + cp += 31; + } + else { + a -= 20; + cp += 20; + } + } else { md[0] = cp[0]; md++; @@ -1346,4 +1362,6 @@ int main(int argc, char **argv) #include "DNA_linestyle_types.h" #include "DNA_cachefile_types.h" #include "DNA_layer_types.h" +#include "DNA_workspace_types.h" + /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 127977c23bb..2982c459169 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -707,6 +707,7 @@ extern StructRNA RNA_WindowManager; extern StructRNA RNA_WipeSequence; extern StructRNA RNA_WireframeModifier; extern StructRNA RNA_WoodTexture; +extern StructRNA RNA_WorkSpace; extern StructRNA RNA_World; extern StructRNA RNA_WorldAmbientOcclusion; extern StructRNA RNA_WorldLighting; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 020c3a755c1..899c9bf77a9 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -94,6 +94,7 @@ set(DEFSRC rna_userdef.c rna_vfont.c rna_wm.c + rna_workspace.c rna_world.c ) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index de436172bfd..bac221f96ab 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -3364,6 +3364,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_userdef.c", NULL, RNA_def_userdef}, {"rna_vfont.c", "rna_vfont_api.c", RNA_def_vfont}, {"rna_wm.c", "rna_wm_api.c", RNA_def_wm}, + {"rna_workspace.c", NULL, RNA_def_workspace}, {"rna_world.c", NULL, RNA_def_world}, {"rna_movieclip.c", NULL, RNA_def_movieclip}, {"rna_tracking.c", NULL, RNA_def_tracking}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 4291e5b63ae..f36c8d0cc0d 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -82,6 +82,7 @@ EnumPropertyItem rna_enum_id_type_items[] = { {ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Texture", ""}, {ID_WM, "WINDOWMANAGER", ICON_FULLSCREEN, "Window Manager", ""}, {ID_WO, "WORLD", ICON_WORLD_DATA, "World", ""}, + {ID_WS, "WORKSPACE", ICON_NONE, "Workspace", ""}, {0, NULL, 0, NULL, NULL} }; @@ -169,6 +170,7 @@ short RNA_type_to_ID_code(const StructRNA *type) if (RNA_struct_is_a(type, &RNA_Texture)) return ID_TE; if (RNA_struct_is_a(type, &RNA_Text)) return ID_TXT; if (RNA_struct_is_a(type, &RNA_VectorFont)) return ID_VF; + if (RNA_struct_is_a(type, &RNA_WorkSpace)) return ID_WS; if (RNA_struct_is_a(type, &RNA_World)) return ID_WO; if (RNA_struct_is_a(type, &RNA_WindowManager)) return ID_WM; @@ -211,6 +213,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_VF: return &RNA_VectorFont; case ID_WM: return &RNA_WindowManager; case ID_WO: return &RNA_World; + case ID_WS: return &RNA_WorkSpace; default: return &RNA_ID; } diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index 360bd5691cd..a0b46a447ae 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -51,6 +51,12 @@ static PointerRNA rna_Context_window_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_Window, CTX_wm_window(C)); } +static PointerRNA rna_Context_workspace_get(PointerRNA *ptr) +{ + bContext *C = (bContext *)ptr->data; + return rna_pointer_inherit_refine(ptr, &RNA_WorkSpace, CTX_wm_workspace(C)); +} + static PointerRNA rna_Context_screen_get(PointerRNA *ptr) { bContext *C = (bContext *)ptr->data; @@ -193,6 +199,11 @@ void RNA_def_context(BlenderRNA *brna) RNA_def_property_struct_type(prop, "Window"); RNA_def_property_pointer_funcs(prop, "rna_Context_window_get", NULL, NULL, NULL); + prop = RNA_def_property(srna, "workspace", PROP_POINTER, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_struct_type(prop, "WorkSpace"); + RNA_def_property_pointer_funcs(prop, "rna_Context_workspace_get", NULL, NULL, NULL); + prop = RNA_def_property(srna, "screen", PROP_POINTER, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "Screen"); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 75fc9a17b07..878ee722c2d 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -186,6 +186,7 @@ void RNA_def_ui(struct BlenderRNA *brna); void RNA_def_userdef(struct BlenderRNA *brna); void RNA_def_vfont(struct BlenderRNA *brna); void RNA_def_wm(struct BlenderRNA *brna); +void RNA_def_workspace(struct BlenderRNA *brna); void RNA_def_world(struct BlenderRNA *brna); void RNA_def_movieclip(struct BlenderRNA *brna); void RNA_def_tracking(struct BlenderRNA *brna); @@ -335,6 +336,7 @@ void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop); +void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 94687b6fd46..4f52ac97961 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -293,6 +293,12 @@ static void rna_Main_paintcurves_begin(CollectionPropertyIterator *iter, Pointer rna_iterator_listbase_begin(iter, &bmain->paintcurves, NULL); } +static void rna_Main_workspaces_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Main *bmain = (Main *)ptr->data; + rna_iterator_listbase_begin(iter, &bmain->workspaces, NULL); +} + static void rna_Main_version_get(PointerRNA *ptr, int *value) { Main *bmain = (Main *)ptr->data; @@ -368,6 +374,7 @@ void RNA_def_main(BlenderRNA *brna) {"linestyles", "FreestyleLineStyle", "rna_Main_linestyle_begin", "Line Styles", "Line Style data-blocks", RNA_def_main_linestyles}, {"cache_files", "CacheFile", "rna_Main_cachefiles_begin", "Cache Files", "Cache Files data-blocks", RNA_def_main_cachefiles}, {"paint_curves", "PaintCurve", "rna_Main_paintcurves_begin", "Paint Curves", "Paint Curves data-blocks", RNA_def_main_paintcurves}, + {"workspaces", "WorkSpace", "rna_Main_workspaces_begin", "Workspaces", "Workspace data-blocks", RNA_def_main_workspaces}, {NULL, NULL, NULL, NULL, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 244820f5a49..cd0fe461428 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -84,6 +84,7 @@ #include "BKE_mask.h" #include "BKE_gpencil.h" #include "BKE_linestyle.h" +#include "BKE_workspace.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" @@ -115,6 +116,8 @@ # include "BPY_extern.h" #endif +#include "WM_api.h" + static void rna_idname_validate(const char *name, char *r_name) { @@ -169,14 +172,15 @@ static void rna_Main_scenes_remove(Main *bmain, bContext *C, ReportList *reports (scene_new = scene->id.next)) { if (do_unlink) { - bScreen *sc = CTX_wm_screen(C); - if (sc->scene == scene) { + wmWindow *win = CTX_wm_window(C); + + if (WM_window_get_active_scene(win) == scene) { #ifdef WITH_PYTHON BPy_BEGIN_ALLOW_THREADS; #endif - ED_screen_set_scene(C, sc, scene_new); + WM_window_change_active_scene(bmain, C, win, scene_new); #ifdef WITH_PYTHON BPy_END_ALLOW_THREADS; @@ -610,6 +614,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(masks, mask, ID_MSK) RNA_MAIN_ID_TAG_FUNCS_DEF(linestyle, linestyle, ID_LS) RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF) RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC) +RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS) #undef RNA_MAIN_ID_TAG_FUNCS_DEF @@ -1819,4 +1824,25 @@ void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_boolean_funcs(prop, "rna_Main_linestyle_is_updated_get", NULL); } +void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + PropertyRNA *prop; + + RNA_def_property_srna(cprop, "BlendDataWorkSpaces"); + srna = RNA_def_struct(brna, "BlendDataWorkSpaces", NULL); + RNA_def_struct_sdna(srna, "Main"); + RNA_def_struct_ui_text(srna, "Main Workspaces", "Collection of workspaces"); + + func = RNA_def_function(srna, "tag", "rna_Main_workspaces_tag"); + parm = RNA_def_boolean(func, "value", 0, "Value", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_Main_workspaces_is_updated_get", NULL); +} + #endif diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 4ff2e03ef9c..266838153e9 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -763,19 +763,21 @@ static int rna_Scene_active_layer_get(PointerRNA *ptr) static void rna_Scene_view3d_update(Main *bmain, Scene *UNUSED(scene_unused), PointerRNA *ptr) { + wmWindowManager *wm = bmain->wm.first; Scene *scene = (Scene *)ptr->data; - BKE_screen_view3d_main_sync(&bmain->screen, scene); + WM_windows_scene_data_sync(&wm->windows, scene); } -static void rna_Scene_layer_update(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_Scene_layer_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) { - rna_Scene_view3d_update(bmain, scene, ptr); /* XXX We would need do_time=true here, else we can have update issues like [#36289]... * However, this has too much drawbacks (like slower layer switch, undesired updates...). * That's TODO for future DAG updates. */ DAG_on_visible_update(bmain, false); + + /* No need to sync scene data here (WM_windows_scene_data_sync), handled through notifier. */ } static void rna_Scene_fps_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index b44e404c364..a145ac95d6b 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -57,6 +57,7 @@ EnumPropertyItem rna_enum_region_type_items[] = { #include "BKE_global.h" #include "BKE_depsgraph.h" +#include "BKE_workspace.h" #include "UI_view2d.h" @@ -64,55 +65,58 @@ EnumPropertyItem rna_enum_region_type_items[] = { # include "BPY_extern.h" #endif -static void rna_Screen_scene_set(PointerRNA *ptr, PointerRNA value) -{ - bScreen *sc = (bScreen *)ptr->data; - if (value.data == NULL) - return; +static void rna_Screen_redraw_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + bScreen *screen = (bScreen *)ptr->data; - sc->newscene = value.data; + /* the settings for this are currently only available from a menu in the TimeLine, hence refresh=SPACE_TIME */ + ED_screen_animation_timer_update(screen, screen->redraws_flag, SPACE_TIME); } -static void rna_Screen_scene_update(bContext *C, PointerRNA *ptr) +static int rna_Screen_is_animation_playing_get(PointerRNA *UNUSED(ptr)) { - bScreen *sc = (bScreen *)ptr->data; - - /* exception: must use context so notifier gets to the right window */ - if (sc->newscene) { -#ifdef WITH_PYTHON - BPy_BEGIN_ALLOW_THREADS; -#endif - - ED_screen_set_scene(C, sc, sc->newscene); - -#ifdef WITH_PYTHON - BPy_END_ALLOW_THREADS; -#endif - - WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, sc->newscene); + /* can be NULL on file load, T42619 */ + wmWindowManager *wm = G.main->wm.first; + return wm ? (ED_screen_animation_playing(wm) != NULL) : 0; +} - if (G.debug & G_DEBUG) - printf("scene set %p\n", sc->newscene); +static void rna_Screen_layout_name_get(PointerRNA *ptr, char *value) +{ + const bScreen *screen = ptr->data; + const WorkSpaceLayout *layout = BKE_workspace_layout_find_global(G.main, screen, NULL); - sc->newscene = NULL; + if (layout) { + const char *name = BKE_workspace_layout_name_get(layout); + strcpy(value, name); + } + else { + value[0] = '\0'; } } -static void rna_Screen_redraw_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static int rna_Screen_layout_name_length(PointerRNA *ptr) { - bScreen *screen = (bScreen *)ptr->data; + const bScreen *screen = ptr->data; + const WorkSpaceLayout *layout = BKE_workspace_layout_find_global(G.main, screen, NULL); - /* the settings for this are currently only available from a menu in the TimeLine, hence refresh=SPACE_TIME */ - ED_screen_animation_timer_update(screen, screen->redraws_flag, SPACE_TIME); -} + if (layout) { + const char *name = BKE_workspace_layout_name_get(layout); + return strlen(name); + } + return 0; +} -static int rna_Screen_is_animation_playing_get(PointerRNA *UNUSED(ptr)) +static void rna_Screen_layout_name_set(PointerRNA *ptr, const char *value) { - /* can be NULL on file load, T42619 */ - wmWindowManager *wm = G.main->wm.first; - return wm ? (ED_screen_animation_playing(wm) != NULL) : 0; + bScreen *screen = ptr->data; + WorkSpace *workspace; + WorkSpaceLayout *layout = BKE_workspace_layout_find_global(G.main, screen, &workspace); + + if (layout) { + BKE_workspace_layout_name_set(workspace, layout, value); + } } static int rna_Screen_fullscreen_get(PointerRNA *ptr) @@ -152,7 +156,7 @@ static void rna_Area_type_update(bContext *C, PointerRNA *ptr) /* XXX this call still use context, so we trick it to work in the right context */ for (win = wm->windows.first; win; win = win->next) { - if (sc == win->screen) { + if (sc == WM_window_get_active_screen(win)) { wmWindow *prevwin = CTX_wm_window(C); ScrArea *prevsa = CTX_wm_area(C); ARegion *prevar = CTX_wm_region(C); @@ -377,13 +381,11 @@ static void rna_def_screen(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Screen", "Screen data-block, defining the layout of areas in a window"); RNA_def_struct_ui_icon(srna, ICON_SPLITSCREEN); - /* pointers */ - prop = RNA_def_property(srna, "scene", PROP_POINTER, PROP_NONE); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); - RNA_def_property_pointer_funcs(prop, NULL, "rna_Screen_scene_set", NULL, NULL); - RNA_def_property_ui_text(prop, "Scene", "Active scene to be edited in the screen"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - RNA_def_property_update(prop, 0, "rna_Screen_scene_update"); + prop = RNA_def_property(srna, "layout_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, "rna_Screen_layout_name_get", "rna_Screen_layout_name_length", + "rna_Screen_layout_name_set"); + RNA_def_property_ui_text(prop, "Layout Name", "The name of the layout that refers to the screen"); + RNA_def_struct_name_property(srna, prop); /* collections */ prop = RNA_def_property(srna, "areas", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 0eedcb25b4c..0e16b410dc7 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -256,6 +256,7 @@ EnumPropertyItem rna_enum_file_sort_items[] = { #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_layer.h" +#include "BKE_global.h" #include "BKE_nla.h" #include "BKE_paint.h" #include "BKE_scene.h" @@ -407,7 +408,7 @@ static void rna_Space_view2d_sync_update(Main *UNUSED(bmain), Scene *UNUSED(scen static PointerRNA rna_CurrentOrientation_get(PointerRNA *ptr) { - Scene *scene = ((bScreen *)ptr->id.data)->scene; + Scene *scene = WM_windows_scene_get_from_screen(G.main->wm.first, ptr->id.data); View3D *v3d = (View3D *)ptr->data; if (v3d->twmode < V3D_MANIP_CUSTOM) @@ -429,7 +430,7 @@ EnumPropertyItem *rna_TransformOrientation_itemf(bContext *C, PointerRNA *ptr, P RNA_enum_items_add(&item, &totitem, transform_orientation_items); if (ptr->type == &RNA_SpaceView3D) - scene = ((bScreen *)ptr->id.data)->scene; + scene = WM_windows_scene_get_from_screen(G.main->wm.first, ptr->id.data); else scene = CTX_data_scene(C); /* can't use scene from ptr->id.data because that enum is also used by operators */ @@ -460,8 +461,10 @@ static void rna_SpaceView3D_camera_update(Main *bmain, Scene *scene, PointerRNA { View3D *v3d = (View3D *)(ptr->data); if (v3d->scenelock) { + wmWindowManager *wm = bmain->wm.first; + scene->camera = v3d->camera; - BKE_screen_view3d_main_sync(&bmain->screen, scene); + WM_windows_scene_data_sync(&wm->windows, scene); } } @@ -473,8 +476,10 @@ static void rna_SpaceView3D_lock_camera_and_layers_set(PointerRNA *ptr, int valu v3d->scenelock = value; if (value) { + Scene *scene = ED_screen_scene_find(sc, G.main->wm.first); int bit; - v3d->lay = sc->scene->lay; + + v3d->lay = scene->lay; /* seek for layact */ bit = 0; while (bit < 32) { @@ -484,15 +489,15 @@ static void rna_SpaceView3D_lock_camera_and_layers_set(PointerRNA *ptr, int valu } bit++; } - v3d->camera = sc->scene->camera; + v3d->camera = scene->camera; } } static void rna_View3D_CursorLocation_get(PointerRNA *ptr, float *values) { View3D *v3d = (View3D *)(ptr->data); - bScreen *sc = (bScreen *)ptr->id.data; - Scene *scene = (Scene *)sc->scene; + bScreen *screen = ptr->id.data; + Scene *scene = ED_screen_scene_find(screen, G.main->wm.first); const float *loc = ED_view3d_cursor3d_get(scene, v3d); copy_v3_v3(values, loc); @@ -501,8 +506,8 @@ static void rna_View3D_CursorLocation_get(PointerRNA *ptr, float *values) static void rna_View3D_CursorLocation_set(PointerRNA *ptr, const float *values) { View3D *v3d = (View3D *)(ptr->data); - bScreen *sc = (bScreen *)ptr->id.data; - Scene *scene = (Scene *)sc->scene; + bScreen *screen = ptr->id.data; + Scene *scene = ED_screen_scene_find(screen, G.main->wm.first); float *cursor = ED_view3d_cursor3d_get(scene, v3d); copy_v3_v3(cursor, values); @@ -511,8 +516,8 @@ static void rna_View3D_CursorLocation_set(PointerRNA *ptr, const float *values) static float rna_View3D_GridScaleUnit_get(PointerRNA *ptr) { View3D *v3d = (View3D *)(ptr->data); - bScreen *sc = (bScreen *)ptr->id.data; - Scene *scene = (Scene *)sc->scene; + bScreen *screen = ptr->id.data; + Scene *scene = ED_screen_scene_find(screen, G.main->wm.first); return ED_view3d_grid_scale(scene, v3d, NULL); } @@ -696,7 +701,7 @@ static void rna_RegionView3D_view_matrix_set(PointerRNA *ptr, const float *value static int rna_SpaceView3D_viewport_shade_get(PointerRNA *ptr) { - Scene *scene = ((bScreen *)ptr->id.data)->scene; + Scene *scene = WM_windows_scene_get_from_screen(G.main->wm.first, ptr->id.data); RenderEngineType *type = RE_engines_find(scene->r.engine); View3D *v3d = (View3D *)ptr->data; int drawtype = v3d->drawtype; @@ -716,10 +721,11 @@ static void rna_SpaceView3D_viewport_shade_set(PointerRNA *ptr, int value) v3d->drawtype = value; } -static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *UNUSED(C), PointerRNA *ptr, +static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { - Scene *scene = ((bScreen *)ptr->id.data)->scene; + wmWindow *win = CTX_wm_window(C); + Scene *scene = WM_window_get_active_scene(win); RenderEngineType *type = RE_engines_find(scene->r.engine); EnumPropertyItem *item = NULL; @@ -740,10 +746,10 @@ static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *UNUSED(C return item; } -static EnumPropertyItem *rna_SpaceView3D_stereo3d_camera_itemf(bContext *UNUSED(C), PointerRNA *ptr, +static EnumPropertyItem *rna_SpaceView3D_stereo3d_camera_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) { - Scene *scene = ((bScreen *)ptr->id.data)->scene; + Scene *scene = CTX_data_scene(C); if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) return multiview_camera_items; @@ -811,16 +817,17 @@ static int rna_SpaceImageEditor_show_uvedit_get(PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)(ptr->data); bScreen *sc = (bScreen *)ptr->id.data; - return ED_space_image_show_uvedit(sima, sc->scene->obedit); + Scene *scene = ED_screen_scene_find(sc, G.main->wm.first); + + return ED_space_image_show_uvedit(sima, scene->obedit); } static int rna_SpaceImageEditor_show_maskedit_get(PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)(ptr->data); bScreen *sc = (bScreen *)ptr->id.data; - - TODO_LAYER_CONTEXT; /* get SceneLayer from context/window/workspace instead */ - SceneLayer *sl = BKE_scene_layer_context_active(sc->scene); + Scene *scene = ED_screen_scene_find(sc, G.main->wm.first); + SceneLayer *sl = BKE_scene_layer_context_active(scene); return ED_space_image_check_show_maskedit(sl, sima); } @@ -829,8 +836,9 @@ static void rna_SpaceImageEditor_image_set(PointerRNA *ptr, PointerRNA value) { SpaceImage *sima = (SpaceImage *)(ptr->data); bScreen *sc = (bScreen *)ptr->id.data; + Scene *scene = ED_screen_scene_find(sc, G.main->wm.first); - ED_space_image_set(sima, sc->scene, sc->scene->obedit, (Image *)value.data); + ED_space_image_set(sima, scene, scene->obedit, (Image *)value.data); } static void rna_SpaceImageEditor_mask_set(PointerRNA *ptr, PointerRNA value) @@ -3880,6 +3888,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) {FILTER_ID_TXT, "TEXT", ICON_TEXT, "Texts", "Show/hide Text data-blocks"}, {FILTER_ID_VF, "FONT", ICON_FONT_DATA, "Fonts", "Show/hide Font data-blocks"}, {FILTER_ID_WO, "WORLD", ICON_WORLD_DATA, "Worlds", "Show/hide World data-blocks"}, + {FILTER_ID_WS, "WORK_SPACE", ICON_NONE, "Workspaces", "Show/hide workspace data-blocks"}, {0, NULL, 0, NULL, NULL} }; @@ -3897,7 +3906,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) "Show/hide materials, nodetrees, textures and Freestyle's linestyles"}, {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO, "IMAGE", ICON_IMAGE_DATA, "Images & Sounds", "Show/hide images, movie clips, sounds and masks"}, - {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO, + {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO | FILTER_ID_WS, "ENVIRONMENT", ICON_WORLD_DATA, "Environment", "Show/hide worlds, lamps, cameras and speakers"}, {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_CF, "MISC", ICON_GREASEPENCIL, "Miscellaneous", "Show/hide other data types"}, diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index 8f771eda99d..aabe421b872 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -31,6 +31,9 @@ #ifdef RNA_RUNTIME +#include "BKE_global.h" + +#include "ED_screen.h" #include "ED_text.h" static void rna_RegionView3D_update(ID *id, RegionView3D *rv3d) @@ -43,11 +46,10 @@ static void rna_RegionView3D_update(ID *id, RegionView3D *rv3d) area_region_from_regiondata(sc, rv3d, &sa, &ar); if (sa && ar && sa->spacetype == SPACE_VIEW3D) { - View3D *v3d; - - v3d = (View3D *)sa->spacedata.first; + View3D *v3d = sa->spacedata.first; + Scene *scene = ED_screen_scene_find(sc, G.main->wm.first); - ED_view3d_update_viewmat(sc->scene, v3d, ar, NULL, NULL, NULL); + ED_view3d_update_viewmat(scene, v3d, ar, NULL, NULL, NULL); } } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index d1e89ea18d0..a0fb398ee3a 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -3335,11 +3335,6 @@ static void rna_def_userdef_view(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Display Object Info", "Display objects name and frame number in 3D view"); RNA_def_property_update(prop, 0, "rna_userdef_update"); - prop = RNA_def_property(srna, "use_global_scene", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_SCENEGLOBAL); - RNA_def_property_ui_text(prop, "Global Scene", "Force the current Scene to be displayed in all Screens"); - RNA_def_property_update(prop, 0, "rna_userdef_update"); - prop = RNA_def_property(srna, "show_large_cursors", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "curssize", 0); RNA_def_property_ui_text(prop, "Large Cursors", "Use large mouse cursors when available"); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b458b2e69d5..f6e67d5f765 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -461,12 +461,23 @@ EnumPropertyItem rna_enum_wm_report_items[] = { #include "WM_api.h" +#include "DNA_workspace_types.h" + +#include "ED_screen.h" + #include "UI_interface.h" +#include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_workspace.h" #include "MEM_guardedalloc.h" +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + + static wmOperator *rna_OperatorProperties_find_operator(PointerRNA *ptr) { wmWindowManager *wm = ptr->id.data; @@ -621,39 +632,120 @@ static PointerRNA rna_PieMenu_layout_get(PointerRNA *ptr) return rptr; } -static void rna_Window_screen_set(PointerRNA *ptr, PointerRNA value) +static void rna_Window_scene_set(PointerRNA *ptr, PointerRNA value) +{ + wmWindow *win = ptr->data; + + if (value.data == NULL) { + return; + } + + win->new_scene = value.data; +} + +static void rna_Window_scene_update(bContext *C, PointerRNA *ptr) +{ + Main *bmain = CTX_data_main(C); + wmWindow *win = ptr->data; + + /* exception: must use context so notifier gets to the right window */ + if (win->new_scene) { +#ifdef WITH_PYTHON + BPy_BEGIN_ALLOW_THREADS; +#endif + + WM_window_change_active_scene(bmain, C, win, win->new_scene); + +#ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +#endif + + WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, win->new_scene); + + if (G.debug & G_DEBUG) + printf("scene set %p\n", win->new_scene); + + win->new_scene = NULL; + } +} + +static PointerRNA rna_Window_workspace_get(PointerRNA *ptr) +{ + wmWindow *win = ptr->data; + return rna_pointer_inherit_refine(ptr, &RNA_WorkSpace, BKE_workspace_active_get(win->workspace_hook)); +} + +static void rna_Window_workspace_set(PointerRNA *ptr, PointerRNA value) { wmWindow *win = (wmWindow *)ptr->data; /* disallow ID-browsing away from temp screens */ - if (win->screen->temp) { + if (WM_window_is_temp_screen(win)) { return; } + if (value.data == NULL) { + return; + } + + /* exception: can't set workspaces inside of area/region handlers */ + win->workspace_hook->temp_workspace_store = value.data; +} + +static void rna_Window_workspace_update(bContext *C, PointerRNA *ptr) +{ + wmWindow *win = ptr->data; + WorkSpace *new_workspace = win->workspace_hook->temp_workspace_store; + + /* exception: can't set screens inside of area/region handlers, + * and must use context so notifier gets to the right window */ + if (new_workspace) { + WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_SET, new_workspace); + win->workspace_hook->temp_workspace_store = NULL; + } +} + +PointerRNA rna_Window_screen_get(PointerRNA *ptr) +{ + wmWindow *win = ptr->data; + return rna_pointer_inherit_refine(ptr, &RNA_Screen, BKE_workspace_active_screen_get(win->workspace_hook)); +} - if (value.data == NULL) +static void rna_Window_screen_set(PointerRNA *ptr, PointerRNA value) +{ + wmWindow *win = ptr->data; + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WorkSpaceLayout *layout_new; + const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + + /* disallow ID-browsing away from temp screens */ + if (screen->temp) { + return; + } + if (value.data == NULL) { return; + } /* exception: can't set screens inside of area/region handlers */ - win->newscreen = value.data; + layout_new = BKE_workspace_layout_find(workspace, value.data); + win->workspace_hook->temp_layout_store = layout_new; } static int rna_Window_screen_assign_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { - bScreen *screen = (bScreen *)value.id.data; - + bScreen *screen = value.id.data; return !screen->temp; } - -static void rna_Window_screen_update(bContext *C, PointerRNA *ptr) +static void rna_workspace_screen_update(bContext *C, PointerRNA *ptr) { - wmWindow *win = (wmWindow *)ptr->data; + wmWindow *win = ptr->data; + WorkSpaceLayout *layout_new = win->workspace_hook->temp_layout_store; /* exception: can't set screens inside of area/region handlers, * and must use context so notifier gets to the right window */ - if (win->newscreen) { - WM_event_add_notifier(C, NC_SCREEN | ND_SCREENBROWSE, win->newscreen); - win->newscreen = NULL; + if (layout_new) { + WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTBROWSE, layout_new); + win->workspace_hook->temp_layout_store = NULL; } } @@ -1910,14 +2002,28 @@ static void rna_def_window(BlenderRNA *brna) rna_def_window_stereo3d(brna); - prop = RNA_def_property(srna, "screen", PROP_POINTER, PROP_NONE); + prop = RNA_def_property(srna, "scene", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); + RNA_def_property_pointer_funcs(prop, NULL, "rna_Window_scene_set", NULL, NULL); + RNA_def_property_ui_text(prop, "Scene", "Active scene to be edited in the window"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, 0, "rna_Window_scene_update"); + + prop = RNA_def_property(srna, "workspace", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "WorkSpace"); + RNA_def_property_ui_text(prop, "Workspace", "Active workspace showing in the window"); + RNA_def_property_pointer_funcs(prop, "rna_Window_workspace_get", "rna_Window_workspace_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, 0, "rna_Window_workspace_update"); + + prop = RNA_def_property(srna, "screen", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Screen"); - RNA_def_property_ui_text(prop, "Screen", "Active screen showing in the window"); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_pointer_funcs(prop, NULL, "rna_Window_screen_set", NULL, "rna_Window_screen_assign_poll"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - RNA_def_property_update(prop, 0, "rna_Window_screen_update"); + RNA_def_property_ui_text(prop, "Screen", "Active workspace screen showing in the window"); + RNA_def_property_pointer_funcs(prop, "rna_Window_screen_get", "rna_Window_screen_set", NULL, + "rna_Window_screen_assign_poll"); + RNA_def_property_flag(prop, PROP_NEVER_NULL | PROP_EDITABLE | PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, 0, "rna_workspace_screen_update"); prop = RNA_def_property(srna, "x", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "posx"); diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c new file mode 100644 index 00000000000..29fe5bae0e4 --- /dev/null +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -0,0 +1,144 @@ +/* + * ***** 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/makesrna/intern/rna_workspace.c + * \ingroup RNA + */ + +#include "RNA_define.h" +#include "RNA_enum_types.h" +#include "RNA_types.h" + +#include "BKE_workspace.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "rna_internal.h" + + +#ifdef RNA_RUNTIME + +#include "BKE_global.h" + +#include "BLI_listbase.h" + +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "RNA_access.h" + + +void rna_workspace_screens_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + WorkSpace *workspace = ptr->id.data; + rna_iterator_listbase_begin(iter, BKE_workspace_layouts_get(workspace), NULL); +} + +static PointerRNA rna_workspace_screens_item_get(CollectionPropertyIterator *iter) +{ + WorkSpaceLayout *layout = rna_iterator_listbase_get(iter); + bScreen *screen = BKE_workspace_layout_screen_get(layout); + + return rna_pointer_inherit_refine(&iter->parent, &RNA_Screen, screen); +} + +#ifdef USE_WORKSPACE_MODE + +static int rna_workspace_object_mode_get(PointerRNA *ptr) +{ + WorkSpace *workspace = ptr->data; + return (int)BKE_workspace_object_mode_get(workspace); +} + +static void rna_workspace_object_mode_set(PointerRNA *ptr, int value) +{ + WorkSpace *workspace = ptr->data; + BKE_workspace_object_mode_set(workspace, value); +} + +#endif /* USE_WORKSPACE_MODE */ + +static PointerRNA rna_workspace_render_layer_get(PointerRNA *ptr) +{ + WorkSpace *workspace = ptr->data; + SceneLayer *render_layer = BKE_workspace_render_layer_get(workspace); + + /* XXX hmrf... lookup in getter... but how could we avoid it? */ + for (Scene *scene = G.main->scene.first; scene; scene = scene->id.next) { + if (BLI_findindex(&scene->render_layers, render_layer) != -1) { + PointerRNA scene_ptr; + + RNA_id_pointer_create(&scene->id, &scene_ptr); + return rna_pointer_inherit_refine(&scene_ptr, &RNA_SceneLayer, render_layer); + } + } + + return PointerRNA_NULL; +} + +static void rna_workspace_render_layer_set(PointerRNA *ptr, PointerRNA value) +{ + WorkSpace *workspace = ptr->data; + BKE_workspace_render_layer_set(workspace, value.data); +} + +#else /* RNA_RUNTIME */ + +static void rna_def_workspace(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "WorkSpace", "ID"); + RNA_def_struct_sdna(srna, "WorkSpace"); + RNA_def_struct_ui_text(srna, "Workspace", "Workspace data-block, defining the working environment for the user"); + /* TODO: real icon, just to show something */ + RNA_def_struct_ui_icon(srna, ICON_RENDER_RESULT); + + prop = RNA_def_property(srna, "screens", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "layouts", NULL); + RNA_def_property_struct_type(prop, "Screen"); + RNA_def_property_collection_funcs(prop, "rna_workspace_screens_begin", NULL, NULL, + "rna_workspace_screens_item_get", NULL, NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Screens", "Screen layouts of a workspace"); + +#ifdef USE_WORKSPACE_MODE + prop = RNA_def_property(srna, "object_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_object_mode_items); + RNA_def_property_enum_funcs(prop, "rna_workspace_object_mode_get", "rna_workspace_object_mode_set", NULL); + RNA_def_property_ui_text(prop, "Mode", "Object interaction mode"); +#endif + + prop = RNA_def_property(srna, "render_layer", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "SceneLayer"); + RNA_def_property_pointer_funcs(prop, "rna_workspace_render_layer_get", "rna_workspace_render_layer_set", + NULL, NULL); + RNA_def_property_ui_text(prop, "Active Render Layer", "The active render layer used in this workspace"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); + RNA_def_property_update(prop, NC_SCREEN | ND_LAYER, NULL); +} + +void RNA_def_workspace(BlenderRNA *brna) +{ + rna_def_workspace(brna); +} + +#endif /* RNA_RUNTIME */ diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index 15f3c665fcf..73265d28a93 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -332,6 +332,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) Main *bmain = CTX_data_main(BPy_GetContext()); Main *mainl = NULL; int err = 0; + const bool do_append = ((self->flag & FILE_LINK) == 0); BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); @@ -341,7 +342,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) { int idcode_step = 0, idcode; while ((idcode = BKE_idcode_iter_step(&idcode_step))) { - if (BKE_idcode_is_linkable(idcode)) { + if (BKE_idcode_is_linkable(idcode) && (idcode != ID_WS || do_append)) { const char *name_plural = BKE_idcode_to_name_plural(idcode); PyObject *ls = PyDict_GetItemString(self->dict, name_plural); // printf("lib: %s\n", name_plural); @@ -414,7 +415,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) BKE_main_lib_objects_recalc_all(bmain); /* append, rather than linking */ - if ((self->flag & FILE_LINK) == 0) { + if (do_append) { BKE_library_make_local(bmain, lib, old_to_new_ids, true, false); } } @@ -427,7 +428,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) { int idcode_step = 0, idcode; while ((idcode = BKE_idcode_iter_step(&idcode_step))) { - if (BKE_idcode_is_linkable(idcode)) { + if (BKE_idcode_is_linkable(idcode) && (idcode != ID_WS || do_append)) { const char *name_plural = BKE_idcode_to_name_plural(idcode); PyObject *ls = PyDict_GetItemString(self->dict, name_plural); if (ls && PyList_Check(ls)) { diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index a0b76bd67ee..3a6d5c9f972 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -310,7 +310,7 @@ static bool rna_id_write_error(PointerRNA *ptr, PyObject *key) ID *id = ptr->id.data; if (id) { const short idcode = GS(id->name); - if (!ELEM(idcode, ID_WM, ID_SCR)) { /* may need more added here */ + if (!ELEM(idcode, ID_WM, ID_SCR, ID_WS)) { /* may need more added here */ const char *idtype = BKE_idcode_to_name(idcode); const char *pyname; if (key && PyUnicode_Check(key)) pyname = _PyUnicode_AsString(key); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 80170d70dbd..6fa9429d04f 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -102,6 +102,21 @@ int WM_window_pixels_x (struct wmWindow *win); int WM_window_pixels_y (struct wmWindow *win); bool WM_window_is_fullscreen (struct wmWindow *win); +void WM_windows_scene_data_sync(const ListBase *win_lb, struct Scene *scene) ATTR_NONNULL(); +struct Scene *WM_windows_scene_get_from_screen(const struct wmWindowManager *wm, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; + +struct Scene *WM_window_get_active_scene(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_change_active_scene(struct Main *bmain, struct bContext *C, struct wmWindow *win, + struct Scene *scene_new) ATTR_NONNULL(); +struct WorkSpace *WM_window_get_active_workspace(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_set_active_workspace(struct wmWindow *win, struct WorkSpace *workspace) ATTR_NONNULL(1); +struct WorkSpaceLayout *WM_window_get_active_layout(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_set_active_layout( + struct wmWindow *win, struct WorkSpace *workspace, struct WorkSpaceLayout *layout) ATTR_NONNULL(1); +struct bScreen *WM_window_get_active_screen(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_set_active_screen(struct wmWindow *win, struct WorkSpace *workspace, struct bScreen *screen) ATTR_NONNULL(1); +bool WM_window_is_temp_screen(const struct wmWindow *win) ATTR_WARN_UNUSED_RESULT; + /* defines for 'type' WM_window_open_temp */ enum { WM_WINDOW_RENDER = 1, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 84209a0e3c8..756a9d6a1d1 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -224,7 +224,7 @@ typedef struct wmNotifier { #define NOTE_CATEGORY 0xFF000000 #define NC_WM (1<<24) #define NC_WINDOW (2<<24) -#define NC_SCREEN (3<<24) +#define NC_SCREEN (3<<24) #define NC_SCENE (4<<24) #define NC_OBJECT (5<<24) #define NC_MATERIAL (6<<24) @@ -258,15 +258,16 @@ typedef struct wmNotifier { #define ND_JOB (5<<16) #define ND_UNDO (6<<16) - /* NC_SCREEN screen */ -#define ND_SCREENBROWSE (1<<16) -#define ND_SCREENDELETE (2<<16) + /* NC_SCREEN */ +#define ND_LAYOUTBROWSE (1<<16) +#define ND_LAYOUTDELETE (2<<16) #define ND_SCREENCAST (3<<16) #define ND_ANIMPLAY (4<<16) #define ND_GPENCIL (5<<16) #define ND_EDITOR_CHANGED (6<<16) /*sent to new editors after switching to them*/ -#define ND_SCREENSET (7<<16) +#define ND_LAYOUTSET (7<<16) #define ND_SKETCH (8<<16) +#define ND_WORKSPACE_SET (9<<16) /* NC_SCENE Scene */ #define ND_SCENEBROWSE (1<<16) diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index d0522fdd7d4..af275394fcb 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -433,10 +433,12 @@ void wm_add_default(bContext *C) wmWindowManager *wm = BKE_libblock_alloc(CTX_data_main(C), ID_WM, "WinMan"); wmWindow *win; bScreen *screen = CTX_wm_screen(C); /* XXX from file read hrmf */ - + struct WorkSpace *workspace = G.main->workspaces.last; + CTX_wm_manager_set(C, wm); win = wm_window_new(C); - win->screen = screen; + WM_window_set_active_workspace(win, workspace); + WM_window_set_active_screen(win, workspace, screen); screen->winid = win->winid; BLI_strncpy(win->screenname, screen->id.name + 2, sizeof(win->screenname)); @@ -457,7 +459,7 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) wm_autosave_timer_ended(wm); while ((win = BLI_pophead(&wm->windows))) { - win->screen = NULL; /* prevent draw clear to use screen */ + WM_window_set_active_workspace(win, NULL); /* prevent draw clear to use screen */ wm_draw_window_clear(win); wm_window_free(C, wm, win); } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 3c176912409..5be3429f2de 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -87,7 +87,7 @@ static void wm_paintcursor_draw(bContext *C, ARegion *ar) if (wm->paintcursors.first) { wmWindow *win = CTX_wm_window(C); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); wmPaintCursor *pc; if (ar->swinid && screen->subwinactive == ar->swinid) { @@ -130,7 +130,7 @@ static bool wm_area_test_invalid_backbuf(ScrArea *sa) return true; } -static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion *ar) +static void wm_region_test_render_do_draw(const Scene *scene, ScrArea *sa, ARegion *ar) { /* tag region for redraw from render engine preview running inside of it */ if (sa->spacetype == SPACE_VIEW3D) { @@ -138,7 +138,6 @@ static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion RenderEngine *engine = (rv3d) ? rv3d->render_engine : NULL; if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) { - Scene *scene = screen->scene; View3D *v3d = sa->spacedata.first; rcti border_rect; @@ -158,7 +157,7 @@ static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion static void wm_method_draw_full(bContext *C, wmWindow *win) { - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; @@ -181,7 +180,7 @@ static void wm_method_draw_full(bContext *C, wmWindow *win) } ED_screen_draw(win); - win->screen->do_draw = false; + screen->do_draw = false; /* draw overlapping regions */ for (ar = screen->regionbase.first; ar; ar = ar->next) { @@ -237,7 +236,7 @@ static void wm_flush_regions_up(bScreen *screen, rcti *dirty) static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) { wmWindowManager *wm = CTX_wm_manager(C); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; static rcti rect = {0, 0, 0, 0}; @@ -318,7 +317,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) /* after area regions so we can do area 'overlay' drawing */ if (screen->do_draw) { ED_screen_draw(win); - win->screen->do_draw = false; + screen->do_draw = false; if (exchange) screen->swap = WIN_FRONT_OK; @@ -326,7 +325,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) else if (exchange) { if (screen->swap == WIN_FRONT_OK) { ED_screen_draw(win); - win->screen->do_draw = false; + screen->do_draw = false; screen->swap = WIN_BOTH_OK; } else if (screen->swap == WIN_BACK_OK) @@ -500,7 +499,9 @@ static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *tripl /* region blend always is 1, except when blend timer is running */ if (fac < 1.0f) { - wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true); + bScreen *screen = WM_window_get_active_screen(win); + + wmSubWindowScissorSet(win, screen->mainwin, &ar->winrct, true); glEnable(GL_BLEND); wm_triple_draw_textures(win, triple, 1.0f - fac); @@ -512,7 +513,7 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) { wmWindowManager *wm = CTX_wm_manager(C); wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first; - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; bool copytex = false; @@ -620,7 +621,7 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* after area regions so we can do area 'overlay' drawing */ ED_screen_draw(win); - win->screen->do_draw = false; + WM_window_get_active_screen(win)->do_draw = false; /* draw floating regions (menus) */ for (ar = screen->regionbase.first; ar; ar = ar->next) { @@ -647,7 +648,7 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoVi wmWindowManager *wm = CTX_wm_manager(C); wmDrawData *drawdata; wmDrawTriple *triple_data, *triple_all; - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; int copytex = false; @@ -793,7 +794,7 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoVi /* after area regions so we can do area 'overlay' drawing */ ED_screen_draw(win); if (sview == STEREO_RIGHT_ID) - win->screen->do_draw = false; + screen->do_draw = false; /* draw floating regions (menus) */ for (ar = screen->regionbase.first; ar; ar = ar->next) { @@ -825,11 +826,13 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoVi /* quick test to prevent changing window drawable */ static bool wm_draw_update_test_window(wmWindow *win) { + const Scene *scene = WM_window_get_active_scene(win); + const bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; bool do_draw = false; - for (ar = win->screen->regionbase.first; ar; ar = ar->next) { + for (ar = screen->regionbase.first; ar; ar = ar->next) { if (ar->do_draw_overlay) { wm_tag_redraw_overlay(win, ar); ar->do_draw_overlay = false; @@ -838,9 +841,9 @@ static bool wm_draw_update_test_window(wmWindow *win) do_draw = true; } - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { for (ar = sa->regionbase.first; ar; ar = ar->next) { - wm_region_test_render_do_draw(win->screen, sa, ar); + wm_region_test_render_do_draw(scene, sa, ar); if (ar->swinid && ar->do_draw) do_draw = true; @@ -850,15 +853,15 @@ static bool wm_draw_update_test_window(wmWindow *win) if (do_draw) return true; - if (win->screen->do_refresh) + if (screen->do_refresh) return true; - if (win->screen->do_draw) + if (screen->do_draw) return true; - if (win->screen->do_draw_gesture) + if (screen->do_draw_gesture) return true; - if (win->screen->do_draw_paintcursor) + if (screen->do_draw_paintcursor) return true; - if (win->screen->do_draw_drag) + if (screen->do_draw_drag) return true; return false; @@ -899,15 +902,18 @@ void wm_tag_redraw_overlay(wmWindow *win, ARegion *ar) { /* for draw triple gestures, paint cursors don't need region redraw */ if (ar && win) { + bScreen *screen = WM_window_get_active_screen(win); + if (wm_automatic_draw_method(win) != USER_DRAW_TRIPLE) ED_region_tag_redraw(ar); - win->screen->do_draw_paintcursor = true; + screen->do_draw_paintcursor = true; } } void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *ar) { - win->screen->do_draw_paintcursor = true; + bScreen *screen = WM_window_get_active_screen(win); + screen->do_draw_paintcursor = true; wm_tag_redraw_overlay(win, ar); } @@ -941,13 +947,15 @@ void wm_draw_update(bContext *C) } if (wm_draw_update_test_window(win)) { + bScreen *screen = WM_window_get_active_screen(win); + CTX_wm_window_set(C, win); /* sets context window+screen */ wm_window_make_drawable(wm, win); /* notifiers for screen redraw */ - if (win->screen->do_refresh) + if (screen->do_refresh) ED_screen_refresh(wm, win); int drawmethod = wm_automatic_draw_method(win); @@ -971,9 +979,9 @@ void wm_draw_update(bContext *C) } } - win->screen->do_draw_gesture = false; - win->screen->do_draw_paintcursor = false; - win->screen->do_draw_drag = false; + screen->do_draw_gesture = false; + screen->do_draw_paintcursor = false; + screen->do_draw_drag = false; wm_window_swap_buffers(win); @@ -994,7 +1002,7 @@ void wm_draw_data_free(wmWindow *win) void wm_draw_window_clear(wmWindow *win) { - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; @@ -1012,12 +1020,13 @@ void wm_draw_window_clear(wmWindow *win) void wm_draw_region_clear(wmWindow *win, ARegion *ar) { + bScreen *screen = WM_window_get_active_screen(win); int drawmethod = wm_automatic_draw_method(win); if (ELEM(drawmethod, USER_DRAW_OVERLAP, USER_DRAW_OVERLAP_FLIP)) - wm_flush_regions_down(win->screen, &ar->winrct); + wm_flush_regions_down(screen, &ar->winrct); - win->screen->do_draw = true; + screen->do_draw = true; } void WM_redraw_windows(bContext *C) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 5d4b7a68d42..c4776f53f42 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -59,6 +59,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "BKE_sound.h" @@ -276,6 +277,7 @@ void wm_event_do_notifiers(bContext *C) /* cache & catch WM level notifiers, such as frame change, scene/screen set */ for (win = wm->windows.first; win; win = win->next) { + Scene *scene = WM_window_get_active_scene(win); bool do_anim = false; CTX_wm_window_set(C, win); @@ -293,17 +295,31 @@ void wm_event_do_notifiers(bContext *C) } if (note->window == win) { if (note->category == NC_SCREEN) { - if (note->data == ND_SCREENBROWSE) { + if (note->data == ND_WORKSPACE_SET) { + WorkSpace *ref_ws = note->reference; + + UI_popup_handlers_remove_all(C, &win->modalhandlers); + + ED_workspace_change(ref_ws, C, wm, win); + if (G.debug & G_DEBUG_EVENTS) + printf("%s: Workspace set %p\n", __func__, note->reference); + } + else if (note->data == ND_LAYOUTBROWSE) { + bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference); + /* free popup handlers only [#35434] */ UI_popup_handlers_remove_all(C, &win->modalhandlers); - ED_screen_set(C, note->reference); // XXX hrms, think this over! + ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */ if (G.debug & G_DEBUG_EVENTS) printf("%s: screen set %p\n", __func__, note->reference); } - else if (note->data == ND_SCREENDELETE) { - ED_screen_delete(C, note->reference); // XXX hrms, think this over! + else if (note->data == ND_LAYOUTDELETE) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = note->reference; + + ED_workspace_layout_delete(workspace, layout, C); // XXX hrms, think this over! if (G.debug & G_DEBUG_EVENTS) printf("%s: screen delete %p\n", __func__, note->reference); } @@ -311,7 +327,7 @@ void wm_event_do_notifiers(bContext *C) } if (note->window == win || - (note->window == NULL && (note->reference == NULL || note->reference == win->screen->scene))) + (note->window == NULL && (note->reference == NULL || note->reference == scene))) { if (note->category == NC_SCENE) { if (note->data == ND_FRAME) @@ -319,7 +335,7 @@ void wm_event_do_notifiers(bContext *C) } } if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) { - SceneLayer *sl = BKE_scene_layer_context_active(win->screen->scene); + SceneLayer *sl = BKE_scene_layer_context_active(scene); ED_info_stats_clear(sl); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL); } @@ -332,7 +348,7 @@ void wm_event_do_notifiers(bContext *C) if (G.is_rendering == false) { /* depsgraph gets called, might send more notifiers */ - ED_update_for_newframe(CTX_data_main(C), win->screen->scene, 1); + ED_update_for_newframe(CTX_data_main(C), scene, 1); } } } @@ -340,10 +356,16 @@ void wm_event_do_notifiers(bContext *C) /* the notifiers are sent without context, to keep it clean */ while ((note = BLI_pophead(&wm->queue))) { for (win = wm->windows.first; win; win = win->next) { - Scene *scene = win->screen->scene; - + Scene *scene = WM_window_get_active_scene(win); + bScreen *screen = WM_window_get_active_screen(win); + /* filter out notifiers */ - if (note->category == NC_SCREEN && note->reference && note->reference != win->screen) { + if (note->category == NC_SCREEN && + note->reference && + note->reference != screen && + note->reference != WM_window_get_active_workspace(win) && + note->reference != WM_window_get_active_layout(win)) + { /* pass */ } else if (note->category == NC_SCENE && note->reference && note->reference != scene) { @@ -359,14 +381,14 @@ void wm_event_do_notifiers(bContext *C) /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name + 2, note->category); */ ED_screen_do_listen(C, note); - for (ar = win->screen->regionbase.first; ar; ar = ar->next) { - ED_region_do_listen(win->screen, NULL, ar, note, scene); + for (ar = screen->regionbase.first; ar; ar = ar->next) { + ED_region_do_listen(screen, NULL, ar, note, scene); } - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - ED_area_do_listen(win->screen, sa, note, scene); + for (sa = screen->areabase.first; sa; sa = sa->next) { + ED_area_do_listen(screen, sa, note, scene); for (ar = sa->regionbase.first; ar; ar = ar->next) { - ED_region_do_listen(win->screen, sa, ar, note, scene); + ED_region_do_listen(screen, sa, ar, note, scene); } } } @@ -374,18 +396,23 @@ void wm_event_do_notifiers(bContext *C) MEM_freeN(note); } - + /* combine datamasks so 1 win doesn't disable UV's in another [#26448] */ for (win = wm->windows.first; win; win = win->next) { - win_combine_v3d_datamask |= ED_view3d_screen_datamask(win->screen); + const Scene *scene = WM_window_get_active_scene(win); + const bScreen *screen = WM_window_get_active_screen(win); + + win_combine_v3d_datamask |= ED_view3d_screen_datamask(scene, screen); } /* cached: editor refresh callbacks now, they get context */ for (win = wm->windows.first; win; win = win->next) { + const bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = WM_window_get_active_scene(win); ScrArea *sa; CTX_wm_window_set(C, win); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { if (sa->do_refresh) { CTX_wm_area_set(C, sa); ED_area_do_refresh(C, sa); @@ -398,12 +425,12 @@ void wm_event_do_notifiers(bContext *C) Main *bmain = CTX_data_main(C); /* copied to set's in scene_update_tagged_recursive() */ - win->screen->scene->customdata_mask = win_combine_v3d_datamask; + scene->customdata_mask = win_combine_v3d_datamask; /* XXX, hack so operators can enforce datamasks [#26482], gl render */ - win->screen->scene->customdata_mask |= win->screen->scene->customdata_mask_modal; + scene->customdata_mask |= scene->customdata_mask_modal; - BKE_scene_update_tagged(bmain->eval_ctx, bmain, win->screen->scene); + BKE_scene_update_tagged(bmain->eval_ctx, bmain, scene); } } @@ -2362,17 +2389,19 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event) static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event) { + bScreen *screen = WM_window_get_active_screen(win); + if (BLI_listbase_is_empty(&wm->drags)) { return; } if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) { - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; } else if (event->type == ESCKEY) { WM_drag_free_list(&wm->drags); - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; } else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { event->type = EVT_DROP; @@ -2388,16 +2417,16 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even event->customdatafree = 1; /* clear drop icon */ - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; /* restore cursor (disabled, see wm_dragdrop.c) */ // WM_cursor_modal_restore(win); } /* overlap fails otherwise */ - if (win->screen->do_draw_drag) + if (screen->do_draw_drag) if (win->drawmethod == USER_DRAW_OVERLAP) - win->screen->do_draw = true; + screen->do_draw = true; } @@ -2429,20 +2458,25 @@ void wm_event_do_handlers(bContext *C) WM_keyconfig_update(wm); for (win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); wmEvent *event; - - if (win->screen == NULL) + + /* some safty checks - these should always be set! */ + BLI_assert(WM_window_get_active_scene(win)); + BLI_assert(WM_window_get_active_screen(win)); + BLI_assert(WM_window_get_active_workspace(win)); + + if (screen == NULL) wm_event_free_all(win); else { - Scene *scene = win->screen->scene; + Scene *scene = WM_window_get_active_scene(win); if (scene) { - int is_playing_sound = BKE_sound_scene_playing(win->screen->scene); + int is_playing_sound = BKE_sound_scene_playing(scene); if (is_playing_sound != -1) { bool is_playing_screen; CTX_wm_window_set(C, win); - CTX_wm_screen_set(C, win->screen); CTX_data_scene_set(C, scene); is_playing_screen = (ED_screen_animation_playing(wm) != NULL); @@ -2475,6 +2509,9 @@ void wm_event_do_handlers(bContext *C) while ( (event = win->queue.first) ) { int action = WM_HANDLER_CONTINUE; + /* active screen might change during handlers, update pointer */ + screen = WM_window_get_active_screen(win); + if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { printf("\n%s: Handling event\n", __func__); WM_event_print(event); @@ -2532,12 +2569,12 @@ void wm_event_do_handlers(bContext *C) } #endif - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { /* after restoring a screen from SCREENMAXIMIZED we have to wait * with the screen handling till the region coordinates are updated */ - if (win->screen->skip_handling == true) { + if (screen->skip_handling == true) { /* restore for the next iteration of wm_event_do_handlers */ - win->screen->skip_handling = false; + screen->skip_handling = false; break; } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 99e0e28ab1b..8d442a2ed25 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -85,6 +85,7 @@ #include "BKE_sound.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "BLO_readfile.h" #include "BLO_writefile.h" @@ -160,7 +161,7 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist) CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); - ED_screen_exit(C, win, win->screen); + ED_screen_exit(C, win, WM_window_get_active_screen(win)); } } @@ -249,16 +250,23 @@ static void wm_window_match_do(bContext *C, ListBase *oldwmlist) /* match oldwm to new dbase, only old files */ for (wm = oldwmlist->first; wm; wm = wm->id.next) { - for (win = wm->windows.first; win; win = win->next) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + /* all windows get active screen from file */ - if (screen->winid == 0) - win->screen = screen; - else - win->screen = ED_screen_duplicate(win, screen); - - BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname)); - win->screen->winid = win->winid; + if (screen->winid == 0) { + WM_window_set_active_screen(win, workspace, screen); + } + else { + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); + WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(workspace, layout_old, win); + + WM_window_set_active_layout(win, workspace, layout_new); + } + + bScreen *win_screen = WM_window_get_active_screen(win); + BLI_strncpy(win->screenname, win_screen->id.name + 2, sizeof(win->screenname)); + win_screen->winid = win->winid; } } } @@ -317,10 +325,8 @@ static void wm_window_match_do(bContext *C, ListBase *oldwmlist) } /* in case UserDef was read, we re-initialize all, and do versioning */ -static void wm_init_userdef(bContext *C, const bool use_factory_settings) +static void wm_init_userdef(Main *bmain, const bool use_factory_settings) { - Main *bmain = CTX_data_main(C); - /* versioning is here */ UI_init_userdef(); @@ -582,7 +588,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) { /* in case a userdef is read from regular .blend */ - wm_init_userdef(C, false); + wm_init_userdef(G.main, false); } if (retval != BKE_BLENDFILE_READ_FAIL) { @@ -828,7 +834,7 @@ int wm_homefile_read( G.fileflags &= ~G_FILE_RELATIVE_REMAP; /* check userdef before open window, keymaps etc */ - wm_init_userdef(C, read_userdef_from_memory); + wm_init_userdef(CTX_data_main(C), read_userdef_from_memory); /* match the read WM with current WM */ wm_window_match_do(C, &wmbase); @@ -1370,7 +1376,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); /* check current window and close it if temp */ - if (win && win->screen->temp) + if (win && WM_window_is_temp_screen(win)) wm_window_close(C, wm, win); /* update keymaps in user preferences */ @@ -1505,6 +1511,42 @@ void WM_OT_save_userpref(wmOperatorType *ot) ot->exec = wm_userpref_write_exec; } +static int wm_workspace_configuration_file_write_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + char filepath[FILE_MAX]; + + const char *app_template = U.app_template[0] ? U.app_template : NULL; + const char * const cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, app_template); + if (cfgdir == NULL) { + BKE_report(op->reports, RPT_ERROR, "Unable to create workspace configuration file path"); + return OPERATOR_CANCELLED; + } + + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_WORKSPACES_FILE, NULL); + printf("trying to save workspace configuration file at %s ", filepath); + + if (BKE_blendfile_workspace_config_write(bmain, filepath, op->reports) != 0) { + printf("ok\n"); + return OPERATOR_FINISHED; + } + else { + printf("fail\n"); + } + + return OPERATOR_CANCELLED; +} + +void WM_OT_save_workspace_file(wmOperatorType *ot) +{ + ot->name = "Save Workspace Configuration"; + ot->idname = "WM_OT_save_workspace_file"; + ot->description = "Save workspaces of the current file as part of the user configuration"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_workspace_configuration_file_write_exec; +} + static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { ED_file_read_bookmarks(); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 82e9be3970f..3c7a48662f8 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -278,6 +278,40 @@ static void wm_link_do( } } +/** + * Check if an item defined by \a name and \a group can be appended/linked. + * + * \param reports: Optionally report an error when an item can't be appended/linked. + */ +static bool wm_link_append_item_poll( + ReportList *reports, const char *path, const char *group, const char *name, const bool do_append) +{ + short idcode; + + if (!group || !name) { + printf("skipping %s\n", path); + return false; + } + + idcode = BKE_idcode_from_name(group); + + /* XXX For now, we do a nasty exception for workspace, forbid linking them. + * Not nice, ultimately should be solved! */ + if (!BKE_idcode_is_linkable(idcode) && (do_append || idcode != ID_WS)) { + if (reports) { + if (do_append) { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't append data-block '%s' of type '%s'", name, group); + } + else { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't link data-block '%s' of type '%s'", name, group); + } + } + return false; + } + + return true; +} + static int wm_link_append_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -289,6 +323,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) char *group, *name; int totfiles = 0; short flag; + bool has_item = false; + bool do_append; RNA_string_get(op->ptr, "filename", relname); RNA_string_get(op->ptr, "directory", root); @@ -326,6 +362,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) } flag = wm_link_append_flag(op); + do_append = (flag & FILE_LINK) == 0; /* sanity checks for flag */ if (scene && scene->id.lib) { @@ -361,7 +398,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) BLI_join_dirfile(path, sizeof(path), root, relname); if (BLO_library_path_explode(path, libname, &group, &name)) { - if (!group || !name) { + if (!wm_link_append_item_poll(NULL, path, group, name, do_append)) { continue; } @@ -369,6 +406,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); lib_idx++; wm_link_append_data_library_add(lapp_data, libname); + has_item = true; } } } @@ -382,8 +420,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) if (BLO_library_path_explode(path, libname, &group, &name)) { WMLinkAppendDataItem *item; - if (!group || !name) { - printf("skipping %s\n", path); + + if (!wm_link_append_item_poll(op->reports, path, group, name, do_append)) { continue; } @@ -391,6 +429,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); BLI_BITMAP_ENABLE(item->libraries, lib_idx); + has_item = true; } } RNA_END; @@ -403,6 +442,12 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) wm_link_append_data_library_add(lapp_data, libname); item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); BLI_BITMAP_ENABLE(item->libraries, 0); + has_item = true; + } + + if (!has_item) { + wm_link_append_data_free(lapp_data); + return OPERATOR_CANCELLED; } /* XXX We'd need re-entrant locking on Main for this to work... */ @@ -417,7 +462,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) IMB_colormanagement_check_file_config(bmain); /* append, rather than linking */ - if ((flag & FILE_LINK) == 0) { + if (do_append) { const bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive"); @@ -572,6 +617,7 @@ static void lib_relocate_do( LinkNode *itemlink; int item_idx; + bool has_item = false; /* Remove all IDs to be reloaded from Main. */ lba_idx = set_listbasepointers(bmain, lbarray); @@ -593,6 +639,7 @@ static void lib_relocate_do( BLI_remlink(lbarray[lba_idx], id); item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id); BLI_BITMAP_SET_ALL(item->libraries, true, lapp_data->num_libraries); + has_item = true; #ifdef PRINT_DEBUG printf("\tdatablock to seek for: %s\n", id->name); @@ -601,6 +648,11 @@ static void lib_relocate_do( } } + if (!has_item) { + /* nothing to relocate */ + return; + } + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); /* We do not want any instanciation here! */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index ce4a65b854a..022502c9935 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -478,7 +478,7 @@ void WM_exit_ext(bContext *C, const bool do_python) CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); - ED_screen_exit(C, win, win->screen); + ED_screen_exit(C, win, WM_window_get_active_screen(win)); } } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 00fc75eb282..3665b6f2c12 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2101,8 +2101,11 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot) static int wm_operator_winactive_normal(bContext *C) { wmWindow *win = CTX_wm_window(C); + bScreen *screen; - if (win == NULL || win->screen == NULL || win->screen->state != SCREENNORMAL) + if (win == NULL) + return 0; + if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) return 0; return 1; @@ -3830,11 +3833,12 @@ static void redraw_timer_step( CTX_wm_window_set(C, win); /* XXX context manipulation warning! */ } else if (type == eRTDrawWindow) { + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa_iter; CTX_wm_menu_set(C, NULL); - for (sa_iter = win->screen->areabase.first; sa_iter; sa_iter = sa_iter->next) { + for (sa_iter = screen->areabase.first; sa_iter; sa_iter = sa_iter->next) { ARegion *ar_iter; CTX_wm_area_set(C, sa_iter); @@ -4209,6 +4213,7 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_read_factory_settings); WM_operatortype_append(WM_OT_save_homefile); WM_operatortype_append(WM_OT_save_userpref); + WM_operatortype_append(WM_OT_save_workspace_file); WM_operatortype_append(WM_OT_userpref_autoexec_path_add); WM_operatortype_append(WM_OT_userpref_autoexec_path_remove); WM_operatortype_append(WM_OT_window_fullscreen_toggle); diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index 94e4e85026f..f8c4639c1b1 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -373,7 +373,8 @@ static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) { - bScreen *screen = win->screen; + const bScreen *screen = WM_window_get_active_screen(win); + const Scene *scene = WM_window_get_active_scene(win); /* some 3d methods change the window arrangement, thus they shouldn't * toggle on/off just because there is no 3d elements being drawn */ @@ -381,7 +382,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen; } - if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) { + if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) { return false; } @@ -508,7 +509,7 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op) prev_display_mode != win_src->stereo3d_format->display_mode) { /* in case the hardward supports pageflip but not the display */ - if ((win_dst = wm_window_copy_test(C, win_src))) { + if ((win_dst = wm_window_copy_test(C, win_src, false))) { /* pass */ } else { @@ -518,14 +519,16 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op) } } else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { - /* ED_screen_duplicate() can't handle other cases yet T44688 */ - if (win_src->screen->state != SCREENNORMAL) { + const bScreen *screen = WM_window_get_active_screen(win_src); + + /* ED_workspace_layout_duplicate() can't handle other cases yet T44688 */ + if (screen->state != SCREENNORMAL) { BKE_report(op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen"); ok = false; } /* pageflip requires a new window to be created with the proper OS flags */ - else if ((win_dst = wm_window_copy_test(C, win_src))) { + else if ((win_dst = wm_window_copy_test(C, win_src, false))) { if (wm_stereo3d_quadbuffer_supported()) { BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created"); } diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c index 7613479aacd..1a8f71891c0 100644 --- a/source/blender/windowmanager/intern/wm_subwindow.c +++ b/source/blender/windowmanager/intern/wm_subwindow.c @@ -141,8 +141,10 @@ void wm_subwindow_origin_get(wmWindow *win, int swinid, int *x, int *y) static void wm_swin_matrix_get(wmWindow *win, wmSubWindow *swin, float mat[4][4]) { + const bScreen *screen = WM_window_get_active_screen(win); + /* used by UI, should find a better way to get the matrix there */ - if (swin->swinid == win->screen->mainwin) { + if (swin->swinid == screen->mainwin) { int width, height; wm_swin_size_get(swin, &width, &height); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 3ff323862b3..66df41e5b84 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -38,6 +38,7 @@ #include "DNA_listBase.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "MEM_guardedalloc.h" @@ -55,6 +56,8 @@ #include "BKE_library.h" #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_screen.h" +#include "BKE_workspace.h" #include "RNA_access.h" @@ -68,6 +71,7 @@ #include "wm_subwindow.h" #include "wm_event_system.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_fileselect.h" @@ -218,6 +222,7 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) wm_ghostwindow_destroy(win); + BKE_workspace_instance_hook_free(G.main, win->workspace_hook); MEM_freeN(win->stereo3d_format); MEM_freeN(win); @@ -238,13 +243,15 @@ static int find_free_winid(wmWindowManager *wm) /* don't change context itself */ wmWindow *wm_window_new(bContext *C) { + Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = MEM_callocN(sizeof(wmWindow), "window"); - + BLI_addtail(&wm->windows, win); win->winid = find_free_winid(wm); win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)"); + win->workspace_hook = BKE_workspace_instance_hook_create(bmain); return win; } @@ -270,22 +277,26 @@ static wmWindow *wm_window_new_test(bContext *C) } /* part of wm_window.c api */ -wmWindow *wm_window_copy(bContext *C, wmWindow *win_src) +wmWindow *wm_window_copy(bContext *C, wmWindow *win_src, const bool duplicate_layout) { wmWindow *win_dst = wm_window_new(C); - + WorkSpace *workspace = WM_window_get_active_workspace(win_src); + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win_src); + Scene *scene = WM_window_get_active_scene(win_src); + WorkSpaceLayout *layout_new; + bScreen *new_screen; + win_dst->posx = win_src->posx + 10; win_dst->posy = win_src->posy; win_dst->sizex = win_src->sizex; win_dst->sizey = win_src->sizey; - - /* duplicate assigns to window */ - win_dst->screen = ED_screen_duplicate(win_dst, win_src->screen); - BLI_strncpy(win_dst->screenname, win_dst->screen->id.name + 2, sizeof(win_dst->screenname)); - win_dst->screen->winid = win_dst->winid; - win_dst->screen->do_refresh = true; - win_dst->screen->do_draw = true; + win_dst->scene = scene; + WM_window_set_active_workspace(win_dst, workspace); + layout_new = duplicate_layout ? ED_workspace_layout_duplicate(workspace, layout_old, win_dst) : layout_old; + WM_window_set_active_layout(win_dst, workspace, layout_new); + new_screen = WM_window_get_active_screen(win_dst); + BLI_strncpy(win_dst->screenname, new_screen->id.name + 2, sizeof(win_dst->screenname)); win_dst->drawmethod = U.wmdrawmethod; @@ -300,12 +311,12 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *win_src) * A higher level version of copy that tests the new window can be added. * (called from the operator directly) */ -wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src) +wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout) { wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win_dst; - win_dst = wm_window_copy(C, win_src); + win_dst = wm_window_copy(C, win_src, duplicate_layout); WM_check(C); @@ -329,7 +340,7 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) { if (tmpwin == win) continue; - if (tmpwin->screen->temp == 0) + if (WM_window_is_temp_screen(tmpwin) == false) break; } @@ -348,8 +359,10 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) WM_exit(C); } else { - bScreen *screen = win->screen; - + bScreen *screen = WM_window_get_active_screen(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = BKE_workspace_active_layout_get(win->workspace_hook); + BLI_remlink(&wm->windows, win); wm_draw_window_clear(win); @@ -360,8 +373,8 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) /* for regular use this will _never_ be NULL, * however we may be freeing an improperly initialized window. */ - if (win->screen) { - ED_screen_exit(C, win, win->screen); + if (screen) { + ED_screen_exit(C, win, screen); } wm_window_free(C, wm, win); @@ -369,14 +382,16 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) /* if temp screen, delete it after window free (it stops jobs that can access it) */ if (screen && screen->temp) { Main *bmain = CTX_data_main(C); - BKE_libblock_free(bmain, screen); + + BLI_assert(BKE_workspace_layout_screen_get(layout) == screen); + BKE_workspace_layout_remove(bmain, workspace, layout); } - } + } } void wm_window_title(wmWindowManager *wm, wmWindow *win) { - if (win->screen && win->screen->temp) { + if (WM_window_is_temp_screen(win)) { /* nothing to do for 'temp' windows, * because WM_window_open_temp always sets window title */ } @@ -668,8 +683,10 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect) */ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, int type) { + Main *bmain = CTX_data_main(C); wmWindow *win_prev = CTX_wm_window(C); wmWindow *win; + bScreen *screen; ScrArea *sa; Scene *scene = CTX_data_scene(C); const char *title; @@ -693,7 +710,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i /* test if we have a temp screen already */ for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) - if (win->screen->temp) + if (WM_window_is_temp_screen(win)) break; /* add new window? */ @@ -704,6 +721,8 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i win->posy = rect.ymin; } + screen = WM_window_get_active_screen(win); + win->sizex = BLI_rcti_size_x(&rect); win->sizey = BLI_rcti_size_y(&rect); @@ -711,19 +730,27 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i wm_window_set_size(win, win->sizex, win->sizey); wm_window_raise(win); } - - if (win->screen == NULL) { - /* add new screen */ - win->screen = ED_screen_add(win, scene, "temp"); + + if (WM_window_get_active_workspace(win) == NULL) { + WorkSpace *workspace = WM_window_get_active_workspace(win_prev); + WM_window_set_active_workspace(win, workspace); } - else { - /* switch scene for rendering */ - if (win->screen->scene != scene) - ED_screen_set_scene(C, win->screen, scene); + + if (screen == NULL) { + /* add new screen layout */ + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = ED_workspace_layout_add(workspace, win, "temp"); + + screen = BKE_workspace_layout_screen_get(layout); + WM_window_set_active_layout(win, workspace, layout); } - win->screen->temp = 1; - + if (WM_window_get_active_scene(win) != scene) { + WM_window_change_active_scene(bmain, C, win, scene); + } + + screen->temp = 1; + /* make window active, and validate/resize */ CTX_wm_window_set(C, win); WM_check(C); @@ -735,7 +762,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i */ /* ensure it shows the right spacetype editor */ - sa = win->screen->areabase.first; + sa = screen->areabase.first; CTX_wm_area_set(C, sa); if (type == WM_WINDOW_RENDER) { @@ -745,7 +772,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i ED_area_newspace(C, sa, SPACE_USERPREF, false); } - ED_screen_set(C, win->screen); + ED_screen_change(C, screen); ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */ if (sa->spacetype == SPACE_IMAGE) @@ -781,23 +808,42 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } +static WorkSpaceLayout *wm_window_new_find_layout(wmOperator *op, WorkSpace *workspace) +{ + ListBase *listbase = BKE_workspace_layouts_get(workspace); + const int layout_id = RNA_enum_get(op->ptr, "screen"); + int i = 0; + + for (WorkSpaceLayout *layout = listbase->first; layout; layout = layout->next) { + if (i++ == layout_id) { + return layout; + } + } + + BLI_assert(0); + return NULL; +} + /* new window operator callback */ int wm_window_new_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); wmWindow *win_src = CTX_wm_window(C); - const int screen_id = RNA_enum_get(op->ptr, "screen"); - bScreen *screen = BLI_findlink(&bmain->screen, screen_id); + WorkSpace *workspace = WM_window_get_active_workspace(win_src); + WorkSpaceLayout *layout_new = wm_window_new_find_layout(op, workspace); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); wmWindow *win_dst; - if (screen->winid) { - /* Screen is already used, duplicate window and screen */ - win_dst = wm_window_copy_test(C, win_src); - } - else if ((win_dst = wm_window_new_test(C))) { - /* New window with a different screen */ - win_dst->screen = screen; - screen->winid = win_dst->winid; + if ((win_dst = wm_window_new_test(C))) { + if (screen_new->winid) { + /* layout/screen is already used, duplicate it */ + layout_new = ED_workspace_layout_duplicate(workspace, layout_new, win_dst); + screen_new = BKE_workspace_layout_screen_get(layout_new); + } + /* New window with a different screen but same workspace */ + WM_window_set_active_workspace(win_dst, workspace); + WM_window_set_active_screen(win_dst, workspace, screen_new); + win_dst->scene = win_src->scene; + screen_new->winid = win_dst->winid; CTX_wm_window_set(C, win_dst); ED_screen_refresh(CTX_wm_manager(C), win_dst); } @@ -807,9 +853,11 @@ int wm_window_new_exec(bContext *C, wmOperator *op) int wm_window_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); + ListBase *listbase = BKE_workspace_layouts_get(workspace); - if (BLI_listbase_count_ex(&bmain->screen, 2) == 1) { + if (BLI_listbase_count_ex(listbase, 2) == 1) { RNA_enum_set(op->ptr, "screen", 0); return wm_window_new_exec(C, op); } @@ -821,7 +869,9 @@ int wm_window_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(even struct EnumPropertyItem *wm_window_new_screen_itemf( bContext *C, struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), bool *r_free) { - Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); + ListBase *listbase = BKE_workspace_layouts_get(workspace); EnumPropertyItem *item = NULL; EnumPropertyItem tmp = {0, "", 0, "", ""}; int value = 0, totitem = 0; @@ -830,18 +880,20 @@ struct EnumPropertyItem *wm_window_new_screen_itemf( * for dynamic strings in EnumPropertyItem.name to avoid this. */ static char active_screens[20][MAX_NAME + 12]; - for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { + for (WorkSpaceLayout *layout = listbase->first; layout; layout = layout->next) { + bScreen *screen = BKE_workspace_layout_screen_get(layout); + const char *layout_name = BKE_workspace_layout_name_get(layout); + if (screen->winid) { - BLI_snprintf(active_screens[count_act_screens], sizeof(*active_screens), "%s (Duplicate)", - screen->id.name + 2); + BLI_snprintf(active_screens[count_act_screens], sizeof(*active_screens), "%s (Duplicate)", layout_name); tmp.name = active_screens[count_act_screens++]; } else { - tmp.name = screen->id.name + 2; + tmp.name = layout_name; } tmp.value = value; - tmp.identifier = screen->id.name; + tmp.identifier = layout_name; UI_id_icon_render(C, CTX_data_scene(C), &screen->id, true, false); tmp.icon = BKE_icon_id_ensure(&screen->id); @@ -1156,7 +1208,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* stop screencast if resize */ if (type == GHOST_kEventWindowSize) { - WM_jobs_stop(wm, win->screen, NULL); + WM_jobs_stop(wm, WM_window_get_active_screen(win), NULL); } wm_window_set_dpi(win); @@ -1191,6 +1243,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr win->posx != posx || win->posy != posy) { + const bScreen *screen = WM_window_get_active_screen(win); + win->sizex = sizex; win->sizey = sizey; win->posx = posx; @@ -1227,7 +1281,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_window_make_drawable(wm, win); wm_draw_window_clear(win); - BKE_icon_changed(win->screen->id.icon_id); + BKE_icon_changed(screen->id.icon_id); WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); @@ -1340,7 +1394,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_window_set_dpi(win); if (U.pixelsize != prev_pixelsize) { - BKE_icon_changed(win->screen->id.icon_id); + BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id); // close all popups since they are positioned with the pixel // size baked in and it's difficult to correct them @@ -1847,6 +1901,86 @@ bool WM_window_is_fullscreen(wmWindow *win) return win->windowstate == GHOST_kWindowStateFullScreen; } +/** + * 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 visible workspaces and their visible layouts. + */ +void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene) +{ + for (wmWindow *win = win_lb->first; win; win = win->next) { + if (WM_window_get_active_scene(win) == scene) { + ED_workspace_scene_data_sync(win->workspace_hook, scene); + } + } +} + +Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen) +{ + 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); + } + } + + return NULL; +} + +Scene *WM_window_get_active_scene(const wmWindow *win) +{ + return win->scene; +} + +/** + * \warning Only call outside of area/region loops + */ +void WM_window_change_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene_new) +{ + const bScreen *screen = WM_window_get_active_screen(win); + + ED_scene_exit(C); + win->scene = scene_new; + ED_scene_changed_update(bmain, C, scene_new, screen); +} + +WorkSpace *WM_window_get_active_workspace(const wmWindow *win) +{ + return BKE_workspace_active_get(win->workspace_hook); +} +void WM_window_set_active_workspace(wmWindow *win, WorkSpace *workspace) +{ + BKE_workspace_active_set(win->workspace_hook, workspace); +} + +WorkSpaceLayout *WM_window_get_active_layout(const wmWindow *win) +{ + const WorkSpace *workspace = WM_window_get_active_workspace(win); + return (LIKELY(workspace != NULL) ? BKE_workspace_active_layout_get(win->workspace_hook): NULL); +} +void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout) +{ + BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout); +} + +/** + * Get the active screen of the active workspace in \a win. + */ +bScreen *WM_window_get_active_screen(const wmWindow *win) +{ + const WorkSpace *workspace = WM_window_get_active_workspace(win); + /* May be NULL in rare cases like closing Blender */ + return (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL); +} +void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen) +{ + BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen); +} + +bool WM_window_is_temp_screen(const wmWindow *win) +{ + const bScreen *screen = WM_window_get_active_screen(win); + return (screen && screen->temp != 0); +} + #ifdef WITH_INPUT_IME /* note: keep in mind wm_window_IME_begin is also used to reposition the IME window */ diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 9a1518e15b0..4fbbe3d2879 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -45,6 +45,7 @@ void WM_OT_save_homefile(struct wmOperatorType *ot); void WM_OT_userpref_autoexec_path_add(struct wmOperatorType *ot); void WM_OT_userpref_autoexec_path_remove(struct wmOperatorType *ot); void WM_OT_save_userpref(struct wmOperatorType *ot); +void WM_OT_save_workspace_file(struct wmOperatorType *ot); void WM_OT_read_history(struct wmOperatorType *ot); void WM_OT_read_homefile(struct wmOperatorType *ot); void WM_OT_read_factory_settings(struct wmOperatorType *ot); diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index 089e2994d3d..c8f89a5bf62 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -46,8 +46,8 @@ void wm_get_screensize(int *r_width, int *r_height); void wm_get_desktopsize(int *r_width, int *r_height); wmWindow *wm_window_new (bContext *C); -wmWindow *wm_window_copy (bContext *C, wmWindow *win_src); -wmWindow *wm_window_copy_test (bContext *C, wmWindow *win_src); +wmWindow *wm_window_copy (bContext *C, wmWindow *win_src, const bool duplicate_layout); +wmWindow *wm_window_copy_test (bContext *C, wmWindow *win_src, const bool duplicate_layout); void wm_window_free (bContext *C, wmWindowManager *wm, wmWindow *win); void wm_window_close (bContext *C, wmWindowManager *wm, wmWindow *win); diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index d06777e792c..9d5d4d37a13 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -316,6 +316,12 @@ struct MenuType *WM_menutype_find(const char *idname, bool quiet) RET_NULL void WM_operator_stack_clear(struct wmWindowManager *wm) RET_NONE void WM_operator_handlers_clear(wmWindowManager *wm, struct wmOperatorType *ot) RET_NONE bool WM_operator_is_repeat(const struct bContext *C, const struct wmOperator *op) RET_ZERO; +void WM_windows_scene_data_sync(const struct ListBase *win_lb, struct Scene *scene) RET_NONE +struct Scene *WM_windows_scene_get_from_screen(const struct wmWindowManager *wm, const struct bScreen *screen) RET_NULL +struct bScreen *WM_window_get_active_screen(const struct wmWindow *win) RET_NULL +struct Scene *WM_window_get_active_scene(const struct wmWindow *win) RET_NULL +void WM_window_change_active_scene(struct Main *bmain, struct bContext *C, struct wmWindow *win, struct Scene *scene_new) RET_NONE +bool WM_window_is_temp_screen(const struct wmWindow *win) RET_ZERO void WM_autosave_init(wmWindowManager *wm) RET_NONE void WM_jobs_kill_all_except(struct wmWindowManager *wm, void *owner) RET_NONE @@ -400,7 +406,6 @@ void ED_space_image_scopes_update(const struct bContext *C, struct SpaceImage *s void ED_uvedit_get_aspect(struct Scene *scene, struct Object *ob, struct BMesh *em, float *aspx, float *aspy) RET_NONE -void ED_screen_set_scene(struct bContext *C, struct bScreen *screen, struct Scene *scene) RET_NONE struct MovieClip *ED_space_clip_get_clip(struct SpaceClip *sc) RET_NULL void ED_space_clip_set_clip(struct bContext *C, struct bScreen *screen, struct SpaceClip *sc, struct MovieClip *clip) RET_NONE void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mask *mask) RET_NONE @@ -495,6 +500,7 @@ void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View void ED_node_shader_default(const struct bContext *C, struct ID *id) RET_NONE void ED_screen_animation_timer_update(struct bScreen *screen, int redraws, int refresh) RET_NONE struct bScreen *ED_screen_animation_playing(const struct wmWindowManager *wm) RET_NULL +struct Scene *ED_screen_scene_find(const struct bScreen *screen, const struct wmWindowManager *wm) RET_NULL void ED_base_object_select(struct BaseLegacy *base, short mode) RET_NONE void ED_object_base_select(struct Base *base, short mode) RET_NONE bool ED_object_modifier_remove(struct ReportList *reports, struct Main *bmain, struct Object *ob, struct ModifierData *md) RET_ZERO diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 8f26e248424..11e22fffa4d 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -412,6 +412,12 @@ if(WITH_OPENCOLORIO) ) endif() +# Add default workspaces.blend to build (under [version]/datafiles +install( + FILES ${CMAKE_SOURCE_DIR}/release/datafiles/workspaces.blend + DESTINATION ${TARGETDIR_VER}/datafiles +) + # helpful tip when using make if("${CMAKE_GENERATOR}" MATCHES ".*Makefiles.*") # message after building. diff --git a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp index a6b2340d7b4..6fdc823ba1a 100644 --- a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp +++ b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp @@ -324,7 +324,6 @@ static unsigned int *screenshot(ScrArea *curarea, int *dumpsx, int *dumpsy) void KX_BlenderCanvas::MakeScreenShot(const char *filename) { ScrArea area_dummy= {0}; - bScreen *screen = m_win->screen; unsigned int *dumprect; int dumpsx, dumpsy; @@ -340,7 +339,7 @@ void KX_BlenderCanvas::MakeScreenShot(const char *filename) } /* initialize image file format data */ - Scene *scene = (screen)? screen->scene: NULL; + Scene *scene = WM_window_get_active_scene(m_win); ImageFormatData *im_format = (ImageFormatData *)MEM_mallocN(sizeof(ImageFormatData), "im_format"); if (scene) |