diff options
Diffstat (limited to 'source/blender/windowmanager/intern/wm_toolsystem.c')
-rw-r--r-- | source/blender/windowmanager/intern/wm_toolsystem.c | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c new file mode 100644 index 00000000000..cee5079a02f --- /dev/null +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -0,0 +1,605 @@ +/* + * ***** 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/windowmanager/intern/wm_toolsystem.c + * \ingroup wm + * + * Experimental tool-system> + */ + +#include <string.h> + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_listbase.h" + +#include "DNA_ID.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" +#include "DNA_object_types.h" + +#include "BKE_context.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_paint.h" +#include "BKE_idprop.h" +#include "BKE_workspace.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "WM_message.h" +#include "WM_toolsystem.h" /* own include */ + +static void toolsystem_reinit_with_toolref( + bContext *C, WorkSpace *UNUSED(workspace), bToolRef *tref); +static void toolsystem_reinit_ensure_toolref( + bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool); +static void toolsystem_refresh_screen_from_active_tool( + Main *bmain, WorkSpace *workspace, bToolRef *tref); + +/* -------------------------------------------------------------------- */ +/** \name Tool Reference API + * \{ */ + +struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + Scene *scene = CTX_data_scene(C); + ScrArea *sa = CTX_wm_area(C); + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype), + }; + bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey); + /* We could return 'sa->runtime.tool' in this case. */ + if (sa->runtime.is_tool_set) { + BLI_assert(tref == sa->runtime.tool); + } + return tref; +} + +struct bToolRef_Runtime *WM_toolsystem_runtime_from_context(struct bContext *C) +{ + bToolRef *tref = WM_toolsystem_ref_from_context(C); + return tref ? tref->runtime : NULL; +} + +bToolRef *WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey) +{ + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + if ((tref->space_type == tkey->space_type) && + (tref->mode == tkey->mode)) + { + return tref; + } + } + return NULL; +} + +bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + return tref ? tref->runtime : NULL; +} + +bool WM_toolsystem_ref_ensure( + struct WorkSpace *workspace, const bToolKey *tkey, + bToolRef **r_tref) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref) { + *r_tref = tref; + return false; + } + tref = MEM_callocN(sizeof(*tref), __func__); + BLI_addhead(&workspace->tools, tref); + tref->space_type = tkey->space_type; + tref->mode = tkey->mode; + *r_tref = tref; + return true; +} + +/** \} */ + + +static void toolsystem_unlink_ref(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + bToolRef_Runtime *tref_rt = tref->runtime; + + if (tref_rt->manipulator_group[0]) { + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(tref_rt->manipulator_group, false); + if (wgt != NULL) { + bool found = false; + + /* TODO(campbell) */ + Main *bmain = CTX_data_main(C); +#if 0 + wmWindowManager *wm = bmain->wm.first; + /* Check another workspace isn't using this tool. */ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + const WorkSpace *workspace_iter = WM_window_get_active_workspace(win); + if (workspace != workspace_iter) { + if (STREQ(workspace->tool.manipulator_group, workspace_iter->tool.manipulator_group)) { + found = true; + break; + } + } + } +#else + UNUSED_VARS(workspace); +#endif + if (!found) { + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulatormaptype_group_unlink(C, bmain, mmap_type, wgt); + } + } + } +} +void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref && tref->runtime) { + toolsystem_unlink_ref(C, workspace, tref); + } +} + +static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + bToolRef_Runtime *tref_rt = tref->runtime; + if (tref_rt->manipulator_group[0]) { + const char *idname = tref_rt->manipulator_group; + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + if (wgt != NULL) { + WM_manipulator_group_type_ensure_ptr(wgt); + } + else { + CLOG_WARN(WM_LOG_TOOLS, "'%s' widget not found", idname); + } + } + + if (tref_rt->data_block[0]) { + Main *bmain = CTX_data_main(C); + + /* Currently only brush data-blocks supported. */ + struct Brush *brush = (struct Brush *)BKE_libblock_find_name(bmain, ID_BR, tref_rt->data_block); + + if (brush) { + wmWindowManager *wm = bmain->wm.first; + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (workspace == WM_window_get_active_workspace(win)) { + Scene *scene = win->scene; + ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene); + Paint *paint = BKE_paint_get_active(scene, view_layer); + if (paint) { + if (brush) { + BKE_paint_brush_set(paint, brush); + } + } + } + } + } + } +} + +static void toolsystem_refresh_ref(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + if (tref->runtime == NULL) { + return; + } + /* currently same operation. */ + toolsystem_ref_link(C, workspace, tref); +} +void WM_toolsystem_refresh(bContext *C, WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref) { + toolsystem_refresh_ref(C, workspace, tref); + } +} + +static void toolsystem_reinit_ref(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + toolsystem_reinit_with_toolref(C, workspace, tref); +} +void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref) { + toolsystem_reinit_ref(C, workspace, tref); + } +} + +/* Operate on all active tools. */ +void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace) +{ + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + tref->tag = 0; + } + + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + if (tref->runtime) { + if (tref->tag == 0) { + toolsystem_unlink_ref(C, workspace, tref); + tref->tag = 1; + } + } + } +} + +void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace) +{ + BLI_assert(0); + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + toolsystem_refresh_ref(C, workspace, tref); + } +} +void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win) +{ + bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = WM_window_get_active_scene(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype), + }; + bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey); + if (tref) { + if (tref->tag == 0) { + toolsystem_reinit_ref(C, workspace, tref); + tref->tag = 1; + } + } + } +} + +void WM_toolsystem_ref_set_from_runtime( + struct bContext *C, struct WorkSpace *workspace, bToolRef *tref, + const bToolRef_Runtime *tref_rt, const char *idname) +{ + Main *bmain = CTX_data_main(C); + + if (tref->runtime) { + toolsystem_unlink_ref(C, workspace, tref); + } + + STRNCPY(tref->idname, idname); + + /* BAD DESIGN WARNING: used for topbar. */ + workspace->tools_space_type = tref->space_type; + workspace->tools_mode = tref->mode; + + if (tref->runtime == NULL) { + tref->runtime = MEM_callocN(sizeof(*tref->runtime), __func__); + } + + if (tref_rt != tref->runtime) { + *tref->runtime = *tref_rt; + } + + toolsystem_ref_link(C, workspace, tref); + + toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref); + + { + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + WM_msg_publish_rna_prop( + mbus, &workspace->id, workspace, WorkSpace, tools); + } +} + +void WM_toolsystem_init(bContext *C) +{ + Main *bmain = CTX_data_main(C); + + BLI_assert(CTX_wm_window(C) == NULL); + + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + MEM_SAFE_FREE(tref->runtime); + tref->tag = 0; + } + } + + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + CTX_wm_window_set(C, win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = WM_window_get_active_scene(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype), + }; + bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey); + if (tref) { + if (tref->tag == 0) { + toolsystem_reinit_ref(C, workspace, tref); + tref->tag = 1; + } + } + } + CTX_wm_window_set(C, NULL); + } + } +} + +int WM_toolsystem_mode_from_spacetype( + WorkSpace *workspace, Scene *scene, ScrArea *sa, + int spacetype) +{ + int mode = -1; + switch (spacetype) { + case SPACE_VIEW3D: + { + /* 'sa' may be NULL in this case. */ + ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene); + Object *obact = OBACT(view_layer); + if (obact != NULL) { + Object *obedit = OBEDIT_FROM_OBACT(obact); + mode = CTX_data_mode_enum_ex(obedit, obact, obact->mode); + } + else { + mode = CTX_MODE_OBJECT; + } + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima = sa->spacedata.first; + mode = sima->mode; + break; + } + } + return mode; +} + +bool WM_toolsystem_key_from_context( + WorkSpace *workspace, Scene *scene, ScrArea *sa, + bToolKey *tkey) +{ + int space_type = SPACE_EMPTY; + int mode = -1; + + if (sa != NULL) { + space_type = sa->spacetype; + mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, space_type); + } + + if (mode != -1) { + tkey->space_type = space_type; + tkey->mode = mode; + return true; + } + return false; +} + +void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, Scene *scene, ScrArea *sa) +{ + sa->runtime.tool = NULL; + sa->runtime.is_tool_set = true; + const int mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype); + for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) { + if (tref->space_type == sa->spacetype) { + if (tref->mode == mode) { + sa->runtime.tool = tref; + break; + } + } + } +} + +void WM_toolsystem_refresh_screen_all(Main *bmain) +{ + /* Update all ScrArea's tools */ + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0}; + for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) { + space_type_has_tools[tref->space_type] = true; + } + bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = WM_window_get_active_scene(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + sa->runtime.tool = NULL; + sa->runtime.is_tool_set = true; + if (space_type_has_tools[sa->spacetype]) { + WM_toolsystem_refresh_screen_area(workspace, scene, sa); + } + } + } + } +} + +static void toolsystem_refresh_screen_from_active_tool( + Main *bmain, WorkSpace *workspace, bToolRef *tref) +{ + /* Update all ScrArea's tools */ + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (workspace == WM_window_get_active_workspace(win)) { + bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = WM_window_get_active_scene(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + if (sa->spacetype == tref->space_type) { + int mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype); + if (mode == tref->mode) { + sa->runtime.tool = tref; + sa->runtime.is_tool_set = true; + } + } + } + } + } + } +} + +bToolRef *WM_toolsystem_ref_set_by_name( + bContext *C, WorkSpace *workspace, const bToolKey *tkey, + const char *name, bool cycle) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_name", false); + /* On startup, Python operatores are not yet loaded. */ + if (ot == NULL) { + return NULL; + } + PointerRNA op_props; + WM_operator_properties_create_ptr(&op_props, ot); + RNA_string_set(&op_props, "name", name); + + /* Will get from context if not set. */ + bToolKey tkey_from_context; + if (tkey == NULL) { + Scene *scene = CTX_data_scene(C); + ScrArea *sa = CTX_wm_area(C); + WM_toolsystem_key_from_context(workspace, scene, sa, &tkey_from_context); + tkey = &tkey_from_context; + } + + RNA_enum_set(&op_props, "space_type", tkey->space_type); + RNA_boolean_set(&op_props, "cycle", cycle); + + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props); + WM_operator_properties_free(&op_props); + + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + + if (tref) { + Main *bmain = CTX_data_main(C); + toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref); + } + + return (tref && STREQ(tref->idname, name)) ? tref : NULL; +} + +static void toolsystem_reinit_with_toolref( + bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + bToolKey tkey = { + .space_type = tref->space_type, + .mode = tref->mode, + }; + WM_toolsystem_ref_set_by_name(C, workspace, &tkey, tref->idname, false); +} + +/** + * Run after changing modes. + */ +static void toolsystem_reinit_ensure_toolref( + bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool) +{ + bToolRef *tref; + if (WM_toolsystem_ref_ensure(workspace, tkey, &tref)) { + STRNCPY(tref->idname, default_tool); + } + + toolsystem_reinit_with_toolref(C, workspace, tref); +} + +void WM_toolsystem_update_from_context_view3d(bContext *C) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + Scene *scene = CTX_data_scene(C); + int space_type = SPACE_VIEW3D; + const bToolKey tkey = { + .space_type = space_type, + .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, NULL, space_type), + }; + toolsystem_reinit_ensure_toolref(C, workspace, &tkey, "Cursor"); +} + +/** + * For paint modes to support non-brush tools. + */ +bool WM_toolsystem_active_tool_is_brush(const bContext *C) +{ + bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C); + return tref_rt && (tref_rt->data_block[0] != '\0'); +} + +/* Follow wmMsgNotifyFn spec */ +void WM_toolsystem_do_msg_notify_tag_refresh( + bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + Scene *scene = CTX_data_scene(C); + ScrArea *sa = msg_val->user_data; + int space_type = sa->spacetype; + const bToolKey tkey = { + .space_type = space_type, + .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype), + }; + WM_toolsystem_refresh(C, workspace, &tkey); +} + +IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref) +{ + if (tref->properties == NULL) { + IDPropertyTemplate val = {0}; + tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + } + return tref->properties; +} + +void WM_toolsystem_ref_properties_ensure(bToolRef *tref, wmOperatorType *ot, PointerRNA *ptr) +{ + IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref); + IDProperty *prop = IDP_GetPropertyFromGroup(group, ot->idname); + if (prop == NULL) { + IDPropertyTemplate val = {0}; + prop = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + STRNCPY(prop->name, ot->idname); + IDP_ReplaceInGroup_ex(group, prop, NULL); + } + else { + BLI_assert(prop->type == IDP_GROUP); + } + + RNA_pointer_create(NULL, ot->srna, prop, ptr); +} + +void WM_toolsystem_ref_properties_init_for_keymap( + bToolRef *tref, PointerRNA *dst_ptr, PointerRNA *src_ptr, wmOperatorType *ot) +{ + *dst_ptr = *src_ptr; + if (dst_ptr->data) { + dst_ptr->data = IDP_CopyProperty(dst_ptr->data); + } + else { + IDPropertyTemplate val = {0}; + dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp"); + } + if (tref->properties != NULL) { + IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname); + if (prop) { + IDP_MergeGroup(dst_ptr->data, prop, true); + } + } +} |