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