d90135001c4697f0704f04ac76a309910628a146
[blender.git] / source / blender / editors / interface / interface.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation 2002-2008, full recode.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface.c
27  *  \ingroup edinterface
28  */
29
30
31 #include <float.h>
32 #include <limits.h>
33 #include <math.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <stddef.h>  /* offsetof() */
37
38 #include "MEM_guardedalloc.h"
39
40 #include "DNA_object_types.h"
41 #include "DNA_scene_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_userdef_types.h"
44 #include "DNA_workspace_types.h"
45
46 #include "BLI_math.h"
47 #include "BLI_listbase.h"
48 #include "BLI_string.h"
49 #include "BLI_string_utf8.h"
50 #include "BLI_rect.h"
51
52 #include "BLI_utildefines.h"
53
54 #include "BKE_animsys.h"
55 #include "BKE_context.h"
56 #include "BKE_idprop.h"
57 #include "BKE_main.h"
58 #include "BKE_scene.h"
59 #include "BKE_screen.h"
60 #include "BKE_unit.h"
61
62 #include "GPU_glew.h"
63 #include "GPU_matrix.h"
64 #include "GPU_state.h"
65
66 #include "BLF_api.h"
67 #include "BLT_translation.h"
68
69 #include "UI_interface.h"
70 #include "UI_interface_icons.h"
71
72 #include "IMB_imbuf.h"
73
74 #include "WM_api.h"
75 #include "WM_types.h"
76 #include "WM_message.h"
77
78 #include "RNA_access.h"
79
80 #include "BPY_extern.h"
81
82 #include "ED_screen.h"
83 #include "ED_numinput.h"
84
85 #include "IMB_colormanagement.h"
86
87 #include "DEG_depsgraph_query.h"
88
89 #include "interface_intern.h"
90
91 /* prototypes. */
92 static void ui_but_to_pixelrect(struct rcti *rect, const struct ARegion *ar, struct uiBlock *block, struct uiBut *but);
93 static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *but_p);
94
95 /* avoid unneeded calls to ui_but_value_get */
96 #define UI_BUT_VALUE_UNSET DBL_MAX
97 #define UI_GET_BUT_VALUE_INIT(_but, _value) if (_value == DBL_MAX) {  (_value) = ui_but_value_get(_but); } (void)0
98
99 #define B_NOP -1
100
101 /**
102  * a full doc with API notes can be found in 'blender/doc/guides/interface_API.txt'
103  *
104  * `uiBlahBlah()`       external function.
105  * `ui_blah_blah()`     internal function.
106  */
107
108 static void ui_but_free(const bContext *C, uiBut *but);
109
110 static bool ui_but_is_unit_radians_ex(UnitSettings *unit, const int unit_type)
111 {
112         return (unit->system_rotation == USER_UNIT_ROT_RADIANS && unit_type == PROP_UNIT_ROTATION);
113 }
114
115 static bool ui_but_is_unit_radians(const uiBut *but)
116 {
117         UnitSettings *unit = but->block->unit;
118         const int unit_type = UI_but_unit_type_get(but);
119
120         return ui_but_is_unit_radians_ex(unit, unit_type);
121 }
122
123 /* ************* window matrix ************** */
124
125 void ui_block_to_window_fl(const ARegion *ar, uiBlock *block, float *x, float *y)
126 {
127         float gx, gy;
128         int sx, sy, getsizex, getsizey;
129
130         getsizex = BLI_rcti_size_x(&ar->winrct) + 1;
131         getsizey = BLI_rcti_size_y(&ar->winrct) + 1;
132         sx = ar->winrct.xmin;
133         sy = ar->winrct.ymin;
134
135         gx = *x;
136         gy = *y;
137
138         if (block->panel) {
139                 gx += block->panel->ofsx;
140                 gy += block->panel->ofsy;
141         }
142
143         *x = ((float)sx) + ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] + block->winmat[3][0]));
144         *y = ((float)sy) + ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] + block->winmat[3][1]));
145 }
146
147 void ui_block_to_window(const ARegion *ar, uiBlock *block, int *x, int *y)
148 {
149         float fx, fy;
150
151         fx = *x;
152         fy = *y;
153
154         ui_block_to_window_fl(ar, block, &fx, &fy);
155
156         *x = (int)(fx + 0.5f);
157         *y = (int)(fy + 0.5f);
158 }
159
160 void ui_block_to_window_rctf(const ARegion *ar, uiBlock *block, rctf *rct_dst, const rctf *rct_src)
161 {
162         *rct_dst = *rct_src;
163         ui_block_to_window_fl(ar, block, &rct_dst->xmin, &rct_dst->ymin);
164         ui_block_to_window_fl(ar, block, &rct_dst->xmax, &rct_dst->ymax);
165 }
166
167 void ui_window_to_block_fl(const ARegion *ar, uiBlock *block, float *x, float *y)   /* for mouse cursor */
168 {
169         float a, b, c, d, e, f, px, py;
170         int sx, sy, getsizex, getsizey;
171
172         getsizex = BLI_rcti_size_x(&ar->winrct) + 1;
173         getsizey = BLI_rcti_size_y(&ar->winrct) + 1;
174         sx = ar->winrct.xmin;
175         sy = ar->winrct.ymin;
176
177         a = 0.5f * ((float)getsizex) * block->winmat[0][0];
178         b = 0.5f * ((float)getsizex) * block->winmat[1][0];
179         c = 0.5f * ((float)getsizex) * (1.0f + block->winmat[3][0]);
180
181         d = 0.5f * ((float)getsizey) * block->winmat[0][1];
182         e = 0.5f * ((float)getsizey) * block->winmat[1][1];
183         f = 0.5f * ((float)getsizey) * (1.0f + block->winmat[3][1]);
184
185         px = *x - sx;
186         py = *y - sy;
187
188         *y =  (a * (py - f) + d * (c - px)) / (a * e - d * b);
189         *x = (px - b * (*y) - c) / a;
190
191         if (block->panel) {
192                 *x -= block->panel->ofsx;
193                 *y -= block->panel->ofsy;
194         }
195 }
196
197 void ui_window_to_block(const ARegion *ar, uiBlock *block, int *x, int *y)
198 {
199         float fx, fy;
200
201         fx = *x;
202         fy = *y;
203
204         ui_window_to_block_fl(ar, block, &fx, &fy);
205
206         *x = (int)(fx + 0.5f);
207         *y = (int)(fy + 0.5f);
208 }
209
210 void ui_window_to_region(const ARegion *ar, int *x, int *y)
211 {
212         *x -= ar->winrct.xmin;
213         *y -= ar->winrct.ymin;
214 }
215
216 void ui_region_to_window(const ARegion *ar, int *x, int *y)
217 {
218         *x += ar->winrct.xmin;
219         *y += ar->winrct.ymin;
220 }
221
222 static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block)
223 {
224         int sepr_flex_len = 0;
225         for (uiBut *but = block->buttons.first; but; but = but->next) {
226                 if (but->type == UI_BTYPE_SEPR_SPACER) {
227                         sepr_flex_len++;
228                 }
229         }
230
231         if (sepr_flex_len == 0) {
232                 return;
233         }
234
235         rcti rect;
236         ui_but_to_pixelrect(&rect, region, block, block->buttons.last);
237         const float buttons_width = (float)rect.xmax + UI_HEADER_OFFSET;
238         const float region_width = (float)region->sizex * U.dpi_fac;
239
240         if (region_width <= buttons_width) {
241                 return;
242         }
243
244         /* We could get rid of this loop if we agree on a max number of spacer */
245         int *spacers_pos = alloca(sizeof(*spacers_pos) * (size_t)sepr_flex_len);
246         int i = 0;
247         for (uiBut *but = block->buttons.first; but; but = but->next) {
248                 if (but->type == UI_BTYPE_SEPR_SPACER) {
249                         ui_but_to_pixelrect(&rect, region, block, but);
250                         spacers_pos[i] = rect.xmax + UI_HEADER_OFFSET;
251                         i++;
252                 }
253         }
254
255         const float segment_width = region_width / (float)sepr_flex_len;
256         float offset = 0, remaining_space = region_width - buttons_width;
257         i = 0;
258         for (uiBut *but = block->buttons.first; but; but = but->next) {
259                 BLI_rctf_translate(&but->rect, offset, 0);
260                 if (but->type == UI_BTYPE_SEPR_SPACER) {
261                         /* How much the next block overlap with the current segment */
262                         int overlap = (
263                                 (i == sepr_flex_len - 1) ?
264                                 buttons_width - spacers_pos[i] :
265                                 (spacers_pos[i + 1] - spacers_pos[i]) / 2);
266                         int segment_end = segment_width * (i + 1);
267                         int spacer_end = segment_end - overlap;
268                         int spacer_sta = spacers_pos[i] + offset;
269                         if (spacer_end > spacer_sta) {
270                                 float step = min_ff(remaining_space, spacer_end - spacer_sta);
271                                 remaining_space -= step;
272                                 offset += step;
273                         }
274                         i++;
275                 }
276         }
277         ui_block_bounds_calc(block);
278 }
279
280 static void ui_update_window_matrix(const wmWindow *window, const ARegion *region, uiBlock *block)
281 {
282         /* window matrix and aspect */
283         if (region && region->visible) {
284                 /* Get projection matrix which includes View2D translation and zoom. */
285                 GPU_matrix_projection_get(block->winmat);
286                 block->aspect = 2.0f / fabsf(region->winx * block->winmat[0][0]);
287         }
288         else {
289                 /* No subwindow created yet, for menus for example, so we use the main
290                  * window instead, since buttons are created there anyway. */
291                 int width = WM_window_pixels_x(window);
292                 int height = WM_window_pixels_y(window);
293                 rcti winrct = {0, width - 1, 0, height - 1};
294
295                 wmGetProjectionMatrix(block->winmat, &winrct);
296                 block->aspect = 2.0f / fabsf(width * block->winmat[0][0]);
297         }
298 }
299
300 /**
301  * Popups will add a margin to #ARegion.winrct for shadow,
302  * for interactivity (point-inside tests for eg), we want the winrct without the margin added.
303  */
304 void ui_region_winrct_get_no_margin(const struct ARegion *ar, struct rcti *r_rect)
305 {
306         uiBlock *block = ar->uiblocks.first;
307         if (block && (block->flag & UI_BLOCK_LOOP) && (block->flag & UI_BLOCK_RADIAL) == 0) {
308                 BLI_rcti_rctf_copy_floor(r_rect, &block->rect);
309                 BLI_rcti_translate(r_rect, ar->winrct.xmin, ar->winrct.ymin);
310         }
311         else {
312                 *r_rect = ar->winrct;
313         }
314 }
315
316 /* ******************* block calc ************************* */
317
318 void UI_block_translate(uiBlock *block, int x, int y)
319 {
320         uiBut *but;
321
322         for (but = block->buttons.first; but; but = but->next) {
323                 BLI_rctf_translate(&but->rect, x, y);
324         }
325
326         BLI_rctf_translate(&block->rect, x, y);
327 }
328
329 static void ui_block_bounds_calc_text(uiBlock *block, float offset)
330 {
331         uiStyle *style = UI_style_get();
332         uiBut *bt, *init_col_bt, *col_bt;
333         int i = 0, j, x1addval = offset;
334
335         UI_fontstyle_set(&style->widget);
336
337         for (init_col_bt = bt = block->buttons.first; bt; bt = bt->next) {
338                 if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) {
339                         j = BLF_width(style->widget.uifont_id, bt->drawstr, sizeof(bt->drawstr));
340
341                         if (j > i)
342                                 i = j;
343                 }
344
345                 if (bt->next && bt->rect.xmin < bt->next->rect.xmin) {
346                         /* End of this column, and it's not the last one. */
347                         for (col_bt = init_col_bt; col_bt->prev != bt; col_bt = col_bt->next) {
348                                 col_bt->rect.xmin = x1addval;
349                                 col_bt->rect.xmax = x1addval + i + block->bounds;
350
351                                 ui_but_update(col_bt);  /* clips text again */
352                         }
353
354                         /* And we prepare next column. */
355                         x1addval += i + block->bounds;
356                         i = 0;
357                         init_col_bt = col_bt;
358                 }
359         }
360
361         /* Last column. */
362         for (col_bt = init_col_bt; col_bt; col_bt = col_bt->next) {
363                 col_bt->rect.xmin = x1addval;
364                 col_bt->rect.xmax = max_ff(x1addval + i + block->bounds, offset + block->minbounds);
365
366                 ui_but_update(col_bt);  /* clips text again */
367         }
368 }
369
370 void ui_block_bounds_calc(uiBlock *block)
371 {
372         uiBut *bt;
373         int xof;
374
375         if (BLI_listbase_is_empty(&block->buttons)) {
376                 if (block->panel) {
377                         block->rect.xmin = 0.0; block->rect.xmax = block->panel->sizex;
378                         block->rect.ymin = 0.0; block->rect.ymax = block->panel->sizey;
379                 }
380         }
381         else {
382
383                 BLI_rctf_init_minmax(&block->rect);
384
385                 for (bt = block->buttons.first; bt; bt = bt->next) {
386                         BLI_rctf_union(&block->rect, &bt->rect);
387                 }
388
389                 block->rect.xmin -= block->bounds;
390                 block->rect.ymin -= block->bounds;
391                 block->rect.xmax += block->bounds;
392                 block->rect.ymax += block->bounds;
393         }
394
395         block->rect.xmax = block->rect.xmin + max_ff(BLI_rctf_size_x(&block->rect), block->minbounds);
396
397         /* hardcoded exception... but that one is annoying with larger safety */
398         bt = block->buttons.first;
399         if (bt && STREQLEN(bt->str, "ERROR", 5)) xof = 10;
400         else xof = 40;
401
402         block->safety.xmin = block->rect.xmin - xof;
403         block->safety.ymin = block->rect.ymin - xof;
404         block->safety.xmax = block->rect.xmax + xof;
405         block->safety.ymax = block->rect.ymax + xof;
406 }
407
408 static void ui_block_bounds_calc_centered(wmWindow *window, uiBlock *block)
409 {
410         int xmax, ymax;
411         int startx, starty;
412         int width, height;
413
414         /* note: this is used for the splash where window bounds event has not been
415          * updated by ghost, get the window bounds from ghost directly */
416
417         xmax = WM_window_pixels_x(window);
418         ymax = WM_window_pixels_y(window);
419
420         ui_block_bounds_calc(block);
421
422         width  = BLI_rctf_size_x(&block->rect);
423         height = BLI_rctf_size_y(&block->rect);
424
425         startx = (xmax * 0.5f) - (width * 0.5f);
426         starty = (ymax * 0.5f) - (height * 0.5f);
427
428         UI_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin);
429
430         /* now recompute bounds and safety */
431         ui_block_bounds_calc(block);
432
433 }
434
435 static void ui_block_bounds_calc_centered_pie(uiBlock *block)
436 {
437         const int xy[2] = {
438             block->pie_data.pie_center_spawned[0],
439             block->pie_data.pie_center_spawned[1]
440         };
441
442         UI_block_translate(block, xy[0], xy[1]);
443
444         /* now recompute bounds and safety */
445         ui_block_bounds_calc(block);
446 }
447
448 static void ui_block_bounds_calc_popup(
449         wmWindow *window, uiBlock *block,
450         eBlockBoundsCalc bounds_calc, const int xy[2], int r_xy[2])
451 {
452         int width, height, oldwidth, oldheight;
453         int oldbounds, xmax, ymax, raw_x, raw_y;
454         const int margin = UI_SCREEN_MARGIN;
455         rcti rect, rect_bounds;
456         int ofs_dummy[2];
457
458         oldbounds = block->bounds;
459
460         /* compute mouse position with user defined offset */
461         ui_block_bounds_calc(block);
462
463         xmax = WM_window_pixels_x(window);
464         ymax = WM_window_pixels_y(window);
465
466         oldwidth  = BLI_rctf_size_x(&block->rect);
467         oldheight = BLI_rctf_size_y(&block->rect);
468
469         /* first we ensure wide enough text bounds */
470         if (bounds_calc == UI_BLOCK_BOUNDS_POPUP_MENU) {
471                 if (block->flag & UI_BLOCK_LOOP) {
472                         block->bounds = 2.5f * UI_UNIT_X;
473                         ui_block_bounds_calc_text(block, block->rect.xmin);
474                 }
475         }
476
477         /* next we recompute bounds */
478         block->bounds = oldbounds;
479         ui_block_bounds_calc(block);
480
481         /* and we adjust the position to fit within window */
482         width  = BLI_rctf_size_x(&block->rect);
483         height = BLI_rctf_size_y(&block->rect);
484
485         /* avoid divide by zero below, caused by calling with no UI, but better not crash */
486         oldwidth = oldwidth > 0 ? oldwidth : MAX2(1, width);
487         oldheight = oldheight > 0 ? oldheight : MAX2(1, height);
488
489         /* offset block based on mouse position, user offset is scaled
490          * along in case we resized the block in ui_block_bounds_calc_text */
491         raw_x = rect.xmin = xy[0] + block->rect.xmin + (block->mx * width) / oldwidth;
492         raw_y = rect.ymin = xy[1] + block->rect.ymin + (block->my * height) / oldheight;
493         rect.xmax = rect.xmin + width;
494         rect.ymax = rect.ymin + height;
495
496         rect_bounds.xmin = margin;
497         rect_bounds.ymin = margin;
498         rect_bounds.xmax = xmax - margin;
499         rect_bounds.ymax = ymax - UI_POPUP_MENU_TOP;
500
501         BLI_rcti_clamp(&rect, &rect_bounds, ofs_dummy);
502         UI_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin);
503
504         /* now recompute bounds and safety */
505         ui_block_bounds_calc(block);
506
507         /* If given, adjust input coordinates such that they would generate real final popup position.
508          * Needed to handle correctly floating panels once they have been dragged around, see T52999. */
509         if (r_xy) {
510                 r_xy[0] = xy[0] + block->rect.xmin - raw_x;
511                 r_xy[1] = xy[1] + block->rect.ymin - raw_y;
512         }
513 }
514
515 /* used for various cases */
516 void UI_block_bounds_set_normal(uiBlock *block, int addval)
517 {
518         if (block == NULL)
519                 return;
520
521         block->bounds = addval;
522         block->bounds_type = UI_BLOCK_BOUNDS;
523 }
524
525 /* used for pulldowns */
526 void UI_block_bounds_set_text(uiBlock *block, int addval)
527 {
528         block->bounds = addval;
529         block->bounds_type = UI_BLOCK_BOUNDS_TEXT;
530 }
531
532 /* used for block popups */
533 void UI_block_bounds_set_popup(uiBlock *block, int addval, int mx, int my)
534 {
535         block->bounds = addval;
536         block->bounds_type = UI_BLOCK_BOUNDS_POPUP_MOUSE;
537         block->mx = mx;
538         block->my = my;
539 }
540
541 /* used for menu popups */
542 void UI_block_bounds_set_menu(uiBlock *block, int addval, int mx, int my)
543 {
544         block->bounds = addval;
545         block->bounds_type = UI_BLOCK_BOUNDS_POPUP_MENU;
546         block->mx = mx;
547         block->my = my;
548 }
549
550 /* used for centered popups, i.e. splash */
551 void UI_block_bounds_set_centered(uiBlock *block, int addval)
552 {
553         block->bounds = addval;
554         block->bounds_type = UI_BLOCK_BOUNDS_POPUP_CENTER;
555 }
556
557 void UI_block_bounds_set_explicit(uiBlock *block, int minx, int miny, int maxx, int maxy)
558 {
559         block->rect.xmin = minx;
560         block->rect.ymin = miny;
561         block->rect.xmax = maxx;
562         block->rect.ymax = maxy;
563         block->bounds_type = UI_BLOCK_BOUNDS_NONE;
564 }
565
566 static int ui_but_calc_float_precision(uiBut *but, double value)
567 {
568         int prec = (int)but->a2;
569
570         /* first check for various special cases:
571          * * If button is radians, we want additional precision (see T39861).
572          * * If prec is not set, we fallback to a simple default */
573         if (ui_but_is_unit_radians(but) && prec < 5) {
574                 prec = 5;
575         }
576         else if (prec == -1) {
577                 prec = (but->hardmax < 10.001f) ? 3 : 2;
578         }
579         else {
580                 CLAMP(prec, 0, UI_PRECISION_FLOAT_MAX);
581         }
582
583         return UI_calc_float_precision(prec, value);
584 }
585
586 /* ************** BLOCK ENDING FUNCTION ************* */
587
588 /* NOTE: if but->poin is allocated memory for every defbut, things fail... */
589 static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
590 {
591         /* various properties are being compared here, hopefully sufficient
592          * to catch all cases, but it is simple to add more checks later */
593         if (but->retval != oldbut->retval) return false;
594         if (but->rnapoin.data != oldbut->rnapoin.data) return false;
595         if (but->rnaprop != oldbut->rnaprop || but->rnaindex != oldbut->rnaindex) return false;
596         if (but->func != oldbut->func) return false;
597         if (but->funcN != oldbut->funcN) return false;
598         if (oldbut->func_arg1 != oldbut && but->func_arg1 != oldbut->func_arg1) return false;
599         if (oldbut->func_arg2 != oldbut && but->func_arg2 != oldbut->func_arg2) return false;
600         if (!but->funcN && ((but->poin != oldbut->poin && (uiBut *)oldbut->poin != oldbut) ||
601                             (but->pointype != oldbut->pointype))) return false;
602         if (but->optype != oldbut->optype) return false;
603
604         return true;
605 }
606
607 uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new)
608 {
609         uiBut *but_old;
610         for (but_old = block_old->buttons.first; but_old; but_old = but_old->next) {
611                 if (ui_but_equals_old(but_new, but_old)) {
612                         break;
613                 }
614         }
615         return but_old;
616 }
617 uiBut *ui_but_find_new(uiBlock *block_new, const uiBut *but_old)
618 {
619         uiBut *but_new;
620         for (but_new = block_new->buttons.first; but_new; but_new = but_new->next) {
621                 if (ui_but_equals_old(but_new, but_old)) {
622                         break;
623                 }
624         }
625         return but_new;
626 }
627
628 /**
629  * \return true when \a but_p is set (only done for active buttons).
630  */
631 static bool ui_but_update_from_old_block(const bContext *C, uiBlock *block, uiBut **but_p, uiBut **but_old_p)
632 {
633         const int drawflag_copy = 0;  /* None currently. */
634
635         uiBlock *oldblock = block->oldblock;
636         uiBut *oldbut = NULL, *but = *but_p;
637         bool found_active = false;
638
639
640 #if 0
641         /* simple/stupid - search every time */
642         oldbut = ui_but_find_old(oldblock, but);
643         (void)but_old_p;
644 #else
645         BLI_assert(*but_old_p == NULL || BLI_findindex(&oldblock->buttons, *but_old_p) != -1);
646
647         /* fastpath - avoid loop-in-loop, calling 'ui_but_find_old'
648          * as long as old/new buttons are aligned */
649         if (LIKELY(*but_old_p && ui_but_equals_old(but, *but_old_p))) {
650                 oldbut = *but_old_p;
651         }
652         else {
653                 /* fallback to block search */
654                 oldbut = ui_but_find_old(oldblock, but);
655         }
656         (*but_old_p) = oldbut ? oldbut->next : NULL;
657 #endif
658
659
660         if (!oldbut) {
661                 return found_active;
662         }
663
664         if (oldbut->active) {
665                 /* flags from the buttons we want to refresh, may want to add more here... */
666                 const int flag_copy = UI_BUT_REDALERT | UI_HAS_ICON;
667
668                 found_active = true;
669
670 #if 0
671                 but->flag = oldbut->flag;
672                 but->active = oldbut->active;
673                 but->pos = oldbut->pos;
674                 but->ofs = oldbut->ofs;
675                 but->editstr = oldbut->editstr;
676                 but->editval = oldbut->editval;
677                 but->editvec = oldbut->editvec;
678                 but->editcoba = oldbut->editcoba;
679                 but->editcumap = oldbut->editcumap;
680                 but->selsta = oldbut->selsta;
681                 but->selend = oldbut->selend;
682                 but->softmin = oldbut->softmin;
683                 but->softmax = oldbut->softmax;
684                 oldbut->active = NULL;
685 #endif
686
687                 /* move button over from oldblock to new block */
688                 BLI_remlink(&oldblock->buttons, oldbut);
689                 BLI_insertlinkafter(&block->buttons, but, oldbut);
690                 oldbut->block = block;
691                 *but_p = oldbut;
692
693                 /* still stuff needs to be copied */
694                 oldbut->rect = but->rect;
695                 oldbut->context = but->context; /* set by Layout */
696
697                 /* drawing */
698                 oldbut->icon = but->icon;
699                 oldbut->iconadd = but->iconadd;
700                 oldbut->alignnr = but->alignnr;
701
702                 /* typically the same pointers, but not on undo/redo */
703                 /* XXX some menu buttons store button itself in but->poin. Ugly */
704                 if (oldbut->poin != (char *)oldbut) {
705                         SWAP(char *, oldbut->poin, but->poin);
706                         SWAP(void *, oldbut->func_argN, but->func_argN);
707                         SWAP(void *, oldbut->tip_argN, but->tip_argN);
708                 }
709
710                 oldbut->flag = (oldbut->flag & ~flag_copy) | (but->flag & flag_copy);
711                 oldbut->drawflag = (oldbut->drawflag & ~drawflag_copy) | (but->drawflag & drawflag_copy);
712
713                 /* copy hardmin for list rows to prevent 'sticking' highlight to mouse position
714                  * when scrolling without moving mouse (see [#28432]) */
715                 if (ELEM(oldbut->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) {
716                         oldbut->hardmax = but->hardmax;
717                 }
718
719                 /* Selectively copy a1, a2 since their use differs across all button types
720                  * (and we'll probably split these out later) */
721                 if (ELEM(oldbut->type, UI_BTYPE_PROGRESS_BAR)) {
722                         oldbut->a1 = but->a1;
723                 }
724
725                 if (!BLI_listbase_is_empty(&block->butstore)) {
726                         UI_butstore_register_update(block, oldbut, but);
727                 }
728
729                 /* move/copy string from the new button to the old */
730                 /* needed for alt+mouse wheel over enums */
731                 if (but->str != but->strdata) {
732                         if (oldbut->str != oldbut->strdata) {
733                                 SWAP(char *, but->str, oldbut->str);
734                         }
735                         else {
736                                 oldbut->str = but->str;
737                                 but->str = but->strdata;
738                         }
739                 }
740                 else {
741                         if (oldbut->str != oldbut->strdata) {
742                                 MEM_freeN(oldbut->str);
743                                 oldbut->str = oldbut->strdata;
744                         }
745                         BLI_strncpy(oldbut->strdata, but->strdata, sizeof(oldbut->strdata));
746                 }
747
748                 if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
749                         SWAP(void *, but->dragpoin, oldbut->dragpoin);
750                 }
751
752                 BLI_remlink(&block->buttons, but);
753                 ui_but_free(C, but);
754
755                 /* note: if layout hasn't been applied yet, it uses old button pointers... */
756         }
757         else {
758                 const int flag_copy = UI_BUT_DRAG_MULTI;
759
760                 but->flag = (but->flag & ~flag_copy) | (oldbut->flag & flag_copy);
761
762                 /* ensures one button can get activated, and in case the buttons
763                  * draw are the same this gives O(1) lookup for each button */
764                 BLI_remlink(&oldblock->buttons, oldbut);
765                 ui_but_free(C, oldbut);
766         }
767
768         return found_active;
769 }
770
771 /* needed for temporarily rename buttons, such as in outliner or file-select,
772  * they should keep calling uiDefButs to keep them alive */
773 /* returns 0 when button removed */
774 bool UI_but_active_only(const bContext *C, ARegion *ar, uiBlock *block, uiBut *but)
775 {
776         uiBlock *oldblock;
777         uiBut *oldbut;
778         bool activate = false, found = false, isactive = false;
779
780         oldblock = block->oldblock;
781         if (!oldblock) {
782                 activate = true;
783         }
784         else {
785                 oldbut = ui_but_find_old(oldblock, but);
786                 if (oldbut) {
787                         found = true;
788
789                         if (oldbut->active) {
790                                 isactive = true;
791                         }
792                 }
793         }
794         if ((activate == true) || (found == false)) {
795                 ui_but_activate_event((bContext *)C, ar, but);
796         }
797         else if ((found == true) && (isactive == false)) {
798                 BLI_remlink(&block->buttons, but);
799                 ui_but_free(C, but);
800                 return false;
801         }
802
803         return true;
804 }
805
806 /* simulate button click */
807 void UI_but_execute(const bContext *C, uiBut *but)
808 {
809         ARegion *ar = CTX_wm_region(C);
810         void *active_back;
811         ui_but_execute_begin((bContext *)C, ar, but, &active_back);
812         /* Value is applied in begin. No further action required. */
813         ui_but_execute_end((bContext *)C, ar, but, active_back);
814 }
815
816 /* use to check if we need to disable undo, but don't make any changes
817  * returns false if undo needs to be disabled. */
818 static bool ui_but_is_rna_undo(const uiBut *but)
819 {
820         if (but->rnapoin.id.data) {
821                 /* avoid undo push for buttons who's ID are screen or wm level
822                  * we could disable undo for buttons with no ID too but may have
823                  * unforeseen consequences, so best check for ID's we _know_ are not
824                  * handled by undo - campbell */
825                 ID *id = but->rnapoin.id.data;
826                 if (ID_CHECK_UNDO(id) == false) {
827                         return false;
828                 }
829                 else {
830                         return true;
831                 }
832         }
833         else if (but->rnapoin.type && !RNA_struct_undo_check(but->rnapoin.type)) {
834                 return false;
835         }
836
837         return true;
838 }
839
840 /* assigns automatic keybindings to menu items for fast access
841  * (underline key in menu) */
842 static void ui_menu_block_set_keyaccels(uiBlock *block)
843 {
844         uiBut *but;
845
846         unsigned int menu_key_mask = 0;
847         unsigned char menu_key;
848         const char *str_pt;
849         int pass;
850         int tot_missing = 0;
851
852         /* only do it before bounding */
853         if (block->rect.xmin != block->rect.xmax)
854                 return;
855
856         for (pass = 0; pass < 2; pass++) {
857                 /* 2 Passes, on for first letter only, second for any letter if first fails
858                  * fun first pass on all buttons so first word chars always get first priority */
859
860                 for (but = block->buttons.first; but; but = but->next) {
861                         if (!ELEM(but->type,
862                                   UI_BTYPE_BUT,
863                                   UI_BTYPE_BUT_MENU,
864                                   UI_BTYPE_MENU, UI_BTYPE_BLOCK,
865                                   UI_BTYPE_PULLDOWN) ||
866                             (but->flag & UI_HIDDEN))
867                         {
868                                 /* pass */
869                         }
870                         else if (but->menu_key == '\0') {
871                                 if (but->str) {
872                                         for (str_pt = but->str; *str_pt; ) {
873                                                 menu_key = tolower(*str_pt);
874                                                 if ((menu_key >= 'a' && menu_key <= 'z') && !(menu_key_mask & 1 << (menu_key - 'a'))) {
875                                                         menu_key_mask |= 1 << (menu_key - 'a');
876                                                         break;
877                                                 }
878
879                                                 if (pass == 0) {
880                                                         /* Skip to next delimiter on first pass (be picky) */
881                                                         while (isalpha(*str_pt))
882                                                                 str_pt++;
883
884                                                         if (*str_pt)
885                                                                 str_pt++;
886                                                 }
887                                                 else {
888                                                         /* just step over every char second pass and find first usable key */
889                                                         str_pt++;
890                                                 }
891                                         }
892
893                                         if (*str_pt) {
894                                                 but->menu_key = menu_key;
895                                         }
896                                         else {
897                                                 /* run second pass */
898                                                 tot_missing++;
899                                         }
900
901                                         /* if all keys have been used just exit, unlikely */
902                                         if (menu_key_mask == (1 << 26) - 1) {
903                                                 return;
904                                         }
905                                 }
906                         }
907                 }
908
909                 /* check if second pass is needed */
910                 if (!tot_missing) {
911                         break;
912                 }
913         }
914 }
915
916 /* XXX, this code will shorten any allocated string to 'UI_MAX_NAME_STR'
917  * since this is really long its unlikely to be an issue,
918  * but this could be supported */
919 void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_strip)
920 {
921         if (do_strip && (but->flag & UI_BUT_HAS_SEP_CHAR)) {
922                 char *cpoin = strrchr(but->str, UI_SEP_CHAR);
923                 if (cpoin) {
924                         *cpoin = '\0';
925                 }
926                 but->flag &= ~UI_BUT_HAS_SEP_CHAR;
927         }
928
929         /* without this, just allow stripping of the shortcut */
930         if (shortcut_str) {
931                 char *butstr_orig;
932
933                 if (but->str != but->strdata) {
934                         butstr_orig = but->str; /* free after using as source buffer */
935                 }
936                 else {
937                         butstr_orig = BLI_strdup(but->str);
938                 }
939                 BLI_snprintf(
940                         but->strdata,
941                         sizeof(but->strdata),
942                         "%s" UI_SEP_CHAR_S "%s",
943                         butstr_orig, shortcut_str);
944                 MEM_freeN(butstr_orig);
945                 but->str = but->strdata;
946                 but->flag |= UI_BUT_HAS_SEP_CHAR;
947                 but->drawflag |= UI_BUT_HAS_SHORTCUT;
948                 ui_but_update(but);
949         }
950 }
951
952 /* -------------------------------------------------------------------- */
953 /** \name Find Key Shortcut for Button
954  *
955  * - #ui_but_event_operator_string (and helpers)
956  * - #ui_but_event_property_operator_string
957  * \{ */
958
959 static bool ui_but_event_operator_string_from_operator(
960         const bContext *C, uiBut *but,
961         char *buf, const size_t buf_len)
962 {
963         BLI_assert(but->optype != NULL);
964         bool found = false;
965         IDProperty *prop = (but->opptr) ? but->opptr->data : NULL;
966
967         if (WM_key_event_operator_string(
968                     C, but->optype->idname, but->opcontext, prop, true,
969                     buf, buf_len))
970         {
971                 found = true;
972         }
973         return found;
974 }
975
976 static bool ui_but_event_operator_string_from_menu(
977         const bContext *C, uiBut *but,
978         char *buf, const size_t buf_len)
979 {
980         MenuType *mt = UI_but_menutype_get(but);
981         BLI_assert(mt != NULL);
982
983         bool found = false;
984         IDProperty *prop_menu;
985
986         /* annoying, create a property */
987         IDPropertyTemplate val = {0};
988         prop_menu = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant  */
989         IDP_AddToGroup(prop_menu, IDP_NewString(mt->idname, "name", sizeof(mt->idname)));
990
991         if (WM_key_event_operator_string(
992                     C, "WM_OT_call_menu", WM_OP_INVOKE_REGION_WIN, prop_menu, true,
993                     buf, buf_len))
994         {
995                 found = true;
996         }
997
998         IDP_FreeProperty(prop_menu);
999         MEM_freeN(prop_menu);
1000         return found;
1001 }
1002
1003 static bool ui_but_event_operator_string_from_panel(
1004         const bContext *C, uiBut *but,
1005         char *buf, const size_t buf_len)
1006 {
1007         /** Nearly exact copy of #ui_but_event_operator_string_from_menu */
1008         PanelType *pt = UI_but_paneltype_get(but);
1009         BLI_assert(pt != NULL);
1010
1011         bool found = false;
1012         IDProperty *prop_panel;
1013
1014         /* annoying, create a property */
1015         IDPropertyTemplate val = {0};
1016         prop_panel = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant  */
1017         IDP_AddToGroup(prop_panel, IDP_NewString(pt->idname, "name", sizeof(pt->idname)));
1018         IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = pt->space_type, }, "space_type"));
1019         IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = pt->region_type, }, "region_type"));
1020
1021         for (int i = 0; i < 2; i++) {
1022                 /* FIXME(campbell): We can't reasonably search all configurations - long term. */
1023                 IDP_ReplaceInGroup(prop_panel, IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = i, }, "keep_open"));
1024                 if (WM_key_event_operator_string(
1025                             C, "WM_OT_call_panel", WM_OP_INVOKE_REGION_WIN, prop_panel, true,
1026                             buf, buf_len))
1027                 {
1028                         found = true;
1029                         break;
1030                 }
1031         }
1032
1033         IDP_FreeProperty(prop_panel);
1034         MEM_freeN(prop_panel);
1035         return found;
1036 }
1037
1038 static bool ui_but_event_operator_string(
1039         const bContext *C, uiBut *but,
1040         char *buf, const size_t buf_len)
1041 {
1042         bool found = false;
1043
1044         if (but->optype != NULL) {
1045                 found = ui_but_event_operator_string_from_operator(C, but, buf, buf_len);
1046         }
1047         else if (UI_but_menutype_get(but) != NULL) {
1048                 found = ui_but_event_operator_string_from_menu(C, but, buf, buf_len);
1049         }
1050         else if (UI_but_paneltype_get(but) != NULL) {
1051                 found = ui_but_event_operator_string_from_panel(C, but, buf, buf_len);
1052         }
1053
1054         return found;
1055 }
1056
1057 static bool ui_but_event_property_operator_string(
1058         const bContext *C, uiBut *but,
1059         char *buf, const size_t buf_len)
1060 {
1061         /* context toggle operator names to check... */
1062
1063         /* This function could use a refactor to generalize button type to operator relationship
1064          * as well as which operators use properties.
1065          * - Campbell
1066          * */
1067         const char *ctx_toggle_opnames[] = {
1068                 "WM_OT_context_toggle",
1069                 "WM_OT_context_toggle_enum",
1070                 "WM_OT_context_cycle_int",
1071                 "WM_OT_context_cycle_enum",
1072                 "WM_OT_context_cycle_array",
1073                 "WM_OT_context_menu_enum",
1074                 NULL
1075         };
1076
1077         const char *ctx_enum_opnames[] = {
1078                 "WM_OT_context_set_enum",
1079                 NULL
1080         };
1081
1082         const char *ctx_enum_opnames_for_Area_ui_type[] = {
1083                 "SCREEN_OT_space_type_set_or_cycle",
1084                 NULL
1085         };
1086
1087         const char **opnames = ctx_toggle_opnames;
1088         int          opnames_len = ARRAY_SIZE(ctx_toggle_opnames);
1089
1090
1091         int  prop_enum_value = -1;
1092         bool prop_enum_value_ok = false;
1093         bool prop_enum_value_is_int = false;
1094         const char *prop_enum_value_id = "value";
1095         PointerRNA *ptr = &but->rnapoin;
1096         PropertyRNA *prop = but->rnaprop;
1097         if ((but->type == UI_BTYPE_BUT_MENU) && (but->block->handle != NULL)) {
1098                 uiBut *but_parent = but->block->handle->popup_create_vars.but;
1099                 if ((but->type == UI_BTYPE_BUT_MENU) &&
1100                     (but_parent && but_parent->rnaprop) &&
1101                     (RNA_property_type(but_parent->rnaprop) == PROP_ENUM) &&
1102                     (but_parent->menu_create_func == ui_def_but_rna__menu))
1103                 {
1104                         prop_enum_value = (int)but->hardmin;
1105                         ptr = &but_parent->rnapoin;
1106                         prop = but_parent->rnaprop;
1107                         prop_enum_value_ok = true;
1108
1109                         opnames = ctx_enum_opnames;
1110                         opnames_len = ARRAY_SIZE(ctx_enum_opnames);
1111                 }
1112         }
1113
1114         bool found = false;
1115
1116         /* Don't use the button again. */
1117         but = NULL;
1118
1119         /* this version is only for finding hotkeys for properties (which get set via context using operators) */
1120         if (prop) {
1121                 /* to avoid massive slowdowns on property panels, for now, we only check the
1122                  * hotkeys for Editor / Scene settings...
1123                  *
1124                  * TODO: userpref settings?
1125                  */
1126                 char *data_path = NULL;
1127
1128                 if (ptr->id.data) {
1129                         ID *id = ptr->id.data;
1130
1131                         if (GS(id->name) == ID_SCR) {
1132                                 /* screen/editor property
1133                                  * NOTE: in most cases, there is actually no info for backwards tracing
1134                                  * how to get back to ID from the editor data we may be dealing with
1135                                  */
1136                                 if (RNA_struct_is_a(ptr->type, &RNA_Space)) {
1137                                         /* data should be directly on here... */
1138                                         data_path = BLI_sprintfN("space_data.%s", RNA_property_identifier(prop));
1139                                 }
1140                                 else if (RNA_struct_is_a(ptr->type, &RNA_Area)) {
1141                                         /* data should be directly on here... */
1142                                         const char *prop_id = RNA_property_identifier(prop);
1143                                         /* Hack since keys access 'type', UI shows 'ui_type'. */
1144                                         if (STREQ(prop_id, "ui_type")) {
1145                                                 prop_id = "type";
1146                                                 prop_enum_value >>= 16;
1147                                                 prop = RNA_struct_find_property(ptr, prop_id);
1148
1149                                                 opnames = ctx_enum_opnames_for_Area_ui_type;
1150                                                 opnames_len = ARRAY_SIZE(ctx_enum_opnames_for_Area_ui_type);
1151                                                 prop_enum_value_id = "space_type";
1152                                                 prop_enum_value_is_int = true;
1153                                         }
1154                                         else {
1155                                                 data_path = BLI_sprintfN("area.%s", prop_id);
1156                                         }
1157                                 }
1158                                 else {
1159                                         /* special exceptions for common nested data in editors... */
1160                                         if (RNA_struct_is_a(ptr->type, &RNA_DopeSheet)) {
1161                                                 /* dopesheet filtering options... */
1162                                                 data_path = BLI_sprintfN("space_data.dopesheet.%s", RNA_property_identifier(prop));
1163                                         }
1164                                         else if (RNA_struct_is_a(ptr->type, &RNA_FileSelectParams)) {
1165                                                 /* Filebrowser options... */
1166                                                 data_path = BLI_sprintfN("space_data.params.%s", RNA_property_identifier(prop));
1167                                         }
1168                                 }
1169                         }
1170                         else if (GS(id->name) == ID_SCE) {
1171                                 if (RNA_struct_is_a(ptr->type, &RNA_ToolSettings)) {
1172                                         /* toolsettings property
1173                                          * NOTE: toolsettings is usually accessed directly (i.e. not through scene)
1174                                          */
1175                                         data_path = RNA_path_from_ID_to_property(ptr, prop);
1176                                 }
1177                                 else {
1178                                         /* scene property */
1179                                         char *path = RNA_path_from_ID_to_property(ptr, prop);
1180
1181                                         if (path) {
1182                                                 data_path = BLI_sprintfN("scene.%s", path);
1183                                                 MEM_freeN(path);
1184                                         }
1185 #if 0
1186                                         else {
1187                                                 printf("ERROR in %s(): Couldn't get path for scene property - %s\n",
1188                                                        __func__, RNA_property_identifier(prop));
1189                                         }
1190 #endif
1191                                 }
1192                         }
1193                         else {
1194                                 //puts("other id");
1195                         }
1196
1197                         //printf("prop shortcut: '%s' (%s)\n", RNA_property_identifier(prop), data_path);
1198                 }
1199
1200                 /* we have a datapath! */
1201                 if (data_path || (prop_enum_value_ok && prop_enum_value_id)) {
1202                         /* create a property to host the "datapath" property we're sending to the operators */
1203                         IDProperty *prop_path;
1204
1205                         IDPropertyTemplate val = {0};
1206                         prop_path = IDP_New(IDP_GROUP, &val, __func__);
1207                         if (data_path) {
1208                                 IDP_AddToGroup(prop_path, IDP_NewString(data_path, "data_path", strlen(data_path) + 1));
1209                         }
1210                         if (prop_enum_value_ok) {
1211                                 const EnumPropertyItem *item;
1212                                 bool free;
1213                                 RNA_property_enum_items((bContext *)C, ptr, prop, &item, NULL, &free);
1214                                 int index = RNA_enum_from_value(item, prop_enum_value);
1215                                 if (index != -1) {
1216                                         IDProperty *prop_value;
1217                                         if (prop_enum_value_is_int) {
1218                                                 int value = item[index].value;
1219                                                 prop_value = IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = value, }, prop_enum_value_id);
1220                                         }
1221                                         else {
1222                                                 const char *id = item[index].identifier;
1223                                                 prop_value = IDP_NewString(id, prop_enum_value_id, strlen(id) + 1);
1224                                         }
1225                                         IDP_AddToGroup(prop_path, prop_value);
1226                                 }
1227                                 else {
1228                                         opnames_len = 0;  /* Do nothing. */
1229                                 }
1230                                 if (free) {
1231                                         MEM_freeN((void *)item);
1232                                 }
1233                         }
1234
1235                         /* check each until one works... */
1236
1237                         for (int i = 0; (i < opnames_len) && (opnames[i]); i++) {
1238                                 if (WM_key_event_operator_string(
1239                                             C, opnames[i], WM_OP_INVOKE_REGION_WIN, prop_path, false,
1240                                             buf, buf_len))
1241                                 {
1242                                         found = true;
1243                                         break;
1244                                 }
1245                         }
1246
1247                         /* cleanup */
1248                         IDP_FreeProperty(prop_path);
1249                         MEM_freeN(prop_path);
1250                         if (data_path) {
1251                                 MEM_freeN(data_path);
1252                         }
1253                 }
1254         }
1255
1256         return found;
1257 }
1258
1259 /** \} */
1260
1261 /**
1262  * This goes in a seemingly weird pattern:
1263  *
1264  * <pre>
1265  *     4
1266  *  5     6
1267  * 1       2
1268  *  7     8
1269  *     3
1270  * </pre>
1271  *
1272  * but it's actually quite logical. It's designed to be 'upwards compatible'
1273  * for muscle memory so that the menu item locations are fixed and don't move
1274  * as new items are added to the menu later on. It also optimises efficiency -
1275  * a radial menu is best kept symmetrical, with as large an angle between
1276  * items as possible, so that the gestural mouse movements can be fast and inexact.
1277  *
1278  * It starts off with two opposite sides for the first two items
1279  * then joined by the one below for the third (this way, even with three items,
1280  * the menu seems to still be 'in order' reading left to right). Then the fourth is
1281  * added to complete the compass directions. From here, it's just a matter of
1282  * subdividing the rest of the angles for the last 4 items.
1283  *
1284  * --Matt 07/2006
1285  */
1286 const char ui_radial_dir_order[8] = {
1287     UI_RADIAL_W,  UI_RADIAL_E,  UI_RADIAL_S,  UI_RADIAL_N,
1288     UI_RADIAL_NW, UI_RADIAL_NE, UI_RADIAL_SW, UI_RADIAL_SE};
1289
1290 const char  ui_radial_dir_to_numpad[8] = {8, 9, 6, 3, 2, 1, 4, 7};
1291 const short ui_radial_dir_to_angle[8] =  {90, 45, 0, 315, 270, 225, 180, 135};
1292
1293 static void ui_but_pie_direction_string(uiBut *but, char *buf, int size)
1294 {
1295         BLI_assert(but->pie_dir < ARRAY_SIZE(ui_radial_dir_to_numpad));
1296         BLI_snprintf(buf, size, "%d", ui_radial_dir_to_numpad[but->pie_dir]);
1297 }
1298
1299 static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
1300 {
1301         uiBut *but;
1302         char buf[128];
1303
1304         BLI_assert(block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS));
1305
1306         /* only do it before bounding */
1307         if (block->rect.xmin != block->rect.xmax) {
1308                 return;
1309         }
1310         if (STREQ(block->name, "splash")) {
1311                 return;
1312         }
1313
1314         if (block->flag & UI_BLOCK_RADIAL) {
1315                 for (but = block->buttons.first; but; but = but->next) {
1316                         if (but->pie_dir != UI_RADIAL_NONE) {
1317                                 ui_but_pie_direction_string(but, buf, sizeof(buf));
1318                                 ui_but_add_shortcut(but, buf, false);
1319                         }
1320                 }
1321         }
1322         else {
1323                 for (but = block->buttons.first; but; but = but->next) {
1324                         if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) {
1325                                 /* Skip icon-only buttons (as used in the toolbar). */
1326                                 if (but->drawstr[0] == '\0') {
1327                                         continue;
1328                                 }
1329                                 else if (((block->flag & UI_BLOCK_POPOVER) == 0) && UI_but_is_tool(but)) {
1330                                         /* For non-popovers, shown in shortcut only (has special shortcut handling code). */
1331                                         continue;
1332                                 }
1333                         }
1334                         else if (but->dt != UI_EMBOSS_PULLDOWN) {
1335                                 continue;
1336                         }
1337
1338                         if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) {
1339                                 ui_but_add_shortcut(but, buf, false);
1340                         }
1341                         else if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) {
1342                                 ui_but_add_shortcut(but, buf, false);
1343                         }
1344                 }
1345         }
1346 }
1347
1348 void ui_but_override_flag(uiBut *but)
1349 {
1350         const int override_status = RNA_property_static_override_status(&but->rnapoin, but->rnaprop, but->rnaindex);
1351
1352         if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) {
1353                 but->flag |= UI_BUT_OVERRIDEN;
1354         }
1355         else {
1356                 but->flag &= ~UI_BUT_OVERRIDEN;
1357         }
1358 }
1359
1360 void UI_block_update_from_old(const bContext *C, uiBlock *block)
1361 {
1362         uiBut *but_old;
1363         uiBut *but;
1364
1365         if (!block->oldblock)
1366                 return;
1367
1368         but_old = block->oldblock->buttons.first;
1369
1370         if (BLI_listbase_is_empty(&block->oldblock->butstore) == false) {
1371                 UI_butstore_update(block);
1372         }
1373
1374         for (but = block->buttons.first; but; but = but->next) {
1375                 if (ui_but_update_from_old_block(C, block, &but, &but_old)) {
1376                         ui_but_update(but);
1377
1378                         /* redraw dynamic tooltip if we have one open */
1379                         if (but->tip_func) {
1380                                 UI_but_tooltip_refresh((bContext *)C, but);
1381                         }
1382                 }
1383         }
1384
1385         block->auto_open = block->oldblock->auto_open;
1386         block->auto_open_last = block->oldblock->auto_open_last;
1387         block->tooltipdisabled = block->oldblock->tooltipdisabled;
1388         BLI_movelisttolist(&block->color_pickers.list, &block->oldblock->color_pickers.list);
1389
1390         block->oldblock = NULL;
1391 }
1392
1393 void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_xy[2])
1394 {
1395         wmWindow *window = CTX_wm_window(C);
1396         Scene *scene = CTX_data_scene(C);
1397         ARegion *region = CTX_wm_region(C);
1398         uiBut *but;
1399
1400         BLI_assert(block->active);
1401
1402         UI_block_update_from_old(C, block);
1403
1404         /* inherit flags from 'old' buttons that was drawn here previous, based
1405          * on matching buttons, we need this to make button event handling non
1406          * blocking, while still allowing buttons to be remade each redraw as it
1407          * is expected by blender code */
1408         for (but = block->buttons.first; but; but = but->next) {
1409                 /* temp? Proper check for graying out */
1410                 if (but->optype) {
1411                         wmOperatorType *ot = but->optype;
1412
1413                         if (but->context)
1414                                 CTX_store_set((bContext *)C, but->context);
1415
1416                         if (ot == NULL || WM_operator_poll_context((bContext *)C, ot, but->opcontext) == 0) {
1417                                 but->flag |= UI_BUT_DISABLED;
1418                         }
1419
1420                         if (but->context)
1421                                 CTX_store_set((bContext *)C, NULL);
1422                 }
1423
1424                 ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f);
1425                 ui_but_override_flag(but);
1426                 if (UI_but_is_decorator(but)) {
1427                         ui_but_anim_decorate_update_from_flag(but);
1428                 }
1429         }
1430
1431
1432
1433         /* handle pending stuff */
1434         if (block->layouts.first) {
1435                 UI_block_layout_resolve(block, NULL, NULL);
1436         }
1437         ui_block_align_calc(block, CTX_wm_region(C));
1438         if ((block->flag & UI_BLOCK_LOOP) && (block->flag & UI_BLOCK_NUMSELECT)) {
1439                 ui_menu_block_set_keyaccels(block); /* could use a different flag to check */
1440         }
1441
1442         if (block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)) {
1443                 ui_menu_block_set_keymaps(C, block);
1444         }
1445
1446         /* after keymaps! */
1447         switch (block->bounds_type) {
1448                 case UI_BLOCK_BOUNDS_NONE:
1449                         break;
1450                 case UI_BLOCK_BOUNDS:
1451                         ui_block_bounds_calc(block);
1452                         break;
1453                 case UI_BLOCK_BOUNDS_TEXT:
1454                         ui_block_bounds_calc_text(block, 0.0f);
1455                         break;
1456                 case UI_BLOCK_BOUNDS_POPUP_CENTER:
1457                         ui_block_bounds_calc_centered(window, block);
1458                         break;
1459                 case UI_BLOCK_BOUNDS_PIE_CENTER:
1460                         ui_block_bounds_calc_centered_pie(block);
1461                         break;
1462
1463                         /* fallback */
1464                 case UI_BLOCK_BOUNDS_POPUP_MOUSE:
1465                 case UI_BLOCK_BOUNDS_POPUP_MENU:
1466                         ui_block_bounds_calc_popup(window, block, block->bounds_type, xy, r_xy);
1467                         break;
1468         }
1469
1470         if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) {
1471                 UI_block_bounds_set_normal(block, 0);
1472         }
1473         if (block->flag & UI_BUT_ALIGN) {
1474                 UI_block_align_end(block);
1475         }
1476
1477         ui_update_flexible_spacing(region, block);
1478
1479         block->endblock = 1;
1480 }
1481
1482 void UI_block_end(const bContext *C, uiBlock *block)
1483 {
1484         wmWindow *window = CTX_wm_window(C);
1485
1486         UI_block_end_ex(C, block, &window->eventstate->x, NULL);
1487 }
1488
1489 /* ************** BLOCK DRAWING FUNCTION ************* */
1490
1491 void ui_fontscale(short *points, float aspect)
1492 {
1493         if (aspect < 0.9f || aspect > 1.1f) {
1494                 float pointsf = *points;
1495
1496                 /* for some reason scaling fonts goes too fast compared to widget size */
1497                 /* XXX not true anymore? (ton) */
1498                 //aspect = sqrt(aspect);
1499                 pointsf /= aspect;
1500
1501                 if (aspect > 1.0f)
1502                         *points = ceilf(pointsf);
1503                 else
1504                         *points = floorf(pointsf);
1505         }
1506 }
1507
1508 /* project button or block (but==NULL) to pixels in regionspace */
1509 static void ui_but_to_pixelrect(rcti *rect, const ARegion *ar, uiBlock *block, uiBut *but)
1510 {
1511         rctf rectf;
1512
1513         ui_block_to_window_rctf(ar, block, &rectf, (but) ? &but->rect : &block->rect);
1514         BLI_rcti_rctf_copy_round(rect, &rectf);
1515         BLI_rcti_translate(rect, -ar->winrct.xmin, -ar->winrct.ymin);
1516 }
1517
1518 /* uses local copy of style, to scale things down, and allow widgets to change stuff */
1519 void UI_block_draw(const bContext *C, uiBlock *block)
1520 {
1521         uiStyle style = *UI_style_get_dpi();  /* XXX pass on as arg */
1522         ARegion *ar;
1523         uiBut *but;
1524         rcti rect;
1525
1526         /* get menu region or area region */
1527         ar = CTX_wm_menu(C);
1528         if (!ar)
1529                 ar = CTX_wm_region(C);
1530
1531         if (!block->endblock)
1532                 UI_block_end(C, block);
1533
1534         /* we set this only once */
1535         GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
1536
1537         /* scale fonts */
1538         ui_fontscale(&style.paneltitle.points, block->aspect);
1539         ui_fontscale(&style.grouplabel.points, block->aspect);
1540         ui_fontscale(&style.widgetlabel.points, block->aspect);
1541         ui_fontscale(&style.widget.points, block->aspect);
1542
1543         /* scale block min/max to rect */
1544         ui_but_to_pixelrect(&rect, ar, block, NULL);
1545
1546         /* pixel space for AA widgets */
1547         GPU_matrix_push_projection();
1548         GPU_matrix_push();
1549         GPU_matrix_identity_set();
1550
1551         wmOrtho2_region_pixelspace(ar);
1552
1553         /* back */
1554         if (block->flag & UI_BLOCK_RADIAL)
1555                 ui_draw_pie_center(block);
1556         else if (block->flag & UI_BLOCK_POPOVER)
1557                 ui_draw_popover_back(ar, &style, block, &rect);
1558         else if (block->flag & UI_BLOCK_LOOP)
1559                 ui_draw_menu_back(&style, block, &rect);
1560         else if (block->panel) {
1561                 bool show_background = ar->alignment != RGN_ALIGN_FLOAT;
1562                 ui_draw_aligned_panel(
1563                         &style, block, &rect,
1564                         UI_panel_category_is_visible(ar), show_background);
1565         }
1566
1567         BLF_batch_draw_begin();
1568         UI_icon_draw_cache_begin();
1569         UI_widgetbase_draw_cache_begin();
1570
1571         /* widgets */
1572         for (but = block->buttons.first; but; but = but->next) {
1573                 if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) {
1574                         ui_but_to_pixelrect(&rect, ar, block, but);
1575
1576                         /* XXX: figure out why invalid coordinates happen when closing render window */
1577                         /* and material preview is redrawn in main window (temp fix for bug #23848) */
1578                         if (rect.xmin < rect.xmax && rect.ymin < rect.ymax)
1579                                 ui_draw_but(C, ar, &style, but, &rect);
1580                 }
1581         }
1582
1583         UI_widgetbase_draw_cache_end();
1584         UI_icon_draw_cache_end();
1585         BLF_batch_draw_end();
1586
1587         /* restore matrix */
1588         GPU_matrix_pop_projection();
1589         GPU_matrix_pop();
1590 }
1591
1592 static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlock *block)
1593 {
1594         uiBut *but_prev = NULL;
1595         /* possibly we should keep the region this block is contained in? */
1596         for (uiBut *but = block->buttons.first; but; but = but->next) {
1597                 if (but->rnapoin.type && but->rnaprop) {
1598                         /* quick check to avoid adding buttons representing a vector, multiple times. */
1599                         if ((but_prev &&
1600                              (but_prev->rnaprop == but->rnaprop) &&
1601                              (but_prev->rnapoin.type == but->rnapoin.type) &&
1602                              (but_prev->rnapoin.data == but->rnapoin.data) &&
1603                              (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false)
1604                         {
1605                                 /* TODO: could make this into utility function. */
1606                                 WM_msg_subscribe_rna(
1607                                         mbus, &but->rnapoin, but->rnaprop,
1608                                         &(const wmMsgSubscribeValue){
1609                                             .owner = ar,
1610                                             .user_data = ar,
1611                                             .notify = ED_region_do_msg_notify_tag_redraw,
1612                                         }, __func__);
1613                                 but_prev = but;
1614                         }
1615                 }
1616         }
1617 }
1618
1619 void UI_region_message_subscribe(ARegion *ar, struct wmMsgBus *mbus)
1620 {
1621         for (uiBlock *block = ar->uiblocks.first; block; block = block->next) {
1622                 ui_block_message_subscribe(ar, mbus, block);
1623         }
1624 }
1625
1626 /* ************* EVENTS ************* */
1627
1628 /**
1629  * Check if the button is pushed, this is only meaningful for some button types.
1630  *
1631  * \return (0 == UNSELECT), (1 == SELECT), (-1 == DO-NOTHING)
1632  */
1633 int ui_but_is_pushed_ex(uiBut *but, double *value)
1634 {
1635         int is_push = 0;
1636
1637         if (but->bit) {
1638                 const bool state = ELEM(but->type, UI_BTYPE_TOGGLE_N, UI_BTYPE_ICON_TOGGLE_N, UI_BTYPE_CHECKBOX_N) ? false : true;
1639                 int lvalue;
1640                 UI_GET_BUT_VALUE_INIT(but, *value);
1641                 lvalue = (int)*value;
1642                 if (UI_BITBUT_TEST(lvalue, (but->bitnr))) {
1643                         is_push = state;
1644                 }
1645                 else {
1646                         is_push = !state;
1647                 }
1648         }
1649         else {
1650                 switch (but->type) {
1651                         case UI_BTYPE_BUT:
1652                         case UI_BTYPE_HOTKEY_EVENT:
1653                         case UI_BTYPE_KEY_EVENT:
1654                         case UI_BTYPE_COLOR:
1655                                 is_push = -1;
1656                                 break;
1657                         case UI_BTYPE_BUT_TOGGLE:
1658                         case UI_BTYPE_TOGGLE:
1659                         case UI_BTYPE_ICON_TOGGLE:
1660                         case UI_BTYPE_CHECKBOX:
1661                                 UI_GET_BUT_VALUE_INIT(but, *value);
1662                                 if (*value != (double)but->hardmin) is_push = true;
1663                                 break;
1664                         case UI_BTYPE_ICON_TOGGLE_N:
1665                         case UI_BTYPE_TOGGLE_N:
1666                         case UI_BTYPE_CHECKBOX_N:
1667                                 UI_GET_BUT_VALUE_INIT(but, *value);
1668                                 if (*value == 0.0) is_push = true;
1669                                 break;
1670                         case UI_BTYPE_ROW:
1671                         case UI_BTYPE_LISTROW:
1672                         case UI_BTYPE_TAB:
1673                                 if ((but->type == UI_BTYPE_TAB) && but->rnaprop && but->custom_data) {
1674                                         /* uiBut.custom_data points to data this tab represents (e.g. workspace).
1675                                          * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
1676                                         if (RNA_property_type(but->rnaprop) == PROP_POINTER) {
1677                                                 PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
1678                                                 if (active_ptr.data == but->custom_data) {
1679                                                         is_push = true;
1680                                                 }
1681                                         }
1682                                         break;
1683                                 }
1684                                 else if (but->optype) {
1685                                         break;
1686                                 }
1687
1688                                 UI_GET_BUT_VALUE_INIT(but, *value);
1689                                 /* support for rna enum buts */
1690                                 if (but->rnaprop && (RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG)) {
1691                                         if ((int)*value & (int)but->hardmax) is_push = true;
1692                                 }
1693                                 else {
1694                                         if (*value == (double)but->hardmax) is_push = true;
1695                                 }
1696                                 break;
1697                         default:
1698                                 is_push = -1;
1699                                 break;
1700                 }
1701         }
1702
1703         return is_push;
1704 }
1705 int ui_but_is_pushed(uiBut *but)
1706 {
1707         double value = UI_BUT_VALUE_UNSET;
1708         return ui_but_is_pushed_ex(but, &value);
1709 }
1710
1711 static void ui_but_update_select_flag(uiBut *but, double *value)
1712 {
1713         switch (ui_but_is_pushed_ex(but, value)) {
1714                 case true:
1715                         but->flag |= UI_SELECT;
1716                         break;
1717                 case false:
1718                         but->flag &= ~UI_SELECT;
1719                         break;
1720         }
1721 }
1722
1723 /* ************************************************ */
1724
1725 void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr)
1726 {
1727         if (val) {
1728                 block->lock = val;
1729                 block->lockstr = lockstr;
1730         }
1731 }
1732
1733 void UI_block_lock_clear(uiBlock *block)
1734 {
1735         block->lock = false;
1736         block->lockstr = NULL;
1737 }
1738
1739 /* *********************** data get/set ***********************
1740  * this either works with the pointed to data, or can work with
1741  * an edit override pointer while dragging for example */
1742
1743 /* Get PointerRNA which will point to a data inside of an evaluated
1744  * ID datablock.
1745  */
1746 static PointerRNA ui_but_evaluated_rnapoin_get(uiBut *but)
1747 {
1748         BLI_assert(but->rnaprop != NULL);
1749         /* TODO(sergey): evil_C sounds.. EVIL! Any clear way to avoid this? */
1750         PointerRNA rnapoin_eval = but->rnapoin;
1751         /* If there is no animation or drivers, it doesn't matter if we read value
1752          * from evaluated datablock or from original one.
1753          *
1754          * Reading from original one is much faster, since we don't need to do any
1755          * PointerRNA remapping or hash lookup.
1756          */
1757         if (BKE_animdata_from_id(but->rnapoin.id.data) == NULL) {
1758                 return rnapoin_eval;
1759         }
1760         /* Same goes for the properties which can not be animated. */
1761         if (!RNA_property_animateable(&but->rnapoin, but->rnaprop)) {
1762                 return rnapoin_eval;
1763         }
1764         Depsgraph *depsgraph = CTX_data_depsgraph(but->block->evil_C);
1765         /* ID pointer we can always remap, they are inside of depsgraph. */
1766         rnapoin_eval.id.data =
1767                 DEG_get_evaluated_id(depsgraph, rnapoin_eval.id.data);
1768         /* Some of ID datablocks do not have their evaluated copy inside
1769          * of dependency graph. If it's such datablock, no need to worry about
1770          * data pointer.
1771          */
1772         if (rnapoin_eval.id.data == but->rnapoin.id.data) {
1773                 return rnapoin_eval;
1774         }
1775         /* For the data pointer it's getting a bit more involved, since it can
1776          * whether be and ID, or can be a property deep inside of ID.
1777          *
1778          * We start from checking if it's an ID, since that is the less involved
1779          * code path, and probably is executed in most of the cases.
1780          */
1781         if (but->rnapoin.data == but->rnapoin.id.data) {
1782                 rnapoin_eval.data = DEG_get_evaluated_id(depsgraph, rnapoin_eval.data);
1783                 return rnapoin_eval;
1784         }
1785         /* We aren't as lucky as we thought we are :(
1786          *
1787          * Since we don't know what the property is, we get it's RNA path
1788          * relative to the original ID, and then we decent down from evaluated
1789          * ID to the same property.
1790          *
1791          * This seems to be most straightforward way to get sub-data pointers
1792          * which can be buried deep inside of ID block.
1793          */
1794         const char *rna_path =
1795                RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
1796         if (rna_path != NULL) {
1797                 PointerRNA id_ptr;
1798                 RNA_id_pointer_create(rnapoin_eval.id.data, &id_ptr);
1799                 if (!RNA_path_resolve_full(&id_ptr,
1800                                            rna_path,
1801                                            &rnapoin_eval,
1802                                            NULL, NULL))
1803                 {
1804                         /* TODO(sergey): Anything to do here to recover? */
1805                 }
1806                 MEM_freeN((void *)rna_path);
1807         }
1808         return rnapoin_eval;
1809 }
1810
1811 /* for buttons pointing to color for example */
1812 void ui_but_v3_get(uiBut *but, float vec[3])
1813 {
1814         PropertyRNA *prop;
1815         int a;
1816
1817         if (but->editvec) {
1818                 copy_v3_v3(vec, but->editvec);
1819         }
1820
1821         if (but->rnaprop) {
1822                 prop = but->rnaprop;
1823
1824                 zero_v3(vec);
1825
1826                 PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
1827
1828                 if (RNA_property_type(prop) == PROP_FLOAT) {
1829                         int tot = RNA_property_array_length(&rnapoin_eval, prop);
1830                         BLI_assert(tot > 0);
1831                         if (tot == 3) {
1832                                 RNA_property_float_get_array(&rnapoin_eval, prop, vec);
1833                         }
1834                         else {
1835                                 tot = min_ii(tot, 3);
1836                                 for (a = 0; a < tot; a++) {
1837                                         vec[a] = RNA_property_float_get_index(&rnapoin_eval, prop, a);
1838                                 }
1839                         }
1840                 }
1841         }
1842         else if (but->pointype == UI_BUT_POIN_CHAR) {
1843                 const char *cp = (char *)but->poin;
1844
1845                 vec[0] = ((float)cp[0]) / 255.0f;
1846                 vec[1] = ((float)cp[1]) / 255.0f;
1847                 vec[2] = ((float)cp[2]) / 255.0f;
1848         }
1849         else if (but->pointype == UI_BUT_POIN_FLOAT) {
1850                 const float *fp = (float *)but->poin;
1851                 copy_v3_v3(vec, fp);
1852         }
1853         else {
1854                 if (but->editvec == NULL) {
1855                         fprintf(stderr, "%s: can't get color, should never happen\n", __func__);
1856                         zero_v3(vec);
1857                 }
1858         }
1859
1860         if (but->type == UI_BTYPE_UNITVEC) {
1861                 normalize_v3(vec);
1862         }
1863 }
1864
1865 /* for buttons pointing to color for example */
1866 void ui_but_v3_set(uiBut *but, const float vec[3])
1867 {
1868         PropertyRNA *prop;
1869
1870         if (but->editvec) {
1871                 copy_v3_v3(but->editvec, vec);
1872         }
1873
1874         if (but->rnaprop) {
1875                 prop = but->rnaprop;
1876
1877                 if (RNA_property_type(prop) == PROP_FLOAT) {
1878                         int tot;
1879                         int a;
1880
1881                         tot = RNA_property_array_length(&but->rnapoin, prop);
1882                         BLI_assert(tot > 0);
1883                         if (tot == 3) {
1884                                 RNA_property_float_set_array(&but->rnapoin, prop, vec);
1885                         }
1886                         else {
1887                                 tot = min_ii(tot, 3);
1888                                 for (a = 0; a < tot; a++) {
1889                                         RNA_property_float_set_index(&but->rnapoin, prop, a, vec[a]);
1890                                 }
1891                         }
1892                 }
1893         }
1894         else if (but->pointype == UI_BUT_POIN_CHAR) {
1895                 char *cp = (char *)but->poin;
1896                 cp[0] = (char)(0.5f + vec[0] * 255.0f);
1897                 cp[1] = (char)(0.5f + vec[1] * 255.0f);
1898                 cp[2] = (char)(0.5f + vec[2] * 255.0f);
1899         }
1900         else if (but->pointype == UI_BUT_POIN_FLOAT) {
1901                 float *fp = (float *)but->poin;
1902                 copy_v3_v3(fp, vec);
1903         }
1904 }
1905
1906 bool ui_but_is_float(const uiBut *but)
1907 {
1908         if (but->pointype == UI_BUT_POIN_FLOAT && but->poin)
1909                 return true;
1910
1911         if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_FLOAT)
1912                 return true;
1913
1914         return false;
1915 }
1916
1917 bool ui_but_is_bool(const uiBut *but)
1918 {
1919         if (ELEM(but->type, UI_BTYPE_TOGGLE, UI_BTYPE_TOGGLE_N, UI_BTYPE_ICON_TOGGLE, UI_BTYPE_ICON_TOGGLE_N, UI_BTYPE_TAB))
1920                 return true;
1921
1922         if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_BOOLEAN)
1923                 return true;
1924
1925         if ((but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) && (but->type == UI_BTYPE_ROW))
1926                 return true;
1927
1928         return false;
1929 }
1930
1931
1932 bool ui_but_is_unit(const uiBut *but)
1933 {
1934         UnitSettings *unit = but->block->unit;
1935         const int unit_type = UI_but_unit_type_get(but);
1936
1937         if (unit_type == PROP_UNIT_NONE)
1938                 return false;
1939
1940 #if 1 /* removed so angle buttons get correct snapping */
1941         if (ui_but_is_unit_radians_ex(unit, unit_type))
1942                 return false;
1943 #endif
1944
1945         /* for now disable time unit conversion */
1946         if (unit_type == PROP_UNIT_TIME)
1947                 return false;
1948
1949         if (unit->system == USER_UNIT_NONE) {
1950                 if (unit_type != PROP_UNIT_ROTATION) {
1951                         return false;
1952                 }
1953         }
1954
1955         return true;
1956 }
1957
1958 /**
1959  * Check if this button is similar enough to be grouped with another.
1960  */
1961 bool ui_but_is_compatible(const uiBut *but_a, const uiBut *but_b)
1962 {
1963         if (but_a->type != but_b->type)
1964                 return false;
1965         if (but_a->pointype != but_b->pointype)
1966                 return false;
1967
1968         if (but_a->rnaprop) {
1969                 /* skip 'rnapoin.data', 'rnapoin.id.data'
1970                  * allow different data to have the same props edited at once */
1971                 if (but_a->rnapoin.type != but_b->rnapoin.type)
1972                         return false;
1973                 if (RNA_property_type(but_a->rnaprop) != RNA_property_type(but_b->rnaprop))
1974                         return false;
1975                 if (RNA_property_subtype(but_a->rnaprop) != RNA_property_subtype(but_b->rnaprop))
1976                         return false;
1977         }
1978
1979         return true;
1980 }
1981
1982 bool ui_but_is_rna_valid(uiBut *but)
1983 {
1984         if (but->rnaprop == NULL || RNA_struct_contains_property(&but->rnapoin, but->rnaprop)) {
1985                 return true;
1986         }
1987         else {
1988                 printf("property removed %s: %p\n", but->drawstr, but->rnaprop);
1989                 return false;
1990         }
1991 }
1992
1993 /**
1994  * Checks if the button supports ctrl+mousewheel cycling
1995  */
1996 bool ui_but_supports_cycling(const uiBut *but)
1997 {
1998         return ((ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_LISTBOX)) ||
1999                 (but->type == UI_BTYPE_MENU && ui_but_menu_step_poll(but)) ||
2000                 (but->type == UI_BTYPE_COLOR && but->a1 != -1) ||
2001                 (but->menu_step_func != NULL));
2002 }
2003
2004 double ui_but_value_get(uiBut *but)
2005 {
2006         PropertyRNA *prop;
2007         double value = 0.0;
2008
2009         if (but->editval) { return *(but->editval); }
2010         if (but->poin == NULL && but->rnapoin.data == NULL) return 0.0;
2011
2012         if (but->rnaprop) {
2013                 prop = but->rnaprop;
2014
2015                 BLI_assert(but->rnaindex != -1);
2016
2017                 PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
2018
2019                 switch (RNA_property_type(prop)) {
2020                         case PROP_BOOLEAN:
2021                                 if (RNA_property_array_check(prop))
2022                                         value = RNA_property_boolean_get_index(&rnapoin_eval, prop, but->rnaindex);
2023                                 else
2024                                         value = RNA_property_boolean_get(&rnapoin_eval, prop);
2025                                 break;
2026                         case PROP_INT:
2027                                 if (RNA_property_array_check(prop))
2028                                         value = RNA_property_int_get_index(&rnapoin_eval, prop, but->rnaindex);
2029                                 else
2030                                         value = RNA_property_int_get(&rnapoin_eval, prop);
2031                                 break;
2032                         case PROP_FLOAT:
2033                                 if (RNA_property_array_check(prop))
2034                                         value = RNA_property_float_get_index(&rnapoin_eval, prop, but->rnaindex);
2035                                 else
2036                                         value = RNA_property_float_get(&rnapoin_eval, prop);
2037                                 break;
2038                         case PROP_ENUM:
2039                                 value = RNA_property_enum_get(&rnapoin_eval, prop);
2040                                 break;
2041                         default:
2042                                 value = 0.0;
2043                                 break;
2044                 }
2045         }
2046         else if (but->pointype == UI_BUT_POIN_CHAR) {
2047                 value = *(char *)but->poin;
2048         }
2049         else if (but->pointype == UI_BUT_POIN_SHORT) {
2050                 value = *(short *)but->poin;
2051         }
2052         else if (but->pointype == UI_BUT_POIN_INT) {
2053                 value = *(int *)but->poin;
2054         }
2055         else if (but->pointype == UI_BUT_POIN_FLOAT) {
2056                 value = *(float *)but->poin;
2057         }
2058
2059         return value;
2060 }
2061
2062 void ui_but_value_set(uiBut *but, double value)
2063 {
2064         PropertyRNA *prop;
2065
2066         /* value is a hsv value: convert to rgb */
2067         if (but->rnaprop) {
2068                 prop = but->rnaprop;
2069
2070                 if (RNA_property_editable(&but->rnapoin, prop)) {
2071                         switch (RNA_property_type(prop)) {
2072                                 case PROP_BOOLEAN:
2073                                         if (RNA_property_array_check(prop))
2074                                                 RNA_property_boolean_set_index(&but->rnapoin, prop, but->rnaindex, value);
2075                                         else
2076                                                 RNA_property_boolean_set(&but->rnapoin, prop, value);
2077                                         break;
2078                                 case PROP_INT:
2079                                         if (RNA_property_array_check(prop))
2080                                                 RNA_property_int_set_index(&but->rnapoin, prop, but->rnaindex, (int)value);
2081                                         else
2082                                                 RNA_property_int_set(&but->rnapoin, prop, (int)value);
2083                                         break;
2084                                 case PROP_FLOAT:
2085                                         if (RNA_property_array_check(prop))
2086                                                 RNA_property_float_set_index(&but->rnapoin, prop, but->rnaindex, value);
2087                                         else
2088                                                 RNA_property_float_set(&but->rnapoin, prop, value);
2089                                         break;
2090                                 case PROP_ENUM:
2091                                         if (RNA_property_flag(prop) & PROP_ENUM_FLAG) {
2092                                                 int ivalue = (int)value;
2093                                                 ivalue ^= RNA_property_enum_get(&but->rnapoin, prop); /* toggle for enum/flag buttons */
2094                                                 RNA_property_enum_set(&but->rnapoin, prop, ivalue);
2095                                         }
2096                                         else {
2097                                                 RNA_property_enum_set(&but->rnapoin, prop, value);
2098                                         }
2099                                         break;
2100                                 default:
2101                                         break;
2102                         }
2103                 }
2104
2105                 /* we can't be sure what RNA set functions actually do,
2106                  * so leave this unset */
2107                 value = UI_BUT_VALUE_UNSET;
2108         }
2109         else if (but->pointype == 0) {
2110                 /* pass */
2111         }
2112         else {
2113                 /* first do rounding */
2114                 if (but->pointype == UI_BUT_POIN_CHAR) {
2115                         value = round_db_to_uchar_clamp(value);
2116                 }
2117                 else if (but->pointype == UI_BUT_POIN_SHORT) {
2118                         value = round_db_to_short_clamp(value);
2119                 }
2120                 else if (but->pointype == UI_BUT_POIN_INT) {
2121                         value = round_db_to_int_clamp(value);
2122                 }
2123                 else if (but->pointype == UI_BUT_POIN_FLOAT) {
2124                         float fval = (float)value;
2125                         if (fval >= -0.00001f && fval <= 0.00001f) fval = 0.0f;  /* prevent negative zero */
2126                         value = fval;
2127                 }
2128
2129                 /* then set value with possible edit override */
2130                 if (but->editval)
2131                         value = *but->editval = value;
2132                 else if (but->pointype == UI_BUT_POIN_CHAR)
2133                         value = *((char *)but->poin) = (char)value;
2134                 else if (but->pointype == UI_BUT_POIN_SHORT)
2135                         value = *((short *)but->poin) = (short)value;
2136                 else if (but->pointype == UI_BUT_POIN_INT)
2137                         value = *((int *)but->poin) = (int)value;
2138                 else if (but->pointype == UI_BUT_POIN_FLOAT)
2139                         value = *((float *)but->poin) = (float)value;
2140         }
2141
2142         ui_but_update_select_flag(but, &value);
2143 }
2144
2145 int ui_but_string_get_max_length(uiBut *but)
2146 {
2147         if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU))
2148                 return but->hardmax;
2149         else
2150                 return UI_MAX_DRAW_STR;
2151 }
2152
2153 uiBut *ui_but_drag_multi_edit_get(uiBut *but)
2154 {
2155         uiBut *but_iter;
2156
2157         BLI_assert(but->flag & UI_BUT_DRAG_MULTI);
2158
2159         for (but_iter = but->block->buttons.first; but_iter; but_iter = but_iter->next) {
2160                 if (but_iter->editstr) {
2161                         break;
2162                 }
2163         }
2164
2165         return but_iter;
2166 }
2167
2168 /** \name Check to show extra icons
2169  *
2170  * Extra icons are shown on the right hand side of buttons.
2171  * This could (should!) definitely become more generic, but for now this is good enough.
2172  * \{ */
2173
2174 static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but)
2175 {
2176         BLI_assert(but->type == UI_BTYPE_TEXT);
2177         return ((but->flag & UI_BUT_VALUE_CLEAR) && but->drawstr[0]);
2178 }
2179
2180 static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but)
2181 {
2182         BLI_assert(ELEM(but->type, UI_BTYPE_SEARCH_MENU));
2183         return ((but->editstr == NULL) &&
2184                 (but->drawstr[0] != '\0') &&
2185                 (but->flag & UI_BUT_VALUE_CLEAR));
2186 }
2187
2188 static bool ui_but_icon_extra_is_visible_search_eyedropper(uiBut *but)
2189 {
2190         StructRNA *type;
2191         short idcode;
2192
2193         BLI_assert(but->type == UI_BTYPE_SEARCH_MENU && (but->flag & UI_BUT_VALUE_CLEAR));
2194
2195         if (but->rnaprop == NULL) {
2196                 return false;
2197         }
2198
2199         type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
2200         idcode = RNA_type_to_ID_code(type);
2201
2202         return ((but->editstr == NULL) &&
2203                 (idcode == ID_OB || OB_DATA_SUPPORT_ID(idcode)));
2204 }
2205
2206 uiButExtraIconType ui_but_icon_extra_get(uiBut *but)
2207 {
2208         switch (but->type) {
2209                 case UI_BTYPE_TEXT:
2210                         if (ui_but_icon_extra_is_visible_text_clear(but)) {
2211                                 return UI_BUT_ICONEXTRA_CLEAR;
2212                         }
2213                         break;
2214                 case UI_BTYPE_SEARCH_MENU:
2215                         if ((but->flag & UI_BUT_VALUE_CLEAR) == 0) {
2216                                 /* pass */
2217                         }
2218                         else if (ui_but_icon_extra_is_visible_search_unlink(but)) {
2219                                 return UI_BUT_ICONEXTRA_CLEAR;
2220                         }
2221                         else if (ui_but_icon_extra_is_visible_search_eyedropper(but)) {
2222                                 return UI_BUT_ICONEXTRA_EYEDROPPER;
2223                         }
2224                         break;
2225                 default:
2226                         break;
2227         }
2228
2229         return UI_BUT_ICONEXTRA_NONE;
2230 }
2231
2232 /** \} */
2233
2234
2235 static double ui_get_but_scale_unit(uiBut *but, double value)
2236 {
2237         UnitSettings *unit = but->block->unit;
2238         int unit_type = UI_but_unit_type_get(but);
2239
2240         /* Time unit is a bit special, not handled by BKE_scene_unit_scale() for now. */
2241         if (unit_type == PROP_UNIT_TIME) { /* WARNING - using evil_C :| */
2242                 Scene *scene = CTX_data_scene(but->block->evil_C);
2243                 return FRA2TIME(value);
2244         }
2245         else {
2246                 return BKE_scene_unit_scale(unit, RNA_SUBTYPE_UNIT_VALUE(unit_type), value);
2247         }
2248 }
2249
2250 /* str will be overwritten */
2251 void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen)
2252 {
2253         if (ui_but_is_unit(but)) {
2254                 UnitSettings *unit = but->block->unit;
2255                 int unit_type = UI_but_unit_type_get(but);
2256                 char *orig_str;
2257
2258                 orig_str = BLI_strdup(str);
2259
2260                 bUnit_ToUnitAltName(str, maxlen, orig_str, unit->system, RNA_SUBTYPE_UNIT_VALUE(unit_type));
2261
2262                 MEM_freeN(orig_str);
2263         }
2264 }
2265
2266 /**
2267  * \param float_precision: Override the button precision.
2268  */
2269 static void ui_get_but_string_unit(uiBut *but, char *str, int len_max, double value, bool pad, int float_precision)
2270 {
2271         UnitSettings *unit = but->block->unit;
2272         int unit_type = UI_but_unit_type_get(but);
2273         int precision;
2274
2275         if (unit->scale_length < 0.0001f) unit->scale_length = 1.0f;  // XXX do_versions
2276
2277         /* Use precision override? */
2278         if (float_precision == -1) {
2279                 /* Sanity checks */
2280                 precision = (int)but->a2;
2281                 if      (precision > UI_PRECISION_FLOAT_MAX) precision = UI_PRECISION_FLOAT_MAX;
2282                 else if (precision == -1)                    precision = 2;
2283         }
2284         else {
2285                 precision = float_precision;
2286         }
2287
2288         bUnit_AsString2(
2289                 str, len_max, ui_get_but_scale_unit(but, value), precision,
2290                 RNA_SUBTYPE_UNIT_VALUE(unit_type), unit, pad);
2291 }
2292
2293 static float ui_get_but_step_unit(uiBut *but, float step_default)
2294 {
2295         int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
2296         const double step_orig = step_default * UI_PRECISION_FLOAT_SCALE;
2297         /* Scaling up 'step_origg ' here is a bit arbitrary, its just giving better scales from user POV */
2298         const double scale_step = ui_get_but_scale_unit(but, step_orig * 10);
2299         const double step = bUnit_ClosestScalar(scale_step, but->block->unit->system, unit_type);
2300
2301         /* -1 is an error value */
2302         if (step != -1.0) {
2303                 const double scale_unit = ui_get_but_scale_unit(but, 1.0);
2304                 const double step_unit = bUnit_ClosestScalar(scale_unit, but->block->unit->system, unit_type);
2305                 double step_final;
2306
2307                 BLI_assert(step > 0.0);
2308
2309                 step_final = (step / scale_unit) / (double)UI_PRECISION_FLOAT_SCALE;
2310
2311                 if (step == step_unit) {
2312                         /* Logic here is to scale by the original 'step_orig'
2313                          * only when the unit step matches the scaled step.
2314                          *
2315                          * This is needed for units that don't have a wide range of scales (degrees for eg.).
2316                          * Without this we can't select between a single degree, or a 10th of a degree.
2317                          */
2318                         step_final *= step_orig;
2319                 }
2320
2321                 return (float)step_final;
2322         }
2323         else {
2324                 return step_default;
2325         }
2326 }
2327
2328 /**
2329  * \param float_precision: For number buttons the precision to use or -1 to fallback to the button default.
2330  * \param use_exp_float: Use exponent representation of floats when out of reasonable range (outside of 1e3/1e-3).
2331  */
2332 void ui_but_string_get_ex(uiBut *but, char *str, const size_t maxlen, const int float_precision, const bool use_exp_float, bool *r_use_exp_float)
2333 {
2334         if (r_use_exp_float) {
2335                 *r_use_exp_float = false;
2336         }
2337
2338         if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB)) {
2339                 PropertyType type;
2340                 const char *buf = NULL;
2341                 int buf_len;
2342
2343                 type = RNA_property_type(but->rnaprop);
2344
2345                 if ((but->type == UI_BTYPE_TAB) && (but->custom_data)) {
2346                         StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
2347                         PointerRNA ptr;
2348
2349                         /* uiBut.custom_data points to data this tab represents (e.g. workspace).
2350                          * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
2351                         RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
2352                         buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len);
2353                 }
2354                 else if (type == PROP_STRING) {
2355                         /* RNA string */
2356                         buf = RNA_property_string_get_alloc(&but->rnapoin, but->rnaprop, str, maxlen, &buf_len);
2357                 }
2358                 else if (type == PROP_ENUM) {
2359                         /* RNA enum */
2360                         int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
2361                         if (RNA_property_enum_name(but->block->evil_C, &but->rnapoin, but->rnaprop, value, &buf)) {
2362                                 BLI_strncpy(str, buf, maxlen);
2363                                 buf = str;
2364                         }
2365                 }
2366                 else if (type == PROP_POINTER) {
2367                         /* RNA pointer */
2368                         PointerRNA ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
2369                         buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len);
2370                 }
2371                 else {
2372                         BLI_assert(0);
2373                 }
2374
2375                 if (!buf) {
2376                         str[0] = '\0';
2377                 }
2378                 else if (buf && buf != str) {
2379                         BLI_assert(maxlen <= buf_len + 1);
2380                         /* string was too long, we have to truncate */
2381                         if (ui_but_is_utf8(but)) {
2382                                 BLI_strncpy_utf8(str, buf, maxlen);
2383                         }
2384                         else {
2385                                 BLI_strncpy(str, buf, maxlen);
2386                         }
2387                         MEM_freeN((void *)buf);
2388                 }
2389         }
2390         else if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
2391                 /* string */
2392                 BLI_strncpy(str, but->poin, maxlen);
2393                 return;
2394         }
2395         else if (ui_but_anim_expression_get(but, str, maxlen)) {
2396                 /* driver expression */
2397         }
2398         else {
2399                 /* number editing */
2400                 double value;
2401
2402                 value = ui_but_value_get(but);
2403
2404                 if (ui_but_is_float(but)) {
2405                         if (ui_but_is_unit(but)) {
2406                                 ui_get_but_string_unit(but, str, maxlen, value, false, float_precision);
2407                         }
2408                         else {
2409                                 int prec = (float_precision == -1) ? ui_but_calc_float_precision(but, value) : float_precision;
2410                                 if (use_exp_float) {
2411                                         const int int_digits_num = integer_digits_f(value);
2412                                         if (int_digits_num < -6 || int_digits_num > 12) {
2413                                                 BLI_snprintf(str, maxlen, "%.*g", prec, value);
2414                                                 if (r_use_exp_float) {
2415                                                         *r_use_exp_float = true;
2416                                                 }
2417                                         }
2418                                         else {
2419                                                 prec -= int_digits_num;
2420                                                 CLAMP(prec, 0, UI_PRECISION_FLOAT_MAX);
2421                                                 BLI_snprintf(str, maxlen, "%.*f", prec, value);
2422                                         }
2423                                 }
2424                                 else {
2425 #if 0                           /* TODO, but will likely break some stuff, so better after 2.79 release. */
2426                                         prec -= int_digits_num;
2427                                         CLAMP(prec, 0, UI_PRECISION_FLOAT_MAX);
2428 #endif
2429                                         BLI_snprintf(str, maxlen, "%.*f", prec, value);
2430                                 }
2431                         }
2432                 }
2433                 else {
2434                         BLI_snprintf(str, maxlen, "%d", (int)value);
2435                 }
2436         }
2437 }
2438 void ui_but_string_get(uiBut *but, char *str, const size_t maxlen)
2439 {
2440         ui_but_string_get_ex(but, str, maxlen, -1, false, NULL);
2441 }
2442
2443 /**
2444  * A version of #ui_but_string_get_ex for dynamic buffer sizes
2445  * (where #ui_but_string_get_max_length returns 0).
2446  *
2447  * \param r_str_size: size of the returned string (including terminator).
2448  */
2449 char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
2450 {
2451         char *str = NULL;
2452         *r_str_size = 1;
2453
2454         if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
2455                 PropertyType type;
2456
2457                 type = RNA_property_type(but->rnaprop);
2458
2459                 if (type == PROP_STRING) {
2460                         /* RNA string */
2461                         str = RNA_property_string_get_alloc(&but->rnapoin, but->rnaprop, NULL, 0, r_str_size);
2462                         (*r_str_size) += 1;
2463                 }
2464                 else if (type == PROP_ENUM) {
2465                         /* RNA enum */
2466                         int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
2467                         const char *value_id;
2468                         if (!RNA_property_enum_name(but->block->evil_C, &but->rnapoin, but->rnaprop, value, &value_id)) {
2469                                 value_id = "";
2470                         }
2471
2472                         *r_str_size = strlen(value_id) + 1;
2473                         str = BLI_strdupn(value_id, *r_str_size);
2474                 }
2475                 else if (type == PROP_POINTER) {
2476                         /* RNA pointer */
2477                         PointerRNA ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
2478                         str = RNA_struct_name_get_alloc(&ptr, NULL, 0, r_str_size);
2479                         (*r_str_size) += 1;
2480                 }
2481                 else {
2482                         BLI_assert(0);
2483                 }
2484         }
2485         else {
2486                 BLI_assert(0);
2487         }
2488
2489         if (UNLIKELY(str == NULL)) {
2490                 /* should never happen, paranoid check */
2491                 *r_str_size = 1;
2492                 str = BLI_strdup("");
2493                 BLI_assert(0);
2494
2495         }
2496
2497         return str;
2498 }
2499
2500 static bool ui_set_but_string_eval_num_unit(bContext *C, uiBut *but, const char *str, double *r_value)
2501 {
2502         const UnitSettings *unit = but->block->unit;
2503         int type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
2504         return user_string_to_number(C, str, unit, type, r_value);
2505 }
2506
2507 bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double *r_value)
2508 {
2509         bool ok = false;
2510
2511 #ifdef WITH_PYTHON
2512
2513         if (str[0] != '\0') {
2514                 bool is_unit_but = (ui_but_is_float(but) && ui_but_is_unit(but));
2515                 /* only enable verbose if we won't run again with units */
2516                 if (BPY_execute_string_as_number(C, NULL, str, is_unit_but == false, r_value)) {
2517                         /* if the value parsed ok without unit conversion this button may still need a unit multiplier */
2518                         if (is_unit_but) {
2519                                 char str_new[128];
2520
2521                                 BLI_snprintf(str_new, sizeof(str_new), "%f", *r_value);
2522                                 ok = ui_set_but_string_eval_num_unit(C, but, str_new, r_value);
2523                         }
2524                         else {
2525                                 ok = true; /* parse normal string via py (no unit conversion needed) */
2526                         }
2527                 }
2528                 else if (is_unit_but) {
2529                         /* parse failed, this is a unit but so run replacements and parse again */
2530                         ok = ui_set_but_string_eval_num_unit(C, but, str, r_value);
2531                 }
2532         }
2533
2534 #else /* WITH_PYTHON */
2535
2536         *r_value = atof(str);
2537         ok = true;
2538
2539         UNUSED_VARS(C, but);
2540
2541 #endif /* WITH_PYTHON */
2542
2543         return ok;
2544 }
2545
2546 /* just the assignment/free part */
2547 static void ui_but_string_set_internal(uiBut *but, const char *str, size_t str_len)
2548 {
2549         BLI_assert(str_len == strlen(str));
2550         BLI_assert(but->str == NULL);
2551         str_len += 1;
2552
2553         if (str_len > UI_MAX_NAME_STR) {
2554                 but->str = MEM_mallocN(str_len, "ui_def_but str");
2555         }
2556         else {
2557                 but->str = but->strdata;
2558         }
2559         memcpy(but->str, str, str_len);
2560 }
2561
2562 static void ui_but_string_free_internal(uiBut *but)
2563 {
2564         if (but->str) {
2565                 if (but->str != but->strdata) {
2566                         MEM_freeN(but->str);
2567                 }
2568                 /* must call 'ui_but_string_set_internal' after */
2569                 but->str = NULL;
2570         }
2571 }
2572
2573 bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
2574 {
2575         if (but->rnaprop && but->rnapoin.data && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
2576                 if (RNA_property_editable(&but->rnapoin, but->rnaprop)) {
2577                         PropertyType type;
2578
2579                         type = RNA_property_type(but->rnaprop);
2580
2581                         if (type == PROP_STRING) {
2582                                 /* RNA string */
2583                                 RNA_property_string_set(&but->rnapoin, but->rnaprop, str);
2584                                 return true;
2585                         }
2586                         else if (type == PROP_POINTER) {
2587                                 if (str[0] == '\0') {
2588                                         RNA_property_pointer_set(&but->rnapoin, but->rnaprop, PointerRNA_NULL);
2589                                         return true;
2590                                 }
2591                                 else {
2592                                         /* RNA pointer */
2593                                         PointerRNA rptr;
2594                                         PointerRNA ptr = but->rnasearchpoin;
2595                                         PropertyRNA *prop = but->rnasearchprop;
2596
2597                                         /* This is kind of hackish, in theory think we could only ever use the second member of
2598                                          * this if/else, since ui_searchbox_apply() is supposed to always set that pointer when
2599                                          * we are storing pointers... But keeping str search first for now, to try to break as little as
2600                                          * possible existing code. All this is band-aids anyway.
2601                                          * Fact remains, using editstr as main 'reference' over whole search button thingy is utterly weak
2602                                          * and should be redesigned imho, but that's not a simple task. */
2603                                         if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr)) {
2604                                                 RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr);
2605                                         }
2606                                         else if (but->func_arg2 != NULL) {
2607                                                 RNA_pointer_create(NULL, RNA_property_pointer_type(&but->rnapoin, but->rnaprop), but->func_arg2, &rptr);
2608                                                 RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr);
2609                                         }
2610
2611                                         return true;
2612                                 }
2613
2614                                 return false;
2615                         }
2616                         else if (type == PROP_ENUM) {
2617                                 int value;
2618                                 if (RNA_property_enum_value(but->block->evil_C, &but->rnapoin, but->rnaprop, str, &value)) {
2619                                         RNA_property_enum_set(&but->rnapoin, but->rnaprop, value);
2620                                         return true;
2621                                 }
2622                                 return false;
2623                         }
2624                         else {
2625                                 BLI_assert(0);
2626                         }
2627                 }
2628         }
2629         else if (but->type == UI_BTYPE_TAB) {
2630                 if (but->rnaprop && but->custom_data) {
2631                         StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
2632                         PointerRNA ptr;
2633                         PropertyRNA *prop;
2634
2635                         /* uiBut.custom_data points to data this tab represents (e.g. workspace).
2636                          * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
2637                         RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
2638                         prop = RNA_struct_name_property(ptr_type);
2639                         if (RNA_property_editable(&ptr, prop)) {
2640                                 RNA_property_string_set(&ptr, prop, str);
2641                         }
2642                 }
2643         }
2644         else if (but->type == UI_BTYPE_TEXT) {
2645                 /* string */
2646                 if (!but->poin || (str[0] == '\0')) {
2647                         str = "";
2648                 }
2649                 else if (ui_but_is_utf8(but)) {
2650                         BLI_strncpy_utf8(but->poin, str, but->hardmax);
2651                 }
2652                 else {
2653                         BLI_strncpy(but->poin, str, but->hardmax);
2654                 }
2655
2656                 return true;
2657         }
2658         else if (but->type == UI_BTYPE_SEARCH_MENU) {
2659                 /* string */
2660                 BLI_strncpy(but->poin, str, but->hardmax);
2661                 return true;
2662         }
2663         else if (ui_but_anim_expression_set(but, str)) {
2664                 /* driver expression */
2665                 return true;
2666         }
2667         else if (str[0] == '#') {
2668                 /* shortcut to create new driver expression (versus immediate Py-execution) */
2669                 return ui_but_anim_expression_create(but, str + 1);
2670         }
2671         else {
2672                 /* number editing */
2673                 double value;
2674
2675                 if (ui_but_string_set_eval_num(C, but, str, &value) == false) {
2676                         WM_report_banner_show();
2677                         return false;
2678                 }
2679
2680                 if (!ui_but_is_float(but)) {
2681                         value = floor(value + 0.5);
2682                 }
2683
2684                 /* not that we use hard limits here */
2685                 if (value < (double)but->hardmin) value = but->hardmin;
2686                 if (value > (double)but->hardmax) value = but->hardmax;
2687
2688                 ui_but_value_set(but, value);
2689                 return true;
2690         }
2691
2692         return false;
2693 }
2694
2695 void ui_but_default_set(bContext *C, const bool all, const bool use_afterfunc)
2696 {
2697         wmOperatorType *ot = WM_operatortype_find("UI_OT_reset_default_button", true);
2698
2699         if (use_afterfunc) {
2700                 PointerRNA *ptr;
2701                 ptr = ui_handle_afterfunc_add_operator(ot, WM_OP_EXEC_DEFAULT, true);
2702                 RNA_boolean_set(ptr, "all", all);
2703         }
2704         else {
2705                 PointerRNA ptr;
2706                 WM_operator_properties_create_ptr(&ptr, ot);
2707                 RNA_boolean_set(&ptr, "all", all);
2708                 WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &ptr);
2709                 WM_operator_properties_free(&ptr);
2710         }
2711 }
2712
2713 static double soft_range_round_up(double value, double max)
2714 {
2715         /* round up to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, ..
2716          * checking for 0.0 prevents floating point exceptions */
2717         double newmax = (value != 0.0) ? pow(10.0, ceil(log(value) / M_LN10)) : 0.0;
2718
2719         if (newmax * 0.2 >= max && newmax * 0.2 >= value)
2720                 return newmax * 0.2;
2721         else if (newmax * 0.5 >= max && newmax * 0.5 >= value)
2722                 return newmax * 0.5;
2723         else
2724                 return newmax;
2725 }
2726
2727 static double soft_range_round_down(double value, double max)
2728 {
2729         /* round down to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, ..
2730          * checking for 0.0 prevents floating point exceptions */
2731         double newmax = (value != 0.0) ? pow(10.0, floor(log(value) / M_LN10)) : 0.0;
2732
2733         if (newmax * 5.0 <= max && newmax * 5.0 <= value)
2734                 return newmax * 5.0;
2735         else if (newmax * 2.0 <= max && newmax * 2.0 <= value)
2736                 return newmax * 2.0;
2737         else
2738                 return newmax;
2739 }
2740
2741 /* note: this could be split up into functions which handle arrays and not */
2742 static void ui_set_but_soft_range(uiBut *but)
2743 {
2744         /* ideally we would not limit this but practically, its more than
2745          * enough worst case is very long vectors wont use a smart soft-range
2746          * which isn't so bad. */
2747
2748         if (but->rnaprop) {
2749                 const PropertyType type = RNA_property_type(but->rnaprop);
2750                 double softmin, softmax /*, step, precision*/;
2751                 double value_min;
2752                 double value_max;
2753
2754                 /* clamp button range to something reasonable in case
2755                  * we get -inf/inf from RNA properties */
2756                 if (type == PROP_INT) {
2757                         const bool is_array = RNA_property_array_check(but->rnaprop);
2758                         int imin, imax, istep;
2759
2760                         RNA_property_int_ui_range(&but->rnapoin, but->rnaprop, &imin, &imax, &istep);
2761                         softmin = (imin == INT_MIN) ? -1e4 : imin;
2762                         softmax = (imin == INT_MAX) ? 1e4 : imax;
2763                         /*step = istep;*/ /*UNUSED*/
2764                         /*precision = 1;*/ /*UNUSED*/
2765
2766                         if (is_array) {
2767                                 int value_range[2];
2768                                 RNA_property_int_get_array_range(&but->rnapoin, but->rnaprop, value_range);
2769                                 value_min = (double)value_range[0];
2770                                 value_max = (double)value_range[1];
2771                         }
2772                         else {
2773                                 value_min = value_max = (double)RNA_property_int_get(&but->rnapoin, but->rnaprop);
2774                         }
2775                 }
2776                 else if (type == PROP_FLOAT) {
2777                         const bool is_array = RNA_property_array_check(but->rnaprop);
2778                         float fmin, fmax, fstep, fprecision;
2779
2780                         RNA_property_float_ui_range(&but->rnapoin, but->rnaprop, &fmin, &fmax, &fstep, &fprecision);
2781                         softmin = (fmin == -FLT_MAX) ? (float)-1e4 : fmin;
2782                         softmax = (fmax == FLT_MAX) ? (float)1e4 : fmax;
2783                         /*step = fstep;*/ /*UNUSED*/
2784                         /*precision = fprecision;*/ /*UNUSED*/
2785
2786                         if (is_array) {
2787                                 float value_range[2];
2788                                 RNA_property_float_get_array_range(&but->rnapoin, but->rnaprop, value_range);
2789                                 value_min = (double)value_range[0];
2790                                 value_max = (double)value_range[1];
2791                         }
2792                         else {
2793                                 value_min = value_max = (double)RNA_property_float_get(&but->rnapoin, but->rnaprop);
2794                         }
2795                 }
2796                 else {
2797                         return;
2798                 }
2799
2800                 /* if the value goes out of the soft/max range, adapt the range */
2801                 if (value_min + 1e-10 < softmin) {
2802                         if (value_min < 0.0)
2803                                 softmin = -soft_range_round_up(-value_min, -softmin);
2804                         else
2805                                 softmin = soft_range_round_down(value_min, softmin);
2806
2807                         if (softmin < (double)but->hardmin)
2808                                 softmin = (double)but->hardmin;
2809                 }
2810                 if (value_max - 1e-10 > softmax) {
2811                         if (value_max < 0.0)
2812                                 softmax = -soft_range_round_down(-value_max, -softmax);
2813                         else
2814                                 softmax = soft_range_round_up(value_max, softmax);
2815
2816                         if (softmax > (double)but->hardmax)
2817                                 softmax = but->hardmax;
2818                 }
2819
2820                 but->softmin = softmin;
2821                 but->softmax = softmax;
2822         }
2823         else if (but->poin && (but->pointype & UI_BUT_POIN_TYPES)) {
2824                 float value = ui_but_value_get(but);
2825                 if (isfinite(value)) {
2826                         CLAMP(value, but->hardmin, but->hardmax);
2827                         but->softmin = min_ff(but->softmin, value);
2828                         but->softmax = max_ff(but->softmax, value);
2829                 }
2830         }
2831         else {
2832                 BLI_assert(0);
2833         }
2834 }
2835
2836 /* ******************* Free ********************/
2837
2838 /* can be called with C==NULL */
2839 static void ui_but_free(const bContext *C, uiBut *but)
2840 {
2841         if (but->opptr) {
2842                 WM_operator_properties_free(but->opptr);
2843                 MEM_freeN(but->opptr);
2844         }
2845
2846         if (but->func_argN) {
2847                 MEM_freeN(but->func_argN);
2848         }
2849
2850         if (but->tip_argN) {
2851                 MEM_freeN(but->tip_argN);
2852         }
2853
2854         if (but->hold_argN) {
2855                 MEM_freeN(but->hold_argN);
2856         }
2857
2858         if (!but->editstr && but->free_search_arg) {
2859                 MEM_SAFE_FREE(but->search_arg);
2860         }
2861
2862         if (but->active) {
2863                 /* XXX solve later, buttons should be free-able without context ideally,
2864                  * however they may have open tooltips or popup windows, which need to
2865                  * be closed using a context pointer */
2866                 if (C) {
2867                         ui_but_active_free(C, but);
2868                 }
2869                 else {
2870                         if (but->active) {
2871                                 MEM_freeN(but->active);
2872                         }
2873                 }
2874         }
2875         if (but->str && but->str != but->strdata) {
2876                 MEM_freeN(but->str);
2877         }
2878
2879         if ((but->type == UI_BTYPE_IMAGE) && but->poin) {
2880                 IMB_freeImBuf((struct ImBuf *)but->poin);
2881         }
2882
2883         if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
2884                 MEM_freeN(but->dragpoin);
2885         }
2886
2887         BLI_assert(UI_butstore_is_registered(but->block, but) == false);
2888
2889         MEM_freeN(but);
2890 }
2891
2892 /* can be called with C==NULL */
2893 void UI_block_free(const bContext *C, uiBlock *block)
2894 {
2895         uiBut *but;
2896
2897         UI_butstore_clear(block);
2898
2899         while ((but = BLI_pophead(&block->buttons))) {
2900                 ui_but_free(C, but);
2901         }
2902
2903         if (block->unit) {
2904                 MEM_freeN(block->unit);
2905         }
2906
2907         if (block->func_argN) {
2908                 MEM_freeN(block->func_argN);
2909         }
2910
2911         CTX_store_free_list(&block->contexts);
2912
2913         BLI_freelistN(&block->saferct);
2914         BLI_freelistN(&block->color_pickers.list);
2915
2916         MEM_freeN(block);
2917 }
2918
2919 void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb)
2920 {
2921         ARegion *region = CTX_wm_region(C);
2922         wmWindow *window = CTX_wm_window(C);
2923
2924         for (uiBlock *block = lb->first; block; block = block->next) {
2925                 if (block->active) {
2926                         ui_update_window_matrix(window, region, block);
2927                 }
2928         }
2929 }
2930
2931 void UI_blocklist_draw(const bContext *C, const ListBase *lb)
2932 {
2933         for (uiBlock *block = lb->first; block; block = block->next) {
2934                 if (block->active) {
2935                         UI_block_draw(C, block);
2936                 }
2937         }
2938 }
2939
2940 /* can be called with C==NULL */
2941 void UI_blocklist_free(const bContext *C, ListBase *lb)
2942 {
2943         uiBlock *block;
2944
2945         while ((block = BLI_pophead(lb))) {
2946                 UI_block_free(C, block);
2947         }
2948 }
2949
2950 void UI_blocklist_free_inactive(const bContext *C, ListBase *lb)
2951 {
2952         uiBlock *block, *nextblock;
2953
2954         for (block = lb->first; block; block = nextblock) {
2955                 nextblock = block->next;
2956
2957                 if (!block->handle) {
2958                         if (!block->active) {
2959                                 BLI_remlink(lb, block);
2960                                 UI_block_free(C, block);
2961                         }
2962                         else
2963                                 block->active = 0;
2964                 }
2965         }
2966 }
2967
2968 void UI_block_region_set(uiBlock *block, ARegion *region)
2969 {
2970         ListBase *lb = &region->uiblocks;
2971         uiBlock *oldblock = NULL;
2972
2973         /* each listbase only has one block with this name, free block
2974          * if is already there so it can be rebuilt from scratch */
2975         if (lb) {
2976                 oldblock = BLI_findstring(lb, block->name, offsetof(uiBlock, name));
2977
2978                 if (oldblock) {
2979                         oldblock->active = 0;
2980                         oldblock->panel = NULL;
2981                         oldblock->handle = NULL;
2982                 }
2983
2984                 /* at the beginning of the list! for dynamical menus/blocks */
2985                 BLI_addhead(lb, block);
2986         }
2987
2988         block->oldblock = oldblock;
2989 }
2990
2991 uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, short dt)
2992 {
2993         uiBlock *block;
2994         wmWindow *window;
2995         Scene *scn;
2996
2997         window = CTX_wm_window(C);
2998         scn = CTX_data_scene(C);
2999
3000         block = MEM_callocN(sizeof(uiBlock), "uiBlock");
3001         block->active = 1;
3002         block->dt = dt;
3003         block->evil_C = (void *)C;  /* XXX */
3004
3005         if (scn) {
3006                 /* store display device name, don't lookup for transformations yet
3007                  * block could be used for non-color displays where looking up for transformation
3008                  * would slow down redraw, so only lookup for actual transform when it's indeed
3009                  * needed
3010                  */
3011                 STRNCPY(block->display_device, scn->display_settings.display_device);
3012
3013                 /* copy to avoid crash when scene gets deleted with ui still open */
3014                 block->unit = MEM_mallocN(sizeof(scn->unit), "UI UnitSettings");
3015                 memcpy(block->unit, &scn->unit, sizeof(scn->unit));
3016         }
3017         else {
3018                 STRNCPY(block->display_device, IMB_colormanagement_display_get_default_name());
3019         }
3020
3021         BLI_strncpy(block->name, name, sizeof(block->name));
3022
3023         if (region)
3024                 UI_block_region_set(block, region);
3025
3026         /* Set window matrix and aspect for region and OpenGL state. */
3027         ui_update_window_matrix(window, region, block);
3028
3029         /* Tag as popup menu if not created within a region. */
3030         if (!(region && region->visible)) {
3031                 block->auto_open = true;
3032                 block->flag |= UI_BLOCK_LOOP;
3033         }
3034
3035         return block;
3036 }
3037
3038 void UI_block_emboss_set(uiBlock *block, char dt)
3039 {
3040         block->dt = dt;
3041 }
3042
3043 void UI_block_theme_style_set(uiBlock *block, char theme_style)
3044 {
3045         block->theme_style = theme_style;
3046 }
3047
3048 /**
3049  * \param but: Button to update.
3050  * \param validate: When set, this function may change the button value.
3051  * Otherwise treat the button value as read-only.
3052  */
3053 void ui_but_update_ex(uiBut *but, const bool validate)
3054 {
3055         /* if something changed in the button */
3056         double value = UI_BUT_VALUE_UNSET;
3057
3058         ui_but_update_select_flag(but, &value);
3059
3060         /* only update soft range while not editing */
3061         if (!(but->editval || but->editstr || but->editvec)) {
3062                 if ((but->rnaprop != NULL) ||
3063                     (but->poin && (but->pointype & UI_BUT_POIN_TYPES)))
3064                 {
3065                         ui_set_but_soft_range(but);
3066                 }
3067         }
3068
3069         /* test for min and max, icon sliders, etc */
3070         switch (but->type) {
3071                 case UI_BTYPE_NUM:
3072                 case UI_BTYPE_SCROLL:
3073                 case UI_BTYPE_NUM_SLIDER:
3074                         if (validate) {
3075                                 UI_GET_BUT_VALUE_INIT(but, value);
3076                                 if      (value < (double)but->hardmin) {
3077                                         ui_but_value_set(but, but->hardmin);
3078                                 }
3079                                 else if (value > (double)but->hardmax) {
3080                                         ui_but_value_set(but, but->hardmax);
3081                                 }
3082
3083                                 /* max must never be smaller than min! Both being equal is allowed though */
3084                                 BLI_assert(but->softmin <= but->softmax &&
3085                                            but->hardmin <= but->hardmax);
3086                         }
3087                         break;
3088
3089                 case UI_BTYPE_ICON_TOGGLE:
3090                 case UI_BTYPE_ICON_TOGGLE_N:
3091                         if ((but->rnaprop == NULL) || (RNA_property_flag(but->rnaprop) & PROP_ICONS_CONSECUTIVE)) {
3092                                 if (but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ICONS_REVERSE) {
3093                                         but->drawflag |= UI_BUT_ICON_REVERSE;
3094                                 }
3095
3096                                 but->iconadd = (but->flag & UI_SELECT) ? 1 : 0;
3097                         }
3098                         break;
3099
3100                         /* quiet warnings for unhandled types */
3101                 default:
3102                         break;
3103         }
3104
3105
3106         /* safety is 4 to enable small number buttons (like 'users') */
3107         // okwidth = -4 + (BLI_rcti_size_x(&but->rect)); // UNUSED
3108
3109         /* name: */
3110         switch (but->type) {
3111
3112                 case UI_BTYPE_MENU:
3113                         if (BLI_rctf_size_x(&but->rect) >= (UI_UNIT_X * 2)) {
3114                                 /* only needed for menus in popup blocks that don't recreate buttons on redraw */
3115                                 if (but->block->flag & UI_BLOCK_LOOP) {
3116                                         if (but->rnaprop && (RNA_property_type(but->rnaprop) == PROP_ENUM)) {
3117                                                 int value_enum = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
3118
3119