Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/util/numinput.c')
-rw-r--r--source/blender/editors/util/numinput.c489
1 files changed, 282 insertions, 207 deletions
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index 0feaf936172..891051185ac 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -24,93 +24,97 @@
* \ingroup edutil
*/
-
-#include <math.h> /* fabs */
-#include <stdio.h> /* for size_t */
+#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
+#include "BLI_math.h"
#include "BLI_string.h"
+#include "BLI_string_utf8.h"
+#include "BLI_string_cursor_utf8.h"
+
+#include "BKE_context.h"
+#include "BKE_unit.h"
+
+#include "DNA_scene_types.h"
+#include "WM_api.h"
#include "WM_types.h"
+#ifdef WITH_PYTHON
+#include "BPY_extern.h"
+#endif
+
#include "ED_numinput.h"
+
+/* NumInput.val_flag[] */
+enum {
+ /* (1 << 8) and below are reserved for public flags! */
+ NUM_EDITED = (1 << 9), /* User has edited this value somehow. */
+ NUM_INVALID = (1 << 10), /* Current expression for this value is invalid. */
+};
+
/* ************************** Functions *************************** */
/* ************************** NUMINPUT **************************** */
void initNumInput(NumInput *n)
{
- n->flag =
- n->idx =
- n->idx_max =
- n->inv[0] =
- n->inv[1] =
- n->inv[2] =
- n->ctrl[0] =
- n->ctrl[1] =
- n->ctrl[2] = 0;
-
- n->val[0] =
- n->val[1] =
- n->val[2] = 0.0f;
+ n->unit_sys = USER_UNIT_NONE;
+ n->unit_type[0] = n->unit_type[1] = n->unit_type[2] = B_UNIT_NONE;
+ n->idx = 0;
+ n->idx_max = 0;
+ n->flag = 0;
+ n->val_flag[0] = n->val_flag[1] = n->val_flag[2] = 0;
+ zero_v3(n->val_org);
+ zero_v3(n->val);
+ n->str[0] = '\0';
+ n->str_cur = 0;
+ copy_v3_fl(n->val_inc, 1.0f);
}
+/* str must be NUM_STR_REP_LEN * (idx_max + 1) length. */
void outputNumInput(NumInput *n, char *str)
{
- char cur;
- char inv[] = "1/";
short i, j;
const int ln = NUM_STR_REP_LEN;
+ const int prec = 4; /* draw-only, and avoids too much issues with radian->degrees conversion. */
for (j = 0; j <= n->idx_max; j++) {
/* if AFFECTALL and no number typed and cursor not on number, use first number */
- if (n->flag & NUM_AFFECT_ALL && n->idx != j && n->ctrl[j] == 0)
- i = 0;
- else
- i = j;
-
- if (n->idx != i)
- cur = ' ';
- else
- cur = '|';
-
- if (n->inv[i])
- inv[0] = '1';
- else
- inv[0] = 0;
-
- if (n->val[i] > 1e10f || n->val[i] < -1e10f)
- BLI_snprintf(&str[j * ln], ln, "%s%.4e%c", inv, n->val[i], cur);
- else
- switch (n->ctrl[i]) {
- case 0:
- BLI_snprintf(&str[j * ln], ln, "%sNONE%c", inv, cur);
- break;
- case 1:
- case -1:
- BLI_snprintf(&str[j * ln], ln, "%s%.0f%c", inv, n->val[i], cur);
- break;
- case 10:
- case -10:
- BLI_snprintf(&str[j * ln], ln, "%s%.f.%c", inv, n->val[i], cur);
- break;
- case 100:
- case -100:
- BLI_snprintf(&str[j * ln], ln, "%s%.1f%c", inv, n->val[i], cur);
- break;
- case 1000:
- case -1000:
- BLI_snprintf(&str[j * ln], ln, "%s%.2f%c", inv, n->val[i], cur);
- break;
- case 10000:
- case -10000:
- BLI_snprintf(&str[j * ln], ln, "%s%.3f%c", inv, n->val[i], cur);
- break;
- default:
- BLI_snprintf(&str[j * ln], ln, "%s%.4e%c", inv, n->val[i], cur);
- break;
+ i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
+
+ if (n->val_flag[i] & NUM_EDITED) {
+ if (i == n->idx && n->str[0]) {
+ char before_cursor[NUM_STR_REP_LEN];
+ char val[16];
+ if (n->val_flag[i] & NUM_INVALID) {
+ BLI_strncpy(val, "Invalid", sizeof(val));
+ }
+ else {
+ bUnit_AsString(val, sizeof(val), (double)n->val[i], prec,
+ n->unit_sys, n->unit_type[i], true, false);
+ }
+ BLI_strncpy(before_cursor, n->str, n->str_cur + 1); /* +1 because of trailing '\0' */
+ BLI_snprintf(&str[j * ln], ln, "[%s|%s] = %s", before_cursor, &n->str[n->str_cur], val);
}
+ else {
+ const char *cur = (i == n->idx) ? "|" : "";
+ if (n->unit_use_radians && n->unit_type[i] == B_UNIT_ROTATION) {
+ /* Radian exception... */
+ BLI_snprintf(&str[j * ln], ln, "%s%.6gr%s", cur, n->val[i], cur);
+ }
+ else {
+ char tstr[NUM_STR_REP_LEN];
+ bUnit_AsString(tstr, ln, (double)n->val[i], prec, n->unit_sys, n->unit_type[i], true, false);
+ BLI_snprintf(&str[j * ln], ln, "%s%s%s", cur, tstr, cur);
+ }
+ }
+ }
+ else {
+ const char *cur = (i == n->idx) ? "|" : "";
+ BLI_snprintf(&str[j * ln], ln, "%sNONE%s", cur, cur);
+ }
}
}
@@ -119,8 +123,9 @@ bool hasNumInput(const NumInput *n)
short i;
for (i = 0; i <= n->idx_max; i++) {
- if (n->ctrl[i])
+ if (n->val_flag[i] & NUM_EDITED) {
return true;
+ }
}
return false;
@@ -132,177 +137,247 @@ bool hasNumInput(const NumInput *n)
void applyNumInput(NumInput *n, float *vec)
{
short i, j;
+ float val;
if (hasNumInput(n)) {
for (j = 0; j <= n->idx_max; j++) {
/* if AFFECTALL and no number typed and cursor not on number, use first number */
- if (n->flag & NUM_AFFECT_ALL && n->idx != j && n->ctrl[j] == 0)
- i = 0;
- else
- i = j;
+ i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
+ val = (!(n->val_flag[i] & NUM_EDITED) && n->val_flag[i] & NUM_NULL_ONE) ? 1.0f : n->val[i];
- if (n->ctrl[i] == 0 && n->flag & NUM_NULL_ONE) {
- vec[j] = 1.0f;
+ if (n->val_flag[i] & NUM_NO_NEGATIVE && val < 0.0f) {
+ val = 0.0f;
}
- else if (n->val[i] == 0.0f && n->flag & NUM_NO_ZERO) {
- vec[j] = 0.0001f;
+ if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
+ val = 0.0001f;
}
- else {
- if (n->inv[i]) {
- vec[j] = 1.0f / n->val[i];
- }
- else {
- vec[j] = n->val[i];
+ if (n->val_flag[i] & NUM_NO_FRACTION && val != floorf(val)) {
+ val = floorf(val + 0.5f);
+ if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
+ val = 1.0f;
}
}
+ vec[j] = val;
}
}
}
-bool handleNumInput(NumInput *n, const wmEvent *event)
+
+static void value_to_editstr(NumInput *n, int idx)
{
- float Val = 0;
- short idx = n->idx, idx_max = n->idx_max;
+ const int prec = 6; /* editing, higher precision needed. */
+ bUnit_AsString(n->str, NUM_STR_REP_LEN, (double)n->val[idx], prec,
+ n->unit_sys, n->unit_type[idx], true, false);
+ n->str_cur = strlen(n->str);
+}
- if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case NUM_MODAL_INCREMENT_UP:
- if (!n->ctrl[idx])
- n->ctrl[idx] = 1;
+static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf_len)
+{
+ int cur = n->str_cur;
+ int len = strlen(&n->str[cur]) + 1; /* +1 for the trailing '\0'. */
+ int n_cur = cur + buf_len;
- n->val[idx] += n->increment;
- break;
- case NUM_MODAL_INCREMENT_DOWN:
- if (!n->ctrl[idx])
- n->ctrl[idx] = 1;
+ if (n_cur + len >= NUM_STR_REP_LEN) {
+ return false;
+ }
+
+ memmove(&n->str[n_cur], &n->str[cur], len);
+ memcpy(&n->str[cur], buf, sizeof(char) * buf_len);
+
+ n->str_cur = n_cur;
+ return true;
+}
+
+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;
- n->val[idx] -= n->increment;
+ 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;
- default:
- return 0;
- }
- }
- else {
- switch (event->type) {
- case BACKSPACEKEY:
- if (n->ctrl[idx] == 0) {
- n->val[0] =
- n->val[1] =
- n->val[2] = 0.0f;
- n->ctrl[0] =
- n->ctrl[1] =
- n->ctrl[2] = 0;
- n->inv[0] =
- n->inv[1] =
- n->inv[2] = 0;
- }
- else {
- n->val[idx] = 0.0f;
- n->ctrl[idx] = 0;
- n->inv[idx] = 0;
- }
+ }
+ 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;
- case PERIODKEY: case PADPERIOD:
- if (n->flag & NUM_NO_FRACTION)
- return 0;
-
- switch (n->ctrl[idx]) {
- case 0:
- case 1:
- n->ctrl[idx] = 10;
- break;
- case -1:
- n->ctrl[idx] = -10;
- 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;
}
- break;
- case PADMINUS:
- if (event->alt)
- break;
- /* fall-through */
- case MINUSKEY:
- if (n->flag & NUM_NO_NEGATIVE)
- return 0;
-
- if (n->ctrl[idx]) {
- n->ctrl[idx] *= -1;
- n->val[idx] *= -1;
+ 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
- n->ctrl[idx] = -1;
- break;
- case PADSLASHKEY: case SLASHKEY:
- if (n->flag & NUM_NO_FRACTION)
- return 0;
+ }
+ 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->inv[idx] = !n->inv[idx];
- break;
- case TABKEY:
- if (idx_max == 0)
- return 0;
-
- idx++;
- if (idx > idx_max)
- idx = 0;
- n->idx = idx;
+ 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 CKEY:
+ if (event->ctrl) {
+ /* Copy current str to the copypaste buffer. */
+ WM_clipboard_text_set(n->str, 0);
+ updated = true;
break;
- case PAD9: case NINEKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD8: case EIGHTKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD7: case SEVENKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD6: case SIXKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD5: case FIVEKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD4: case FOURKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD3: case THREEKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD2: case TWOKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD1: case ONEKEY:
- Val += 1.0f;
- /* fall-through */
- case PAD0: case ZEROKEY:
- if (!n->ctrl[idx])
- n->ctrl[idx] = 1;
-
- if (fabsf(n->val[idx]) > 9999999.0f) {
- /* pass */
- }
- else if (n->ctrl[idx] == 1) {
- n->val[idx] *= 10;
- n->val[idx] += Val;
- }
- else if (n->ctrl[idx] == -1) {
- n->val[idx] *= 10;
- n->val[idx] -= Val;
- }
- else {
- /* float resolution breaks when over six digits after comma */
- if (ABS(n->ctrl[idx]) < 10000000) {
- n->val[idx] += Val / (float)n->ctrl[idx];
- n->ctrl[idx] *= 10;
+ }
+ /* fall-through */
+ case VKEY:
+ if (event->ctrl) {
+ /* extract the first line from the clipboard */
+ char *pbuf = WM_clipboard_text_get(0);
+
+ if (pbuf) {
+ bool success;
+ /* Only copy string until first of this char. */
+ char *cr = strchr(pbuf, '\r');
+ char *cn = strchr(pbuf, '\n');
+ if (cn && cn < cr) cr = cn;
+ if (cr) *cr = '\0';
+
+ success = editstr_insert_at_cursor(n, pbuf, strlen(pbuf));
+
+ MEM_freeN(pbuf);
+ if (!success) {
+ return false;
}
+
+ n->val_flag[idx] |= NUM_EDITED;
}
+ updated = true;
break;
- default:
- return 0;
+ }
+ /* 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 (!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 */
}
-
- // printf("%f\n", n->val[idx]);
/* REDRAW SINCE NUMBERS HAVE CHANGED */
- return 1;
+ return true;
}