static PyObject *bpyunits_to_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
{
	static const char *kwlist[] = {"unit_system", "unit_category", "str_input", "str_ref_unit", NULL};

	char *usys_str = NULL, *ucat_str = NULL, *inpt = NULL, *uref = NULL;
	const float scale = 1.0f;

	char *str;
	Py_ssize_t str_len;
	double result;
	int usys, ucat;
	PyObject *ret;

	if (!PyArg_ParseTupleAndKeywords(args, kw, "sss#|z:bpy.utils.units.to_value", (char **)kwlist,
	                                 &usys_str, &ucat_str, &inpt, &str_len, &uref))
	{
		return NULL;
	}

	if (!bpyunits_validate(usys_str, ucat_str, &usys, &ucat)) {
		return NULL;
	}

	str_len = str_len * 2 + 64;
	str = PyMem_MALLOC(sizeof(*str) * (size_t)str_len);
	BLI_strncpy(str, inpt, (size_t)str_len);

	bUnit_ReplaceString(str, (int)str_len, uref, scale, usys, ucat);

	if (!PyC_RunString_AsNumber(str, &result, "<bpy_units_api>")) {
		if (PyErr_Occurred()) {
			PyErr_Print();
			PyErr_Clear();
		}

		PyErr_Format(PyExc_ValueError,
		             "'%.200s' (converted as '%s') could not be evaluated.",
		             inpt, str);
		ret = NULL;
	}
	else {
		ret = PyFloat_FromDouble(result);
	}

	PyMem_FREE(str);
	return ret;
}
Beispiel #2
0
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
{
	const char *utf8_buf = NULL;
	char ascii[2] = {'\0', '\0'};
	bool updated = false;
	short idx = n->idx, idx_max = n->idx_max;
	short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
	int cur;
	double val;

	switch (event->type) {
		case EVT_MODAL_MAP:
			if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
				n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
				value_to_editstr(n, idx);
				n->val_flag[idx] |= NUM_EDITED;
				updated = true;
			}
			else {
				/* might be a char too... */
				utf8_buf = event->utf8_buf;
				ascii[0] = event->ascii;
			}
			break;
		case BACKSPACEKEY:
			/* Part specific to backspace... */
			if (!(n->val_flag[idx] & NUM_EDITED)) {
				copy_v3_v3(n->val, n->val_org);
				n->val_flag[0] &= ~NUM_EDITED;
				n->val_flag[1] &= ~NUM_EDITED;
				n->val_flag[2] &= ~NUM_EDITED;
				updated = true;
				break;
			}
			else if (event->shift || !n->str[0]) {
				n->val[idx] = n->val_org[idx];
				n->val_flag[idx] &= ~NUM_EDITED;
				n->str[0] = '\0';
				n->str_cur = 0;
				updated = true;
				break;
			}
			/* Else, common behavior with DELKEY, only difference is remove char(s) before/after the cursor. */
			dir = STRCUR_DIR_PREV;
			/* fall-through */
		case DELKEY:
			if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
				int t_cur = cur = n->str_cur;
				if (event->ctrl) {
					mode = STRCUR_JUMP_DELIM;
				}
				BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
				if (t_cur != cur) {
					if (t_cur < cur) {
						SWAP(int, t_cur, cur);
						n->str_cur = cur;
					}
					memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1);  /* +1 for trailing '\0'. */
					updated = true;
				}
			}
			else {
				return false;
			}
			break;
		case LEFTARROWKEY:
			dir = STRCUR_DIR_PREV;
			/* fall-through */
		case RIGHTARROWKEY:
			cur = n->str_cur;
			if (event->ctrl) {
				mode = STRCUR_JUMP_DELIM;
			}
			BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
			if (cur != n->str_cur) {
				n->str_cur = cur;
				return true;
			}
			return false;
		case HOMEKEY:
			if (n->str[0]) {
				n->str_cur = 0;
				return true;
			}
			return false;
		case ENDKEY:
			if (n->str[0]) {
				n->str_cur = strlen(n->str);
				return true;
			}
			return false;
		case TABKEY:
			n->val_org[idx] = n->val[idx];
			n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);

			idx += event->ctrl ? -1 : 1;
			idx %= idx_max + 1;
			n->idx = idx;
			n->val[idx] = n->val_org[idx];
			if (n->val_flag[idx] & NUM_EDITED) {
				value_to_editstr(n, idx);
			}
			else {
				n->str[0] = '\0';
				n->str_cur = 0;
			}
			return true;
		case PADPERIOD:
			/* Force numdot, some OSs/countries generate a comma char in this case, sic...  (T37992) */
			ascii[0] = '.';
			utf8_buf = ascii;
			break;
		case EQUALKEY:
		case PADASTERKEY:
			if (!(n->flag & NUM_EDIT_FULL)) {
				n->flag |= NUM_EDIT_FULL;
				n->val_flag[idx] |= NUM_EDITED;
				return true;
			}
			else if (event->ctrl) {
				n->flag &= ~NUM_EDIT_FULL;
				return true;
			}
			/* fall-through */
		case PADMINUS:
		case MINUSKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_NEGATE;
				updated = true;
				break;
			}
			/* fall-through */
		case PADSLASHKEY:
		case SLASHKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_INVERSE;
				updated = true;
				break;
			}
			/* fall-through */
		case CKEY:
			if (event->ctrl) {
				/* Copy current str to the copypaste buffer. */
				WM_clipboard_text_set(n->str, 0);
				updated = true;
				break;
			}
			/* fall-through */
		case VKEY:
			if (event->ctrl) {
				/* extract the first line from the clipboard */
				int pbuf_len;
				char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);

				if (pbuf) {
					bool success;

					success = editstr_insert_at_cursor(n, pbuf, pbuf_len);

					MEM_freeN(pbuf);
					if (!success) {
						return false;
					}

					n->val_flag[idx] |= NUM_EDITED;
				}
				updated = true;
				break;
			}
			/* fall-through */
		default:
			utf8_buf = event->utf8_buf;
			ascii[0] = event->ascii;
			break;
	}

	if (utf8_buf && !utf8_buf[0] && ascii[0]) {
		/* Fallback to ascii. */
		utf8_buf = ascii;
	}

	if (utf8_buf && utf8_buf[0]) {
		if (!(n->flag & NUM_EDIT_FULL)) {
			/* In simple edit mode, we only keep a few chars as valid! */
			/* no need to decode unicode, ascii is first char only */
			if (!editstr_is_simple_numinput(utf8_buf[0])) {
				return false;
			}
		}

		if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
			return false;
		}

		n->val_flag[idx] |= NUM_EDITED;
	}
	else if (!updated) {
		return false;
	}

	/* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
	if (n->str[0]) {
#ifdef WITH_PYTHON
		char str_unit_convert[NUM_STR_REP_LEN * 6];  /* Should be more than enough! */
		const char *default_unit = NULL;

		/* Make radian default unit when needed. */
		if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION)
			default_unit = "r";

		BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert));

		bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, 1.0,
		                    n->unit_sys, n->unit_type[idx]);

		/* Note: with angles, we always get values as radians here... */
		if (BPY_button_exec(C, str_unit_convert, &val, false) != -1) {
			n->val[idx] = (float)val;
			n->val_flag[idx] &= ~NUM_INVALID;
		}
		else {
			n->val_flag[idx] |= NUM_INVALID;
		}
