diff options
Diffstat (limited to 'source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c')
-rw-r--r-- | source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c b/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c new file mode 100644 index 00000000000..f9f46b0f71a --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c @@ -0,0 +1,588 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c + * \ingroup wm + * + * \name Manipulator-Group + * + * Manipulator-groups store and manage groups of manipulators. They can be + * attached to modal handlers and have own keymaps. + */ + +#include <stdlib.h> + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BPY_extern.h" + +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm_event_system.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorGroup + * + * \{ */ + +/* wmManipulatorGroup.flag */ +enum { + WM_MANIPULATORGROUP_INITIALIZED = (1 << 2), /* mgroup has been initialized */ +}; + +/** + * Create a new manipulator-group from \a mgrouptype. + */ +wmManipulatorGroup *wm_manipulatorgroup_new_from_type(wmManipulatorGroupType *mgrouptype) +{ + wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group"); + mgroup->type = mgrouptype; + + return mgroup; +} + +void wm_manipulatorgroup_free(bContext *C, wmManipulatorMap *mmap, wmManipulatorGroup *mgroup) +{ + for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator;) { + wmManipulator *manipulator_next = manipulator->next; + WM_manipulator_delete(&mgroup->manipulators, mmap, manipulator, C); + manipulator = manipulator_next; + } + BLI_assert(BLI_listbase_is_empty(&mgroup->manipulators)); + +#ifdef WITH_PYTHON + if (mgroup->py_instance) { + /* do this first in case there are any __del__ functions or + * similar that use properties */ + BPY_DECREF_RNA_INVALIDATE(mgroup->py_instance); + } +#endif + + if (mgroup->reports && (mgroup->reports->flag & RPT_FREE)) { + BKE_reports_clear(mgroup->reports); + MEM_freeN(mgroup->reports); + } + + if (mgroup->customdata_free) { + mgroup->customdata_free(mgroup->customdata); + } + else { + MEM_SAFE_FREE(mgroup->customdata); + } + + BLI_remlink(&mmap->manipulator_groups, mgroup); + MEM_freeN(mgroup); +} + +/** + * Add \a manipulator to \a mgroup and make sure its name is unique within the group. + */ +void wm_manipulatorgroup_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *manipulator) +{ + BLI_assert(!BLI_findstring(&mgroup->manipulators, manipulator->idname, offsetof(wmManipulator, idname))); + BLI_addtail(&mgroup->manipulators, manipulator); + manipulator->mgroup = mgroup; +} + +void wm_manipulatorgroup_attach_to_modal_handler( + bContext *C, wmEventHandler *handler, + wmManipulatorGroupType *mgrouptype, wmOperator *op) +{ + /* maybe overly careful, but manipulator-grouptype could come from a failed creation */ + if (!mgrouptype) { + return; + } + + /* now instantiate the manipulator-map */ + mgrouptype->op = op; + + /* try to find map in handler region that contains mgrouptype */ + if (handler->op_region && handler->op_region->manipulator_map) { + handler->manipulator_map = handler->op_region->manipulator_map; + ED_region_tag_redraw(handler->op_region); + } + + WM_event_add_mousemove(C); +} + +wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator( + const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event, + unsigned char *part) +{ + for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) { + if (manipulator->intersect && (manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if ((*part = manipulator->intersect(C, event, manipulator))) { + return manipulator; + } + } + } + + return NULL; +} + +/** + * Adds all manipulators of \a mgroup that can be selected to the head of \a listbase. Added items need freeing! + */ +void wm_manipulatorgroup_intersectable_manipulators_to_list(const wmManipulatorGroup *mgroup, ListBase *listbase) +{ + for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) { + if ((manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) && manipulator->render_3d_intersection) || + ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) == 0 && manipulator->intersect)) + { + BLI_addhead(listbase, BLI_genericNodeN(manipulator)); + } + } + } +} + +void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C) +{ + /* prepare for first draw */ + if (UNLIKELY((mgroup->flag & WM_MANIPULATORGROUP_INITIALIZED) == 0)) { + mgroup->type->init(C, mgroup); + mgroup->flag |= WM_MANIPULATORGROUP_INITIALIZED; + } +} + +bool wm_manipulatorgroup_is_visible(const wmManipulatorGroup *mgroup, const bContext *C) +{ + /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */ + return ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_OP) == 0 || mgroup->type->op) && + (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)); +} + +bool wm_manipulatorgroup_is_visible_in_drawstep(const wmManipulatorGroup *mgroup, const int drawstep) +{ + switch (drawstep) { + case WM_MANIPULATORMAP_DRAWSTEP_2D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) == 0; + case WM_MANIPULATORMAP_DRAWSTEP_3D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D); + case WM_MANIPULATORMAP_DRAWSTEP_IN_SCENE: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCENE_DEPTH); + default: + BLI_assert(0); + return false; + } +} + +/** \name Manipulator operators + * + * Basic operators for manipulator interaction with user configurable keymaps. + * + * \{ */ + +static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + ARegion *ar = CTX_wm_region(C); + wmManipulatorMap *mmap = ar->manipulator_map; + wmManipulator ***sel = &mmap->mmap_context.selected_manipulator; + wmManipulator *highlighted = mmap->mmap_context.highlighted_manipulator; + + bool extend = RNA_boolean_get(op->ptr, "extend"); + bool deselect = RNA_boolean_get(op->ptr, "deselect"); + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + + /* deselect all first */ + if (extend == false && deselect == false && toggle == false) { + wm_manipulatormap_deselect_all(mmap, sel); + BLI_assert(*sel == NULL && mmap->mmap_context.tot_selected == 0); + } + + if (highlighted) { + const bool is_selected = (highlighted->state & WM_MANIPULATOR_SELECTED); + bool redraw = false; + + if (toggle) { + /* toggle: deselect if already selected, else select */ + deselect = is_selected; + } + + if (deselect) { + if (is_selected && wm_manipulator_deselect(mmap, highlighted)) { + redraw = true; + } + } + else if (wm_manipulator_select(C, mmap, highlighted)) { + redraw = true; + } + + if (redraw) { + ED_region_tag_redraw(ar); + } + + return OPERATOR_FINISHED; + } + else { + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + return OPERATOR_PASS_THROUGH; +} + +void MANIPULATORGROUP_OT_manipulator_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Manipulator Select"; + ot->description = "Select the currently highlighted manipulator"; + ot->idname = "MANIPULATORGROUP_OT_manipulator_select"; + + /* api callbacks */ + ot->invoke = manipulator_select_invoke; + + ot->flag = OPTYPE_UNDO; + + WM_operator_properties_mouse_select(ot); +} + +typedef struct ManipulatorTweakData { + wmManipulatorMap *mmap; + wmManipulator *active; + + int init_event; /* initial event type */ + int flag; /* tweak flags */ +} ManipulatorTweakData; + +static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel) +{ + ManipulatorTweakData *mtweak = op->customdata; + if (mtweak->active->exit) { + mtweak->active->exit(C, mtweak->active, cancel); + } + wm_manipulatormap_set_active_manipulator(mtweak->mmap, C, NULL, NULL); + MEM_freeN(mtweak); +} + +static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ManipulatorTweakData *mtweak = op->customdata; + wmManipulator *manipulator = mtweak->active; + + if (!manipulator) { + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + if (event->type == mtweak->init_event && event->val == KM_RELEASE) { + manipulator_tweak_finish(C, op, false); + return OPERATOR_FINISHED; + } + + + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case TWEAK_MODAL_CANCEL: + manipulator_tweak_finish(C, op, true); + return OPERATOR_CANCELLED; + case TWEAK_MODAL_CONFIRM: + manipulator_tweak_finish(C, op, false); + return OPERATOR_FINISHED; + case TWEAK_MODAL_PRECISION_ON: + mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE; + break; + case TWEAK_MODAL_PRECISION_OFF: + mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE; + break; + } + } + + /* handle manipulator */ + if (manipulator->handler) { + manipulator->handler(C, event, manipulator, mtweak->flag); + } + + /* Ugly hack to send manipulator events */ + ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE; + + /* always return PASS_THROUGH so modal handlers + * with manipulators attached can update */ + return OPERATOR_PASS_THROUGH; +} + +static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + wmManipulatorMap *mmap = ar->manipulator_map; + wmManipulator *manipulator = mmap->mmap_context.highlighted_manipulator; + + if (!manipulator) { + /* wm_handlers_do_intern shouldn't let this happen */ + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + + /* activate highlighted manipulator */ + wm_manipulatormap_set_active_manipulator(mmap, C, event, manipulator); + + /* XXX temporary workaround for modal manipulator operator + * conflicting with modal operator attached to manipulator */ + if (manipulator->opname) { + wmOperatorType *ot = WM_operatortype_find(manipulator->opname, true); + if (ot->modal) { + return OPERATOR_FINISHED; + } + } + + + ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__); + + mtweak->init_event = event->type; + mtweak->active = mmap->mmap_context.highlighted_manipulator; + mtweak->mmap = mmap; + mtweak->flag = 0; + + op->customdata = mtweak; + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Manipulator Tweak"; + ot->description = "Tweak the active manipulator"; + ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak"; + + /* api callbacks */ + ot->invoke = manipulator_tweak_invoke; + ot->modal = manipulator_tweak_modal; + + ot->flag = OPTYPE_UNDO; +} + +/** \} */ // Manipulator operators + + +static wmKeyMap *manipulatorgroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char *mgroupname) +{ + wmKeyMap *keymap; + char name[KMAP_MAX_NAME]; + + static EnumPropertyItem modal_items[] = { + {TWEAK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, + {TWEAK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + {TWEAK_MODAL_PRECISION_ON, "PRECISION_ON", 0, "Enable Precision", ""}, + {TWEAK_MODAL_PRECISION_OFF, "PRECISION_OFF", 0, "Disable Precision", ""}, + {0, NULL, 0, NULL, NULL} + }; + + + BLI_snprintf(name, sizeof(name), "%s Tweak Modal Map", mgroupname); + keymap = WM_modalkeymap_get(keyconf, name); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) + return NULL; + + keymap = WM_modalkeymap_add(keyconf, name, modal_items); + + + /* items for modal map */ + WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + + WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + + WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + + + WM_modalkeymap_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak"); + + return keymap; +} + +/** + * Common default keymap for manipulator groups + */ +wmKeyMap *WM_manipulatorgroup_keymap_common(const struct wmManipulatorGroupType *mgrouptype, wmKeyConfig *config) +{ + /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */ + wmKeyMap *km = WM_keymap_find(config, mgrouptype->name, mgrouptype->spaceid, mgrouptype->regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0); + manipulatorgroup_tweak_modal_keymap(config, mgrouptype->name); + + return km; +} + +/** + * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection + */ +wmKeyMap *WM_manipulatorgroup_keymap_common_sel(const struct wmManipulatorGroupType *mgrouptype, wmKeyConfig *config) +{ + /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */ + wmKeyMap *km = WM_keymap_find(config, mgrouptype->name, mgrouptype->spaceid, mgrouptype->regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0); + manipulatorgroup_tweak_modal_keymap(config, mgrouptype->name); + + wmKeyMapItem *kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + RNA_boolean_set(kmi->ptr, "deselect", false); + RNA_boolean_set(kmi->ptr, "toggle", false); + kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + RNA_boolean_set(kmi->ptr, "deselect", false); + RNA_boolean_set(kmi->ptr, "toggle", true); + + return km; +} + +/** \} */ /* wmManipulatorGroup */ + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorGroupType + * + * \{ */ + +/** + * Use this for registering manipulators on startup. For runtime, use #WM_manipulatorgrouptype_append_runtime. + */ +wmManipulatorGroupType *WM_manipulatorgrouptype_append( + wmManipulatorMapType *mmaptype, void (*mgrouptype_func)(wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *mgrouptype = MEM_callocN(sizeof(wmManipulatorGroupType), "manipulator-group"); + + mgrouptype_func(mgrouptype); + mgrouptype->spaceid = mmaptype->spaceid; + mgrouptype->regionid = mmaptype->regionid; + BLI_strncpy(mgrouptype->mapidname, mmaptype->idname, MAX_NAME); + /* if not set, use default */ + if (!mgrouptype->keymap_init) { + mgrouptype->keymap_init = WM_manipulatorgroup_keymap_common; + } + + /* add the type for future created areas of the same type */ + BLI_addtail(&mmaptype->manipulator_grouptypes, mgrouptype); + return mgrouptype; +} + +/** + * Use this for registering manipulators on runtime. + */ +wmManipulatorGroupType *WM_manipulatorgrouptype_append_runtime( + const Main *main, wmManipulatorMapType *mmaptype, + void (*mgrouptype_func)(wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *mgrouptype = WM_manipulatorgrouptype_append(mmaptype, mgrouptype_func); + + /* Main is missing on startup when we create new areas. + * So this is only called for manipulators initialized on runtime */ + WM_manipulatorgrouptype_init_runtime(main, mmaptype, mgrouptype); + + return mgrouptype; +} + +void WM_manipulatorgrouptype_init_runtime( + const Main *bmain, wmManipulatorMapType *mmaptype, + wmManipulatorGroupType *mgrouptype) +{ + /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */ + wm_manipulatorgrouptype_keymap_init(mgrouptype, ((wmWindowManager *)bmain->wm.first)->defaultconf); + + /* now create a manipulator for all existing areas */ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + if (mmap->type == mmaptype) { + wmManipulatorGroup *mgroup = wm_manipulatorgroup_new_from_type(mgrouptype); + + /* just add here, drawing will occur on next update */ + BLI_addtail(&mmap->manipulator_groups, mgroup); + wm_manipulatormap_set_highlighted_manipulator(mmap, NULL, NULL, 0); + ED_region_tag_redraw(ar); + } + } + } + } + } +} + +void WM_manipulatorgrouptype_unregister(bContext *C, Main *bmain, wmManipulatorGroupType *mgrouptype) +{ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + wmManipulatorGroup *mgroup, *mgroup_next; + + for (mgroup = mmap->manipulator_groups.first; mgroup; mgroup = mgroup_next) { + mgroup_next = mgroup->next; + if (mgroup->type == mgrouptype) { + wm_manipulatorgroup_free(C, mmap, mgroup); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + + wmManipulatorMapType *mmaptype = WM_manipulatormaptype_find(&(const struct wmManipulatorMapType_Params) { + mgrouptype->mapidname, mgrouptype->spaceid, + mgrouptype->regionid}); + + BLI_remlink(&mmaptype->manipulator_grouptypes, mgrouptype); + mgrouptype->prev = mgrouptype->next = NULL; + + MEM_freeN(mgrouptype); +} + +void wm_manipulatorgrouptype_keymap_init(wmManipulatorGroupType *mgrouptype, wmKeyConfig *keyconf) +{ + mgrouptype->keymap = mgrouptype->keymap_init(mgrouptype, keyconf); +} + +/** \} */ /* wmManipulatorGroupType */ |