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