Outliner RMB Menu - AnimData mangement

* When clicking on "Animation" items in the Outliner, there's now a
menu containing from which you can change the action used, and
refresh/delete all drivers.

* Moved action-setting logic for AnimData actions to a single utility
function in anim_sys, since this was starting to be done in too many
places already.

* Fixed Outliner refresh bug after changing the active action
This commit is contained in:
2011-07-07 03:35:48 +00:00
parent 34c5784f99
commit d00a3c8ddf
8 changed files with 329 additions and 59 deletions

View File

@@ -41,6 +41,7 @@ struct KeyingSet;
struct KS_Path;
struct PointerRNA;
struct ReportList;
struct bAction;
struct bActionGroup;
struct AnimMapper;
@@ -57,6 +58,9 @@ struct AnimData *BKE_animdata_from_id(struct ID *id);
/* Add AnimData to the given ID-block */
struct AnimData *BKE_id_add_animdata(struct ID *id);
/* Set active action used by AnimData from the given ID-block */
short BKE_animdata_set_action(struct ReportList *reports, struct ID *id, struct bAction *act);
/* Free AnimData */
void BKE_free_animdata(struct ID *id);

View File

@@ -56,6 +56,7 @@
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_library.h"
#include "BKE_report.h"
#include "BKE_utildefines.h"
#include "RNA_access.h"
@@ -144,6 +145,59 @@ AnimData *BKE_id_add_animdata (ID *id)
return NULL;
}
/* Action Setter --------------------------------------- */
/* Called when user tries to change the active action of an AnimData block (via RNA, Outliner, etc.) */
short BKE_animdata_set_action (ReportList *reports, ID *id, bAction *act)
{
AnimData *adt = BKE_animdata_from_id(id);
short ok = 0;
/* animdata validity check */
if (adt == NULL) {
BKE_report(reports, RPT_WARNING, "No AnimData to set action on");
return ok;
}
/* active action is only editable when it is not a tweaking strip
* see rna_AnimData_action_editable() in rna_animation.c
*/
if ((adt->flag & ADT_NLA_EDIT_ON) || (adt->actstrip) || (adt->tmpact)) {
/* cannot remove, otherwise things turn to custard */
BKE_report(reports, RPT_ERROR, "Cannot change action, as it is still being edited in NLA");
return ok;
}
/* manage usercount for current action */
if (adt->action)
id_us_min((ID*)adt->action);
/* assume that AnimData's action can in fact be edited... */
if (act) {
/* action must have same type as owner */
if (ELEM(act->idroot, 0, GS(id->name))) {
/* can set */
adt->action = act;
id_us_plus((ID*)adt->action);
ok = 1;
}
else {
/* cannot set */
BKE_reportf(reports, RPT_ERROR,
"Couldn't set Action '%s' onto ID '%s', as it doesn't have suitably rooted paths for this purpose",
act->id.name+2, id->name);
//ok = 0;
}
}
else {
/* just clearing the action... */
adt->action = NULL;
ok = 1;
}
return ok;
}
/* Freeing -------------------------------------------- */
/* Free AnimData used by the nominated ID-block, and clear ID-block's AnimData pointer */

View File

