static int node_animation_properties(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; const ListBase *lb; Link *link; PointerRNA ptr; PropertyRNA *prop; /* check to see if any of the node's properties have fcurves */ RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); lb = RNA_struct_type_properties(ptr.type); for (link = lb->first; link; link = link->next) { int driven, len = 1, index; prop = (PropertyRNA *)link; if (RNA_property_array_check(prop)) len = RNA_property_array_length(&ptr, prop); for (index = 0; index < len; index++) { if (rna_get_fcurve(&ptr, prop, index, NULL, &driven)) { nodeUpdate(ntree, node); return 1; } } } /* now check node sockets */ for (sock = node->inputs.first; sock; sock = sock->next) { int driven, len = 1, index; RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); prop = RNA_struct_find_property(&ptr, "default_value"); if (prop) { if (RNA_property_array_check(prop)) len = RNA_property_array_length(&ptr, prop); for (index = 0; index < len; index++) { if (rna_get_fcurve(&ptr, prop, index, NULL, &driven)) { nodeUpdate(ntree, node); return 1; } } } } return 0; }
static bool eyedropper_init(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Eyedropper *eye; op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper"); UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index); if ((eye->ptr.data == NULL) || (eye->prop == NULL) || (RNA_property_editable(&eye->ptr, eye->prop) == false) || (RNA_property_array_length(&eye->ptr, eye->prop) < 3) || (RNA_property_type(eye->prop) != PROP_FLOAT)) { return false; } if (RNA_property_subtype(eye->prop) == PROP_COLOR) { const char *display_device; display_device = scene->display_settings.display_device; eye->display = IMB_colormanagement_display_get_named(display_device); } return true; }
/* get reference value from F-Curve using RNA */ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value) { PointerRNA id_ptr, ptr; PropertyRNA *prop; bool found = false; /* base pointer is always the object -> id_ptr */ RNA_id_pointer_create(&ob->id, &id_ptr); /* resolve the property... */ if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) { if (RNA_property_array_check(prop)) { /* array */ if (fcu->array_index < RNA_property_array_length(&ptr, prop)) { found = true; switch (RNA_property_type(prop)) { case PROP_BOOLEAN: *value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index); break; case PROP_INT: *value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index); break; case PROP_FLOAT: *value = RNA_property_float_get_index(&ptr, prop, fcu->array_index); break; default: found = false; break; } } } else { /* not an array */ found = true; switch (RNA_property_type(prop)) { case PROP_BOOLEAN: *value = (float)RNA_property_boolean_get(&ptr, prop); break; case PROP_INT: *value = (float)RNA_property_int_get(&ptr, prop); break; case PROP_ENUM: *value = (float)RNA_property_enum_get(&ptr, prop); break; case PROP_FLOAT: *value = RNA_property_float_get(&ptr, prop); break; default: found = false; break; } } } return found; }
static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { ARegion *ar = CTX_wm_region(C); uiBut *but = NULL; float color[4]; bool gamma; RNA_float_get_array(op->ptr, "color", color); gamma = RNA_boolean_get(op->ptr, "gamma"); /* find button under mouse, check if it has RNA color property and * if it does copy the data */ but = ui_region_find_active_but(ar); if (but && but->type == UI_BTYPE_COLOR && but->rnaprop) { const int color_len = RNA_property_array_length(&but->rnapoin, but->rnaprop); BLI_assert(color_len <= 4); /* keep alpha channel as-is */ if (color_len == 4) { color[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3); } if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { if (!gamma) { IMB_colormanagement_scene_linear_to_srgb_v3(color); } RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); RNA_property_update(C, &but->rnapoin, but->rnaprop); } else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) { if (gamma) { IMB_colormanagement_srgb_to_scene_linear_v3(color); } RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); RNA_property_update(C, &but->rnapoin, but->rnaprop); } } else { if (gamma) { srgb_to_linearrgb_v3_v3(color, color); } ED_imapaint_bucket_fill(C, color, op); } ED_region_tag_redraw(ar); return OPERATOR_FINISHED; }
static void eyedropper_sample(bContext *C, Eyedropper *eye, int mx, int my) { if(RNA_property_type(eye->prop) == PROP_FLOAT) { const int color_manage = CTX_data_scene(C)->r.color_mgt_flag & R_COLOR_MANAGEMENT; float col[4]; RNA_property_float_get_array(&eye->ptr, eye->prop, col); glReadBuffer(GL_FRONT); glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, col); glReadBuffer(GL_BACK); if (RNA_property_array_length(&eye->ptr, eye->prop) < 3) return; /* convert from screen (srgb) space to linear rgb space */ if (color_manage && RNA_property_subtype(eye->prop) == PROP_COLOR) srgb_to_linearrgb_v3_v3(col, col); RNA_property_float_set_array(&eye->ptr, eye->prop, col); RNA_property_update(C, &eye->ptr, eye->prop); } }
/* Helper func to extract an RNA path from selected tree element * NOTE: the caller must zero-out all values of the pointers that it passes here first, as * this function does not do that yet */ static void tree_element_to_path(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, ID **id, char **path, int *array_index, short *flag, short *UNUSED(groupmode)) { ListBase hierarchy = {NULL, NULL}; LinkData *ld; TreeElement *tem, *temnext, *temsub; TreeStoreElem *tse, *tsenext; PointerRNA *ptr, *nextptr; PropertyRNA *prop; char *newpath=NULL; /* optimise tricks: * - Don't do anything if the selected item is a 'struct', but arrays are allowed */ if (tselem->type == TSE_RNA_STRUCT) return; /* Overview of Algorithm: * 1. Go up the chain of parents until we find the 'root', taking note of the * levels encountered in reverse-order (i.e. items are added to the start of the list * for more convenient looping later) * 2. Walk down the chain, adding from the first ID encountered * (which will become the 'ID' for the KeyingSet Path), and build a * path as we step through the chain */ /* step 1: flatten out hierarchy of parents into a flat chain */ for (tem= te->parent; tem; tem= tem->parent) { ld= MEM_callocN(sizeof(LinkData), "LinkData for tree_element_to_path()"); ld->data= tem; BLI_addhead(&hierarchy, ld); } /* step 2: step down hierarchy building the path (NOTE: addhead in previous loop was needed so that we can loop like this) */ for (ld= hierarchy.first; ld; ld= ld->next) { /* get data */ tem= (TreeElement *)ld->data; tse= TREESTORE(tem); ptr= &tem->rnaptr; prop= tem->directdata; /* check if we're looking for first ID, or appending to path */ if (*id) { /* just 'append' property to path * - to prevent memory leaks, we must write to newpath not path, then free old path + swap them */ if(tse->type == TSE_RNA_PROPERTY) { if(RNA_property_type(prop) == PROP_POINTER) { /* for pointer we just append property name */ newpath= RNA_path_append(*path, ptr, prop, 0, NULL); } else if(RNA_property_type(prop) == PROP_COLLECTION) { char buf[128], *name; temnext= (TreeElement*)(ld->next->data); tsenext= TREESTORE(temnext); nextptr= &temnext->rnaptr; name= RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf)); if(name) { /* if possible, use name as a key in the path */ newpath= RNA_path_append(*path, NULL, prop, 0, name); if(name != buf) MEM_freeN(name); } else { /* otherwise use index */ int index= 0; for(temsub=tem->subtree.first; temsub; temsub=temsub->next, index++) if(temsub == temnext) break; newpath= RNA_path_append(*path, NULL, prop, index, NULL); } ld= ld->next; } } if(newpath) { if (*path) MEM_freeN(*path); *path= newpath; newpath= NULL; } } else { /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */ if (tse->type == TSE_RNA_STRUCT) { /* ptr->data not ptr->id.data seems to be the one we want, since ptr->data is sometimes the owner of this ID? */ if(RNA_struct_is_ID(ptr->type)) { *id= (ID *)ptr->data; /* clear path */ if(*path) { MEM_freeN(*path); path= NULL; } } } } } /* step 3: if we've got an ID, add the current item to the path */ if (*id) { /* add the active property to the path */ ptr= &te->rnaptr; prop= te->directdata; /* array checks */ if (tselem->type == TSE_RNA_ARRAY_ELEM) { /* item is part of an array, so must set the array_index */ *array_index= te->index; } else if (RNA_property_array_length(ptr, prop)) { /* entire array was selected, so keyframe all */ *flag |= KSP_FLAG_WHOLE_ARRAY; } /* path */ newpath= RNA_path_append(*path, NULL, prop, 0, NULL); if (*path) MEM_freeN(*path); *path= newpath; } /* free temp data */ BLI_freelistN(&hierarchy); }
/* Given a KeyingSet and context info (if required), modify keyframes for the channels specified * by the KeyingSet. This takes into account many of the different combinations of using KeyingSets. * Returns the number of channels that keyframes were added to */ int ANIM_apply_keyingset(bContext *C, ListBase *dsources, bAction *act, KeyingSet *ks, short mode, float cfra) { Scene *scene = CTX_data_scene(C); ReportList *reports = CTX_wm_reports(C); KS_Path *ksp; int kflag = 0, success = 0; char *groupname = NULL; /* sanity checks */ if (ks == NULL) return 0; /* get flags to use */ if (mode == MODIFYKEY_MODE_INSERT) { /* use KeyingSet's flags as base */ kflag = ks->keyingflag; /* suppliment with info from the context */ kflag |= ANIM_get_keyframing_flags(scene, 1); } else if (mode == MODIFYKEY_MODE_DELETE) kflag = 0; /* if relative Keying Sets, poll and build up the paths */ success = ANIM_validate_keyingset(C, dsources, ks); if (success != 0) { /* return error code if failed */ return success; } /* apply the paths as specified in the KeyingSet now */ for (ksp = ks->paths.first; ksp; ksp = ksp->next) { int arraylen, i; short kflag2; /* skip path if no ID pointer is specified */ if (ksp->id == NULL) { BKE_reportf(reports, RPT_WARNING, "Skipping path in keying set, as it has no ID (KS = '%s', path = '%s[%d]')", ks->name, ksp->rna_path, ksp->array_index); continue; } /* since keying settings can be defined on the paths too, extend the path before using it */ kflag2 = (kflag | ksp->keyingflag); /* get pointer to name of group to add channels to */ if (ksp->groupmode == KSP_GROUP_NONE) groupname = NULL; else if (ksp->groupmode == KSP_GROUP_KSNAME) groupname = ks->name; else groupname = ksp->group; /* init arraylen and i - arraylen should be greater than i so that * normal non-array entries get keyframed correctly */ i = ksp->array_index; arraylen = i; /* get length of array if whole array option is enabled */ if (ksp->flag & KSP_FLAG_WHOLE_ARRAY) { PointerRNA id_ptr, ptr; PropertyRNA *prop; RNA_id_pointer_create(ksp->id, &id_ptr); if (RNA_path_resolve_property(&id_ptr, ksp->rna_path, &ptr, &prop)) arraylen = RNA_property_array_length(&ptr, prop); } /* we should do at least one step */ if (arraylen == i) arraylen++; /* for each possible index, perform operation * - assume that arraylen is greater than index */ for (; i < arraylen; i++) { /* action to take depends on mode */ if (mode == MODIFYKEY_MODE_INSERT) success += insert_keyframe(reports, ksp->id, act, groupname, ksp->rna_path, i, cfra, kflag2); else if (mode == MODIFYKEY_MODE_DELETE) success += delete_keyframe(reports, ksp->id, act, groupname, ksp->rna_path, i, cfra, kflag2); } /* set recalc-flags */ switch (GS(ksp->id->name)) { case ID_OB: /* Object (or Object-Related) Keyframes */ { Object *ob = (Object *)ksp->id; // XXX: only object transforms? DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); break; } } /* send notifiers for updates (this doesn't require context to work!) */ WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } /* return the number of channels successfully affected */ return success; }
/* Main Driver Management API calls: * Add a new driver for the specified property on the given ID block */ int ANIM_add_driver(ReportList *reports, ID *id, const char rna_path[], int array_index, short flag, int type) { PointerRNA id_ptr, ptr; PropertyRNA *prop; FCurve *fcu; int array_index_max; int done_tot = 0; /* validate pointer first - exit if failure */ RNA_id_pointer_create(id, &id_ptr); if (RNA_path_resolve_property(&id_ptr, rna_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)", id->name, rna_path); return 0; } /* key entire array convenience method */ if (array_index == -1) { array_index_max = RNA_property_array_length(&ptr, prop); array_index = 0; } else array_index_max = array_index; /* maximum index should be greater than the start index */ if (array_index == array_index_max) array_index_max += 1; /* will only loop once unless the array index was -1 */ for (; array_index < array_index_max; array_index++) { short add_mode = (flag & CREATEDRIVER_WITH_FMODIFIER) ? 2 : 1; /* create F-Curve with Driver */ fcu = verify_driver_fcurve(id, rna_path, array_index, add_mode); if (fcu && fcu->driver) { ChannelDriver *driver = fcu->driver; /* set the type of the driver */ driver->type = type; /* creating drivers for buttons will create the driver(s) with type * "scripted expression" so that their values won't be lost immediately, * so here we copy those values over to the driver's expression */ if (type == DRIVER_TYPE_PYTHON) { PropertyType proptype = RNA_property_type(prop); int array = RNA_property_array_length(&ptr, prop); char *expression = driver->expression; int val, maxlen = sizeof(driver->expression); float fval; if (proptype == PROP_BOOLEAN) { if (!array) val = RNA_property_boolean_get(&ptr, prop); else val = RNA_property_boolean_get_index(&ptr, prop, array_index); BLI_strncpy(expression, (val) ? "True" : "False", maxlen); } else if (proptype == PROP_INT) { if (!array) val = RNA_property_int_get(&ptr, prop); else val = RNA_property_int_get_index(&ptr, prop, array_index); BLI_snprintf(expression, maxlen, "%d", val); } else if (proptype == PROP_FLOAT) { if (!array) fval = RNA_property_float_get(&ptr, prop); else fval = RNA_property_float_get_index(&ptr, prop, array_index); BLI_snprintf(expression, maxlen, "%.3f", fval); } } /* for easier setup of drivers from UI, a driver variable should be * added if flag is set (UI calls only) */ if (flag & CREATEDRIVER_WITH_DEFAULT_DVAR) { /* assume that users will mostly want this to be of type "Transform Channel" too, * since this allows the easiest setting up of common rig components */ DriverVar *dvar = driver_add_new_variable(driver); driver_change_variable_type(dvar, DVAR_TYPE_TRANSFORM_CHAN); } } /* set the done status */ done_tot += (fcu != NULL); } /* done */ return done_tot; }
/* Write into "name" buffer, the name of the property (retrieved using RNA from the curve's settings), * and return the icon used for the struct that this property refers to * WARNING: name buffer we're writing to cannot exceed 256 chars (check anim_channels_defines.c for details) */ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) { int icon = 0; /* sanity checks */ if (name == NULL) return icon; else if (ELEM3(NULL, id, fcu, fcu->rna_path)) { if (fcu == NULL) strcpy(name, "<invalid>"); else if (fcu->rna_path == NULL) strcpy(name, "<no path>"); else /* id == NULL */ BLI_snprintf(name, 256, "%s[%d]", fcu->rna_path, fcu->array_index); } else { PointerRNA id_ptr, ptr; PropertyRNA *prop; /* get RNA pointer, and resolve the path */ RNA_id_pointer_create(id, &id_ptr); /* try to resolve the path */ if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop)) { const char *structname = NULL, *propname = NULL; char arrayindbuf[16]; const char *arrayname = NULL; short free_structname = 0; /* For now, name will consist of 3 parts: struct-name, property name, array index * There are several options possible: * 1) <struct-name>.<property-name>.<array-index> * i.e. Bone1.Location.X, or Object.Location.X * 2) <array-index> <property-name> (<struct name>) * i.e. X Location (Bone1), or X Location (Object) * * Currently, option 2 is in use, to try and make it easier to quickly identify F-Curves (it does have * problems with looking rather odd though). Option 1 is better in terms of revealing a consistent sense of * hierarchy though, which isn't so clear with option 2. */ /* for structname * - as base, we use a custom name from the structs if one is available * - however, if we're showing subdata of bones (probably there will be other exceptions later) * need to include that info too since it gets confusing otherwise * - if a pointer just refers to the ID-block, then don't repeat this info * since this just introduces clutter */ if (strstr(fcu->rna_path, "bones") && strstr(fcu->rna_path, "constraints")) { /* perform string 'chopping' to get "Bone Name : Constraint Name" */ char *pchanName = BLI_getQuotedStr(fcu->rna_path, "bones["); char *constName = BLI_getQuotedStr(fcu->rna_path, "constraints["); /* assemble the string to display in the UI... */ structname = BLI_sprintfN("%s : %s", pchanName, constName); free_structname = 1; /* free the temp names */ if (pchanName) MEM_freeN(pchanName); if (constName) MEM_freeN(constName); } else if (ptr.data != ptr.id.data) { PropertyRNA *nameprop = RNA_struct_name_property(ptr.type); if (nameprop) { /* this gets a string which will need to be freed */ structname = RNA_property_string_get_alloc(&ptr, nameprop, NULL, 0, NULL); free_structname = 1; } else structname = RNA_struct_ui_name(ptr.type); } /* Property Name is straightforward */ propname = RNA_property_ui_name(prop); /* Array Index - only if applicable */ if (RNA_property_array_length(&ptr, prop)) { char c = RNA_property_array_item_char(prop, fcu->array_index); /* we need to write the index to a temp buffer (in py syntax) */ if (c) BLI_snprintf(arrayindbuf, sizeof(arrayindbuf), "%c ", c); else BLI_snprintf(arrayindbuf, sizeof(arrayindbuf), "[%d]", fcu->array_index); arrayname = &arrayindbuf[0]; } else { /* no array index */ arrayname = ""; } /* putting this all together into the buffer */ // XXX we need to check for invalid names... // XXX the name length limit needs to be passed in or as some define if (structname) BLI_snprintf(name, 256, "%s%s (%s)", arrayname, propname, structname); else BLI_snprintf(name, 256, "%s%s", arrayname, propname); /* free temp name if nameprop is set */ if (free_structname) MEM_freeN((void *)structname); /* Icon for this property's owner: * use the struct's icon if it is set */ icon = RNA_struct_ui_icon(ptr.type); /* valid path - remove the invalid tag since we now know how to use it saving * users manual effort to reenable using "Revive Disabled FCurves" [#29629] */ fcu->flag &= ~FCURVE_DISABLED; } else { /* invalid path */ BLI_snprintf(name, 256, "\"%s[%d]\"", fcu->rna_path, fcu->array_index); /* icon for this should be the icon for the base ID */ // TODO: or should we just use the error icon? icon = RNA_struct_ui_icon(id_ptr.type); /* tag F-Curve as disabled - as not usable path */ fcu->flag |= FCURVE_DISABLED; } } /* return the icon that the active data had */ return icon; }
uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, const char *name, int icon, int x1, int y1, int x2, int y2) { uiBut *but = NULL; switch (RNA_property_type(prop)) { case PROP_BOOLEAN: { int arraylen = RNA_property_array_length(ptr, prop); if (arraylen && index == -1) return NULL; if (icon && name && name[0] == '\0') but = uiDefIconButR_prop(block, ICONTOG, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else if (icon) but = uiDefIconTextButR_prop(block, ICONTOG, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, OPTION, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; } case PROP_INT: case PROP_FLOAT: { int arraylen = RNA_property_array_length(ptr, prop); if (arraylen && index == -1) { if (ELEM(RNA_property_subtype(prop), PROP_COLOR, PROP_COLOR_GAMMA)) but = uiDefButR_prop(block, COL, 0, name, x1, y1, x2, y2, ptr, prop, 0, 0, 0, -1, -1, NULL); } else if (RNA_property_subtype(prop) == PROP_PERCENTAGE || RNA_property_subtype(prop) == PROP_FACTOR) but = uiDefButR_prop(block, NUMSLI, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, NUM, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; } case PROP_ENUM: if (icon && name && name[0] == '\0') but = uiDefIconButR_prop(block, MENU, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else if (icon) but = uiDefIconTextButR_prop(block, MENU, 0, icon, NULL, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, MENU, 0, NULL, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; case PROP_STRING: if (icon && name && name[0] == '\0') but = uiDefIconButR_prop(block, TEX, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else if (icon) but = uiDefIconTextButR_prop(block, TEX, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, TEX, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; case PROP_POINTER: { PointerRNA pptr; pptr = RNA_property_pointer_get(ptr, prop); if (!pptr.type) pptr.type = RNA_property_pointer_type(ptr, prop); icon = RNA_struct_ui_icon(pptr.type); if (icon == ICON_DOT) icon = 0; but = uiDefIconTextButR_prop(block, IDPOIN, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; } case PROP_COLLECTION: { char text[256]; BLI_snprintf(text, sizeof(text), IFACE_("%d items"), RNA_property_collection_length(ptr, prop)); but = uiDefBut(block, LABEL, 0, text, x1, y1, x2, y2, NULL, 0, 0, 0, 0, NULL); uiButSetFlag(but, UI_BUT_DISABLED); break; } default: but = NULL; break; } return but; }
/* 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; }
/* 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; }
/* Modifies property array length if needed and PROP_DYNAMIC flag is set. */ static int validate_array_length(PyObject *rvalue, PointerRNA *ptr, PropertyRNA *prop, int lvalue_dim, int *totitem, const char *error_prefix) { int dimsize[MAX_ARRAY_DIMENSION]; int tot, totdim, len; totdim = RNA_property_array_dimension(ptr, prop, dimsize); tot = count_items(rvalue, totdim - lvalue_dim); if (tot == -1) { PyErr_Format(PyExc_ValueError, "%s %.200s.%.200s, error validating the sequence length", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop)); return -1; } else if ((RNA_property_flag(prop) & PROP_DYNAMIC) && lvalue_dim == 0) { if (RNA_property_array_length(ptr, prop) != tot) { #if 0 /* length is flexible */ if (!RNA_property_dynamic_array_set_length(ptr, prop, tot)) { /* BLI_snprintf(error_str, error_str_size, * "%s.%s: array length cannot be changed to %d", * RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); */ PyErr_Format(PyExc_ValueError, "%s %s.%s: array length cannot be changed to %d", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); return -1; } #else *totitem = tot; return 0; #endif } len = tot; } else { /* length is a constraint */ if (!lvalue_dim) { len = RNA_property_array_length(ptr, prop); } /* array item assignment */ else { int i; len = 1; /* arr[3][4][5] * * arr[2] = x * dimsize = {4, 5} * dimsize[1] = 4 * dimsize[2] = 5 * lvalue_dim = 0, totdim = 3 * * arr[2][3] = x * lvalue_dim = 1 * * arr[2][3][4] = x * lvalue_dim = 2 */ for (i = lvalue_dim; i < totdim; i++) len *= dimsize[i]; } if (tot != len) { /* BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); */ PyErr_Format(PyExc_ValueError, "%s %.200s.%.200s, sequence must have %d items total, not %d", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), len, tot); return -1; } } *totitem = len; 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); } }
/* TODO, multi-dimensional arrays */ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) { int len = RNA_property_array_length(ptr, prop); int type; int i; if (len == 0) /* possible with dynamic arrays */ return 0; if (RNA_property_array_dimension(ptr, prop, NULL) > 1) { PyErr_SetString(PyExc_TypeError, "PropertyRNA - multi dimensional arrays not supported yet"); return -1; } type = RNA_property_type(prop); switch (type) { case PROP_FLOAT: { float value_f = PyFloat_AsDouble(value); if (value_f == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; } else { float tmp[32]; float *tmp_arr; if (len * sizeof(float) > sizeof(tmp)) { tmp_arr = PyMem_MALLOC(len * sizeof(float)); } else { tmp_arr = tmp; } RNA_property_float_get_array(ptr, prop, tmp_arr); for (i = 0; i < len; i++) { if (tmp_arr[i] == value_f) { break; } } if (tmp_arr != tmp) PyMem_FREE(tmp_arr); return i < len ? 1 : 0; } break; } case PROP_BOOLEAN: case PROP_INT: { int value_i = PyLong_AsSsize_t(value); if (value_i == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; } else { int tmp[32]; int *tmp_arr; if (len * sizeof(int) > sizeof(tmp)) { tmp_arr = PyMem_MALLOC(len * sizeof(int)); } else { tmp_arr = tmp; } if (type == PROP_BOOLEAN) RNA_property_boolean_get_array(ptr, prop, tmp_arr); else RNA_property_int_get_array(ptr, prop, tmp_arr); for (i = 0; i < len; i++) { if (tmp_arr[i] == value_i) { break; } } if (tmp_arr != tmp) PyMem_FREE(tmp_arr); return i < len ? 1 : 0; } break; } } /* should never reach this */ PyErr_SetString(PyExc_TypeError, "PropertyRNA - type not in float/bool/int"); return -1; }
uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, const char *name, int icon, int x1, int y1, int x2, int y2) { uiBut *but = NULL; switch (RNA_property_type(prop)) { case PROP_BOOLEAN: { int arraylen = RNA_property_array_length(ptr, prop); if (arraylen && index == -1) return NULL; if (icon && name && name[0] == '\0') but = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else if (icon) but = uiDefIconTextButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, UI_BTYPE_CHECKBOX, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; } case PROP_INT: case PROP_FLOAT: { int arraylen = RNA_property_array_length(ptr, prop); if (arraylen && index == -1) { if (ELEM(RNA_property_subtype(prop), PROP_COLOR, PROP_COLOR_GAMMA)) { but = uiDefButR_prop(block, UI_BTYPE_COLOR, 0, name, x1, y1, x2, y2, ptr, prop, -1, 0, 0, -1, -1, NULL); } else { return NULL; } } else if (RNA_property_subtype(prop) == PROP_PERCENTAGE || RNA_property_subtype(prop) == PROP_FACTOR) but = uiDefButR_prop(block, UI_BTYPE_NUM_SLIDER, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, UI_BTYPE_NUM, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) { UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE); } break; } case PROP_ENUM: if (icon && name && name[0] == '\0') but = uiDefIconButR_prop(block, UI_BTYPE_MENU, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else if (icon) but = uiDefIconTextButR_prop(block, UI_BTYPE_MENU, 0, icon, NULL, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, UI_BTYPE_MENU, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; case PROP_STRING: if (icon && name && name[0] == '\0') but = uiDefIconButR_prop(block, UI_BTYPE_TEXT, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else if (icon) but = uiDefIconTextButR_prop(block, UI_BTYPE_TEXT, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); else but = uiDefButR_prop(block, UI_BTYPE_TEXT, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); PropertySubType subtype = RNA_property_subtype(prop); if (!(ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME) || (block->flag & UI_BLOCK_LIST_ITEM))) { UI_but_flag_enable(but, UI_BUT_VALUE_CLEAR); } if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) { UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE); } break; case PROP_POINTER: { PointerRNA pptr; pptr = RNA_property_pointer_get(ptr, prop); if (!pptr.type) pptr.type = RNA_property_pointer_type(ptr, prop); icon = RNA_struct_ui_icon(pptr.type); if (icon == ICON_DOT) icon = 0; but = uiDefIconTextButR_prop(block, UI_BTYPE_SEARCH_MENU, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); break; } case PROP_COLLECTION: { char text[256]; BLI_snprintf(text, sizeof(text), IFACE_("%d items"), RNA_property_collection_length(ptr, prop)); but = uiDefBut(block, UI_BTYPE_LABEL, 0, text, x1, y1, x2, y2, NULL, 0, 0, 0, 0, NULL); UI_but_flag_enable(but, UI_BUT_DISABLED); break; } default: but = NULL; break; } return but; }
static int override_remove_button_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); PointerRNA ptr, id_refptr, src; PropertyRNA *prop; int index; const bool all = RNA_boolean_get(op->ptr, "all"); /* try to reset the nominated setting to its default value */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); ID *id = ptr.id.data; IDOverrideStaticProperty *oprop = RNA_property_override_property_find(&ptr, prop); BLI_assert(oprop != NULL); BLI_assert(id != NULL && id->override_static != NULL); const bool is_template = (id->override_static->reference == NULL); /* We need source (i.e. linked data) to restore values of deleted overrides... * If this is an override template, we obviously do not need to restore anything. */ if (!is_template) { RNA_id_pointer_create(id->override_static->reference, &id_refptr); if (!RNA_path_resolve(&id_refptr, oprop->rna_path, &src, NULL)) { BLI_assert(0 && "Failed to create matching source (linked data) RNA pointer"); } } if (!all && index != -1) { bool is_strict_find; /* Remove override operation for given item, * add singular operations for the other items as needed. */ IDOverrideStaticPropertyOperation *opop = BKE_override_static_property_operation_find( oprop, NULL, NULL, index, index, false, &is_strict_find); BLI_assert(opop != NULL); if (!is_strict_find) { /* No specific override operation, we have to get generic one, * and create item-specific override operations for all but given index, * before removing generic one. */ for (int idx = RNA_property_array_length(&ptr, prop); idx--;) { if (idx != index) { BKE_override_static_property_operation_get( oprop, opop->operation, NULL, NULL, idx, idx, true, NULL, NULL); } } } BKE_override_static_property_operation_delete(oprop, opop); if (!is_template) { RNA_property_copy(bmain, &ptr, &src, prop, index); } if (BLI_listbase_is_empty(&oprop->operations)) { BKE_override_static_property_delete(id->override_static, oprop); } } else { /* Just remove whole generic override operation of this property. */ BKE_override_static_property_delete(id->override_static, oprop); if (!is_template) { RNA_property_copy(bmain, &ptr, &src, prop, -1); } } return operator_button_property_finish(C, &ptr, prop); }