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