diff options
Diffstat (limited to 'source/blender/editors/util/numinput.c')
-rw-r--r-- | source/blender/editors/util/numinput.c | 489 |
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; } |