@@ -101,6 +101,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "ED_keyframing.h"
@@ -3170,29 +3171,10 @@ static void set_operation_types(SpaceOops *soops, ListBase *lb,
}
}
static void unlink_action_cb(bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *UNUSED(tselem))
static void unlink_action_cb(bContext *C, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *tsep, TreeStoreElem *UNUSED(tselem))
{
IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id;
AnimData *adt = iat->adt;
//printf("iat = '%s' | act = '%s'\n", iat->id.name, tselem->id->name);
/* active action is only editable when it is not a tweaking strip
* see rna_AnimData_action_editable() in rna_animation.c
*/
if ((adt->flag & ADT_NLA_EDIT_ON) || (adt->actstrip) || (adt->tmpact)) {
/* cannot remove, otherwise things turn to custard */
ReportList *reports = CTX_wm_reports(C);
// FIXME: this only gets shown in info-window, since this is global not operator report
BKE_report(reports, RPT_ERROR, "Cannot unlink action, as it is still being edited in NLA");
return;
}
/* remove action... */
id_us_min((ID*)adt->action);
adt->action = NULL;
/* just set action to NULL */
BKE_animdata_set_action(CTX_wm_reports(C), tsep->id, NULL);
}
static void unlink_material_cb(bContext *UNUSED(C), Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *UNUSED(tselem))
@@ -3414,6 +3396,36 @@ static void outliner_do_object_operation(bContext *C, Scene *scene_act, SpaceOop
/* ******************************************** */
static void unlinkact_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), TreeStoreElem *tselem)
{
/* just set action to NULL */
BKE_animdata_set_action(NULL, tselem->id, NULL);
}
static void cleardrivers_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), TreeStoreElem *tselem)
{
IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
/* just free drivers - stored as a list of F-Curves */
free_fcurves(&iat->adt->drivers);
}
static void refreshdrivers_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), TreeStoreElem *tselem)
{
IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
FCurve *fcu;
/* loop over drivers, performing refresh (i.e. check graph_buttons.c and rna_fcurve.c for details) */
for (fcu = iat->adt->drivers.first; fcu; fcu= fcu->next) {
fcu->flag &= ~FCURVE_DISABLED;
if (fcu->driver)
fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
}
}
/* --------------------------------- */
static void pchan_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem))
{
bPoseChannel *pchan= (bPoseChannel *)te->directdata;
@@ -3775,6 +3787,224 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot)
/* **************************************** */
static void outliner_do_id_set_operation(SpaceOops *soops, int type, ListBase *lb, ID *newid,
void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
{
TreeElement *te;
TreeStoreElem *tselem;
for (te=lb->first; te; te= te->next) {
tselem= TREESTORE(te);
if (tselem->flag & TSE_SELECTED) {
if(tselem->type==type) {
TreeStoreElem *tsep = TREESTORE(te->parent);
operation_cb(te, tselem, tsep, newid);
}
}
if ((tselem->flag & TSE_CLOSED)==0) {
outliner_do_id_set_operation(soops, type, &te->subtree, newid, operation_cb);
}
}
}
/* ------------------------------------------ */
static void actionset_id_cb(TreeElement *te, TreeStoreElem *tselem, TreeStoreElem *tsep, ID *actId)
{
bAction *act = (bAction *)actId;
if (tselem->type == TSE_ANIM_DATA) {
/* "animation" entries - action is child of this */
BKE_animdata_set_action(NULL, tselem->id, act);
}
/* TODO: if any other "expander" channels which own actions need to support this menu,
* add: tselem->type = ...
*/
else if (tsep && (tsep->type == TSE_ANIM_DATA)) {
/* "animation" entries case again */
BKE_animdata_set_action(NULL, tsep->id, act);
}
// TODO: other cases not supported yet
}
static int outliner_action_set_exec(bContext *C, wmOperator *op)
{
SpaceOops *soops= CTX_wm_space_outliner(C);
int scenelevel=0, objectlevel=0, idlevel=0, datalevel=0;
bAction *act;
/* check for invalid states */
if (soops == NULL)
return OPERATOR_CANCELLED;
set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
/* get action to use */
act= BLI_findlink(&CTX_data_main(C)->action, RNA_enum_get(op->ptr, "action"));
if (act == NULL) {
BKE_report(op->reports, RPT_ERROR, "No valid Action to add.");
return OPERATOR_CANCELLED;
}
else if (act->idroot == 0) {
/* hopefully in this case (i.e. library of userless actions), the user knows what they're doing... */
BKE_reportf(op->reports, RPT_WARNING,
"Action '%s' does not specify what datablocks it can be used on. Try setting the 'ID Root Type' setting from the Datablocks Editor for this Action to avoid future problems",
act->id.name+2);
}
/* perform action if valid channel */
if (datalevel == TSE_ANIM_DATA)
outliner_do_id_set_operation(soops, datalevel, &soops->tree, (ID*)act, actionset_id_cb);
else if (idlevel == ID_AC)
outliner_do_id_set_operation(soops, idlevel, &soops->tree, (ID*)act, actionset_id_cb);
else
return OPERATOR_CANCELLED;
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL);
ED_undo_push(C, "Set action");
/* done */
return OPERATOR_FINISHED;
}
void OUTLINER_OT_action_set(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name= "Outliner Set Action";
ot->idname= "OUTLINER_OT_action_set";
ot->description= "Change the active action used";
/* api callbacks */
ot->invoke= WM_enum_search_invoke;
ot->exec= outliner_action_set_exec;
ot->poll= ED_operator_outliner_active;
/* flags */
ot->flag= 0;
/* props */
// TODO: this would be nicer as an ID-pointer...
prop= RNA_def_enum(ot->srna, "action", DummyRNA_NULL_items, 0, "Action", "");
RNA_def_enum_funcs(prop, RNA_action_itemf);
ot->prop= prop;
}
/* **************************************** */
typedef enum eOutliner_AnimDataOps {
OUTLINER_ANIMOP_INVALID = 0,
OUTLINER_ANIMOP_SET_ACT,
OUTLINER_ANIMOP_CLEAR_ACT,
OUTLINER_ANIMOP_REFRESH_DRV,
OUTLINER_ANIMOP_CLEAR_DRV
//OUTLINER_ANIMOP_COPY_DRIVERS,
//OUTLINER_ANIMOP_PASTE_DRIVERS
} eOutliner_AnimDataOps;
static EnumPropertyItem prop_animdata_op_types[] = {
{OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""},
{OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""},
{OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""},
//{OUTLINER_ANIMOP_COPY_DRIVERS, "COPY_DRIVERS", 0, "Copy Drivers", ""},
//{OUTLINER_ANIMOP_PASTE_DRIVERS, "PASTE_DRIVERS", 0, "Paste Drivers", ""},
{OUTLINER_ANIMOP_CLEAR_DRV, "CLEAR_DRIVERS", 0, "Clear Drivers", ""},
{0, NULL, 0, NULL, NULL}
};
static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
{
SpaceOops *soops= CTX_wm_space_outliner(C);
int scenelevel=0, objectlevel=0, idlevel=0, datalevel=0;
eOutliner_AnimDataOps event;
short updateDeps = 0;
/* check for invalid states */
if (soops == NULL)
return OPERATOR_CANCELLED;
event= RNA_enum_get(op->ptr, "type");
set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
if (datalevel != TSE_ANIM_DATA)
return OPERATOR_CANCELLED;
/* perform the core operation */
switch (event) {
case OUTLINER_ANIMOP_SET_ACT:
/* delegate once again... */
WM_operator_name_call(C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, NULL);
break;
case OUTLINER_ANIMOP_CLEAR_ACT:
/* clear active action - using standard rules */
outliner_do_data_operation(soops, datalevel, event, &soops->tree, unlinkact_animdata_cb);
WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL);
ED_undo_push(C, "Unlink action");
break;
case OUTLINER_ANIMOP_REFRESH_DRV:
outliner_do_data_operation(soops, datalevel, event, &soops->tree, refreshdrivers_animdata_cb);
WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN, NULL);
//ED_undo_push(C, "Refresh Drivers"); /* no undo needed - shouldn't have any impact? */
updateDeps = 1;
break;
case OUTLINER_ANIMOP_CLEAR_DRV:
outliner_do_data_operation(soops, datalevel, event, &soops->tree, cleardrivers_animdata_cb);
WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN, NULL);
ED_undo_push(C, "Clear Drivers");
updateDeps = 1;
break;
default: // invalid
break;
}
/* update dependencies */
if (updateDeps) {
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
/* rebuild depsgraph for the new deps */
DAG_scene_sort(bmain, scene);
/* force an update of depsgraph */
DAG_ids_flush_update(bmain, 0);
}
return OPERATOR_FINISHED;
}
void OUTLINER_OT_animdata_operation(wmOperatorType *ot)
{
/* identifiers */
ot->name= "Outliner Animation Data Operation";
ot->idname= "OUTLINER_OT_animdata_operation";
ot->description= "";
/* callbacks */
ot->invoke= WM_menu_invoke;
ot->exec= outliner_animdata_operation_exec;
ot->poll= ED_operator_outliner_active;
ot->flag= 0;
ot->prop= RNA_def_enum(ot->srna, "type", prop_animdata_op_types, 0, "Animation Operation", "");
}
/* **************************************** */
static EnumPropertyItem prop_data_op_types[] = {
{1, "SELECT", 0, "Select", ""},
{2, "DESELECT", 0, "Deselect", ""},
@@ -3888,7 +4118,12 @@ static int do_outliner_operation_event(bContext *C, Scene *scene, ARegion *ar, S
else if(datalevel) {
if(datalevel==-1) error("Mixed selection");
else {
WM_operator_name_call(C, "OUTLINER_OT_data_operation", WM_OP_INVOKE_REGION_WIN, NULL);
if (datalevel == TSE_ANIM_DATA)
WM_operator_name_call(C, "OUTLINER_OT_animdata_operation", WM_OP_INVOKE_REGION_WIN, NULL);
else if (datalevel == TSE_DRIVER_BASE)
/* do nothing... no special ops needed yet */;
else
WM_operator_name_call(C, "OUTLINER_OT_data_operation", WM_OP_INVOKE_REGION_WIN, NULL);
}
}

View File

@@ -128,6 +128,8 @@ void OUTLINER_OT_object_operation(struct wmOperatorType *ot);
void OUTLINER_OT_group_operation(struct wmOperatorType *ot);
void OUTLINER_OT_id_operation(struct wmOperatorType *ot);
void OUTLINER_OT_data_operation(struct wmOperatorType *ot);
void OUTLINER_OT_animdata_operation(struct wmOperatorType *ot);
void OUTLINER_OT_action_set(struct wmOperatorType *ot);
void OUTLINER_OT_show_one_level(struct wmOperatorType *ot);
void OUTLINER_OT_show_active(struct wmOperatorType *ot);

View File

@@ -57,6 +57,8 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_group_operation);
WM_operatortype_append(OUTLINER_OT_id_operation);
WM_operatortype_append(OUTLINER_OT_data_operation);
WM_operatortype_append(OUTLINER_OT_animdata_operation);
WM_operatortype_append(OUTLINER_OT_action_set);
WM_operatortype_append(OUTLINER_OT_show_one_level);
WM_operatortype_append(OUTLINER_OT_show_active);

View File

@@ -179,6 +179,13 @@ static void outliner_main_area_listener(ARegion *ar, wmNotifier *wmn)
break;
}
break;
case NC_ANIMATION:
switch(wmn->data) {
case ND_NLA_ACTCHANGE:
ED_region_tag_redraw(ar);
break;
}
break;
}
}

