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