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