#else  /* Very unlikely, but does not harm... */
		n->val[idx] = (float)atof(n->str);
#endif  /* WITH_PYTHON */

		if (n->val_flag[idx] & NUM_NEGATE) {
			n->val[idx] = -n->val[idx];
		}
		if (n->val_flag[idx] & NUM_INVERSE) {
			n->val[idx] = 1.0f / n->val[idx];
		}
	}

	/* REDRAW SINCE NUMBERS HAVE CHANGED */
	return true;
}
Beispiel #3
0
/* make a copy of the string that replaces the units with numbers
 * this is used before parsing
 * This is only used when evaluating user input and can afford to be a bit slower
 *
 * This is to be used before python evaluation so..
 * 10.1km -> 10.1*1000.0
 * ...will be resolved by python.
 *
 * values will be split by a comma's
 * 5'2" -> 5'0.0254, 2*0.3048
 *
 * str_prev is optional, when valid it is used to get a base unit when none is set.
 *
 * return true of a change was made.
 */
int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type)
{
	bUnitCollection *usys = unit_get_system(system, type);

	bUnitDef *unit;
	char str_tmp[TEMP_STR_SIZE];
	int changed = 0;

	if (usys == NULL || usys->units[0].name == NULL) {
		return 0;
	}

	/* make lowercase */
	BLI_ascii_strtolower(str, len_max);

	for (unit = usys->units; unit->name; unit++) {
		/* in case there are multiple instances */
		while (unit_replace(str, len_max, str_tmp, scale_pref, unit))
			changed = true;
	}
	unit = NULL;

	{
		/* try other unit systems now, so we can evaluate imperial when metric is set for eg. */
		bUnitCollection *usys_iter;
		int system_iter;

		for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) {
			if (system_iter != system) {
				usys_iter = unit_get_system(system_iter, type);
				if (usys_iter) {
					for (unit = usys_iter->units; unit->name; unit++) {
						int ofs = 0;
						/* in case there are multiple instances */
						while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref, unit)))
							changed = true;
					}
				}
			}
		}
	}
	unit = NULL;

	if (changed == 0) {
		/* no units given so infer a unit from the previous string or default */
		if (str_prev) {
			/* see which units the original value had */
			for (unit = usys->units; unit->name; unit++) {
				if (unit_find(str_prev, unit))
					break;
			}
		}

		if (unit == NULL || unit->name == NULL)
			unit = unit_default(usys);

		/* add the unit prefix and re-run, use brackets in case there was an expression given */
		if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)%s", str, unit->name) < sizeof(str_tmp)) {
			strncpy(str, str_tmp, len_max);
			return bUnit_ReplaceString(str, len_max, NULL, scale_pref, system, type);
		}
		else {
			/* BLI_snprintf would not fit into str_tmp, cant do much in this case
			 * check for this because otherwise bUnit_ReplaceString could call its self forever */
			return 0;
		}

	}

	/* replace # with commas when there is no operator between it and the next number
	 *
	 * "1*1# 3*100# * 3"  ->  "1 *1, 3 *100  * 3"
	 *
	 * */
	{
		char *str_found = str;
		char *ch = str;

		while ((str_found = strchr(str_found, SEP_CHR))) {

			int op_found = 0;
			/* any operators after this?*/
			for (ch = str_found + 1; *ch != '\0'; ch++) {

				if (*ch == ' ' || *ch == '\t') {
					/* do nothing */
				}
				else if (ch_is_op(*ch) || *ch == ',') { /* found an op, no need to insert a ',' */
					op_found = 1;
					break;
				}
				else { /* found a non-op character */
					op_found = 0;
					break;
				}
			}

			*str_found++ = op_found ? ' ' : ',';
		}
	}

	return changed;
}
Beispiel #4
0
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
{
	const char *utf8_buf = NULL;
	char ascii[2] = {'\0', '\0'};
	bool updated = false;
	short idx = n->idx, idx_max = n->idx_max;
	short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
	int cur;

	switch (event->type) {
		case EVT_MODAL_MAP:
			if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
				n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
				value_to_editstr(n, idx);
				n->val_flag[idx] |= NUM_EDITED;
				updated = true;
			}
			else {
				/* might be a char too... */
				utf8_buf = event->utf8_buf;
				ascii[0] = event->ascii;
			}
			break;
		case BACKSPACEKEY:
			/* Part specific to backspace... */
			if (!(n->val_flag[idx] & NUM_EDITED)) {
				copy_v3_v3(n->val, n->val_org);
				n->val_flag[0] &= ~NUM_EDITED;
				n->val_flag[1] &= ~NUM_EDITED;
				n->val_flag[2] &= ~NUM_EDITED;
				n->flag |= NUM_FAKE_EDITED;
				updated = true;
				break;
			}
			else if (event->shift || !n->str[0]) {
				n->val[idx] = n->val_org[idx];
				n->val_flag[idx] &= ~NUM_EDITED;
				n->str[0] = '\0';
				n->str_cur = 0;
				updated = true;
				break;
			}
			/* Else, common behavior with DELKEY, only difference is remove char(s) before/after the cursor. */
			dir = STRCUR_DIR_PREV;
			ATTR_FALLTHROUGH;
		case DELKEY:
			if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
				int t_cur = cur = n->str_cur;
				if (event->ctrl) {
					mode = STRCUR_JUMP_DELIM;
				}
				BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
				if (t_cur != cur) {
					if (t_cur < cur) {
						SWAP(int, t_cur, cur);
						n->str_cur = cur;
					}
					memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1);  /* +1 for trailing '\0'. */
					updated = true;
				}
				if (!n->str[0]) {
					n->val[idx] = n->val_org[idx];
				}
			}
			else {
				return false;
			}
			break;
		case LEFTARROWKEY:
			dir = STRCUR_DIR_PREV;
			ATTR_FALLTHROUGH;
		case RIGHTARROWKEY:
			cur = n->str_cur;
			if (event->ctrl) {
				mode = STRCUR_JUMP_DELIM;
			}
			BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
			if (cur != n->str_cur) {
				n->str_cur = cur;
				return true;
			}
			return false;
		case HOMEKEY:
			if (n->str[0]) {
				n->str_cur = 0;
				return true;
			}
			return false;
		case ENDKEY:
			if (n->str[0]) {
				n->str_cur = strlen(n->str);
				return true;
			}
			return false;
		case TABKEY:
			n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);

			idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1);
			n->idx = idx;
			if (n->val_flag[idx] & NUM_EDITED) {
				value_to_editstr(n, idx);
			}
			else {
				n->str[0] = '\0';
				n->str_cur = 0;
			}
			return true;
		case PADPERIOD:
		case PERIODKEY:
			/* Force numdot, some OSs/countries generate a comma char in this case, sic...  (T37992) */
			ascii[0] = '.';
			utf8_buf = ascii;
			break;
