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:
2011-06-29 04:34:20 +00:00
parent b85e0c3e85
commit 2f60a5030f
5 changed files with 175 additions and 78 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 ) {

View File

@@ -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:

View File

@@ -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... */