Merge branch 'master' into blender2.8
[blender.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_scene.h"
37 #include "BKE_unit.h"
38
39 #include "DNA_scene_types.h"
40
41 #include "WM_api.h"
42 #include "WM_types.h"
43
44 #ifdef WITH_PYTHON
45 #include "BPY_extern.h"
46 #endif
47
48 #include "ED_numinput.h"
49 #include "UI_interface.h"
50
51 /* Numeric input which isn't allowing full numeric editing. */
52 #define USE_FAKE_EDIT
53
54 /* NumInput.flag */
55 enum {
56         /* (1 << 8) and below are reserved for public flags! */
57         NUM_EDIT_FULL       = (1 << 9),   /* Enable full editing, with units and math operators support. */
58 #ifdef USE_FAKE_EDIT
59         NUM_FAKE_EDITED     = (1 << 10),  /* Fake edited state (temp, avoids issue with backspace). */
60 #endif
61 };
62
63 /* NumInput.val_flag[] */
64 enum {
65         /* (1 << 8) and below are reserved for public flags! */
66         NUM_EDITED          = (1 << 9),    /* User has edited this value somehow. */
67         NUM_INVALID         = (1 << 10),   /* Current expression for this value is invalid. */
68 #ifdef USE_FAKE_EDIT
69         NUM_NEGATE          = (1 << 11),   /* Current expression's result has to be negated. */
70         NUM_INVERSE         = (1 << 12),   /* Current expression's result has to be inverted. */
71 #endif
72 };
73
74 /* ************************** Functions *************************** */
75
76 /* ************************** NUMINPUT **************************** */
77
78 void initNumInput(NumInput *n)
79 {
80         n->idx_max = 0;
81         n->unit_sys = USER_UNIT_NONE;
82         copy_vn_i(n->unit_type, NUM_MAX_ELEMENTS, B_UNIT_NONE);
83         n->unit_use_radians = false;
84
85         n->flag = 0;
86         copy_vn_short(n->val_flag, NUM_MAX_ELEMENTS, 0);
87         zero_v3(n->val);
88         copy_vn_fl(n->val_org, NUM_MAX_ELEMENTS, 0.0f);
89         copy_vn_fl(n->val_inc, NUM_MAX_ELEMENTS, 1.0f);
90
91         n->idx = 0;
92         n->str[0] = '\0';
93         n->str_cur = 0;
94 }
95
96 /* str must be NUM_STR_REP_LEN * (idx_max + 1) length. */
97 void outputNumInput(NumInput *n, char *str, UnitSettings *unit_settings)
98 {
99         short j;
100         const int ln = NUM_STR_REP_LEN;
101         int prec = 2; /* draw-only, and avoids too much issues with radian->degrees conversion. */
102
103         for (j = 0; j <= n->idx_max; j++) {
104                 /* if AFFECTALL and no number typed and cursor not on number, use first number */
105                 const short i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
106
107                 /* Use scale_length if needed! */
108                 const float fac = (float)BKE_scene_unit_scale(unit_settings, n->unit_type[j], 1.0);
109
110                 if (n->val_flag[i] & NUM_EDITED) {
111                         /* Get the best precision, allows us to draw '10.0001' as '10' instead! */
112                         prec = UI_calc_float_precision(prec, (double)n->val[i]);
113                         if (i == n->idx) {
114                                 const char *heading_exp = "", *trailing_exp = "";
115                                 char before_cursor[NUM_STR_REP_LEN];
116                                 char val[16];
117
118 #ifdef USE_FAKE_EDIT
119                                 if (n->val_flag[i] & NUM_NEGATE) {
120                                         heading_exp = (n->val_flag[i] & NUM_INVERSE) ? "-1/(" : "-(";
121                                         trailing_exp = ")";
122                                 }
123                                 else if (n->val_flag[i] & NUM_INVERSE) {
124                                         heading_exp = "1/(";
125                                         trailing_exp = ")";
126                                 }
127 #endif
128
129                                 if (n->val_flag[i] & NUM_INVALID) {
130                                         BLI_strncpy(val, "Invalid", sizeof(val));
131                                 }
132                                 else {
133                                         bUnit_AsString(val, sizeof(val), (double)(n->val[i] * fac), prec,
134                                                        n->unit_sys, n->unit_type[i], true, false);
135                                 }
136
137                                 BLI_strncpy(before_cursor, n->str, n->str_cur + 1);  /* +1 because of trailing '\0' */
138                                 BLI_snprintf(&str[j * ln], ln, "[%s%s|%s%s] = %s",
139                                              heading_exp, before_cursor, &n->str[n->str_cur], trailing_exp, val);
140                         }
141                         else {
142                                 const char *cur = (i == n->idx) ? "|" : "";
143                                 if (n->unit_use_radians && n->unit_type[i] == B_UNIT_ROTATION) {
144                                         /* Radian exception... */
145                                         BLI_snprintf(&str[j * ln], ln, "%s%.6gr%s", cur, n->val[i], cur);
146                                 }
147                                 else {
148                                         char tstr[NUM_STR_REP_LEN];
149                                         bUnit_AsString(tstr, ln, (double)n->val[i], prec, n->unit_sys, n->unit_type[i], true, false);
150                                         BLI_snprintf(&str[j * ln], ln, "%s%s%s", cur, tstr, cur);
151                                 }
152                         }
153                 }
154                 else {
155                         const char *cur = (i == n->idx) ? "|" : "";
156                         BLI_snprintf(&str[j * ln], ln, "%sNONE%s", cur, cur);
157                 }
158                 /* We might have cut some multi-bytes utf8 chars (e.g. trailing '°' of degrees values can become only 'A')... */
159                 BLI_utf8_invalid_strip(&str[j * ln], strlen(&str[j * ln]));
160         }
161 }
162
163 bool hasNumInput(const NumInput *n)
164 {
165         short i;
166
167 #ifdef USE_FAKE_EDIT
168         if (n->flag & NUM_FAKE_EDITED) {
169                 return true;
170         }
171 #endif
172
173         for (i = 0; i <= n->idx_max; i++) {
174                 if (n->val_flag[i] & NUM_EDITED) {
175                         return true;
176                 }
177         }
178
179         return false;
180 }
181
182 /**
183  * \warning \a vec must be set beforehand otherwise we risk uninitialized vars.
184  */
185 bool applyNumInput(NumInput *n, float *vec)
186 {
187         short i, j;
188         float val;
189
190         if (hasNumInput(n)) {
191                 for (j = 0; j <= n->idx_max; j++) {
192 #ifdef USE_FAKE_EDIT
193                         if (n->flag & NUM_FAKE_EDITED) {
194                                 val = n->val[j];
195                         }
196                         else
197 #endif
198                         {
199                                 /* if AFFECTALL and no number typed and cursor not on number, use first number */
200                                 i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
201                                 val = (!(n->val_flag[i] & NUM_EDITED) && n->val_flag[i] & NUM_NULL_ONE) ? 1.0f : n->val[i];
202
203                                 if (n->val_flag[i] & NUM_NO_NEGATIVE && val < 0.0f) {
204                                         val = 0.0f;
205                                 }
206                                 if (n->val_flag[i] & NUM_NO_FRACTION && val != floorf(val)) {
207                                         val = floorf(val + 0.5f);
208                                         if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
209                                                 val = 1.0f;
210                                         }
211                                 }
212                                 else if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
213                                         val = 0.0001f;
214                                 }
215                         }
216                         vec[j] = val;
217                 }
218 #ifdef USE_FAKE_EDIT
219                 n->flag &= ~NUM_FAKE_EDITED;
220 #endif
221                 return true;
222         }
223         else {
224                 /* Else, we set the 'org' values for numinput! */
225                 for (j = 0; j <= n->idx_max; j++) {
226                         n->val[j] = n->val_org[j] = vec[j];
227                 }
228                 return false;
229         }
230 }
231
232
233 static void value_to_editstr(NumInput *n, int idx)
234 {
235         const int prec = 6; /* editing, higher precision needed. */
236         n->str_cur = bUnit_AsString(n->str, NUM_STR_REP_LEN, (double)n->val[idx], prec,
237                                     n->unit_sys, n->unit_type[idx], true, false);
238 }
239
240 static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf_len)
241 {
242         int cur = n->str_cur;
243         int len = strlen(&n->str[cur]) + 1;  /* +1 for the trailing '\0'. */
244         int n_cur = cur + buf_len;
245
246         if (n_cur + len >= NUM_STR_REP_LEN) {
247                 return false;
248         }
249
250         memmove(&n->str[n_cur], &n->str[cur], len);
251         memcpy(&n->str[cur], buf, sizeof(char) * buf_len);
252
253         n->str_cur = n_cur;
254         return true;
255 }
256
257 static bool editstr_is_simple_numinput(const char ascii)
258 {
259         if (ascii >= '0' && ascii <= '9') {
260                 return true;
261         }
262         else if (ascii == '.') {
263                 return true;
264         }
265         else {
266                 return false;
267         }
268 }
269
270 bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
271 {
272         const char *utf8_buf = NULL;
273         char ascii[2] = {'\0', '\0'};
274         bool updated = false;
275         short idx = n->idx, idx_max = n->idx_max;
276         short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
277         int cur;
278
279 #ifdef USE_FAKE_EDIT
280         if (U.flag & USER_FLAG_NUMINPUT_ADVANCED)
281 #endif
282         {
283                 if ((event->ctrl == 0) && (event->alt == 0) && (event->ascii != '\0') &&
284                     strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii))
285                 {
286                         if (!(n->flag & NUM_EDIT_FULL)) {
287                                 n->flag |= NUM_EDITED;
288                                 n->flag |= NUM_EDIT_FULL;
289                                 n->val_flag[idx] |= NUM_EDITED;
290                         }
291                 }
292         }
293
294         switch (event->type) {
295                 case EVT_MODAL_MAP:
296                         if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
297                                 n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
298                                 value_to_editstr(n, idx);
299                                 n->val_flag[idx] |= NUM_EDITED;
300                                 updated = true;
301                         }
302                         else {
303                                 /* might be a char too... */
304                                 utf8_buf = event->utf8_buf;
305                                 ascii[0] = event->ascii;
306                         }
307                         break;
308                 case BACKSPACEKEY:
309                         /* Part specific to backspace... */
310                         if (!(n->val_flag[idx] & NUM_EDITED)) {
311                                 copy_v3_v3(n->val, n->val_org);
312                                 n->val_flag[0] &= ~NUM_EDITED;
313                                 n->val_flag[1] &= ~NUM_EDITED;
314                                 n->val_flag[2] &= ~NUM_EDITED;
315 #ifdef USE_FAKE_EDIT
316                                 n->flag |= NUM_FAKE_EDITED;
317 #else
318                                 n->flag |= NUM_EDIT_FULL;
319 #endif
320                                 updated = true;
321                                 break;
322                         }
323                         else if (event->shift || !n->str[0]) {
324                                 n->val[idx] = n->val_org[idx];
325                                 n->val_flag[idx] &= ~NUM_EDITED;
326                                 n->str[0] = '\0';
327                                 n->str_cur = 0;
328                                 updated = true;
329                                 break;
330                         }
331                         /* Else, common behavior with DELKEY, only difference is remove char(s) before/after the cursor. */
332                         dir = STRCUR_DIR_PREV;
333                         ATTR_FALLTHROUGH;
334                 case DELKEY:
335                         if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
336                                 int t_cur = cur = n->str_cur;
337                                 if (event->ctrl) {
338                                         mode = STRCUR_JUMP_DELIM;
339                                 }
340                                 BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
341                                 if (t_cur != cur) {
342                                         if (t_cur < cur) {
343                                                 SWAP(int, t_cur, cur);
344                                                 n->str_cur = cur;
345                                         }
346                                         memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1);  /* +1 for trailing '\0'. */
347                                         updated = true;
348                                 }
349                                 if (!n->str[0]) {
350                                         n->val[idx] = n->val_org[idx];
351                                 }
352                         }
353                         else {
354                                 return false;
355                         }
356                         break;
357                 case LEFTARROWKEY:
358                         dir = STRCUR_DIR_PREV;
359                         ATTR_FALLTHROUGH;
360                 case RIGHTARROWKEY:
361                         cur = n->str_cur;
362                         if (event->ctrl) {
363                                 mode = STRCUR_JUMP_DELIM;
364                         }
365                         BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
366                         if (cur != n->str_cur) {
367                                 n->str_cur = cur;
368                                 return true;
369                         }
370                         return false;
371                 case HOMEKEY:
372                         if (n->str[0]) {
373                                 n->str_cur = 0;
374                                 return true;
375                         }
376                         return false;
377                 case ENDKEY:
378                         if (n->str[0]) {
379                                 n->str_cur = strlen(n->str);
380                                 return true;
381                         }
382                         return false;
383                 case TABKEY:
384 #ifdef USE_FAKE_EDIT
385                         n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);
386 #endif
387
388                         idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1);
389                         n->idx = idx;
390                         if (n->val_flag[idx] & NUM_EDITED) {
391                                 value_to_editstr(n, idx);
392                         }
393                         else {
394                                 n->str[0] = '\0';
395                                 n->str_cur = 0;
396                         }
397                         return true;
398                 case PADPERIOD:
399                 case PERIODKEY:
400                         /* Force numdot, some OSs/countries generate a comma char in this case, sic...  (T37992) */
401                         ascii[0] = '.';
402                         utf8_buf = ascii;
403                         break;
404 #if 0
405                 /* Those keys are not directly accessible in all layouts, preventing to generate matching events.
406                  * So we use a hack (ascii value) instead, see below.
407                  */
408                 case EQUALKEY:
409                 case PADASTERKEY:
410                         if (!(n->flag & NUM_EDIT_FULL)) {
411                                 n->flag |= NUM_EDIT_FULL;
412                                 n->val_flag[idx] |= NUM_EDITED;
413                                 return true;
414                         }
415                         else if (event->ctrl) {
416                                 n->flag &= ~NUM_EDIT_FULL;
417                                 return true;
418                         }
419                         break;
420 #endif
421
422 #ifdef USE_FAKE_EDIT
423                 case PADMINUS:
424                 case MINUSKEY:
425                         if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
426                                 n->val_flag[idx] ^= NUM_NEGATE;
427                                 updated = true;
428                         }
429                         break;
430                 case PADSLASHKEY:
431                 case SLASHKEY:
432                         if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
433                                 n->val_flag[idx] ^= NUM_INVERSE;
434                                 updated = true;
435                         }
436                         break;
437 #endif
438                 case CKEY:
439                         if (event->ctrl) {
440                                 /* Copy current str to the copypaste buffer. */
441                                 WM_clipboard_text_set(n->str, 0);
442                                 updated = true;
443                         }
444                         break;
445                 case VKEY:
446                         if (event->ctrl) {
447                                 /* extract the first line from the clipboard */
448                                 int pbuf_len;
449                                 char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);
450
451                                 if (pbuf) {
452                                         const bool success = editstr_insert_at_cursor(n, pbuf, pbuf_len);
453
454                                         MEM_freeN(pbuf);
455                                         if (!success) {
456                                                 return false;
457                                         }
458
459                                         n->val_flag[idx] |= NUM_EDITED;
460                                 }
461                                 updated = true;
462                         }
463                         break;
464                 default:
465                         break;
466         }
467
468         if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) {
469                 utf8_buf = event->utf8_buf;
470                 ascii[0] = event->ascii;
471         }
472
473 #ifdef USE_FAKE_EDIT
474         /* XXX Hack around keyboards without direct access to '=' nor '*'... */
475         if (ELEM(ascii[0], '=', '*')) {
476                 if (!(n->flag & NUM_EDIT_FULL)) {
477                         n->flag |= NUM_EDIT_FULL;
478                         n->val_flag[idx] |= NUM_EDITED;
479                         return true;
480                 }
481                 else if (event->ctrl) {
482                         n->flag &= ~NUM_EDIT_FULL;
483                         return true;
484                 }
485         }
486 #endif
487
488         /* Up to this point, if we have a ctrl modifier, skip.
489          * This allows to still access most of modals' shortcuts even in numinput mode.
490          */
491         if (!updated && event->ctrl) {
492                 return false;
493         }
494
495         if ((!utf8_buf || !utf8_buf[0]) && ascii[0]) {
496                 /* Fallback to ascii. */
497                 utf8_buf = ascii;
498         }
499
500         if (utf8_buf && utf8_buf[0]) {
501                 if (!(n->flag & NUM_EDIT_FULL)) {
502                         /* In simple edit mode, we only keep a few chars as valid! */
503                         /* no need to decode unicode, ascii is first char only */
504                         if (!editstr_is_simple_numinput(utf8_buf[0])) {
505                                 return false;
506                         }
507                 }
508
509                 if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
510                         return false;
511                 }
512
513                 n->val_flag[idx] |= NUM_EDITED;
514         }
515         else if (!updated) {
516                 return false;
517         }
518
519         /* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
520         if (n->str[0]) {
521                 const float val_prev = n->val[idx];
522                 double val;
523 #ifdef WITH_PYTHON
524                 Scene *sce = CTX_data_scene(C);
525                 char str_unit_convert[NUM_STR_REP_LEN * 6];  /* Should be more than enough! */
526                 const char *default_unit = NULL;
527
528                 /* Use scale_length if needed! */
529                 const float fac = (float)BKE_scene_unit_scale(&sce->unit, n->unit_type[idx], 1.0);
530
531                 /* Make radian default unit when needed. */
532                 if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION) {
533                         default_unit = "r";
534                 }
535
536                 BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert));
537
538                 bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, fac,
539                                     n->unit_sys, n->unit_type[idx]);
540
541                 /* Note: with angles, we always get values as radians here... */
542                 if (BPY_execute_string_as_number(C, NULL, str_unit_convert, false, &val)) {
543                         n->val[idx] = (float)val;
544                         n->val_flag[idx] &= ~NUM_INVALID;
545                 }
546                 else {
547                         n->val_flag[idx] |= NUM_INVALID;
548                 }
549 #else  /* Very unlikely, but does not harm... */
550                 val = atof(n->str);
551                 n->val[idx] = (float)val;
552                 UNUSED_VARS(C);
553 #endif  /* WITH_PYTHON */
554
555
556 #ifdef USE_FAKE_EDIT
557                 if (n->val_flag[idx] & NUM_NEGATE) {
558                         n->val[idx] = -n->val[idx];
559                 }
560                 if (n->val_flag[idx] & NUM_INVERSE) {
561                         val = n->val[idx];
562                         /* If we invert on radians when user is in degrees, you get unexpected results... See T53463. */
563                         if (!n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION) {
564                                 val = RAD2DEG(val);
565                         }
566                         val = 1.0 / val;
567                         if (!n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION) {
568                                 val = DEG2RAD(val);
569                         }
570                         n->val[idx] = (float)val;
571                 }
572 #endif
573
574                 if (UNLIKELY(!isfinite(n->val[idx]))) {
575                         n->val[idx] = val_prev;
576                         n->val_flag[idx] |= NUM_INVALID;
577                 }
578         }
579
580         /* REDRAW SINCE NUMBERS HAVE CHANGED */
581         return true;
582 }