Drivers Editing: Added "Copy/Paste" buttons beside "Add Variable" for copying all...
authorJoshua Leung <aligorith@gmail.com>
Fri, 15 Apr 2016 08:04:07 +0000 (20:04 +1200)
committerJoshua Leung <aligorith@gmail.com>
Fri, 15 Apr 2016 08:05:09 +0000 (20:05 +1200)
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!

source/blender/blenkernel/BKE_fcurve.h
source/blender/blenkernel/intern/fcurve.c
source/blender/editors/animation/CMakeLists.txt
source/blender/editors/animation/drivers.c
source/blender/editors/include/ED_keyframing.h
source/blender/editors/space_graph/graph_buttons.c
source/blender/editors/space_graph/graph_edit.c
source/blender/editors/space_graph/graph_intern.h
source/blender/editors/space_graph/graph_ops.c
source/blender/makesrna/intern/rna_fcurve.c
source/blender/windowmanager/intern/wm_init_exit.c

index 664338214bf61a86485d70e0350b7fcf4432c1c2..2022d11d5089cdf2288b7a8b3041172e01946ca0 100644 (file)
@@ -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);
index 11b1bbd64acf3f45a2356f92d3614b41b66dee6c..a2b5a05feac511db3e7d235d126fdd7b5a510667 100644 (file)
@@ -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;
index 473b0e6bc5afd885e61b3b278177081352cb5b35..1bf1bb2a474b6ce672f0a1505317e92a3b8bdbfe 100644 (file)
@@ -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}")
index 4ddf37aa3a16e5eae100dc0997f7e6fc29fc6924..27bed69361902ba01de99e5348960118756bfebf 100644 (file)
@@ -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 */
 
index 9a411975880392e599e2b6888ec252b114f84597..81e2558e765f0f658983636a6bf50af440090d38 100644 (file)
@@ -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)
index f8b081eda208264a070fe63e4908509bf214a573..5846a439b3c0ca9210d409cce68170c96f6956de 100644 (file)
@@ -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) {
index dd2ec2401a517b26e16bfbb19db23863bc1bc33c..a54b7e141f38b8749830819243c7c4ec36081b66 100644 (file)
@@ -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");
+}
+
+/* ************************************************************************** */
index a478a86a5e2a8058bba38fae865b692e41967a76..b6b711e129f1213e913cf0ae2340218ebbb6e0c9 100644 (file)
@@ -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);
 
index 59215531ac088cdc7c54811603e17f7bbd8e8ad8..7ffa82500676e89303d99c0a3d62d00c72aef5e3 100644 (file)
@@ -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)
index b765ca10eec7515d5abb68bad8d6b8b0d4c1dc2e..32008640930101d200de8ce794782c4ea8d38762 100644 (file)
@@ -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);
 }
 
index 3d981b66f9c07b0b31fce372d8cf7a879ce33b6d..e08d0afb906be5651d4e089d41d1d854b30e2708 100644 (file)
@@ -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();