static int add_driver_button_exec(bContext *C, wmOperator *op) { PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; int success = 0; int index; const bool all = RNA_boolean_get(op->ptr, "all"); /* try to create driver using property retrieved from UI */ uiContextActiveProperty(C, &ptr, &prop, &index); if (all) index = -1; if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { char *path = get_driver_path_hack(C, &ptr, prop); short flags = CREATEDRIVER_WITH_DEFAULT_DVAR; if (path) { success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON); MEM_freeN(path); } } if (success) { /* send updates */ uiContextAnimUpdate(C); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX } return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; }
/* Wrapper for creating a driver without knowing what the targets will be yet * (i.e. "manual/add later"). */ static int add_driver_button_none(bContext *C, wmOperator *op, short mapping_type) { PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; int index; int success = 0; UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (mapping_type == CREATEDRIVER_MAPPING_NONE_ALL) { index = -1; } if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); short flags = CREATEDRIVER_WITH_DEFAULT_DVAR; if (path) { success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON); MEM_freeN(path); } } if (success) { /* send updates */ UI_context_update_anim_flag(C); DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX return OPERATOR_FINISHED; } else { return OPERATOR_CANCELLED; } }
static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; int index; /* try to find driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { /* 1) Create a new "empty" driver for this property */ char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); short flags = CREATEDRIVER_WITH_DEFAULT_DVAR; short success = 0; if (path) { success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON); MEM_freeN(path); } if (success) { /* send updates */ UI_context_update_anim_flag(C); DEG_id_tag_update(ptr.id.data, ID_RECALC_COPY_ON_WRITE); DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); } /* 2) Show editing panel for setting up this driver */ /* TODO: Use a different one from the editing popever, so we can have the single/all toggle? */ UI_popover_panel_invoke(C, "GRAPH_PT_drivers_popover", true, op->reports); } return OPERATOR_INTERFACE; }
PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args) { const char *path, *path_full; int index = -1; PYRNA_STRUCT_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "s|i:driver_add", &path, &index)) return NULL; if (pyrna_struct_anim_args_parse(&self->ptr, "bpy_struct.driver_add():", path, &path_full, &index) == -1) { return NULL; } else { PyObject *ret = NULL; ReportList reports; int result; BKE_reports_init(&reports, RPT_STORE); result = ANIM_add_driver(&reports, (ID *)self->ptr.id.data, path_full, index, CREATEDRIVER_WITH_FMODIFIER, DRIVER_TYPE_PYTHON); if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) return NULL; if (result) { ID *id = self->ptr.id.data; AnimData *adt = BKE_animdata_from_id(id); FCurve *fcu; PointerRNA tptr; if (index == -1) { /* all, use a list */ int i = 0; ret = PyList_New(0); while ((fcu = list_find_fcurve(&adt->drivers, path_full, i++))) { RNA_pointer_create(id, &RNA_FCurve, fcu, &tptr); PyList_APPEND(ret, pyrna_struct_CreatePyObject(&tptr)); } } else { fcu = list_find_fcurve(&adt->drivers, path_full, index); RNA_pointer_create(id, &RNA_FCurve, fcu, &tptr); ret = pyrna_struct_CreatePyObject(&tptr); } WM_event_add_notifier(BPy_GetContext(), NC_ANIMATION | ND_FCURVES_ORDER, NULL); } else { /* XXX, should be handled by reports, */ PyErr_SetString(PyExc_TypeError, "bpy_struct.driver_add(): failed because of an internal error"); return NULL; } MEM_freeN((void *)path_full); return ret; } }
/* Main Driver Management API calls: * Add a new driver for the specified property on the given ID block, * and make it be driven by the specified target. * * This is intended to be used in conjunction with a modal "eyedropper" * for picking the variable that is going to be used to drive this one. * * - flag: eCreateDriverFlags * - driver_type: eDriver_Types * - mapping_type: eCreateDriver_MappingTypes */ int ANIM_add_driver_with_target(ReportList *reports, ID *dst_id, const char dst_path[], int dst_index, ID *src_id, const char src_path[], int src_index, short flag, int driver_type, short mapping_type) { PointerRNA id_ptr, ptr; PropertyRNA *prop; PointerRNA id_ptr2, ptr2; PropertyRNA *prop2; int done_tot = 0; /* validate pointers first - exit if failure */ RNA_id_pointer_create(dst_id, &id_ptr); if (RNA_path_resolve_property(&id_ptr, dst_path, &ptr, &prop) == false) { BKE_reportf( reports, RPT_ERROR, "Could not add driver, as RNA path is invalid for the given ID (ID = %s, path = %s)", dst_id->name, dst_path); return 0; } RNA_id_pointer_create(src_id, &id_ptr2); if ((RNA_path_resolve_property(&id_ptr2, src_path, &ptr2, &prop2) == false) || (mapping_type == CREATEDRIVER_MAPPING_NONE)) { /* No target - So, fall back to default method for adding a "simple" driver normally */ return ANIM_add_driver( reports, dst_id, dst_path, dst_index, flag | CREATEDRIVER_WITH_DEFAULT_DVAR, driver_type); } /* handle curve-property mappings based on mapping_type */ switch (mapping_type) { case CREATEDRIVER_MAPPING_N_N: /* N-N - Try to match as much as possible, * then use the first one */ { /* Use the shorter of the two (to avoid out of bounds access) */ int dst_len = (RNA_property_array_check(prop)) ? RNA_property_array_length(&ptr, prop) : 1; int src_len = (RNA_property_array_check(prop)) ? RNA_property_array_length(&ptr2, prop2) : 1; int len = MIN2(dst_len, src_len); int i; for (i = 0; i < len; i++) { done_tot += add_driver_with_target(reports, dst_id, dst_path, i, src_id, src_path, i, &ptr, prop, &ptr2, prop2, flag, driver_type); } break; } case CREATEDRIVER_MAPPING_1_N: /* 1-N - Specified target index for all */ default: { int len = (RNA_property_array_check(prop)) ? RNA_property_array_length(&ptr, prop) : 1; int i; for (i = 0; i < len; i++) { done_tot += add_driver_with_target(reports, dst_id, dst_path, i, src_id, src_path, src_index, &ptr, prop, &ptr2, prop2, flag, driver_type); } break; } case CREATEDRIVER_MAPPING_1_1: /* 1-1 - Use the specified index (unless -1) */ { done_tot = add_driver_with_target(reports, dst_id, dst_path, dst_index, src_id, src_path, src_index, &ptr, prop, &ptr2, prop2, flag, driver_type); break; } } /* done */ return done_tot; }
/* Recursively iterate over tree, finding and working on selected items */ static void do_outliner_drivers_editop(SpaceOops *soops, ListBase *tree, ReportList *reports, short mode) { TreeElement *te; TreeStoreElem *tselem; for (te= tree->first; te; te=te->next) { tselem= TREESTORE(te); /* if item is selected, perform operation */ if (tselem->flag & TSE_SELECTED) { ID *id= NULL; char *path= NULL; int array_index= 0; short flag= 0; short groupmode= KSP_GROUP_KSNAME; /* check if RNA-property described by this selected element is an animateable prop */ if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) { /* get id + path + index info from the selected element */ tree_element_to_path(soops, te, tselem, &id, &path, &array_index, &flag, &groupmode); } /* only if ID and path were set, should we perform any actions */ if (id && path) { short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; int arraylen = 1; /* array checks */ if (flag & KSP_FLAG_WHOLE_ARRAY) { /* entire array was selected, so add drivers for all */ arraylen= RNA_property_array_length(&te->rnaptr, te->directdata); } else arraylen= array_index; /* we should do at least one step */ if (arraylen == array_index) arraylen++; /* for each array element we should affect, add driver */ for (; array_index < arraylen; array_index++) { /* action depends on mode */ switch (mode) { case DRIVERS_EDITMODE_ADD: { /* add a new driver with the information obtained (only if valid) */ ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); } break; case DRIVERS_EDITMODE_REMOVE: { /* remove driver matching the information obtained (only if valid) */ ANIM_remove_driver(reports, id, path, array_index, dflags); } break; } } /* free path, since it had to be generated */ MEM_freeN(path); } } /* go over sub-tree */ if ((tselem->flag & TSE_CLOSED)==0) do_outliner_drivers_editop(soops, &te->subtree, reports, mode); } }