Fix T41590: When scene scale is not 1.0, and units are "None," Blender assumes transl...
[blender-staging.git] / source / blender / editors / util / numinput.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Jonathan Smith
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/util/numinput.c
24  *  \ingroup edutil
25  */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_utildefines.h"
30 #include "BLI_math.h"
31 #include "BLI_string.h"
32 #include "BLI_string_utf8.h"
33 #include "BLI_string_cursor_utf8.h"
34
35 #include "BKE_context.h"
36 #include "BKE_unit.h"
37
38 #include "DNA_scene_types.h"
39
40 #include "WM_api.h"
41 #include "WM_types.h"
42
43 #ifdef WITH_PYTHON
44 #include "BPY_extern.h"
45 #endif
46
47 #include "ED_numinput.h"
48 #include "UI_interface.h"
49
50
51 /* NumInput.flag */
52 enum {
53         /* (1 << 8) and below are reserved for public flags! */
54         NUM_EDIT_FULL       = (1 << 9),   /* Enable full editing, with units and math operators support. */
55         NUM_FAKE_EDITED     = (1 << 10),  /* Fake edited state (temp, avoids issue with backspace). */
56 };
57
58 /* NumInput.val_flag[] */
59 enum {
60         /* (1 << 8) and below are reserved for public flags! */
61         NUM_EDITED          = (1 << 9),    /* User has edited this value somehow. */
62         NUM_INVALID         = (1 << 10),   /* Current expression for this value is invalid. */
63         NUM_NEGATE          = (1 << 11),   /* Current expression's result has to be negated. */
64         NUM_INVERSE         = (1 << 12),   /* Current expression's result has to be inverted. */
65 };
66
67 /* ************************** Functions *************************** */
68
69 /* ************************** NUMINPUT **************************** */
70
71 void initNumInput(NumInput *n)
72 {
73         n->idx_max = 0;
74         n->unit_sys = USER_UNIT_NONE;
75         fill_vn_i(n->unit_type, NUM_MAX_ELEMENTS, B_UNIT_NONE);
76         n->unit_use_radians = false;
77
78         n->flag = 0;
79         fill_vn_short(n->val_flag, NUM_MAX_ELEMENTS, 0);
80         zero_v3(n->val);
81         fill_vn_fl(n->val_org, NUM_MAX_ELEMENTS, 0.0f);
82         fill_vn_fl(n->val_inc, NUM_MAX_ELEMENTS, 1.0f);
83
84         n->idx = 0;
85         n->str[0] = '\0';
86         n->str_cur = 0;
87 }
88
89 /* str must be NUM_STR_REP_LEN * (idx_max + 1) length. */
90 void outputNumInput(NumInput *n, char *str, UnitSettings *unit_settings)
91 {
92         short j;
93         const int ln = NUM_STR_REP_LEN;
94         int prec = 2; /* draw-only, and avoids too much issues with radian->degrees conversion. */
95
96         for (j = 0; j <= n->idx_max; j++) {
97                 /* if AFFECTALL and no number typed and cursor not on number, use first number */
98                 const short i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
99
100                 /* Use scale_length if needed! */
101                 const float fac = (float)bUnit_getScaleUnit(unit_settings, n->unit_type[j], 1.0);
102
103                 if (n->val_flag[i] & NUM_EDITED) {
104                         /* Get the best precision, allows us to draw '10.0001' as '10' instead! */
105                         prec = uiFloatPrecisionCalc(prec, (double)n->val[i]);
106                         if (i == n->idx) {
107                                 const char *heading_exp = "", *trailing_exp = "";
108                                 char before_cursor[NUM_STR_REP_LEN];
109                                 char val[16];
110
111                                 if (n->val_flag[i] & NUM_NEGATE) {
112                                         heading_exp = (n->val_flag[i] & NUM_INVERSE) ? "-1/(" : "-(";
113                                         trailing_exp = ")";
114                                 }
115                                 else if (n->val_flag[i] & NUM_INVERSE) {
116                                         heading_exp = "1/(";
117                                         trailing_exp = ")";
118                                 }
119
120                                 if (n->val_flag[i] & NUM_INVALID) {
121                                         BLI_strncpy(val, "Invalid", sizeof(val));
122                                 }
123                                 else {
124                                         bUnit_AsString(val, sizeof(val), (double)(n->val[i] * fac), prec,
125                                                        n->unit_sys, n->unit_type[i], true, false);
126                                 }
127
128                                 BLI_strncpy(before_cursor, n->str, n->str_cur + 1);  /* +1 because of trailing '\0' */
129                                 BLI_snprintf(&str[j * ln], ln, "[%s%s|%s%s] = %s",
130                                              heading_exp, before_cursor, &n->str[n->str_cur], trailing_exp, val);
131                         }
132                         else {
133                                 const char *cur = (i == n->idx) ? "|" : "";
134                                 if (n->unit_use_radians && n->unit_type[i] == B_UNIT_ROTATION) {
135                                         /* Radian exception... */
136                                         BLI_snprintf(&str[j * ln], ln, "%s%.6gr%s", cur, n->val[i], cur);
137                                 }
138                                 else {
139                                         char tstr[NUM_STR_REP_LEN];
140                                         bUnit_AsString(tstr, ln, (double)n->val[i], prec, n->unit_sys, n->unit_type[i], true, false);
141                                         BLI_snprintf(&str[j * ln], ln, "%s%s%s", cur, tstr, cur);
142                                 }
143                         }
144                 }
145                 else {
146                         const char *cur = (i == n->idx) ? "|" : "";
147                         BLI_snprintf(&str[j * ln], ln, "%sNONE%s", cur, cur);
148                 }
149                 /* We might have cut some multi-bytes utf8 chars (e.g. trailing '°' of degrees values can become only 'A')... */
150                 BLI_utf8_invalid_strip(&str[j * ln], strlen(&str[j * ln]));
151         }
152 }
153
154 bool hasNumInput(const NumInput *n)
155 {
156         short i;
157
158         if (n->flag & NUM_FAKE_EDITED) {
159                 return true;
160         }
161
162         for (i = 0; i <= n->idx_max; i++) {
163                 if (n->val_flag[i] & NUM_EDITED) {
164                         return true;
165                 }
166         }
167
168         return false;
169 }
170
171 /**
172  * \warning \a vec must be set beforehand otherwise we risk uninitialized vars.
173  */
174 bool applyNumInput(NumInput *n, float *vec)
175 {
176         short i, j;
177         float val;
178
179         if (hasNumInput(n)) {
180                 for (j = 0; j <= n->idx_max; j++) {
181                         if (n->flag & NUM_FAKE_EDITED) {
182                                 val = n->val[j];
183                         }
184                         else {
185                                 /* if AFFECTALL and no number typed and cursor not on number, use first number */
186                                 i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
187                                 val = (!(n->val_flag[i] & NUM_EDITED) && n->val_flag[i] & NUM_NULL_ONE) ? 1.0f : n->val[i];
188
189                                 if (n->val_flag[i] & NUM_NO_NEGATIVE && val < 0.0f) {
190                                         val = 0.0f;
191                                 }
192                                 if (n->val_flag[i] & NUM_NO_FRACTION && val != floorf(val)) {
193                                         val = floorf(val + 0.5f);
194                                         if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
195                                                 val = 1.0f;
196                                         }
197                                 }
198                                 else if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
199                                         val = 0.0001f;
200                                 }
201                         }
202                         vec[j] = val;
203                 }
204                 n->flag &= ~NUM_FAKE_EDITED;
205                 return true;
206         }
207         else {
208                 /* Else, we set the 'org' values for numinput! */
209                 for (j = 0; j <= n->idx_max; j++) {
210                         n->val[j] = n->val_org[j] = vec[j];
211                 }
212                 return false;
213         }
214 }
215
216
217 static void value_to_editstr(NumInput *n, int idx)
218 {
219         const int prec = 6; /* editing, higher precision needed. */
220         n->str_cur = bUnit_AsString(n->str, NUM_STR_REP_LEN, (double)n->val[idx], prec,
221                                     n->unit_sys, n->unit_type[idx], true, false);
222 }
223
224 static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf_len)
225 {
226         int cur = n->str_cur;
227         int len = strlen(&n->str[cur]) + 1;  /* +1 for the trailing '\0'. */
228         int n_cur = cur + buf_len;
229
230         if (n_cur + len >= NUM_STR_REP_LEN) {
231                 return false;
232         }
233
234         memmove(&n->str[n_cur], &n->str[cur], len);
235         memcpy(&n->str[cur], buf, sizeof(char) * buf_len);
236
237         n->str_cur = n_cur;
238         return true;
239 }
240
241 static bool editstr_is_simple_numinput(const char ascii)
242 {
243         if (ascii >= '0' && ascii <= '9') {
244                 return true;
245         }
246         else if (ascii == '.') {
247                 return true;
248         }
249         else {
250                 return false;
251         }
252 }
253
254 bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
255 {
256         const char *utf8_buf = NULL;
257         char ascii[2] = {'\0', '\0'};
258         bool updated = false;
259         short idx = n->idx, idx_max = n->idx_max;
260         short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
261         int cur;
262
263         switch (event->type) {
264                 case EVT_MODAL_MAP:
265                         if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
266                                 n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
267                                 value_to_editstr(n, idx);
268                                 n->val_flag[idx] |= NUM_EDITED;
269                                 updated = true;
270                         }
271                         else {
272                                 /* might be a char too... */
273                                 utf8_buf = event->utf8_buf;
274                                 ascii[0] = event->ascii;
275                         }
276                         break;
277                 case BACKSPACEKEY:
278                         /* Part specific to backspace... */
279                         if (!(n->val_flag[idx] & NUM_EDITED)) {
280                                 copy_v3_v3(n->val, n->val_org);
281                                 n->val_flag[0] &= ~NUM_EDITED;
282                                 n->val_flag[1] &= ~NUM_EDITED;
283                                 n->val_flag[2] &= ~NUM_EDITED;
284                                 n->flag |= NUM_FAKE_EDITED;
285                                 updated = true;
286                                 break;
287                         }
288                         else if (event->shift || !n->str[0]) {
289                                 n->val[idx] = n->val_org[idx];
290                                 n->val_flag[idx] &= ~NUM_EDITED;
291                                 n->str[0] = '\0';
292                                 n->str_cur = 0;
293                                 updated = true;
294                                 break;
295                         }
296                         /* Else, common behavior with DELKEY, only difference is remove char(s) before/after the cursor. */
297                         dir = STRCUR_DIR_PREV;
298                         /* fall-through */
299                 case DELKEY:
300                         if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
301                                 int t_cur = cur = n->str_cur;
302                                 if (event->ctrl) {
303                                         mode = STRCUR_JUMP_DELIM;
304                                 }
305                                 BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
306                                 if (t_cur != cur) {
307                                         if (t_cur < cur) {
308                                                 SWAP(int, t_cur, cur);
309                                                 n->str_cur = cur;
310                                         }
311                                         memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1);  /* +1 for trailing '\0'. */
312                                         updated = true;
313                                 }
314                                 if (!n->str[0]) {
315                                         n->val[idx] = n->val_org[idx];
316                                 }
317                         }
318                         else {
319                                 return false;
320                         }
321                         break;
322                 case LEFTARROWKEY:
323                         dir = STRCUR_DIR_PREV;
324                         /* fall-through */
325                 case RIGHTARROWKEY:
326                         cur = n->str_cur;
327                         if (event->ctrl) {
328                                 mode = STRCUR_JUMP_DELIM;
329                         }
330                         BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
331                         if (cur != n->str_cur) {
332                                 n->str_cur = cur;
333                                 return true;
334                         }
335                         return false;
336                 case HOMEKEY:
337                         if (n->str[0]) {
338                                 n->str_cur = 0;
339                                 return true;
340                         }
341                         return false;
342                 case ENDKEY:
343                         if (n->str[0]) {
344                                 n->str_cur = strlen(n->str);
345                                 return true;
346                         }
347                         return false;
348                 case TABKEY:
349                         n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);
350
351                         idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1);
352                         n->idx = idx;
353                         if (n->val_flag[idx] & NUM_EDITED) {
354                                 value_to_editstr(n, idx);
355                         }
356                         else {
357                                 n->str[0] = '\0';
358                                 n->str_cur = 0;
359                         }
360                         return true;
361                 case PADPERIOD:
362                 case PERIODKEY:
363                         /* Force numdot, some OSs/countries generate a comma char in this case, sic...  (T37992) */
364                         ascii[0] = '.';
365                         utf8_buf = ascii;
366                         break;
367 #if 0  /* Those keys are not directly accessible in all layouts, preventing to generate matching events.
368         * So we use a hack (ascii value) instead, see below.
369         */
370                 case EQUALKEY:
371                 case PADASTERKEY:
372                         if (!(n->flag & NUM_EDIT_FULL)) {
373                                 n->flag |= NUM_EDIT_FULL;
374                                 n->val_flag[idx] |= NUM_EDITED;
375                                 return true;
376                         }
377                         else if (event->ctrl) {
378                                 n->flag &= ~NUM_EDIT_FULL;
379                                 return true;
380                         }
381                         break;
382 #endif
383                 case PADMINUS:
384                 case MINUSKEY:
385                         if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
386                                 n->val_flag[idx] ^= NUM_NEGATE;
387                                 updated = true;
388                         }
389                         break;
390                 case PADSLASHKEY:
391                 case SLASHKEY:
392                         if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
393                                 n->val_flag[idx] ^= NUM_INVERSE;
394                                 updated = true;
395                         }
396                         break;
397                 case CKEY:
398                         if (event->ctrl) {
399                                 /* Copy current str to the copypaste buffer. */
400                                 WM_clipboard_text_set(n->str, 0);
401                                 updated = true;
402                         }
403                         break;
404                 case VKEY:
405                         if (event->ctrl) {
406                                 /* extract the first line from the clipboard */
407                                 int pbuf_len;
408                                 char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);
409
410                                 if (pbuf) {
411                                         const bool success = editstr_insert_at_cursor(n, pbuf, pbuf_len);
412
413                                         MEM_freeN(pbuf);
414                                         if (!success) {
415                                                 return false;
416                                         }
417
418                                         n->val_flag[idx] |= NUM_EDITED;
419                                 }
420                                 updated = true;
421                         }
422                         break;
423                 default:
424                         break;
425         }
426
427         if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) {
428                 utf8_buf = event->utf8_buf;
429                 ascii[0] = event->ascii;
430         }
431
432         /* XXX Hack around keyboards without direct access to '=' nor '*'... */
433         if (ELEM(ascii[0], '=', '*')) {
434                 if (!(n->flag & NUM_EDIT_FULL)) {
435                         n->flag |= NUM_EDIT_FULL;
436                         n->val_flag[idx] |= NUM_EDITED;
437                         return true;
438                 }
439                 else if (event->ctrl) {
440                         n->flag &= ~NUM_EDIT_FULL;
441                         return true;
442                 }
443         }
444
445         /* Up to this point, if we have a ctrl modifier, skip.
446          * This allows to still access most of modals' shortcuts even in numinput mode.
447          */
448         if (!updated && event->ctrl) {
449                 return false;
450         }
451
452         if ((!utf8_buf || !utf8_buf[0]) && ascii[0]) {
453                 /* Fallback to ascii. */
454                 utf8_buf = ascii;
455         }
456
457         if (utf8_buf && utf8_buf[0]) {
458                 if (!(n->flag & NUM_EDIT_FULL)) {
459                         /* In simple edit mode, we only keep a few chars as valid! */
460                         /* no need to decode unicode, ascii is first char only */
461                         if (!editstr_is_simple_numinput(utf8_buf[0])) {
462                                 return false;
463                         }
464                 }
465
466                 if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
467                         return false;
468                 }
469
470                 n->val_flag[idx] |= NUM_EDITED;
471         }
472         else if (!updated) {
473                 return false;
474         }
475
476         /* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
477         if (n->str[0]) {
478 #ifdef WITH_PYTHON
479                 Scene *sce = CTX_data_scene(C);
480                 double val;
481                 char str_unit_convert[NUM_STR_REP_LEN * 6];  /* Should be more than enough! */
482                 const char *default_unit = NULL;
483
484                 /* Use scale_length if needed! */
485                 const float fac = (float)bUnit_getScaleUnit(&sce->unit, n->unit_type[idx], 1.0);
486
487                 /* Make radian default unit when needed. */
488                 if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION)
489                         default_unit = "r";
490
491                 BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert));
492
493                 bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, fac,
494                                     n->unit_sys, n->unit_type[idx]);
495
496                 /* Note: with angles, we always get values as radians here... */
497                 if (BPY_button_exec(C, str_unit_convert, &val, false) != -1) {
498                         n->val[idx] = (float)val;
499                         n->val_flag[idx] &= ~NUM_INVALID;
500                 }
501                 else {
502                         n->val_flag[idx] |= NUM_INVALID;
503                 }
504 #else  /* Very unlikely, but does not harm... */
505                 n->val[idx] = (float)atof(n->str);
506                 (void)C;
507 #endif  /* WITH_PYTHON */
508
509                 if (n->val_flag[idx] & NUM_NEGATE) {
510                         n->val[idx] = -n->val[idx];
511                 }
512                 if (n->val_flag[idx] & NUM_INVERSE) {
513                         n->val[idx] = 1.0f / n->val[idx];
514                 }
515         }
516
517         /* REDRAW SINCE NUMBERS HAVE CHANGED */
518         return true;
519 }