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