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