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