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