From 2f60a5030f6c90c2278d3938460809de43012f85 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Wed, 29 Jun 2011 04:34:20 +0000 Subject: 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. --- source/blender/blenkernel/BKE_library.h | 3 + source/blender/blenkernel/intern/action.c | 88 ++++++++++------ source/blender/blenkernel/intern/library.c | 28 +++++ .../editors/interface/interface_templates.c | 19 +--- source/blender/editors/space_outliner/outliner.c | 115 +++++++++++++++------ 5 files changed, 175 insertions(+), 78 deletions(-) (limited to 'source') diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 871a78bbab3..0d6d41109b4 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -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); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 08eed6d61ba..e69ff5df913 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -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; diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 0b07f40cad6..5dbe35b2f7d 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -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 ) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 32a20e82d2f..39abcecbb6b 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -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: diff --git a/source/blender/editors/space_outliner/outliner.c b/source/blender/editors/space_outliner/outliner.c index 20be507f5a0..3e4641bc0b9 100644 --- a/source/blender/editors/space_outliner/outliner.c +++ b/source/blender/editors/space_outliner/outliner.c @@ -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... */ -- cgit v1.2.3