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