#if 0
		/* Those keys are not directly accessible in all layouts, preventing to generate matching events.
		 * So we use a hack (ascii value) instead, see below.
		 */
		case EQUALKEY:
		case PADASTERKEY:
			if (!(n->flag & NUM_EDIT_FULL)) {
				n->flag |= NUM_EDIT_FULL;
				n->val_flag[idx] |= NUM_EDITED;
				return true;
			}
			else if (event->ctrl) {
				n->flag &= ~NUM_EDIT_FULL;
				return true;
			}
			break;
#endif
		case PADMINUS:
		case MINUSKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_NEGATE;
				updated = true;
			}
			break;
		case PADSLASHKEY:
		case SLASHKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_INVERSE;
				updated = true;
			}
			break;
		case CKEY:
			if (event->ctrl) {
				/* Copy current str to the copypaste buffer. */
				WM_clipboard_text_set(n->str, 0);
				updated = true;
			}
			break;
		case VKEY:
			if (event->ctrl) {
				/* extract the first line from the clipboard */
				int pbuf_len;
				char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);

				if (pbuf) {
					const bool success = editstr_insert_at_cursor(n, pbuf, pbuf_len);

					MEM_freeN(pbuf);
					if (!success) {
						return false;
					}

					n->val_flag[idx] |= NUM_EDITED;
				}
				updated = true;
			}
			break;
		default:
			break;
	}

	if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) {
		utf8_buf = event->utf8_buf;
		ascii[0] = event->ascii;
	}

	/* XXX Hack around keyboards without direct access to '=' nor '*'... */
	if (ELEM(ascii[0], '=', '*')) {
		if (!(n->flag & NUM_EDIT_FULL)) {
			n->flag |= NUM_EDIT_FULL;
			n->val_flag[idx] |= NUM_EDITED;
			return true;
		}
		else if (event->ctrl) {
			n->flag &= ~NUM_EDIT_FULL;
			return true;
		}
	}

	/* Up to this point, if we have a ctrl modifier, skip.
	 * This allows to still access most of modals' shortcuts even in numinput mode.
	 */
	if (!updated && event->ctrl) {
		return false;
	}

	if ((!utf8_buf || !utf8_buf[0]) && ascii[0]) {
		/* Fallback to ascii. */
		utf8_buf = ascii;
	}

	if (utf8_buf && utf8_buf[0]) {
		if (!(n->flag & NUM_EDIT_FULL)) {
			/* In simple edit mode, we only keep a few chars as valid! */
			/* no need to decode unicode, ascii is first char only */
			if (!editstr_is_simple_numinput(utf8_buf[0])) {
				return false;
			}
		}

		if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
			return false;
		}

		n->val_flag[idx] |= NUM_EDITED;
	}
	else if (!updated) {
		return false;
	}

	/* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
	if (n->str[0]) {
		const float val_prev = n->val[idx];
#ifdef WITH_PYTHON
		Scene *sce = CTX_data_scene(C);
		double val;
		char str_unit_convert[NUM_STR_REP_LEN * 6];  /* Should be more than enough! */
		const char *default_unit = NULL;

		/* Use scale_length if needed! */
		const float fac = (float)BKE_scene_unit_scale(&sce->unit, n->unit_type[idx], 1.0);

		/* Make radian default unit when needed. */
		if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION)
			default_unit = "r";

		BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert));

		bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, fac,
		                    n->unit_sys, n->unit_type[idx]);

		/* Note: with angles, we always get values as radians here... */
		if (BPY_execute_string_as_number(C, str_unit_convert, false, &val)) {
			n->val[idx] = (float)val;
			n->val_flag[idx] &= ~NUM_INVALID;
		}
		else {
			n->val_flag[idx] |= NUM_INVALID;
		}
#else  /* Very unlikely, but does not harm... */
		n->val[idx] = (float)atof(n->str);
		(void)C;
#endif  /* WITH_PYTHON */

		if (n->val_flag[idx] & NUM_NEGATE) {
			n->val[idx] = -n->val[idx];
		}
		if (n->val_flag[idx] & NUM_INVERSE) {
			n->val[idx] = 1.0f / n->val[idx];
		}

		if (UNLIKELY(!isfinite(n->val[idx]))) {
			n->val[idx] = val_prev;
			n->val_flag[idx] |= NUM_INVALID;
		}
	}

	/* REDRAW SINCE NUMBERS HAVE CHANGED */
	return true;
}