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