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