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