Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2020-12-28 22:41:46 +01:00
commit 193806f590
15 changed files with 430 additions and 57 deletions

View File

@ -943,10 +943,7 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
GHOST_TABLET_DATA_NONE;
/* Ensure button click occurs at its intended position. */
DWORD msgPos = ::GetMessagePos();
GHOST_TInt32 x_screen = GET_X_LPARAM(msgPos), y_screen = GET_Y_LPARAM(msgPos);
system->pushEvent(new GHOST_EventCursor(
system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen, y_screen, td));
processCursorEvent(window);
window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased);
return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td);
@ -1106,22 +1103,19 @@ void GHOST_SystemWin32::processPointerEvent(
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
{
if (window->m_tabletInRange) {
return NULL;
}
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
GHOST_TInt32 x_screen, y_screen;
system->getCursorPosition(x_screen, y_screen);
DWORD msgPos = ::GetMessagePos();
GHOST_TInt32 x_screen = GET_X_LPARAM(msgPos);
GHOST_TInt32 y_screen = GET_Y_LPARAM(msgPos);
GHOST_TInt32 x_accum = 0, y_accum = 0;
if (window->getCursorGrabModeIsWarp()) {
if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) {
GHOST_TInt32 x_new = x_screen;
GHOST_TInt32 y_new = y_screen;
GHOST_TInt32 x_accum, y_accum;
GHOST_Rect bounds;
/* fallback to window bounds */
/* Fallback to window bounds. */
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
window->getClientBounds(bounds);
}
@ -1132,29 +1126,19 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
window->getCursorGrabAccum(x_accum, y_accum);
if (x_new != x_screen || y_new != y_screen) {
/* when wrapping we don't need to add an event because the
* setCursorPosition call will cause a new event after */
system->setCursorPosition(x_new, y_new); /* wrap */
window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
}
else {
return new GHOST_EventCursor(system->getMilliSeconds(),
GHOST_kEventCursorMove,
window,
x_screen + x_accum,
y_screen + y_accum,
GHOST_TABLET_DATA_NONE);
}
}
else {
return new GHOST_EventCursor(system->getMilliSeconds(),
GHOST_kEventCursorMove,
window,
x_screen,
y_screen,
GHOST_TABLET_DATA_NONE);
}
return NULL;
GHOST_TabletData td = window->m_tabletInRange ? window->getLastTabletData() :
GHOST_TABLET_DATA_NONE;
return new GHOST_EventCursor(system->getMilliSeconds(),
GHOST_kEventCursorMove,
window,
x_screen + x_accum,
y_screen + y_accum,
td);
}
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
@ -1641,7 +1625,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
window->setWintabOverlap(true);
}
event = processCursorEvent(window);
if (!window->m_tabletInRange) {
event = processCursorEvent(window);
}
break;
case WM_MOUSEWHEEL: {
/* The WM_MOUSEWHEEL message is sent to the focus window

View File

@ -75,6 +75,7 @@ bool BKE_object_modifier_gpencil_use_time(struct Object *ob, struct GpencilModif
bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *md);
bool BKE_object_supports_modifiers(const struct Object *ob);
bool BKE_object_support_modifier_type_check(const struct Object *ob, int modifier_type);
/* Active modifier. */

View File

@ -1315,6 +1315,15 @@ ModifierData *BKE_object_active_modifier(const Object *ob)
return NULL;
}
/**
* \return True if the object's type supports regular modifiers (not grease pencil modifiers).
*/
bool BKE_object_supports_modifiers(const Object *ob)
{
return (
ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE, OB_POINTCLOUD, OB_VOLUME));
}
bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
{
const ModifierTypeInfo *mti = BKE_modifier_get_info(modifier_type);
@ -1377,6 +1386,7 @@ bool BKE_object_copy_modifier(struct Object *ob_dst, const struct Object *ob_src
BKE_modifier_copydata(md, nmd);
BLI_addtail(&ob_dst->modifiers, nmd);
BKE_modifier_unique_name(&ob_dst->modifiers, nmd);
BKE_object_modifier_set_active(ob_dst, nmd);
return true;
}

View File

@ -75,6 +75,7 @@
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_scene.h"
@ -3948,6 +3949,7 @@ static ModifierData *object_add_or_copy_particle_system(
psmd = (ParticleSystemModifierData *)md;
psmd->psys = psys;
BLI_addtail(&ob->modifiers, md);
BKE_object_modifier_set_active(ob, md);
psys->totpart = 0;
psys->flag = PSYS_CURRENT;

View File

@ -835,3 +835,99 @@ void OBJECT_OT_gpencil_modifier_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
gpencil_edit_modifier_properties(ot);
}
/************************ Copy Modifier to Selected Operator *********************/
static int gpencil_modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
{
Object *obact = ED_object_active_context(C);
GpencilModifierData *md = gpencil_edit_modifier_property_get(op, obact, 0);
if (!md) {
return OPERATOR_CANCELLED;
}
if (obact->type != OB_GPENCIL) {
BKE_reportf(op->reports,
RPT_ERROR,
"Source object '%s' is not a grease pencil object",
obact->id.name + 2);
return OPERATOR_CANCELLED;
}
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob == obact) {
continue;
}
if (ob->type != OB_GPENCIL) {
BKE_reportf(op->reports,
RPT_WARNING,
"Destination object '%s' is not a grease pencil object",
ob->id.name + 2);
continue;
}
/* This always returns true right now. */
BKE_object_copy_gpencil_modifier(ob, md);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
}
CTX_DATA_END;
return OPERATOR_FINISHED;
}
static int gpencil_modifier_copy_to_selected_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
int retval;
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
return gpencil_modifier_copy_to_selected_exec(C, op);
}
return retval;
}
static bool gpencil_modifier_copy_to_selected_poll(bContext *C)
{
Object *obact = ED_object_active_context(C);
/* This could have a performance impact in the worst case, where there are many objects selected
* and none of them pass the check. But that should be uncommon, and this operator is only
* exposed in a drop-down menu anyway. */
bool found_supported_objects = false;
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob == obact) {
continue;
}
if (ob->type == OB_GPENCIL) {
found_supported_objects = true;
break;
}
}
CTX_DATA_END;
if (!found_supported_objects) {
CTX_wm_operator_poll_msg_set(C, "No supported objects were selected");
return false;
}
return true;
}
void OBJECT_OT_gpencil_modifier_copy_to_selected(wmOperatorType *ot)
{
ot->name = "Copy Modifier to Selected";
ot->description = "Copy the modifier from the active object to all selected objects";
ot->idname = "OBJECT_OT_gpencil_modifier_copy_to_selected";
ot->invoke = gpencil_modifier_copy_to_selected_invoke;
ot->exec = gpencil_modifier_copy_to_selected_exec;
ot->poll = gpencil_modifier_copy_to_selected_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
gpencil_edit_modifier_properties(ot);
}

