static int paste_driver_button_exec(bContext *C, wmOperator *op) { PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; short success = 0; int index; /* try to create driver using property retrieved from UI */ uiContextActiveProperty(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { char *path = get_driver_path_hack(C, &ptr, prop); if (path) { /* only copy the driver for the button that this was involved for */ success = ANIM_paste_driver(op->reports, ptr.id.data, path, index, 0); uiContextAnimUpdate(C); MEM_freeN(path); } } /* since we're just copying, we don't really need to do anything else...*/ 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_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; }
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; }
static int paste_driver_button_exec(bContext *C, wmOperator *op) { PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; short success = 0; int index; /* try to create 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)) { char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); if (path) { /* only copy the driver for the button that this was involved for */ success = ANIM_paste_driver(op->reports, ptr.id.data, path, index, 0); UI_context_update_anim_flag(C); DEG_relations_tag_update(CTX_data_main(C)); DEG_id_tag_update(ptr.id.data, ID_RECALC_ANIMATION); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); // XXX MEM_freeN(path); } } /* since we're just copying, we don't really need to do anything else...*/ return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; }
/* Recursively iterate over tree, finding and working on selected items */ static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, 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) { /* action depends on mode */ switch (mode) { case KEYINGSET_EDITMODE_ADD: { /* add a new path with the information obtained (only if valid) */ // TODO: what do we do with group name? for now, we don't supply one, and just let this use the KeyingSet name BKE_keyingset_add_path(ks, id, NULL, path, array_index, flag, groupmode); ks->active_path= BLI_countlist(&ks->paths); } break; case KEYINGSET_EDITMODE_REMOVE: { /* find the relevant path, then remove it from the KeyingSet */ KS_Path *ksp= BKE_keyingset_find_path(ks, id, NULL, path, array_index, groupmode); if (ksp) { /* free path's data */ BKE_keyingset_free_path(ks, ksp); ks->active_path= 0; } } break; } /* free path, since it had to be generated */ MEM_freeN(path); } } /* go over sub-tree */ if ((tselem->flag & TSE_CLOSED)==0) do_outliner_keyingset_editop(soops, ks, &te->subtree, mode); } }
static int add_driver_button_poll(bContext *C) { PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; int index; /* this operator can only run if there's a property button active, and it can be animated */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); return (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)); }
static FCurve *ui_but_get_fcurve(uiBut *but, bAction **action, int *driven) { FCurve *fcu= NULL; *driven= 0; /* there must be some RNA-pointer + property combo for this button */ if(but->rnaprop && but->rnapoin.id.data && RNA_property_animateable(&but->rnapoin, but->rnaprop)) { AnimData *adt= BKE_animdata_from_id(but->rnapoin.id.data); char *path; if(adt) { if((adt->action && adt->action->curves.first) || (adt->drivers.first)) { /* XXX this function call can become a performance bottleneck */ path= RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); if(path) { /* animation takes priority over drivers */ if(adt->action && adt->action->curves.first) fcu= list_find_fcurve(&adt->action->curves, path, but->rnaindex); /* if not animated, check if driven */ if(!fcu && (adt->drivers.first)) { fcu= list_find_fcurve(&adt->drivers, path, but->rnaindex); if(fcu) *driven= 1; } if(fcu && action) *action= adt->action; MEM_freeN(path); } } } } return fcu; }
static bool driverdropper_init(bContext *C, wmOperator *op) { DriverDropper *ddr; uiBut *but; op->customdata = ddr = MEM_callocN(sizeof(DriverDropper), "DriverDropper"); but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &ddr->index); if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) || (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || (RNA_property_animateable(&ddr->ptr, ddr->prop) == false) || (but->flag & UI_BUT_DRIVEN)) { return false; } return true; }
static bool add_driver_button_poll(bContext *C) { PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; int index; bool driven, special; /* this operator can only run if there's a property button active, and it can be animated */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (!(ptr.id.data && ptr.data && prop)) { return false; } if (!RNA_property_animateable(&ptr, prop)) { return false; } /* Don't do anything if there is an fcurve for animation without a driver. */ FCurve *fcu = rna_get_fcurve_context_ui(C, &ptr, prop, index, NULL, NULL, &driven, &special); return (fcu == NULL || fcu->driver); }
/* Filtering callback for driver mapping types enum */ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C, PointerRNA *UNUSED(owner_ptr), PropertyRNA *UNUSED(owner_prop), bool *r_free) { EnumPropertyItem *input = prop_driver_create_mapping_types; EnumPropertyItem *item = NULL; PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; int index; int totitem = 0; if (!C) { /* needed for docs */ return prop_driver_create_mapping_types; } UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { const bool is_array = RNA_property_array_check(prop); while (input->identifier) { if (ELEM(input->value, CREATEDRIVER_MAPPING_1_1, CREATEDRIVER_MAPPING_NONE) || (is_array)) { RNA_enum_item_add(&item, &totitem, input); } input++; } } else { /* We need at least this one! */ RNA_enum_items_add_value(&item, &totitem, input, CREATEDRIVER_MAPPING_NONE); } RNA_enum_item_end(&item, &totitem); *r_free = true; return item; }
static int add_keyingset_button_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); KeyingSet *ks = NULL; PropertyRNA *prop = NULL; PointerRNA ptr = {{NULL}}; char *path = NULL; short success = 0; int index = 0, pflag = 0; const bool all = RNA_boolean_get(op->ptr, "all"); /* verify the Keying Set to use: * - use the active one for now (more control over this can be added later) * - add a new one if it doesn't exist */ if (scene->active_keyingset == 0) { short flag = 0, keyingflag = 0; /* validate flags * - absolute KeyingSets should be created by default */ flag |= KEYINGSET_ABSOLUTE; keyingflag |= ANIM_get_keyframing_flags(scene, 0); if (IS_AUTOKEY_FLAG(scene, XYZ2RGB)) keyingflag |= INSERTKEY_XYZ2RGB; /* call the API func, and set the active keyingset index */ ks = BKE_keyingset_add(&scene->keyingsets, "ButtonKeyingSet", "Button Keying Set", flag, keyingflag); scene->active_keyingset = BLI_countlist(&scene->keyingsets); } else if (scene->active_keyingset < 0) { BKE_report(op->reports, RPT_ERROR, "Cannot add property to built in keying set"); return OPERATOR_CANCELLED; } else { ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1); } /* try to add to keyingset using property retrieved from UI */ uiContextActiveProperty(C, &ptr, &prop, &index); /* check if property is able to be added */ if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { path = RNA_path_from_ID_to_property(&ptr, prop); if (path) { /* set flags */ if (all) { pflag |= KSP_FLAG_WHOLE_ARRAY; /* we need to set the index for this to 0, even though it may break in some cases, this is * necessary if we want the entire array for most cases to get included without the user * having to worry about where they clicked */ index = 0; } /* add path to this setting */ BKE_keyingset_add_path(ks, ptr.id.data, NULL, path, index, pflag, KSP_GROUP_KSNAME); ks->active_path = BLI_countlist(&ks->paths); success = 1; /* free the temp path created */ MEM_freeN(path); } } if (success) { /* send updates */ WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, NULL); /* show notification/report header, so that users notice that something changed */ BKE_reportf(op->reports, RPT_INFO, "Property added to Keying Set: '%s'", ks->name); } return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; }
/* for keyframes and drivers */ static int pyrna_struct_anim_args_parse( PointerRNA *ptr, const char *error_prefix, const char *path, const char **path_full, int *index) { const bool is_idbase = RNA_struct_is_ID(ptr->type); PropertyRNA *prop; PointerRNA r_ptr; if (ptr->data == NULL) { PyErr_Format(PyExc_TypeError, "%.200s this struct has no data, can't be animated", error_prefix); return -1; } /* full paths can only be given from ID base */ if (is_idbase) { int r_index = -1; if (RNA_path_resolve_property_full(ptr, path, &r_ptr, &prop, &r_index) == false) { prop = NULL; } else if (r_index != -1) { PyErr_Format(PyExc_ValueError, "%.200s path includes index, must be a separate argument", error_prefix, path); return -1; } else if (ptr->id.data != r_ptr.id.data) { PyErr_Format(PyExc_ValueError, "%.200s path spans ID blocks", error_prefix, path); return -1; } } else { prop = RNA_struct_find_property(ptr, path); r_ptr = *ptr; } if (prop == NULL) { PyErr_Format(PyExc_TypeError, "%.200s property \"%s\" not found", error_prefix, path); return -1; } if (!RNA_property_animateable(&r_ptr, prop)) { PyErr_Format(PyExc_TypeError, "%.200s property \"%s\" not animatable", error_prefix, path); return -1; } if (RNA_property_array_check(prop) == 0) { if ((*index) == -1) { *index = 0; } else { PyErr_Format(PyExc_TypeError, "%.200s index %d was given while property \"%s\" is not an array", error_prefix, *index, path); return -1; } } else { int array_len = RNA_property_array_length(&r_ptr, prop); if ((*index) < -1 || (*index) >= array_len) { PyErr_Format(PyExc_TypeError, "%.200s index out of range \"%s\", given %d, array length is %d", error_prefix, path, *index, array_len); return -1; } } if (is_idbase) { *path_full = BLI_strdup(path); } else { *path_full = RNA_path_from_ID_to_property(&r_ptr, prop); if (*path_full == NULL) { PyErr_Format(PyExc_TypeError, "%.200s could not make path to \"%s\"", error_prefix, path); return -1; } } return 0; }
/* 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); } }
static int add_keyingset_button_exec (bContext *C, wmOperator *op) { Scene *scene= CTX_data_scene(C); KeyingSet *ks = NULL; PropertyRNA *prop= NULL; PointerRNA ptr; char *path = NULL; short success= 0; int index=0, pflag=0; int all= RNA_boolean_get(op->ptr, "all"); /* verify the Keying Set to use: * - use the active one for now (more control over this can be added later) * - add a new one if it doesn't exist */ if (scene->active_keyingset == 0) { short flag=0, keyingflag=0; /* validate flags * - absolute KeyingSets should be created by default */ flag |= KEYINGSET_ABSOLUTE; if (IS_AUTOKEY_FLAG(AUTOMATKEY)) keyingflag |= INSERTKEY_MATRIX; if (IS_AUTOKEY_FLAG(INSERTNEEDED)) keyingflag |= INSERTKEY_NEEDED; /* call the API func, and set the active keyingset index */ ks= BKE_keyingset_add(&scene->keyingsets, "ButtonKeyingSet", flag, keyingflag); scene->active_keyingset= BLI_countlist(&scene->keyingsets); } else ks= BLI_findlink(&scene->keyingsets, scene->active_keyingset-1); /* try to add to keyingset using property retrieved from UI */ memset(&ptr, 0, sizeof(PointerRNA)); uiAnimContextProperty(C, &ptr, &prop, &index); /* check if property is able to be added */ if (ptr.data && prop && RNA_property_animateable(ptr.data, prop)) { path= RNA_path_from_ID_to_property(&ptr, prop); if (path) { /* set flags */ if (all) { pflag |= KSP_FLAG_WHOLE_ARRAY; /* we need to set the index for this to 0, even though it may break in some cases, this is * necessary if we want the entire array for most cases to get included without the user * having to worry about where they clicked */ index= 0; } /* add path to this setting */ BKE_keyingset_add_destination(ks, ptr.id.data, NULL, path, index, pflag, KSP_GROUP_KSNAME); ks->active_path= BLI_countlist(&ks->paths); success= 1; /* free the temp path created */ MEM_freeN(path); } } if (success) { /* send updates */ ED_anim_dag_flush_update(C); /* for now, only send ND_KEYS for KeyingSets */ WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL); } return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED; }