Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
/* 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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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);
	}
}
Ejemplo n.º 6
0
/* 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);
}
Ejemplo n.º 7
0
/* 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;
}
Ejemplo n.º 8
0
/* 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;
}
Ejemplo n.º 9
0
/* 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;
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
/* 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;
}
Ejemplo n.º 12
0
/* 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;
}
Ejemplo n.º 13
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;
}
Ejemplo n.º 14
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);
	}
}
Ejemplo n.º 15
0
/* 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;
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 17
0
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);
}