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