View File

@ -175,6 +175,7 @@ void OBJECT_OT_modifier_apply(struct wmOperatorType *ot);
void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot);
void OBJECT_OT_modifier_convert(struct wmOperatorType *ot);
void OBJECT_OT_modifier_copy(struct wmOperatorType *ot);
void OBJECT_OT_modifier_copy_to_selected(struct wmOperatorType *ot);
void OBJECT_OT_modifier_set_active(struct wmOperatorType *ot);
void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot);
void OBJECT_OT_multires_reshape(struct wmOperatorType *ot);
@ -203,6 +204,7 @@ void OBJECT_OT_gpencil_modifier_move_down(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_move_to_index(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_copy_to_selected(struct wmOperatorType *ot);
/* object_shader_fx.c */
void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot);

View File

@ -30,6 +30,8 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_curve_types.h"
#include "DNA_dynamicpaint_types.h"
#include "DNA_fluid_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
@ -1681,6 +1683,229 @@ void OBJECT_OT_modifier_set_active(wmOperatorType *ot)
edit_modifier_properties(ot);
}
/** \} */
/** \name Copy Modifier To Selected Operator
* \{ */
/* If the modifier uses particles, copy particle system to destination object
* or reuse existing if it has the same ParticleSettings */
static void copy_or_reuse_particle_system(bContext *C, Object *ob, ModifierData *md)
{
ParticleSystem *psys_on_modifier = NULL;
if (md->type == eModifierType_DynamicPaint) {
DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
if (pmd->brush && pmd->brush->psys) {
psys_on_modifier = pmd->brush->psys;
}
}
else if (md->type == eModifierType_Fluid) {
FluidModifierData *fmd = (FluidModifierData *)md;
if (fmd->type == MOD_FLUID_TYPE_FLOW) {
if (fmd->flow && fmd->flow->psys) {
psys_on_modifier = fmd->flow->psys;
}
}
}
if (!psys_on_modifier) {
return;
}
ParticleSystem *psys_on_new_modifier = NULL;
/* Check if a particle system with the same particle settings
* already exists on the destination object. */
LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) {
if (psys_on_modifier->part == psys->part) {
psys_on_new_modifier = psys;
break;
}
}
/* If it does not exist, copy the particle system to the destination object. */
if (!psys_on_new_modifier) {
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
object_copy_particle_system(bmain, scene, ob, psys_on_modifier);
LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) {
if (psys_on_modifier->part == psys->part) {
psys_on_new_modifier = psys;
}
}
}
/* Update the modifier to point to the new/existing particle system. */
LISTBASE_FOREACH (ModifierData *, new_md, &ob->modifiers) {
if (new_md->type == eModifierType_DynamicPaint) {
DynamicPaintModifierData *new_pmd = (DynamicPaintModifierData *)new_md;
if (psys_on_modifier == new_pmd->brush->psys) {
new_pmd->brush->psys = psys_on_new_modifier;
}
}
else if (new_md->type == eModifierType_Fluid) {
FluidModifierData *new_fmd = (FluidModifierData *)new_md;
if (psys_on_modifier == new_fmd->flow->psys) {
new_fmd->flow->psys = psys_on_new_modifier;
}
}
}
}
static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *obact = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, obact, 0);
if (!md) {
return OPERATOR_CANCELLED;
}
int num_copied = 0;
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob == obact) {
continue;
}
/* Checked in #BKE_object_copy_modifier, but check here too so we can give a better message. */
if (!BKE_object_support_modifier_type_check(ob, md->type)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Object '%s' does not support %s modifiers",
ob->id.name + 2,
mti->name);
continue;
}
if (mti->flags & eModifierTypeFlag_Single) {
if (BKE_modifiers_findby_type(ob, md->type)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Modifier can only be added once to object '%s'",
ob->id.name + 2);
continue;
}
}
if (md->type == eModifierType_ParticleSystem) {
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
object_copy_particle_system(bmain, scene, ob, psmd->psys);
}
else {
if (!BKE_object_copy_modifier(ob, obact, md)) {
BKE_reportf(op->reports,
RPT_ERROR,
"Copying modifier '%s' to object '%s' failed",
md->name,
ob->id.name + 2);
}
}
if (ELEM(md->type, eModifierType_DynamicPaint, eModifierType_Fluid)) {
copy_or_reuse_particle_system(C, ob, md);
}
num_copied++;
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
}
CTX_DATA_END;
if (num_copied > 0) {
DEG_relations_tag_update(bmain);
}
else {
BKE_reportf(op->reports, RPT_ERROR, "Modifier '%s' was not copied to any objects", md->name);
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
static int modifier_copy_to_selected_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
if (edit_modifier_invoke_properties(C, op)) {
return modifier_copy_to_selected_exec(C, op);
}
/* Work around multiple operators using the same shortcut. */
return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
}
static bool modifier_copy_to_selected_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
ModifierData *md = ptr.data;
/* This just mirrors the check in #BKE_object_copy_modifier,
* but there is no reasoning for it there. */
if (md && ELEM(md->type, eModifierType_Hook, eModifierType_Collision)) {
CTX_wm_operator_poll_msg_set(C, "Not supported for \"Collision\" or \"Hook\" modifiers");
return false;
}
if (!obact) {
CTX_wm_operator_poll_msg_set(C, "No selected object is active");
return false;
}
if (!BKE_object_supports_modifiers(obact)) {
CTX_wm_operator_poll_msg_set(C, "Object type of source object is not supported");
return false;
}
/* This could have a performance impact in the worst case, where there are many objects selected
* and none of them pass either of the checks. But that should be uncommon, and this operator is
* only exposed in a drop-down menu anyway. */
bool found_supported_objects = false;
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob == obact) {
continue;
}
if (!md && BKE_object_supports_modifiers(ob)) {
/* Skip type check if modifier could not be found ("modifier" context variable not set). */
found_supported_objects = true;
break;
}
else if (BKE_object_support_modifier_type_check(ob, md->type)) {
found_supported_objects = true;
break;
}
}
CTX_DATA_END;
if (!found_supported_objects) {
CTX_wm_operator_poll_msg_set(C, "No supported objects were selected");
return false;
}
return true;
}
void OBJECT_OT_modifier_copy_to_selected(wmOperatorType *ot)
{
ot->name = "Copy Modifier to Selected";
ot->description = "Copy the modifier from the active object to all selected objects";
ot->idname = "OBJECT_OT_modifier_copy_to_selected";
ot->invoke = modifier_copy_to_selected_invoke;
ot->exec = modifier_copy_to_selected_exec;
ot->poll = modifier_copy_to_selected_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */

View File

@ -137,6 +137,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_modifier_apply_as_shapekey);
WM_operatortype_append(OBJECT_OT_modifier_convert);
WM_operatortype_append(OBJECT_OT_modifier_copy);
WM_operatortype_append(OBJECT_OT_modifier_copy_to_selected);
WM_operatortype_append(OBJECT_OT_modifier_set_active);
WM_operatortype_append(OBJECT_OT_multires_subdivide);
WM_operatortype_append(OBJECT_OT_multires_reshape);
@ -159,6 +160,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_to_index);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_apply);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy_to_selected);
/* shader fx */
WM_operatortype_append(OBJECT_OT_shaderfx_add);

