diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/editors/armature/armature_select.c | 204 | ||||
-rw-r--r-- | source/blender/editors/armature/pose_select.c | 186 | ||||
-rw-r--r-- | source/blender/editors/include/ED_armature.h | 14 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_ops.c | 1 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_select.c | 259 |
6 files changed, 477 insertions, 188 deletions
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 226253cc063..9b598872d8b 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -961,131 +961,145 @@ bool ED_armature_edit_deselect_all_visible_multi(bContext *C) /** \name Select Cursor Pick API * \{ */ -/* context: editmode armature in view3d */ -bool ED_armature_edit_select_pick( - bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +bool ED_armature_edit_select_pick_bone(bContext *C, + Base *basact, + EditBone *ebone, + const int selmask, + const bool extend, + const bool deselect, + const bool toggle) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewContext vc; - EditBone *nearBone = NULL; - int selmask; - Base *basact = NULL; + if (!ebone) { + return false; + } - ED_view3d_viewcontext_init(C, &vc, depsgraph); - vc.mval[0] = mval[0]; - vc.mval[1] = mval[1]; + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); - nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask); - if (nearBone) { - ED_view3d_viewcontext_init_object(&vc, basact->object); - bArmature *arm = vc.obedit->data; + BLI_assert(BKE_object_is_in_editmode(basact->object)); + bArmature *arm = basact->object->data; - if (!EBONE_SELECTABLE(arm, nearBone)) { - return false; - } + if (!EBONE_SELECTABLE(arm, ebone)) { + return false; + } - if (!extend && !deselect && !toggle) { - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( - vc.view_layer, vc.v3d, &bases_len); - ED_armature_edit_deselect_all_multi_ex(bases, bases_len); - MEM_freeN(bases); - } + if (!extend && !deselect && !toggle) { + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( + view_layer, v3d, &bases_len); + ED_armature_edit_deselect_all_multi_ex(bases, bases_len); + MEM_freeN(bases); + } - /* by definition the non-root connected bones have no root point drawn, - * so a root selection needs to be delivered to the parent tip */ + /* By definition the non-root connected bones have no root point drawn, + * so a root selection needs to be delivered to the parent tip. */ - if (selmask & BONE_SELECTED) { - if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) { - /* click in a chain */ - if (extend) { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; - } - else if (deselect) { - /* deselect this bone */ - nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* only deselect parent tip if it is not selected */ - if (!(nearBone->parent->flag & BONE_SELECTED)) { - nearBone->parent->flag &= ~BONE_TIPSEL; - } + if (selmask & BONE_SELECTED) { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + /* Bone is in a chain. */ + if (extend) { + /* Select this bone. */ + ebone->flag |= BONE_TIPSEL; + ebone->parent->flag |= BONE_TIPSEL; + } + else if (deselect) { + /* Deselect this bone. */ + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* Only deselect parent tip if it is not selected. */ + if (!(ebone->parent->flag & BONE_SELECTED)) { + ebone->parent->flag &= ~BONE_TIPSEL; } - else if (toggle) { - /* hold shift inverts this bone's selection */ - if (nearBone->flag & BONE_SELECTED) { - /* deselect this bone */ - nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* only deselect parent tip if it is not selected */ - if (!(nearBone->parent->flag & BONE_SELECTED)) { - nearBone->parent->flag &= ~BONE_TIPSEL; - } - } - else { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; + } + else if (toggle) { + /* Toggle inverts this bone's selection. */ + if (ebone->flag & BONE_SELECTED) { + /* Deselect this bone. */ + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* Only deselect parent tip if it is not selected. */ + if (!(ebone->parent->flag & BONE_SELECTED)) { + ebone->parent->flag &= ~BONE_TIPSEL; } } else { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; + /* Select this bone. */ + ebone->flag |= BONE_TIPSEL; + ebone->parent->flag |= BONE_TIPSEL; } } else { - if (extend) { - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - else if (deselect) { - nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); - } - else if (toggle) { - /* hold shift inverts this bone's selection */ - if (nearBone->flag & BONE_SELECTED) { - nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); - } - else { - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - } - else { - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } + /* Select this bone. */ + ebone->flag |= BONE_TIPSEL; + ebone->parent->flag |= BONE_TIPSEL; } } else { if (extend) { - nearBone->flag |= selmask; + ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); } else if (deselect) { - nearBone->flag &= ~selmask; + ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); } - else if (toggle && (nearBone->flag & selmask)) { - nearBone->flag &= ~selmask; + else if (toggle) { + /* Toggle inverts this bone's selection. */ + if (ebone->flag & BONE_SELECTED) { + ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + } + else { + ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } } else { - nearBone->flag |= selmask; + ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); } } - - ED_armature_edit_sync_selection(arm->edbo); - - /* then now check for active status */ - if (ED_armature_ebone_selectflag_get(nearBone)) { - arm->act_edbone = nearBone; + } + else { + if (extend) { + ebone->flag |= selmask; } - - if (vc.view_layer->basact != basact) { - ED_object_base_activate(C, basact); + else if (deselect) { + ebone->flag &= ~selmask; + } + else if (toggle && (ebone->flag & selmask)) { + ebone->flag &= ~selmask; } + else { + ebone->flag |= selmask; + } + } - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - return true; + ED_armature_edit_sync_selection(arm->edbo); + + /* Then now check for active status. */ + if (ED_armature_ebone_selectflag_get(ebone)) { + arm->act_edbone = ebone; + } + + if (view_layer->basact != basact) { + ED_object_base_activate(C, basact); } - return false; + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + return true; +} + +/* context: editmode armature in view3d */ +bool ED_armature_edit_select_pick( + bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewContext vc; + EditBone *nearBone = NULL; + int selmask; + Base *basact = NULL; + + ED_view3d_viewcontext_init(C, &vc, depsgraph); + vc.mval[0] = mval[0]; + vc.mval[1] = mval[1]; + + nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask); + return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, extend, deselect, toggle); } /** \} */ diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index a3f97000509..0b0f1925746 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -138,6 +138,106 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) } } +void ED_armature_pose_select_pick_bone(ViewLayer *view_layer, + View3D *v3d, + Object *ob, + Bone *bone, + const bool extend, + const bool deselect, + const bool toggle) +{ + if (!ob || !ob->pose) { + return; + } + + Object *ob_act = OBACT(view_layer); + BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL); + + /* If the bone cannot be affected, don't do anything. */ + if (bone == NULL || (bone->flag & BONE_UNSELECTABLE)) { + return; + } + bArmature *arm = ob->data; + + /* Since we do unified select, we don't shift+select a bone if the + * armature object was not active yet. + * Note, special exception for armature mode so we can do multi-select + * we could check for multi-select explicitly but think its fine to + * always give predictable behavior in weight paint mode - campbell */ + if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) { + /* When we are entering into posemode via toggle-select, + * from another active object - always select the bone. */ + if (!extend && !deselect && toggle) { + /* Re-select the bone again later in this function. */ + bone->flag &= ~BONE_SELECTED; + } + } + + if (!extend && !deselect && !toggle) { + { + /* Don't use 'BKE_object_pose_base_array_get_unique' + * because we may be selecting from object mode. */ + FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) { + Object *ob_iter = base_iter->object; + if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) { + if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) { + ED_pose_bone_select_tag_update(ob_iter); + } + } + } + FOREACH_VISIBLE_BASE_END; + } + bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = bone; + } + else { + if (extend) { + bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = bone; + } + else if (deselect) { + bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else if (toggle) { + if (bone->flag & BONE_SELECTED) { + /* If not active, we make it active. */ + if (bone != arm->act_bone) { + arm->act_bone = bone; + } + else { + bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + else { + bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = bone; + } + } + } + + if (ob_act) { + /* In weightpaint we select the associated vertex group too. */ + if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) { + if (bone == arm->act_bone) { + ED_vgroup_select_by_name(ob_act, bone->name); + DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY); + } + } + /* If there are some dependencies for visualizing armature state + * (e.g. Mask Modifier in 'Armature' mode), force update. + */ + else if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* NOTE: ob not ob_act here is intentional - it's the source of the + * bones being selected [T37247] + */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + + /* Tag armature for copy-on-write update (since act_bone is in armature not object). */ + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + } +} + /** * Called for mode-less pose selection. * assumes the active object is still on old situation. @@ -159,96 +259,12 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, return 0; } - Object *ob_act = OBACT(view_layer); - BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL); - /* Callers happen to already get the active base */ Base *base_dummy = NULL; nearBone = ED_armature_pick_bone_from_selectbuffer( &base, 1, buffer, hits, 1, do_nearest, &base_dummy); - /* if the bone cannot be affected, don't do anything */ - if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { - bArmature *arm = ob->data; - - /* since we do unified select, we don't shift+select a bone if the - * armature object was not active yet. - * note, special exception for armature mode so we can do multi-select - * we could check for multi-select explicitly but think its fine to - * always give predictable behavior in weight paint mode - campbell */ - if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) { - /* when we are entering into posemode via toggle-select, - * from another active object - always select the bone. */ - if (!extend && !deselect && toggle) { - /* re-select below */ - nearBone->flag &= ~BONE_SELECTED; - } - } - - if (!extend && !deselect && !toggle) { - { - /* Don't use 'BKE_object_pose_base_array_get_unique' - * because we may be selecting from object mode. */ - FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) { - Object *ob_iter = base_iter->object; - if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) { - if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) { - ED_pose_bone_select_tag_update(ob_iter); - } - } - } - FOREACH_VISIBLE_BASE_END; - } - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - else { - if (extend) { - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - else if (deselect) { - nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else if (toggle) { - if (nearBone->flag & BONE_SELECTED) { - /* if not active, we make it active */ - if (nearBone != arm->act_bone) { - arm->act_bone = nearBone; - } - else { - nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - else { - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - } - } - - if (ob_act) { - /* in weightpaint we select the associated vertex group too */ - if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) { - if (nearBone == arm->act_bone) { - ED_vgroup_select_by_name(ob_act, nearBone->name); - DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY); - } - } - /* if there are some dependencies for visualizing armature state - * (e.g. Mask Modifier in 'Armature' mode), force update - */ - else if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* NOTE: ob not ob_act here is intentional - it's the source of the - * bones being selected [T37247] - */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - - /* tag armature for copy-on-write update (since act_bone is in armature not object) */ - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - } - } + ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, extend, deselect, toggle); return nearBone != NULL; } diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 0c4576096fb..85563b76f38 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -125,6 +125,13 @@ bool ED_armature_edit_deselect_all_visible(struct Object *obedit); bool ED_armature_edit_deselect_all_multi_ex(struct Base **bases, uint bases_len); bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint bases_len); bool ED_armature_edit_deselect_all_visible_multi(struct bContext *C); +bool ED_armature_edit_select_pick_bone(struct bContext *C, + struct Base *basact, + struct EditBone *ebone, + int selmask, + bool extend, + bool deselect, + bool toggle); bool ED_armature_edit_select_pick( struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); bool ED_armature_edit_select_op_from_tagged(struct bArmature *arm, const int sel_op); @@ -201,6 +208,13 @@ void ED_pose_recalculate_paths(struct bContext *C, ePosePathCalcRange range); /* pose_select.c */ +void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer, + struct View3D *v3d, + struct Object *ob, + struct Bone *bone, + bool extend, + bool deselect, + bool toggle); bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer, struct View3D *v3d, struct Base *base, diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index e766ae96c2f..6f07cb8b44d 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -146,6 +146,7 @@ void VIEW3D_OT_select_circle(struct wmOperatorType *ot); void VIEW3D_OT_select_box(struct wmOperatorType *ot); void VIEW3D_OT_select_lasso(struct wmOperatorType *ot); void VIEW3D_OT_select_menu(struct wmOperatorType *ot); +void VIEW3D_OT_bone_select_menu(struct wmOperatorType *ot); /* view3d_view.c */ void VIEW3D_OT_smoothview(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 344168e895b..56dedbbdbb2 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -187,6 +187,7 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_cursor3d); WM_operatortype_append(VIEW3D_OT_select_lasso); WM_operatortype_append(VIEW3D_OT_select_menu); + WM_operatortype_append(VIEW3D_OT_bone_select_menu); WM_operatortype_append(VIEW3D_OT_camera_to_view); WM_operatortype_append(VIEW3D_OT_camera_to_view_selected); WM_operatortype_append(VIEW3D_OT_object_as_camera); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 3166b818d3c..757ed13ac28 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -96,6 +96,7 @@ #include "ED_select_utils.h" #include "UI_interface.h" +#include "UI_resources.h" #include "GPU_matrix.h" @@ -1432,6 +1433,8 @@ void VIEW3D_OT_select_lasso(wmOperatorType *ot) typedef struct SelMenuItemF { char idname[MAX_ID_NAME - 2]; int icon; + Base *base_ptr; + void *item_ptr; } SelMenuItemF; #define SEL_MENU_SIZE 22 @@ -1580,7 +1583,7 @@ static Base *object_mouse_select_menu(bContext *C, { short baseCount = 0; bool ok; - LinkNode *linklist = NULL; + LinkNodePair linklist = {NULL, NULL}; /* handle base->object->select_id */ CTX_DATA_BEGIN (C, Base *, base, selectable_bases) { @@ -1608,7 +1611,7 @@ static Base *object_mouse_select_menu(bContext *C, if (ok) { baseCount++; - BLI_linklist_prepend(&linklist, base); + BLI_linklist_append(&linklist, base); if (baseCount == SEL_MENU_SIZE) { break; @@ -1621,8 +1624,8 @@ static Base *object_mouse_select_menu(bContext *C, return NULL; } if (baseCount == 1) { - Base *base = (Base *)linklist->link; - BLI_linklist_free(linklist, NULL); + Base *base = (Base *)linklist.list->link; + BLI_linklist_free(linklist.list, NULL); return base; } @@ -1632,7 +1635,7 @@ static Base *object_mouse_select_menu(bContext *C, memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data)); - for (node = linklist, i = 0; node; node = node->next, i++) { + for (node = linklist.list, i = 0; node; node = node->next, i++) { Base *base = node->link; Object *ob = base->object; const char *name = ob->id.name + 2; @@ -1651,10 +1654,231 @@ static Base *object_mouse_select_menu(bContext *C, WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); WM_operator_properties_free(&ptr); - BLI_linklist_free(linklist, NULL); + BLI_linklist_free(linklist.list, NULL); return NULL; } +static int bone_select_menu_exec(bContext *C, wmOperator *op) +{ + const int name_index = RNA_enum_get(op->ptr, "name"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool deselect = RNA_boolean_get(op->ptr, "deselect"); + const bool toggle = RNA_boolean_get(op->ptr, "toggle"); + + View3D *v3d = CTX_wm_view3d(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + const Base *oldbasact = BASACT(view_layer); + + Base *basact = object_mouse_select_menu_data[name_index].base_ptr; + + if (basact == NULL) { + return OPERATOR_CANCELLED; + } + + BLI_assert(BASE_SELECTABLE(v3d, basact)); + + if (basact->object->mode == OB_MODE_EDIT) { + EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr; + ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, extend, deselect, toggle); + } + else { + bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr; + ED_armature_pose_select_pick_bone( + view_layer, v3d, basact->object, pchan->bone, extend, deselect, toggle); + } + + /* Weak but ensures we activate the menu again before using the enum. */ + memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data)); + + /* We make the armature selected: + * Not-selected active object in posemode won't work well for tools. */ + ED_object_base_select(basact, BA_SELECT); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object); + + /* In weight-paint, we use selected bone to select vertex-group, + * so don't switch to new active object. */ + if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) { + /* Prevent activating. + * Selection causes this to be considered the 'active' pose in weight-paint mode. + * Eventually this limitation may be removed. + * For now, de-select all other pose objects deforming this mesh. */ + ED_armature_pose_select_in_wpaint_mode(view_layer, basact); + + basact = NULL; + } + + /* Undo? */ + Scene *scene = CTX_data_scene(C); + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_bone_select_menu(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select Menu"; + ot->description = "Menu bone selection"; + ot->idname = "VIEW3D_OT_bone_select_menu"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = bone_select_menu_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* keyingset to use (dynamic enum) */ + prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Bone Name", ""); + RNA_def_enum_funcs(prop, object_select_menu_enum_itemf); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; + + RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); + RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); +} +static bool bone_mouse_select_menu(bContext *C, + const uint *buffer, + const int hits, + const bool is_editmode, + const bool extend, + const bool deselect, + const bool toggle) +{ + BLI_assert(buffer); + + short baseCount = 0; + LinkNodePair base_list = {NULL, NULL}; + LinkNodePair bone_list = {NULL, NULL}; + GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu"); + + /* Select logic taken from ed_armature_pick_bone_from_selectbuffer_impl in armature_select.c */ + for (int a = 0; a < hits; a++) { + void *bone_ptr = NULL; + Base *bone_base = NULL; + uint hitresult = buffer[3 + (a * 4)]; + + if (!(hitresult & BONESEL_ANY)) { + /* To avoid including objects in selection. */ + continue; + } + + hitresult &= ~BONESEL_ANY; + const uint hit_object = hitresult & 0xFFFF; + + /* Find the hit bone base (armature object). */ + CTX_DATA_BEGIN (C, Base *, base, selectable_bases) { + if (base->object->runtime.select_id == hit_object) { + bone_base = base; + break; + } + } + CTX_DATA_END; + + if (!bone_base) { + continue; + } + + /* Determine what the current bone is */ + if (is_editmode) { + EditBone *ebone; + const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16; + bArmature *arm = bone_base->object->data; + ebone = BLI_findlink(arm->edbo, hit_bone); + if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) { + bone_ptr = ebone; + } + } + else { + bPoseChannel *pchan; + const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16; + pchan = BLI_findlink(&bone_base->object->pose->chanbase, hit_bone); + if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) { + bone_ptr = pchan; + } + } + + if (!bone_ptr) { + continue; + } + /* We can hit a bone multiple times, so make sure we are not adding an already included bone + * to the list.*/ + const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr); + + if (!is_duplicate_bone) { + baseCount++; + BLI_linklist_append(&base_list, bone_base); + BLI_linklist_append(&bone_list, bone_ptr); + BLI_gset_insert(added_bones, bone_ptr); + + if (baseCount == SEL_MENU_SIZE) { + break; + } + } + } + + BLI_gset_free(added_bones, NULL); + + if (baseCount == 0) { + return false; + } + if (baseCount == 1) { + BLI_linklist_free(base_list.list, NULL); + BLI_linklist_free(bone_list.list, NULL); + return false; + } + + /* UI, full in static array values that we later use in an enum function */ + LinkNode *bone_node, *base_node; + int i; + + memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data)); + + for (base_node = base_list.list, bone_node = bone_list.list, i = 0; bone_node; + base_node = base_node->next, bone_node = bone_node->next, i++) { + char *name; + + object_mouse_select_menu_data[i].base_ptr = base_node->link; + + if (is_editmode) { + EditBone *ebone = bone_node->link; + object_mouse_select_menu_data[i].item_ptr = ebone; + name = ebone->name; + } + else { + bPoseChannel *pchan = bone_node->link; + object_mouse_select_menu_data[i].item_ptr = pchan; + name = pchan->name; + } + + BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2); + object_mouse_select_menu_data[i].icon = ICON_BONE_DATA; + } + + wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_bone_select_menu", false); + PointerRNA ptr; + + WM_operator_properties_create_ptr(&ptr, ot); + RNA_boolean_set(&ptr, "extend", extend); + RNA_boolean_set(&ptr, "deselect", deselect); + RNA_boolean_set(&ptr, "toggle", toggle); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + WM_operator_properties_free(&ptr); + + BLI_linklist_free(base_list.list, NULL); + BLI_linklist_free(bone_list.list, NULL); + return true; +} + static bool selectbuffer_has_bones(const uint *buffer, const uint hits) { for (uint i = 0; i < hits; i++) { @@ -2113,7 +2337,13 @@ static bool ed_object_select_pick(bContext *C, /* note; shift+alt goes to group-flush-selecting */ if (enumerate) { - basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle); + if (has_bones && + bone_mouse_select_menu(C, buffer, hits, false, extend, deselect, toggle)) { + basact = NULL; + } + else { + basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle); + } } else { basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest); @@ -2410,7 +2640,20 @@ static int view3d_select_exec(bContext *C, wmOperator *op) } } else if (obedit->type == OB_ARMATURE) { - retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle); + if (enumerate) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc, depsgraph); + + uint buffer[MAXPICKBUF]; + const int hits = mixed_bones_object_selectbuffer( + &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true); + retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle); + } + if (!retval) { + retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle); + } + if (!retval && deselect_all) { retval = ED_armature_edit_deselect_all_visible_multi(C); } |