View File

@@ -74,42 +74,7 @@ static int rna_AnimData_action_editable(PointerRNA *ptr)
static void rna_AnimData_action_set(PointerRNA *ptr, PointerRNA value)
{
ID *ownerId = (ID *)ptr->id.data;
AnimData *adt = (AnimData *)ptr->data;
/* manage usercount for current action */
if (adt->action)
id_us_min((ID*)adt->action);
/* assume that AnimData's action can in fact be edited... */
if ((value.data) && (ownerId)) {
bAction *act = (bAction *)value.data;
/* action must have same type as owner */
if (ownerId) {
if (ELEM(act->idroot, 0, GS(ownerId->name))) {
/* can set */
adt->action = act;
id_us_plus((ID*)adt->action);
}
else {
/* cannot set */
printf("ERROR: Couldn't set Action '%s' onto ID '%s', as it doesn't have suitably rooted paths for this purpose\n",
act->id.name+2, ownerId->name);
}
}
else {
/* cannot tell if we can set, so let's just be generous... */
printf("Warning: Set Action '%s' onto AnimData block with an unknown ID-owner. May have attached invalid data\n",
act->id.name+2);
adt->action = act;
id_us_plus((ID*)adt->action);
}
}
else {
/* just clearing the action... */
adt->action = NULL;
}
BKE_animdata_set_action(NULL, ownerId, value.data);
}
/* ****************************** */

View File

@@ -424,6 +424,7 @@ static void rna_def_nlastrip(BlenderRNA *brna)
RNA_def_property_update(prop, NC_ANIMATION|ND_NLA, NULL); /* this will do? */
/* Action */
// TODO: this should only be editable if it is not being edited atm...
prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "act");
RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Action_id_poll");