diff options
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 94 | ||||
-rw-r--r-- | source/blender/editors/object/object_constraint.c | 324 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 3 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 3 |
4 files changed, 405 insertions, 19 deletions
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 766840909cc..47ff0c9fd3c 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2621,6 +2621,72 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v) ED_object_constraint_active_set(ob_v, con_v); } +static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v) +{ + PointerRNA op_ptr; + uiLayout *row; + bConstraint *con = (bConstraint *)con_v; + + PointerRNA ptr; + Object *ob = ED_object_active_context(C); + + RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr); + uiLayoutSetContextPointer(layout, "constraint", &ptr); + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + + uiLayoutSetUnitsX(layout, 4.0f); + + /* Apply. */ + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), + ICON_CHECKMARK, + "CONSTRAINT_OT_apply"); + + /* Duplicate. */ + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"), + ICON_DUPLICATE, + "CONSTRAINT_OT_copy"); + + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"), + 0, + "CONSTRAINT_OT_copy_to_selected"); + + uiItemS(layout); + + /* Move to first. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "CONSTRAINT_OT_move_to_index", + IFACE_("Move to First"), + ICON_TRIA_UP, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + RNA_int_set(&op_ptr, "index", 0); + if (!con->prev) { + uiLayoutSetEnabled(row, false); + } + + /* Move to last. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "CONSTRAINT_OT_move_to_index", + IFACE_("Move to Last"), + ICON_TRIA_DOWN, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + ListBase *constraint_list = ED_object_constraint_list_from_constraint(ob, con, NULL); + RNA_int_set(&op_ptr, "index", BLI_listbase_count(constraint_list) - 1); + if (!con->next) { + uiLayoutSetEnabled(row, false); + } +} + static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con) { bPoseChannel *pchan = BKE_pose_channel_active(ob); @@ -2652,11 +2718,13 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co UI_block_emboss_set(block, UI_EMBOSS); + uiLayout *row = uiLayoutRow(layout, true); + if (proxy_protected == 0) { - uiItemR(layout, &ptr, "name", 0, "", ICON_NONE); + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); } else { - uiItemL(layout, con->name, ICON_NONE); + uiItemL(row, con->name, ICON_NONE); } /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */ @@ -2697,22 +2765,22 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co UI_block_emboss_set(block, UI_EMBOSS); } else { - /* enabled */ - UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS); - uiItemR(layout, &ptr, "mute", 0, "", 0); - UI_block_emboss_set(block, UI_EMBOSS); + /* Enabled eye icon. */ + uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE); - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + /* Extra operators menu. */ + uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con); /* Close 'button' - emboss calls here disable drawing of 'button' behind X */ - UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete"); - UI_block_emboss_set(block, UI_EMBOSS); - - /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ - uiItemS(layout); + sub = uiLayoutRow(row, false); + uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); + uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT); + uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete"); } + /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ + uiItemS(layout); + /* Set but-locks for protected settings (magic numbers are used here!) */ if (proxy_protected) { UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint")); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 3d0213f1830..e0419e0a4cc 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1483,13 +1483,11 @@ static int constraint_delete_exec(bContext *C, wmOperator *op) /* free the constraint */ if (BKE_constraint_remove_ex(lb, ob, con, true)) { - /* there's no active constraint now, so make sure this is the case */ - BKE_constraints_active_set(&ob->constraints, NULL); /* needed to set the flags on posebones correctly */ ED_object_constraint_update(bmain, ob); /* relations */ - DEG_relations_tag_update(CTX_data_main(C)); + DEG_relations_tag_update(bmain); /* notifiers */ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); @@ -1507,10 +1505,10 @@ static int constraint_delete_exec(bContext *C, wmOperator *op) static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_constraint_invoke_properties(C, op, event, &retval)) { - return constraint_delete_exec(C, op); + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; } - return OPERATOR_CANCELLED; + return constraint_delete_exec(C, op); } void CONSTRAINT_OT_delete(wmOperatorType *ot) @@ -1534,6 +1532,320 @@ void CONSTRAINT_OT_delete(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ +/** \name Apply Constraint Operator + * \{ */ + +static int constraint_apply_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + bPoseChannel *pchan; + ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); + + /* Store name temporarily for report. */ + char name[MAX_NAME]; + strcpy(name, con->name); + const bool is_first_constraint = con != constraints->first; + + /* Copy the constraint. */ + bool success; + if (pchan) { + success = BKE_constraint_apply_and_remove_for_pose( + depsgraph, scene, constraints, ob, con, pchan); + } + else { + success = BKE_constraint_apply_and_remove_for_object(depsgraph, scene, constraints, ob, con); + } + + if (!success) { + /* Couldn't remove due to some invalid data. */ + return OPERATOR_CANCELLED; + } + + /* Update for any children that may get moved. */ + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + + /* Needed to set the flags on posebones correctly. */ + ED_object_constraint_update(bmain, ob); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); + if (pchan) { + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + } + else { + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + } + + if (RNA_boolean_get(op->ptr, "report")) { + if (is_first_constraint) { + BKE_report(op->reports, + RPT_INFO, + "Applied constraint was not first, result may not be as expected"); + } + else { + /* Only add this report if the operator didn't cause another one. The purpose here is + * to alert that something happened, and the previous report will do that anyway. */ + BKE_reportf(op->reports, RPT_INFO, "Applied constraint: %s", name); + } + } + + return OPERATOR_FINISHED; +} + +static int constraint_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; + } + return constraint_apply_exec(C, op); +} + +void CONSTRAINT_OT_apply(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Constraint"; + ot->idname = "CONSTRAINT_OT_apply"; + ot->description = "Apply constraint and remove from the stack"; + + /* callbacks */ + ot->invoke = constraint_apply_invoke; + ot->exec = constraint_apply_exec; + ot->poll = edit_constraint_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); + edit_constraint_report_property(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Copy Constraint Operator + * \{ */ + +static int constraint_copy_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + bPoseChannel *pchan; + ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); + + /* Store name temporarily for report. */ + char name[MAX_NAME]; + strcpy(name, con->name); + + /* Copy the constraint. */ + bConstraint *copy_con; + if (pchan) { + copy_con = BKE_constraint_copy_for_pose(ob, pchan, con); + } + else { + copy_con = BKE_constraint_copy_for_object(ob, con); + } + + if (!copy_con) { + /* Couldn't remove due to some invalid data. */ + return OPERATOR_CANCELLED; + } + /* Move constraint to correct position. */ + const int new_index = BLI_findindex(constraints, con) + 1; + const int current_index = BLI_findindex(constraints, copy_con); + BLI_assert(new_index >= 0); + BLI_assert(current_index >= 0); + BLI_listbase_link_move(constraints, copy_con, new_index - current_index); + + /* Needed to set the flags on posebones correctly. */ + ED_object_constraint_update(bmain, ob); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob); + + if (RNA_boolean_get(op->ptr, "report")) { + BKE_reportf(op->reports, RPT_INFO, "Copied constraint: %s", name); + } + + return OPERATOR_FINISHED; +} + +static int constraint_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; + } + return constraint_copy_exec(C, op); +} + +void CONSTRAINT_OT_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Constraint"; + ot->idname = "CONSTRAINT_OT_copy"; + ot->description = "Duplicate constraint at the same position in the stack"; + + /* callbacks */ + ot->invoke = constraint_copy_invoke; + ot->exec = constraint_copy_exec; + ot->poll = edit_constraint_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); + edit_constraint_report_property(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Copy Constraint To Selected Operator + * \{ */ + +static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *obact = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, obact, 0); + bPoseChannel *pchan; + ED_object_constraint_list_from_constraint(obact, con, &pchan); + + if (pchan) { + /* Don't do anything if bone doesn't exist or doesn't have any constraints. */ + if (pchan->constraints.first == NULL) { + BKE_report(op->reports, RPT_ERROR, "No constraints for copying"); + return OPERATOR_CANCELLED; + } + + Object *prev_ob = NULL; + + /* Copy all constraints from active posebone to all selected posebones. */ + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { + /* If we're not handling the object we're copying from, copy all constraints over. */ + if (pchan == chan) { + continue; + } + + BKE_constraint_copy_for_pose(ob, chan, con); + /* Update flags (need to add here, not just copy). */ + chan->constflag |= pchan->constflag; + + if (prev_ob == ob) { + continue; + } + + BKE_pose_tag_recalc(bmain, ob->pose); + DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY); + prev_ob = ob; + } + CTX_DATA_END; + } + else { + /* Copy all constraints from active object to all selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + /* If we're not handling the object we're copying from, copy all constraints over. */ + if (obact == ob) { + continue; + } + + BKE_constraint_copy_for_object(ob, con); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM); + } + CTX_DATA_END; + } + + /* Force depsgraph to get recalculated since new relationships added. */ + DEG_relations_tag_update(bmain); + + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL); + + return OPERATOR_FINISHED; +} + +static int constraint_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return retval; + } + return constraint_copy_to_selected_exec(C, op); +} + +static bool constraint_copy_to_selected_poll(bContext *C) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); + Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); + bConstraint *con = ptr.data; + bPoseChannel *pchan; + ED_object_constraint_list_from_constraint(obact, con, &pchan); + + if (pchan) { + bool found = false; + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, UNUSED(ob)) { + if (pchan != chan) { + /** NOTE: Can not return here, because CTX_DATA_BEGIN_WITH_ID allocated + * a list that needs to be freed by CTX_DATA_END. */ + found = true; + break; + } + } + CTX_DATA_END; + if (found) { + return true; + } + + CTX_wm_operator_poll_msg_set(C, "No other bones are selected"); + return false; + } + + if (!obact) { + CTX_wm_operator_poll_msg_set(C, "No selected object to copy from"); + return false; + } + + bool found = false; + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob != obact) { + /** NOTE: Can not return here, because CTX_DATA_BEGIN allocated + * a list that needs to be freed by CTX_DATA_END. */ + found = true; + break; + } + } + CTX_DATA_END; + if (found) { + return true; + } + + CTX_wm_operator_poll_msg_set(C, "No other objects are selected"); + return false; +} + +void CONSTRAINT_OT_copy_to_selected(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Constraint To Selected"; + ot->idname = "CONSTRAINT_OT_copy_to_selected"; + ot->description = "Copy constraint to other selected objects/bones"; + + /* api callbacks */ + ot->exec = constraint_copy_to_selected_exec; + ot->invoke = constraint_copy_to_selected_invoke; + ot->poll = constraint_copy_to_selected_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ /** \name Move Down Constraint Operator * \{ */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 6299fdcc7f7..10e016738d0 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -226,6 +226,9 @@ void POSE_OT_ik_add(struct wmOperatorType *ot); void POSE_OT_ik_clear(struct wmOperatorType *ot); void CONSTRAINT_OT_delete(struct wmOperatorType *ot); +void CONSTRAINT_OT_apply(struct wmOperatorType *ot); +void CONSTRAINT_OT_copy(struct wmOperatorType *ot); +void CONSTRAINT_OT_copy_to_selected(struct wmOperatorType *ot); void CONSTRAINT_OT_move_up(struct wmOperatorType *ot); void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index a438c760d3b..c1928cf7f8a 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -183,6 +183,9 @@ void ED_operatortypes_object(void) WM_operatortype_append(POSE_OT_ik_add); WM_operatortype_append(POSE_OT_ik_clear); WM_operatortype_append(CONSTRAINT_OT_delete); + WM_operatortype_append(CONSTRAINT_OT_apply); + WM_operatortype_append(CONSTRAINT_OT_copy); + WM_operatortype_append(CONSTRAINT_OT_copy_to_selected); WM_operatortype_append(CONSTRAINT_OT_move_up); WM_operatortype_append(CONSTRAINT_OT_move_down); WM_operatortype_append(CONSTRAINT_OT_move_to_index); |