This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/screen/workspace_edit.c
Campbell Barton d4161845b2 Tool System: show manipulators at startup
Support duplicating, switching workspaces too.
2017-11-27 18:36:54 +11:00

449 lines
14 KiB
C

/*
* ***** 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_scene.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 "MEM_guardedalloc.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, ViewLayer *act_view_layer, ViewRender *view_render)
{
WorkSpace *workspace = BKE_workspace_add(bmain, name);
BKE_workspace_view_layer_set(workspace, act_view_layer);
BKE_viewrender_copy(&workspace->view_render, view_render);
#ifdef USE_WORKSPACE_MODE
BKE_workspace_object_mode_set(workspace, OB_MODE_OBJECT);
#endif
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)
{
eObjectMode mode_old = BKE_workspace_object_mode_get(workspace_old);
eObjectMode 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_view_layer(
WorkSpace *workspace_new, const WorkSpace *workspace_old)
{
if (!BKE_workspace_view_layer_get(workspace_new)) {
BKE_workspace_view_layer_set(workspace_new, BKE_workspace_view_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_view_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_view_layer_get(workspace_new) != NULL);
BLI_assert(CTX_wm_workspace(C) == workspace_new);
WM_toolsystem_unlink(C, workspace_old);
WM_toolsystem_link(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_view_layer_get(workspace_old),
&workspace_old->view_render);
ListBase *transform_orientations_old = BKE_workspace_transform_orientations_get(workspace_old);
ListBase *transform_orientations_new = BKE_workspace_transform_orientations_get(workspace_new);
#ifdef USE_WORKSPACE_MODE
BKE_workspace_object_mode_set(workspace_new, BKE_workspace_object_mode_get(workspace_old));
#endif
BLI_duplicatelist(transform_orientations_new, transform_orientations_old);
workspace_new->tool = workspace_old->tool;
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)
{
ID *workspace_id = (ID *)workspace;
if (BLI_listbase_is_single(&bmain->workspaces)) {
return false;
}
for (wmWindow *win = wm->windows.first; win; win = win->next) {
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);
}
void ED_workspace_view_layer_unset(
const Main *bmain, const ViewLayer *layer_unset, ViewLayer *layer_new)
{
for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
if (BKE_workspace_view_layer_get(workspace) == layer_unset) {
BKE_workspace_view_layer_set(workspace, layer_new);
}
}
}
/** \} 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);
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"));
uiItemFullO_ptr(
layout, ot_append, workspace->id.name + 2, ICON_NONE, NULL,
WM_OP_EXEC_DEFAULT, 0, &opptr);
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 */