View File

@ -956,16 +956,14 @@ float ED_view3d_grid_view_scale(Scene *scene,
RegionView3D *rv3d = region->regiondata;
if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
/* Decrease the distance between grid snap points depending on zoom. */
/* `0.38` was a value visually obtained in order to get a snap distance
* that matches previous versions Blender.*/
float min_dist = 16.0f / (region->sizex * rv3d->winmat[0][0]);
float dist = 12.0f / (region->sizex * rv3d->winmat[0][0]);
float grid_steps[STEPS_LEN];
ED_view3d_grid_steps(scene, v3d, rv3d, grid_steps);
/* Skip last item, in case the 'mid_dist' is greater than the largest unit. */
int i;
for (i = 0; i < ARRAY_SIZE(grid_steps) - 1; i++) {
grid_scale = grid_steps[i];
if (grid_scale > min_dist) {
if (grid_scale > dist) {
break;
}
}

View File

@ -85,7 +85,8 @@ bool transdata_check_local_islands(TransInfo *t, short around)
void setTransformViewMatrices(TransInfo *t)
{
if (t->spacetype == SPACE_VIEW3D && t->region && t->region->regiontype == RGN_TYPE_WINDOW) {
if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && t->region &&
(t->region->regiontype == RGN_TYPE_WINDOW)) {
RegionView3D *rv3d = t->region->regiondata;
copy_m4_m4(t->viewmat, rv3d->viewmat);

View File

@ -1298,6 +1298,11 @@ static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup,
void drawDial3d(const TransInfo *t)
{
if (t->mode == TFM_ROTATION && t->spacetype == SPACE_VIEW3D) {
if (t->options & CTX_PAINT_CURVE) {
/* Matrices are in the screen space. Not supported. */
return;
}
wmGizmo *gz = wm_gizmomap_modal_get(t->region->gizmo_map);
if (gz == NULL) {
/* We only draw Dial3d if the operator has been called by a gizmo. */

View File

@ -602,20 +602,34 @@ short transform_orientation_matrix_get(
orientation = V3D_ORIENT_CUSTOM;
}
if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
if ((t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
rv3d = t->region->regiondata;
}
return ED_transform_calc_orientation_from_type_ex(C,
r_spacemtx,
/* extra args (can be accessed from context) */
scene,
rv3d,
ob,
obedit,
orientation,
orientation_index_custom,
t->around);
short orient_type = ED_transform_calc_orientation_from_type_ex(
C,
r_spacemtx,
/* extra args (can be accessed from context) */
scene,
rv3d,
ob,
obedit,
orientation,
orientation_index_custom,
t->around);
if (rv3d && (t->options & CTX_PAINT_CURVE)) {
/* Screen space in the 3d region. */
if (orient_type == V3D_ORIENT_VIEW) {
unit_m3(r_spacemtx);
}
else {
mul_m3_m4m3(r_spacemtx, rv3d->viewmat, r_spacemtx);
normalize_m3(r_spacemtx);
}
}
return orient_type;
}
const char *transform_orientations_spacename_get(TransInfo *t, const short orient_type)

View File

@ -270,6 +270,11 @@ static void gpencil_modifier_ops_extra_draw(bContext *C, uiLayout *layout, void
ICON_DUPLICATE,
"OBJECT_OT_gpencil_modifier_copy");
uiItemO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"),
0,
"OBJECT_OT_gpencil_modifier_copy_to_selected");
uiItemS(layout);
/* Move to first. */

View File

@ -5696,8 +5696,7 @@ static char *rna_idp_path(PointerRNA *ptr,
BLI_assert(haystack->type == IDP_GROUP);
link.up = parent_link;
/* always set both name and index,
* else a stale value might get used */
/* Always set both name and index, else a stale value might get used. */
link.name = NULL;
link.index = -1;
@ -5708,11 +5707,30 @@ static char *rna_idp_path(PointerRNA *ptr,
path = rna_idp_path_create(&link);
break;
}
/* Early out in case the IDProperty type cannot contain RNA properties. */
if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) {
continue;
}
/* Ensure this is RNA. */
/* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name
* collides with an actual fully static RNA property of the same struct (which would then not
* be flagged with `PROP_IDPROPERTY`).
*
* That case must be ignored here, we only want to deal with runtime RNA properties stored in
* IDProps.
*
* See T84091. */
PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) {
continue;
}
if (iter->type == IDP_GROUP) {
/* ensure this is RNA */
PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
if (prop && prop->type == PROP_POINTER) {
if (prop->type == PROP_POINTER) {
PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
BLI_assert(child_ptr.type != NULL);
link.name = iter->name;
link.index = -1;
if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
@ -5721,8 +5739,7 @@ static char *rna_idp_path(PointerRNA *ptr,
}
}
else if (iter->type == IDP_IDPARRAY) {
PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
if (prop && prop->type == PROP_COLLECTION) {
if (prop->type == PROP_COLLECTION) {
IDProperty *array = IDP_IDPArray(iter);
if (needle >= array && needle < (iter->len + array)) { /* found! */
link.name = iter->name;
@ -5735,6 +5752,7 @@ static char *rna_idp_path(PointerRNA *ptr,
for (j = 0; j < iter->len; j++, array++) {
PointerRNA child_ptr;
if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) {
BLI_assert(child_ptr.type != NULL);
link.index = j;
if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
break;
@ -5752,7 +5770,10 @@ static char *rna_idp_path(PointerRNA *ptr,
}
/**
* Find the path from the structure referenced by the pointer to the #IDProperty object.
* Find the path from the structure referenced by the pointer to the runtime RNA-defined
* #IDProperty object.
*
* \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties).
*
* \param ptr: Reference to the object owning the custom property storage.
* \param needle: Custom property object to find.

View File

@ -256,6 +256,11 @@ static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
"OBJECT_OT_modifier_copy");
}
uiItemO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"),
0,
"OBJECT_OT_modifier_copy_to_selected");
uiItemS(layout);
/* Move to first. */