Merge branch 'blender2.8' into userpref_redesign
[blender.git] / source / blender / editors / interface / interface_layout.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  * Contributor(s): Blender Foundation 2009.
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/interface/interface_layout.c
24  *  \ingroup edinterface
25  */
26
27
28 #include <limits.h>
29 #include <math.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_screen_types.h"
37 #include "DNA_armature_types.h"
38 #include "DNA_userdef_types.h"
39
40 #include "BLI_alloca.h"
41 #include "BLI_listbase.h"
42 #include "BLI_string.h"
43 #include "BLI_rect.h"
44 #include "BLI_utildefines.h"
45 #include "BLI_math.h"
46
47 #include "BLT_translation.h"
48
49 #include "BKE_context.h"
50 #include "BKE_global.h"
51 #include "BKE_idprop.h"
52 #include "BKE_screen.h"
53 #include "BKE_animsys.h"
54
55 #include "RNA_access.h"
56
57 #include "UI_interface.h"
58
59 #include "ED_armature.h"
60
61
62 #include "WM_api.h"
63 #include "WM_types.h"
64
65 #include "interface_intern.h"
66
67 /* Show an icon button after each RNA button to use to quickly set keyframes,
68  * this is a way to display animation/driven/override status, see T54951. */
69 #define UI_PROP_DECORATE
70 /* Alternate draw mode where some buttons can use single icon width,
71  * giving more room for the text at the expense of nicely aligned text. */
72 #define UI_PROP_SEP_ICON_WIDTH_EXCEPTION
73
74 /************************ Structs and Defines *************************/
75
76 #define UI_OPERATOR_ERROR_RET(_ot, _opname, return_statement)                 \
77         if (ot == NULL) {                                                         \
78                 ui_item_disabled(layout, _opname);                                    \
79                 RNA_warning("'%s' unknown operator", _opname);                        \
80                 return_statement;                                                     \
81         } (void)0                                                                 \
82
83 #define UI_ITEM_PROP_SEP_DIVIDE 0.5f
84
85 /* uiLayoutRoot */
86
87 typedef struct uiLayoutRoot {
88         struct uiLayoutRoot *next, *prev;
89
90         int type;
91         int opcontext;
92
93         int emw, emh;
94         int padding;
95
96         uiMenuHandleFunc handlefunc;
97         void *argv;
98
99         uiStyle *style;
100         uiBlock *block;
101         uiLayout *layout;
102 } uiLayoutRoot;
103
104 /* Item */
105
106 typedef enum uiItemType {
107         ITEM_BUTTON,
108
109         ITEM_LAYOUT_ROW,
110         ITEM_LAYOUT_COLUMN,
111         ITEM_LAYOUT_COLUMN_FLOW,
112         ITEM_LAYOUT_ROW_FLOW,
113         ITEM_LAYOUT_GRID_FLOW,
114         ITEM_LAYOUT_BOX,
115         ITEM_LAYOUT_ABSOLUTE,
116         ITEM_LAYOUT_SPLIT,
117         ITEM_LAYOUT_OVERLAP,
118         ITEM_LAYOUT_RADIAL,
119
120         ITEM_LAYOUT_ROOT
121 #if 0
122         TEMPLATE_COLUMN_FLOW,
123         TEMPLATE_SPLIT,
124         TEMPLATE_BOX,
125
126         TEMPLATE_HEADER,
127         TEMPLATE_HEADER_ID
128 #endif
129 } uiItemType;
130
131 typedef struct uiItem {
132         void *next, *prev;
133         uiItemType type;
134         int flag;
135 } uiItem;
136
137 enum {
138         UI_ITEM_FIXED     = 1 << 0,
139         UI_ITEM_MIN       = 1 << 1,
140
141         UI_ITEM_BOX_ITEM  = 1 << 2, /* The item is "inside" a box item */
142         UI_ITEM_PROP_SEP  = 1 << 3,
143         /* Show an icon button next to each property (to set keyframes, show status).
144          * Enabled by default, depends on 'UI_ITEM_PROP_SEP'. */
145         UI_ITEM_PROP_DECORATE = 1 << 4,
146         UI_ITEM_PROP_DECORATE_NO_PAD  = 1 << 5,
147 };
148
149 typedef struct uiButtonItem {
150         uiItem item;
151         uiBut *but;
152 } uiButtonItem;
153
154 struct uiLayout {
155         uiItem item;
156
157         uiLayoutRoot *root;
158         bContextStore *context;
159         ListBase items;
160
161         /* Sub layout to add child items, if not the layout itself. */
162         uiLayout *child_items_layout;
163
164         int x, y, w, h;
165         float scale[2];
166         short space;
167         bool align;
168         bool active;
169         bool enabled;
170         bool redalert;
171         bool keepaspect;
172         bool variable_size;  /* For layouts inside gridflow, they and their items shall never have a fixed maximal size. */
173         char alignment;
174         char emboss;
175         float units[2];  /* for fixed width or height to avoid UI size changes */
176 };
177
178 typedef struct uiLayoutItemFlow {
179         uiLayout litem;
180         int number;
181         int totcol;
182 } uiLayoutItemFlow;
183
184 typedef struct uiLayoutItemGridFlow {
185         uiLayout litem;
186
187         /* Extra parameters */
188         bool row_major;     /* Fill first row first, instead of filling first column first. */
189         bool even_columns;  /* Same width for all columns. */
190         bool even_rows;     /* Same height for all rows. */
191         /* If positive, absolute fixed number of columns.
192          * If 0, fully automatic (based on available width).
193          * If negative, automatic but only generates number of columns/rows multiple of given (absolute) value. */
194         int columns_len;
195
196         /* Pure internal runtime storage. */
197         int tot_items, tot_columns, tot_rows;
198 } uiLayoutItemGridFlow;
199
200 typedef struct uiLayoutItemBx {
201         uiLayout litem;
202         uiBut *roundbox;
203 } uiLayoutItemBx;
204
205 typedef struct uiLayoutItemSplit {
206         uiLayout litem;
207         float percentage;
208 } uiLayoutItemSplit;
209
210 typedef struct uiLayoutItemRoot {
211         uiLayout litem;
212 } uiLayoutItemRoot;
213
214 /************************** Item ***************************/
215
216 static const char *ui_item_name_add_colon(const char *name, char namestr[UI_MAX_NAME_STR])
217 {
218         int len = strlen(name);
219
220         if (len != 0 && len + 1 < UI_MAX_NAME_STR) {
221                 memcpy(namestr, name, len);
222                 namestr[len] = ':';
223                 namestr[len + 1] = '\0';
224                 return namestr;
225         }
226
227         return name;
228 }
229
230 static int ui_item_fit(int item, int pos, int all, int available, bool is_last, int alignment, float *extra_pixel)
231 {
232         /* available == 0 is unlimited */
233         if (ELEM(0, available, all)) {
234                 return item;
235         }
236
237         if (all > available) {
238                 /* contents is bigger than available space */
239                 if (is_last)
240                         return available - pos;
241                 else {
242                         float width = *extra_pixel + (item * available) / (float)all;
243                         *extra_pixel = width - (int)width;
244                         return (int)width;
245                 }
246         }
247         else {
248                 /* contents is smaller or equal to available space */
249                 if (alignment == UI_LAYOUT_ALIGN_EXPAND) {
250                         if (is_last)
251                                 return available - pos;
252                         else {
253                                 float width = *extra_pixel + (item * available) / (float)all;
254                                 *extra_pixel = width - (int)width;
255                                 return (int)width;
256                         }
257                 }
258                 else {
259                         return item;
260                 }
261         }
262 }
263
264 /* variable button size in which direction? */
265 #define UI_ITEM_VARY_X  1
266 #define UI_ITEM_VARY_Y  2
267
268 static int ui_layout_vary_direction(uiLayout *layout)
269 {
270         return ((ELEM(layout->root->type, UI_LAYOUT_HEADER, UI_LAYOUT_PIEMENU) ||
271                  (layout->alignment != UI_LAYOUT_ALIGN_EXPAND)) ?
272                 UI_ITEM_VARY_X : UI_ITEM_VARY_Y);
273 }
274
275 static bool ui_layout_variable_size(uiLayout *layout)
276 {
277         /* Note that this code is probably a bit flacky, we'd probably want to know whether it's variable in X and/or Y,
278          * etc. But for now it mimics previous one, with addition of variable flag set for children of gridflow layouts. */
279         return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size;
280 }
281
282 /* estimated size of text + icon */
283 static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact)
284 {
285         bool variable;
286         const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f);
287
288         if (icon && !name[0])
289                 return unit_x;  /* icon only */
290
291         variable = ui_layout_variable_size(layout);
292
293         if (variable) {
294                 if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) {
295                         layout->item.flag |= UI_ITEM_MIN;
296                 }
297                 const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
298                 /* it may seem odd that the icon only adds (unit_x / 4)
299                  * but taking margins into account its fine */
300                 return (UI_fontstyle_string_width(fstyle, name) +
301                         (unit_x * ((compact ? 1.25f : 1.50f) +
302                                    (icon    ? 0.25f : 0.0f))));
303         }
304         else {
305                 return unit_x * 10;
306         }
307 }
308
309 static void ui_item_size(uiItem *item, int *r_w, int *r_h)
310 {
311         if (item->type == ITEM_BUTTON) {
312                 uiButtonItem *bitem = (uiButtonItem *)item;
313
314                 if (r_w) *r_w = BLI_rctf_size_x(&bitem->but->rect);
315                 if (r_h) *r_h = BLI_rctf_size_y(&bitem->but->rect);
316         }
317         else {
318                 uiLayout *litem = (uiLayout *)item;
319
320                 if (r_w) *r_w = litem->w;
321                 if (r_h) *r_h = litem->h;
322         }
323 }
324
325 static void ui_item_offset(uiItem *item, int *r_x, int *r_y)
326 {
327         if (item->type == ITEM_BUTTON) {
328                 uiButtonItem *bitem = (uiButtonItem *)item;
329
330                 if (r_x) *r_x = bitem->but->rect.xmin;
331                 if (r_y) *r_y = bitem->but->rect.ymin;
332         }
333         else {
334                 if (r_x) *r_x = 0;
335                 if (r_y) *r_y = 0;
336         }
337 }
338
339 static void ui_item_position(uiItem *item, int x, int y, int w, int h)
340 {
341         if (item->type == ITEM_BUTTON) {
342                 uiButtonItem *bitem = (uiButtonItem *)item;
343
344                 bitem->but->rect.xmin = x;
345                 bitem->but->rect.ymin = y;
346                 bitem->but->rect.xmax = x + w;
347                 bitem->but->rect.ymax = y + h;
348
349                 ui_but_update(bitem->but); /* for strlen */
350         }
351         else {
352                 uiLayout *litem = (uiLayout *)item;
353
354                 litem->x = x;
355                 litem->y = y + h;
356                 litem->w = w;
357                 litem->h = h;
358         }
359 }
360
361 static void ui_item_move(uiItem *item, int delta_xmin, int delta_xmax)
362 {
363         if (item->type == ITEM_BUTTON) {
364                 uiButtonItem *bitem = (uiButtonItem *)item;
365
366                 bitem->but->rect.xmin += delta_xmin;
367                 bitem->but->rect.xmax += delta_xmax;
368
369                 ui_but_update(bitem->but); /* for strlen */
370         }
371         else {
372                 uiLayout *litem = (uiLayout *)item;
373
374                 if (delta_xmin > 0)
375                         litem->x += delta_xmin;
376                 else
377                         litem->w += delta_xmax;
378         }
379 }
380
381 /******************** Special RNA Items *********************/
382
383 static int ui_layout_local_dir(uiLayout *layout)
384 {
385         switch (layout->item.type) {
386                 case ITEM_LAYOUT_ROW:
387                 case ITEM_LAYOUT_ROOT:
388                 case ITEM_LAYOUT_OVERLAP:
389                         return UI_LAYOUT_HORIZONTAL;
390                 case ITEM_LAYOUT_COLUMN:
391                 case ITEM_LAYOUT_COLUMN_FLOW:
392                 case ITEM_LAYOUT_GRID_FLOW:
393                 case ITEM_LAYOUT_SPLIT:
394                 case ITEM_LAYOUT_ABSOLUTE:
395                 case ITEM_LAYOUT_BOX:
396                 default:
397                         return UI_LAYOUT_VERTICAL;
398         }
399 }
400
401 static uiLayout *ui_item_local_sublayout(uiLayout *test, uiLayout *layout, bool align)
402 {
403         uiLayout *sub;
404
405         if (ui_layout_local_dir(test) == UI_LAYOUT_HORIZONTAL)
406                 sub = uiLayoutRow(layout, align);
407         else
408                 sub = uiLayoutColumn(layout, align);
409
410         sub->space = 0;
411         return sub;
412 }
413
414 static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
415 {
416         wmWindow *win = CTX_wm_window(C);
417         uiBut *but = arg_but, *cbut;
418         PointerRNA *ptr = &but->rnapoin;
419         PropertyRNA *prop = but->rnaprop;
420         int i, index = POINTER_AS_INT(arg_index);
421         int shift = win->eventstate->shift;
422         int len = RNA_property_array_length(ptr, prop);
423
424         if (!shift) {
425                 RNA_property_boolean_set_index(ptr, prop, index, true);
426
427                 for (i = 0; i < len; i++)
428                         if (i != index)
429                                 RNA_property_boolean_set_index(ptr, prop, i, 0);
430
431                 RNA_property_update(C, ptr, prop);
432
433                 for (cbut = but->block->buttons.first; cbut; cbut = cbut->next)
434                         ui_but_update(cbut);
435         }
436 }
437
438 /* create buttons for an item with an RNA array */
439 static void ui_item_array(
440         uiLayout *layout, uiBlock *block, const char *name, int icon,
441         PointerRNA *ptr, PropertyRNA *prop, int len, int x, int y, int w, int UNUSED(h),
442         bool expand, bool slider, bool toggle, bool icon_only, bool compact, bool show_text)
443 {
444         uiStyle *style = layout->root->style;
445         uiBut *but;
446         PropertyType type;
447         PropertySubType subtype;
448         uiLayout *sub;
449         unsigned int a, b;
450
451         /* retrieve type and subtype */
452         type = RNA_property_type(prop);
453         subtype = RNA_property_subtype(prop);
454
455         sub = ui_item_local_sublayout(layout, layout, 1);
456         UI_block_layout_set_current(block, sub);
457
458         /* create label */
459         if (name[0] && show_text) {
460                 uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
461         }
462
463         /* create buttons */
464         if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) {
465                 /* special check for layer layout */
466                 int butw, buth, unit;
467                 int cols = (len >= 20) ? 2 : 1;
468                 const unsigned int colbuts = len / (2 * cols);
469                 unsigned int layer_used = 0;
470                 unsigned int layer_active = 0;
471
472                 UI_block_layout_set_current(block, uiLayoutAbsolute(layout, false));
473
474                 unit = UI_UNIT_X * 0.75;
475                 butw = unit;
476                 buth = unit;
477
478                 if (ptr->type == &RNA_Armature) {
479                         bArmature *arm = (bArmature *)ptr->data;
480
481                         layer_used = arm->layer_used;
482
483                         if (arm->edbo) {
484                                 if (arm->act_edbone) {
485                                         layer_active |= arm->act_edbone->layer;
486                                 }
487                         }
488                         else {
489                                 if (arm->act_bone) {
490                                         layer_active |= arm->act_bone->layer;
491                                 }
492                         }
493                 }
494
495                 for (b = 0; b < cols; b++) {
496                         UI_block_align_begin(block);
497
498                         for (a = 0; a < colbuts; a++) {
499                                 const int layer_num  = a + b * colbuts;
500                                 const unsigned int layer_flag = (1u << layer_num);
501
502                                 if (layer_used & layer_flag) {
503                                         if (layer_active & layer_flag)
504                                                 icon = ICON_LAYER_ACTIVE;
505                                         else
506                                                 icon = ICON_LAYER_USED;
507                                 }
508                                 else {
509                                         icon = ICON_BLANK1;
510                                 }
511
512                                 but = uiDefAutoButR(block, ptr, prop, layer_num, "", icon, x + butw * a, y + buth, butw, buth);
513                                 if (subtype == PROP_LAYER_MEMBER)
514                                         UI_but_func_set(but, ui_layer_but_cb, but, POINTER_FROM_INT(layer_num));
515                         }
516                         for (a = 0; a < colbuts; a++) {
517                                 const int layer_num  = a + len / 2 + b * colbuts;
518                                 const unsigned int layer_flag = (1u << layer_num);
519
520                                 if (layer_used & layer_flag) {
521                                         if (layer_active & layer_flag)
522                                                 icon = ICON_LAYER_ACTIVE;
523                                         else
524                                                 icon = ICON_LAYER_USED;
525                                 }
526                                 else {
527                                         icon = ICON_BLANK1;
528                                 }
529
530                                 but = uiDefAutoButR(block, ptr, prop, layer_num, "", icon, x + butw * a, y, butw, buth);
531                                 if (subtype == PROP_LAYER_MEMBER)
532                                         UI_but_func_set(but, ui_layer_but_cb, but, POINTER_FROM_INT(layer_num));
533                         }
534                         UI_block_align_end(block);
535
536                         x += colbuts * butw + style->buttonspacex;
537                 }
538         }
539         else if (subtype == PROP_MATRIX) {
540                 int totdim, dim_size[3];    /* 3 == RNA_MAX_ARRAY_DIMENSION */
541                 int row, col;
542
543                 UI_block_layout_set_current(block, uiLayoutAbsolute(layout, true));
544
545                 totdim = RNA_property_array_dimension(ptr, prop, dim_size);
546                 if (totdim != 2) return;    /* only 2D matrices supported in UI so far */
547
548                 w /= dim_size[0];
549                 /* h /= dim_size[1]; */ /* UNUSED */
550
551                 for (a = 0; a < len; a++) {
552                         col = a % dim_size[0];
553                         row = a / dim_size[0];
554
555                         but = uiDefAutoButR(block, ptr, prop, a, "", ICON_NONE, x + w * col, y + (dim_size[1] * UI_UNIT_Y) - (row * UI_UNIT_Y), w, UI_UNIT_Y);
556                         if (slider && but->type == UI_BTYPE_NUM)
557                                 but->type = UI_BTYPE_NUM_SLIDER;
558                 }
559         }
560         else if (subtype == PROP_DIRECTION && !expand) {
561                 uiDefButR_prop(block, UI_BTYPE_UNITVEC, 0, name, x, y, UI_UNIT_X * 3, UI_UNIT_Y * 3, ptr, prop, -1, 0, 0, -1, -1, NULL);
562         }
563         else {
564                 /* note, this block of code is a bit arbitrary and has just been made
565                  * to work with common cases, but may need to be re-worked */
566
567                 /* special case, boolean array in a menu, this could be used in a more generic way too */
568                 if (ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA) && !expand) {
569                         uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, 0, 0, w, UI_UNIT_Y);
570                 }
571                 else {
572                         bool *boolarr = NULL;
573
574                         /* even if 'expand' is fale, expanding anyway */
575
576                         /* layout for known array subtypes */
577                         char str[3] = {'\0'};
578
579                         if (!icon_only && show_text) {
580                                 if (type != PROP_BOOLEAN) {
581                                         str[1] = ':';
582                                 }
583                         }
584
585                         /* show checkboxes for rna on a non-emboss block (menu for eg) */
586                         if (type == PROP_BOOLEAN && ELEM(layout->root->block->dt, UI_EMBOSS_NONE, UI_EMBOSS_PULLDOWN)) {
587                                 boolarr = MEM_callocN(sizeof(bool) * len, __func__);
588                                 RNA_property_boolean_get_array(ptr, prop, boolarr);
589                         }
590
591                         const char *str_buf = show_text ? str: "";
592                         for (a = 0; a < len; a++) {
593                                 int width_item;
594
595                                 if (!icon_only && show_text) {
596                                         str[0] = RNA_property_array_item_char(prop, a);
597                                 }
598                                 if (boolarr) {
599                                         icon = boolarr[a] ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
600                                 }
601
602                                 width_item = (
603                                         (compact && type == PROP_BOOLEAN) ?
604                                         min_ii(w, ui_text_icon_width(layout, str_buf, icon, false)) : w);
605
606                                 but = uiDefAutoButR(block, ptr, prop, a, str_buf, icon, 0, 0, width_item, UI_UNIT_Y);
607                                 if (slider && but->type == UI_BTYPE_NUM)
608                                         but->type = UI_BTYPE_NUM_SLIDER;
609                                 if (toggle && but->type == UI_BTYPE_CHECKBOX)
610                                         but->type = UI_BTYPE_TOGGLE;
611                                 if ((a == 0) && (subtype == PROP_AXISANGLE))
612                                         UI_but_unit_type_set(but, PROP_UNIT_ROTATION);
613                         }
614
615                         if (boolarr) {
616                                 MEM_freeN(boolarr);
617                         }
618                 }
619         }
620
621         UI_block_layout_set_current(block, layout);
622 }
623
624 static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2)
625 {
626         wmWindow *win = CTX_wm_window(C);
627
628         if (!win->eventstate->shift) {
629                 uiBut *but = (uiBut *)arg1;
630                 int enum_value = POINTER_AS_INT(arg2);
631
632                 int current_value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
633                 if (!(current_value & enum_value)) {
634                         current_value = enum_value;
635                 }
636                 else {
637                         current_value &= enum_value;
638                 }
639                 RNA_property_enum_set(&but->rnapoin, but->rnaprop, current_value);
640         }
641 }
642 static void ui_item_enum_expand_exec(
643         uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop,
644         const char *uiname, int h, int but_type, bool icon_only)
645 {
646         /* XXX The way this function currently handles uiname parameter is insane and inconsistent with general UI API:
647          *     * uiname is the *enum property* label.
648          *     * when it is NULL or empty, we do not draw *enum items* labels, this doubles the icon_only parameter.
649          *     * we *never* draw (i.e. really use) the enum label uiname, it is just used as a mere flag!
650          *     Unfortunately, fixing this implies an API "soft break", so better to defer it for later... :/
651          *     --mont29
652          */
653
654         uiBut *but;
655         uiLayout *layout_radial = NULL;
656         const EnumPropertyItem *item, *item_array;
657         const char *name;
658         int itemw, icon, value;
659         bool free;
660         bool radial = (layout->root->type == UI_LAYOUT_PIEMENU);
661
662         BLI_assert(RNA_property_type(prop) == PROP_ENUM);
663
664         if (radial)
665                 RNA_property_enum_items_gettexted_all(block->evil_C, ptr, prop, &item_array, NULL, &free);
666         else
667                 RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item_array, NULL, &free);
668
669         /* we dont want nested rows, cols in menus */
670         if (radial) {
671                 if (layout->root->layout == layout) {
672                         layout_radial = uiLayoutRadial(layout);
673                         UI_block_layout_set_current(block, layout_radial);
674                 }
675                 else {
676                         if (layout->item.type == ITEM_LAYOUT_RADIAL) {
677                                 layout_radial = layout;
678                         }
679                         UI_block_layout_set_current(block, layout);
680                 }
681         }
682         else if (layout->root->type != UI_LAYOUT_MENU) {
683                 UI_block_layout_set_current(block, ui_item_local_sublayout(layout, layout, 1));
684         }
685         else {
686                 UI_block_layout_set_current(block, layout);
687         }
688
689         for (item = item_array; item->identifier; item++) {
690                 const bool is_first = item == item_array;
691
692                 if (!item->identifier[0]) {
693                         if (item->name) {
694                                 size_t group_name_len = strlen(item->name) + 2;
695                                 char *group_name = alloca(group_name_len);
696
697                                 if (!is_first) {
698                                         uiItemS(block->curlayout);
699                                 }
700                                 BLI_snprintf(group_name, group_name_len, "%s:", item->name);
701                                 uiItemL(block->curlayout, group_name, ICON_NONE);
702                         }
703                         else {
704                                 const EnumPropertyItem *next_item = item + 1;
705                                 if (next_item->identifier) {
706                                         if (radial && layout_radial) {
707                                                 uiItemS(layout_radial);
708                                         }
709                                         else {
710                                                 uiItemS(block->curlayout);
711                                         }
712                                 }
713                         }
714                         continue;
715                 }
716
717                 name = (!uiname || uiname[0]) ? item->name : "";
718                 icon = item->icon;
719                 value = item->value;
720                 itemw = ui_text_icon_width(block->curlayout, icon_only ? "" : name, icon, 0);
721
722                 if (icon && name[0] && !icon_only)
723                         but = uiDefIconTextButR_prop(block, but_type, 0, icon, name, 0, 0, itemw, h, ptr, prop, -1, 0, value, -1, -1, NULL);
724                 else if (icon)
725                         but = uiDefIconButR_prop(block, but_type, 0, icon, 0, 0, itemw, h, ptr, prop, -1, 0, value, -1, -1, NULL);
726                 else
727                         but = uiDefButR_prop(block, but_type, 0, name, 0, 0, itemw, h, ptr, prop, -1, 0, value, -1, -1, NULL);
728
729                 if (RNA_property_flag(prop) & PROP_ENUM_FLAG) {
730                         UI_but_func_set(but, ui_item_enum_expand_handle, but, POINTER_FROM_INT(value));
731                 }
732
733                 if (ui_layout_local_dir(layout) != UI_LAYOUT_HORIZONTAL)
734                         but->drawflag |= UI_BUT_TEXT_LEFT;
735         }
736         UI_block_layout_set_current(block, layout);
737
738         if (free) {
739                 MEM_freeN((void *)item_array);
740         }
741 }
742 static void ui_item_enum_expand(
743         uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop,
744         const char *uiname, int h, bool icon_only)
745 {
746         ui_item_enum_expand_exec(layout, block, ptr, prop, uiname, h, UI_BTYPE_ROW, icon_only);
747 }
748 static void ui_item_enum_expand_tabs(
749         uiLayout *layout, bContext *C, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop,
750         const char *uiname, int h, bool icon_only)
751 {
752         uiBut *last = block->buttons.last;
753
754         ui_item_enum_expand_exec(layout, block, ptr, prop, uiname, h, UI_BTYPE_TAB, icon_only);
755         BLI_assert(last != block->buttons.last);
756         for (uiBut *tab = last ? last->next : block->buttons.first; tab; tab = tab->next) {
757                 UI_but_drawflag_enable(tab, ui_but_align_opposite_to_area_align_get(CTX_wm_region(C)));
758         }
759 }
760
761 /* callback for keymap item change button */
762 static void ui_keymap_but_cb(bContext *UNUSED(C), void *but_v, void *UNUSED(key_v))
763 {
764         uiBut *but = but_v;
765
766         RNA_boolean_set(&but->rnapoin, "shift", (but->modifier_key & KM_SHIFT) != 0);
767         RNA_boolean_set(&but->rnapoin, "ctrl", (but->modifier_key & KM_CTRL) != 0);
768         RNA_boolean_set(&but->rnapoin, "alt", (but->modifier_key & KM_ALT) != 0);
769         RNA_boolean_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) != 0);
770 }
771
772 /**
773  * Create label + button for RNA property
774  *
775  * \param w_hint: For varying width layout, this becomes the label width.
776  *                Otherwise it's used to fit both items into it.
777  **/
778 static uiBut *ui_item_with_label(
779         uiLayout *layout, uiBlock *block, const char *name, int icon,
780         PointerRNA *ptr, PropertyRNA *prop, int index,
781         int x, int y, int w_hint, int h, int flag)
782 {
783         uiLayout *sub;
784         uiBut *but = NULL;
785         PropertyType type;
786         PropertySubType subtype;
787         int prop_but_width = w_hint;
788         const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
789
790         /* Always align item with label since text is already given enough space not to overlap. */
791         sub = uiLayoutRow(layout, true);
792         UI_block_layout_set_current(block, sub);
793
794         if (name[0]) {
795                 int w_label;
796
797                 if (use_prop_sep) {
798                         w_label = (int)((w_hint * 2) * UI_ITEM_PROP_SEP_DIVIDE);
799                 }
800                 else {
801                         if (ui_layout_variable_size(layout)) {
802                                 /* w_hint is width for label in this case. Use a default width for property button(s) */
803                                 prop_but_width = UI_UNIT_X * 5;
804                                 w_label = w_hint;
805                         }
806                         else {
807                                 w_label = w_hint / 3;
808                         }
809                 }
810
811                 uiBut *but_label = uiDefBut(block, UI_BTYPE_LABEL, 0, name, x, y, w_label, h, NULL, 0.0, 0.0, 0, 0, "");
812                 if (use_prop_sep) {
813                         but_label->drawflag |= UI_BUT_TEXT_RIGHT;
814                         but_label->drawflag &= ~UI_BUT_TEXT_LEFT;
815                 }
816         }
817
818         type = RNA_property_type(prop);
819         subtype = RNA_property_subtype(prop);
820
821         if (subtype == PROP_FILEPATH || subtype == PROP_DIRPATH) {
822                 UI_block_layout_set_current(block, uiLayoutRow(sub, true));
823                 but = uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, prop_but_width - UI_UNIT_X, h);
824
825                 /* BUTTONS_OT_file_browse calls UI_context_active_but_prop_get_filebrowser */
826                 uiDefIconButO(
827                         block, UI_BTYPE_BUT, subtype == PROP_DIRPATH ? "BUTTONS_OT_directory_browse" : "BUTTONS_OT_file_browse",
828                         WM_OP_INVOKE_DEFAULT, ICON_FILEBROWSER, x, y, UI_UNIT_X, h, NULL);
829         }
830         else if (flag & UI_ITEM_R_EVENT) {
831                 but = uiDefButR_prop(block, UI_BTYPE_KEY_EVENT, 0, name, x, y, prop_but_width, h, ptr, prop, index, 0, 0, -1, -1, NULL);
832         }
833         else if (flag & UI_ITEM_R_FULL_EVENT) {
834                 if (RNA_struct_is_a(ptr->type, &RNA_KeyMapItem)) {
835                         char buf[128];
836
837                         WM_keymap_item_to_string(ptr->data, false, buf, sizeof(buf));
838
839                         but = uiDefButR_prop(block, UI_BTYPE_HOTKEY_EVENT, 0, buf, x, y, prop_but_width, h, ptr, prop, 0, 0, 0, -1, -1, NULL);
840                         UI_but_func_set(but, ui_keymap_but_cb, but, NULL);
841                         if (flag & UI_ITEM_R_IMMEDIATE)
842                                 UI_but_flag_enable(but, UI_BUT_IMMEDIATE);
843                 }
844         }
845         else {
846                 const char *str = (type == PROP_ENUM && !(flag & UI_ITEM_R_ICON_ONLY)) ? NULL : "";
847                 but = uiDefAutoButR(
848                         block, ptr, prop, index, str, icon,
849                         x, y, prop_but_width, h);
850         }
851
852 #ifdef UI_PROP_DECORATE
853         /* Only for alignment. */
854         if (layout->item.flag & UI_ITEM_PROP_SEP) {
855                 if ((layout->item.flag & UI_ITEM_PROP_DECORATE) &&
856                     (layout->item.flag & UI_ITEM_PROP_DECORATE_NO_PAD) == 0)
857                 {
858                         uiItemL(sub, NULL, ICON_BLANK1);
859                 }
860         }
861 #endif  /* UI_PROP_DECORATE */
862
863         UI_block_layout_set_current(block, layout);
864         return but;
865 }
866
867 void UI_context_active_but_prop_get_filebrowser(
868         const bContext *C,
869         PointerRNA *r_ptr, PropertyRNA **r_prop, bool *r_is_undo)
870 {
871         ARegion *ar = CTX_wm_region(C);
872         uiBlock *block;
873         uiBut *but, *prevbut;
874
875         memset(r_ptr, 0, sizeof(*r_ptr));
876         *r_prop = NULL;
877         *r_is_undo = false;
878
879         if (!ar)
880                 return;
881
882         for (block = ar->uiblocks.first; block; block = block->next) {
883                 for (but = block->buttons.first; but; but = but->next) {
884                         prevbut = but->prev;
885
886                         /* find the button before the active one */
887                         if ((but->flag & UI_BUT_LAST_ACTIVE) && prevbut && prevbut->rnapoin.data) {
888                                 if (RNA_property_type(prevbut->rnaprop) == PROP_STRING) {
889                                         *r_ptr = prevbut->rnapoin;
890                                         *r_prop = prevbut->rnaprop;
891                                         *r_is_undo = (prevbut->flag & UI_BUT_UNDO) != 0;
892                                         return;
893                                 }
894                         }
895                 }
896         }
897 }
898
899 /********************* Button Items *************************/
900
901 /**
902  * Update a buttons tip with an enum's description if possible.
903  */
904 static void ui_but_tip_from_enum_item(uiBut *but, const EnumPropertyItem *item)
905 {
906         if (but->tip == NULL || but->tip[0] == '\0') {
907                 if (item->description && item->description[0]) {
908                         but->tip = item->description;
909                 }
910         }
911 }
912
913 /* disabled item */
914 static void ui_item_disabled(uiLayout *layout, const char *name)
915 {
916         uiBlock *block = layout->root->block;
917         uiBut *but;
918         int w;
919
920         UI_block_layout_set_current(block, layout);
921
922         if (!name)
923                 name = "";
924
925         w = ui_text_icon_width(layout, name, 0, 0);
926
927         but = uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
928         but->flag |= UI_BUT_DISABLED;
929         but->disabled_info = "";
930 }
931
932 /**
933  * Operator Item
934  * \param r_opptr: Optional, initialize with operator properties when not NULL.
935  * Will always be written to even in the case of errors.
936  */
937 static uiBut *uiItemFullO_ptr_ex(
938         uiLayout *layout, wmOperatorType *ot,
939         const char *name, int icon, IDProperty *properties, int context, int flag,
940         PointerRNA *r_opptr)
941 {
942         /* Take care to fill 'r_opptr' whatever happens. */
943         uiBlock *block = layout->root->block;
944         uiBut *but;
945         int w;
946
947         if (!name) {
948                 if (ot && ot->srna && (flag & UI_ITEM_R_ICON_ONLY) == 0)
949                         name = RNA_struct_ui_name(ot->srna);
950                 else
951                         name = "";
952         }
953
954         if (layout->root->type == UI_LAYOUT_MENU && !icon)
955                 icon = ICON_BLANK1;
956
957         /* create button */
958         UI_block_layout_set_current(block, layout);
959
960         w = ui_text_icon_width(layout, name, icon, 0);
961
962         int prev_emboss = layout->emboss;
963         if (flag & UI_ITEM_R_NO_BG) {
964                 layout->emboss = UI_EMBOSS_NONE;
965         }
966
967         /* create the button */
968         if (icon) {
969                 if (name[0]) {
970                         but = uiDefIconTextButO_ptr(block, UI_BTYPE_BUT, ot, context, icon, name, 0, 0, w, UI_UNIT_Y, NULL);
971                 }
972                 else {
973                         but = uiDefIconButO_ptr(block, UI_BTYPE_BUT, ot, context, icon, 0, 0, w, UI_UNIT_Y, NULL);
974                 }
975         }
976         else {
977                 but = uiDefButO_ptr(block, UI_BTYPE_BUT, ot, context, name, 0, 0, w, UI_UNIT_Y, NULL);
978         }
979
980         assert(but->optype != NULL);
981
982         /* text alignment for toolbar buttons */
983         if ((layout->root->type == UI_LAYOUT_TOOLBAR) && !icon)
984                 but->drawflag |= UI_BUT_TEXT_LEFT;
985
986         if (flag & UI_ITEM_R_NO_BG) {
987                 layout->emboss = prev_emboss;
988         }
989
990         if (flag & UI_ITEM_O_DEPRESS) {
991                 but->flag |= UI_SELECT_DRAW;
992         }
993
994         if (layout->redalert)
995                 UI_but_flag_enable(but, UI_BUT_REDALERT);
996
997         /* assign properties */
998         if (properties || r_opptr) {
999                 PointerRNA *opptr = UI_but_operator_ptr_get(but);
1000                 if (properties) {
1001                         opptr->data = properties;
1002                 }
1003                 else {
1004                         IDPropertyTemplate val = {0};
1005                         opptr->data = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1006                 }
1007                 if (r_opptr) {
1008                         *r_opptr = *opptr;
1009                 }
1010         }
1011
1012         return but;
1013 }
1014
1015 static void ui_item_menu_hold(struct bContext *C, ARegion *butregion, uiBut *but)
1016 {
1017         uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE);
1018         uiLayout *layout = UI_popup_menu_layout(pup);
1019         uiBlock *block = layout->root->block;
1020         UI_popup_menu_but_set(pup, butregion, but);
1021
1022         block->flag |= UI_BLOCK_POPUP_HOLD;
1023         block->flag |= UI_BLOCK_IS_FLIP;
1024
1025         char direction = UI_DIR_DOWN;
1026         if (!but->drawstr[0]) {
1027                 if (butregion->alignment == RGN_ALIGN_LEFT) {
1028                         direction = UI_DIR_RIGHT;
1029                 }
1030                 else if (butregion->alignment == RGN_ALIGN_RIGHT) {
1031                         direction = UI_DIR_LEFT;
1032                 }
1033                 else if (butregion->alignment == RGN_ALIGN_BOTTOM) {
1034                         direction = UI_DIR_UP;
1035                 }
1036                 else {
1037                         direction = UI_DIR_DOWN;
1038                 }
1039         }
1040         UI_block_direction_set(block, direction);
1041
1042         const char *menu_id = but->hold_argN;
1043         MenuType *mt = WM_menutype_find(menu_id, true);
1044         if (mt) {
1045                 uiLayoutSetContextFromBut(layout, but);
1046                 UI_menutype_draw(C, mt, layout);
1047         }
1048         else {
1049                 uiItemL(layout, "Menu Missing:", ICON_NONE);
1050                 uiItemL(layout, menu_id, ICON_NONE);
1051         }
1052         UI_popup_menu_end(C, pup);
1053 }
1054
1055 void uiItemFullO_ptr(
1056         uiLayout *layout, wmOperatorType *ot,
1057         const char *name, int icon, IDProperty *properties, int context, int flag,
1058         PointerRNA *r_opptr)
1059 {
1060         uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr);
1061 }
1062
1063 void uiItemFullOMenuHold_ptr(
1064         uiLayout *layout, wmOperatorType *ot,
1065         const char *name, int icon, IDProperty *properties, int context, int flag,
1066         const char *menu_id,
1067         PointerRNA *r_opptr)
1068 {
1069         uiBut *but = uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr);
1070         UI_but_func_hold_set(but, ui_item_menu_hold, BLI_strdup(menu_id));
1071 }
1072
1073 void uiItemFullO(
1074         uiLayout *layout, const char *opname,
1075         const char *name, int icon, IDProperty *properties, int context, int flag,
1076         PointerRNA *r_opptr)
1077 {
1078         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1079
1080         UI_OPERATOR_ERROR_RET(
1081                 ot, opname, {
1082                     if (r_opptr) {
1083                         *r_opptr = PointerRNA_NULL;
1084                     }
1085                     return;
1086                 });
1087
1088         uiItemFullO_ptr(layout, ot, name, icon, properties, context, flag, r_opptr);
1089 }
1090
1091 static const char *ui_menu_enumpropname(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int retval)
1092 {
1093         const EnumPropertyItem *item;
1094         bool free;
1095         const char *name;
1096
1097         RNA_property_enum_items(layout->root->block->evil_C, ptr, prop, &item, NULL, &free);
1098         if (RNA_enum_name(item, retval, &name)) {
1099                 name = CTX_IFACE_(RNA_property_translation_context(prop), name);
1100         }
1101         else {
1102                 name = "";
1103         }
1104
1105         if (free) {
1106                 MEM_freeN((void *)item);
1107         }
1108
1109         return name;
1110 }
1111
1112 void uiItemEnumO_ptr(uiLayout *layout, wmOperatorType *ot, const char *name, int icon, const char *propname, int value)
1113 {
1114         PointerRNA ptr;
1115         PropertyRNA *prop;
1116
1117         WM_operator_properties_create_ptr(&ptr, ot);
1118
1119         if ((prop = RNA_struct_find_property(&ptr, propname))) {
1120                 /* pass */
1121         }
1122         else {
1123                 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1124                 return;
1125         }
1126
1127         RNA_property_enum_set(&ptr, prop, value);
1128
1129         if (!name)
1130                 name = ui_menu_enumpropname(layout, &ptr, prop, value);
1131
1132         uiItemFullO_ptr(layout, ot, name, icon, ptr.data, layout->root->opcontext, 0, NULL);
1133 }
1134 void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int icon, const char *propname, int value)
1135 {
1136         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1137
1138         if (ot) {
1139                 uiItemEnumO_ptr(layout, ot, name, icon, propname, value);
1140         }
1141         else {
1142                 ui_item_disabled(layout, opname);
1143                 RNA_warning("unknown operator '%s'", opname);
1144         }
1145
1146 }
1147
1148 BLI_INLINE bool ui_layout_is_radial(const uiLayout *layout)
1149 {
1150         return (layout->item.type == ITEM_LAYOUT_RADIAL) ||
1151                ((layout->item.type == ITEM_LAYOUT_ROOT) && (layout->root->type == UI_LAYOUT_PIEMENU));
1152 }
1153
1154 /**
1155  * Create ui items for enum items in \a item_array.
1156  *
1157  * A version of #uiItemsFullEnumO that takes pre-calculated item array.
1158  */
1159 void uiItemsFullEnumO_items(
1160         uiLayout *layout, wmOperatorType *ot, PointerRNA ptr, PropertyRNA *prop, IDProperty *properties,
1161         int context, int flag,
1162         const EnumPropertyItem *item_array, int totitem)
1163 {
1164         const char *propname = RNA_property_identifier(prop);
1165         if (RNA_property_type(prop) != PROP_ENUM) {
1166                 RNA_warning("%s.%s, not an enum type", RNA_struct_identifier(ptr.type), propname);
1167                 return;
1168         }
1169
1170         uiLayout *target, *split = NULL;
1171         const EnumPropertyItem *item;
1172         uiBlock *block = layout->root->block;
1173         const bool radial = ui_layout_is_radial(layout);
1174
1175         if (radial) {
1176                 target = uiLayoutRadial(layout);
1177         }
1178         else {
1179                 split = uiLayoutSplit(layout, 0.0f, false);
1180                 target = uiLayoutColumn(split, layout->align);
1181         }
1182
1183
1184         int i;
1185         bool last_iter = false;
1186
1187         for (i = 1, item = item_array; item->identifier && !last_iter; i++, item++) {
1188                 /* handle oversized pies */
1189                 if (radial && (totitem > PIE_MAX_ITEMS) && (i >= PIE_MAX_ITEMS)) {
1190                         if (item->name) { /* only visible items */
1191                                 const EnumPropertyItem *tmp;
1192
1193                                 /* Check if there are more visible items for the next level. If not, we don't
1194                                  * add a new level and add the remaining item instead of the 'more' button. */
1195                                 for (tmp = item + 1; tmp->identifier; tmp++)
1196                                         if (tmp->name)
1197                                                 break;
1198
1199                                 if (tmp->identifier) { /* only true if loop above found item and did early-exit */
1200                                         ui_pie_menu_level_create(block, ot, propname, properties, item_array, totitem, context, flag);
1201                                         /* break since rest of items is handled in new pie level */
1202                                         break;
1203                                 }
1204                                 else {
1205                                         last_iter = true;
1206                                 }
1207                         }
1208                         else {
1209                                 continue;
1210                         }
1211                 }
1212
1213                 if (item->identifier[0]) {
1214                         PointerRNA tptr;
1215
1216                         WM_operator_properties_create_ptr(&tptr, ot);
1217                         if (properties) {
1218                                 if (tptr.data) {
1219                                         IDP_FreeProperty(tptr.data);
1220                                         MEM_freeN(tptr.data);
1221                                 }
1222                                 tptr.data = IDP_CopyProperty(properties);
1223                         }
1224                         RNA_property_enum_set(&tptr, prop, item->value);
1225
1226                         uiItemFullO_ptr(target, ot, item->name, item->icon, tptr.data, context, flag, NULL);
1227
1228                         ui_but_tip_from_enum_item(block->buttons.last, item);
1229                 }
1230                 else {
1231                         if (item->name) {
1232                                 uiBut *but;
1233
1234                                 if (item != item_array && !radial) {
1235                                         target = uiLayoutColumn(split, layout->align);
1236
1237                                         /* inconsistent, but menus with labels do not look good flipped */
1238                                         block->flag |= UI_BLOCK_NO_FLIP;
1239                                 }
1240
1241                                 if (item->icon || radial) {
1242                                         uiItemL(target, item->name, item->icon);
1243
1244                                         but = block->buttons.last;
1245                                 }
1246                                 else {
1247                                         /* Do not use uiItemL here, as our root layout is a menu one, it will add a fake blank icon! */
1248                                         but = uiDefBut(
1249                                                 block, UI_BTYPE_LABEL, 0, item->name, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL,
1250                                                 0.0, 0.0, 0, 0, "");
1251                                 }
1252                                 ui_but_tip_from_enum_item(but, item);
1253                         }
1254                         else {
1255                                 if (radial) {
1256                                         /* invisible dummy button to ensure all items are always at the same position */
1257                                         uiItemS(target);
1258                                 }
1259                                 else {
1260                                         /* XXX bug here, columns draw bottom item badly */
1261                                         uiItemS(target);
1262                                 }
1263                         }
1264                 }
1265         }
1266 }
1267
1268 void uiItemsFullEnumO(
1269         uiLayout *layout, const char *opname, const char *propname, IDProperty *properties,
1270         int context, int flag)
1271 {
1272         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1273
1274         PointerRNA ptr;
1275         PropertyRNA *prop;
1276         uiBlock *block = layout->root->block;
1277
1278         if (!ot || !ot->srna) {
1279                 ui_item_disabled(layout, opname);
1280                 RNA_warning("%s '%s'", ot ? "unknown operator" : "operator missing srna", opname);
1281                 return;
1282         }
1283
1284         WM_operator_properties_create_ptr(&ptr, ot);
1285         /* so the context is passed to itemf functions (some need it) */
1286         WM_operator_properties_sanitize(&ptr, false);
1287         prop = RNA_struct_find_property(&ptr, propname);
1288
1289         /* don't let bad properties slip through */
1290         BLI_assert((prop == NULL) || (RNA_property_type(prop) == PROP_ENUM));
1291
1292         if (prop && RNA_property_type(prop) == PROP_ENUM) {
1293                 const EnumPropertyItem *item_array = NULL;
1294                 int totitem;
1295                 bool free;
1296
1297                 if (ui_layout_is_radial(layout)) {
1298                         /* XXX: While "_all()" guarantees spatial stability, it's bad when an enum has > 8 items total,
1299                          * but only a small subset will ever be shown at once (e.g. Mode Switch menu, after the
1300                          * introduction of GP editing modes)
1301                          */
1302 #if 0
1303                         RNA_property_enum_items_gettexted_all(block->evil_C, &ptr, prop, &item_array, &totitem, &free);
1304 #else
1305                         RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, &totitem, &free);
1306 #endif
1307                 }
1308                 else {
1309                         RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, &totitem, &free);
1310                 }
1311
1312                 /* add items */
1313                 uiItemsFullEnumO_items(
1314                         layout, ot, ptr, prop, properties, context, flag,
1315                         item_array, totitem);
1316
1317                 if (free) {
1318                         MEM_freeN((void *)item_array);
1319                 }
1320
1321                 /* intentionally don't touch UI_BLOCK_IS_FLIP here,
1322                  * we don't know the context this is called in */
1323         }
1324         else if (prop && RNA_property_type(prop) != PROP_ENUM) {
1325                 RNA_warning("%s.%s, not an enum type", RNA_struct_identifier(ptr.type), propname);
1326                 return;
1327         }
1328         else {
1329                 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1330                 return;
1331         }
1332 }
1333
1334 void uiItemsEnumO(uiLayout *layout, const char *opname, const char *propname)
1335 {
1336         uiItemsFullEnumO(layout, opname, propname, NULL, layout->root->opcontext, 0);
1337 }
1338
1339 /* for use in cases where we have */
1340 void uiItemEnumO_value(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
1341 {
1342         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1343         PointerRNA ptr;
1344         PropertyRNA *prop;
1345
1346         UI_OPERATOR_ERROR_RET(ot, opname, return);
1347
1348         WM_operator_properties_create_ptr(&ptr, ot);
1349
1350         /* enum lookup */
1351         if ((prop = RNA_struct_find_property(&ptr, propname))) {
1352                 /* pass */
1353         }
1354         else {
1355                 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1356                 return;
1357         }
1358
1359         RNA_property_enum_set(&ptr, prop, value);
1360
1361         /* same as uiItemEnumO */
1362         if (!name)
1363                 name = ui_menu_enumpropname(layout, &ptr, prop, value);
1364
1365         uiItemFullO_ptr(layout, ot, name, icon, ptr.data, layout->root->opcontext, 0, NULL);
1366 }
1367
1368 void uiItemEnumO_string(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, const char *value_str)
1369 {
1370         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1371         PointerRNA ptr;
1372         PropertyRNA *prop;
1373
1374         const EnumPropertyItem *item;
1375         int value;
1376         bool free;
1377
1378         UI_OPERATOR_ERROR_RET(ot, opname, return);
1379
1380         WM_operator_properties_create_ptr(&ptr, ot);
1381
1382         /* enum lookup */
1383         if ((prop = RNA_struct_find_property(&ptr, propname))) {
1384                 /* no need for translations here */
1385                 RNA_property_enum_items(layout->root->block->evil_C, &ptr, prop, &item, NULL, &free);
1386                 if (item == NULL || RNA_enum_value_from_id(item, value_str, &value) == 0) {
1387                         if (free) {
1388                                 MEM_freeN((void *)item);
1389                         }
1390                         RNA_warning("%s.%s, enum %s not found", RNA_struct_identifier(ptr.type), propname, value_str);
1391                         return;
1392                 }
1393
1394                 if (free) {
1395                         MEM_freeN((void *)item);
1396                 }
1397         }
1398         else {
1399                 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1400                 return;
1401         }
1402
1403         RNA_property_enum_set(&ptr, prop, value);
1404
1405         /* same as uiItemEnumO */
1406         if (!name)
1407                 name = ui_menu_enumpropname(layout, &ptr, prop, value);
1408
1409         uiItemFullO_ptr(layout, ot, name, icon, ptr.data, layout->root->opcontext, 0, NULL);
1410 }
1411
1412 void uiItemBooleanO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
1413 {
1414         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1415         PointerRNA ptr;
1416
1417         UI_OPERATOR_ERROR_RET(ot, opname, return);
1418
1419         WM_operator_properties_create_ptr(&ptr, ot);
1420         RNA_boolean_set(&ptr, propname, value);
1421
1422         uiItemFullO_ptr(layout, ot, name, icon, ptr.data, layout->root->opcontext, 0, NULL);
1423 }
1424
1425 void uiItemIntO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
1426 {
1427         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1428         PointerRNA ptr;
1429
1430         UI_OPERATOR_ERROR_RET(ot, opname, return);
1431
1432         WM_operator_properties_create_ptr(&ptr, ot);
1433         RNA_int_set(&ptr, propname, value);
1434
1435         uiItemFullO_ptr(layout, ot, name, icon, ptr.data, layout->root->opcontext, 0, NULL);
1436 }
1437
1438 void uiItemFloatO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, float value)
1439 {
1440         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1441         PointerRNA ptr;
1442
1443         UI_OPERATOR_ERROR_RET(ot, opname, return);
1444
1445         WM_operator_properties_create_ptr(&ptr, ot);
1446         RNA_float_set(&ptr, propname, value);
1447
1448         uiItemFullO_ptr(layout, ot, name, icon, ptr.data, layout->root->opcontext, 0, NULL);
1449 }
1450
1451 void uiItemStringO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, const char *value)
1452 {
1453         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
1454         PointerRNA ptr;
1455
1456         UI_OPERATOR_ERROR_RET(ot, opname, return);
1457
1458         WM_operator_properties_create_ptr(&ptr, ot);
1459         RNA_string_set(&ptr, propname, value);
1460
1461         uiItemFullO_ptr(layout, ot, name, icon, ptr.data, layout->root->opcontext, 0, NULL);
1462 }
1463
1464 void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
1465 {
1466         uiItemFullO(layout, opname, name, icon, NULL, layout->root->opcontext, 0, NULL);
1467 }
1468
1469 /* RNA property items */
1470
1471 static void ui_item_rna_size(
1472         uiLayout *layout, const char *name, int icon,
1473         PointerRNA *ptr, PropertyRNA *prop,
1474         int index, bool icon_only, bool compact,
1475         int *r_w, int *r_h)
1476 {
1477         PropertyType type;
1478         PropertySubType subtype;
1479         int len, w = 0, h;
1480
1481         /* arbitrary extended width by type */
1482         type = RNA_property_type(prop);
1483         subtype = RNA_property_subtype(prop);
1484         len = RNA_property_array_length(ptr, prop);
1485
1486         if (!name[0] && !icon_only) {
1487                 if (ELEM(type, PROP_STRING, PROP_POINTER)) {
1488                         name = "non-empty text";
1489                 }
1490                 else if (type == PROP_BOOLEAN) {
1491                         icon = ICON_DOT;
1492                 }
1493                 else if (type == PROP_ENUM) {
1494                         /* Find the longest enum item name, instead of using a dummy text! */
1495                         const EnumPropertyItem *item, *item_array;
1496                         bool free;
1497
1498                         RNA_property_enum_items_gettexted(layout->root->block->evil_C, ptr, prop, &item_array, NULL, &free);
1499                         for (item = item_array; item->identifier; item++) {
1500                                 if (item->identifier[0]) {
1501                                         w = max_ii(w, ui_text_icon_width(layout, item->name, item->icon, compact));
1502                                 }
1503                         }
1504                         if (free) {
1505                                 MEM_freeN((void *)item_array);
1506                         }
1507                 }
1508         }
1509
1510         if (!w) {
1511                 if (type == PROP_ENUM && icon_only) {
1512                         w = ui_text_icon_width(layout, "", ICON_BLANK1, compact);
1513                         if (index != RNA_ENUM_VALUE)
1514                                 w += 0.6f * UI_UNIT_X;
1515                 }
1516                 else {
1517                         /* not compact for float/int buttons, looks too squashed */
1518                         w = ui_text_icon_width(layout, name, icon, ELEM(type, PROP_FLOAT, PROP_INT) ? false : compact);
1519                 }
1520         }
1521         h = UI_UNIT_Y;
1522
1523         /* increase height for arrays */
1524         if (index == RNA_NO_INDEX && len > 0) {
1525                 if (!name[0] && icon == ICON_NONE)
1526                         h = 0;
1527                 if (layout->item.flag & UI_ITEM_PROP_SEP)
1528                         h = 0;
1529                 if (ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER))
1530                         h += 2 * UI_UNIT_Y;
1531                 else if (subtype == PROP_MATRIX)
1532                         h += ceilf(sqrtf(len)) * UI_UNIT_Y;
1533                 else
1534                         h += len * UI_UNIT_Y;
1535         }
1536         else if (ui_layout_variable_size(layout)) {
1537                 if (type == PROP_BOOLEAN && name[0])
1538                         w += UI_UNIT_X / 5;
1539                 else if (type == PROP_ENUM && !icon_only)
1540                         w += UI_UNIT_X / 4;
1541                 else if (type == PROP_FLOAT || type == PROP_INT)
1542                         w += UI_UNIT_X * 3;
1543         }
1544
1545         *r_w = w;
1546         *r_h = h;
1547 }
1548
1549 void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index, int value, int flag, const char *name, int icon)
1550 {
1551         uiBlock *block = layout->root->block;
1552         uiBut *but = NULL;
1553         PropertyType type;
1554         char namestr[UI_MAX_NAME_STR];
1555         int len, w, h;
1556         bool slider, toggle, expand, icon_only, no_bg, compact;
1557         bool is_array;
1558         const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
1559
1560 #ifdef UI_PROP_DECORATE
1561         struct {
1562                 bool use_prop_decorate;
1563                 int len;
1564                 uiLayout *layout;
1565                 uiBut *but;
1566         } ui_decorate = {
1567                 .use_prop_decorate = (
1568                         ((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) &&
1569                         (use_prop_sep && ptr->id.data && id_can_have_animdata(ptr->id.data))),
1570         };
1571 #endif  /* UI_PROP_DECORATE */
1572
1573         UI_block_layout_set_current(block, layout);
1574
1575         /* retrieve info */
1576         type = RNA_property_type(prop);
1577         is_array = RNA_property_array_check(prop);
1578         len = (is_array) ? RNA_property_array_length(ptr, prop) : 0;
1579
1580         /* set name and icon */
1581         if (!name) {
1582                 if ((flag & UI_ITEM_R_ICON_ONLY) == 0) {
1583                         name = RNA_property_ui_name(prop);
1584                 }
1585                 else {
1586                         name = "";
1587                 }
1588         }
1589
1590         if (icon == ICON_NONE)
1591                 icon = RNA_property_ui_icon(prop);
1592
1593         if (flag & UI_ITEM_R_ICON_ONLY) {
1594                 /* pass */
1595         }
1596         else if (ELEM(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_POINTER)) {
1597                 if (use_prop_sep == false) {
1598                         name = ui_item_name_add_colon(name, namestr);
1599                 }
1600         }
1601         else if (type == PROP_BOOLEAN && is_array && index == RNA_NO_INDEX) {
1602                 if (use_prop_sep == false) {
1603                         name = ui_item_name_add_colon(name, namestr);
1604                 }
1605         }
1606         else if (type == PROP_ENUM && index != RNA_ENUM_VALUE) {
1607                 if (flag & UI_ITEM_R_COMPACT) {
1608                         name = "";
1609                 }
1610                 else {
1611                         if (use_prop_sep == false) {
1612                                 name = ui_item_name_add_colon(name, namestr);
1613                         }
1614                 }
1615         }
1616
1617         /* menus and pie-menus don't show checkbox without this */
1618         if ((layout->root->type == UI_LAYOUT_MENU) ||
1619             /* use checkboxes only as a fallback in pie-menu's, when no icon is defined */
1620             ((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE)))
1621         {
1622                 int prop_flag = RNA_property_flag(prop);
1623                 if (type == PROP_BOOLEAN && ((is_array == false) || (index != RNA_NO_INDEX))) {
1624                         if (prop_flag & PROP_ICONS_CONSECUTIVE) {
1625                                 icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
1626                         }
1627                         else if (is_array) {
1628                                 icon = (RNA_property_boolean_get_index(ptr, prop, index)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
1629                         }
1630                         else {
1631                                 icon = (RNA_property_boolean_get(ptr, prop)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
1632                         }
1633                 }
1634                 else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) {
1635                         int enum_value = RNA_property_enum_get(ptr, prop);
1636                         if (prop_flag & PROP_ICONS_CONSECUTIVE) {
1637                                 icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
1638                         }
1639                         else if (prop_flag & PROP_ENUM_FLAG) {
1640                                 icon = (enum_value & value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
1641                         }
1642                         else {
1643                                 icon = (enum_value == value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
1644                         }
1645                 }
1646         }
1647
1648         if ((type == PROP_ENUM) && (RNA_property_flag(prop) & PROP_ENUM_FLAG)) {
1649                 flag |= UI_ITEM_R_EXPAND;
1650         }
1651
1652         slider = (flag & UI_ITEM_R_SLIDER) != 0;
1653         toggle = (flag & UI_ITEM_R_TOGGLE) != 0;
1654         expand = (flag & UI_ITEM_R_EXPAND) != 0;
1655         icon_only = (flag & UI_ITEM_R_ICON_ONLY) != 0;
1656         no_bg = (flag & UI_ITEM_R_NO_BG) != 0;
1657         compact = (flag & UI_ITEM_R_COMPACT) != 0;
1658
1659         /* get size */
1660         ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, compact, &w, &h);
1661
1662         int prev_emboss = layout->emboss;
1663         if (no_bg) {
1664                 layout->emboss = UI_EMBOSS_NONE;
1665         }
1666
1667         /* Split the label / property. */
1668         uiLayout *layout_parent = layout;
1669         if (use_prop_sep) {
1670                 uiLayout *layout_row = NULL;
1671 #ifdef UI_PROP_DECORATE
1672                 if (ui_decorate.use_prop_decorate) {
1673                         layout_row = uiLayoutRow(layout, true);
1674                         layout_row->space = 0;
1675                         ui_decorate.len = max_ii(1, len);
1676                 }
1677 #endif  /* UI_PROP_DECORATE */
1678
1679                 if (name[0] == '\0') {
1680                         /* Ensure we get a column when text is not set. */
1681                         layout = uiLayoutColumn(layout_row ? layout_row : layout, true);
1682                         layout->space = 0;
1683                 }
1684                 else {
1685                         const PropertySubType subtype = RNA_property_subtype(prop);
1686                         uiLayout *layout_split;
1687 #ifdef UI_PROP_SEP_ICON_WIDTH_EXCEPTION
1688                         if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) {
1689                                 layout_split = uiLayoutRow(layout_row ? layout_row : layout, true);
1690                         }
1691                         else
1692 #endif  /* UI_PROP_SEP_ICON_WIDTH_EXCEPTION */
1693                         {
1694                                 layout_split = uiLayoutSplit(
1695                                         layout_row ? layout_row : layout,
1696                                         UI_ITEM_PROP_SEP_DIVIDE, true);
1697                         }
1698                         layout_split->space = 0;
1699                         uiLayout *layout_sub = uiLayoutColumn(layout_split, true);
1700                         layout_sub->space = 0;
1701
1702                         if ((index == RNA_NO_INDEX && is_array) &&
1703                             ((!expand && ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA, PROP_DIRECTION)) == 0))
1704                         {
1705                                 char name_with_suffix[UI_MAX_DRAW_STR + 2];
1706                                 char str[2] = {'\0'};
1707                                 for (int a = 0; a < len; a++) {
1708                                         str[0] = RNA_property_array_item_char(prop, a);
1709                                         const bool use_prefix = (a == 0 && name && name[0]);
1710                                         if (use_prefix) {
1711                                                 char *s = name_with_suffix;
1712                                                 s += STRNCPY_RLEN(name_with_suffix, name);
1713                                                 *s++ = ' ';
1714                                                 *s++ = str[0];
1715                                                 *s++ = '\0';
1716                                         }
1717                                         but = uiDefBut(
1718                                                 block, UI_BTYPE_LABEL, 0, use_prefix ? name_with_suffix : str,
1719                                                 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1720                                         but->drawflag |= UI_BUT_TEXT_RIGHT;
1721                                         but->drawflag &= ~UI_BUT_TEXT_LEFT;
1722                                 }
1723                         }
1724                         else {
1725                                 if (name) {
1726                                         but = uiDefBut(
1727                                                 block, UI_BTYPE_LABEL, 0, name,
1728                                                 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1729                                         but->drawflag |= UI_BUT_TEXT_RIGHT;
1730                                         but->drawflag &= ~UI_BUT_TEXT_LEFT;
1731                                 }
1732                         }
1733
1734                         /* Hack to add further items in a row into the second part of
1735                          * the split layout, so the label part keeps a fixed size. */
1736                         if (layout_parent && layout_parent->item.type == ITEM_LAYOUT_ROW) {
1737                                 layout_split = uiLayoutRow(layout_split, true);
1738                                 layout_parent->child_items_layout = layout_split;
1739                         }
1740
1741                         /* Watch out! We can only write into the new layout now. */
1742                         if ((type == PROP_ENUM) && (flag & UI_ITEM_R_EXPAND)) {
1743                                 /* Expanded enums each have their own name. */
1744
1745                                 /* Often expanded enum's are better arranged into a row, so check the existing layout. */
1746                                 if (ui_layout_local_dir(layout) == UI_LAYOUT_HORIZONTAL) {
1747                                         layout = uiLayoutRow(layout_split, true);
1748                                 }
1749                                 else {
1750                                         layout = uiLayoutColumn(layout_split, true);
1751                                 }
1752                         }
1753                         else {
1754                                 name = "";
1755                                 layout = uiLayoutColumn(layout_split, true);
1756                         }
1757                         layout->space = 0;
1758
1759 #ifdef UI_PROP_SEP_ICON_WIDTH_EXCEPTION
1760                         if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) {
1761                                 w = UI_UNIT_X;
1762                         }
1763 #endif  /* UI_PROP_SEP_ICON_WIDTH_EXCEPTION */
1764                 }
1765
1766 #ifdef UI_PROP_DECORATE
1767                 if (ui_decorate.use_prop_decorate) {
1768                         ui_decorate.layout = uiLayoutColumn(layout_row, true);
1769                         ui_decorate.layout->space = 0;
1770                         UI_block_layout_set_current(block, layout);
1771                         ui_decorate.but = block->buttons.last;
1772
1773                         /* Clear after. */
1774                         layout->item.flag |= UI_ITEM_PROP_DECORATE_NO_PAD;
1775                 }
1776 #endif  /* UI_PROP_DECORATE */
1777         }
1778         /* End split. */
1779
1780         /* array property */
1781         if (index == RNA_NO_INDEX && is_array) {
1782                 ui_item_array(
1783                         layout, block, name, icon, ptr, prop, len, 0, 0, w, h,
1784                         expand, slider, toggle, icon_only, compact, !use_prop_sep);
1785         }
1786         /* enum item */
1787         else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) {
1788                 if (icon && name[0] && !icon_only)
1789                         uiDefIconTextButR_prop(block, UI_BTYPE_ROW, 0, icon, name, 0, 0, w, h, ptr, prop, -1, 0, value, -1, -1, NULL);
1790                 else if (icon)
1791                         uiDefIconButR_prop(block, UI_BTYPE_ROW, 0, icon, 0, 0, w, h, ptr, prop, -1, 0, value, -1, -1, NULL);
1792                 else
1793                         uiDefButR_prop(block, UI_BTYPE_ROW, 0, name, 0, 0, w, h, ptr, prop, -1, 0, value, -1, -1, NULL);
1794         }
1795         /* expanded enum */
1796         else if (type == PROP_ENUM && expand) {
1797                 ui_item_enum_expand(layout, block, ptr, prop, name, h, icon_only);
1798         }
1799         /* property with separate label */
1800         else if (type == PROP_ENUM || type == PROP_STRING || type == PROP_POINTER) {
1801                 but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag);
1802                 ui_but_add_search(but, ptr, prop, NULL, NULL);
1803
1804                 if (layout->redalert)
1805                         UI_but_flag_enable(but, UI_BUT_REDALERT);
1806         }
1807         /* single button */
1808         else {
1809                 but = uiDefAutoButR(block, ptr, prop, index, name, icon, 0, 0, w, h);
1810
1811                 if (slider && but->type == UI_BTYPE_NUM)
1812                         but->type = UI_BTYPE_NUM_SLIDER;
1813
1814                 if (toggle && but->type == UI_BTYPE_CHECKBOX)
1815                         but->type = UI_BTYPE_TOGGLE;
1816
1817                 if (layout->redalert)
1818                         UI_but_flag_enable(but, UI_BUT_REDALERT);
1819         }
1820
1821         /* Mark non-embossed textfields inside a listbox. */
1822         if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) && (but->dt & UI_EMBOSS_NONE)) {
1823                 UI_but_flag_enable(but, UI_BUT_LIST_ITEM);
1824         }
1825
1826 #ifdef UI_PROP_DECORATE
1827         if (ui_decorate.use_prop_decorate) {
1828                 const bool is_anim = RNA_property_animateable(ptr, prop);
1829                 uiBut *but_decorate = ui_decorate.but ? ui_decorate.but->next : block->buttons.first;
1830                 uiLayout *layout_col = uiLayoutColumn(ui_decorate.layout, false);
1831                 layout_col->space = 0;
1832                 layout_col->emboss = UI_EMBOSS_NONE;
1833                 int i;
1834                 for (i = 0; i < ui_decorate.len && but_decorate; i++) {
1835                         /* The icons are set in 'ui_but_anim_flag' */
1836                         if (is_anim) {
1837                                 but = uiDefIconBut(
1838                                         block, UI_BTYPE_BUT, 0, ICON_DOT, 0, 0, UI_UNIT_X, UI_UNIT_Y,
1839                                         NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Animate property"));
1840                                 UI_but_func_set(but, ui_but_anim_decorate_cb, but, NULL);
1841                                 but->flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK;
1842                         }
1843                         else {
1844                                 /* We may show other information here in future, for now use empty space. */
1845                                 but = uiDefIconBut(
1846                                         block, UI_BTYPE_BUT, 0, ICON_BLANK1, 0, 0, UI_UNIT_X, UI_UNIT_Y,
1847                                         NULL, 0.0, 0.0, 0.0, 0.0, "");
1848                                 but->flag |= UI_BUT_DISABLED;
1849                         }
1850                         /* Order the decorator after the button we decorate, this is used so we can always
1851                          * do a quick lookup. */
1852                         BLI_remlink(&block->buttons, but);
1853                         BLI_insertlinkafter(&block->buttons, but_decorate, but);
1854                         but_decorate = but->next;
1855                 }
1856                 BLI_assert(ELEM(i, 1, ui_decorate.len));
1857
1858                 layout->item.flag &= ~UI_ITEM_PROP_DECORATE_NO_PAD;
1859         }
1860 #endif  /* UI_PROP_DECORATE */
1861
1862         if (no_bg) {
1863                 layout->emboss = prev_emboss;
1864         }
1865
1866         /* ensure text isn't added to icon_only buttons */
1867         if (but && icon_only) {
1868                 BLI_assert(but->str[0] == '\0');
1869         }
1870
1871 }
1872
1873 void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
1874 {
1875         PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
1876
1877         if (!prop) {
1878                 ui_item_disabled(layout, propname);
1879                 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1880                 return;
1881         }
1882
1883         uiItemFullR(layout, ptr, prop, RNA_NO_INDEX, 0, flag, name, icon);
1884 }
1885
1886 void uiItemEnumR_prop(uiLayout *layout, const char *name, int icon, struct PointerRNA *ptr, PropertyRNA *prop, int value)
1887 {
1888         if (RNA_property_type(prop) != PROP_ENUM) {
1889                 const char *propname = RNA_property_identifier(prop);
1890                 ui_item_disabled(layout, propname);
1891                 RNA_warning("property not an enum: %s.%s", RNA_struct_identifier(ptr->type), propname);
1892                 return;
1893         }
1894
1895         uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, value, 0, name, icon);
1896 }
1897
1898 void uiItemEnumR(uiLayout *layout, const char *name, int icon, struct PointerRNA *ptr, const char *propname, int value)
1899 {
1900         PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
1901
1902         if (prop == NULL) {
1903                 ui_item_disabled(layout, propname);
1904                 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1905                 return;
1906         }
1907
1908         uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, value, 0, name, icon);
1909 }
1910
1911
1912 void uiItemEnumR_string_prop(
1913         uiLayout *layout, struct PointerRNA *ptr, PropertyRNA *prop,
1914         const char *value, const char *name, int icon)
1915 {
1916         const EnumPropertyItem *item;
1917         int ivalue, a;
1918         bool free;
1919
1920         if (UNLIKELY(RNA_property_type(prop) != PROP_ENUM)) {
1921                 const char *propname = RNA_property_identifier(prop);
1922                 ui_item_disabled(layout, propname);
1923                 RNA_warning("not an enum property: %s.%s", RNA_struct_identifier(ptr->type), propname);
1924                 return;
1925         }
1926
1927         RNA_property_enum_items(layout->root->block->evil_C, ptr, prop, &item, NULL, &free);
1928
1929         if (!RNA_enum_value_from_id(item, value, &ivalue)) {
1930                 const char *propname = RNA_property_identifier(prop);
1931                 if (free) {
1932                         MEM_freeN((void *)item);
1933                 }
1934                 ui_item_disabled(layout, propname);
1935                 RNA_warning("enum property value not found: %s", value);
1936                 return;
1937         }
1938
1939         for (a = 0; item[a].identifier; a++) {
1940                 if (item[a].value == ivalue) {
1941                         const char *item_name = name ? name : CTX_IFACE_(RNA_property_translation_context(prop), item[a].name);
1942                         const int flag = item_name[0] ? 0 : UI_ITEM_R_ICON_ONLY;
1943
1944                         uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, ivalue, flag, item_name, icon ? icon : item[a].icon);
1945                         break;
1946                 }
1947         }
1948
1949         if (free) {
1950                 MEM_freeN((void *)item);
1951         }
1952 }
1953
1954 void uiItemEnumR_string(
1955         uiLayout *layout, struct PointerRNA *ptr, const char *propname,
1956         const char *value, const char *name, int icon)
1957 {
1958         PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
1959         if (UNLIKELY(prop == NULL)) {
1960                 ui_item_disabled(layout, propname);
1961                 RNA_warning("enum property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1962                 return;
1963         }
1964         uiItemEnumR_string_prop(layout, ptr, prop, value, name, icon);
1965 }
1966
1967 void uiItemsEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname)
1968 {
1969         PropertyRNA *prop;
1970         uiBlock *block = layout->root->block;
1971         uiBut *bt;
1972
1973         prop = RNA_struct_find_property(ptr, propname);
1974
1975         if (!prop) {
1976                 ui_item_disabled(layout, propname);
1977                 RNA_warning("enum property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1978                 return;
1979         }
1980
1981         if (RNA_property_type(prop) != PROP_ENUM) {
1982                 RNA_warning("not an enum property: %s.%s", RNA_struct_identifier(ptr->type), propname);
1983                 return;
1984         }
1985         else {
1986                 const EnumPropertyItem *item;
1987                 int totitem, i;
1988                 bool free;
1989                 uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
1990                 uiLayout *column = uiLayoutColumn(split, false);
1991
1992                 RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item, &totitem, &free);
1993
1994                 for (i = 0; i < totitem; i++) {
1995                         if (item[i].identifier[0]) {
1996                                 uiItemEnumR_prop(column, item[i].name, item[i].icon, ptr, prop, item[i].value);
1997                                 ui_but_tip_from_enum_item(block->buttons.last, &item[i]);
1998                         }
1999                         else {
2000                                 if (item[i].name) {
2001                                         if (i != 0) {
2002                                                 column = uiLayoutColumn(split, false);
2003                                                 /* inconsistent, but menus with labels do not look good flipped */
2004                                                 block->flag |= UI_BLOCK_NO_FLIP;
2005                                         }
2006
2007                                         uiItemL(column, item[i].name, ICON_NONE);
2008                                         bt = block->buttons.last;
2009                                         bt->drawflag = UI_BUT_TEXT_LEFT;
2010
2011                                         ui_but_tip_from_enum_item(bt, &item[i]);
2012                                 }
2013                                 else
2014                                         uiItemS(column);
2015                         }
2016                 }
2017
2018                 if (free) {
2019                         MEM_freeN((void *)item);
2020                 }
2021         }
2022
2023         /* intentionally don't touch UI_BLOCK_IS_FLIP here,
2024          * we don't know the context this is called in */
2025 }
2026
2027 /* Pointer RNA button with search */
2028
2029
2030 static void search_id_collection(StructRNA *ptype, PointerRNA *ptr, PropertyRNA **prop)
2031 {
2032         StructRNA *srna;
2033
2034         /* look for collection property in Main */
2035         /* Note: using global Main is OK-ish here, UI shall not access other Mains anyay... */
2036         RNA_main_pointer_create(G_MAIN, ptr);
2037
2038         *prop = NULL;
2039
2040         RNA_STRUCT_BEGIN (ptr, iprop)
2041         {
2042                 /* if it's a collection and has same pointer type, we've got it */
2043                 if (RNA_property_type(iprop) == PROP_COLLECTION) {
2044                         srna = RNA_property_pointer_type(ptr, iprop);
2045
2046                         if (ptype == srna) {
2047                                 *prop = iprop;
2048                                 break;
2049                         }
2050                 }
2051         }
2052         RNA_STRUCT_END;
2053 }
2054
2055 void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop)
2056 {
2057         StructRNA *ptype;
2058         PointerRNA sptr;
2059
2060         /* for ID's we do automatic lookup */
2061         if (!searchprop) {
2062                 if (RNA_property_type(prop) == PROP_POINTER) {
2063                         ptype = RNA_property_pointer_type(ptr, prop);
2064                         search_id_collection(ptype, &sptr, &searchprop);
2065                         searchptr = &sptr;
2066                 }
2067         }
2068
2069         /* turn button into search button */
2070         if (searchprop) {
2071                 uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__);
2072
2073                 but->type = UI_BTYPE_SEARCH_MENU;
2074                 but->hardmax = MAX2(but->hardmax, 256.0f);
2075                 but->rnasearchpoin = *searchptr;
2076                 but->rnasearchprop = searchprop;
2077                 but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT;
2078                 if (RNA_property_is_unlink(prop)) {
2079                         but->flag |= UI_BUT_VALUE_CLEAR;
2080                 }
2081
2082                 coll_search->target_ptr = *ptr;
2083                 coll_search->target_prop = prop;
2084                 coll_search->search_ptr = *searchptr;
2085                 coll_search->search_prop = searchprop;
2086                 coll_search->but_changed = &but->changed;
2087
2088                 if (RNA_property_type(prop) == PROP_ENUM) {
2089                         /* XXX, this will have a menu string,
2090                          * but in this case we just want the text */
2091                         but->str[0] = 0;
2092                 }
2093
2094                 UI_but_func_search_set(
2095                         but, ui_searchbox_create_generic, ui_rna_collection_search_cb,
2096                         coll_search, NULL, NULL);
2097                 but->free_search_arg = true;
2098         }
2099         else if (but->type == UI_BTYPE_SEARCH_MENU) {
2100                 /* In case we fail to find proper searchprop, so other code might have already set but->type to search menu... */
2101                 but->flag |= UI_BUT_DISABLED;
2102         }
2103 }
2104
2105 void uiItemPointerR_prop(
2106         uiLayout *layout,
2107         PointerRNA *ptr, PropertyRNA *prop,
2108         PointerRNA *searchptr, PropertyRNA *searchprop,
2109         const char *name, int icon)
2110 {
2111         PropertyType type;
2112         uiBut *but;
2113         uiBlock *block;
2114         StructRNA *icontype;
2115         int w, h;
2116         char namestr[UI_MAX_NAME_STR];
2117         const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
2118
2119         type = RNA_property_type(prop);
2120         if (!ELEM(type, PROP_POINTER, PROP_STRING, PROP_ENUM)) {
2121                 RNA_warning("Property %s.%s must be a pointer, string or enum",
2122                             RNA_struct_identifier(ptr->type), RNA_property_identifier(prop));
2123                 return;
2124         }
2125         if (RNA_property_type(searchprop) != PROP_COLLECTION) {
2126                 RNA_warning("search collection property is not a collection type: %s.%s",
2127                             RNA_struct_identifier(searchptr->type), RNA_property_identifier(searchprop));
2128                 return;
2129         }
2130
2131         /* get icon & name */
2132         if (icon == ICON_NONE) {
2133                 if (type == PROP_POINTER)
2134                         icontype = RNA_property_pointer_type(ptr, prop);
2135                 else
2136                         icontype = RNA_property_pointer_type(searchptr, searchprop);
2137
2138                 icon = RNA_struct_ui_icon(icontype);
2139         }
2140         if (!name)
2141                 name = RNA_property_ui_name(prop);
2142
2143         if (use_prop_sep == false) {
2144                 name = ui_item_name_add_colon(name, namestr);
2145         }
2146
2147         /* create button */
2148         block = uiLayoutGetBlock(layout);
2149
2150         ui_item_rna_size(layout, name, icon, ptr, prop, 0, 0, false, &w, &h);
2151         w += UI_UNIT_X; /* X icon needs more space */
2152         but = ui_item_with_label(layout, block, name, icon, ptr, prop, 0, 0, 0, w, h, 0);
2153
2154         ui_but_add_search(but, ptr, prop, searchptr, searchprop);
2155 }
2156
2157 void uiItemPointerR(
2158         uiLayout *layout,
2159         PointerRNA *ptr, const char *propname,
2160         PointerRNA *searchptr, const char *searchpropname,
2161         const char *name, int icon)
2162 {
2163         PropertyRNA *prop, *searchprop;
2164
2165         /* validate arguments */
2166         prop = RNA_struct_find_property(ptr, propname);
2167         if (!prop) {
2168                 RNA_warning("property not found: %s.%s",
2169                             RNA_struct_identifier(ptr->type), propname);
2170                 return;
2171         }
2172         searchprop = RNA_struct_find_property(searchptr, searchpropname);
2173         if (!searchprop) {
2174                 RNA_warning("search collection property not found: %s.%s",
2175                             RNA_struct_identifier(searchptr->type), searchpropname);
2176                 return;
2177         }
2178
2179         uiItemPointerR_prop(layout, ptr, prop, searchptr, searchprop, name, icon);
2180 }
2181
2182 /* menu item */
2183 static void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
2184 {
2185         MenuType *mt = (MenuType *)arg_mt;
2186
2187         UI_menutype_draw(C, mt, layout);
2188
2189         /* menus are created flipped (from event handling pov) */
2190         layout->root->block->flag ^= UI_BLOCK_IS_FLIP;
2191 }
2192
2193 void ui_item_paneltype_func(bContext *C, uiLayout *layout, void *arg_pt)
2194 {
2195         PanelType *pt = (PanelType *)arg_pt;
2196         UI_paneltype_draw(C, pt, layout);
2197
2198         /* panels are created flipped (from event handling pov) */
2199         layout->root->block->flag ^= UI_BLOCK_IS_FLIP;
2200 }
2201
2202 static uiBut *ui_item_menu(
2203         uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN,
2204         const char *tip, bool force_menu)
2205 {
2206         uiBlock *block = layout->root->block;
2207         uiBut *but;
2208         int w, h;
2209
2210         UI_block_layout_set_current(block, layout);
2211
2212         if (!name)
2213                 name = "";
2214         if (layout->root->type == UI_LAYOUT_MENU && !icon)
2215                 icon = ICON_BLANK1;
2216
2217         w = ui_text_icon_width(layout, name, icon, 1);
2218         h = UI_UNIT_Y;
2219
2220         if (layout->root->type == UI_LAYOUT_HEADER) { /* ugly .. */
2221                 if (icon == ICON_NONE && force_menu) {
2222                         /* pass */
2223                 }
2224                 else if (force_menu) {
2225                         w += UI_UNIT_X;
2226                 }
2227                 else {
2228                         if (name[0]) {
2229                                 w -= UI_UNIT_X / 2;
2230                         }
2231                 }
2232         }
2233
2234         if (name[0] && icon)
2235                 but = uiDefIconTextMenuBut(block, func, arg, icon, name, 0, 0, w, h, tip);
2236         else if (icon)
2237                 but = uiDefIconMenuBut(block, func, arg, icon, 0, 0, w, h, tip);
2238         else
2239                 but = uiDefMenuBut(block, func, arg, name, 0, 0, w, h, tip);
2240
2241         if (argN) {
2242                 /* ugly .. */
2243                 if (arg != argN) {
2244                         but->poin = (char *)but;
2245                 }
2246                 but->func_argN = argN;
2247         }
2248
2249         if (ELEM(layout->root->type, UI_LAYOUT_PANEL, UI_LAYOUT_TOOLBAR) ||
2250             (force_menu && layout->root->type != UI_LAYOUT_MENU))  /* We never want a dropdown in menu! */
2251         {
2252                 UI_but_type_set_menu_from_pulldown(but);
2253         }
2254
2255         return but;
2256 }
2257
2258 void uiItemM(uiLayout *layout, const char *menuname, const char *name, int icon)
2259 {
2260         MenuType *mt;
2261
2262         mt = WM_menutype_find(menuname, false);
2263
2264         if (mt == NULL) {
2265                 RNA_warning("not found %s", menuname);
2266                 return;
2267         }
2268
2269         if (!name) {
2270                 name = CTX_IFACE_(mt->translation_context, mt->label);
2271         }
2272
2273         if (layout->root->type == UI_LAYOUT_MENU && !icon)
2274                 icon = ICON_BLANK1;
2275
2276         ui_item_menu(
2277                 layout, name, icon, ui_item_menutype_func, mt, NULL,
2278                 mt->description ? TIP_(mt->description) : "", false);
2279 }
2280
2281 /* popover */
2282 void uiItemPopoverPanel_ptr(uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon)
2283 {
2284         if (!name) {
2285                 name = CTX_IFACE_(pt->translation_context, pt->label);
2286         }
2287
2288         if (layout->root->type == UI_LAYOUT_MENU && !icon) {
2289                 icon = ICON_BLANK1;
2290         }
2291
2292         const bool ok = (pt->poll == NULL) || pt->poll(C, pt);
2293         if (ok && (pt->draw_header != NULL)) {
2294                 layout = uiLayoutRow(layout, true);
2295                 Panel panel = {
2296                         .type = pt,
2297                         .layout = layout,
2298                         .flag = PNL_POPOVER,
2299                 };
2300                 pt->draw_header(C, &panel);
2301         }
2302         uiBut *but = ui_item_menu(layout, name, icon, ui_item_paneltype_func, pt, NULL, NULL, true);
2303         but->type = UI_BTYPE_POPOVER;
2304         if (!ok) {
2305                 but->flag |= UI_BUT_DISABLED;
2306         }
2307 }
2308
2309 void uiItemPopoverPanel(
2310         uiLayout *layout, bContext *C,
2311         const char *panel_type, const char *name, int icon)
2312 {
2313         PanelType *pt = WM_paneltype_find(panel_type, true);
2314         if (pt == NULL) {
2315                 RNA_warning("Panel type not found '%s'", panel_type);
2316                 return;
2317         }
2318         uiItemPopoverPanel_ptr(layout, C, pt, name, icon);
2319 }
2320
2321 void uiItemPopoverPanelFromGroup(
2322         uiLayout *layout, bContext *C,
2323         int space_id, int region_id, const char *context, const char *category)
2324 {
2325         SpaceType *st = BKE_spacetype_from_id(space_id);
2326         if (st == NULL) {
2327                 RNA_warning("space type not found %d", space_id);
2328                 return;
2329         }
2330         ARegionType *art = BKE_regiontype_from_id(st, region_id);
2331         if (art == NULL) {
2332                 RNA_warning("region type not found %d", region_id);
2333                 return;
2334         }
2335
2336         for (PanelType *pt = art->paneltypes.first; pt; pt = pt->next) {
2337                 /* Causes too many panels, check context. */
2338                 if (pt->parent_id[0] == '\0') {
2339                         if (/* (*context == '\0') || */ STREQ(pt->context, context)) {
2340                                 if ((*category == '\0') || STREQ(pt->category, category)) {
2341                                         if (pt->poll == NULL || pt->poll(C, pt)) {
2342                                                 uiItemPopoverPanel_ptr(layout, C, pt, NULL, ICON_NONE);
2343                                         }
2344                                 }
2345                         }
2346                 }
2347         }
2348 }
2349
2350
2351 /* label item */
2352 static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon)
2353 {
2354         uiBlock *block = layout->root->block;
2355         uiBut *but;
2356         int w;
2357
2358         UI_block_layout_set_current(block, layout);
2359
2360         if (!name)
2361                 name = "";
2362         if (layout->root->type == UI_LAYOUT_MENU && !icon)
2363                 icon = ICON_BLANK1;
2364
2365         w = ui_text_icon_width(layout, name, icon, 0);
2366
2367         if (icon && name[0]) {
2368                 but = uiDefIconTextBut(block, UI_BTYPE_LABEL, 0, icon, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, NULL);
2369         }
2370         else if (icon) {
2371                 but = uiDefIconBut(block, UI_BTYPE_LABEL, 0, icon, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, NULL);
2372         }
2373         else {
2374                 but = uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, NULL);
2375         }
2376
2377         /* to compensate for string size padding in ui_text_icon_width,
2378          * make text aligned right if the layout is aligned right.
2379          */
2380         if (uiLayoutGetAlignment(layout) == UI_LAYOUT_ALIGN_RIGHT) {
2381                 but->drawflag &= ~UI_BUT_TEXT_LEFT;     /* default, needs to be unset */
2382                 but->drawflag |= UI_BUT_TEXT_RIGHT;
2383         }
2384
2385         /* Mark as a label inside a listbox. */
2386         if (block->flag & UI_BLOCK_LIST_ITEM) {
2387                 but->flag |= UI_BUT_LIST_ITEM;
2388         }
2389
2390         if (layout->redalert) {
2391                 UI_but_flag_enable(but, UI_BUT_REDALERT);
2392         }
2393
2394         return but;
2395 }
2396
2397 void uiItemL(uiLayout *layout, const char *name, int icon)
2398 {
2399         uiItemL_(layout, name, icon);
2400 }
2401
2402 void uiItemLDrag(uiLayout *layout, PointerRNA *ptr, const char *name, int icon)
2403 {
2404         uiBut *but = uiItemL_(layout, name, icon);
2405
2406         if (ptr && ptr->type)
2407                 if (RNA_struct_is_ID(ptr->type))
2408                         UI_but_drag_set_id(but, ptr->id.data);
2409 }
2410
2411
2412 /* value item */
2413 void uiItemV(uiLayout *layout, const char *name, int icon, int argval)
2414 {
2415         /* label */
2416         uiBlock *block = layout->root->block;
2417         int *retvalue = (block->handle) ? &block->handle->retvalue : NULL;
2418         int w;
2419
2420         UI_block_layout_set_current(block, layout);
2421
2422         if (!name)
2423                 name = "";
2424         if (layout->root->type == UI_LAYOUT_MENU && !icon)
2425                 icon = ICON_BLANK1;
2426
2427         w = ui_text_icon_width(layout, name, icon, 0);
2428
2429         if (icon && name[0])
2430                 uiDefIconTextButI(block, UI_BTYPE_BUT, argval, icon, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
2431         else if (icon)
2432                 uiDefIconButI(block, UI_BTYPE_BUT, argval, icon, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
2433         else
2434                 uiDefButI(block, UI_BTYPE_BUT, argval, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
2435 }
2436
2437 /* separator item */
2438 void uiItemS_ex(uiLayout *layout, float factor)
2439 {
2440         uiBlock *block = layout->root->block;
2441         bool is_menu = ui_block_is_menu(block);
2442         int space = (is_menu) ? 0.45f * UI_UNIT_X : 0.3f * UI_UNIT_X;
2443         space *= factor;
2444
2445         UI_block_layout_set_current(block, layout);
2446         uiDefBut(block, (is_menu) ? UI_BTYPE_SEPR_LINE : UI_BTYPE_SEPR, 0, "", 0, 0, space, space, NULL, 0.0, 0.0, 0, 0, "");
2447 }
2448
2449 /* separator item */
2450 void uiItemS(uiLayout *layout)
2451 {
2452         uiItemS_ex(layout, 1.0f);
2453 }
2454
2455 /* Flexible spacing. */
2456 void uiItemSpacer(uiLayout *layout)
2457 {
2458         uiBlock *block = layout->root->block;
2459         const bool is_popup = ui_block_is_popup_any(block);
2460
2461         if (is_popup) {
2462                 printf("Error: separator_spacer() not supported in popups.\n");
2463                 return;
2464         }
2465
2466         if (block->direction & UI_DIR_RIGHT) {
2467                 printf("Error: separator_spacer() only supported in horizontal blocks.\n");
2468                 return;
2469         }
2470
2471         UI_block_layout_set_current(block, layout);
2472         uiDefBut(block, UI_BTYPE_SEPR_SPACER, 0, "", 0, 0, 0.3f * UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2473 }
2474
2475 /* level items */
2476 void uiItemMenuF(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg)
2477 {
2478         if (!func)
2479                 return;
2480
2481         ui_item_menu(layout, name, icon, func, arg, NULL, "", false);
2482 }
2483
2484 /**
2485  * Version of #uiItemMenuF that free's `argN`.
2486  */
2487 void uiItemMenuFN(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *argN)
2488 {
2489         if (!func)
2490                 return;
2491
2492         /* Second 'argN' only ensures it gets freed. */
2493         ui_item_menu(layout, name, icon, func, argN, argN, "", false);
2494 }
2495
2496 typedef struct MenuItemLevel {
2497         int opcontext;
2498         /* don't use pointers to the strings because python can dynamically
2499          * allocate strings and free before the menu draws, see [#27304] */
2500         char opname[OP_MAX_TYPENAME];
2501         char propname[MAX_IDPROP_NAME];
2502         PointerRNA rnapoin;
2503 } MenuItemLevel;
2504
2505 static void menu_item_enum_opname_menu(bContext *UNUSED(C), uiLayout *layout, void *arg)
2506 {
2507         MenuItemLevel *lvl = (MenuItemLevel *)(((uiBut *)arg)->func_argN);
2508
2509         uiLayoutSetOperatorContext(layout, lvl->opcontext);
2510         uiItemsEnumO(layout, lvl->opname, lvl->propname);
2511
2512         layout->root->block->flag |= UI_BLOCK_IS_FLIP;
2513
2514         /* override default, needed since this was assumed pre 2.70 */
2515         UI_block_direction_set(layout->root->block, UI_DIR_DOWN);
2516 }
2517
2518 void uiItemMenuEnumO_ptr(
2519         uiLayout *layout, bContext *C, wmOperatorType *ot, const char *propname,
2520         const char *name, int icon)
2521 {
2522         MenuItemLevel *lvl;
2523         uiBut *but;
2524
2525         /* Caller must check */
2526         BLI_assert(ot->srna != NULL);
2527
2528         if (name == NULL) {
2529                 name = RNA_struct_ui_name(ot->srna);
2530         }
2531
2532         if (layout->root->type == UI_LAYOUT_MENU && !icon)
2533                 icon = ICON_BLANK1;
2534
2535         lvl = MEM_callocN(sizeof(MenuItemLevel), "MenuItemLevel");
2536         BLI_strncpy(lvl->opname, ot->idname, sizeof(lvl->opname));
2537         BLI_strncpy(lvl->propname, propname, sizeof(lvl->propname));
2538         lvl->opcontext = layout->root->opcontext;
2539
2540         but = ui_item_menu(
2541                 layout, name, icon, menu_item_enum_opname_menu, NULL, lvl,
2542                 RNA_struct_ui_description(ot->srna), true);
2543
2544         /* add hotkey here, lower UI code can't detect it */
2545         if ((layout->root->block->flag & UI_BLOCK_LOOP) &&
2546             (ot->prop && ot->invoke))
2547         {
2548                 char keybuf[128];
2549                 if (WM_key_event_operator_string(
2550                             C, ot->idname, layout->root->opcontext, NULL, false,
2551                             keybuf, sizeof(keybuf)))
2552                 {
2553                         ui_but_add_shortcut(but, keybuf, false);
2554                 }
2555         }
2556 }
2557
2558 void uiItemMenuEnumO(
2559         uiLayout *layout, bContext *C, const char *opname, const char *propname,
2560         const char *name, int icon)
2561 {
2562         wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */
2563
2564         UI_OPERATOR_ERROR_RET(ot, opname, return);
2565
2566         if (!ot->srna) {
2567                 ui_item_disabled(layout, opname);
2568                 RNA_warning("operator missing srna '%s'", opname);
2569                 return;
2570         }
2571
2572         uiItemMenuEnumO_ptr(layout, C, ot, propname, name, icon);
2573 }
2574
2575 static void menu_item_enum_rna_menu(bContext *UNUSED(C), uiLayout *layout, void *arg)
2576 {
2577         MenuItemLevel *lvl = (MenuItemLevel *)(((uiBut *)arg)->func_argN);
2578
2579         uiLayoutSetOperatorContext(layout, lvl->opcontext);
2580         uiItemsEnumR(layout, &lvl->rnapoin, lvl->propname);
2581         layout->root->block->flag |= UI_BLOCK_IS_FLIP;
2582 }
2583
2584 void uiItemMenuEnumR_prop(uiLayout *layout, struct PointerRNA *ptr, PropertyRNA *prop, const char *name, int icon)
2585 {
2586         MenuItemLevel *lvl;
2587
2588         if (!name)
2589                 name = RNA_property_ui_name(prop);
2590         if (layout->root->type == UI_LAYOUT_MENU && !icon)
2591                 icon = ICON_BLANK1;
2592
2593         lvl = MEM_callocN(sizeof(MenuItemLevel), "MenuItemLevel");
2594         lvl->rnapoin = *ptr;
2595         BLI_strncpy(lvl->propname, RNA_property_identifier(prop), sizeof(lvl->propname));
2596         lvl->opcontext = layout->root->opcontext;
2597
2598         ui_item_menu(layout, name, icon, menu_item_enum_rna_menu, NULL, lvl, RNA_property_description(prop), false);
2599 }
2600
2601 void uiItemMenuEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name, int icon)
2602 {
2603         PropertyRNA *prop;
2604
2605         prop = RNA_struct_find_property(ptr, propname);
2606         if (!prop) {
2607                 ui_item_disabled(layout, propname);
2608                 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
2609                 return;
2610         }
2611
2612         uiItemMenuEnumR_prop(layout, ptr, prop, name, icon);
2613 }
2614
2615 void uiItemTabsEnumR_prop(uiLayout *layout, bContext *C, PointerRNA *ptr, PropertyRNA *prop, bool icon_only)
2616 {
2617         uiBlock *block = layout->root->block;
2618
2619         UI_block_layout_set_current(block, layout);
2620         ui_item_enum_expand_tabs(layout, C, block, ptr, prop, NULL, UI_UNIT_Y, icon_only);
2621 }
2622
2623 /**************************** Layout Items ***************************/
2624
2625 /* single-row layout */
2626 static void ui_litem_estimate_row(uiLayout *litem)
2627 {
2628         uiItem *item;
2629         int itemw, itemh;
2630         bool min_size_flag = true;
2631
2632         litem->w = 0;
2633         litem->h = 0;
2634
2635         for (item = litem->items.first; item; item = item->next) {
2636                 ui_item_size(item, &itemw, &itemh);
2637
2638                 min_size_flag = min_size_flag && (item->flag & UI_ITEM_MIN);
2639
2640                 litem->w += itemw;
2641                 litem->h = MAX2(itemh, litem->h);
2642
2643                 if (item->next)
2644                         litem->w += litem->space;
2645         }
2646
2647         if (min_size_flag) {
2648                 litem->item.flag |= UI_ITEM_MIN;
2649         }
2650 }
2651
2652 static int ui_litem_min_width(int itemw)
2653 {
2654         return MIN2(2 * UI_UNIT_X, itemw);
2655 }
2656
2657 static void ui_litem_layout_row(uiLayout *litem)
2658 {
2659         uiItem *item, *last_free_item = NULL;
2660         int x, y, w, tot, totw, neww, newtotw, itemw, minw, itemh, offset;
2661         int fixedw, freew, fixedx, freex, flag = 0, lastw = 0;
2662         float extra_pixel;
2663
2664         /* x = litem->x; */ /* UNUSED */
2665         y = litem->y;
2666         w = litem->w;
2667         totw = 0;
2668         tot = 0;
2669
2670         for (item = litem->items.first; item; item = item->next) {
2671                 ui_item_size(item, &itemw, &itemh);
2672                 totw += itemw;
2673                 tot++;
2674         }
2675
2676         if (totw == 0)
2677                 return;
2678
2679         if (w != 0)
2680                 w -= (tot - 1) * litem->space;
2681         fixedw = 0;
2682
2683         /* keep clamping items to fixed minimum size until all are done */
2684         do {
2685                 freew = 0;
2686                 x = 0;
2687                 flag = 0;
2688                 newtotw = totw;
2689                 extra_pixel = 0.0f;
2690
2691                 for (item = litem->items.first; item; item = item->next) {
2692                         if (item->flag & UI_ITEM_FIXED)
2693                                 continue;
2694
2695                         ui_item_size(item, &itemw, &itemh);
2696                         minw = ui_litem_min_width(itemw);
2697
2698                         if (w - lastw > 0)
2699                                 neww = ui_item_fit(itemw, x, totw, w - lastw, !item->next, litem->alignment, &extra_pixel);
2700                         else
2701                                 neww = 0;  /* no space left, all will need clamping to minimum size */
2702
2703                         x += neww;
2704
2705                         bool min_flag = item->flag & UI_ITEM_MIN;
2706                         /* ignore min flag for rows with right or center alignment */
2707                         if (item->type != ITEM_BUTTON &&
2708                             ELEM(((uiLayout *)item)->alignment, UI_LAYOUT_ALIGN_RIGHT, UI_LAYOUT_ALIGN_CENTER) &&
2709                             litem->alignment == UI_LAYOUT_ALIGN_EXPAND &&
2710                             ((uiItem *)litem)->flag & UI_ITEM_MIN)
2711                         {
2712                                 min_flag = false;
2713                         }
2714
2715                         if ((neww < minw || min_flag) && w != 0) {
2716                                 /* fixed size */
2717                                 item->flag |= UI_ITEM_FIXED;
2718                                 if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_MIN) {
2719                                         minw = itemw;
2720                                 }
2721                                 fixedw += minw;
2722                                 flag = 1;
2723                                 newtotw -= itemw;
2724                         }
2725                         else {
2726                                 /* keep free size */
2727                                 item->flag &= ~UI_ITEM_FIXED;
2728                                 freew += itemw;
2729                         }
2730                 }
2731
2732                 totw = newtotw;
2733                 lastw = fixedw;
2734         } while (flag);
2735
2736         freex = 0;
2737         fixedx = 0;
2738         extra_pixel = 0.0f;
2739         x = litem->x;
2740
2741         for (item = litem->items.first; item; item = item->next) {
2742                 ui_item_size(item, &itemw, &itemh);
2743                 minw = ui_litem_min_width(itemw);
2744
2745                 if (item->flag & UI_ITEM_FIXED) {
2746                         /* fixed minimum size items */
2747                         if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_MIN) {
2748                                 minw = itemw;
2749                         }
2750                         itemw = ui_item_fit(minw, fixedx, fixedw, min_ii(w, fixedw), !item->next, litem->alignment, &extra_pixel);
2751                         fixedx += itemw;
2752                 }
2753                 else {
2754                         /* free size item */
2755                         itemw = ui_item_fit(itemw, freex, freew, w - fixedw, !item->next, litem->alignment, &extra_pixel);
2756                         freex += itemw;
2757                         last_free_item = item;
2758                 }
2759
2760                 /* align right/center */
2761                 offset = 0;
2762                 if (litem->alignment == UI_LAYOUT_ALIGN_RIGHT) {
2763                         if (freew + fixedw > 0 && freew + fixedw < w)
2764                                 offset = w - (fixedw + freew);
2765                 }
2766                 else if (litem->alignment == UI_LAYOUT_ALIGN_CENTER) {
2767                         if (freew + fixedw > 0 && freew + fixedw < w)
2768                                 offset = (w - (fixedw + freew)) / 2;
2769                 }
2770
2771                 /* position item */
2772                 ui_item_position(item, x + offset, y - itemh, itemw, itemh);
2773
2774                 x += itemw;
2775                 if (item->next)
2776                         x += litem->space;
2777         }
2778
2779         /* add extra pixel */
2780         uiItem *last_item = litem->items.last;
2781         extra_pixel = litem->w - (x - litem->x);
2782         if (extra_pixel > 0 && litem->alignment == UI_LAYOUT_ALIGN_EXPAND &&
2783             last_free_item && last_item && last_item->flag & UI_ITEM_FIXED)
2784         {
2785                 ui_item_move(last_free_item, 0, extra_pixel);
2786                 for (item = last_free_item->next; item; item = item->next)
2787                         ui_item_move(item, extra_pixel, extra_pixel);
2788         }
2789
2790         litem->w = x - litem->x;
2791         litem->h = litem->y - y;
2792         litem->x = x;
2793         litem->y = y;
2794 }
2795
2796 /* single-column layout */
2797 static void ui_litem_estimate_column(uiLayout *litem, bool is_box)
2798 {
2799         uiItem *item;
2800         int itemw, itemh;
2801         bool min_size_flag = true;
2802
2803         litem->w = 0;
2804         litem->h = 0;
2805
2806         for (item = litem->items.first; item; item = item->next) {
2807                 ui_item_size(item, &itemw, &itemh);
2808
2809                 min_size_flag = min_size_flag && (item->flag & UI_ITEM_MIN);
2810
2811                 litem->w = MAX2(litem->w, itemw);
2812                 litem->h += itemh;
2813
2814                 if (item->next && (!is_box || item != litem->items.first))
2815                         litem->h += litem->space;
2816         }
2817
2818         if (min_size_flag) {
2819                 litem->item.flag |= UI_ITEM_MIN;
2820         }
2821 }
2822
2823 static void ui_litem_layout_column(uiLayout *litem, bool is_box)
2824 {
2825         uiItem *item;
2826         int itemh, x, y;
2827
2828         x = litem->x;
2829         y = litem->y;
2830
2831         for (item = litem->items.first; item; item = item->next) {
2832                 ui_item_size(item, NULL, &itemh);
2833
2834                 y -= itemh;
2835                 ui_item_position(item, x, y, litem->w, itemh);
2836
2837                 if (item->next && (!is_box || item != litem->items.first))
2838                         y -= litem->space;
2839
2840                 if (is_box) {
2841                         item->flag |= UI_ITEM_BOX_ITEM;
2842                 }
2843         }
2844
2845         litem->h = litem->y - y;
2846         litem->x = x;
2847         litem->y = y;
2848 }
2849
2850 /* calculates the angle of a specified button in a radial menu,
2851  * stores a float vector in unit circle */
2852 static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum)
2853 {
2854         RadialDirection dir;
2855
2856         if (itemnum >= PIE_MAX_ITEMS) {
2857                 itemnum %= PIE_MAX_ITEMS;
2858                 printf("Warning: Pie menus with more than %i items are currently unsupported\n", PIE_MAX_ITEMS);
2859         }
2860
2861         dir = ui_radial_dir_order[itemnum];
2862         ui_but_pie_dir(dir, vec);
2863
2864         return dir;
2865 }
2866
2867 static bool ui_item_is_radial_displayable(uiItem *item)
2868 {
2869
2870         if ((item->type == ITEM_BUTTON) && (((uiButtonItem *)item)->but->type == UI_BTYPE_LABEL))
2871                 return false;
2872
2873         return true;
2874 }
2875
2876 static bool ui_item_is_radial_drawable(uiButtonItem *bitem)
2877 {
2878
2879         if (ELEM(bitem->but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER))
2880                 return false;
2881
2882         return true;
2883 }
2884
2885 static void ui_litem_layout_radial(uiLayout *litem)
2886 {
2887         uiItem *item;
2888         int itemh, itemw, x, y;
2889         int itemnum = 0;
2890         int totitems = 0;
2891
2892         /* For the radial layout we will use Matt Ebb's design
2893          * for radiation, see http://mattebb.com/weblog/radiation/
2894          * also the old code at http://developer.blender.org/T5103
2895          */
2896
2897         int pie_radius = U.pie_menu_radius * UI_DPI_FAC;
2898
2899         x = litem->x;
2900         y = litem->y;
2901
2902         int minx = x, miny = y, maxx = x, maxy = y;
2903
2904         /* first count total items */
2905         for (item = litem->items.first; item; item = item->next)
2906                 totitems++;
2907
2908         if (totitems < 5)
2909                 litem->root->block->pie_data.flags |= UI_PIE_DEGREES_RANGE_LARGE;
2910
2911         for (item = litem->items.first; item; item = item->next) {
2912                 /* not all button types are drawn in a radial menu, do filtering here */
2913                 if (ui_item_is_radial_displayable(item)) {
2914                         RadialDirection dir;
2915                         float vec[2];
2916                         float factor[2];
2917
2918                         dir = ui_get_radialbut_vec(vec, itemnum);
2919                         factor[0] = (vec[0] > 0.01f) ? 0.0f : ((vec[0] < -0.01f) ? -1.0f : -0.5f);
2920                         factor[1] = (vec[1] > 0.99f) ? 0.0f : ((vec[1] < -0.99f) ? -1.0f : -0.5f);
2921
2922                         itemnum++;
2923
2924                         if (item->type == ITEM_BUTTON) {
2925                                 uiButtonItem *bitem = (uiButtonItem *) item;
2926
2927                                 bitem->but->pie_dir = dir;
2928                                 /* scale the buttons */
2929                                 bitem->but->rect.ymax *= 1.5f;
2930                                 /* add a little bit more here to include number */
2931                                 bitem->but->rect.xmax += 1.5f * UI_UNIT_X;
2932                                 /* enable drawing as pie item if supported by widget */
2933                                 if (ui_item_is_radial_drawable(bitem)) {
2934                                         bitem->but->dt = UI_EMBOSS_RADIAL;
2935                                         bitem->but->drawflag |= UI_BUT_ICON_LEFT;
2936                                 }
2937                         }
2938
2939                         ui_item_size(item, &itemw, &itemh);
2940
2941                         ui_item_position(item, x + vec[0] * pie_radius + factor[0] * itemw, y + vec[1] * pie_radius + factor[1] * itemh, itemw, itemh);
2942
2943                         minx = min_ii(minx, x + vec[0] * pie_radius - itemw / 2);
2944                         maxx = max_ii(maxx, x + vec[0] * pie_radius + itemw / 2);
2945                         miny = min_ii(miny, y + vec[1] * pie_radius - itemh / 2);
2946                         maxy = max_ii(maxy, y + vec[1] * pie_radius + itemh / 2);
2947                 }
2948         }
2949
2950         litem->x = minx;
2951         litem->y = miny;
2952         litem->w = maxx - minx;
2953         litem->h = maxy - miny;
2954 }
2955
2956 /* root layout */
2957 static void ui_litem_estimate_root(uiLayout *UNUSED(litem))
2958 {
2959         /* nothing to do */
2960 }
2961
2962 static void ui_litem_layout_root_radial(uiLayout *litem)
2963 {
2964         /* first item is pie menu title, align on center of menu */
2965         uiItem *item = litem->items.first;
2966
2967         if (item->type == ITEM_BUTTON) {
2968                 int itemh, itemw, x, y;
2969                 x = litem->x;
2970                 y = litem->y;
2971
2972                 ui_item_size(item, &itemw, &itemh);
2973
2974                 ui_item_position(item, x - itemw / 2, y + U.dpi_fac * (U.pie_menu_threshold + 9.0f), itemw, itemh);
2975         }
2976 }
2977
2978 static void ui_litem_layout_root(uiLayout *litem)
2979 {
2980         if (litem->root->type == UI_LAYOUT_HEADER)
2981                 ui_litem_layout_row(litem);
2982         else if (litem->root->type == UI_LAYOUT_PIEMENU)
2983                 ui_litem_layout_root_radial(litem);
2984         else
2985                 ui_litem_layout_column(litem, false);
2986 }
2987
2988 /* box layout */
2989 static void ui_litem_estimate_box(uiLayout *litem)
2990 {
2991         uiStyle *style = litem->root->style;
2992
2993         ui_litem_estimate_column(litem, true);
2994         litem->w += 2 * style->boxspace;
2995         litem->h += 2 * style->boxspace;
2996 }
2997
2998 static void ui_litem_layout_box(uiLayout *litem)
2999 {
3000         uiLayoutItemBx *box = (uiLayoutItemBx *)litem;
3001         uiStyle *style = litem->root->style;
3002         uiBut *but;
3003         int w, h;
3004
3005         w = litem->w;
3006         h = litem->h;
3007
3008         litem->x += style->boxspace;
3009         litem->y -= style->boxspace;
3010
3011         if (w != 0) litem->w -= 2 * style->boxspace;
3012         if (h != 0) litem->h -= 2 * style->boxspace;
3013
3014         ui_litem_layout_column(litem, true);
3015
3016         litem->x -= style->boxspace;
3017         litem->y -= style->boxspace;
3018
3019         if (w != 0) litem->w += 2 * style->boxspace;
3020         if (h != 0) litem->h += 2 * style->boxspace;
3021
3022         /* roundbox around the sublayout */
3023         but = box->roundbox;
3024         but->rect.xmin = litem->x;
3025         but->rect.ymin = litem->y;
3026         but->rect.xmax = litem->x + litem->w;
3027         but->rect.ymax = litem->y + litem->h;
3028 }
3029
3030 /* multi-column layout, automatically flowing to the next */
3031 static void ui_litem_estimate_column_flow(uiLayout *litem)
3032 {
3033         uiStyle *style = litem->root->style;
3034         uiLayoutItemFlow *flow = (uiLayoutItemFlow *)litem;
3035         uiItem *item;
3036         int col, x, y, emh, emy, miny, itemw, itemh, maxw = 0;
3037         int toth, totitem;
3038
3039         /* compute max needed width and total height */
3040         toth = 0;
3041         totitem = 0;
3042         for (item = litem->items.first; item; item = item->next) {
3043                 ui_item_size(item, &itemw, &itemh);
3044                 maxw = MAX2(maxw, itemw);
3045                 toth += itemh;
3046                 totitem++;
3047         }
3048
3049         if (flow->number <= 0) {
3050                 /* auto compute number of columns, not very good */
3051                 if (maxw == 0) {
3052                         flow->totcol = 1;
3053                         return;
3054                 }
3055
3056                 flow->totcol = max_ii(litem->root->emw / maxw, 1);
3057                 flow->totcol = min_ii(flow->totcol, totitem);
3058         }
3059         else
3060                 flow->totcol = flow->number;
3061
3062         /* compute sizes */
3063         x = 0;
3064         y = 0;
3065         emy = 0;
3066         miny = 0;
3067
3068         maxw = 0;
3069         emh = toth / flow->totcol;
3070
3071         /* create column per column */
3072         col = 0;
3073         for (item = litem->items.first; item; item = item->next) {
3074                 ui_item_size(item, &itemw, &itemh);
3075
3076                 y -= itemh + style->buttonspacey;
3077                 miny = min_ii(miny, y);
3078                 emy -= itemh;
3079                 maxw = max_ii(itemw, maxw);
3080
3081                 /* decide to go to next one */
3082                 if (col < flow->totcol - 1 && emy <= -emh) {
3083                         x += maxw + litem->space;
3084                         maxw = 0;
3085                         y = 0;
3086                         emy = 0; /* need to reset height again for next column */
3087                         col++;
3088                 }
3089         }
3090
3091         litem->w = x;
3092         litem->h = litem->y - miny;
3093 }
3094
3095 static void ui_litem_layout_column_flow(uiLayout *litem)
3096 {
3097         uiStyle *style = litem->root->style;
3098         uiLayoutItemFlow *flow = (uiLayoutItemFlow *)litem;
3099         uiItem *item;
3100         int col, x, y, w, emh, emy, miny, itemw, itemh;
3101         int toth, totitem;
3102
3103         /* compute max needed width and total height */
3104         toth = 0;
3105         totitem = 0;
3106         for (item = litem->items.first; item; item = item->next) {
3107                 ui_item_size(item, &itemw, &itemh);
3108                 toth += itemh;
3109                 totitem++;
3110         }
3111
3112         /* compute sizes */
3113         x = litem->x;
3114         y = litem->y;
3115         emy = 0;
3116         miny = 0;
3117
3118         w = litem->w - (flow->totcol - 1) * style->columnspace;
3119         emh = toth / flow->totcol;
3120
3121         /* create column per column */
3122         col = 0;
3123         w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol;
3124         for (item = litem->items.first; item; item = item->next) {
3125                 ui_item_size(item, &itemw, &itemh);
3126
3127      &nb