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