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