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