Actions can now be made single-user from the Outliner
* Use the same method as from unlinking actions to do this. * Split off the make single-user code used for the ID-browser into a function in blenkernel which can be used elsewhere. Getting materials to also work using this method proved to be a bit too tricky (due to the whole messy ob vs obdata situation), so I haven't done that.
This commit is contained in:
@@ -44,6 +44,8 @@ struct Main;
|
||||
struct Library;
|
||||
struct wmWindowManager;
|
||||
struct bContext;
|
||||
struct PointerRNA;
|
||||
struct PropertyRNA;
|
||||
|
||||
void *alloc_libblock(struct ListBase *lb, short type, const char *name);
|
||||
void *copy_libblock(void *rt);
|
||||
@@ -53,6 +55,7 @@ void id_lib_extern(struct ID *id);
|
||||
void id_us_plus(struct ID *id);
|
||||
void id_us_min(struct ID *id);
|
||||
int id_make_local(struct ID *id, int test);
|
||||
int id_single_user(struct bContext *C, struct ID *id, struct PointerRNA *ptr, struct PropertyRNA *prop);
|
||||
int id_copy(struct ID *id, struct ID **newid, int test);
|
||||
int id_unlink(struct ID *id, int test);
|
||||
|
||||
|
||||
@@ -89,60 +89,80 @@ bAction *add_empty_action(const char name[])
|
||||
return act;
|
||||
}
|
||||
|
||||
/* .................................. */
|
||||
|
||||
/* temp data for make_local_action */
|
||||
typedef struct tMakeLocalActionContext {
|
||||
bAction *act; /* original action */
|
||||
bAction *actn; /* new action */
|
||||
|
||||
int lib; /* some action users were libraries */
|
||||
int local; /* some action users were not libraries */
|
||||
} tMakeLocalActionContext;
|
||||
|
||||
/* helper function for make_local_action() - local/lib init step */
|
||||
static void make_localact_init_cb(ID *id, AnimData *adt, void *mlac_ptr)
|
||||
{
|
||||
tMakeLocalActionContext *mlac = (tMakeLocalActionContext *)mlac_ptr;
|
||||
|
||||
if (adt->action == mlac->act) {
|
||||
if (id->lib)
|
||||
mlac->lib = 1;
|
||||
else
|
||||
mlac->local = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* helper function for make_local_action() - change references */
|
||||
static void make_localact_apply_cb(ID *id, AnimData *adt, void *mlac_ptr)
|
||||
{
|
||||
tMakeLocalActionContext *mlac = (tMakeLocalActionContext *)mlac_ptr;
|
||||
|
||||
if (adt->action == mlac->act) {
|
||||
if (id->lib==0) {
|
||||
adt->action = mlac->actn;
|
||||
|
||||
id_us_plus(&mlac->actn->id);
|
||||
id_us_min(&mlac->act->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// does copy_fcurve...
|
||||
void make_local_action(bAction *act)
|
||||
{
|
||||
// Object *ob;
|
||||
tMakeLocalActionContext mlac = {act, NULL, 0, 0};
|
||||
Main *bmain= G.main;
|
||||
bAction *actn;
|
||||
int local=0, lib=0;
|
||||
|
||||
if (act->id.lib==NULL) return;
|
||||
if (act->id.us==1) {
|
||||
if (act->id.lib==NULL)
|
||||
return;
|
||||
|
||||
// XXX: double-check this; it used to be just single-user check, but that was when fake-users were still default
|
||||
if ((act->id.flag & LIB_FAKEUSER) && (act->id.us<=1)) {
|
||||
act->id.lib= NULL;
|
||||
act->id.flag= LIB_LOCAL;
|
||||
new_id(&bmain->action, (ID *)act, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0 // XXX old animation system
|
||||
ob= G.main->object.first;
|
||||
while(ob) {
|
||||
if(ob->action==act) {
|
||||
if(ob->id.lib) lib= 1;
|
||||
else local= 1;
|
||||
}
|
||||
ob= ob->id.next;
|
||||
}
|
||||
#endif
|
||||
BKE_animdata_main_cb(bmain, make_localact_init_cb, &mlac);
|
||||
|
||||
if(local && lib==0) {
|
||||
if (mlac.local && mlac.lib==0) {
|
||||
act->id.lib= NULL;
|
||||
act->id.flag= LIB_LOCAL;
|
||||
//make_local_action_channels(act);
|
||||
new_id(&bmain->action, (ID *)act, NULL);
|
||||
}
|
||||
else if(local && lib) {
|
||||
actn= copy_action(act);
|
||||
actn->id.us= 0;
|
||||
else if (mlac.local && mlac.lib) {
|
||||
mlac.actn= copy_action(act);
|
||||
mlac.actn->id.us= 0;
|
||||
|
||||
#if 0 // XXX old animation system
|
||||
ob= G.main->object.first;
|
||||
while(ob) {
|
||||
if(ob->action==act) {
|
||||
|
||||
if(ob->id.lib==0) {
|
||||
ob->action = actn;
|
||||
actn->id.us++;
|
||||
act->id.us--;
|
||||
}
|
||||
}
|
||||
ob= ob->id.next;
|
||||
}
|
||||
#endif // XXX old animation system
|
||||
BKE_animdata_main_cb(bmain, make_localact_apply_cb, &mlac);
|
||||
}
|
||||
}
|
||||
|
||||
/* .................................. */
|
||||
|
||||
void free_action (bAction *act)
|
||||
{
|
||||
/* sanity check */
|
||||
@@ -161,6 +181,8 @@ void free_action (bAction *act)
|
||||
BLI_freelistN(&act->markers);
|
||||
}
|
||||
|
||||
/* .................................. */
|
||||
|
||||
bAction *copy_action (bAction *src)
|
||||
{
|
||||
bAction *dst = NULL;
|
||||
|
||||
@@ -368,6 +368,34 @@ int id_unlink(ID *id, int test)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
ID *newid = NULL;
|
||||
PointerRNA idptr;
|
||||
|
||||
if (id) {
|
||||
/* if property isn't editable, we're going to have an extra block hanging around until we save */
|
||||
if (RNA_property_editable(ptr, prop)) {
|
||||
if (id_copy(id, &newid, 0) && newid) {
|
||||
/* copy animation actions too */
|
||||
BKE_copy_animdata_id_action(id);
|
||||
/* us is 1 by convention, but RNA_property_pointer_set
|
||||
will also incremement it, so set it to zero */
|
||||
newid->us= 0;
|
||||
|
||||
/* assign copy */
|
||||
RNA_id_pointer_create(newid, &idptr);
|
||||
RNA_property_pointer_set(ptr, prop, idptr);
|
||||
RNA_property_update(C, ptr, prop);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ListBase *which_libbase(Main *mainlib, short type)
|
||||
{
|
||||
switch( type ) {
|
||||
|
||||
@@ -234,7 +234,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
|
||||
{
|
||||
TemplateID *template= (TemplateID*)arg_litem;
|
||||
PointerRNA idptr= RNA_property_pointer_get(&template->ptr, template->prop);
|
||||
ID *id= idptr.data, *newid;
|
||||
ID *id= idptr.data;
|
||||
int event= GET_INT_FROM_POINTER(arg_event);
|
||||
|
||||
switch(event) {
|
||||
@@ -273,21 +273,8 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
|
||||
}
|
||||
break;
|
||||
case UI_ID_ALONE:
|
||||
if(id) {
|
||||
/* make copy */
|
||||
if(id_copy(id, &newid, 0) && newid) {
|
||||
/* copy animation actions too */
|
||||
BKE_copy_animdata_id_action(id);
|
||||
/* us is 1 by convention, but RNA_property_pointer_set
|
||||
will also incremement it, so set it to zero */
|
||||
newid->us= 0;
|
||||
|
||||
/* assign copy */
|
||||
RNA_id_pointer_create(newid, &idptr);
|
||||
RNA_property_pointer_set(&template->ptr, template->prop, idptr);
|
||||
RNA_property_update(C, &template->ptr, template->prop);
|
||||
}
|
||||
}
|
||||
if(id)
|
||||
id_single_user(C, id, &template->ptr, template->prop);
|
||||
break;
|
||||
#if 0
|
||||
case UI_ID_AUTO_NAME:
|
||||
|
||||
@@ -3340,6 +3340,23 @@ static void id_local_cb(bContext *UNUSED(C), Scene *UNUSED(scene), TreeElement *
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void singleuser_action_cb(bContext *C, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *tsep, TreeStoreElem *tselem)
|
||||
{
|
||||
ID *id = tselem->id;
|
||||
|
||||
if (id) {
|
||||
IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id;
|
||||
PointerRNA ptr = {{0}};
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_pointer_create(&iat->id, &RNA_AnimData, iat->adt, &ptr);
|
||||
prop = RNA_struct_find_property(&ptr, "action");
|
||||
|
||||
id_single_user(C, id, &ptr, prop);
|
||||
}
|
||||
}
|
||||
|
||||
static void group_linkobs2scene_cb(bContext *UNUSED(C), Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
|
||||
{
|
||||
Group *group= (Group *)tselem->id;
|
||||
@@ -3634,10 +3651,18 @@ void OUTLINER_OT_group_operation(wmOperatorType *ot)
|
||||
|
||||
/* **************************************** */
|
||||
|
||||
typedef enum eOutlinerIdOpTypes {
|
||||
OUTLINER_IDOP_INVALID = 0,
|
||||
OUTLINER_IDOP_UNLINK,
|
||||
OUTLINER_IDOP_LOCAL,
|
||||
OUTLINER_IDOP_SINGLE
|
||||
} eOutlinerIdOpTypes;
|
||||
|
||||
// TODO: implement support for changing the ID-block used
|
||||
static EnumPropertyItem prop_id_op_types[] = {
|
||||
{1, "UNLINK", 0, "Unlink", ""},
|
||||
{2, "LOCAL", 0, "Make Local", ""},
|
||||
{OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
|
||||
{OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
|
||||
{OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -3646,7 +3671,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
SpaceOops *soops= CTX_wm_space_outliner(C);
|
||||
int scenelevel=0, objectlevel=0, idlevel=0, datalevel=0;
|
||||
int event;
|
||||
eOutlinerIdOpTypes event;
|
||||
|
||||
/* check for invalid states */
|
||||
if (soops == NULL)
|
||||
@@ -3656,33 +3681,65 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
|
||||
|
||||
event= RNA_enum_get(op->ptr, "type");
|
||||
|
||||
if(event==1) {
|
||||
switch(idlevel) {
|
||||
case ID_AC:
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_action_cb);
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL);
|
||||
ED_undo_push(C, "Unlink action");
|
||||
break;
|
||||
case ID_MA:
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_material_cb);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_OB_SHADING, NULL);
|
||||
ED_undo_push(C, "Unlink material");
|
||||
break;
|
||||
case ID_TE:
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_texture_cb);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_OB_SHADING, NULL);
|
||||
ED_undo_push(C, "Unlink texture");
|
||||
break;
|
||||
default:
|
||||
BKE_report(op->reports, RPT_WARNING, "Not Yet");
|
||||
switch (event) {
|
||||
case OUTLINER_IDOP_UNLINK:
|
||||
{
|
||||
/* unlink datablock from its parent */
|
||||
switch (idlevel) {
|
||||
case ID_AC:
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_action_cb);
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL);
|
||||
ED_undo_push(C, "Unlink action");
|
||||
break;
|
||||
case ID_MA:
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_material_cb);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_OB_SHADING, NULL);
|
||||
ED_undo_push(C, "Unlink material");
|
||||
break;
|
||||
case ID_TE:
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_texture_cb);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_OB_SHADING, NULL);
|
||||
ED_undo_push(C, "Unlink texture");
|
||||
break;
|
||||
default:
|
||||
BKE_report(op->reports, RPT_WARNING, "Not Yet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(event==2) {
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_local_cb);
|
||||
ED_undo_push(C, "Localized Data");
|
||||
break;
|
||||
|
||||
case OUTLINER_IDOP_LOCAL:
|
||||
{
|
||||
/* make local */
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_local_cb);
|
||||
ED_undo_push(C, "Localized Data");
|
||||
}
|
||||
break;
|
||||
|
||||
case OUTLINER_IDOP_SINGLE:
|
||||
{
|
||||
/* make single user */
|
||||
switch (idlevel) {
|
||||
case ID_AC:
|
||||
outliner_do_libdata_operation(C, scene, soops, &soops->tree, singleuser_action_cb);
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL);
|
||||
ED_undo_push(C, "Single-User Action");
|
||||
break;
|
||||
|
||||
default:
|
||||
BKE_report(op->reports, RPT_WARNING, "Not Yet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// invalid - unhandled
|
||||
break;
|
||||
}
|
||||
|
||||
/* wrong notifier still... */
|
||||
|
||||
Reference in New Issue
Block a user