forked from bartvdbraak/blender
Drivers Editing: Added "Copy/Paste" buttons beside "Add Variable" for copying all variables from one driver to another
This was a feature request from a few years back (IIRC from ZanQdo?) to make it easier to reuse one set of driver variables across several different drivers. Dev Notes: * Finally it's done! All that trouble for two little buttons. * Grr... cmake... grrr!
This commit is contained in:
parent
c97b0b14e3
commit
69ee6c9866
@ -93,7 +93,11 @@ void bezt_add_to_cfra_elem(ListBase *lb, struct BezTriple *bezt);
|
||||
void fcurve_free_driver(struct FCurve *fcu);
|
||||
struct ChannelDriver *fcurve_copy_driver(struct ChannelDriver *driver);
|
||||
|
||||
void driver_free_variable(struct ChannelDriver *driver, struct DriverVar *dvar);
|
||||
void driver_variables_copy(struct ListBase *dst_list, const struct ListBase *src_list);
|
||||
|
||||
void driver_free_variable(struct ListBase *variables, struct DriverVar *dvar);
|
||||
void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar);
|
||||
|
||||
void driver_change_variable_type(struct DriverVar *dvar, int type);
|
||||
void driver_variable_name_validate(struct DriverVar *dvar);
|
||||
struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver);
|
||||
|
@ -1600,8 +1600,8 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type)
|
||||
|
||||
/* Driver API --------------------------------- */
|
||||
|
||||
/* This frees the driver variable itself */
|
||||
void driver_free_variable(ChannelDriver *driver, DriverVar *dvar)
|
||||
/* Perform actual freeing driver variable and remove it from the given list */
|
||||
void driver_free_variable(ListBase *variables, DriverVar *dvar)
|
||||
{
|
||||
/* sanity checks */
|
||||
if (dvar == NULL)
|
||||
@ -1621,8 +1621,15 @@ void driver_free_variable(ChannelDriver *driver, DriverVar *dvar)
|
||||
DRIVER_TARGETS_LOOPER_END
|
||||
|
||||
/* remove the variable from the driver */
|
||||
BLI_freelinkN(&driver->variables, dvar);
|
||||
BLI_freelinkN(variables, dvar);
|
||||
}
|
||||
|
||||
/* Free the driver variable and do extra updates */
|
||||
void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar)
|
||||
{
|
||||
/* remove and free the driver variable */
|
||||
driver_free_variable(&driver->variables, dvar);
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
/* since driver variables are cached, the expression needs re-compiling too */
|
||||
if (driver->type == DRIVER_TYPE_PYTHON)
|
||||
@ -1630,6 +1637,24 @@ void driver_free_variable(ChannelDriver *driver, DriverVar *dvar)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Copy driver variables from src_vars list to dst_vars list */
|
||||
void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars)
|
||||
{
|
||||
BLI_assert(BLI_listbase_is_empty(dst_vars));
|
||||
BLI_duplicatelist(dst_vars, src_vars);
|
||||
|
||||
for (DriverVar *dvar = dst_vars->first; dvar; dvar = dvar->next) {
|
||||
/* need to go over all targets so that we don't leave any dangling paths */
|
||||
DRIVER_TARGETS_LOOPER(dvar)
|
||||
{
|
||||
/* make a copy of target's rna path if available */
|
||||
if (dtar->rna_path)
|
||||
dtar->rna_path = MEM_dupallocN(dtar->rna_path);
|
||||
}
|
||||
DRIVER_TARGETS_LOOPER_END
|
||||
}
|
||||
}
|
||||
|
||||
/* Change the type of driver variable */
|
||||
void driver_change_variable_type(DriverVar *dvar, int type)
|
||||
{
|
||||
@ -1770,7 +1795,7 @@ void fcurve_free_driver(FCurve *fcu)
|
||||
/* free driver targets */
|
||||
for (dvar = driver->variables.first; dvar; dvar = dvarn) {
|
||||
dvarn = dvar->next;
|
||||
driver_free_variable(driver, dvar);
|
||||
driver_free_variable_ex(driver, dvar);
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
@ -1788,7 +1813,6 @@ void fcurve_free_driver(FCurve *fcu)
|
||||
ChannelDriver *fcurve_copy_driver(ChannelDriver *driver)
|
||||
{
|
||||
ChannelDriver *ndriver;
|
||||
DriverVar *dvar;
|
||||
|
||||
/* sanity checks */
|
||||
if (driver == NULL)
|
||||
@ -1799,19 +1823,8 @@ ChannelDriver *fcurve_copy_driver(ChannelDriver *driver)
|
||||
ndriver->expr_comp = NULL;
|
||||
|
||||
/* copy variables */
|
||||
BLI_listbase_clear(&ndriver->variables);
|
||||
BLI_duplicatelist(&ndriver->variables, &driver->variables);
|
||||
|
||||
for (dvar = ndriver->variables.first; dvar; dvar = dvar->next) {
|
||||
/* need to go over all targets so that we don't leave any dangling paths */
|
||||
DRIVER_TARGETS_LOOPER(dvar)
|
||||
{
|
||||
/* make a copy of target's rna path if available */
|
||||
if (dtar->rna_path)
|
||||
dtar->rna_path = MEM_dupallocN(dtar->rna_path);
|
||||
}
|
||||
DRIVER_TARGETS_LOOPER_END
|
||||
}
|
||||
BLI_listbase_clear(&ndriver->variables); /* to get rid of refs to non-copied data (that's still used on original) */
|
||||
driver_variables_copy(&ndriver->variables, &driver->variables);
|
||||
|
||||
/* return the new driver */
|
||||
return ndriver;
|
||||
|
@ -59,6 +59,10 @@ if(WITH_INTERNATIONAL)
|
||||
add_definitions(-DWITH_INTERNATIONAL)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON)
|
||||
add_definitions(-DWITH_PYTHON)
|
||||
endif()
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_editor_animation "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
@ -600,7 +600,7 @@ bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int a
|
||||
|
||||
/* create Driver F-Curve, but without data which will be copied across... */
|
||||
fcu = verify_driver_fcurve(id, rna_path, array_index, -1);
|
||||
|
||||
|
||||
if (fcu) {
|
||||
/* copy across the curve data from the buffer curve
|
||||
* NOTE: this step needs care to not miss new settings
|
||||
@ -624,6 +624,120 @@ bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int a
|
||||
return (fcu != NULL);
|
||||
}
|
||||
|
||||
/* ************************************************** */
|
||||
/* Driver Management API - Copy/Paste Driver Variables */
|
||||
|
||||
/* Copy/Paste Buffer for Driver Variables... */
|
||||
static ListBase driver_vars_copybuf = {NULL, NULL};
|
||||
|
||||
/* This function frees any MEM_calloc'ed copy/paste buffer data */
|
||||
void ANIM_driver_vars_copybuf_free(void)
|
||||
{
|
||||
/* Free the driver variables kept in the buffer */
|
||||
if (driver_vars_copybuf.first) {
|
||||
/* We use a dummy driver here, as the variable freeing code assumes there's a driver */
|
||||
ChannelDriver dummy_driver = {{NULL}};
|
||||
dummy_driver.variables = driver_vars_copybuf;
|
||||
|
||||
/* Free variables (and any data they use) */
|
||||
DriverVar *dvar, *dvarn;
|
||||
for (dvar = driver_vars_copybuf.first; dvar; dvar = dvarn) {
|
||||
dvarn = dvar->next;
|
||||
driver_free_variable(&driver_vars_copybuf, dvar);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_listbase_clear(&driver_vars_copybuf);
|
||||
}
|
||||
|
||||
/* Checks if there are driver variables in the copy/paste buffer */
|
||||
bool ANIM_driver_vars_can_paste(void)
|
||||
{
|
||||
return (BLI_listbase_is_empty(&driver_vars_copybuf) == false);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
/* Copy the given driver's variables to the buffer */
|
||||
bool ANIM_driver_vars_copy(ReportList *reports, FCurve *fcu)
|
||||
{
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, fcu, fcu->driver)) {
|
||||
BKE_report(reports, RPT_ERROR, "No driver to copy variables from");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BLI_listbase_is_empty(&fcu->driver->variables)) {
|
||||
BKE_report(reports, RPT_ERROR, "Driver has no variables to copy");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* clear buffer */
|
||||
ANIM_driver_vars_copybuf_free();
|
||||
|
||||
/* copy over the variables */
|
||||
driver_variables_copy(&driver_vars_copybuf, &fcu->driver->variables);
|
||||
|
||||
return (BLI_listbase_is_empty(&driver_vars_copybuf) == false);
|
||||
}
|
||||
|
||||
/* Paste the variables in the buffer to the given FCurve */
|
||||
bool ANIM_driver_vars_paste(ReportList *reports, FCurve *fcu, bool replace)
|
||||
{
|
||||
ChannelDriver *driver = (fcu) ? fcu->driver : NULL;
|
||||
ListBase tmp_list = {NULL, NULL};
|
||||
|
||||
/* sanity checks */
|
||||
if (BLI_listbase_is_empty(&driver_vars_copybuf)) {
|
||||
BKE_report(reports, RPT_ERROR, "No driver variables in clipboard to paste");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ELEM(NULL, fcu, fcu->driver)) {
|
||||
BKE_report(reports, RPT_ERROR, "Cannot paste driver variables without a driver");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 1) Make a new copy of the variables in the buffer - these will get pasted later... */
|
||||
driver_variables_copy(&tmp_list, &driver_vars_copybuf);
|
||||
|
||||
/* 2) Prepare destination array */
|
||||
if (replace) {
|
||||
DriverVar *dvar, *dvarn;
|
||||
|
||||
/* Free all existing vars first - We aren't retaining anything */
|
||||
for (dvar = driver->variables.first; dvar; dvar = dvarn) {
|
||||
dvarn = dvar->next;
|
||||
driver_free_variable_ex(driver, dvar);
|
||||
}
|
||||
|
||||
BLI_listbase_clear(&driver->variables);
|
||||
}
|
||||
|
||||
/* 3) Add new vars */
|
||||
if (driver->variables.last) {
|
||||
DriverVar *last = driver->variables.last;
|
||||
DriverVar *first = tmp_list.first;
|
||||
|
||||
last->next = first;
|
||||
first->prev = last;
|
||||
|
||||
driver->variables.last = tmp_list.last;
|
||||
}
|
||||
else {
|
||||
driver->variables.first = tmp_list.first;
|
||||
driver->variables.last = tmp_list.last;
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
/* since driver variables are cached, the expression needs re-compiling too */
|
||||
if (driver->type == DRIVER_TYPE_PYTHON)
|
||||
driver->flag |= DRIVER_FLAG_RENAMEVAR;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ************************************************** */
|
||||
/* UI-Button Interface */
|
||||
|
||||
|
@ -291,6 +291,9 @@ bool ANIM_remove_driver(struct ReportList *reports, struct ID *id, const char rn
|
||||
/* Clear copy-paste buffer for drivers */
|
||||
void ANIM_drivers_copybuf_free(void);
|
||||
|
||||
/* Clear copy-paste buffer for driver variable sets */
|
||||
void ANIM_driver_vars_copybuf_free(void);
|
||||
|
||||
/* -------- */
|
||||
|
||||
/* Returns whether there is a driver in the copy/paste buffer to paste */
|
||||
@ -307,6 +310,17 @@ bool ANIM_copy_driver(struct ReportList *reports, struct ID *id, const char rna_
|
||||
*/
|
||||
bool ANIM_paste_driver(struct ReportList *reports, struct ID *id, const char rna_path[], int array_index, short flag);
|
||||
|
||||
/* -------- */
|
||||
|
||||
/* Checks if there are driver variables in the copy/paste buffer */
|
||||
bool ANIM_driver_vars_can_paste(void);
|
||||
|
||||
/* Copy the given driver's variables to the buffer */
|
||||
bool ANIM_driver_vars_copy(struct ReportList *reports, struct FCurve *fcu);
|
||||
|
||||
/* Paste the variables in the buffer to the given FCurve */
|
||||
bool ANIM_driver_vars_paste(struct ReportList *reports, struct FCurve *fcu, bool replace);
|
||||
|
||||
/* ************ Auto-Keyframing ********************** */
|
||||
/* Notes:
|
||||
* - All the defines for this (User-Pref settings and Per-Scene settings)
|
||||
|
@ -516,7 +516,7 @@ static void driver_delete_var_cb(bContext *UNUSED(C), void *driver_v, void *dvar
|
||||
DriverVar *dvar = (DriverVar *)dvar_v;
|
||||
|
||||
/* remove the active variable */
|
||||
driver_free_variable(driver, dvar);
|
||||
driver_free_variable_ex(driver, dvar);
|
||||
}
|
||||
|
||||
/* callback to report why a driver variable is invalid */
|
||||
@ -825,14 +825,26 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
|
||||
uiItemL(row, valBuf, ICON_NONE);
|
||||
}
|
||||
|
||||
/* add driver variables */
|
||||
col = uiLayoutColumn(pa->layout, false);
|
||||
block = uiLayoutGetBlock(col);
|
||||
but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMIN, IFACE_("Add Variable"),
|
||||
0, 0, 10 * UI_UNIT_X, UI_UNIT_Y,
|
||||
NULL, 0.0, 0.0, 0, 0,
|
||||
TIP_("Driver variables ensure that all dependencies will be accounted for and that drivers will update correctly"));
|
||||
UI_but_func_set(but, driver_add_var_cb, driver, NULL);
|
||||
/* add/copy/paste driver variables */
|
||||
{
|
||||
uiLayout *row;
|
||||
|
||||
/* add driver variable */
|
||||
row = uiLayoutRow(pa->layout, false);
|
||||
block = uiLayoutGetBlock(row);
|
||||
but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMIN, IFACE_("Add Variable"),
|
||||
0, 0, 10 * UI_UNIT_X, UI_UNIT_Y,
|
||||
NULL, 0.0, 0.0, 0, 0,
|
||||
TIP_("Driver variables ensure that all dependencies will be accounted for and that drivers will update correctly"));
|
||||
UI_but_func_set(but, driver_add_var_cb, driver, NULL);
|
||||
|
||||
/* copy/paste (as sub-row) */
|
||||
row = uiLayoutRow(row, true);
|
||||
block = uiLayoutGetBlock(row);
|
||||
|
||||
uiItemO(row, "", ICON_COPYDOWN, "GRAPH_OT_driver_variables_copy");
|
||||
uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_driver_variables_paste");
|
||||
}
|
||||
|
||||
/* loop over targets, drawing them */
|
||||
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
|
||||
|
@ -54,6 +54,7 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_nla.h"
|
||||
@ -2610,3 +2611,130 @@ void GRAPH_OT_fmodifier_paste(wmOperatorType *ot)
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Drivers */
|
||||
|
||||
/* ******************** Copy Driver Vars Operator *********************** */
|
||||
|
||||
static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bAnimContext ac;
|
||||
bAnimListElem *ale;
|
||||
bool ok = false;
|
||||
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* clear buffer first */
|
||||
ANIM_driver_vars_copybuf_free();
|
||||
|
||||
/* get the active F-Curve */
|
||||
ale = get_active_fcurve_channel(&ac);
|
||||
|
||||
/* if this exists, call the copy driver vars API function */
|
||||
if (ale && ale->data) {
|
||||
FCurve *fcu = (FCurve *)ale->data;
|
||||
|
||||
ok = ANIM_driver_vars_copy(op->reports, fcu);
|
||||
|
||||
/* free temp data now */
|
||||
MEM_freeN(ale);
|
||||
}
|
||||
|
||||
/* successful or not? */
|
||||
if (ok == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
else
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GRAPH_OT_driver_variables_copy(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Copy Driver Variables";
|
||||
ot->idname = "GRAPH_OT_driver_variables_copy";
|
||||
ot->description = "Copy the driver variables of the active F-Curve";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = graph_driver_vars_copy_exec;
|
||||
ot->poll = graphop_active_fcurve_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ******************** Paste Driver Vars Operator *********************** */
|
||||
|
||||
static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bAnimContext ac;
|
||||
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
|
||||
const bool replace = RNA_boolean_get(op->ptr, "replace");
|
||||
bool ok = false;
|
||||
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* filter data */
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
|
||||
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
||||
|
||||
/* paste modifiers */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
FCurve *fcu = (FCurve *)ale->data;
|
||||
bool paste_ok;
|
||||
|
||||
printf("pasting vars to %p -> %p\n", fcu, fcu->driver);
|
||||
paste_ok = ANIM_driver_vars_paste(op->reports, fcu, replace);
|
||||
|
||||
if (paste_ok) {
|
||||
//ale->update |= ANIM_UPDATE_DEPS;
|
||||
printf("now we have: %d vars\n", BLI_listbase_count(&fcu->driver->variables));
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: something causes a crash after adding the copies (to empty list), if we do an update immediately
|
||||
if (ok) {
|
||||
DAG_relations_tag_update(CTX_data_main(C));
|
||||
//ANIM_animdata_update(&ac, &anim_data);
|
||||
}
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
|
||||
/* successful or not? */
|
||||
if (ok) {
|
||||
/* set notifier that keyframes have changed */
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, CTX_data_scene(C));
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
else {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
void GRAPH_OT_driver_variables_paste(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Paste Driver Variables";
|
||||
ot->idname = "GRAPH_OT_driver_variables_paste";
|
||||
ot->description = "Add copied driver variables to the active driver";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = graph_driver_vars_paste_exec;
|
||||
ot->poll = graphop_active_fcurve_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_boolean(ot->srna, "only_active", true, "Only Active", "Only paste F-Modifiers on active F-Curve");
|
||||
RNA_def_boolean(ot->srna, "replace", false, "Replace Existing",
|
||||
"Replace existing F-Modifiers, instead of just appending to the end of the existing list");
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
@ -148,6 +148,11 @@ void GRAPH_OT_fmodifier_paste(struct wmOperatorType *ot);
|
||||
|
||||
/* ----------- */
|
||||
|
||||
void GRAPH_OT_driver_variables_copy(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_driver_variables_paste(struct wmOperatorType *ot);
|
||||
|
||||
/* ----------- */
|
||||
|
||||
void GRAPH_OT_ghost_curves_create(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_ghost_curves_clear(struct wmOperatorType *ot);
|
||||
|
||||
|
@ -458,6 +458,10 @@ void graphedit_operatortypes(void)
|
||||
WM_operatortype_append(GRAPH_OT_fmodifier_add);
|
||||
WM_operatortype_append(GRAPH_OT_fmodifier_copy);
|
||||
WM_operatortype_append(GRAPH_OT_fmodifier_paste);
|
||||
|
||||
/* Drivers */
|
||||
WM_operatortype_append(GRAPH_OT_driver_variables_copy);
|
||||
WM_operatortype_append(GRAPH_OT_driver_variables_paste);
|
||||
}
|
||||
|
||||
void ED_operatormacros_graph(void)
|
||||
|
@ -300,7 +300,7 @@ static void rna_Driver_remove_variable(ChannelDriver *driver, ReportList *report
|
||||
return;
|
||||
}
|
||||
|
||||
driver_free_variable(driver, dvar);
|
||||
driver_free_variable_ex(driver, dvar);
|
||||
RNA_POINTER_INVALIDATE(dvar_ptr);
|
||||
}
|
||||
|
||||
|
@ -510,6 +510,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
|
||||
// free_matcopybuf();
|
||||
ANIM_fcurves_copybuf_free();
|
||||
ANIM_drivers_copybuf_free();
|
||||
ANIM_driver_vars_copybuf_free();
|
||||
ANIM_fmodifiers_copybuf_free();
|
||||
ED_gpencil_anim_copybuf_free();
|
||||
ED_gpencil_strokes_copybuf_free();
|
||||
|
Loading…
Reference in New Issue
Block a user