UI: ignore events in empty region overlap areas
[blender.git] / source / blender / editors / interface / interface_handlers.c
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  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edinterface
22  */
23
24 #include <float.h>
25 #include <limits.h>
26 #include <math.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <assert.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_brush_types.h"
35
36 #include "DNA_scene_types.h"
37 #include "DNA_screen_types.h"
38
39 #include "BLI_math.h"
40 #include "BLI_listbase.h"
41 #include "BLI_linklist.h"
42 #include "BLI_string.h"
43 #include "BLI_string_utf8.h"
44 #include "BLI_string_cursor_utf8.h"
45 #include "BLI_rect.h"
46 #include "BLI_utildefines.h"
47
48 #include "PIL_time.h"
49
50 #include "BKE_colorband.h"
51 #include "BKE_blender_undo.h"
52 #include "BKE_brush.h"
53 #include "BKE_colortools.h"
54 #include "BKE_context.h"
55 #include "BKE_report.h"
56 #include "BKE_screen.h"
57 #include "BKE_tracking.h"
58 #include "BKE_unit.h"
59 #include "BKE_paint.h"
60
61 #include "IMB_colormanagement.h"
62
63 #include "ED_screen.h"
64 #include "ED_undo.h"
65
66 #include "UI_interface.h"
67 #include "UI_view2d.h"
68
69 #include "BLF_api.h"
70
71 #include "interface_intern.h"
72
73 #include "RNA_access.h"
74
75 #include "WM_api.h"
76 #include "WM_types.h"
77 #include "wm_event_system.h"
78
79 #ifdef WITH_INPUT_IME
80 #  include "wm_window.h"
81 #  include "BLT_translation.h"
82 #  include "BLT_lang.h"
83 #endif
84
85 /* place the mouse at the scaled down location when un-grabbing */
86 #define USE_CONT_MOUSE_CORRECT
87 /* support dragging toggle buttons */
88 #define USE_DRAG_TOGGLE
89
90 /* support dragging multiple number buttons at once */
91 #define USE_DRAG_MULTINUM
92
93 /* allow dragging/editing all other selected items at once */
94 #define USE_ALLSELECT
95
96 /* so we can avoid very small mouse-moves from jumping away from keyboard navigation [#34936] */
97 #define USE_KEYNAV_LIMIT
98
99 /* drag popups by their header */
100 #define USE_DRAG_POPUP
101
102 #define UI_MAX_PASSWORD_STR 128
103
104 /* proto */
105 static int ui_do_but_EXIT(bContext *C,
106                           uiBut *but,
107                           struct uiHandleButtonData *data,
108                           const wmEvent *event);
109 static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b);
110 static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str);
111
112 #ifdef USE_KEYNAV_LIMIT
113 static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event);
114 static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event);
115 #endif
116
117 /* -------------------------------------------------------------------- */
118 /** \name Structs & Defines
119  * \{ */
120
121 #define BUTTON_FLASH_DELAY 0.020
122 #define MENU_SCROLL_INTERVAL 0.1
123 #define PIE_MENU_INTERVAL 0.01
124 #define BUTTON_AUTO_OPEN_THRESH 0.2
125 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0
126 /* pixels to move the cursor to get out of keyboard navigation */
127 #define BUTTON_KEYNAV_PX_LIMIT 8
128
129 #define MENU_TOWARDS_MARGIN 20      /* margin in pixels */
130 #define MENU_TOWARDS_WIGGLE_ROOM 64 /* tolerance in pixels */
131 /* drag-lock distance threshold in pixels */
132 #define BUTTON_DRAGLOCK_THRESH 3
133
134 typedef enum uiButtonActivateType {
135   BUTTON_ACTIVATE_OVER,
136   BUTTON_ACTIVATE,
137   BUTTON_ACTIVATE_APPLY,
138   BUTTON_ACTIVATE_TEXT_EDITING,
139   BUTTON_ACTIVATE_OPEN,
140 } uiButtonActivateType;
141
142 typedef enum uiHandleButtonState {
143   BUTTON_STATE_INIT,
144   BUTTON_STATE_HIGHLIGHT,
145   BUTTON_STATE_WAIT_FLASH,
146   BUTTON_STATE_WAIT_RELEASE,
147   BUTTON_STATE_WAIT_KEY_EVENT,
148   BUTTON_STATE_NUM_EDITING,
149   BUTTON_STATE_TEXT_EDITING,
150   BUTTON_STATE_TEXT_SELECTING,
151   BUTTON_STATE_MENU_OPEN,
152   BUTTON_STATE_WAIT_DRAG,
153   BUTTON_STATE_EXIT,
154 } uiHandleButtonState;
155
156 #ifdef USE_ALLSELECT
157
158 /* Unfortunately there's no good way handle more generally:
159  * (propagate single clicks on layer buttons to other objects) */
160 #  define USE_ALLSELECT_LAYER_HACK
161
162 typedef struct uiSelectContextElem {
163   PointerRNA ptr;
164   union {
165     bool val_b;
166     int val_i;
167     float val_f;
168   };
169 } uiSelectContextElem;
170
171 typedef struct uiSelectContextStore {
172   uiSelectContextElem *elems;
173   int elems_len;
174   bool do_free;
175   bool is_enabled;
176   /* When set, simply copy values (don't apply difference).
177    * Rules are:
178    * - dragging numbers uses delta.
179    * - typing in values will assign to all. */
180   bool is_copy;
181 } uiSelectContextStore;
182
183 static bool ui_selectcontext_begin(bContext *C,
184                                    uiBut *but,
185                                    struct uiSelectContextStore *selctx_data);
186 static void ui_selectcontext_end(uiBut *but, uiSelectContextStore *selctx_data);
187 static void ui_selectcontext_apply(bContext *C,
188                                    uiBut *but,
189                                    struct uiSelectContextStore *selctx_data,
190                                    const double value,
191                                    const double value_orig);
192
193 #  define IS_ALLSELECT_EVENT(event) ((event)->alt != 0)
194
195 /** just show a tinted color so users know its activated */
196 #  define UI_BUT_IS_SELECT_CONTEXT UI_BUT_NODE_ACTIVE
197
198 #endif /* USE_ALLSELECT */
199
200 #ifdef USE_DRAG_MULTINUM
201
202 /**
203  * how far to drag before we check for gesture direction (in pixels),
204  * note: half the height of a button is about right... */
205 #  define DRAG_MULTINUM_THRESHOLD_DRAG_X (UI_UNIT_Y / 4)
206
207 /**
208  * How far to drag horizontally
209  * before we stop checking which buttons the gesture spans (in pixels),
210  * locking down the buttons so we can drag freely without worrying about vertical movement.
211  */
212 #  define DRAG_MULTINUM_THRESHOLD_DRAG_Y (UI_UNIT_Y / 4)
213
214 /**
215  * How strict to be when detecting a vertical gesture:
216  * [0.5 == sloppy], [0.9 == strict], (unsigned dot-product).
217  *
218  * \note We should be quite strict here,
219  * since doing a vertical gesture by accident should be avoided,
220  * however with some care a user should be able to do a vertical movement without _missing_.
221  */
222 #  define DRAG_MULTINUM_THRESHOLD_VERTICAL (0.75f)
223
224 /* a simple version of uiHandleButtonData when accessing multiple buttons */
225 typedef struct uiButMultiState {
226   double origvalue;
227   uiBut *but;
228
229 #  ifdef USE_ALLSELECT
230   uiSelectContextStore select_others;
231 #  endif
232 } uiButMultiState;
233
234 typedef struct uiHandleButtonMulti {
235   enum {
236     /** gesture direction unknown, wait until mouse has moved enough... */
237     BUTTON_MULTI_INIT_UNSET = 0,
238     /** vertical gesture detected, flag buttons interactively (UI_BUT_DRAG_MULTI) */
239     BUTTON_MULTI_INIT_SETUP,
240     /** flag buttons finished, apply horizontal motion to active and flagged */
241     BUTTON_MULTI_INIT_ENABLE,
242     /** vertical gesture _not_ detected, take no further action */
243     BUTTON_MULTI_INIT_DISABLE,
244   } init;
245
246   bool has_mbuts; /* any buttons flagged UI_BUT_DRAG_MULTI */
247   LinkNode *mbuts;
248   uiButStore *bs_mbuts;
249
250   bool is_proportional;
251
252   /* In some cases we directly apply the changes to multiple buttons,
253    * so we don't want to do it twice. */
254   bool skip;
255
256   /* before activating, we need to check gesture direction accumulate signed cursor movement
257    * here so we can tell if this is a vertical motion or not. */
258   float drag_dir[2];
259
260   /* values copied direct from event->x,y
261    * used to detect buttons between the current and initial mouse position */
262   int drag_start[2];
263
264   /* store x location once BUTTON_MULTI_INIT_SETUP is set,
265    * moving outside this sets BUTTON_MULTI_INIT_ENABLE */
266   int drag_lock_x;
267
268 } uiHandleButtonMulti;
269
270 #endif /* USE_DRAG_MULTINUM */
271
272 typedef struct uiHandleButtonData {
273   wmWindowManager *wm;
274   wmWindow *window;
275   ARegion *region;
276
277   bool interactive;
278
279   /* overall state */
280   uiHandleButtonState state;
281   int retval;
282   /* booleans (could be made into flags) */
283   bool cancel, escapecancel;
284   bool applied, applied_interactive;
285   bool changed_cursor;
286   wmTimer *flashtimer;
287
288   /* edited value */
289   /* use 'ui_textedit_string_set' to assign new strings */
290   char *str;
291   char *origstr;
292   double value, origvalue, startvalue;
293   float vec[3], origvec[3];
294 #if 0 /* UNUSED */
295   int togdual, togonly;
296 #endif
297   ColorBand *coba;
298
299   /* tooltip */
300   uint tooltip_force : 1;
301
302   /* auto open */
303   bool used_mouse;
304   wmTimer *autoopentimer;
305
306   /* auto open (hold) */
307   wmTimer *hold_action_timer;
308
309   /* text selection/editing */
310   /* size of 'str' (including terminator) */
311   int maxlen;
312   /* Button text selection:
313    * extension direction, selextend, inside ui_do_but_TEX */
314   enum {
315     EXTEND_NONE = 0,
316     EXTEND_LEFT = 1,
317     EXTEND_RIGHT = 2,
318   } selextend;
319   float selstartx;
320   /* allow to realloc str/editstr and use 'maxlen' to track alloc size (maxlen + 1) */
321   bool is_str_dynamic;
322
323   /* number editing / dragging */
324   /* coords are Window/uiBlock relative (depends on the button) */
325   int draglastx, draglasty;
326   int dragstartx, dragstarty;
327   int draglastvalue;
328   int dragstartvalue;
329   bool dragchange, draglock;
330   int dragsel;
331   float dragf, dragfstart;
332   CBData *dragcbd;
333
334 #ifdef USE_CONT_MOUSE_CORRECT
335   /* when ungrabbing buttons which are #ui_but_is_cursor_warp(),
336    * we may want to position them.
337    * FLT_MAX signifies do-nothing, use #ui_block_to_window_fl()
338    * to get this into a usable space. */
339   float ungrab_mval[2];
340 #endif
341
342   /* menu open (watch UI_screen_free_active_but) */
343   uiPopupBlockHandle *menu;
344   int menuretval;
345
346   /* search box (watch UI_screen_free_active_but) */
347   ARegion *searchbox;
348 #ifdef USE_KEYNAV_LIMIT
349   struct uiKeyNavLock searchbox_keynav_state;
350 #endif
351
352 #ifdef USE_DRAG_MULTINUM
353   /* Multi-buttons will be updated in unison with the active button. */
354   uiHandleButtonMulti multi_data;
355 #endif
356
357 #ifdef USE_ALLSELECT
358   uiSelectContextStore select_others;
359 #endif
360
361   /* post activate */
362   uiButtonActivateType posttype;
363   uiBut *postbut;
364 } uiHandleButtonData;
365
366 typedef struct uiAfterFunc {
367   struct uiAfterFunc *next, *prev;
368
369   uiButHandleFunc func;
370   void *func_arg1;
371   void *func_arg2;
372
373   uiButHandleNFunc funcN;
374   void *func_argN;
375
376   uiButHandleRenameFunc rename_func;
377   void *rename_arg1;
378   void *rename_orig;
379
380   uiBlockHandleFunc handle_func;
381   void *handle_func_arg;
382   int retval;
383
384   uiMenuHandleFunc butm_func;
385   void *butm_func_arg;
386   int a2;
387
388   wmOperator *popup_op;
389   wmOperatorType *optype;
390   int opcontext;
391   PointerRNA *opptr;
392
393   PointerRNA rnapoin;
394   PropertyRNA *rnaprop;
395
396   bContextStore *context;
397
398   char undostr[BKE_UNDO_STR_MAX];
399 } uiAfterFunc;
400
401 static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type);
402 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state);
403 static void button_activate_exit(
404     bContext *C, uiBut *but, uiHandleButtonData *data, const bool mousemove, const bool onfree);
405 static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *userdata);
406 static void ui_handle_button_activate(bContext *C,
407                                       ARegion *ar,
408                                       uiBut *but,
409                                       uiButtonActivateType type);
410
411 #ifdef USE_DRAG_MULTINUM
412 static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block);
413 static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut *but);
414 #endif
415
416 /* buttons clipboard */
417 static ColorBand but_copypaste_coba = {0};
418 static CurveMapping but_copypaste_curve = {0};
419 static bool but_copypaste_curve_alive = false;
420
421 /** \} */
422
423 /* -------------------------------------------------------------------- */
424 /** \name UI Queries
425  * \{ */
426
427 bool ui_but_is_editing(const uiBut *but)
428 {
429   uiHandleButtonData *data = but->active;
430   return (data && ELEM(data->state, BUTTON_STATE_TEXT_EDITING, BUTTON_STATE_NUM_EDITING));
431 }
432
433 /* assumes event type is MOUSEPAN */
434 void ui_pan_to_scroll(const wmEvent *event, int *type, int *val)
435 {
436   static int lastdy = 0;
437   int dy = event->prevy - event->y;
438
439   /* This event should be originally from event->type,
440    * converting wrong event into wheel is bad, see [#33803] */
441   BLI_assert(*type == MOUSEPAN);
442
443   /* sign differs, reset */
444   if ((dy > 0 && lastdy < 0) || (dy < 0 && lastdy > 0)) {
445     lastdy = dy;
446   }
447   else {
448     lastdy += dy;
449
450     if (ABS(lastdy) > (int)UI_UNIT_Y) {
451       if (U.uiflag2 & USER_TRACKPAD_NATURAL) {
452         dy = -dy;
453       }
454
455       *val = KM_PRESS;
456
457       if (dy > 0) {
458         *type = WHEELUPMOUSE;
459       }
460       else {
461         *type = WHEELDOWNMOUSE;
462       }
463
464       lastdy = 0;
465     }
466   }
467 }
468
469 static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b)
470 {
471   return ((but_a->type == but_b->type) && (but_a->alignnr == but_b->alignnr) &&
472           (but_a->poin == but_b->poin) && (but_a->rnapoin.type == but_b->rnapoin.type) &&
473           (but_a->rnaprop == but_b->rnaprop));
474 }
475
476 /**
477  * Finds the pressed button in an aligned row (typically an expanded enum).
478  *
479  * \param direction: Use when there may be multiple buttons pressed.
480  */
481 uiBut *ui_but_find_select_in_enum(uiBut *but, int direction)
482 {
483   uiBut *but_iter = but;
484   uiBut *but_found = NULL;
485   BLI_assert(ELEM(direction, -1, 1));
486
487   while ((but_iter->prev) && ui_but_find_select_in_enum__cmp(but_iter->prev, but)) {
488     but_iter = but_iter->prev;
489   }
490
491   while (but_iter && ui_but_find_select_in_enum__cmp(but_iter, but)) {
492     if (but_iter->flag & UI_SELECT) {
493       but_found = but_iter;
494       if (direction == 1) {
495         break;
496       }
497     }
498     but_iter = but_iter->next;
499   }
500
501   return but_found;
502 }
503
504 static float ui_mouse_scale_warp_factor(const bool shift)
505 {
506   return shift ? 0.05f : 1.0f;
507 }
508
509 static void ui_mouse_scale_warp(uiHandleButtonData *data,
510                                 const float mx,
511                                 const float my,
512                                 float *r_mx,
513                                 float *r_my,
514                                 const bool shift)
515 {
516   const float fac = ui_mouse_scale_warp_factor(shift);
517
518   /* slow down the mouse, this is fairly picky */
519   *r_mx = (data->dragstartx * (1.0f - fac) + mx * fac);
520   *r_my = (data->dragstarty * (1.0f - fac) + my * fac);
521 }
522
523 /** \} */
524
525 /* -------------------------------------------------------------------- */
526 /** \name UI Utilities
527  * \{ */
528
529 /**
530  * Ignore mouse movements within some horizontal pixel threshold before starting to drag
531  */
532 static bool ui_but_dragedit_update_mval(uiHandleButtonData *data, int mx)
533 {
534   if (mx == data->draglastx) {
535     return false;
536   }
537
538   if (data->draglock) {
539     if (ABS(mx - data->dragstartx) <= BUTTON_DRAGLOCK_THRESH) {
540       return false;
541     }
542 #ifdef USE_DRAG_MULTINUM
543     if (ELEM(data->multi_data.init, BUTTON_MULTI_INIT_UNSET, BUTTON_MULTI_INIT_SETUP)) {
544       return false;
545     }
546 #endif
547     data->draglock = false;
548     data->dragstartx = mx; /* ignore mouse movement within drag-lock */
549   }
550
551   return true;
552 }
553
554 /** \} */
555
556 /* -------------------------------------------------------------------- */
557 /** \name Button Snap Values
558  *
559  * \{ */
560
561 enum eSnapType {
562   SNAP_OFF = 0,
563   SNAP_ON,
564   SNAP_ON_SMALL,
565 };
566
567 static enum eSnapType ui_event_to_snap(const wmEvent *event)
568 {
569   return (event->ctrl) ? (event->shift) ? SNAP_ON_SMALL : SNAP_ON : SNAP_OFF;
570 }
571
572 static bool ui_event_is_snap(const wmEvent *event)
573 {
574   return (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY) ||
575           ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY));
576 }
577
578 static void ui_color_snap_hue(const enum eSnapType snap, float *r_hue)
579 {
580   const float snap_increment = (snap == SNAP_ON_SMALL) ? 24 : 12;
581   BLI_assert(snap != SNAP_OFF);
582   *r_hue = roundf((*r_hue) * snap_increment) / snap_increment;
583 }
584
585 /** \} */
586
587 /* -------------------------------------------------------------------- */
588 /** \name Button Apply/Revert
589  * \{ */
590
591 static ListBase UIAfterFuncs = {NULL, NULL};
592
593 static uiAfterFunc *ui_afterfunc_new(void)
594 {
595   uiAfterFunc *after;
596
597   after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
598
599   BLI_addtail(&UIAfterFuncs, after);
600
601   return after;
602 }
603
604 /**
605  * For executing operators after the button is pressed.
606  * (some non operator buttons need to trigger operators), see: [#37795]
607  *
608  * \note Can only call while handling buttons.
609  */
610 PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
611 {
612   PointerRNA *ptr = NULL;
613   uiAfterFunc *after = ui_afterfunc_new();
614
615   after->optype = ot;
616   after->opcontext = opcontext;
617
618   if (create_props) {
619     ptr = MEM_callocN(sizeof(PointerRNA), __func__);
620     WM_operator_properties_create_ptr(ptr, ot);
621     after->opptr = ptr;
622   }
623
624   return ptr;
625 }
626
627 static void popup_check(bContext *C, wmOperator *op)
628 {
629   if (op && op->type->check) {
630     op->type->check(C, op);
631   }
632 }
633
634 /**
635  * Check if a #uiAfterFunc is needed for this button.
636  */
637 static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but)
638 {
639   return (but->func || but->funcN || but->rename_func || but->optype || but->rnaprop ||
640           block->handle_func || (but->type == UI_BTYPE_BUT_MENU && block->butm_func) ||
641           (block->handle && block->handle->popup_op));
642 }
643
644 static void ui_apply_but_func(bContext *C, uiBut *but)
645 {
646   uiAfterFunc *after;
647   uiBlock *block = but->block;
648
649   /* these functions are postponed and only executed after all other
650    * handling is done, i.e. menus are closed, in order to avoid conflicts
651    * with these functions removing the buttons we are working with */
652
653   if (ui_afterfunc_check(block, but)) {
654     after = ui_afterfunc_new();
655
656     if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) {
657       /* exception, this will crash due to removed button otherwise */
658       but->func(C, but->func_arg1, but->func_arg2);
659     }
660     else {
661       after->func = but->func;
662     }
663
664     after->func_arg1 = but->func_arg1;
665     after->func_arg2 = but->func_arg2;
666
667     after->funcN = but->funcN;
668     after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL;
669
670     after->rename_func = but->rename_func;
671     after->rename_arg1 = but->rename_arg1;
672     after->rename_orig = but->rename_orig; /* needs free! */
673
674     after->handle_func = block->handle_func;
675     after->handle_func_arg = block->handle_func_arg;
676     after->retval = but->retval;
677
678     if (but->type == UI_BTYPE_BUT_MENU) {
679       after->butm_func = block->butm_func;
680       after->butm_func_arg = block->butm_func_arg;
681       after->a2 = but->a2;
682     }
683
684     if (block->handle) {
685       after->popup_op = block->handle->popup_op;
686     }
687
688     after->optype = but->optype;
689     after->opcontext = but->opcontext;
690     after->opptr = but->opptr;
691
692     after->rnapoin = but->rnapoin;
693     after->rnaprop = but->rnaprop;
694
695     if (but->context) {
696       after->context = CTX_store_copy(but->context);
697     }
698
699     but->optype = NULL;
700     but->opcontext = 0;
701     but->opptr = NULL;
702   }
703 }
704
705 /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */
706 static void ui_apply_but_undo(uiBut *but)
707 {
708   uiAfterFunc *after;
709
710   if (but->flag & UI_BUT_UNDO) {
711     const char *str = NULL;
712
713     /* define which string to use for undo */
714     if (but->type == UI_BTYPE_MENU) {
715       str = but->drawstr;
716     }
717     else if (but->drawstr[0]) {
718       str = but->drawstr;
719     }
720     else {
721       str = but->tip;
722     }
723
724     /* fallback, else we don't get an undo! */
725     if (str == NULL || str[0] == '\0') {
726       str = "Unknown Action";
727     }
728
729     /* delayed, after all other funcs run, popups are closed, etc */
730     after = ui_afterfunc_new();
731     BLI_strncpy(after->undostr, str, sizeof(after->undostr));
732   }
733 }
734
735 static void ui_apply_but_autokey(bContext *C, uiBut *but)
736 {
737   Scene *scene = CTX_data_scene(C);
738
739   /* try autokey */
740   ui_but_anim_autokey(C, but, scene, scene->r.cfra);
741
742   /* make a little report about what we've done! */
743   if (but->rnaprop) {
744     char *buf;
745
746     if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) {
747       return;
748     }
749
750     buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex);
751     if (buf) {
752       BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf);
753       MEM_freeN(buf);
754
755       WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
756     }
757   }
758 }
759
760 static void ui_apply_but_funcs_after(bContext *C)
761 {
762   uiAfterFunc *afterf, after;
763   PointerRNA opptr;
764   ListBase funcs;
765
766   /* copy to avoid recursive calls */
767   funcs = UIAfterFuncs;
768   BLI_listbase_clear(&UIAfterFuncs);
769
770   for (afterf = funcs.first; afterf; afterf = after.next) {
771     after = *afterf; /* copy to avoid memleak on exit() */
772     BLI_freelinkN(&funcs, afterf);
773
774     if (after.context) {
775       CTX_store_set(C, after.context);
776     }
777
778     if (after.popup_op) {
779       popup_check(C, after.popup_op);
780     }
781
782     if (after.opptr) {
783       /* free in advance to avoid leak on exit */
784       opptr = *after.opptr;
785       MEM_freeN(after.opptr);
786     }
787
788     if (after.optype) {
789       WM_operator_name_call_ptr(C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL);
790     }
791
792     if (after.opptr) {
793       WM_operator_properties_free(&opptr);
794     }
795
796     if (after.rnapoin.data) {
797       RNA_property_update(C, &after.rnapoin, after.rnaprop);
798     }
799
800     if (after.context) {
801       CTX_store_set(C, NULL);
802       CTX_store_free(after.context);
803     }
804
805     if (after.func) {
806       after.func(C, after.func_arg1, after.func_arg2);
807     }
808     if (after.funcN) {
809       after.funcN(C, after.func_argN, after.func_arg2);
810     }
811     if (after.func_argN) {
812       MEM_freeN(after.func_argN);
813     }
814
815     if (after.handle_func) {
816       after.handle_func(C, after.handle_func_arg, after.retval);
817     }
818     if (after.butm_func) {
819       after.butm_func(C, after.butm_func_arg, after.a2);
820     }
821
822     if (after.rename_func) {
823       after.rename_func(C, after.rename_arg1, after.rename_orig);
824     }
825     if (after.rename_orig) {
826       MEM_freeN(after.rename_orig);
827     }
828
829     if (after.undostr[0]) {
830       ED_undo_push(C, after.undostr);
831     }
832   }
833 }
834
835 static void ui_apply_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data)
836 {
837   ui_apply_but_func(C, but);
838
839   data->retval = but->retval;
840   data->applied = true;
841 }
842
843 static void ui_apply_but_BUTM(bContext *C, uiBut *but, uiHandleButtonData *data)
844 {
845   ui_but_value_set(but, but->hardmin);
846   ui_apply_but_func(C, but);
847
848   data->retval = but->retval;
849   data->applied = true;
850 }
851
852 static void ui_apply_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data)
853 {
854   if (but->type == UI_BTYPE_MENU) {
855     ui_but_value_set(but, data->value);
856   }
857
858   ui_but_update_edited(but);
859   ui_apply_but_func(C, but);
860   data->retval = but->retval;
861   data->applied = true;
862 }
863
864 static void ui_apply_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data)
865 {
866   const double value = ui_but_value_get(but);
867   int value_toggle;
868   if (but->bit) {
869     value_toggle = UI_BITBUT_VALUE_TOGGLED((int)value, but->bitnr);
870   }
871   else {
872     value_toggle = (value == 0.0);
873     if (ELEM(but->type, UI_BTYPE_TOGGLE_N, UI_BTYPE_ICON_TOGGLE_N, UI_BTYPE_CHECKBOX_N)) {
874       value_toggle = !value_toggle;
875     }
876   }
877
878   ui_but_value_set(but, (double)value_toggle);
879   if (but->type == UI_BTYPE_ICON_TOGGLE || but->type == UI_BTYPE_ICON_TOGGLE_N) {
880     ui_but_update_edited(but);
881   }
882
883   ui_apply_but_func(C, but);
884
885   data->retval = but->retval;
886   data->applied = true;
887 }
888
889 static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
890 {
891   uiBut *bt;
892
893   ui_but_value_set(but, but->hardmax);
894
895   ui_apply_but_func(C, but);
896
897   /* states of other row buttons */
898   for (bt = block->buttons.first; bt; bt = bt->next) {
899     if (bt != but && bt->poin == but->poin && ELEM(bt->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) {
900       ui_but_update_edited(bt);
901     }
902   }
903
904   data->retval = but->retval;
905   data->applied = true;
906 }
907
908 static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
909 {
910   if (!data->str) {
911     return;
912   }
913
914   ui_but_string_set(C, but, data->str);
915   ui_but_update_edited(but);
916
917   /* give butfunc a copy of the original text too.
918    * feature used for bone renaming, channels, etc.
919    * afterfunc frees rename_orig */
920   if (data->origstr && (but->flag & UI_BUT_TEXTEDIT_UPDATE)) {
921     /* In this case, we need to keep origstr available,
922      * to restore real org string in case we cancel after having typed something already. */
923     but->rename_orig = BLI_strdup(data->origstr);
924   }
925   /* only if there are afterfuncs, otherwise 'renam_orig' isn't freed */
926   else if (ui_afterfunc_check(but->block, but)) {
927     but->rename_orig = data->origstr;
928     data->origstr = NULL;
929   }
930   ui_apply_but_func(C, but);
931
932   data->retval = but->retval;
933   data->applied = true;
934 }
935
936 static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data)
937 {
938   if (data->str) {
939     ui_but_string_set(C, but, data->str);
940     ui_but_update_edited(but);
941   }
942   else {
943     ui_but_value_set(but, but->hardmax);
944     ui_apply_but_func(C, but);
945   }
946
947   data->retval = but->retval;
948   data->applied = true;
949 }
950
951 static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
952 {
953   if (data->str) {
954     if (ui_but_string_set(C, but, data->str)) {
955       data->value = ui_but_value_get(but);
956     }
957     else {
958       data->cancel = true;
959       return;
960     }
961   }
962   else {
963     ui_but_value_set(but, data->value);
964   }
965
966   ui_but_update_edited(but);
967   ui_apply_but_func(C, but);
968
969   data->retval = but->retval;
970   data->applied = true;
971 }
972
973 static void ui_apply_but_VEC(bContext *C, uiBut *but, uiHandleButtonData *data)
974 {
975   ui_but_v3_set(but, data->vec);
976   ui_but_update_edited(but);
977   ui_apply_but_func(C, but);
978
979   data->retval = but->retval;
980   data->applied = true;
981 }
982
983 static void ui_apply_but_COLORBAND(bContext *C, uiBut *but, uiHandleButtonData *data)
984 {
985   ui_apply_but_func(C, but);
986   data->retval = but->retval;
987   data->applied = true;
988 }
989
990 static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data)
991 {
992   ui_apply_but_func(C, but);
993   data->retval = but->retval;
994   data->applied = true;
995 }
996
997 /** \} */
998
999 /* -------------------------------------------------------------------- */
1000 /** \name Button Drag Multi-Number
1001  * \{ */
1002
1003 #ifdef USE_DRAG_MULTINUM
1004
1005 /* small multi-but api */
1006 static void ui_multibut_add(uiHandleButtonData *data, uiBut *but)
1007 {
1008   uiButMultiState *mbut_state;
1009
1010   BLI_assert(but->flag & UI_BUT_DRAG_MULTI);
1011   BLI_assert(data->multi_data.has_mbuts);
1012
1013   mbut_state = MEM_callocN(sizeof(*mbut_state), __func__);
1014   mbut_state->but = but;
1015   mbut_state->origvalue = ui_but_value_get(but);
1016
1017   BLI_linklist_prepend(&data->multi_data.mbuts, mbut_state);
1018
1019   UI_butstore_register(data->multi_data.bs_mbuts, &mbut_state->but);
1020 }
1021
1022 static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut *but)
1023 {
1024   LinkNode *l;
1025
1026   for (l = data->multi_data.mbuts; l; l = l->next) {
1027     uiButMultiState *mbut_state;
1028
1029     mbut_state = l->link;
1030
1031     if (mbut_state->but == but) {
1032       return mbut_state;
1033     }
1034   }
1035
1036   return NULL;
1037 }
1038
1039 static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block)
1040 {
1041   uiBut *but;
1042
1043   for (but = block->buttons.first; but; but = but->next) {
1044     if (but->flag & UI_BUT_DRAG_MULTI) {
1045       uiButMultiState *mbut_state = ui_multibut_lookup(data, but);
1046       if (mbut_state) {
1047         ui_but_value_set(but, mbut_state->origvalue);
1048
1049 #  ifdef USE_ALLSELECT
1050         if (mbut_state->select_others.elems_len > 0) {
1051           ui_selectcontext_apply(
1052               C, but, &mbut_state->select_others, mbut_state->origvalue, mbut_state->origvalue);
1053         }
1054 #  else
1055         UNUSED_VARS(C);
1056 #  endif
1057       }
1058     }
1059   }
1060 }
1061
1062 static void ui_multibut_free(uiHandleButtonData *data, uiBlock *block)
1063 {
1064 #  ifdef USE_ALLSELECT
1065   if (data->multi_data.mbuts) {
1066     LinkNode *list = data->multi_data.mbuts;
1067     while (list) {
1068       LinkNode *next = list->next;
1069       uiButMultiState *mbut_state = list->link;
1070
1071       if (mbut_state->select_others.elems) {
1072         MEM_freeN(mbut_state->select_others.elems);
1073       }
1074
1075       MEM_freeN(list->link);
1076       MEM_freeN(list);
1077       list = next;
1078     }
1079   }
1080 #  else
1081   BLI_linklist_freeN(data->multi_data.mbuts);
1082 #  endif
1083
1084   data->multi_data.mbuts = NULL;
1085
1086   if (data->multi_data.bs_mbuts) {
1087     UI_butstore_free(block, data->multi_data.bs_mbuts);
1088     data->multi_data.bs_mbuts = NULL;
1089   }
1090 }
1091
1092 static bool ui_multibut_states_tag(uiBut *but_active,
1093                                    uiHandleButtonData *data,
1094                                    const wmEvent *event)
1095 {
1096   uiBut *but;
1097   float seg[2][2];
1098   bool changed = false;
1099
1100   seg[0][0] = data->multi_data.drag_start[0];
1101   seg[0][1] = data->multi_data.drag_start[1];
1102
1103   seg[1][0] = event->x;
1104   seg[1][1] = event->y;
1105
1106   BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_SETUP);
1107
1108   ui_window_to_block_fl(data->region, but_active->block, &seg[0][0], &seg[0][1]);
1109   ui_window_to_block_fl(data->region, but_active->block, &seg[1][0], &seg[1][1]);
1110
1111   data->multi_data.has_mbuts = false;
1112
1113   /* follow ui_but_find_mouse_over_ex logic */
1114   for (but = but_active->block->buttons.first; but; but = but->next) {
1115     bool drag_prev = false;
1116     bool drag_curr = false;
1117
1118     /* re-set each time */
1119     if (but->flag & UI_BUT_DRAG_MULTI) {
1120       but->flag &= ~UI_BUT_DRAG_MULTI;
1121       drag_prev = true;
1122     }
1123
1124     if (ui_but_is_interactive(but, false)) {
1125
1126       /* drag checks */
1127       if (but_active != but) {
1128         if (ui_but_is_compatible(but_active, but)) {
1129
1130           BLI_assert(but->active == NULL);
1131
1132           /* finally check for overlap */
1133           if (BLI_rctf_isect_segment(&but->rect, seg[0], seg[1])) {
1134
1135             but->flag |= UI_BUT_DRAG_MULTI;
1136             data->multi_data.has_mbuts = true;
1137             drag_curr = true;
1138           }
1139         }
1140       }
1141     }
1142
1143     changed |= (drag_prev != drag_curr);
1144   }
1145
1146   return changed;
1147 }
1148
1149 static void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *data)
1150 {
1151   BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_SETUP);
1152   BLI_assert(data->multi_data.has_mbuts);
1153
1154   data->multi_data.bs_mbuts = UI_butstore_create(but_active->block);
1155
1156   for (uiBut *but = but_active->block->buttons.first; but; but = but->next) {
1157     if (but->flag & UI_BUT_DRAG_MULTI) {
1158       ui_multibut_add(data, but);
1159     }
1160   }
1161
1162   /* edit buttons proportionally to eachother
1163    * note: if we mix buttons which are proportional and others which are not,
1164    * this may work a bit strangely */
1165   if ((but_active->rnaprop && (RNA_property_flag(but_active->rnaprop) & PROP_PROPORTIONAL)) ||
1166       ELEM(but_active->unit_type, PROP_UNIT_LENGTH)) {
1167     if (data->origvalue != 0.0) {
1168       data->multi_data.is_proportional = true;
1169     }
1170   }
1171 }
1172
1173 static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBlock *block)
1174 {
1175   ARegion *ar = data->region;
1176   const double value_delta = data->value - data->origvalue;
1177   const double value_scale = data->multi_data.is_proportional ? (data->value / data->origvalue) :
1178                                                                 0.0;
1179   uiBut *but;
1180
1181   BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_ENABLE);
1182   BLI_assert(data->multi_data.skip == false);
1183
1184   for (but = block->buttons.first; but; but = but->next) {
1185     if (but->flag & UI_BUT_DRAG_MULTI) {
1186       /* mbut_states for delta */
1187       uiButMultiState *mbut_state = ui_multibut_lookup(data, but);
1188
1189       if (mbut_state) {
1190         void *active_back;
1191
1192         ui_but_execute_begin(C, ar, but, &active_back);
1193
1194 #  ifdef USE_ALLSELECT
1195         if (data->select_others.is_enabled) {
1196           /* init once! */
1197           if (mbut_state->select_others.elems_len == 0) {
1198             ui_selectcontext_begin(C, but, &mbut_state->select_others);
1199           }
1200           if (mbut_state->select_others.elems_len == 0) {
1201             mbut_state->select_others.elems_len = -1;
1202           }
1203         }
1204
1205         /* needed so we apply the right deltas */
1206         but->active->origvalue = mbut_state->origvalue;
1207         but->active->select_others = mbut_state->select_others;
1208         but->active->select_others.do_free = false;
1209 #  endif
1210
1211         BLI_assert(active_back == NULL);
1212         /* no need to check 'data->state' here */
1213         if (data->str) {
1214           /* entering text (set all) */
1215           but->active->value = data->value;
1216           ui_but_string_set(C, but, data->str);
1217         }
1218         else {
1219           /* dragging (use delta) */
1220           if (data->multi_data.is_proportional) {
1221             but->active->value = mbut_state->origvalue * value_scale;
1222           }
1223           else {
1224             but->active->value = mbut_state->origvalue + value_delta;
1225           }
1226
1227           /* clamp based on soft limits, see: T40154 */
1228           CLAMP(but->active->value, (double)but->softmin, (double)but->softmax);
1229         }
1230         ui_but_execute_end(C, ar, but, active_back);
1231       }
1232       else {
1233         /* highly unlikely */
1234         printf("%s: cant find button\n", __func__);
1235       }
1236       /* end */
1237     }
1238   }
1239 }
1240
1241 #endif /* USE_DRAG_MULTINUM */
1242
1243 /** \} */
1244
1245 /* -------------------------------------------------------------------- */
1246 /** \name Button Drag Toggle
1247  * \{ */
1248
1249 #ifdef USE_DRAG_TOGGLE
1250
1251 /* Helpers that wrap boolean functions, to support different kinds of buttons. */
1252
1253 static bool ui_drag_toggle_but_is_supported(const uiBut *but)
1254 {
1255   if (ui_but_is_bool(but)) {
1256     return true;
1257   }
1258   else if (UI_but_is_decorator(but)) {
1259     return ELEM(but->icon,
1260                 ICON_DECORATE,
1261                 ICON_DECORATE_KEYFRAME,
1262                 ICON_DECORATE_ANIMATE,
1263                 ICON_DECORATE_OVERRIDE);
1264   }
1265   else {
1266     return false;
1267   }
1268 }
1269
1270 /* Button pushed state to compare if other buttons match. Can be more
1271  * then just true or false for toggle buttons with more than 2 states. */
1272 static int ui_drag_toggle_but_pushed_state(bContext *C, uiBut *but)
1273 {
1274   if (but->rnapoin.data == NULL && but->poin == NULL && but->icon) {
1275     if (but->pushed_state_func) {
1276       return but->pushed_state_func(C, but->pushed_state_arg);
1277     }
1278     else {
1279       /* Assume icon identifies a unique state, for buttons that
1280        * work though functions callbacks and don't have an boolean
1281        * value that indicates the state. */
1282       return but->icon + but->iconadd;
1283     }
1284   }
1285   else if (ui_but_is_bool(but)) {
1286     return ui_but_is_pushed(but);
1287   }
1288   else {
1289     return 0;
1290   }
1291 }
1292
1293 typedef struct uiDragToggleHandle {
1294   /* init */
1295   int pushed_state;
1296   float but_cent_start[2];
1297
1298   bool is_xy_lock_init;
1299   bool xy_lock[2];
1300
1301   int xy_init[2];
1302   int xy_last[2];
1303 } uiDragToggleHandle;
1304
1305 static bool ui_drag_toggle_set_xy_xy(
1306     bContext *C, ARegion *ar, const int pushed_state, const int xy_src[2], const int xy_dst[2])
1307 {
1308   /* popups such as layers won't re-evaluate on redraw */
1309   const bool do_check = (ar->regiontype == RGN_TYPE_TEMPORARY);
1310   bool changed = false;
1311   uiBlock *block;
1312
1313   for (block = ar->uiblocks.first; block; block = block->next) {
1314     uiBut *but;
1315
1316     float xy_a_block[2] = {UNPACK2(xy_src)};
1317     float xy_b_block[2] = {UNPACK2(xy_dst)};
1318
1319     ui_window_to_block_fl(ar, block, &xy_a_block[0], &xy_a_block[1]);
1320     ui_window_to_block_fl(ar, block, &xy_b_block[0], &xy_b_block[1]);
1321
1322     for (but = block->buttons.first; but; but = but->next) {
1323       /* Note: ctrl is always true here because (at least for now)
1324        * we always want to consider text control in this case, even when not embossed. */
1325       if (ui_but_is_interactive(but, true)) {
1326         if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) {
1327
1328           /* execute the button */
1329           if (ui_drag_toggle_but_is_supported(but)) {
1330             /* is it pressed? */
1331             int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but);
1332             if (pushed_state_but != pushed_state) {
1333               UI_but_execute(C, but);
1334               if (do_check) {
1335                 ui_but_update_edited(but);
1336               }
1337               changed = true;
1338             }
1339           }
1340           /* done */
1341         }
1342       }
1343     }
1344   }
1345   if (changed) {
1346     /* apply now, not on release (or if handlers are canceled for whatever reason) */
1347     ui_apply_but_funcs_after(C);
1348   }
1349
1350   return changed;
1351 }
1352
1353 static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const int xy_input[2])
1354 {
1355   ARegion *ar = CTX_wm_region(C);
1356   bool do_draw = false;
1357   int xy[2];
1358
1359   /**
1360    * Initialize Locking:
1361    *
1362    * Check if we need to initialize the lock axis by finding if the first
1363    * button we mouse over is X or Y aligned, then lock the mouse to that axis after.
1364    */
1365   if (drag_info->is_xy_lock_init == false) {
1366     /* first store the buttons original coords */
1367     uiBut *but = ui_but_find_mouse_over_ex(ar, xy_input[0], xy_input[1], true);
1368
1369     if (but) {
1370       if (but->flag & UI_BUT_DRAG_LOCK) {
1371         const float but_cent_new[2] = {
1372             BLI_rctf_cent_x(&but->rect),
1373             BLI_rctf_cent_y(&but->rect),
1374         };
1375
1376         /* check if this is a different button,
1377          * chances are high the button wont move about :) */
1378         if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) {
1379           if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) <
1380               fabsf(drag_info->but_cent_start[1] - but_cent_new[1])) {
1381             drag_info->xy_lock[0] = true;
1382           }
1383           else {
1384             drag_info->xy_lock[1] = true;
1385           }
1386           drag_info->is_xy_lock_init = true;
1387         }
1388       }
1389       else {
1390         drag_info->is_xy_lock_init = true;
1391       }
1392     }
1393   }
1394   /* done with axis locking */
1395
1396   xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : drag_info->xy_last[0];
1397   xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : drag_info->xy_last[1];
1398
1399   /* touch all buttons between last mouse coord and this one */
1400   do_draw = ui_drag_toggle_set_xy_xy(C, ar, drag_info->pushed_state, drag_info->xy_last, xy);
1401
1402   if (do_draw) {
1403     ED_region_tag_redraw(ar);
1404   }
1405
1406   copy_v2_v2_int(drag_info->xy_last, xy);
1407 }
1408
1409 static void ui_handler_region_drag_toggle_remove(bContext *UNUSED(C), void *userdata)
1410 {
1411   uiDragToggleHandle *drag_info = userdata;
1412   MEM_freeN(drag_info);
1413 }
1414
1415 static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void *userdata)
1416 {
1417   uiDragToggleHandle *drag_info = userdata;
1418   bool done = false;
1419
1420   switch (event->type) {
1421     case LEFTMOUSE: {
1422       if (event->val == KM_RELEASE) {
1423         done = true;
1424       }
1425       break;
1426     }
1427     case MOUSEMOVE: {
1428       ui_drag_toggle_set(C, drag_info, &event->x);
1429       break;
1430     }
1431   }
1432
1433   if (done) {
1434     wmWindow *win = CTX_wm_window(C);
1435     ARegion *ar = CTX_wm_region(C);
1436     uiBut *but = ui_but_find_mouse_over_ex(ar, drag_info->xy_init[0], drag_info->xy_init[1], true);
1437
1438     if (but) {
1439       ui_apply_but_undo(but);
1440     }
1441
1442     WM_event_remove_ui_handler(&win->modalhandlers,
1443                                ui_handler_region_drag_toggle,
1444                                ui_handler_region_drag_toggle_remove,
1445                                drag_info,
1446                                false);
1447     ui_handler_region_drag_toggle_remove(C, drag_info);
1448
1449     WM_event_add_mousemove(C);
1450     return WM_UI_HANDLER_BREAK;
1451   }
1452   else {
1453     return WM_UI_HANDLER_CONTINUE;
1454   }
1455 }
1456
1457 static bool ui_but_is_drag_toggle(const uiBut *but)
1458 {
1459   return ((ui_drag_toggle_but_is_supported(but) == true) &&
1460           /* menu check is importnt so the button dragged over isn't removed instantly */
1461           (ui_block_is_menu(but->block) == false));
1462 }
1463
1464 #endif /* USE_DRAG_TOGGLE */
1465
1466 #ifdef USE_ALLSELECT
1467
1468 static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore *selctx_data)
1469 {
1470   PointerRNA ptr, lptr, idptr;
1471   PropertyRNA *prop, *lprop;
1472   bool success = false;
1473   int index;
1474
1475   char *path = NULL;
1476   ListBase lb = {NULL};
1477
1478   ptr = but->rnapoin;
1479   prop = but->rnaprop;
1480   index = but->rnaindex;
1481
1482   /* for now don't support whole colors */
1483   if (index == -1) {
1484     return false;
1485   }
1486
1487   /* if there is a valid property that is editable... */
1488   if (ptr.data && prop) {
1489     CollectionPointerLink *link;
1490     bool use_path_from_id;
1491     int i;
1492
1493     /* some facts we want to know */
1494     const bool is_array = RNA_property_array_check(prop);
1495     const int rna_type = RNA_property_type(prop);
1496
1497     if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) &&
1498         !BLI_listbase_is_empty(&lb)) {
1499       selctx_data->elems_len = BLI_listbase_count(&lb);
1500       selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len,
1501                                        __func__);
1502
1503       for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) {
1504         uiSelectContextElem *other = &selctx_data->elems[i];
1505         /* TODO,. de-duplicate copy_to_selected_button */
1506         if (link->ptr.data != ptr.data) {
1507           if (use_path_from_id) {
1508             /* Path relative to ID. */
1509             lprop = NULL;
1510             RNA_id_pointer_create(link->ptr.id.data, &idptr);
1511             RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
1512           }
1513           else if (path) {
1514             /* Path relative to elements from list. */
1515             lprop = NULL;
1516             RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
1517           }
1518           else {
1519             lptr = link->ptr;
1520             lprop = prop;
1521           }
1522
1523           /* lptr might not be the same as link->ptr! */
1524           if ((lptr.data != ptr.data) && (lprop == prop) && RNA_property_editable(&lptr, lprop)) {
1525             other->ptr = lptr;
1526             if (is_array) {
1527               if (rna_type == PROP_FLOAT) {
1528                 other->val_f = RNA_property_float_get_index(&lptr, lprop, index);
1529               }
1530               else if (rna_type == PROP_INT) {
1531                 other->val_i = RNA_property_int_get_index(&lptr, lprop, index);
1532               }
1533               /* ignored for now */
1534 #  if 0
1535               else if (rna_type == PROP_BOOLEAN) {
1536                 other->val_b = RNA_property_boolean_get_index(&lptr, lprop, index);
1537               }
1538 #  endif
1539             }
1540             else {
1541               if (rna_type == PROP_FLOAT) {
1542                 other->val_f = RNA_property_float_get(&lptr, lprop);
1543               }
1544               else if (rna_type == PROP_INT) {
1545                 other->val_i = RNA_property_int_get(&lptr, lprop);
1546               }
1547               /* ignored for now */
1548 #  if 0
1549               else if (rna_type == PROP_BOOLEAN) {
1550                 other->val_b = RNA_property_boolean_get(&lptr, lprop);
1551               }
1552               else if (rna_type == PROP_ENUM) {
1553                 other->val_i = RNA_property_enum_get(&lptr, lprop);
1554               }
1555 #  endif
1556             }
1557
1558             continue;
1559           }
1560         }
1561
1562         selctx_data->elems_len -= 1;
1563         i -= 1;
1564       }
1565
1566       success = (selctx_data->elems_len != 0);
1567     }
1568   }
1569
1570   if (selctx_data->elems_len == 0) {
1571     MEM_SAFE_FREE(selctx_data->elems);
1572   }
1573
1574   MEM_SAFE_FREE(path);
1575   BLI_freelistN(&lb);
1576
1577   /* caller can clear */
1578   selctx_data->do_free = true;
1579
1580   if (success) {
1581     but->flag |= UI_BUT_IS_SELECT_CONTEXT;
1582   }
1583
1584   return success;
1585 }
1586
1587 static void ui_selectcontext_end(uiBut *but, uiSelectContextStore *selctx_data)
1588 {
1589   if (selctx_data->do_free) {
1590     if (selctx_data->elems) {
1591       MEM_freeN(selctx_data->elems);
1592     }
1593   }
1594
1595   but->flag &= ~UI_BUT_IS_SELECT_CONTEXT;
1596 }
1597
1598 static void ui_selectcontext_apply(bContext *C,
1599                                    uiBut *but,
1600                                    uiSelectContextStore *selctx_data,
1601                                    const double value,
1602                                    const double value_orig)
1603 {
1604   if (selctx_data->elems) {
1605     PropertyRNA *prop = but->rnaprop;
1606     PropertyRNA *lprop = but->rnaprop;
1607     int index = but->rnaindex;
1608     int i;
1609     const bool use_delta = (selctx_data->is_copy == false);
1610
1611     union {
1612       bool b;
1613       int i;
1614       float f;
1615       PointerRNA p;
1616     } delta, min, max;
1617
1618     const bool is_array = RNA_property_array_check(prop);
1619     const int rna_type = RNA_property_type(prop);
1620
1621     if (rna_type == PROP_FLOAT) {
1622       delta.f = use_delta ? (value - value_orig) : value;
1623       RNA_property_float_range(&but->rnapoin, prop, &min.f, &max.f);
1624     }
1625     else if (rna_type == PROP_INT) {
1626       delta.i = use_delta ? ((int)value - (int)value_orig) : (int)value;
1627       RNA_property_int_range(&but->rnapoin, prop, &min.i, &max.i);
1628     }
1629     else if (rna_type == PROP_ENUM) {
1630       /* not a delta infact */
1631       delta.i = RNA_property_enum_get(&but->rnapoin, prop);
1632     }
1633     else if (rna_type == PROP_BOOLEAN) {
1634       if (is_array) {
1635         /* not a delta infact */
1636         delta.b = RNA_property_boolean_get_index(&but->rnapoin, prop, index);
1637       }
1638       else {
1639         /* not a delta infact */
1640         delta.b = RNA_property_boolean_get(&but->rnapoin, prop);
1641       }
1642     }
1643     else if (rna_type == PROP_POINTER) {
1644       /* not a delta infact */
1645       delta.p = RNA_property_pointer_get(&but->rnapoin, prop);
1646     }
1647
1648 #  ifdef USE_ALLSELECT_LAYER_HACK
1649     /* make up for not having 'handle_layer_buttons' */
1650     {
1651       PropertySubType subtype = RNA_property_subtype(prop);
1652
1653       if ((rna_type == PROP_BOOLEAN) && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER) && is_array &&
1654           /* could check for 'handle_layer_buttons' */
1655           but->func) {
1656         wmWindow *win = CTX_wm_window(C);
1657         if (!win->eventstate->shift) {
1658           const int len = RNA_property_array_length(&but->rnapoin, prop);
1659           bool *tmparray = MEM_callocN(sizeof(bool) * len, __func__);
1660
1661           tmparray[index] = true;
1662
1663           for (i = 0; i < selctx_data->elems_len; i++) {
1664             uiSelectContextElem *other = &selctx_data->elems[i];
1665             PointerRNA lptr = other->ptr;
1666             RNA_property_boolean_set_array(&lptr, lprop, tmparray);
1667             RNA_property_update(C, &lptr, lprop);
1668           }
1669
1670           MEM_freeN(tmparray);
1671
1672           return;
1673         }
1674       }
1675     }
1676 #  endif
1677
1678     for (i = 0; i < selctx_data->elems_len; i++) {
1679       uiSelectContextElem *other = &selctx_data->elems[i];
1680       PointerRNA lptr = other->ptr;
1681
1682       if (rna_type == PROP_FLOAT) {
1683         float other_value = use_delta ? (other->val_f + delta.f) : delta.f;
1684         CLAMP(other_value, min.f, max.f);
1685         if (is_array) {
1686           RNA_property_float_set_index(&lptr, lprop, index, other_value);
1687         }
1688         else {
1689           RNA_property_float_set(&lptr, lprop, other_value);
1690         }
1691       }
1692       else if (rna_type == PROP_INT) {
1693         int other_value = use_delta ? (other->val_i + delta.i) : delta.i;
1694         CLAMP(other_value, min.i, max.i);
1695         if (is_array) {
1696           RNA_property_int_set_index(&lptr, lprop, index, other_value);
1697         }
1698         else {
1699           RNA_property_int_set(&lptr, lprop, other_value);
1700         }
1701       }
1702       else if (rna_type == PROP_BOOLEAN) {
1703         const bool other_value = delta.b;
1704         if (is_array) {
1705           RNA_property_boolean_set_index(&lptr, lprop, index, other_value);
1706         }
1707         else {
1708           RNA_property_boolean_set(&lptr, lprop, delta.b);
1709         }
1710       }
1711       else if (rna_type == PROP_ENUM) {
1712         const int other_value = delta.i;
1713         BLI_assert(!is_array);
1714         RNA_property_enum_set(&lptr, lprop, other_value);
1715       }
1716       else if (rna_type == PROP_POINTER) {
1717         const PointerRNA other_value = delta.p;
1718         RNA_property_pointer_set(&lptr, lprop, other_value);
1719       }
1720
1721       RNA_property_update(C, &lptr, prop);
1722     }
1723   }
1724 }
1725
1726 #endif /* USE_ALLSELECT */
1727
1728 /** \} */
1729
1730 /* -------------------------------------------------------------------- */
1731 /** \name Button Drag
1732  * \{ */
1733
1734 static bool ui_but_drag_init(bContext *C,
1735                              uiBut *but,
1736                              uiHandleButtonData *data,
1737                              const wmEvent *event)
1738 {
1739   /* prevent other WM gestures to start while we try to drag */
1740   WM_gestures_remove(C);
1741
1742   /* Clamp the maximum to half the UI unit size so a high user preference
1743    * doesn't require the user to drag more then half the default button height. */
1744   const int drag_threshold = min_ii(
1745       U.tweak_threshold * U.dpi_fac,
1746       (int)((UI_UNIT_Y / 2) * ui_block_to_window_scale(data->region, but->block)));
1747
1748   if (ABS(data->dragstartx - event->x) + ABS(data->dragstarty - event->y) > drag_threshold) {
1749     button_activate_state(C, but, BUTTON_STATE_EXIT);
1750     data->cancel = true;
1751 #ifdef USE_DRAG_TOGGLE
1752     if (ui_drag_toggle_but_is_supported(but)) {
1753       uiDragToggleHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__);
1754       ARegion *ar_prev;
1755
1756       /* call here because regular mouse-up event wont run,
1757        * typically 'button_activate_exit()' handles this */
1758       ui_apply_but_autokey(C, but);
1759
1760       drag_info->pushed_state = ui_drag_toggle_but_pushed_state(C, but);
1761       drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
1762       drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
1763       copy_v2_v2_int(drag_info->xy_init, &event->x);
1764       copy_v2_v2_int(drag_info->xy_last, &event->x);
1765
1766       /* needed for toggle drag on popups */
1767       ar_prev = CTX_wm_region(C);
1768       CTX_wm_region_set(C, data->region);
1769
1770       WM_event_add_ui_handler(C,
1771                               &data->window->modalhandlers,
1772                               ui_handler_region_drag_toggle,
1773                               ui_handler_region_drag_toggle_remove,
1774                               drag_info,
1775                               WM_HANDLER_BLOCKING);
1776
1777       CTX_wm_region_set(C, ar_prev);
1778
1779       /* Initialize alignment for single row/column regions,
1780        * otherwise we use the relative position of the first other button dragged over. */
1781       if (ELEM(data->region->regiontype,
1782                RGN_TYPE_NAV_BAR,
1783                RGN_TYPE_HEADER,
1784                RGN_TYPE_TOOL_HEADER,
1785                RGN_TYPE_FOOTER)) {
1786         int lock_axis = -1;
1787         if (ELEM(data->region->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
1788           lock_axis = 0;
1789         }
1790         else if (ELEM(data->region->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
1791           lock_axis = 1;
1792         }
1793         if (lock_axis != -1) {
1794           drag_info->xy_lock[lock_axis] = true;
1795           drag_info->is_xy_lock_init = true;
1796         }
1797       }
1798     }
1799     else
1800 #endif
1801         if (but->type == UI_BTYPE_COLOR) {
1802       bool valid = false;
1803       uiDragColorHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__);
1804
1805       /* TODO support more button pointer types */
1806       if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
1807         ui_but_v3_get(but, drag_info->color);
1808         drag_info->gamma_corrected = true;
1809         valid = true;
1810       }
1811       else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
1812         ui_but_v3_get(but, drag_info->color);
1813         drag_info->gamma_corrected = false;
1814         valid = true;
1815       }
1816       else if (ELEM(but->pointype, UI_BUT_POIN_FLOAT, UI_BUT_POIN_CHAR)) {
1817         ui_but_v3_get(but, drag_info->color);
1818         copy_v3_v3(drag_info->color, (float *)but->poin);
1819         valid = true;
1820       }
1821
1822       if (valid) {
1823         WM_event_start_drag(C, ICON_COLOR, WM_DRAG_COLOR, drag_info, 0.0, WM_DRAG_FREE_DATA);
1824       }
1825       else {
1826         MEM_freeN(drag_info);
1827         return false;
1828       }
1829     }
1830     else {
1831       wmDrag *drag = WM_event_start_drag(
1832           C, but->icon, but->dragtype, but->dragpoin, ui_but_value_get(but), WM_DRAG_NOP);
1833
1834       if (but->imb) {
1835         WM_event_drag_image(drag,
1836                             but->imb,
1837                             but->imb_scale,
1838                             BLI_rctf_size_x(&but->rect),
1839                             BLI_rctf_size_y(&but->rect));
1840       }
1841     }
1842     return true;
1843   }
1844
1845   return false;
1846 }
1847
1848 /** \} */
1849
1850 /* -------------------------------------------------------------------- */
1851 /** \name Button Apply
1852  * \{ */
1853
1854 static void ui_apply_but_IMAGE(bContext *C, uiBut *but, uiHandleButtonData *data)
1855 {
1856   ui_apply_but_func(C, but);
1857   data->retval = but->retval;
1858   data->applied = true;
1859 }
1860
1861 static void ui_apply_but_HISTOGRAM(bContext *C, uiBut *but, uiHandleButtonData *data)
1862 {
1863   ui_apply_but_func(C, but);
1864   data->retval = but->retval;
1865   data->applied = true;
1866 }
1867
1868 static void ui_apply_but_WAVEFORM(bContext *C, uiBut *but, uiHandleButtonData *data)
1869 {
1870   ui_apply_but_func(C, but);
1871   data->retval = but->retval;
1872   data->applied = true;
1873 }
1874
1875 static void ui_apply_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonData *data)
1876 {
1877   ui_apply_but_func(C, but);
1878   data->retval = but->retval;
1879   data->applied = true;
1880 }
1881
1882 static void ui_apply_but(
1883     bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const bool interactive)
1884 {
1885   char *editstr;
1886   double *editval;
1887   float *editvec;
1888   ColorBand *editcoba;
1889   CurveMapping *editcumap;
1890
1891   data->retval = 0;
1892
1893   /* if we cancel and have not applied yet, there is nothing to do,
1894    * otherwise we have to restore the original value again */
1895   if (data->cancel) {
1896     if (!data->applied) {
1897       return;
1898     }
1899
1900     if (data->str) {
1901       MEM_freeN(data->str);
1902     }
1903     data->str = data->origstr;
1904     data->origstr = NULL;
1905     data->value = data->origvalue;
1906     copy_v3_v3(data->vec, data->origvec);
1907     /* postpone clearing origdata */
1908   }
1909   else {
1910     /* we avoid applying interactive edits a second time
1911      * at the end with the appliedinteractive flag */
1912     if (interactive) {
1913       data->applied_interactive = true;
1914     }
1915     else if (data->applied_interactive) {
1916       return;
1917     }
1918
1919 #ifdef USE_ALLSELECT
1920 #  ifdef USE_DRAG_MULTINUM
1921     if (but->flag & UI_BUT_DRAG_MULTI) {
1922       /* pass */
1923     }
1924     else
1925 #  endif
1926         if (data->select_others.elems_len == 0) {
1927       wmWindow *win = CTX_wm_window(C);
1928       /* may have been enabled before activating */
1929       if (data->select_others.is_enabled || IS_ALLSELECT_EVENT(win->eventstate)) {
1930         ui_selectcontext_begin(C, but, &data->select_others);
1931         data->select_others.is_enabled = true;
1932       }
1933     }
1934     if (data->select_others.elems_len == 0) {
1935       /* dont check again */
1936       data->select_others.elems_len = -1;
1937     }
1938 #endif
1939   }
1940
1941   /* ensures we are writing actual values */
1942   editstr = but->editstr;
1943   editval = but->editval;
1944   editvec = but->editvec;
1945   editcoba = but->editcoba;
1946   editcumap = but->editcumap;
1947   but->editstr = NULL;
1948   but->editval = NULL;
1949   but->editvec = NULL;
1950   but->editcoba = NULL;
1951   but->editcumap = NULL;
1952
1953   /* handle different types */
1954   switch (but->type) {
1955     case UI_BTYPE_BUT:
1956       ui_apply_but_BUT(C, but, data);
1957       break;
1958     case UI_BTYPE_TEXT:
1959     case UI_BTYPE_SEARCH_MENU:
1960       ui_apply_but_TEX(C, but, data);
1961       break;
1962     case UI_BTYPE_BUT_TOGGLE:
1963     case UI_BTYPE_TOGGLE:
1964     case UI_BTYPE_TOGGLE_N:
1965     case UI_BTYPE_ICON_TOGGLE:
1966     case UI_BTYPE_ICON_TOGGLE_N:
1967     case UI_BTYPE_CHECKBOX:
1968     case UI_BTYPE_CHECKBOX_N:
1969       ui_apply_but_TOG(C, but, data);
1970       break;
1971     case UI_BTYPE_ROW:
1972     case UI_BTYPE_LISTROW:
1973       ui_apply_but_ROW(C, block, but, data);
1974       break;
1975     case UI_BTYPE_TAB:
1976       ui_apply_but_TAB(C, but, data);
1977       break;
1978     case UI_BTYPE_SCROLL:
1979     case UI_BTYPE_GRIP:
1980     case UI_BTYPE_NUM:
1981     case UI_BTYPE_NUM_SLIDER:
1982       ui_apply_but_NUM(C, but, data);
1983       break;
1984     case UI_BTYPE_MENU:
1985     case UI_BTYPE_BLOCK:
1986     case UI_BTYPE_PULLDOWN:
1987       ui_apply_but_BLOCK(C, but, data);
1988       break;
1989     case UI_BTYPE_COLOR:
1990       if (data->cancel) {
1991         ui_apply_but_VEC(C, but, data);
1992       }
1993       else {
1994         ui_apply_but_BLOCK(C, but, data);
1995       }
1996       break;
1997     case UI_BTYPE_BUT_MENU:
1998       ui_apply_but_BUTM(C, but, data);
1999       break;
2000     case UI_BTYPE_UNITVEC:
2001     case UI_BTYPE_HSVCUBE:
2002     case UI_BTYPE_HSVCIRCLE:
2003       ui_apply_but_VEC(C, but, data);
2004       break;
2005     case UI_BTYPE_COLORBAND:
2006       ui_apply_but_COLORBAND(C, but, data);
2007       break;
2008     case UI_BTYPE_CURVE:
2009       ui_apply_but_CURVE(C, but, data);
2010       break;
2011     case UI_BTYPE_KEY_EVENT:
2012     case UI_BTYPE_HOTKEY_EVENT:
2013       ui_apply_but_BUT(C, but, data);
2014       break;
2015     case UI_BTYPE_IMAGE:
2016       ui_apply_but_IMAGE(C, but, data);
2017       break;
2018     case UI_BTYPE_HISTOGRAM:
2019       ui_apply_but_HISTOGRAM(C, but, data);
2020       break;
2021     case UI_BTYPE_WAVEFORM:
2022       ui_apply_but_WAVEFORM(C, but, data);
2023       break;
2024     case UI_BTYPE_TRACK_PREVIEW:
2025       ui_apply_but_TRACKPREVIEW(C, but, data);
2026       break;
2027     default:
2028       break;
2029   }
2030
2031 #ifdef USE_DRAG_MULTINUM
2032   if (data->multi_data.has_mbuts) {
2033     if ((data->multi_data.init == BUTTON_MULTI_INIT_ENABLE) && (data->multi_data.skip == false)) {
2034       if (data->cancel) {
2035         ui_multibut_restore(C, data, block);
2036       }
2037       else {
2038         ui_multibut_states_apply(C, data, block);
2039       }
2040     }
2041   }
2042 #endif
2043
2044 #ifdef USE_ALLSELECT
2045   ui_selectcontext_apply(C, but, &data->select_others, data->value, data->origvalue);
2046 #endif
2047
2048   if (data->cancel) {
2049     data->origvalue = 0.0;
2050     zero_v3(data->origvec);
2051   }
2052
2053   but->editstr = editstr;
2054   but->editval = editval;
2055   but->editvec = editvec;
2056   but->editcoba = editcoba;
2057   but->editcumap = editcumap;
2058 }
2059
2060 /** \} */
2061
2062 /* -------------------------------------------------------------------- */
2063 /** \name Button Drop Event
2064  * \{ */
2065
2066 /* only call if event type is EVT_DROP */
2067 static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data)
2068 {
2069   wmDrag *wmd;
2070   ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */
2071
2072   for (wmd = drags->first; wmd; wmd = wmd->next) {
2073     if (wmd->type == WM_DRAG_ID) {
2074       /* align these types with UI_but_active_drop_name */
2075       if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
2076         ID *id = WM_drag_ID(wmd, 0);
2077
2078         button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2079
2080         ui_textedit_string_set(but, data, id->name + 2);
2081
2082         if (ELEM(but->type, UI_BTYPE_SEARCH_MENU)) {
2083           but->changed = true;
2084           ui_searchbox_update(C, data->searchbox, but, true);
2085         }
2086
2087         button_activate_state(C, but, BUTTON_STATE_EXIT);
2088       }
2089     }
2090   }
2091 }
2092
2093 /** \} */
2094
2095 /* -------------------------------------------------------------------- */
2096 /** \name Button Copy & Paste
2097  * \{ */
2098
2099 static void ui_but_get_pasted_text_from_clipboard(char **buf_paste, int *buf_len)
2100 {
2101   char *text;
2102   int length;
2103   /* get only first line even if the clipboard contains multiple lines */
2104   text = WM_clipboard_text_get_firstline(false, &length);
2105
2106   if (text) {
2107     *buf_paste = text;
2108     *buf_len = length;
2109   }
2110   else {
2111     *buf_paste = MEM_callocN(sizeof(char), __func__);
2112     *buf_len = 0;
2113   }
2114 }
2115
2116 static int get_but_property_array_length(uiBut *but)
2117 {
2118   return RNA_property_array_length(&but->rnapoin, but->rnaprop);
2119 }
2120
2121 static void ui_but_set_float_array(
2122     bContext *C, uiBut *but, uiHandleButtonData *data, float *values, int array_length)
2123 {
2124   button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2125
2126   for (int i = 0; i < array_length; i++) {
2127     RNA_property_float_set_index(&but->rnapoin, but->rnaprop, i, values[i]);
2128   }
2129   if (data) {
2130     data->value = values[but->rnaindex];
2131   }
2132
2133   button_activate_state(C, but, BUTTON_STATE_EXIT);
2134 }
2135
2136 static void float_array_to_string(float *values,
2137                                   int array_length,
2138                                   char *output,
2139                                   int output_len_max)
2140 {
2141   /* to avoid buffer overflow attacks; numbers are quite arbitrary */
2142   BLI_assert(output_len_max > 15);
2143   output_len_max -= 10;
2144
2145   int current_index = 0;
2146   output[current_index] = '[';
2147   current_index++;
2148
2149   for (int i = 0; i < array_length; i++) {
2150     int length = BLI_snprintf(
2151         output + current_index, output_len_max - current_index, "%f", values[i]);
2152     current_index += length;
2153
2154     if (i < array_length - 1) {
2155       if (current_index < output_len_max) {
2156         output[current_index + 0] = ',';
2157         output[current_index + 1] = ' ';
2158         current_index += 2;
2159       }
2160     }
2161   }
2162
2163   output[current_index + 0] = ']';
2164   output[current_index + 1] = '\0';
2165 }
2166
2167 static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max)
2168 {
2169   int array_length = get_but_property_array_length(but);
2170   float *values = alloca(array_length * sizeof(float));
2171   RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values);
2172   float_array_to_string(values, array_length, output, output_len_max);
2173 }
2174
2175 static bool parse_float_array(char *text, float *values, int expected_length)
2176 {
2177   /* can parse max 4 floats for now */
2178   BLI_assert(0 <= expected_length && expected_length <= 4);
2179
2180   float v[5];
2181   int actual_length = sscanf(text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]);
2182
2183   if (actual_length == expected_length) {
2184     memcpy(values, v, sizeof(float) * expected_length);
2185     return true;
2186   }
2187   else {
2188     return false;
2189   }
2190 }
2191
2192 static void ui_but_paste_numeric_array(bContext *C,
2193                                        uiBut *but,
2194                                        uiHandleButtonData *data,
2195                                        char *buf_paste)
2196 {
2197   int array_length = get_but_property_array_length(but);
2198   if (array_length > 4) {
2199     // not supported for now
2200     return;
2201   }
2202
2203   float *values = alloca(sizeof(float) * array_length);
2204
2205   if (parse_float_array(buf_paste, values, array_length)) {
2206     ui_but_set_float_array(C, but, data, values, array_length);
2207   }
2208   else {
2209     WM_report(RPT_ERROR, "Expected an array of numbers: [n, n, ...]");
2210   }
2211 }
2212
2213 static void ui_but_copy_numeric_value(uiBut *but, char *output, int output_len_max)
2214 {
2215   /* Get many decimal places, then strip trailing zeros.
2216    * note: too high values start to give strange results */
2217   ui_but_string_get_ex(but, output, output_len_max, UI_PRECISION_FLOAT_MAX, false, NULL);
2218   BLI_str_rstrip_float_zero(output, '\0');
2219 }
2220
2221 static void ui_but_paste_numeric_value(bContext *C,
2222                                        uiBut *but,
2223                                        uiHandleButtonData *data,
2224                                        char *buf_paste)
2225 {
2226   double value;
2227
2228   if (ui_but_string_set_eval_num(C, but, buf_paste, &value)) {
2229     button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2230     data->value = value;
2231     ui_but_string_set(C, but, buf_paste);
2232     button_activate_state(C, but, BUTTON_STATE_EXIT);
2233   }
2234   else {
2235     WM_report(RPT_ERROR, "Expected a number");
2236   }
2237 }
2238
2239 static void ui_but_paste_normalized_vector(bContext *C, uiBut *but, char *buf_paste)
2240 {
2241   float xyz[3];
2242   if (parse_float_array(buf_paste, xyz, 3)) {
2243     if (normalize_v3(xyz) == 0.0f) {
2244       /* better set Z up then have a zero vector */
2245       xyz[2] = 1.0;
2246     }
2247     ui_but_set_float_array(C, but, NULL, xyz, 3);
2248   }
2249   else {
2250     WM_report(RPT_ERROR, "Paste expected 3 numbers, formatted: '[n, n, n]'");
2251   }
2252 }
2253
2254 static void ui_but_copy_color(uiBut *but, char *output, int output_len_max)
2255 {
2256   float rgba[4];
2257
2258   if (but->rnaprop && get_but_property_array_length(but) == 4) {
2259     rgba[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
2260   }
2261   else {
2262     rgba[3] = 1.0f;
2263   }
2264
2265   ui_but_v3_get(but, rgba);
2266
2267   /* convert to linear color to do compatible copy between gamma and non-gamma */
2268   if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2269     srgb_to_linearrgb_v3_v3(rgba, rgba);
2270   }
2271
2272   float_array_to_string(rgba, 4, output, output_len_max);
2273 }
2274
2275 static void ui_but_paste_color(bContext *C, uiBut *but, char *buf_paste)
2276 {
2277   float rgba[4];
2278   if (parse_float_array(buf_paste, rgba, 4)) {
2279     if (but->rnaprop) {
2280       /* Assume linear colors in buffer. */
2281       if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2282         linearrgb_to_srgb_v3_v3(rgba, rgba);
2283       }
2284
2285       /* Some color properties are RGB, not RGBA. */
2286       int array_len = get_but_property_array_length(but);
2287       BLI_assert(ELEM(array_len, 3, 4));
2288       ui_but_set_float_array(C, but, NULL, rgba, array_len);
2289     }
2290   }
2291   else {
2292     WM_report(RPT_ERROR, "Paste expected 4 numbers, formatted: '[n, n, n, n]'");
2293   }
2294 }
2295
2296 static void ui_but_copy_text(uiBut *but, char *output, int output_len_max)
2297 {
2298   ui_but_string_get(but, output, output_len_max);
2299 }
2300
2301 static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste)
2302 {
2303   button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2304   ui_textedit_string_set(but, but->active, buf_paste);
2305
2306   if (but->type == UI_BTYPE_SEARCH_MENU) {
2307     but->changed = true;
2308     ui_searchbox_update(C, data->searchbox, but, true);
2309   }
2310
2311   button_activate_state(C, but, BUTTON_STATE_EXIT);
2312 }
2313
2314 static void ui_but_copy_colorband(uiBut *but)
2315 {
2316   if (but->poin != NULL) {
2317     memcpy(&but_copypaste_coba, but->poin, sizeof(ColorBand));
2318   }
2319 }
2320
2321 static void ui_but_paste_colorband(bContext *C, uiBut *but, uiHandleButtonData *data)
2322 {
2323   if (but_copypaste_coba.tot != 0) {
2324     if (!but->poin) {
2325       but->poin = MEM_callocN(sizeof(ColorBand), "colorband");
2326     }
2327
2328     button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2329     memcpy(data->coba, &but_copypaste_coba, sizeof(ColorBand));
2330     button_activate_state(C, but, BUTTON_STATE_EXIT);
2331   }
2332 }
2333
2334 static void ui_but_copy_curvemapping(uiBut *but)
2335 {
2336   if (but->poin != NULL) {
2337     but_copypaste_curve_alive = true;
2338     curvemapping_free_data(&but_copypaste_curve);
2339     curvemapping_copy_data(&but_copypaste_curve, (CurveMapping *)but->poin);
2340   }
2341 }
2342
2343 static void ui_but_paste_curvemapping(bContext *C, uiBut *but)
2344 {
2345   if (but_copypaste_curve_alive) {
2346     if (!but->poin) {
2347       but->poin = MEM_callocN(sizeof(CurveMapping), "curvemapping");
2348     }
2349
2350     button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2351     curvemapping_free_data((CurveMapping *)but->poin);
2352     curvemapping_copy_data((CurveMapping *)but->poin, &but_copypaste_curve);
2353     button_activate_state(C, but, BUTTON_STATE_EXIT);
2354   }
2355 }
2356
2357 static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max)
2358 {
2359   PointerRNA *opptr;
2360   opptr = UI_but_operator_ptr_get(but);
2361
2362   char *str;
2363   str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr);
2364   BLI_strncpy(output, str, output_len_max);
2365   MEM_freeN(str);
2366 }
2367
2368 static bool ui_but_copy_menu(uiBut *but, char *output, int output_len_max)
2369 {
2370   MenuType *mt = UI_but_menutype_get(but);
2371   if (mt) {
2372     BLI_snprintf(output, output_len_max, "bpy.ops.wm.call_menu(name=\"%s\")", mt->idname);
2373     return true;
2374   }
2375   return false;
2376 }
2377
2378 static bool ui_but_copy_popover(uiBut *but, char *output, int output_len_max)
2379 {
2380   PanelType *pt = UI_but_paneltype_get(but);
2381   if (pt) {
2382     BLI_snprintf(output, output_len_max, "bpy.ops.wm.call_panel(name=\"%s\")", pt->idname);
2383     return true;
2384   }
2385   return false;
2386 }
2387
2388 static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array)
2389 {
2390   if (ui_but_contains_password(but)) {
2391     return;
2392   }
2393
2394   /* Arbitrary large value (allow for paths: 'PATH_MAX') */
2395   char buf[4096] = {0};
2396   const int buf_max_len = sizeof(buf);
2397
2398   /* Left false for copying internal data (color-band for eg). */
2399   bool is_buf_set = false;
2400
2401   bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
2402
2403   switch (but->type) {
2404     case UI_BTYPE_NUM:
2405     case UI_BTYPE_NUM_SLIDER:
2406       if (!has_required_data) {
2407         break;
2408       }
2409       if (copy_array && ui_but_has_array_value(but)) {
2410         ui_but_copy_numeric_array(but, buf, buf_max_len);
2411       }
2412       else {
2413         ui_but_copy_numeric_value(but, buf, buf_max_len);
2414       }
2415       is_buf_set = true;
2416       break;
2417
2418     case UI_BTYPE_UNITVEC:
2419       if (!has_required_data) {
2420         break;
2421       }
2422       ui_but_copy_numeric_array(but, buf, buf_max_len);
2423       is_buf_set = true;
2424       break;
2425
2426     case UI_BTYPE_COLOR:
2427       if (!has_required_data) {
2428         break;
2429       }
2430       ui_but_copy_color(but, buf, buf_max_len);
2431       is_buf_set = true;
2432       break;
2433
2434     case UI_BTYPE_TEXT:
2435     case UI_BTYPE_SEARCH_MENU:
2436       if (!has_required_data) {
2437         break;
2438       }
2439       ui_but_copy_text(but, buf, buf_max_len);
2440       is_buf_set = true;
2441       break;
2442
2443     case UI_BTYPE_COLORBAND:
2444       ui_but_copy_colorband(but);
2445       break;
2446
2447     case UI_BTYPE_CURVE:
2448       ui_but_copy_curvemapping(but);
2449       break;
2450
2451     case UI_BTYPE_BUT:
2452       ui_but_copy_operator(C, but, buf, buf_max_len);
2453       is_buf_set = true;
2454       break;
2455
2456     case UI_BTYPE_MENU:
2457     case UI_BTYPE_PULLDOWN:
2458       if (ui_but_copy_menu(but, buf, buf_max_len)) {
2459         is_buf_set = true;
2460       }
2461       break;
2462     case UI_BTYPE_POPOVER:
2463       if (ui_but_copy_popover(but, buf, buf_max_len)) {
2464         is_buf_set = true;
2465       }
2466       break;
2467
2468     default:
2469       break;
2470   }
2471
2472   if (is_buf_set) {
2473     WM_clipboard_text_set(buf, 0);
2474   }
2475 }
2476
2477 static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, const bool paste_array)
2478 {
2479   BLI_assert((but->flag & UI_BUT_DISABLED) == 0); /* caller should check */
2480
2481   int buf_paste_len = 0;
2482   char *buf_paste;
2483   ui_but_get_pasted_text_from_clipboard(&buf_paste, &buf_paste_len);
2484
2485   bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
2486
2487   switch (but->type) {
2488     case UI_BTYPE_NUM:
2489     case UI_BTYPE_NUM_SLIDER:
2490       if (!has_required_data) {
2491         break;
2492       }
2493       if (paste_array && ui_but_has_array_value(but)) {
2494         ui_but_paste_numeric_array(C, but, data, buf_paste);
2495       }
2496       else {
2497         ui_but_paste_numeric_value(C, but, data, buf_paste);
2498       }
2499       break;
2500
2501     case UI_BTYPE_UNITVEC:
2502       if (!has_required_data) {
2503         break;
2504       }
2505       ui_but_paste_normalized_vector(C, but, buf_paste);
2506       break;
2507
2508     case UI_BTYPE_COLOR:
2509       if (!has_required_data) {
2510         break;
2511       }
2512       ui_but_paste_color(C, but, buf_paste);
2513       break;
2514
2515     case UI_BTYPE_TEXT:
2516     case UI_BTYPE_SEARCH_MENU:
2517       if (!has_required_data) {
2518         break;
2519       }
2520       ui_but_paste_text(C, but, data, buf_paste);
2521       break;
2522
2523     case UI_BTYPE_COLORBAND:
2524       ui_but_paste_colorband(C, but, data);
2525       break;
2526
2527     case UI_BTYPE_CURVE:
2528       ui_but_paste_curvemapping(C, but);
2529       break;
2530
2531     default:
2532       break;
2533   }
2534
2535   MEM_freeN((void *)buf_paste);
2536 }
2537
2538 void ui_but_clipboard_free(void)
2539 {
2540   curvemapping_free_data(&but_copypaste_curve);
2541 }
2542
2543 /** \} */
2544
2545 /* -------------------------------------------------------------------- */
2546 /** \name Button Text Password
2547  *
2548  * Functions to convert password strings that should not be displayed
2549  * to asterisk representation (e.g. 'mysecretpasswd' -> '*************')
2550  *
2551  * It converts every UTF-8 character to an asterisk, and also remaps
2552  * the cursor position and selection start/end.
2553  *
2554  * \note: remapping is used, because password could contain UTF-8 characters.
2555  *
2556  * \{ */
2557
2558 static int ui_text_position_from_hidden(uiBut *but, int pos)
2559 {
2560   const char *strpos, *butstr;
2561   int i;
2562
2563   butstr = (but->editstr) ? but->editstr : but->drawstr;
2564
2565   for (i = 0, strpos = butstr; i < pos; i++) {
2566     strpos = BLI_str_find_next_char_utf8(strpos, NULL);
2567   }
2568
2569   return (strpos - butstr);
2570 }
2571
2572 static int ui_text_position_to_hidden(uiBut *but, int pos)
2573 {
2574   const char *butstr = (but->editstr) ? but->editstr : but->drawstr;
2575   return BLI_strnlen_utf8(butstr, pos);
2576 }
2577
2578 void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR],
2579                                uiBut *but,
2580                                const bool restore)
2581 {
2582   char *butstr;
2583
2584   if (!(but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) {
2585     return;
2586   }
2587
2588   butstr = (but->editstr) ? but->editstr : but->drawstr;
2589
2590   if (restore) {
2591     /* restore original string */
2592     BLI_strncpy(butstr, password_str, UI_MAX_PASSWORD_STR);
2593
2594     /* remap cursor positions */
2595     if (but->pos >= 0) {
2596       but->pos = ui_text_position_from_hidden(but, but->pos);
2597       but->selsta = ui_text_position_from_hidden(but, but->selsta);
2598       but->selend = ui_text_position_from_hidden(but, but->selend);
2599     }
2600   }
2601   else {
2602     /* convert text to hidden text using asterisks (e.g. pass -> ****) */
2603     const size_t len = BLI_strlen_utf8(butstr);
2604
2605     /* remap cursor positions */
2606     if (but->pos >= 0) {
2607       but->pos = ui_text_position_to_hidden(but, but->pos);
2608       but->selsta = ui_text_position_to_hidden(but, but->selsta);
2609       but->selend = ui_text_position_to_hidden(but, but->selend);
2610     }
2611
2612     /* save original string */
2613     BLI_strncpy(password_str, butstr, UI_MAX_PASSWORD_STR);
2614     memset(butstr, '*', len);
2615     butstr[len] = '\0';
2616   }
2617 }
2618
2619 /** \} */
2620
2621 /* -------------------------------------------------------------------- */
2622 /** \name Button Text Selection/Editing
2623  * \{ */
2624
2625 static void ui_textedit_string_clear_and_exit(bContext *C, uiBut *but, uiHandleButtonData *data)
2626 {
2627   /* most likely NULL, but let's check, and give it temp zero string */
2628   if (!data->str) {
2629     data->str = MEM_callocN(1, "temp str");
2630   }
2631   data->str[0] = 0;
2632
2633   ui_apply_but_TEX(C, but, data);
2634   button_activate_state(C, but, BUTTON_STATE_EXIT);
2635 }
2636
2637 static void ui_textedit_string_ensure_max_length(uiBut *but, uiHandleButtonData *data, int maxlen)
2638 {
2639   BLI_assert(data->is_str_dynamic);
2640   BLI_assert(data->str == but->editstr);
2641
2642   if (maxlen > data->maxlen) {
2643     data->str = but->editstr = MEM_reallocN(data->str, sizeof(char) * maxlen);
2644     data->maxlen = maxlen;
2645   }
2646 }
2647
2648 static void ui_textedit_string_set(uiBut *but, uiHandleButtonData *data, const char *str)
2649 {
2650   if (data->is_str_dynamic) {
2651     ui_textedit_string_ensure_max_length(but, data, strlen(str) + 1);
2652   }
2653
2654   if (ui_but_is_utf8(but)) {
2655     BLI_strncpy_utf8(data->str, str, data->maxlen);
2656   }
2657   else {
2658     BLI_strncpy(data->str, str, data->maxlen);
2659   }
2660 }
2661
2662 static bool ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data)
2663 {
2664   char *str = data->str;
2665   const int len = strlen(str);
2666   bool changed = false;
2667   if (but->selsta != but->selend && len) {
2668     memmove(str + but->selsta, str + but->selend, (len - but->selend) + 1);
2669     changed = true;
2670   }
2671
2672   but->pos = but->selend = but->selsta;
2673   return changed;
2674 }
2675
2676 /**
2677  * \param x: Screen space cursor location - #wmEvent.x
2678  *
2679  * \note ``but->block->aspect`` is used here, so drawing button style is getting scaled too.
2680  */
2681 static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, const float x)
2682 {
2683   uiStyle *style = UI_style_get();  // XXX pass on as arg
2684   uiFontStyle *fstyle = &style->widget;
2685   const float aspect = but->block->aspect;
2686   const short fstyle_points_prev = fstyle->points;
2687
2688   float startx = but->rect.xmin;
2689   float starty_dummy = 0.0f;
2690   char password_str[UI_MAX_PASSWORD_STR];
2691   /* treat 'str_last' as null terminator for str, no need to modify in-place */
2692   const char *str = but->editstr, *str_last;
2693
2694   ui_block_to_window_fl(data->region, but->block, &startx, &starty_dummy);
2695
2696   ui_fontscale(&fstyle->points, aspect);
2697
2698   UI_fontstyle_set(fstyle);
2699
2700   if (fstyle->kerning == 1) {
2701     /* for BLF_width */
2702     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2703   }
2704
2705   ui_but_text_password_hide(password_str, but, false);
2706
2707   if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
2708     if (but->flag & UI_HAS_ICON) {
2709       startx += UI_DPI_ICON_SIZE / aspect;
2710     }
2711   }
2712   /* but this extra .05 makes clicks inbetween characters feel nicer */
2713   startx += ((UI_TEXT_MARGIN_X + 0.05f) * U.widget_unit) / aspect;
2714
2715   /* mouse dragged outside the widget to the left */
2716   if (x < startx) {
2717     int i = but->ofs;
2718
2719     str_last = &str[but->ofs];
2720
2721     while (i > 0) {
2722       if (BLI_str_cursor_step_prev_utf8(str, but->ofs, &i)) {
2723         /* 0.25 == scale factor for less sensitivity */
2724         if (BLF_width(fstyle->uifont_id, str + i, (str_last - str) - i) > (startx - x) * 0.25f) {
2725           break;
2726         }
2727       }
2728       else {
2729         break; /* unlikely but possible */
2730       }
2731     }
2732     but->ofs = i;
2733     but->pos = but->ofs;
2734   }
2735   /* mouse inside the widget, mouse coords mapped in widget space */
2736   else { /* (x >= startx) */
2737     int pos_i;
2738
2739     /* keep track of previous distance from the cursor to the char */
2740     float cdist, cdist_prev = 0.0f;
2741     short pos_prev;
2742
2743     str_last = &str[strlen(str)];
2744
2745     but->pos = pos_prev = ((str_last - str) - but->ofs);
2746
2747     while (true) {
2748       cdist = startx + BLF_width(fstyle->uifont_id, str + but->ofs, (str_last - str) - but->ofs);
2749
2750       /* check if position is found */
2751       if (cdist < x) {
2752         /* check is previous location was in fact closer */
2753         if ((x - cdist) > (cdist_prev - x)) {
2754           but->pos = pos_prev;
2755         }
2756         break;
2757       }
2758       cdist_prev = cdist;
2759       pos_prev = but->pos;
2760       /* done with tricky distance checks */
2761
2762       pos_i = but->pos;
2763       if (but->pos <= 0) {
2764         break;
2765       }
2766       if (BLI_str_cursor_step_prev_utf8(str, but->ofs, &pos_i)) {
2767         but->pos = pos_i;
2768         str_last = &str[but->pos + but->ofs];
2769       }
2770       else {
2771         break; /* unlikely but possible */
2772       }
2773     }
2774     but->pos += but->ofs;
2775     if (but->pos < 0) {
2776       but->pos = 0;
2777     }
2778   }
2779
2780   if (fstyle->kerning == 1) {
2781     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2782   }
2783
2784   ui_but_text_password_hide(password_str, but, true);
2785
2786   fstyle->points = fstyle_points_prev;
2787 }
2788
2789 static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data, const float x)
2790 {
2791   if (x > data->selstartx) {
2792     data->selextend = EXTEND_RIGHT;
2793   }
2794   else if (x < data->selstartx) {
2795     data->selextend = EXTEND_LEFT;
2796   }
2797
2798   ui_textedit_set_cursor_pos(but, data, x);
2799
2800   if (data->selextend == EXTEND_RIGHT) {
2801     but->selend = but->pos;
2802   }
2803   else if (data->selextend == EXTEND_LEFT) {
2804     but->selsta = but->pos;
2805   }
2806
2807   ui_but_update(but);
2808 }
2809
2810 /**
2811  * This is used for both utf8 and ascii
2812  *
2813  * For unicode buttons, \a buf is treated as unicode.
2814  */
2815 static bool ui_textedit_insert_buf(uiBut *but,
2816                                    uiHandleButtonData *data,
2817                                    const char *buf,
2818                                    int buf_len)
2819 {
2820   int len = strlen(data->str);
2821   int len_new = len - (but->selend - but->selsta) + 1;
2822   bool changed = false;
2823
2824   if (data->is_str_dynamic) {
2825     ui_textedit_string_ensure_max_length(but, data, len_new + buf_len);
2826   }
2827
2828   if (len_new <= data->maxlen) {
2829     char *str = data->str;
2830     size_t step = buf_len;
2831
2832     /* type over the current selection */
2833     if ((but->selend - but->selsta) > 0) {
2834       changed = ui_textedit_delete_selection(but, data);
2835       len = strlen(str);
2836     }
2837
2838     if ((len + step >= data->maxlen) && (data->maxlen - (len + 1) > 0)) {
2839       if (ui_but_is_utf8(but)) {
2840         /* shorten 'step' to a utf8 aligned size that fits  */
2841         BLI_strnlen_utf8_ex(buf, data->maxlen - (len + 1), &step);
2842       }
2843       else {
2844         step = data->maxlen - (len + 1);
2845       }
2846     }
2847
2848     if (step && (len + step < data->maxlen)) {
2849       memmove(&str[but->pos + step], &str[but->pos], (len + 1) - but->pos);
2850       memcpy(&str[but->pos], buf, step * sizeof(char));
2851       but->pos += step;
2852       changed = true;
2853     }
2854   }
2855
2856   return changed;
2857 }
2858
2859 static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
2860 {
2861   char buf[2] = {ascii, '\0'};
2862
2863   if (ui_but_is_utf8(but) && (BLI_str_utf8_size(buf) == -1)) {
2864     printf(
2865         "%s: entering invalid ascii char into an ascii key (%d)\n", __func__, (int)(uchar)ascii);
2866
2867     return false;
2868   }
2869
2870   /* in some cases we want to allow invalid utf8 chars */
2871   return ui_textedit_insert_buf(but, data, buf, 1);
2872 }
2873
2874 static void ui_textedit_move(uiBut *but,
2875                              uiHandleButtonData *data,
2876                              eStrCursorJumpDirection direction,
2877                              const bool select,
2878                              eStrCursorJumpType jump)
2879 {
2880   const char *str = data->str;
2881   const int len = strlen(str);
2882   const int pos_prev = but->pos;
2883   const bool has_sel = (but->selend - but->selsta) > 0;
2884
2885   ui_but_update(but);
2886
2887   /* special case, quit selection and set cursor */
2888   if (has_sel && !select) {
2889     if (jump == STRCUR_JUMP_ALL) {
2890       but->selsta = but->selend = but->pos = direction ? len : 0;
2891     }
2892     else {
2893       if (direction) {
2894         but->selsta = but->pos = but->selend;
2895       }
2896       else {
2897         but->pos = but->selend = but->selsta;
2898       }
2899     }
2900     data->selextend = EXTEND_NONE;
2901   }
2902   else {
2903     int pos_i = but->pos;
2904     BLI_str_cursor_step_utf8(str, len, &pos_i, direction, jump, true);
2905     but->pos = pos_i;
2906
2907     if (select) {
2908       /* existing selection */
2909       if (has_sel) {
2910
2911         if (data->selextend == EXTEND_NONE) {
2912           data->selextend = EXTEND_RIGHT;
2913         }
2914
2915         if (direction) {
2916           if (data->selextend == EXTEND_RIGHT) {
2917             but->selend = but->pos;
2918           }
2919           else {
2920             but->selsta = but->pos;
2921           }
2922         }
2923         else {
2924           if (data->selextend == EXTEND_LEFT) {
2925             but->selsta = but->pos;
2926           }
2927           else {
2928             but->selend = but->pos;
2929           }
2930         }
2931
2932         if (but->selend < but->selsta) {
2933           SWAP(short, but->selsta, but->selend);
2934           data->selextend = (data->selextend == EXTEND_RIGHT) ? EXTEND_LEFT : EXTEND_RIGHT;
2935         }
2936
2937       } /* new selection */
2938       else {
2939         if (direction) {
2940           data->selextend = EXTEND_RIGHT;
2941           but->selend = but->pos;
2942           but->selsta = pos_prev;
2943         }
2944         else {
2945           data->selextend = EXTEND_LEFT;
2946           but->selend = pos_prev;
2947           but->selsta = but->pos;
2948         }
2949       }
2950     }
2951   }
2952 }
2953
2954 static bool ui_textedit_delete(uiBut *but,
2955                                uiHandleButtonData *data,
2956                                int direction,
2957                                eStrCursorJumpType jump)
2958 {
2959   char *str = data->str;
2960   const int len = strlen(str);
2961
2962   bool changed = false;
2963
2964   if (jump == STRCUR_JUMP_ALL) {
2965     if (len) {
2966       changed = true;
2967     }
2968     str[0] = '\0';
2969     but->pos = 0;
2970   }
2971   else if (direction) { /* delete */
2972     if ((but->selend - but->selsta) > 0) {
2973       changed = ui_textedit_delete_selection(but, data);
2974     }
2975     else if (but->pos >= 0 && but->pos < len) {
2976       int pos = but->pos;
2977       int step;
2978       BLI_str_cursor_step_utf8(str, len, &pos, direction, jump, true);
2979       step = pos - but->pos;
2980       memmove(&str[but->pos], &str[but->pos + step], (len + 1) - (but->pos + step));
2981       changed = true;
2982     }
2983   }
2984   else { /* backspace */
2985     if (len != 0) {
2986       if ((but->selend - but->selsta) > 0) {
2987         changed = ui_textedit_delete_selection(but, data);
2988       }
2989       else if (but->pos > 0) {
2990         int pos = but->pos;
2991         int step;
2992
2993         BLI_str_cursor_step_utf8(str, len, &pos, direction, jump, true);
2994         step = but->pos - pos;
2995         memmove(&str[but->pos - step], &str[but->pos], (len + 1) - but->pos);
2996         but->pos -= step;
2997         changed = true;
2998       }
2999     }
3000   }
3001
3002   return changed;
3003 }
3004
3005 static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data)
3006 {
3007   char *str;
3008   int changed;
3009
3010   str = data->str;
3011
3012   if (data->searchbox) {
3013     changed = ui_searchbox_autocomplete(C, data->searchbox, but, data->str);
3014   }
3015   else {
3016     changed = but->autocomplete_func(C, str, but->autofunc_arg);
3017   }
3018
3019   but->pos = strlen(str);
3020   but->selsta = but->selend = but->pos;
3021
3022   return changed;
3023 }
3024
3025 /* mode for ui_textedit_copypaste() */
3026 enum {
3027   UI_TEXTEDIT_PASTE = 1,
3028   UI_TEXTEDIT_COPY,
3029   UI_TEXTEDIT_CUT,
3030 };
3031
3032 static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const int mode)
3033 {
3034   char *pbuf;
3035   bool changed = false;
3036   int buf_len;
3037
3038   /* paste */
3039   if (mode == UI_TEXTEDIT_PASTE) {
3040     /* extract the first line from the clipboard */
3041     pbuf = WM_clipboard_text_get_firstline(false, &buf_len);
3042
3043     if (pbuf) {
3044       if (ui_but_is_utf8(but)) {
3045         buf_len -= BLI_utf8_invalid_strip(pbuf, (size_t)buf_len);
3046       }
3047
3048       ui_textedit_insert_buf(but, data, pbuf, buf_len);
3049
3050       changed = true;
3051
3052       MEM_freeN(pbuf);
3053     }
3054   }
3055   /* cut & copy */
3056   else if (ELEM(mode, UI_TEXTEDIT_COPY, UI_TEXTEDIT_CUT)) {
3057     /* copy the contents to the copypaste buffer */
3058     int sellen = but->selend - but->selsta;
3059     char *buf = MEM_mallocN(sizeof(char) * (sellen + 1), "ui_textedit_copypaste");
3060
3061     BLI_strncpy(buf, data->str + but->selsta, sellen + 1);
3062     WM_clipboard_text_set(buf, 0);
3063     MEM_freeN(buf);
3064
3065     /* for cut only, delete the selection afterwards */
3066     if (mode == UI_TEXTEDIT_CUT) {
3067       if ((but->selend - but->selsta) > 0) {
3068         changed = ui_textedit_delete_selection(but, data);
3069       }
3070     }
3071   }
3072
3073   return changed;
3074 }
3075
3076 #ifdef WITH_INPUT_IME
3077 /* enable ime, and set up uibut ime data */
3078 static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but))
3079 {
3080   /* XXX Is this really needed? */
3081   int x, y;
3082
3083   BLI_assert(win->ime_data == NULL);
3084
3085   /* enable IME and position to cursor, it's a trick */
3086   x = win->eventstate->x;
3087   /* flip y and move down a bit, prevent the IME panel cover the edit button */
3088   y = win->eventstate->y - 12;
3089
3090   wm_window_IME_begin(win, x, y, 0, 0, true);
3091 }
3092
3093 /* disable ime, and clear uibut ime data */
3094 static void ui_textedit_ime_end(wmWindow *win, uiBut *UNUSED(but))
3095 {
3096   wm_window_IME_end(win);
3097 }
3098
3099 void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete)
3100 {
3101   BLI_assert(but->active);
3102
3103   ui_region_to_window(but->active->region, &x, &y);
3104   wm_window_IME_begin(but->active->window, x, y - 4, 0, 0, complete);
3105 }
3106
3107 wmIMEData *ui_but_ime_data_get(uiBut *but)
3108 {
3109   if (but->active && but->active->window) {
3110     return but->active->window->ime_data;
3111   }
3112   else {
3113     return NULL;
3114   }
3115 }
3116 #endif /* WITH_INPUT_IME */
3117
3118 static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
3119 {
3120   wmWindow *win = CTX_wm_window(C);
3121   int len;
3122   const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER);
3123   bool no_zero_strip = false;
3124
3125   if (data->str) {
3126     MEM_freeN(data->str);
3127     data->str = NULL;
3128   }
3129
3130 #ifdef USE_DRAG_MULTINUM
3131   /* this can happen from multi-drag */
3132   if (data->applied_interactive) {
3133     /* remove any small changes so canceling edit doesn't restore invalid value: T40538 */
3134     data->cancel = true;
3135     ui_apply_but(C, but->block, but, data, true);
3136     data->cancel = false;
3137
3138     data->applied_interactive = false;
3139   }
3140 #endif
3141
3142 #ifdef USE_ALLSELECT
3143   if (is_num_but) {
3144     if (IS_ALLSELECT_EVENT(win->eventstate)) {
3145       data->select_others.is_enabled = true;
3146       data->select_others.is_copy = true;
3147     }
3148   }
3149 #endif
3150
3151   /* retrieve string */
3152   data->maxlen = ui_but_string_get_max_length(but);
3153   if (data->maxlen != 0) {
3154     data->str = MEM_callocN(sizeof(char) * data->maxlen, "textedit str");
3155     /* We do not want to truncate precision to default here, it's nice to show value,
3156      * not to edit it - way too much precision is lost then. */
3157     ui_but_string_get_ex(
3158         but, data->str, data->maxlen, UI_PRECISION_FLOAT_MAX, true, &no_zero_strip);
3159   }
3160   else {
3161     data->is_str_dynamic = true;
3162     data->str = ui_but_string_get_dynamic(but, &data->maxlen);
3163   }
3164
3165   if (ui_but_is_float(but) && !ui_but_is_unit(but) && !ui_but_anim_expression_get(but, NULL, 0) &&
3166       !no_zero_strip) {
3167     BLI_str_rstrip_float_zero(data->str, '\0');
3168   }
3169
3170   if (is_num_but) {
3171     BLI_assert(data->is_str_dynamic == false);
3172     ui_but_convert_to_unit_alt_name(but, data->str, data->maxlen);
3173   }
3174
3175   /* won't change from now on */
3176   len = strlen(data->str);
3177
3178   data->origstr = BLI_strdupn(data->str, len);
3179   data->selextend = EXTEND_NONE;
3180   data->selstartx = 0.0f;
3181
3182   /* set cursor pos to the end of the text */
3183   but->editstr = data->str;
3184   but->pos = len;
3185   but->selsta = 0;
3186   but->selend = len;
3187
3188   /* optional searchbox */
3189   if (but->type == UI_BTYPE_SEARCH_MENU) {
3190     data->searchbox = but->search_create_func(C, data->region, but);
3191     ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */
3192   }
3193
3194   /* reset alert flag (avoid confusion, will refresh on exit) */
3195   but->flag &= ~UI_BUT_REDALERT;
3196
3197   ui_but_update(but);
3198
3199   WM_cursor_modal_set(win, BC_TEXTEDITCURSOR);
3200
3201 #ifdef WITH_INPUT_IME
3202   if (is_num_but == false && BLT_lang_is_ime_supported()) {
3203     ui_textedit_ime_begin(win, but);
3204   }
3205 #endif
3206 }
3207
3208 static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
3209 {
3210   wmWindow *win = CTX_wm_window(C);
3211
3212   if (but) {
3213     if (ui_but_is_utf8(but)) {
3214       int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
3215       /* not a file?, strip non utf-8 chars */
3216       if (strip) {
3217         /* wont happen often so isn't that annoying to keep it here for a while */
3218         printf("%s: invalid utf8 - stripped chars %d\n", __func__, strip);
3219       }
3220     }
3221
3222     if (data->searchbox) {
3223       if (data->cancel == false) {
3224         if ((ui_searchbox_apply(but, data->searchbox) == false) &&
3225             (ui_searchbox_find_index(data->searchbox, but->editstr) == -1)) {
3226           data->cancel = true;
3227
3228           /* ensure menu (popup) too is closed! */
3229           data->escapecancel = true;
3230         }
3231       }
3232
3233       ui_searchbox_free(C, data->searchbox);
3234       data->searchbox = NULL;
3235     }
3236
3237     but->editstr = NULL;
3238     but->pos = -1;
3239   }
3240
3241   WM_cursor_modal_restore(win);
3242
3243 #ifdef WITH_INPUT_IME
3244   if (win->ime_data) {
3245     ui_textedit_ime_end(win, but);
3246   }
3247 #endif
3248 }
3249
3250 static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
3251 {
3252   uiBut *but;
3253
3254   /* label and roundbox can overlap real buttons (backdrops...) */
3255   if (ELEM(actbut->type,
3256            UI_BTYPE_LABEL,
3257            UI_BTYPE_SEPR,
3258            UI_BTYPE_SEPR_LINE,
3259            UI_BTYPE_ROUNDBOX,
3260            UI_BTYPE_LISTBOX)) {
3261     return;
3262   }
3263
3264   for (but = actbut->next; but; but = but->next) {
3265     if (ui_but_is_editable_as_text(but)) {
3266       if (!(but->flag & UI_BUT_DISABLED)) {
3267         data->postbut = but;
3268         data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3269         return;
3270       }
3271     }
3272   }
3273   for (but = block->buttons.first; but != actbut; but = but->next) {
3274     if (ui_but_is_editable_as_text(but)) {
3275       if (!(but->flag & UI_BUT_DISABLED)) {
3276         data->postbut = but;
3277         data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3278         return;
3279       }
3280     }
3281   }
3282 }
3283
3284 static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
3285 {
3286   uiBut *but;
3287
3288   /* label and roundbox can overlap real buttons (backdrops...) */
3289   if (ELEM(actbut->type,
3290            UI_BTYPE_LABEL,
3291            UI_BTYPE_SEPR,
3292            UI_BTYPE_SEPR_LINE,
3293            UI_BTYPE_ROUNDBOX,
3294            UI_BTYPE_LISTBOX)) {
3295     return;
3296   }
3297
3298   for (but = actbut->prev; but; but = but->prev) {
3299     if (ui_but_is_editable_as_text(but)) {
3300       if (!(but->flag & UI_BUT_DISABLED)) {
3301         data->postbut = but;
3302         data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3303         return;
3304       }
3305     }
3306   }
3307   for (but = block->buttons.last; but != actbut; but = but->prev) {
3308     if (ui_but_is_editable_as_text(but)) {
3309       if (!(but->flag & UI_BUT_DISABLED)) {
3310         data->postbut = but;
3311         data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3312         return;
3313       }
3314     }
3315   }
3316 }
3317
3318 static void ui_do_but_textedit(
3319     bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
3320 {
3321   int retval = WM_UI_HANDLER_CONTINUE;
3322   bool changed = false, inbox = false, update = false;
3323
3324 #ifdef WITH_INPUT_IME
3325   wmWindow *win = CTX_wm_window(C);
3326   wmIMEData *ime_data = win->ime_data;
3327   bool is_ime_composing = ime_data && ime_data->is_ime_composing;
3328 #else
3329   bool is_ime_composing = false;
3330 #endif
3331
3332   switch (event->type) {
3333     case MOUSEMOVE:
3334     case MOUSEPAN:
3335       if (data->searchbox) {
3336 #ifdef USE_KEYNAV_LIMIT
3337         if ((event->type == MOUSEMOVE) &&
3338             ui_mouse_motion_keynav_test(&data->searchbox_keynav_state, event)) {
3339           /* pass */
3340         }
3341         else {
3342           ui_searchbox_event(C, data->searchbox, but, event);
3343         }
3344 #else
3345         ui_searchbox_event(C, data->searchbox, but, event);
3346 #endif
3347       }
3348
3349       break;
3350     case RIGHTMOUSE:
3351     case ESCKEY:
3352       if (event->val == KM_PRESS) {
3353 #ifdef WITH_INPUT_IME
3354         /* skips button handling since it is not wanted */
3355         if (is_ime_composing) {
3356           break;
3357         }
3358 #endif
3359         data->cancel = true;
3360         data->escapecancel = true;
3361         button_activate_state(C, but, BUTTON_STATE_EXIT);
3362         retval = WM_UI_HANDLER_BREAK;
3363       }
3364       break;
3365     case LEFTMOUSE: {
3366       bool had_selection = but->selsta != but->selend;
3367
3368       /* exit on LMB only on RELEASE for searchbox, to mimic other popups,
3369        * and allow multiple menu levels */
3370       if (data->searchbox) {
3371         inbox = ui_searchbox_inside(data->searchbox, event->x, event->y);
3372       }
3373
3374       /* for double click: we do a press again for when you first click on button
3375        * (selects all text, no cursor pos) */
3376       if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) {
3377         float mx, my;
3378
3379         mx = event->x;
3380         my = event->y;
3381         ui_window_to_block_fl(data->region, block, &mx, &my);
3382
3383         if (ui_but_contains_pt(but, mx, my)) {
3384           ui_textedit_set_cursor_pos(but, data, event->x);
3385           but->selsta = but->selend = but->pos;
3386           data->selstartx = event->x;
3387
3388           button_activate_state(C, but, BUTTON_STATE_TEXT_SELECTING);
3389           retval = WM_UI_HANDLER_BREAK;
3390         }
3391         else if (inbox == false) {
3392           /* if searchbox, click outside will cancel */
3393           if (data->searchbox) {
3394             data->cancel = data->escapecancel = true;
3395           }
3396           button_activate_state(C, but, BUTTON_STATE_EXIT);
3397           retval = WM_UI_HANDLER_BREAK;
3398         }
3399       }
3400
3401       /* only select a word in button if there was no selection before */
3402       if (event->val == KM_DBL_CLICK && had_selection == false) {
3403         ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_DELIM);
3404         ui_textedit_move(but, data, STRCUR_DIR_NEXT, true, STRCUR_JUMP_DELIM);
3405         retval = WM_UI_HANDLER_BREAK;
3406         changed = true;
3407       }
3408       else if (inbox) {
3409         /* if we allow activation on key press,
3410          * it gives problems launching operators T35713. */
3411         if (event->val == KM_RELEASE) {
3412           button_activate_state(C, but, BUTTON_STATE_EXIT);
3413           retval = WM_UI_HANDLER_BREAK;
3414         }
3415       }
3416       break;
3417     }
3418   }
3419
3420   if (event->val == KM_PRESS && !is_ime_composing) {
3421     switch (event->type) {
3422       case VKEY:
3423       case XKEY:
3424       case CKEY:
3425         if (IS_EVENT_MOD(event, ctrl, oskey)) {
3426           if (event->type == VKEY) {
3427             changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_PASTE);
3428           }
3429           else if (event->type == CKEY) {
3430             changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_COPY);
3431           }
3432           else if (event->type == XKEY) {
3433             changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_CUT);
3434           }
3435
3436           retval = WM_UI_HANDLER_BREAK;
3437         }
3438         break;
3439       case RIGHTARROWKEY:
3440         ui_textedit_move(but,
3441                          data,
3442                          STRCUR_DIR_NEXT,
3443                          event->shift != 0,
3444                          event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
3445         retval = WM_UI_HANDLER_BREAK;
3446         break;
3447       case LEFTARROWKEY:
3448         ui_textedit_move(but,
3449                          data,
3450                          STRCUR_DIR_PREV,
3451                          event->shift != 0,
3452                          event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
3453         retval = WM_UI_HANDLER_BREAK;
3454         break;
3455       case WHEELDOWNMOUSE:
3456       case DOWNARROWKEY:
3457         if (data->searchbox) {
3458 #ifdef USE_KEYNAV_LIMIT
3459           ui_mouse_motion_keynav_init(&data->searchbox_keynav_state, event);
3460 #endif
3461           ui_searchbox_event(C, data->searchbox, but, event);
3462           break;
3463         }
3464         if (event->type == WHEELDOWNMOUSE) {
3465           break;
3466         }
3467         ATTR_FALLTHROUGH;
3468       case ENDKEY:
3469         ui_textedit_move(but, data, STRCUR_DIR_NEXT, event->shift != 0, STRCUR_JUMP_ALL);
3470         retval = WM_UI_HANDLER_BREAK;
3471         break;
3472       case WHEELUPMOUSE:
3473       case UPARROWKEY:
3474         if (data->searchbox) {
3475 #ifdef USE_KEYNAV_LIMIT
3476           ui_mouse_motion_keynav_init(&data->searchbox_keynav_state, event);
3477 #endif
3478           ui_searchbox_event(C, data->searchbox, but, event);
3479           break;
3480         }
3481         if (event->type == WHEELUPMOUSE) {
3482           break;
3483         }
3484         ATTR_FALLTHROUGH;
3485       case HOMEKEY:
3486         ui_textedit_move(but, data, STRCUR_DIR_PREV, event->shift != 0, STRCUR_JUMP_ALL);
3487         retval = WM_UI_HANDLER_BREAK;
3488         break;
3489       case PADENTER:
3490       case RETKEY:
3491         button_activate_state(C, but, BUTTON_STATE_EXIT);
3492         retval = WM_UI_HANDLER_BREAK;
3493         break;
3494       case DELKEY:
3495         changed = ui_textedit_delete(
3496             but, data, 1, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
3497         retval = WM_UI_HANDLER_BREAK;
3498         break;
3499
3500       case BACKSPACEKEY:
3501         changed = ui_textedit_delete(
3502             but, data, 0, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
3503         retval = WM_UI_HANDLER_BREAK;
3504         break;
3505
3506       case AKEY:
3507
3508         /* Ctrl + A: Select all */
3509 #if defined(__APPLE__)
3510         /* OSX uses cmd-a systemwide, so add it */
3511         if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) ||
3512             (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)))
3513 #else
3514         if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))
3515 #endif
3516         {
3517           ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_ALL);
3518           ui_textedit_move(but, data, STRCUR_DIR_NEXT, true, STRCUR_JUMP_ALL);
3519           retval = WM_UI_HANDLER_BREAK;
3520         }
3521         break;
3522
3523       case TABKEY:
3524         /* there is a key conflict here, we can't tab with autocomplete */
3525         if (but->autocomplete_func || data->searchbox) {
3526           int autocomplete = ui_textedit_autocomplete(C, but, data);
3527           changed = autocomplete != AUTOCOMPLETE_NO_MATCH;
3528
3529           if (autocomplete == AUTOCOMPLETE_FULL_MATCH) {
3530             button_activate_state(C, but, BUTTON_STATE_EXIT);
3531           }
3532         }
3533         /* the hotkey here is not well defined, was G.qual so we check all */
3534         else if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
3535           ui_textedit_prev_but(block, but, data);
3536           button_activate_state(C, but, BUTTON_STATE_EXIT);
3537         }
3538         else {
3539           ui_textedit_next_but(block, but, data);
3540           button_activate_state(C, but, BUTTON_STATE_EXIT);
3541         }
3542         retval = WM_UI_HANDLER_BREAK;
3543         break;
3544     }
3545
3546     if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)
3547 #ifdef WITH_INPUT_IME
3548         && !is_ime_composing && (!WM_event_is_ime_switch(event) || !BLT_lang_is_ime_supported())
3549 #endif
3550     ) {
3551       char ascii = event->ascii;
3552       const char *utf8_buf = event->utf8_buf;
3553
3554       /* exception that's useful for number buttons, some keyboard
3555        * numpads have a comma instead of a period */
3556       if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* could use data->min*/
3557         if (event->type == PADPERIOD && ascii == ',') {
3558           ascii = '.';
3559           utf8_buf = NULL; /* force ascii fallback */
3560         }
3561       }
3562
3563       if (utf8_buf && utf8_buf[0]) {
3564         int utf8_buf_len = BLI_str_utf8_size(utf8_buf);
3565         /* keep this printf until utf8 is well tested */
3566         if (utf8_buf_len != 1) {
3567           printf("%s: utf8 char '%.*s'\n", __func__, utf8_buf_len, utf8_buf);
3568         }
3569
3570         // strcpy(utf8_buf, "12345");
3571         changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len);
3572       }
3573       else {
3574         changed = ui_textedit_insert_ascii(but, data, ascii);
3575       }
3576
3577       retval = WM_UI_HANDLER_BREAK;
3578     }
3579     /* textbutton with this flag: do live update (e.g. for search buttons) */
3580     if (but->flag & UI_BUT_TEXTEDIT_UPDATE) {
3581       update = true;
3582     }
3583   }
3584
3585 #ifdef WITH_INPUT_IME
3586   if (event->type == WM_IME_COMPOSITE_START || event->type == WM_IME_COMPOSITE_EVENT) {
3587     changed = true;
3588
3589     if (event->type == WM_IME_COMPOSITE_START && but->selend > but->selsta) {
3590       ui_textedit_delete_selection(but, data);
3591     }
3592     if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) {
3593       ui_textedit_insert_buf(but, data, ime_data->str_result, ime_data->result_len);
3594     }
3595   }
3596   else if (event->type == WM_IME_COMPOSITE_END) {
3597     changed = true;
3598   }
3599 #endif
3600
3601   if (changed) {
3602     /* only do live update when but flag request it (UI_BUT_TEXTEDIT_UPDATE). */
3603     if (update && data->interactive) {
3604       ui_apply_but(C, block, but, data, true);
3605     }
3606     else {
3607       ui_but_update_edited(but);
3608     }
3609     but->changed = true;
3610
3611     if (data->searchbox) {
3612       ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */
3613     }
3614   }
3615
3616   if (changed || (retval == WM_UI_HANDLER_BREAK)) {
3617     ED_region_tag_redraw(data->region);
3618   }
3619 }
3620
3621 static void ui_do_but_textedit_select(
3622     bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
3623 {
3624   int mx, my, retval = WM_UI_HANDLER_CONTINUE;
3625
3626   switch (event->type) {
3627     case MOUSEMOVE: {
3628       mx = event->x;
3629       my = event->y;
3630       ui_window_to_block(data->region, block, &mx, &my);
3631
3632       ui_textedit_set_cursor_select(but, data, event->x);
3633       retval = WM_UI_HANDLER_BREAK;
3634       break;
3635     }
3636     case LEFTMOUSE:
3637       if (event->val == KM_RELEASE) {