UI: fix for issue in last commit, popups show above the buttons again
[blender.git] / source / blender / editors / interface / interface_regions.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  * 
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_regions.c
27  *  \ingroup edinterface
28  */
29
30
31
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "DNA_userdef_types.h"
40
41 #include "BLI_math.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_dynstr.h"
45 #include "BLI_ghash.h"
46
47 #include "BKE_context.h"
48 #include "BKE_screen.h"
49 #include "BKE_idcode.h"
50 #include "BKE_global.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54 #include "wm_draw.h"
55 #include "wm_subwindow.h"
56 #include "wm_window.h"
57
58 #include "RNA_access.h"
59
60 #include "BIF_gl.h"
61
62 #include "UI_interface.h"
63 #include "UI_interface_icons.h"
64 #include "UI_view2d.h"
65
66 #include "BLF_api.h"
67 #include "BLF_translation.h"
68
69 #include "ED_screen.h"
70
71 #include "IMB_colormanagement.h"
72
73 #include "interface_intern.h"
74
75 #define MENU_TOP            8
76 #define MENU_PADDING            (int)(0.2f * UI_UNIT_Y)
77
78 static int rna_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int direction)
79 {
80         EnumPropertyItem *item_array;
81         int totitem;
82         bool free;
83         int value;
84         int i, i_init;
85         int step = (direction < 0) ? -1 : 1;
86         int step_tot = 0;
87
88         RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free);
89         value = RNA_property_enum_get(ptr, prop);
90         i = RNA_enum_from_value(item_array, value);
91         i_init = i;
92
93         do {
94                 i = mod_i(i + step, totitem);
95                 if (item_array[i].identifier[0]) {
96                         step_tot += step;
97                 }
98         } while ((i != i_init) && (step_tot != direction));
99
100         if (i != i_init) {
101                 value = item_array[i].value;
102         }
103
104         if (free) {
105                 MEM_freeN(item_array);
106         }
107
108         return value;
109 }
110
111 int ui_step_name_menu(uiBut *but, int direction)
112 {
113         /* currenly only RNA buttons */
114         if ((but->rnaprop == NULL) || (RNA_property_type(but->rnaprop) != PROP_ENUM)) {
115                 printf("%s: cannot cycle button '%s'", __func__, but->str);
116                 return 0;
117         }
118
119         return rna_property_enum_step(but->block->evil_C, &but->rnapoin, but->rnaprop, direction);
120 }
121
122 /******************** Creating Temporary regions ******************/
123
124 static ARegion *ui_add_temporary_region(bScreen *sc)
125 {
126         ARegion *ar;
127
128         ar = MEM_callocN(sizeof(ARegion), "area region");
129         BLI_addtail(&sc->regionbase, ar);
130
131         ar->regiontype = RGN_TYPE_TEMPORARY;
132         ar->alignment = RGN_ALIGN_FLOAT;
133
134         return ar;
135 }
136
137 static void ui_remove_temporary_region(bContext *C, bScreen *sc, ARegion *ar)
138 {
139         wmWindow *win = CTX_wm_window(C);
140         if (win)
141                 wm_draw_region_clear(win, ar);
142
143         ED_region_exit(C, ar);
144         BKE_area_region_free(NULL, ar);     /* NULL: no spacetype */
145         BLI_freelinkN(&sc->regionbase, ar);
146 }
147
148 /************************* Creating Tooltips **********************/
149
150 typedef enum {
151         UI_TIP_LC_MAIN,
152         UI_TIP_LC_NORMAL,
153         UI_TIP_LC_PYTHON,
154         UI_TIP_LC_ALERT,
155         UI_TIP_LC_SUBMENU
156 } uiTooltipLineColor;
157 #define UI_TIP_LC_MAX 5
158
159 #define MAX_TOOLTIP_LINES 8
160 typedef struct uiTooltipData {
161         rcti bbox;
162         uiFontStyle fstyle;
163         char lines[MAX_TOOLTIP_LINES][512];
164         uiTooltipLineColor color_id[MAX_TOOLTIP_LINES];
165         int totline;
166         int toth, spaceh, lineh;
167 } uiTooltipData;
168
169 static void rgb_tint(float col[3],
170                      float h, float h_strength,
171                      float v, float v_strength)
172 {
173         float col_hsv_from[3];
174         float col_hsv_to[3];
175
176         rgb_to_hsv_v(col, col_hsv_from);
177
178         col_hsv_to[0] = h;
179         col_hsv_to[1] = h_strength;
180         col_hsv_to[2] = (col_hsv_from[2] * (1.0f - v_strength)) + (v * v_strength);
181
182         hsv_to_rgb_v(col_hsv_to, col);
183 }
184
185 static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
186 {
187         uiTooltipData *data = ar->regiondata;
188         uiWidgetColors *theme = ui_tooltip_get_theme();
189         rcti bbox = data->bbox;
190         float tip_colors[UI_TIP_LC_MAX][3];
191
192         float *main_color    = tip_colors[UI_TIP_LC_MAIN]; /* the color from the theme */
193         float *normal_color  = tip_colors[UI_TIP_LC_NORMAL];
194         float *python_color  = tip_colors[UI_TIP_LC_PYTHON];
195         float *alert_color   = tip_colors[UI_TIP_LC_ALERT];
196         float *submenu_color = tip_colors[UI_TIP_LC_SUBMENU];
197
198         float background_color[3];
199         float tone_bg;
200         int i, multisample_enabled;
201
202         /* disable AA, makes widgets too blurry */
203         multisample_enabled = glIsEnabled(GL_MULTISAMPLE_ARB);
204         if (multisample_enabled)
205                 glDisable(GL_MULTISAMPLE_ARB);
206
207         /* draw background */
208         ui_draw_tooltip_background(UI_GetStyle(), NULL, &bbox);
209
210         /* set background_color */
211         rgb_uchar_to_float(background_color, (const unsigned char *)theme->inner);
212
213         /* calculate normal_color */
214         rgb_uchar_to_float(main_color, (const unsigned char *)theme->text);
215         copy_v3_v3(normal_color, main_color);
216         copy_v3_v3(python_color, main_color);
217         copy_v3_v3(alert_color, main_color);
218         copy_v3_v3(submenu_color, main_color);
219
220         /* find the brightness difference between background and text colors */
221         
222         tone_bg = rgb_to_grayscale(background_color);
223         /* tone_fg = rgb_to_grayscale(main_color); */
224
225         rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.3f);   /* a shade darker (to bg) */
226         rgb_tint(python_color, 0.666f, 0.25f, tone_bg, 0.3f); /* blue */
227         rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f);    /* bright red */
228         rgb_tint(submenu_color, 0.0f, 0.0f, tone_bg, 0.3f);  /* a shade darker (to bg) */
229
230         /* draw text */
231         uiStyleFontSet(&data->fstyle);
232
233         bbox.ymax = bbox.ymax - 0.5f * (BLI_rcti_size_y(&bbox) - data->toth);
234         bbox.ymin = bbox.ymax - data->lineh;
235
236         for (i = 0; i < data->totline; i++) {
237                 glColor3fv(tip_colors[data->color_id[i]]);
238                 uiStyleFontDraw(&data->fstyle, &bbox, data->lines[i]);
239                 bbox.ymin -= data->lineh + data->spaceh;
240                 bbox.ymax -= data->lineh + data->spaceh;
241         }
242
243         if (multisample_enabled)
244                 glEnable(GL_MULTISAMPLE_ARB);
245 }
246
247 static void ui_tooltip_region_free_cb(ARegion *ar)
248 {
249         uiTooltipData *data;
250
251         data = ar->regiondata;
252         MEM_freeN(data);
253         ar->regiondata = NULL;
254 }
255
256 ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
257 {
258         wmWindow *win = CTX_wm_window(C);
259         uiStyle *style = UI_GetStyle();
260         static ARegionType type;
261         ARegion *ar;
262         uiTooltipData *data;
263 /*      IDProperty *prop;*/
264         char buf[512];
265         /* aspect values that shrink text are likely unreadable */
266         const float aspect = min_ff(1.0f, but->block->aspect);
267         float fonth, fontw;
268         int winx /*, winy */, ofsx, ofsy, w, h, a;
269         rctf rect_fl;
270         rcti rect_i;
271
272         uiStringInfo but_tip = {BUT_GET_TIP, NULL};
273         uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
274         uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
275         uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
276         uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, NULL};
277         uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
278         uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
279
280         if (but->drawflag & UI_BUT_NO_TOOLTIP)
281                 return NULL;
282
283         /* create tooltip data */
284         data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
285
286         uiButGetStrInfo(C, but, &but_tip, &enum_label, &enum_tip, &op_keymap, &prop_keymap, &rna_struct, &rna_prop, NULL);
287
288         /* special case, enum rna buttons only have enum item description,
289          * use general enum description too before the specific one */
290
291         /* Tip */
292         if (but_tip.strinfo) {
293                 /* Expanded Bit-flag enums have a specific way to select multiple... */
294                 if ((but->type & ROW) && but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) {
295                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
296                                      "%s %s", but_tip.strinfo, IFACE_("(Shift-click to select multiple)"));
297                 }
298                 else {
299                         BLI_strncpy(data->lines[data->totline], but_tip.strinfo, sizeof(data->lines[0]));
300                 }
301                 data->color_id[data->totline] = UI_TIP_LC_MAIN;
302                 data->totline++;
303         }
304         /* Enum item label & tip */
305         if (enum_label.strinfo && enum_tip.strinfo) {
306                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
307                              "%s: %s", enum_label.strinfo, enum_tip.strinfo);
308                 data->color_id[data->totline] = UI_TIP_LC_SUBMENU;
309                 data->totline++;
310         }
311
312         /* Op shortcut */
313         if (op_keymap.strinfo) {
314                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), op_keymap.strinfo);
315                 data->color_id[data->totline] = UI_TIP_LC_NORMAL;
316                 data->totline++;
317         }
318         
319         /* Property context-toggle shortcut */
320         if (prop_keymap.strinfo) {
321                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), prop_keymap.strinfo);
322                 data->color_id[data->totline] = UI_TIP_LC_NORMAL;
323                 data->totline++;
324         }
325
326         if (ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) {
327                 /* full string */
328                 ui_get_but_string(but, buf, sizeof(buf));
329                 if (buf[0]) {
330                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf);
331                         data->color_id[data->totline] = UI_TIP_LC_NORMAL;
332                         data->totline++;
333                 }
334         }
335
336         if (but->rnaprop) {
337                 int unit_type = uiButGetUnitType(but);
338                 
339                 if (unit_type == PROP_UNIT_ROTATION) {
340                         if (RNA_property_type(but->rnaprop) == PROP_FLOAT) {
341                                 float value = RNA_property_array_check(but->rnaprop) ?
342                                                   RNA_property_float_get_index(&but->rnapoin, but->rnaprop, but->rnaindex) :
343                                                   RNA_property_float_get(&but->rnapoin, but->rnaprop);
344                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value);
345                                 data->color_id[data->totline] = UI_TIP_LC_NORMAL;
346                                 data->totline++;
347                         }
348                 }
349                 
350                 if (but->flag & UI_BUT_DRIVEN) {
351                         if (ui_but_anim_expression_get(but, buf, sizeof(buf))) {
352                                 /* expression */
353                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf);
354                                 data->color_id[data->totline] = UI_TIP_LC_NORMAL;
355                                 data->totline++;
356                         }
357                 }
358
359                 if (but->rnapoin.id.data) {
360                         ID *id = but->rnapoin.id.data;
361                         if (id->lib) {
362                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name);
363                                 data->color_id[data->totline] = UI_TIP_LC_NORMAL;
364                                 data->totline++;
365                         }
366                 }
367         }
368         else if (but->optype) {
369                 PointerRNA *opptr;
370                 char *str;
371                 opptr = uiButGetOperatorPtrRNA(but); /* allocated when needed, the button owns it */
372
373                 /* so the context is passed to itemf functions (some py itemf functions use it) */
374                 WM_operator_properties_sanitize(opptr, false);
375
376                 str = WM_operator_pystring_ex(C, NULL, false, false, but->optype, opptr);
377
378                 /* avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary */
379                 WM_operator_pystring_abbreviate(str, 32);
380
381                 /* operator info */
382                 if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
383                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str);
384                         data->color_id[data->totline] = UI_TIP_LC_PYTHON;
385                         data->totline++;
386                 }
387
388                 MEM_freeN(str);
389
390                 /* second check if we are disabled - why */
391                 if (but->flag & UI_BUT_DISABLED) {
392                         const char *poll_msg;
393                         CTX_wm_operator_poll_msg_set(C, NULL);
394                         WM_operator_poll_context(C, but->optype, but->opcontext);
395                         poll_msg = CTX_wm_operator_poll_msg_get(C);
396                         if (poll_msg) {
397                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), poll_msg);
398                                 data->color_id[data->totline] = UI_TIP_LC_ALERT; /* alert */
399                                 data->totline++;
400                         }
401                 }
402         }
403         if ((U.flag & USER_TOOLTIPS_PYTHON) == 0 && !but->optype && rna_struct.strinfo) {
404                 if (rna_prop.strinfo) {
405                         /* Struct and prop */
406                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
407                                      TIP_("Python: %s.%s"),
408                                      rna_struct.strinfo, rna_prop.strinfo);
409                 }
410                 else {
411                         /* Only struct (e.g. menus) */
412                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
413                                      TIP_("Python: %s"), rna_struct.strinfo);
414                 }
415                 data->color_id[data->totline] = UI_TIP_LC_PYTHON;
416                 data->totline++;
417
418                 if (but->rnapoin.id.data) {
419                         /* this could get its own 'BUT_GET_...' type */
420                         PointerRNA *ptr = &but->rnapoin;
421                         PropertyRNA *prop = but->rnaprop;
422                         ID *id = ptr->id.data;
423
424                         char *id_path;
425                         char *data_path = NULL;
426
427                         /* never fails */
428                         id_path = RNA_path_full_ID_py(id);
429
430                         if (ptr->data && prop) {
431                                 data_path = RNA_path_from_ID_to_property(ptr, prop);
432                         }
433
434                         if (data_path) {
435                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
436                                              "%s.%s",  /* no need to translate */
437                                              id_path, data_path);
438                                 MEM_freeN(data_path);
439                         }
440                         else if (prop) {
441                                 /* can't find the path. be explicit in our ignorance "..." */
442                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
443                                              "%s ... %s",  /* no need to translate */
444                                              id_path, rna_prop.strinfo ? rna_prop.strinfo : RNA_property_identifier(prop));
445                         }
446                         MEM_freeN(id_path);
447
448                         data->color_id[data->totline] = UI_TIP_LC_PYTHON;
449                         data->totline++;
450                 }
451         }
452
453         /* Free strinfo's... */
454         if (but_tip.strinfo)
455                 MEM_freeN(but_tip.strinfo);
456         if (enum_label.strinfo)
457                 MEM_freeN(enum_label.strinfo);
458         if (enum_tip.strinfo)
459                 MEM_freeN(enum_tip.strinfo);
460         if (op_keymap.strinfo)
461                 MEM_freeN(op_keymap.strinfo);
462         if (prop_keymap.strinfo)
463                 MEM_freeN(prop_keymap.strinfo);
464         if (rna_struct.strinfo)
465                 MEM_freeN(rna_struct.strinfo);
466         if (rna_prop.strinfo)
467                 MEM_freeN(rna_prop.strinfo);
468
469         BLI_assert(data->totline < MAX_TOOLTIP_LINES);
470         
471         if (data->totline == 0) {
472                 MEM_freeN(data);
473                 return NULL;
474         }
475
476         /* create area region */
477         ar = ui_add_temporary_region(CTX_wm_screen(C));
478
479         memset(&type, 0, sizeof(ARegionType));
480         type.draw = ui_tooltip_region_draw_cb;
481         type.free = ui_tooltip_region_free_cb;
482         type.regionid = RGN_TYPE_TEMPORARY;
483         ar->type = &type;
484         
485         /* set font, get bb */
486         data->fstyle = style->widget; /* copy struct */
487         data->fstyle.align = UI_STYLE_TEXT_CENTER;
488         ui_fontscale(&data->fstyle.points, aspect);
489
490         uiStyleFontSet(&data->fstyle);
491
492         /* these defines tweaked depending on font */
493 #define TIP_MARGIN_Y (2.0f / aspect)
494 #define TIP_BORDER_X (16.0f / aspect)
495 #define TIP_BORDER_Y (6.0f / aspect)
496
497         h = BLF_height_max(data->fstyle.uifont_id);
498
499         for (a = 0, fontw = 0, fonth = 0; a < data->totline; a++) {
500                 w = BLF_width(data->fstyle.uifont_id, data->lines[a], sizeof(data->lines[a]));
501                 fontw = max_ff(fontw, (float)w);
502                 fonth += (a == 0) ? h : h + TIP_MARGIN_Y;
503         }
504
505         //fontw *= aspect;
506
507         ar->regiondata = data;
508
509         data->toth = fonth;
510         data->lineh = h;
511         data->spaceh = TIP_MARGIN_Y;
512
513         /* compute position */
514         ofsx = 0; //(but->block->panel) ? but->block->panel->ofsx : 0;
515         ofsy = 0; //(but->block->panel) ? but->block->panel->ofsy : 0;
516
517         rect_fl.xmin = BLI_rctf_cent_x(&but->rect) + ofsx - TIP_BORDER_X;
518         rect_fl.xmax = rect_fl.xmin + fontw + (TIP_BORDER_X * 2);
519         rect_fl.ymax = but->rect.ymin + ofsy - TIP_BORDER_Y;
520         rect_fl.ymin = rect_fl.ymax - fonth  - TIP_BORDER_Y;
521
522 #undef TIP_MARGIN_Y
523 #undef TIP_BORDER_X
524 #undef TIP_BORDER_Y
525         
526         /* since the text has beens caled already, the size of tooltips is defined now */
527         /* here we try to figure out the right location */
528         if (butregion) {
529                 float ofsx_fl = rect_fl.xmin, ofsy_fl = rect_fl.ymax;
530                 ui_block_to_window_fl(butregion, but->block, &ofsx_fl, &ofsy_fl);
531                 BLI_rctf_translate(&rect_fl, ofsx_fl - rect_fl.xmin, ofsy_fl - rect_fl.ymax);
532         }
533         BLI_rcti_rctf_copy(&rect_i, &rect_fl);
534
535         /* clip with window boundaries */
536         winx = WM_window_pixels_x(win);
537
538         if (rect_i.xmax > winx) {
539                 /* super size */
540                 if (rect_i.xmax > winx + rect_i.xmin) {
541                         rect_i.xmax = winx;
542                         rect_i.xmin = 0;
543                 }
544                 else {
545                         rect_i.xmin -= rect_i.xmax - winx;
546                         rect_i.xmax = winx;
547                 }
548         }
549         /* ensure at least 5 px above screen bounds
550          * 25 is just a guess to be above the menu item */
551         if (rect_i.ymin < 5) {
552                 rect_i.ymax += (-rect_i.ymin) + 30;
553                 rect_i.ymin = 30;
554         }
555
556         /* widget rect, in region coords */
557         {
558                 int width = UI_ThemeMenuShadowWidth();
559                 
560                 data->bbox.xmin = width;
561                 data->bbox.xmax = BLI_rcti_size_x(&rect_i) + width;
562                 data->bbox.ymin = width;
563                 data->bbox.ymax = BLI_rcti_size_y(&rect_i) + width;
564                 
565                 /* region bigger for shadow */
566                 ar->winrct.xmin = rect_i.xmin - width;
567                 ar->winrct.xmax = rect_i.xmax + width;
568                 ar->winrct.ymin = rect_i.ymin - width;
569                 ar->winrct.ymax = rect_i.ymax + MENU_TOP;
570         }
571
572         /* adds subwindow */
573         ED_region_init(C, ar);
574         
575         /* notify change and redraw */
576         ED_region_tag_redraw(ar);
577
578         return ar;
579 }
580
581 void ui_tooltip_free(bContext *C, ARegion *ar)
582 {
583         ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
584 }
585
586
587 /************************* Creating Search Box **********************/
588
589 struct uiSearchItems {
590         int maxitem, totitem, maxstrlen;
591         
592         int offset, offset_i; /* offset for inserting in array */
593         int more;  /* flag indicating there are more items */
594         
595         char **names;
596         void **pointers;
597         int *icons;
598
599         AutoComplete *autocpl;
600         void *active;
601 };
602
603 typedef struct uiSearchboxData {
604         rcti bbox;
605         uiFontStyle fstyle;
606         uiSearchItems items;
607         int active;     /* index in items array */
608         bool noback;    /* when menu opened with enough space for this */
609         bool preview;   /* draw thumbnail previews, rather than list */
610         bool use_sep;   /* use the UI_SEP_CHAR char for splitting shortcuts (good for operators, bad for data) */
611         int prv_rows, prv_cols;
612 } uiSearchboxData;
613
614 #define SEARCH_ITEMS    10
615
616 /* exported for use by search callbacks */
617 /* returns zero if nothing to add */
618 bool uiSearchItemAdd(uiSearchItems *items, const char *name, void *poin, int iconid)
619 {
620         /* hijack for autocomplete */
621         if (items->autocpl) {
622                 autocomplete_do_name(items->autocpl, name);
623                 return true;
624         }
625         
626         /* hijack for finding active item */
627         if (items->active) {
628                 if (poin == items->active)
629                         items->offset_i = items->totitem;
630                 items->totitem++;
631                 return true;
632         }
633         
634         if (items->totitem >= items->maxitem) {
635                 items->more = 1;
636                 return false;
637         }
638         
639         /* skip first items in list */
640         if (items->offset_i > 0) {
641                 items->offset_i--;
642                 return true;
643         }
644         
645         if (items->names)
646                 BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
647         if (items->pointers)
648                 items->pointers[items->totitem] = poin;
649         if (items->icons)
650                 items->icons[items->totitem] = iconid;
651         
652         items->totitem++;
653         
654         return true;
655 }
656
657 int uiSearchBoxHeight(void)
658 {
659         return SEARCH_ITEMS * UI_UNIT_Y + 2 * MENU_TOP;
660 }
661
662 int uiSearchBoxWidth(void)
663 {
664         /* was hardcoded at 150 */
665         return 9 * UI_UNIT_X;
666 }
667
668 int uiSearchItemFindIndex(uiSearchItems *items, const char *name)
669 {
670         int i;
671         for (i = 0; i < items->totitem; i++) {
672                 if (STREQ(name, items->names[i])) {
673                         return i;
674                 }
675         }
676         return -1;
677 }
678
679 /* ar is the search box itself */
680 static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
681 {
682         uiSearchboxData *data = ar->regiondata;
683         
684         /* apply step */
685         data->active += step;
686         
687         if (data->items.totitem == 0) {
688                 data->active = -1;
689         }
690         else if (data->active >= data->items.totitem) {
691                 if (data->items.more) {
692                         data->items.offset++;
693                         data->active = data->items.totitem - 1;
694                         ui_searchbox_update(C, ar, but, false);
695                 }
696                 else {
697                         data->active = data->items.totitem - 1;
698                 }
699         }
700         else if (data->active < 0) {
701                 if (data->items.offset) {
702                         data->items.offset--;
703                         data->active = 0;
704                         ui_searchbox_update(C, ar, but, false);
705                 }
706                 else if (data->active < -1)
707                         data->active = -1;
708         }
709         
710         ED_region_tag_redraw(ar);
711 }
712
713 static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr)
714 {
715         /* thumbnail preview */
716         if (data->preview) {
717                 int butw =  BLI_rcti_size_x(&data->bbox)                 / data->prv_cols;
718                 int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_TOP) / data->prv_rows;
719                 int row, col;
720                 
721                 *r_rect = data->bbox;
722                 
723                 col = itemnr % data->prv_cols;
724                 row = itemnr / data->prv_cols;
725                 
726                 r_rect->xmin += col * butw;
727                 r_rect->xmax = r_rect->xmin + butw;
728                 
729                 r_rect->ymax = data->bbox.ymax - MENU_TOP - (row * buth);
730                 r_rect->ymin = r_rect->ymax - buth;
731         }
732         /* list view */
733         else {
734                 int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_TOP) / SEARCH_ITEMS;
735                 
736                 *r_rect = data->bbox;
737                 r_rect->xmin = data->bbox.xmin + 3.0f;
738                 r_rect->xmax = data->bbox.xmax - 3.0f;
739                 
740                 r_rect->ymax = data->bbox.ymax - MENU_TOP - itemnr * buth;
741                 r_rect->ymin = r_rect->ymax - buth;
742         }
743         
744 }
745
746 int ui_searchbox_find_index(ARegion *ar, const char *name)
747 {
748         uiSearchboxData *data = ar->regiondata;
749         return uiSearchItemFindIndex(&data->items, name);
750 }
751
752 /* x and y in screencoords */
753 bool ui_searchbox_inside(ARegion *ar, int x, int y)
754 {
755         uiSearchboxData *data = ar->regiondata;
756         
757         return BLI_rcti_isect_pt(&data->bbox, x - ar->winrct.xmin, y - ar->winrct.ymin);
758 }
759
760 /* string validated to be of correct length (but->hardmax) */
761 bool ui_searchbox_apply(uiBut *but, ARegion *ar)
762 {
763         uiSearchboxData *data = ar->regiondata;
764
765         but->func_arg2 = NULL;
766         
767         if (data->active != -1) {
768                 const char *name = data->items.names[data->active];
769                 const char *name_sep = data->use_sep ? strchr(name, UI_SEP_CHAR) : NULL;
770
771                 BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) : data->items.maxstrlen);
772                 
773                 but->func_arg2 = data->items.pointers[data->active];
774
775                 return true;
776         }
777         else {
778                 return false;
779         }
780 }
781
782 void ui_searchbox_event(bContext *C, ARegion *ar, uiBut *but, const wmEvent *event)
783 {
784         uiSearchboxData *data = ar->regiondata;
785         int type = event->type, val = event->val;
786         
787         if (type == MOUSEPAN)
788                 ui_pan_to_scroll(event, &type, &val);
789         
790         switch (type) {
791                 case WHEELUPMOUSE:
792                 case UPARROWKEY:
793                         ui_searchbox_select(C, ar, but, -1);
794                         break;
795                 case WHEELDOWNMOUSE:
796                 case DOWNARROWKEY:
797                         ui_searchbox_select(C, ar, but, 1);
798                         break;
799                 case MOUSEMOVE:
800                         if (BLI_rcti_isect_pt(&ar->winrct, event->x, event->y)) {
801                                 rcti rect;
802                                 int a;
803                                 
804                                 for (a = 0; a < data->items.totitem; a++) {
805                                         ui_searchbox_butrect(&rect, data, a);
806                                         if (BLI_rcti_isect_pt(&rect, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
807                                                 if (data->active != a) {
808                                                         data->active = a;
809                                                         ui_searchbox_select(C, ar, but, 0);
810                                                         break;
811                                                 }
812                                         }
813                                 }
814                         }
815                         break;
816         }
817 }
818
819 /* ar is the search box itself */
820 void ui_searchbox_update(bContext *C, ARegion *ar, uiBut *but, const bool reset)
821 {
822         uiSearchboxData *data = ar->regiondata;
823         
824         /* reset vars */
825         data->items.totitem = 0;
826         data->items.more = 0;
827         if (reset == false) {
828                 data->items.offset_i = data->items.offset;
829         }
830         else {
831                 data->items.offset_i = data->items.offset = 0;
832                 data->active = -1;
833                 
834                 /* handle active */
835                 if (but->search_func && but->func_arg2) {
836                         data->items.active = but->func_arg2;
837                         but->search_func(C, but->search_arg, but->editstr, &data->items);
838                         data->items.active = NULL;
839                         
840                         /* found active item, calculate real offset by centering it */
841                         if (data->items.totitem) {
842                                 /* first case, begin of list */
843                                 if (data->items.offset_i < data->items.maxitem) {
844                                         data->active = data->items.offset_i;
845                                         data->items.offset_i = 0;
846                                 }
847                                 else {
848                                         /* second case, end of list */
849                                         if (data->items.totitem - data->items.offset_i <= data->items.maxitem) {
850                                                 data->active = data->items.offset_i - data->items.totitem + data->items.maxitem;
851                                                 data->items.offset_i = data->items.totitem - data->items.maxitem;
852                                         }
853                                         else {
854                                                 /* center active item */
855                                                 data->items.offset_i -= data->items.maxitem / 2;
856                                                 data->active = data->items.maxitem / 2;
857                                         }
858                                 }
859                         }
860                         data->items.offset = data->items.offset_i;
861                         data->items.totitem = 0;
862                 }
863         }
864         
865         /* callback */
866         if (but->search_func)
867                 but->search_func(C, but->search_arg, but->editstr, &data->items);
868         
869         /* handle case where editstr is equal to one of items */
870         if (reset && data->active == -1) {
871                 int a;
872                 
873                 for (a = 0; a < data->items.totitem; a++) {
874                         const char *name = data->items.names[a];
875                         const char *name_sep = data->use_sep ? strchr(name, UI_SEP_CHAR) : NULL;
876                         if (STREQLEN(but->editstr, name, name_sep ? (name_sep - name) : data->items.maxstrlen)) {
877                                 data->active = a;
878                                 break;
879                         }
880                 }
881                 if (data->items.totitem == 1 && but->editstr[0])
882                         data->active = 0;
883         }
884
885         /* validate selected item */
886         ui_searchbox_select(C, ar, but, 0);
887         
888         ED_region_tag_redraw(ar);
889 }
890
891 int ui_searchbox_autocomplete(bContext *C, ARegion *ar, uiBut *but, char *str)
892 {
893         uiSearchboxData *data = ar->regiondata;
894         int match = AUTOCOMPLETE_NO_MATCH;
895
896         if (str[0]) {
897                 data->items.autocpl = autocomplete_begin(str, ui_get_but_string_max_length(but));
898
899                 but->search_func(C, but->search_arg, but->editstr, &data->items);
900
901                 match = autocomplete_end(data->items.autocpl, str);
902                 data->items.autocpl = NULL;
903         }
904
905         return match;
906 }
907
908 static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
909 {
910         uiSearchboxData *data = ar->regiondata;
911         
912         /* pixel space */
913         wmOrtho2(-0.01f, ar->winx - 0.01f, -0.01f, ar->winy - 0.01f);
914
915         if (data->noback == false)
916                 ui_draw_search_back(NULL, NULL, &data->bbox);  /* style not used yet */
917         
918         /* draw text */
919         if (data->items.totitem) {
920                 rcti rect;
921                 int a;
922                 
923                 if (data->preview) {
924                         /* draw items */
925                         for (a = 0; a < data->items.totitem; a++) {
926                                 ui_searchbox_butrect(&rect, data, a);
927                                 
928                                 /* widget itself */
929                                 if (data->preview) {
930                                         ui_draw_preview_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a],
931                                                              (a == data->active) ? UI_ACTIVE : 0);
932                                 }
933                                 else {
934                                         ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a],
935                                                           (a == data->active) ? UI_ACTIVE : 0, data->use_sep);
936                                 }
937                         }
938                         
939                         /* indicate more */
940                         if (data->items.more) {
941                                 ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
942                                 glEnable(GL_BLEND);
943                                 UI_icon_draw(rect.xmax - 18, rect.ymin - 7, ICON_TRIA_DOWN);
944                                 glDisable(GL_BLEND);
945                         }
946                         if (data->items.offset) {
947                                 ui_searchbox_butrect(&rect, data, 0);
948                                 glEnable(GL_BLEND);
949                                 UI_icon_draw(rect.xmin, rect.ymax - 9, ICON_TRIA_UP);
950                                 glDisable(GL_BLEND);
951                         }
952                         
953                 }
954                 else {
955                         /* draw items */
956                         for (a = 0; a < data->items.totitem; a++) {
957                                 ui_searchbox_butrect(&rect, data, a);
958                                 
959                                 /* widget itself */
960                                 ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a],
961                                                   (a == data->active) ? UI_ACTIVE : 0, data->use_sep);
962                                 
963                         }
964                         /* indicate more */
965                         if (data->items.more) {
966                                 ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
967                                 glEnable(GL_BLEND);
968                                 UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
969                                 glDisable(GL_BLEND);
970                         }
971                         if (data->items.offset) {
972                                 ui_searchbox_butrect(&rect, data, 0);
973                                 glEnable(GL_BLEND);
974                                 UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP);
975                                 glDisable(GL_BLEND);
976                         }
977                 }
978         }
979 }
980
981 static void ui_searchbox_region_free_cb(ARegion *ar)
982 {
983         uiSearchboxData *data = ar->regiondata;
984         int a;
985
986         /* free search data */
987         for (a = 0; a < data->items.maxitem; a++) {
988                 MEM_freeN(data->items.names[a]);
989         }
990         MEM_freeN(data->items.names);
991         MEM_freeN(data->items.pointers);
992         MEM_freeN(data->items.icons);
993         
994         MEM_freeN(data);
995         ar->regiondata = NULL;
996 }
997
998 ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but)
999 {
1000         wmWindow *win = CTX_wm_window(C);
1001         uiStyle *style = UI_GetStyle();
1002         static ARegionType type;
1003         ARegion *ar;
1004         uiSearchboxData *data;
1005         float aspect = but->block->aspect;
1006         rctf rect_fl;
1007         rcti rect_i;
1008         int winx /*, winy */, ofsx, ofsy;
1009         int i;
1010         
1011         /* create area region */
1012         ar = ui_add_temporary_region(CTX_wm_screen(C));
1013         
1014         memset(&type, 0, sizeof(ARegionType));
1015         type.draw = ui_searchbox_region_draw_cb;
1016         type.free = ui_searchbox_region_free_cb;
1017         type.regionid = RGN_TYPE_TEMPORARY;
1018         ar->type = &type;
1019         
1020         /* create searchbox data */
1021         data = MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
1022
1023         /* set font, get bb */
1024         data->fstyle = style->widget; /* copy struct */
1025         data->fstyle.align = UI_STYLE_TEXT_CENTER;
1026         ui_fontscale(&data->fstyle.points, aspect);
1027         uiStyleFontSet(&data->fstyle);
1028         
1029         ar->regiondata = data;
1030         
1031         /* special case, hardcoded feature, not draw backdrop when called from menus,
1032          * assume for design that popup already added it */
1033         if (but->block->flag & UI_BLOCK_SEARCH_MENU)
1034                 data->noback = true;
1035         
1036         if (but->a1 > 0 && but->a2 > 0) {
1037                 data->preview = true;
1038                 data->prv_rows = but->a1;
1039                 data->prv_cols = but->a2;
1040         }
1041
1042         /* only show key shortcuts when needed (not rna buttons) [#36699] */
1043         if (but->rnaprop == NULL) {
1044                 data->use_sep = true;
1045         }
1046         
1047         /* compute position */
1048         if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
1049                 int width = UI_ThemeMenuShadowWidth();
1050                 /* this case is search menu inside other menu */
1051                 /* we copy region size */
1052
1053                 ar->winrct = butregion->winrct;
1054                 
1055                 /* widget rect, in region coords */
1056                 data->bbox.xmin = width;
1057                 data->bbox.xmax = BLI_rcti_size_x(&ar->winrct) - width;
1058                 data->bbox.ymin = width;
1059                 data->bbox.ymax = BLI_rcti_size_y(&ar->winrct) - width;
1060                 
1061                 /* check if button is lower half */
1062                 if (but->rect.ymax < BLI_rctf_cent_y(&but->block->rect)) {
1063                         data->bbox.ymin += BLI_rctf_size_y(&but->rect);
1064                 }
1065                 else {
1066                         data->bbox.ymax -= BLI_rctf_size_y(&but->rect);
1067                 }
1068         }
1069         else {
1070                 const int searchbox_width = uiSearchBoxWidth();
1071                 const int shadow_width = UI_ThemeMenuShadowWidth();
1072
1073                 rect_fl.xmin = but->rect.xmin - 5;   /* align text with button */
1074                 rect_fl.xmax = but->rect.xmax + 5;   /* symmetrical */
1075                 rect_fl.ymax = but->rect.ymin;
1076                 rect_fl.ymin = rect_fl.ymax - uiSearchBoxHeight();
1077
1078                 ofsx = (but->block->panel) ? but->block->panel->ofsx : 0;
1079                 ofsy = (but->block->panel) ? but->block->panel->ofsy : 0;
1080
1081                 BLI_rctf_translate(&rect_fl, ofsx, ofsy);
1082         
1083                 /* minimal width */
1084                 if (BLI_rctf_size_x(&rect_fl) < searchbox_width) {
1085                         rect_fl.xmax = rect_fl.xmin + searchbox_width;
1086                 }
1087                 
1088                 /* copy to int, gets projected if possible too */
1089                 BLI_rcti_rctf_copy(&rect_i, &rect_fl);
1090                 
1091                 if (butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
1092                         UI_view2d_to_region_no_clip(&butregion->v2d, rect_fl.xmin, rect_fl.ymin, &rect_i.xmin, &rect_i.ymin);
1093                         UI_view2d_to_region_no_clip(&butregion->v2d, rect_fl.xmax, rect_fl.ymax, &rect_i.xmax, &rect_i.ymax);
1094                 }
1095
1096                 BLI_rcti_translate(&rect_i, butregion->winrct.xmin, butregion->winrct.ymin);
1097
1098                 winx = WM_window_pixels_x(win);
1099                 // winy = WM_window_pixels_y(win);  /* UNUSED */
1100                 //wm_window_get_size(win, &winx, &winy);
1101                 
1102                 if (rect_i.xmax > winx) {
1103                         /* super size */
1104                         if (rect_i.xmax > winx + rect_i.xmin) {
1105                                 rect_i.xmax = winx;
1106                                 rect_i.xmin = 0;
1107                         }
1108                         else {
1109                                 rect_i.xmin -= rect_i.xmax - winx;
1110                                 rect_i.xmax = winx;
1111                         }
1112                 }
1113
1114                 if (rect_i.ymin < 0) {
1115                         int newy1 = but->rect.ymax + ofsy;
1116
1117                         if (butregion->v2d.cur.xmin != butregion->v2d.cur.xmax)
1118                                 UI_view2d_to_region_no_clip(&butregion->v2d, 0, newy1, NULL, &newy1);
1119
1120                         newy1 += butregion->winrct.ymin;
1121
1122                         rect_i.ymax = BLI_rcti_size_y(&rect_i) + newy1;
1123                         rect_i.ymin = newy1;
1124                 }
1125
1126                 /* widget rect, in region coords */
1127                 data->bbox.xmin = shadow_width;
1128                 data->bbox.xmax = BLI_rcti_size_x(&rect_i) + shadow_width;
1129                 data->bbox.ymin = shadow_width;
1130                 data->bbox.ymax = BLI_rcti_size_y(&rect_i) + shadow_width;
1131                 
1132                 /* region bigger for shadow */
1133                 ar->winrct.xmin = rect_i.xmin - shadow_width;
1134                 ar->winrct.xmax = rect_i.xmax + shadow_width;
1135                 ar->winrct.ymin = rect_i.ymin - shadow_width;
1136                 ar->winrct.ymax = rect_i.ymax;
1137         }
1138         
1139         /* adds subwindow */
1140         ED_region_init(C, ar);
1141         
1142         /* notify change and redraw */
1143         ED_region_tag_redraw(ar);
1144         
1145         /* prepare search data */
1146         if (data->preview) {
1147                 data->items.maxitem = data->prv_rows * data->prv_cols;
1148         }
1149         else {
1150                 data->items.maxitem = SEARCH_ITEMS;
1151         }
1152         data->items.maxstrlen = but->hardmax;
1153         data->items.totitem = 0;
1154         data->items.names = MEM_callocN(data->items.maxitem * sizeof(void *), "search names");
1155         data->items.pointers = MEM_callocN(data->items.maxitem * sizeof(void *), "search pointers");
1156         data->items.icons = MEM_callocN(data->items.maxitem * sizeof(int), "search icons");
1157         for (i = 0; i < data->items.maxitem; i++)
1158                 data->items.names[i] = MEM_callocN(but->hardmax + 1, "search pointers");
1159         
1160         return ar;
1161 }
1162
1163 void ui_searchbox_free(bContext *C, ARegion *ar)
1164 {
1165         ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
1166 }
1167
1168 /* sets red alert if button holds a string it can't find */
1169 /* XXX weak: search_func adds all partial matches... */
1170 void ui_but_search_test(uiBut *but)
1171 {
1172         uiSearchItems *items;
1173         int x1;
1174
1175         /* possibly very large lists (such as ID datablocks) only
1176          * only validate string RNA buts (not pointers) */
1177         if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
1178                 return;
1179         }
1180
1181         items = MEM_callocN(sizeof(uiSearchItems), "search items");
1182
1183         /* setup search struct */
1184         items->maxitem = 10;
1185         items->maxstrlen = 256;
1186         items->names = MEM_callocN(items->maxitem * sizeof(void *), "search names");
1187         for (x1 = 0; x1 < items->maxitem; x1++)
1188                 items->names[x1] = MEM_callocN(but->hardmax + 1, "search names");
1189         
1190         but->search_func(but->block->evil_C, but->search_arg, but->drawstr, items);
1191         
1192         /* only redalert when we are sure of it, this can miss cases when >10 matches */
1193         if (items->totitem == 0) {
1194                 uiButSetFlag(but, UI_BUT_REDALERT);
1195         }
1196         else if (items->more == 0) {
1197                 if (uiSearchItemFindIndex(items, but->drawstr) == -1) {
1198                         uiButSetFlag(but, UI_BUT_REDALERT);
1199                 }
1200         }
1201         
1202         for (x1 = 0; x1 < items->maxitem; x1++) {
1203                 MEM_freeN(items->names[x1]);
1204         }
1205         MEM_freeN(items->names);
1206         MEM_freeN(items);
1207 }
1208
1209
1210 /************************* Creating Menu Blocks **********************/
1211
1212 /* position block relative to but, result is in window space */
1213 static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
1214 {
1215         uiBut *bt;
1216         uiSafetyRct *saferct;
1217         rctf butrct;
1218         /*float aspect;*/ /*UNUSED*/
1219         int xsize, ysize, xof = 0, yof = 0, center;
1220         short dir1 = 0, dir2 = 0;
1221         
1222         /* transform to window coordinates, using the source button region/block */
1223         butrct = but->rect;
1224
1225         ui_block_to_window_fl(butregion, but->block, &butrct.xmin, &butrct.ymin);
1226         ui_block_to_window_fl(butregion, but->block, &butrct.xmax, &butrct.ymax);
1227
1228         /* widget_roundbox_set has this correction too, keep in sync */
1229         if (but->type != PULLDOWN) {
1230                 if (but->drawflag & UI_BUT_ALIGN_TOP)
1231                         butrct.ymax += U.pixelsize;
1232                 if (but->drawflag & UI_BUT_ALIGN_LEFT)
1233                         butrct.xmin -= U.pixelsize;
1234         }
1235         
1236         /* calc block rect */
1237         if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) {
1238                 if (block->buttons.first) {
1239                         BLI_rctf_init_minmax(&block->rect);
1240
1241                         for (bt = block->buttons.first; bt; bt = bt->next) {
1242                                 BLI_rctf_union(&block->rect, &bt->rect);
1243                         }
1244                 }
1245                 else {
1246                         /* we're nice and allow empty blocks too */
1247                         block->rect.xmin = block->rect.ymin = 0;
1248                         block->rect.xmax = block->rect.ymax = 20;
1249                 }
1250         }
1251                 
1252         /* aspect = (float)(BLI_rcti_size_x(&block->rect) + 4);*/ /*UNUSED*/
1253         ui_block_to_window_fl(butregion, but->block, &block->rect.xmin, &block->rect.ymin);
1254         ui_block_to_window_fl(butregion, but->block, &block->rect.xmax, &block->rect.ymax);
1255
1256         //block->rect.xmin -= 2.0; block->rect.ymin -= 2.0;
1257         //block->rect.xmax += 2.0; block->rect.ymax += 2.0;
1258         
1259         xsize = BLI_rctf_size_x(&block->rect) + 0.2f * UI_UNIT_X;  /* 4 for shadow */
1260         ysize = BLI_rctf_size_y(&block->rect) + 0.2f * UI_UNIT_Y;
1261         /* aspect /= (float)xsize;*/ /*UNUSED*/
1262
1263         {
1264                 int left = 0, right = 0, top = 0, down = 0;
1265                 int winx, winy;
1266                 // int offscreen;
1267
1268                 winx = WM_window_pixels_x(window);
1269                 winy = WM_window_pixels_y(window);
1270                 // wm_window_get_size(window, &winx, &winy);
1271
1272                 if (block->direction & UI_CENTER) center = ysize / 2;
1273                 else center = 0;
1274                 
1275                 /* check if there's space at all */
1276                 if (butrct.xmin - xsize > 0.0f) left = 1;
1277                 if (butrct.xmax + xsize < winx) right = 1;
1278                 if (butrct.ymin - ysize + center > 0.0f) down = 1;
1279                 if (butrct.ymax + ysize - center < winy) top = 1;
1280
1281                 if (top == 0 && down == 0) {
1282                         if (butrct.ymin - ysize < winy - butrct.ymax - ysize)
1283                                 top = 1;
1284                         else
1285                                 down = 1;
1286                 }
1287                 
1288                 dir1 = block->direction & UI_DIRECTION;
1289
1290                 /* secundary directions */
1291                 if (dir1 & (UI_TOP | UI_DOWN)) {
1292                         if (dir1 & UI_LEFT) dir2 = UI_LEFT;
1293                         else if (dir1 & UI_RIGHT) dir2 = UI_RIGHT;
1294                         dir1 &= (UI_TOP | UI_DOWN);
1295                 }
1296
1297                 if ((dir2 == 0) && (dir1 == UI_LEFT || dir1 == UI_RIGHT)) dir2 = UI_DOWN;
1298                 if ((dir2 == 0) && (dir1 == UI_TOP  || dir1 == UI_DOWN))  dir2 = UI_LEFT;
1299                 
1300                 /* no space at all? don't change */
1301                 if (left || right) {
1302                         if (dir1 == UI_LEFT && left == 0) dir1 = UI_RIGHT;
1303                         if (dir1 == UI_RIGHT && right == 0) dir1 = UI_LEFT;
1304                         /* this is aligning, not append! */
1305                         if (dir2 == UI_LEFT && right == 0) dir2 = UI_RIGHT;
1306                         if (dir2 == UI_RIGHT && left == 0) dir2 = UI_LEFT;
1307                 }
1308                 if (down || top) {
1309                         if (dir1 == UI_TOP && top == 0) dir1 = UI_DOWN;
1310                         if (dir1 == UI_DOWN && down == 0) dir1 = UI_TOP;
1311                         if (dir2 == UI_TOP && top == 0) dir2 = UI_DOWN;
1312                         if (dir2 == UI_DOWN && down == 0) dir2 = UI_TOP;
1313                 }
1314
1315                 if (dir1 == UI_LEFT) {
1316                         xof = butrct.xmin - block->rect.xmax;
1317                         if (dir2 == UI_TOP) yof = butrct.ymin - block->rect.ymin - center - MENU_PADDING;
1318                         else yof = butrct.ymax - block->rect.ymax + center + MENU_PADDING;
1319                 }
1320                 else if (dir1 == UI_RIGHT) {
1321                         xof = butrct.xmax - block->rect.xmin;
1322                         if (dir2 == UI_TOP) yof = butrct.ymin - block->rect.ymin - center - MENU_PADDING;
1323                         else yof = butrct.ymax - block->rect.ymax + center + MENU_PADDING;
1324                 }
1325                 else if (dir1 == UI_TOP) {
1326                         yof = butrct.ymax - block->rect.ymin;
1327                         if (dir2 == UI_RIGHT) xof = butrct.xmax - block->rect.xmax;
1328                         else xof = butrct.xmin - block->rect.xmin;
1329                         /* changed direction? */
1330                         if ((dir1 & block->direction) == 0) {
1331                                 if (block->direction & UI_SHIFT_FLIPPED)
1332                                         xof += dir2 == UI_LEFT ? 25 : -25;
1333                                 uiBlockFlipOrder(block);
1334                         }
1335                 }
1336                 else if (dir1 == UI_DOWN) {
1337                         yof = butrct.ymin - block->rect.ymax;
1338                         if (dir2 == UI_RIGHT) xof = butrct.xmax - block->rect.xmax;
1339                         else xof = butrct.xmin - block->rect.xmin;
1340                         /* changed direction? */
1341                         if ((dir1 & block->direction) == 0) {
1342                                 if (block->direction & UI_SHIFT_FLIPPED)
1343                                         xof += dir2 == UI_LEFT ? 25 : -25;
1344                                 uiBlockFlipOrder(block);
1345                         }
1346                 }
1347
1348                 /* and now we handle the exception; no space below or to top */
1349                 if (top == 0 && down == 0) {
1350                         if (dir1 == UI_LEFT || dir1 == UI_RIGHT) {
1351                                 /* align with bottom of screen */
1352                                 // yof = ysize; (not with menu scrolls)
1353                         }
1354                 }
1355                 
1356                 /* or no space left or right */
1357                 if (left == 0 && right == 0) {
1358                         if (dir1 == UI_TOP || dir1 == UI_DOWN) {
1359                                 /* align with left size of screen */
1360                                 xof = -block->rect.xmin + 5;
1361                         }
1362                 }
1363                 
1364 #if 0
1365                 /* clamp to window bounds, could be made into an option if its ever annoying */
1366                 if (     (offscreen = (block->rect.ymin + yof)) < 0) yof -= offscreen;   /* bottom */
1367                 else if ((offscreen = (block->rect.ymax + yof) - winy) > 0) yof -= offscreen;  /* top */
1368                 if (     (offscreen = (block->rect.xmin + xof)) < 0) xof -= offscreen;   /* left */
1369                 else if ((offscreen = (block->rect.xmax + xof) - winx) > 0) xof -= offscreen;  /* right */
1370 #endif
1371         }
1372         
1373         /* apply offset, buttons in window coords */
1374         
1375         for (bt = block->buttons.first; bt; bt = bt->next) {
1376                 ui_block_to_window_fl(butregion, but->block, &bt->rect.xmin, &bt->rect.ymin);
1377                 ui_block_to_window_fl(butregion, but->block, &bt->rect.xmax, &bt->rect.ymax);
1378
1379                 BLI_rctf_translate(&bt->rect, xof, yof);
1380
1381                 bt->aspect = 1.0f;
1382                 /* ui_check_but recalculates drawstring size in pixels */
1383                 ui_check_but(bt);
1384         }
1385         
1386         BLI_rctf_translate(&block->rect, xof, yof);
1387
1388         /* safety calculus */
1389         {
1390                 const float midx = BLI_rctf_cent_x(&butrct);
1391                 const float midy = BLI_rctf_cent_y(&butrct);
1392                 
1393                 /* when you are outside parent button, safety there should be smaller */
1394                 
1395                 /* parent button to left */
1396                 if (midx < block->rect.xmin) block->safety.xmin = block->rect.xmin - 3;
1397                 else block->safety.xmin = block->rect.xmin - 40;
1398                 /* parent button to right */
1399                 if (midx > block->rect.xmax) block->safety.xmax = block->rect.xmax + 3;
1400                 else block->safety.xmax = block->rect.xmax + 40;
1401
1402                 /* parent button on bottom */
1403                 if (midy < block->rect.ymin) block->safety.ymin = block->rect.ymin - 3;
1404                 else block->safety.ymin = block->rect.ymin - 40;
1405                 /* parent button on top */
1406                 if (midy > block->rect.ymax) block->safety.ymax = block->rect.ymax + 3;
1407                 else block->safety.ymax = block->rect.ymax + 40;
1408
1409                 /* exception for switched pulldowns... */
1410                 if (dir1 && (dir1 & block->direction) == 0) {
1411                         if (dir2 == UI_RIGHT) block->safety.xmax = block->rect.xmax + 3;
1412                         if (dir2 == UI_LEFT) block->safety.xmin = block->rect.xmin - 3;
1413                 }
1414                 block->direction = dir1;
1415         }
1416
1417         /* keep a list of these, needed for pulldown menus */
1418         saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1419         saferct->parent = butrct;
1420         saferct->safety = block->safety;
1421         BLI_freelistN(&block->saferct);
1422         BLI_duplicatelist(&block->saferct, &but->block->saferct);
1423         BLI_addhead(&block->saferct, saferct);
1424 }
1425
1426 static void ui_block_region_draw(const bContext *C, ARegion *ar)
1427 {
1428         uiBlock *block;
1429
1430         for (block = ar->uiblocks.first; block; block = block->next)
1431                 uiDrawBlock(C, block);
1432 }
1433
1434 static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
1435 {
1436         uiBut *bt;
1437         float xofs = 0.0f;
1438         int width = UI_SCREEN_MARGIN;
1439         int winx, winy;
1440
1441         if (block->flag & UI_BLOCK_NO_WIN_CLIP) {
1442                 return;
1443         }
1444
1445         winx = WM_window_pixels_x(window);
1446         winy = WM_window_pixels_y(window);
1447         
1448         /* shift menus to right if outside of view */
1449         if (block->rect.xmin < width) {
1450                 xofs = (width - block->rect.xmin);
1451                 block->rect.xmin += xofs;
1452                 block->rect.xmax += xofs;
1453         }
1454         /* or shift to left if outside of view */
1455         if (block->rect.xmax > winx - width) {
1456                 xofs = winx - width - block->rect.xmax;
1457                 block->rect.xmin += xofs;
1458                 block->rect.xmax += xofs;
1459         }
1460         
1461         if (block->rect.ymin < width)
1462                 block->rect.ymin = width;
1463         if (block->rect.ymax > winy - MENU_TOP)
1464                 block->rect.ymax = winy - MENU_TOP;
1465         
1466         /* ensure menu items draw inside left/right boundary */
1467         for (bt = block->buttons.first; bt; bt = bt->next) {
1468                 bt->rect.xmin += xofs;
1469                 bt->rect.xmax += xofs;
1470         }
1471
1472 }
1473
1474 void ui_popup_block_scrolltest(uiBlock *block)
1475 {
1476         uiBut *bt;
1477         
1478         block->flag &= ~(UI_BLOCK_CLIPBOTTOM | UI_BLOCK_CLIPTOP);
1479         
1480         for (bt = block->buttons.first; bt; bt = bt->next)
1481                 bt->flag &= ~UI_SCROLLED;
1482         
1483         if (block->buttons.first == block->buttons.last)
1484                 return;
1485         
1486         /* mark buttons that are outside boundary */
1487         for (bt = block->buttons.first; bt; bt = bt->next) {
1488                 if (bt->rect.ymin < block->rect.ymin) {
1489                         bt->flag |= UI_SCROLLED;
1490                         block->flag |= UI_BLOCK_CLIPBOTTOM;
1491                 }
1492                 if (bt->rect.ymax > block->rect.ymax) {
1493                         bt->flag |= UI_SCROLLED;
1494                         block->flag |= UI_BLOCK_CLIPTOP;
1495                 }
1496         }
1497
1498         /* mark buttons overlapping arrows, if we have them */
1499         for (bt = block->buttons.first; bt; bt = bt->next) {
1500                 if (block->flag & UI_BLOCK_CLIPBOTTOM) {
1501                         if (bt->rect.ymin < block->rect.ymin + UI_MENU_SCROLL_ARROW)
1502                                 bt->flag |= UI_SCROLLED;
1503                 }
1504                 if (block->flag & UI_BLOCK_CLIPTOP) {
1505                         if (bt->rect.ymax > block->rect.ymax - UI_MENU_SCROLL_ARROW)
1506                                 bt->flag |= UI_SCROLLED;
1507                 }
1508         }
1509 }
1510
1511 uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but,
1512                                           uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func,
1513                                           void *arg)
1514 {
1515         wmWindow *window = CTX_wm_window(C);
1516         static ARegionType type;
1517         ARegion *ar;
1518         uiBlock *block;
1519         uiPopupBlockHandle *handle;
1520         uiSafetyRct *saferct;
1521         int width = UI_ThemeMenuShadowWidth();
1522
1523         /* create handle */
1524         handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
1525
1526         /* store context for operator */
1527         handle->ctx_area = CTX_wm_area(C);
1528         handle->ctx_region = CTX_wm_region(C);
1529         
1530         /* create area region */
1531         ar = ui_add_temporary_region(CTX_wm_screen(C));
1532         handle->region = ar;
1533
1534         memset(&type, 0, sizeof(ARegionType));
1535         type.draw = ui_block_region_draw;
1536         type.regionid = RGN_TYPE_TEMPORARY;
1537         ar->type = &type;
1538
1539         UI_add_region_handlers(&ar->handlers);
1540
1541         /* create ui block */
1542         if (create_func)
1543                 block = create_func(C, handle->region, arg);
1544         else
1545                 block = handle_create_func(C, handle, arg);
1546         
1547         if (block->handle) {
1548                 memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
1549                 MEM_freeN(handle);
1550                 handle = block->handle;
1551         }
1552         else
1553                 block->handle = handle;
1554
1555         ar->regiondata = handle;
1556
1557         /* set UI_BLOCK_NUMSELECT before uiEndBlock() so we get alphanumeric keys assigned */
1558         if (but) {
1559                 if (but->type == PULLDOWN) {
1560                         block->flag |= UI_BLOCK_NUMSELECT;
1561                 }
1562         }
1563         else {
1564                 block->flag |= UI_BLOCK_POPUP | UI_BLOCK_NUMSELECT;
1565         }
1566
1567         block->flag |= UI_BLOCK_LOOP;
1568
1569         if (!block->endblock)
1570                 uiEndBlock(C, block);
1571
1572         /* if this is being created from a button */
1573         if (but) {
1574                 block->aspect = but->block->aspect;
1575                 ui_block_position(window, butregion, but, block);
1576                 handle->direction = block->direction;
1577         }
1578         else {
1579                 /* keep a list of these, needed for pulldown menus */
1580                 saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1581                 saferct->safety = block->safety;
1582                 BLI_addhead(&block->saferct, saferct);
1583         }
1584
1585         /* clip block with window boundary */
1586         ui_popup_block_clip(window, block);
1587         
1588         /* the block and buttons were positioned in window space as in 2.4x, now
1589          * these menu blocks are regions so we bring it back to region space.
1590          * additionally we add some padding for the menu shadow or rounded menus */
1591         ar->winrct.xmin = block->rect.xmin - width;
1592         ar->winrct.xmax = block->rect.xmax + width;
1593         ar->winrct.ymin = block->rect.ymin - width;
1594         ar->winrct.ymax = block->rect.ymax + MENU_TOP;
1595         
1596         ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin);
1597
1598         /* adds subwindow */
1599         ED_region_init(C, ar);
1600
1601         /* checks which buttons are visible, sets flags to prevent draw (do after region init) */
1602         ui_popup_block_scrolltest(block);
1603         
1604         /* get winmat now that we actually have the subwindow */
1605         wmSubWindowSet(window, ar->swinid);
1606         
1607         wm_subwindow_getmatrix(window, ar->swinid, block->winmat);
1608         
1609         /* notify change and redraw */
1610         ED_region_tag_redraw(ar);
1611
1612         return handle;
1613 }
1614
1615 void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
1616 {
1617         ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
1618         
1619         if (handle->scrolltimer)
1620                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
1621         
1622         MEM_freeN(handle);
1623 }
1624
1625 /***************************** Menu Button ***************************/
1626
1627 #if 0
1628 static void ui_warp_pointer(int x, int y)
1629 {
1630         /* XXX 2.50 which function to use for this? */
1631         /* OSX has very poor mouse-warp support, it sends events;
1632          * this causes a menu being pressed immediately ... */
1633 #  ifndef __APPLE__
1634         warp_pointer(x, y);
1635 #  endif
1636 }
1637 #endif
1638
1639 /********************* Color Button ****************/
1640
1641 /* for picker, while editing hsv */
1642 void ui_set_but_hsv(uiBut *but)
1643 {
1644         float col[3];
1645         float *hsv = ui_block_hsv_get(but->block);
1646         
1647         hsv_to_rgb_v(hsv, col);
1648         ui_set_but_vectorf(but, col);
1649 }
1650
1651 /* also used by small picker, be careful with name checks below... */
1652 static void ui_update_block_buts_rgb(uiBlock *block, const float rgb[3])
1653 {
1654         uiBut *bt;
1655         float *hsv = ui_block_hsv_get(block);
1656         struct ColorManagedDisplay *display = NULL;
1657         
1658         /* this is to keep the H and S value when V is equal to zero
1659          * and we are working in HSV mode, of course!
1660          */
1661         rgb_to_hsv_compat_v(rgb, hsv);
1662
1663         if (block->color_profile)
1664                 display = ui_block_display_get(block);
1665         
1666         /* this updates button strings, is hackish... but button pointers are on stack of caller function */
1667         for (bt = block->buttons.first; bt; bt = bt->next) {
1668                 if (bt->rnaprop) {
1669                         
1670                         ui_set_but_vectorf(bt, rgb);
1671                         
1672                 }
1673                 else if (strcmp(bt->str, "Hex: ") == 0) {
1674                         float rgb_gamma[3];
1675                         double intpart;
1676                         char col[16];
1677                         
1678                         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1679                         
1680                         copy_v3_v3(rgb_gamma, rgb);
1681
1682                         if (display) {
1683                                 /* make a display version, for Hex code */
1684                                 IMB_colormanagement_scene_linear_to_display_v3(rgb_gamma, display);
1685                         }
1686                         
1687                         if (rgb_gamma[0] > 1.0f) rgb_gamma[0] = modf(rgb_gamma[0], &intpart);
1688                         if (rgb_gamma[1] > 1.0f) rgb_gamma[1] = modf(rgb_gamma[1], &intpart);
1689                         if (rgb_gamma[2] > 1.0f) rgb_gamma[2] = modf(rgb_gamma[2], &intpart);
1690
1691                         BLI_snprintf(col, sizeof(col), "%02X%02X%02X",
1692                                      FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
1693                         
1694                         strcpy(bt->poin, col);
1695                 }
1696                 else if (bt->str[1] == ' ') {
1697                         if (bt->str[0] == 'R') {
1698                                 ui_set_but_val(bt, rgb[0]);
1699                         }
1700                         else if (bt->str[0] == 'G') {
1701                                 ui_set_but_val(bt, rgb[1]);
1702                         }
1703                         else if (bt->str[0] == 'B') {
1704                                 ui_set_but_val(bt, rgb[2]);
1705                         }
1706                         else if (bt->str[0] == 'H') {
1707                                 ui_set_but_val(bt, hsv[0]);
1708                         }
1709                         else if (bt->str[0] == 'S') {
1710                                 ui_set_but_val(bt, hsv[1]);
1711                         }
1712                         else if (bt->str[0] == 'V') {
1713                                 ui_set_but_val(bt, hsv[2]);
1714                         }
1715                 }
1716
1717                 ui_check_but(bt);
1718         }
1719 }
1720
1721 static void do_picker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1722 {
1723         uiBut *but = (uiBut *)bt1;
1724         uiPopupBlockHandle *popup = but->block->handle;
1725         PropertyRNA *prop = but->rnaprop;
1726         PointerRNA ptr = but->rnapoin;
1727         float rgb[4];
1728         
1729         if (prop) {
1730                 RNA_property_float_get_array(&ptr, prop, rgb);
1731                 ui_update_block_buts_rgb(but->block, rgb);
1732         }
1733         
1734         if (popup)
1735                 popup->menuretval = UI_RETURN_UPDATE;
1736 }
1737
1738 static void do_hsv_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1739 {
1740         uiBut *but = (uiBut *)bt1;
1741         uiPopupBlockHandle *popup = but->block->handle;
1742         float rgb[3];
1743         float *hsv = ui_block_hsv_get(but->block);
1744         
1745         hsv_to_rgb_v(hsv, rgb);
1746         
1747         ui_update_block_buts_rgb(but->block, rgb);
1748         
1749         if (popup)
1750                 popup->menuretval = UI_RETURN_UPDATE;
1751 }
1752
1753 static void do_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
1754 {
1755         uiBut *but = (uiBut *)bt1;
1756         uiPopupBlockHandle *popup = but->block->handle;
1757         char *hexcol = (char *)hexcl;
1758         float rgb[3];
1759         
1760         hex_to_rgb(hexcol, rgb, rgb + 1, rgb + 2);
1761         
1762         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1763         if (but->block->color_profile) {
1764                 /* so we need to linearise it for Blender */
1765                 ui_block_to_scene_linear_v3(but->block, rgb);
1766         }
1767         
1768         ui_update_block_buts_rgb(but->block, rgb);
1769         
1770         if (popup)
1771                 popup->menuretval = UI_RETURN_UPDATE;
1772 }
1773
1774 static void close_popup_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1775 {
1776         uiBut *but = (uiBut *)bt1;
1777         uiPopupBlockHandle *popup = but->block->handle;
1778         
1779         if (popup)
1780                 popup->menuretval = UI_RETURN_OK;
1781 }
1782
1783 static void picker_new_hide_reveal(uiBlock *block, short colormode)
1784 {
1785         uiBut *bt;
1786         
1787         /* tag buttons */
1788         for (bt = block->buttons.first; bt; bt = bt->next) {
1789                 if (bt->func == do_picker_rna_cb && bt->type == NUMSLI && bt->rnaindex != 3) {
1790                         /* RGB sliders (color circle and alpha are always shown) */
1791                         if (colormode == 0) bt->flag &= ~UI_HIDDEN;
1792                         else bt->flag |= UI_HIDDEN;
1793                 }
1794                 else if (bt->func == do_hsv_rna_cb) {
1795                         /* HSV sliders */
1796                         if (colormode == 1) bt->flag &= ~UI_HIDDEN;
1797                         else bt->flag |= UI_HIDDEN;
1798                 }
1799                 else if (bt->func == do_hex_rna_cb || bt->type == LABEL) {
1800                         /* hex input or gamma correction status label */
1801                         if (colormode == 2) bt->flag &= ~UI_HIDDEN;
1802                         else bt->flag |= UI_HIDDEN;
1803                 }
1804         }
1805 }
1806
1807 static void do_picker_new_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1808 {
1809         uiBut *bt = bt1;
1810         short colormode = ui_get_but_val(bt);
1811         picker_new_hide_reveal(bt->block, colormode);
1812 }
1813
1814 #define PICKER_H    (7.5f * U.widget_unit)
1815 #define PICKER_W    (7.5f * U.widget_unit)
1816 #define PICKER_SPACE    (0.3f * U.widget_unit)
1817 #define PICKER_BAR      (0.7f * U.widget_unit)
1818
1819 #define PICKER_TOTAL_W  (PICKER_W + PICKER_SPACE + PICKER_BAR)
1820
1821 static void circle_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop)
1822 {
1823         uiBut *bt;
1824         
1825         /* HS circle */
1826         bt = uiDefButR_prop(block, HSVCIRCLE, 0, "", 0, 0, PICKER_H, PICKER_W, ptr, prop, -1, 0.0, 0.0, 0, 0, "Color");
1827         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1828         
1829         /* value */
1830         bt = uiDefButR_prop(block, HSVCUBE, 0, "", PICKER_W + PICKER_SPACE, 0, PICKER_BAR, PICKER_H, ptr, prop, -1, 0.0, 0.0, UI_GRAD_V_ALT, 0, "Value");
1831         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1832 }
1833
1834
1835 static void square_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int type)
1836 {
1837         uiBut *bt;
1838         int bartype = type + 3;
1839         
1840         /* HS square */
1841         bt = uiDefButR_prop(block, HSVCUBE, 0, "",   0, PICKER_BAR + PICKER_SPACE, PICKER_TOTAL_W, PICKER_H, ptr, prop, -1, 0.0, 0.0, type, 0, "Color");
1842         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1843         
1844         /* value */
1845         bt = uiDefButR_prop(block, HSVCUBE, 0, "",       0, 0, PICKER_TOTAL_W, PICKER_BAR, ptr, prop, -1, 0.0, 0.0, bartype, 0, "Value");
1846         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1847 }
1848
1849
1850 /* a HS circle, V slider, rgb/hsv/hex sliders */
1851 static void uiBlockPicker(uiBlock *block, float rgba[4], PointerRNA *ptr, PropertyRNA *prop, bool show_picker)
1852 {
1853         static short colormode = 0;  /* temp? 0=rgb, 1=hsv, 2=hex */
1854         uiBut *bt;
1855         int width, butwidth;
1856         static char tip[50];
1857         static char hexcol[128];
1858         float rgb_gamma[3];
1859         float softmin, softmax, hardmin, hardmax, step, precision;
1860         float *hsv = ui_block_hsv_get(block);
1861         int yco;
1862         
1863         ui_block_hsv_get(block);
1864         
1865         width = PICKER_TOTAL_W;
1866         butwidth = width - 1.5f * UI_UNIT_X;
1867         
1868         /* existence of profile means storage is in linear color space, with display correction */
1869         /* XXX That tip message is not use anywhere! */
1870         if (!block->color_profile) {
1871                 BLI_strncpy(tip, N_("Value in Display Color Space"), sizeof(tip));
1872                 copy_v3_v3(rgb_gamma, rgba);
1873         }
1874         else {
1875                 BLI_strncpy(tip, N_("Value in Linear RGB Color Space"), sizeof(tip));
1876
1877                 /* make a display version, for Hex code */
1878                 copy_v3_v3(rgb_gamma, rgba);
1879                 ui_block_to_display_space_v3(block, rgb_gamma);
1880         }
1881         
1882         /* sneaky way to check for alpha */
1883         rgba[3] = FLT_MAX;
1884
1885         RNA_property_float_ui_range(ptr, prop, &softmin, &softmax, &step, &precision);
1886         RNA_property_float_range(ptr, prop, &hardmin, &hardmax);
1887         RNA_property_float_get_array(ptr, prop, rgba);
1888
1889         switch (U.color_picker_type) {
1890                 case USER_CP_CIRCLE:
1891                         circle_picker(block, ptr, prop);
1892                         break;
1893                 case USER_CP_SQUARE_SV:
1894                         square_picker(block, ptr, prop, UI_GRAD_SV);
1895                         break;
1896                 case USER_CP_SQUARE_HS:
1897                         square_picker(block, ptr, prop, UI_GRAD_HS);
1898                         break;
1899                 case USER_CP_SQUARE_HV:
1900                         square_picker(block, ptr, prop, UI_GRAD_HV);
1901                         break;
1902         }
1903         
1904         /* mode */
1905         yco = -1.5f * UI_UNIT_Y;
1906         uiBlockBeginAlign(block);
1907         bt = uiDefButS(block, ROW, 0, IFACE_("RGB"), 0, yco, width / 3, UI_UNIT_Y, &colormode, 0.0, 0.0, 0, 0, "");
1908         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
1909         bt = uiDefButS(block, ROW, 0, IFACE_("HSV"), width / 3, yco, width / 3, UI_UNIT_Y, &colormode, 0.0, 1.0, 0, 0, "");
1910         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
1911         bt = uiDefButS(block, ROW, 0, IFACE_("Hex"), 2 * width / 3, yco, width / 3, UI_UNIT_Y, &colormode, 0.0, 2.0, 0, 0, "");
1912         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
1913         uiBlockEndAlign(block);
1914
1915         yco = -3.0f * UI_UNIT_Y;
1916         if (show_picker) {
1917                 bt = uiDefIconButO(block, BUT, "UI_OT_eyedropper_color", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, butwidth + 10, yco, UI_UNIT_X, UI_UNIT_Y, NULL);
1918                 uiButSetFunc(bt, close_popup_cb, bt, NULL);
1919         }
1920         
1921         /* RGB values */
1922         uiBlockBeginAlign(block);
1923         bt = uiDefButR_prop(block, NUMSLI, 0, IFACE_("R:"),  0, yco, butwidth, UI_UNIT_Y, ptr, prop, 0, 0.0, 0.0, 0, 3, TIP_("Red"));
1924         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1925         bt = uiDefButR_prop(block, NUMSLI, 0, IFACE_("G:"),  0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, ptr, prop, 1, 0.0, 0.0, 0, 3, TIP_("Green"));
1926         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1927         bt = uiDefButR_prop(block, NUMSLI, 0, IFACE_("B:"),  0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, ptr, prop, 2, 0.0, 0.0, 0, 3, TIP_("Blue"));
1928         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1929
1930         /* could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", ICON_NONE);
1931          * but need to use uiButSetFunc for updating other fake buttons */
1932         
1933         /* HSV values */
1934         yco = -3.0f * UI_UNIT_Y;
1935         uiBlockBeginAlign(block);
1936         bt = uiDefButF(block, NUMSLI, 0, IFACE_("H:"),   0, yco, butwidth, UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, TIP_("Hue"));
1937         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
1938         bt = uiDefButF(block, NUMSLI, 0, IFACE_("S:"),   0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, hsv + 1, 0.0, 1.0, 10, 3, TIP_("Saturation"));
1939         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
1940         bt = uiDefButF(block, NUMSLI, 0, IFACE_("V:"),   0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, hsv + 2, 0.0, softmax, 10, 3, TIP_("Value"));
1941         bt->hardmax = hardmax;  /* not common but rgb  may be over 1.0 */
1942         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
1943         uiBlockEndAlign(block);
1944
1945         if (rgba[3] != FLT_MAX) {
1946                 bt = uiDefButR_prop(block, NUMSLI, 0, IFACE_("A "),  0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, ptr, prop, 3, 0.0, 0.0, 0, 3, TIP_("Alpha"));
1947                 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1948         }
1949         else {
1950                 rgba[3] = 1.0f;
1951         }
1952
1953         BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
1954
1955         yco = -3.0f * UI_UNIT_Y;
1956         bt = uiDefBut(block, TEX, 0, IFACE_("Hex: "), 0, yco, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, TIP_("Hex triplet for color (#RRGGBB)"));
1957         uiButSetFunc(bt, do_hex_rna_cb, bt, hexcol);
1958         uiDefBut(block, LABEL, 0, IFACE_("(Gamma Corrected)"), 0, yco - UI_UNIT_Y, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1959
1960         rgb_to_hsv_v(rgba, hsv);
1961
1962         picker_new_hide_reveal(block, colormode);
1963 }
1964
1965
1966 static int ui_picker_small_wheel_cb(const bContext *UNUSED(C), uiBlock *block, const wmEvent *event)
1967 {
1968         float add = 0.0f;
1969         
1970         if (event->type == WHEELUPMOUSE)
1971                 add = 0.05f;
1972         else if (event->type == WHEELDOWNMOUSE)
1973                 add = -0.05f;
1974         
1975         if (add != 0.0f) {
1976                 uiBut *but;
1977                 
1978                 for (but = block->buttons.first; but; but = but->next) {
1979                         if (but->type == HSVCUBE && but->active == NULL) {
1980                                 uiPopupBlockHandle *popup = block->handle;
1981                                 float rgb[3];
1982                                 float *hsv = ui_block_hsv_get(block);
1983                                 
1984                                 ui_get_but_vectorf(but, rgb);
1985                                 
1986                                 rgb_to_hsv_compat_v(rgb, hsv);
1987                                 hsv[2] = CLAMPIS(hsv[2] + add, 0.0f, 1.0f);
1988                                 hsv_to_rgb_v(hsv, rgb);
1989
1990                                 ui_set_but_vectorf(but, rgb);
1991                                 
1992                                 ui_update_block_buts_rgb(block, rgb);
1993                                 if (popup)
1994                                         popup->menuretval = UI_RETURN_UPDATE;
1995                                 
1996                                 return 1;
1997                         }
1998                 }
1999         }
2000         return 0;
2001 }
2002
2003 uiBlock *ui_block_func_COLOR(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
2004 {
2005         uiBut *but = arg_but;
2006         uiBlock *block;
2007         bool show_picker = true;
2008         
2009         block = uiBeginBlock(C, handle->region, __func__, UI_EMBOSS);
2010         
2011         if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2012                 block->color_profile = false;
2013         }
2014
2015         if (but->block) {
2016                 /* if color block is invoked from a popup we wouldn't be able to set color properly
2017                  * this is because color picker will close popups first and then will try to figure
2018                  * out active button RNA, and of course it'll fail
2019                  */
2020                 show_picker = (but->block->flag & UI_BLOCK_POPUP) == 0;
2021         }
2022         
2023         copy_v3_v3(handle->retvec, but->editvec);
2024         
2025         uiBlockPicker(block, handle->retvec, &but->rnapoin, but->rnaprop, show_picker);
2026         
2027         block->flag = UI_BLOCK_LOOP | UI_BLOCK_REDRAW | UI_BLOCK_KEEP_OPEN | UI_BLOCK_OUT_1 | UI_BLOCK_MOVEMOUSE_QUIT;
2028         uiBoundsBlock(block, 0.5 * UI_UNIT_X);
2029         
2030         block->block_event_func = ui_picker_small_wheel_cb;
2031         
2032         /* and lets go */
2033         block->direction = UI_TOP;
2034         
2035         return block;
2036 }
2037
2038 /************************ Popup Menu Memory ****************************/
2039
2040 static int ui_popup_string_hash(const char *str)
2041 {
2042         /* sometimes button contains hotkey, sometimes not, strip for proper compare */
2043         int hash;
2044         char *delimit = strchr(str, UI_SEP_CHAR);
2045
2046         if (delimit) *delimit = '\0';
2047         hash = BLI_ghashutil_strhash(str);
2048         if (delimit) *delimit = UI_SEP_CHAR;
2049
2050         return hash;
2051 }
2052
2053 static int ui_popup_menu_hash(const char *str)
2054 {
2055         return BLI_ghashutil_strhash(str);
2056 }
2057
2058 /* but == NULL read, otherwise set */
2059 uiBut *ui_popup_menu_memory(uiBlock *block, uiBut *but)
2060 {
2061         static int mem[256], first = 1;
2062         int hash = block->puphash;
2063         
2064         if (first) {
2065                 /* init */
2066                 memset(mem, -1, sizeof(mem));
2067                 first = 0;
2068         }
2069
2070         if (but) {
2071                 /* set */
2072                 mem[hash & 255] = ui_popup_string_hash(but->str);
2073                 return NULL;
2074         }
2075         else {
2076                 /* get */
2077                 for (but = block->buttons.first; but; but = but->next)
2078                         if (ui_popup_string_hash(but->str) == mem[hash & 255])
2079                                 return but;
2080
2081                 return NULL;
2082         }
2083 }
2084
2085 /******************** Popup Menu with callback or string **********************/
2086
2087 struct uiPopupMenu {
2088         uiBlock *block;
2089         uiLayout *layout;
2090         uiBut *but;
2091
2092         int mx, my;
2093         bool popup, slideout;
2094
2095         uiMenuCreateFunc menu_func;
2096         void *menu_arg;
2097 };
2098
2099 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
2100 {
2101         uiBlock *block;
2102         uiBut *bt;
2103         uiPopupMenu *pup = arg_pup;
2104         int offset[2], direction, minwidth, width, height, flip;
2105
2106         if (pup->menu_func) {
2107                 pup->block->handle = handle;
2108                 pup->menu_func(C, pup->layout, pup->menu_arg);
2109                 pup->block->handle = NULL;
2110         }
2111
2112         if (pup->but) {
2113                 /* minimum width to enforece */
2114                 minwidth = BLI_rctf_size_x(&pup->but->rect);
2115
2116                 /* settings (typically rna-enum-popups) show above the button,
2117                  * menu's like file-menu, show below */
2118                 if (pup->but->type == PULLDOWN || (uiButGetMenuType(pup->but) != NULL)) {
2119                         direction = UI_DOWN;
2120                         flip = 1;
2121                 }
2122                 else {
2123                         direction = UI_TOP;
2124                         flip = 0;
2125                 }
2126         }
2127         else {
2128                 minwidth = 50;
2129                 direction = UI_DOWN;
2130                 flip = 1;
2131         }
2132
2133         block = pup->block;
2134         
2135         /* in some cases we create the block before the region,
2136          * so we set it delayed here if necessary */
2137         if (BLI_findindex(&handle->region->uiblocks, block) == -1)
2138                 uiBlockSetRegion(block, handle->region);
2139
2140         block->direction = direction;
2141
2142         uiBlockLayoutResolve(block, &width, &height);
2143
2144         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2145         
2146         if (pup->popup) {
2147                 uiBlockSetFlag(block, UI_BLOCK_LOOP | UI_BLOCK_REDRAW | UI_BLOCK_NUMSELECT);
2148                 uiBlockSetDirection(block, direction);
2149
2150                 /* offset the mouse position, possibly based on earlier selection */
2151                 if ((block->flag & UI_BLOCK_POPUP_MEMORY) &&
2152                     (bt = ui_popup_menu_memory(block, NULL)))
2153                 {
2154                         /* position mouse on last clicked item, at 0.8*width of the
2155                          * button, so it doesn't overlap the text too much, also note
2156                          * the offset is negative because we are inverse moving the
2157                          * block to be under the mouse */
2158                         offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect));
2159                         offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y);
2160                 }
2161                 else {
2162                         /* position mouse at 0.8*width of the button and below the tile
2163                          * on the first item */
2164                         offset[0] = 0;
2165                         for (bt = block->buttons.first; bt; bt = bt->next)
2166                                 offset[0] = min_ii(offset[0], -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)));
2167
2168                         offset[1] = 2.1 * UI_UNIT_Y;
2169                 }
2170
2171                 block->minbounds = minwidth;
2172                 uiMenuPopupBoundsBlock(block, 1, offset[0], offset[1]);
2173         }
2174         else {
2175                 /* for a header menu we set the direction automatic */
2176                 if (!pup->slideout && flip) {
2177                         ScrArea *sa = CTX_wm_area(C);
2178                         if (sa && sa->headertype == HEADERDOWN) {
2179                                 ARegion *ar = CTX_wm_region(C);
2180                                 if (ar && ar->regiontype == RGN_TYPE_HEADER) {
2181                                         uiBlockSetDirection(block, UI_TOP);
2182                                         uiBlockFlipOrder(block);
2183                                 }
2184                         }
2185                 }
2186
2187                 block->minbounds = minwidth;
2188                 uiTextBoundsBlock(block, 3.0f * UI_UNIT_X);
2189         }
2190
2191         /* if menu slides out of other menu, override direction */
2192         if (pup->slideout)
2193                 uiBlockSetDirection(block, UI_RIGHT);
2194
2195         uiEndBlock(C, block);
2196
2197         return pup->block;
2198 }
2199
2200 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but,
2201                                          uiMenuCreateFunc menu_func, void *arg)
2202 {
2203         wmWindow *window = CTX_wm_window(C);
2204         uiStyle *style = UI_GetStyleDraw();
2205         uiPopupBlockHandle *handle;
2206         uiPopupMenu *pup;
2207
2208         pup = MEM_callocN(sizeof(uiPopupMenu), __func__);
2209         pup->block = uiBeginBlock(C, NULL, __func__, UI_EMBOSSP);
2210         pup->block->flag |= UI_BLOCK_NUMSELECT;  /* default menus to numselect */
2211         pup->layout = uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, MENU_PADDING, style);
2212         pup->slideout = but ? ui_block_is_menu(but->block) : false;
2213         pup->but = but;
2214         uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
2215
2216         if (!but) {
2217                 /* no button to start from, means we are a popup */
2218                 pup->mx = window->eventstate->x;
2219                 pup->my = window->eventstate->y;
2220                 pup->popup = true;
2221                 pup->block->flag |= UI_BLOCK_NO_FLIP;
2222         }
2223         /* some enums reversing is strange, currently we have no good way to
2224          * reverse some enum's but not others, so reverse all so the first menu
2225          * items are always close to the mouse cursor */
2226         else {
2227 #if 0
2228                 /* if this is an rna button then we can assume its an enum
2229                  * flipping enums is generally not good since the order can be
2230                  * important [#28786] */
2231                 if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
2232                         pup->block->flag |= UI_BLOCK_NO_FLIP;
2233                 }
2234 #endif
2235                 if (but->context)
2236                         uiLayoutContextCopy(pup->layout, but->context);
2237         }
2238
2239         /* menu is created from a callback */
2240         pup->menu_func = menu_func;
2241         pup->menu_arg = arg;
2242         
2243         handle = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
2244
2245         if (!but) {
2246                 handle->popup = true;
2247
2248                 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2249                 WM_event_add_mousemove(C);
2250         }
2251         
2252         MEM_freeN(pup);
2253
2254         return handle;
2255 }
2256
2257 /******************** Popup Menu API with begin and end ***********************/
2258
2259 /* only return handler, and set optional title */
2260 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
2261 {
2262         uiStyle *style = UI_GetStyleDraw();
2263         uiPopupMenu *pup = MEM_callocN(sizeof(uiPopupMenu), "popup menu");
2264         uiBut *but;
2265
2266         pup->block = uiBeginBlock(C, NULL, __func__, UI_EMBOSSP);
2267         pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
2268         pup->block->puphash = ui_popup_menu_hash(title);
2269         pup->layout = uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, MENU_PADDING, style);
2270
2271         /* note, this intentionally differs from the menu & submenu default because many operators
2272          * use popups like this to select one of their options - where having invoke doesn't make sense */
2273         uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
2274
2275         /* create in advance so we can let buttons point to retval already */
2276         pup->block->handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2277         
2278         /* create title button */
2279         if (title[0]) {
2280                 char titlestr[256];
2281                 
2282                 if (icon) {
2283                         BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
2284                         uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2285                 }
2286                 else {
2287                         but = uiDefBut(pup->block, LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2288                         but->drawflag = UI_BUT_TEXT_LEFT;
2289                 }
2290
2291                 uiItemS(pup->layout);
2292         }
2293
2294         return pup;
2295 }
2296
2297 /* set the whole structure to work */
2298 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
2299 {
2300         wmWindow *window = CTX_wm_window(C);
2301         uiPopupBlockHandle *menu;
2302         
2303         pup->popup = true;
2304         pup->mx = window->eventstate->x;
2305         pup->my = window->eventstate->y;
2306         
2307         menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
2308         menu->popup = true;
2309         
2310         UI_add_popup_handlers(C, &window->modalhandlers, menu);
2311         WM_event_add_mousemove(C);
2312         
2313         MEM_freeN(pup);
2314 }
2315
2316 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
2317 {
2318         return pup->layout;
2319 }
2320
2321 /*************************** Standard Popup Menus ****************************/
2322
2323 void uiPupMenuReports(bContext *C, ReportList *reports)
2324 {
2325         Report *report;
2326
2327         uiPopupMenu *pup = NULL;
2328         uiLayout *layout;
2329
2330         if (!CTX_wm_window(C))
2331                 return;
2332
2333         for (report = reports->list.first; report; report = report->next) {
2334                 int icon;
2335                 const char *msg, *msg_next;
2336
2337                 if (report->type < reports->printlevel) {
2338                         continue;
2339                 }
2340
2341                 if (pup == NULL) {
2342                         char title[UI_MAX_DRAW_STR];
2343                         BLI_snprintf(title, sizeof(title), "%s: %s", IFACE_("Report"), report->typestr);
2344                         pup = uiPupMenuBegin(C, title, ICON_NONE);
2345                         layout = uiPupMenuLayout(pup);
2346                 }
2347                 else {
2348                         uiItemS(layout);
2349                 }
2350
2351                 /* split each newline into a label */
2352                 msg = report->message;
2353                 icon = uiIconFromReportType(report->type);
2354                 do {
2355                         char buf[UI_MAX_DRAW_STR];
2356                         msg_next = strchr(msg, '\n');
2357                         if (msg_next) {
2358                                 msg_next++;
2359                                 BLI_strncpy(buf, msg, MIN2(sizeof(buf), msg_next - msg));
2360                                 msg = buf;
2361                         }
2362                         uiItemL(layout, msg, icon);
2363                         icon = ICON_NONE;
2364                 } while ((msg = msg_next) && *msg);
2365         }
2366
2367         if (pup) {
2368                 uiPupMenuEnd(C, pup);
2369         }
2370 }
2371
2372 void uiPupMenuInvoke(bContext *C, const char *idname)
2373 {
2374         uiPopupMenu *pup;
2375         uiLayout *layout;
2376         Menu menu;
2377         MenuType *mt = WM_menutype_find(idname, true);
2378
2379         if (mt == NULL) {
2380                 printf("%s: named menu \"%s\" not found\n", __func__, idname);
2381                 return;
2382         }
2383
2384         if (mt->poll && mt->poll(C, mt) == 0)
2385                 return;
2386
2387         pup = uiPupMenuBegin(C, IFACE_(mt->label), ICON_NONE);
2388         layout = uiPupMenuLayout(pup);
2389
2390         menu.layout = layout;
2391         menu.type = mt;
2392
2393         if (G.debug & G_DEBUG_WM) {
2394                 printf("%s: opening menu \"%s\"\n", __func__, idname);
2395         }
2396
2397         mt->draw(C, &menu);
2398
2399         uiPupMenuEnd(C, pup);
2400 }
2401
2402
2403 /*************************** Popup Block API **************************/
2404
2405 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext)
2406 {
2407         wmWindow *window = CTX_wm_window(C);
2408         uiPopupBlockHandle *handle;
2409         
2410         handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2411         handle->popup = true;
2412         handle->optype = (opname) ? WM_operatortype_find(opname, 0) : NULL;
2413         handle->opcontext = opcontext;
2414         
2415         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2416         WM_event_add_mousemove(C);
2417 }
2418
2419 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2420 {
2421         uiPupBlockO(C, func, arg, NULL, 0);
2422 }
2423
2424 void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg)
2425 {
2426         wmWindow *window = CTX_wm_window(C);
2427         uiPopupBlockHandle *handle;
2428         
2429         handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2430         handle->popup = true;
2431         handle->retvalue = 1;
2432
2433         handle->popup_arg = arg;
2434         handle->popup_func = popup_func;
2435         handle->cancel_func = cancel_func;
2436         // handle->opcontext = opcontext;
2437         
2438         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2439         WM_event_add_mousemove(C);
2440 }
2441
2442 #if 0 /* UNUSED */
2443 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2444 {
2445         wmWindow *window = CTX_wm_window(C);
2446         uiPopupBlockHandle *handle;
2447         
2448         handle = ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2449         handle->popup = 1;
2450         handle->retvalue = 1;
2451
2452         handle->popup_arg = op;
2453         handle->popup_func = operator_cb;
2454         handle->cancel_func = confirm_cancel_operator;
2455         handle->opcontext = opcontext;
2456         
2457         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2458         WM_event_add_mousemove(C);
2459 }
2460 #endif
2461
2462 void uiPupBlockClose(bContext *C, uiBlock *block)
2463 {
2464         if (block->handle) {
2465                 wmWindow *win = CTX_wm_window(C);
2466
2467                 /* if loading new .blend while popup is open, window will be NULL */
2468                 if (win) {
2469                         UI_remove_popup_handlers(&win->modalhandlers, block->handle);
2470                         ui_popup_block_free(C, block->handle);
2471                 }
2472         }
2473 }
2474
2475 float *ui_block_hsv_get(uiBlock *block)
2476 {
2477         return block->_hsv;
2478 }