replace fixed sizes with sizeof when passing string length since size wasn't always...
[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
1615         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1616         
1617         /* compute menu data */
1618         md= decompose_menu_string(instr);
1619
1620         /* columns and row estimation */
1621         columns= (md->nitems+20)/20;
1622         if(columns<1)
1623                 columns= 1;
1624         if(columns>8)
1625                 columns= (md->nitems+25)/25;
1626         
1627         rows= md->nitems/columns;
1628         if(rows<1)
1629                 rows= 1;
1630         while(rows*columns<md->nitems)
1631                 rows++;
1632
1633         /* create title */
1634         if(md->title) {
1635                 if(md->titleicon) {
1636                         uiItemL(layout, md->title, md->titleicon);
1637                 }
1638                 else {
1639                         uiItemL(layout, md->title, ICON_NONE);
1640                         bt= block->buttons.last;
1641                         bt->flag= UI_TEXT_LEFT;
1642                 }
1643         }
1644
1645         /* inconsistent, but menus with labels do not look good flipped */
1646         entry= md->items;
1647         for(a=0; a<md->nitems; a++, entry++) {
1648                 if(entry->sepr && entry->str[0]) {
1649                         block->flag |= UI_BLOCK_NO_FLIP;
1650                         break;
1651                 }
1652         }
1653
1654         /* create items */
1655         split= uiLayoutSplit(layout, 0, 0);
1656
1657         for(a=0, b=0; a<md->nitems; a++, b++) {
1658                 if(block->flag & UI_BLOCK_NO_FLIP)
1659                         entry= &md->items[a];
1660                 else
1661                         entry= &md->items[md->nitems-a-1];
1662                 
1663                 /* new column on N rows or on separation label */
1664                 if((b % rows == 0) || (entry->sepr && entry->str[0])) {
1665                         column= uiLayoutColumn(split, 0);
1666                         b= 0;
1667                 }
1668
1669                 if(entry->sepr) {
1670                         uiItemL(column, entry->str, entry->icon);
1671                         bt= block->buttons.last;
1672                         bt->flag= UI_TEXT_LEFT;
1673                 }
1674                 else if(entry->icon) {
1675                         uiDefIconTextButF(block, BUTM|FLO, B_NOP, entry->icon, entry->str, 0, 0,
1676                                 UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1677                 }
1678                 else {
1679                         uiDefButF(block, BUTM|FLO, B_NOP, entry->str, 0, 0,
1680                                 UI_UNIT_X*5, UI_UNIT_X, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1681                 }
1682         }
1683         
1684         menudata_free(md);
1685 }
1686
1687 void ui_block_func_ICONROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1688 {
1689         uiBlock *block= uiLayoutGetBlock(layout);
1690         uiPopupBlockHandle *handle= block->handle;
1691         uiBut *but= arg_but;
1692         int a;
1693         
1694         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1695         
1696         for(a=(int)but->hardmin; a<=(int)but->hardmax; a++)
1697                 uiDefIconButF(block, BUTM|FLO, B_NOP, but->icon+(a-but->hardmin), 0, 0, UI_UNIT_X*5, UI_UNIT_Y,
1698                         &handle->retvalue, (float)a, 0.0, 0, 0, "");
1699 }
1700
1701 void ui_block_func_ICONTEXTROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1702 {
1703         uiBlock *block= uiLayoutGetBlock(layout);
1704         uiPopupBlockHandle *handle= block->handle;
1705         uiBut *but= arg_but, *bt;
1706         MenuData *md;
1707         MenuEntry *entry;
1708         int a;
1709         
1710         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1711
1712         md= decompose_menu_string(but->str);
1713
1714         /* title */
1715         if(md->title) {
1716                 bt= uiDefBut(block, LABEL, 0, md->title, 0, 0, UI_UNIT_X*5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1717                 bt->flag= UI_TEXT_LEFT;
1718         }
1719
1720         /* loop through the menu options and draw them out with icons & text labels */
1721         for(a=0; a<md->nitems; a++) {
1722                 entry= &md->items[md->nitems-a-1];
1723
1724                 if(entry->sepr)
1725                         uiItemS(layout);
1726                 else
1727                         uiDefIconTextButF(block, BUTM|FLO, B_NOP, (short)((but->icon)+(entry->retval-but->hardmin)), entry->str,
1728                                 0, 0, UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1729         }
1730
1731         menudata_free(md);
1732 }
1733
1734 #if 0
1735 static void ui_warp_pointer(int x, int y)
1736 {
1737         /* XXX 2.50 which function to use for this? */
1738         /* OSX has very poor mousewarp support, it sends events;
1739            this causes a menu being pressed immediately ... */
1740         #ifndef __APPLE__
1741         warp_pointer(x, y);
1742         #endif
1743 }
1744 #endif
1745
1746 /********************* Color Button ****************/
1747
1748 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1749 #define SPICK   110.0
1750 #define FPICK   180.0
1751 #define DPICK   6.0
1752 #define BPICK   24.0
1753
1754 /* for picker, while editing hsv */
1755 void ui_set_but_hsv(uiBut *but)
1756 {
1757         float col[3];
1758         float *hsv= ui_block_hsv_get(but->block);
1759         
1760         hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
1761         ui_set_but_vectorf(but, col);
1762 }
1763
1764 /* also used by small picker, be careful with name checks below... */
1765 static void ui_update_block_buts_rgb(uiBlock *block, float *rgb)
1766 {
1767         uiBut *bt;
1768         float *hsv= ui_block_hsv_get(block);
1769         
1770         /* this is to keep the H and S value when V is equal to zero
1771          * and we are working in HSV mode, of course!
1772          */
1773         rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
1774         
1775         // this updates button strings, is hackish... but button pointers are on stack of caller function
1776         for(bt= block->buttons.first; bt; bt= bt->next) {
1777                 if (bt->rnaprop) {
1778                         
1779                         ui_set_but_vectorf(bt, rgb);
1780                         
1781                 }
1782                 else if(strcmp(bt->str, "Hex: ")==0) {
1783                         float rgb_gamma[3];
1784                         double intpart;
1785                         char col[16];
1786                         
1787                         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1788                         
1789                         if (block->color_profile == BLI_PR_NONE) {
1790                                 copy_v3_v3(rgb_gamma, rgb);
1791                         } else {
1792                                 /* make an sRGB version, for Hex code */
1793                                 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1794                         }
1795                         
1796                         if (rgb_gamma[0] > 1.0f) rgb_gamma[0] = modf(rgb_gamma[0], &intpart);
1797                         if (rgb_gamma[1] > 1.0f) rgb_gamma[1] = modf(rgb_gamma[1], &intpart);
1798                         if (rgb_gamma[2] > 1.0f) rgb_gamma[2] = modf(rgb_gamma[2], &intpart);
1799
1800                         BLI_snprintf(col, sizeof(col), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
1801                         
1802                         strcpy(bt->poin, col);
1803                 }
1804                 else if(bt->str[1]==' ') {
1805                         if(bt->str[0]=='R') {
1806                                 ui_set_but_val(bt, rgb[0]);
1807                         }
1808                         else if(bt->str[0]=='G') {
1809                                 ui_set_but_val(bt, rgb[1]);
1810                         }
1811                         else if(bt->str[0]=='B') {
1812                                 ui_set_but_val(bt, rgb[2]);
1813                         }
1814                         else if(bt->str[0]=='H') {
1815                                 ui_set_but_val(bt, hsv[0]);
1816                         }
1817                         else if(bt->str[0]=='S') {
1818                                 ui_set_but_val(bt, hsv[1]);
1819                         }
1820                         else if(bt->str[0]=='V') {
1821                                 ui_set_but_val(bt, hsv[2]);
1822                         }
1823                 }               
1824
1825                 ui_check_but(bt);
1826         }
1827 }
1828
1829 static void do_picker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1830 {
1831         uiBut *but= (uiBut *)bt1;
1832         uiPopupBlockHandle *popup= but->block->handle;
1833         PropertyRNA *prop = but->rnaprop;
1834         PointerRNA ptr = but->rnapoin;
1835         float rgb[4];
1836         
1837         if (prop) {
1838                 RNA_property_float_get_array(&ptr, prop, rgb);
1839                 ui_update_block_buts_rgb(but->block, rgb);
1840         }
1841         
1842         if(popup)
1843                 popup->menuretval= UI_RETURN_UPDATE;
1844 }
1845
1846 static void do_hsv_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1847 {
1848         uiBut *but= (uiBut *)bt1;
1849         uiPopupBlockHandle *popup= but->block->handle;
1850         float rgb[3];
1851         float *hsv= ui_block_hsv_get(but->block);
1852         
1853         hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
1854         
1855         ui_update_block_buts_rgb(but->block, rgb);
1856         
1857         if(popup)
1858                 popup->menuretval= UI_RETURN_UPDATE;
1859 }
1860
1861 static void do_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
1862 {
1863         uiBut *but= (uiBut *)bt1;
1864         uiPopupBlockHandle *popup= but->block->handle;
1865         char *hexcol= (char *)hexcl;
1866         float rgb[3];
1867         
1868         hex_to_rgb(hexcol, rgb, rgb+1, rgb+2);
1869         
1870         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1871         if (but->block->color_profile != BLI_PR_NONE) {
1872                 /* so we need to linearise it for Blender */
1873                 srgb_to_linearrgb_v3_v3(rgb, rgb);
1874         }
1875         
1876         ui_update_block_buts_rgb(but->block, rgb);
1877         
1878         if(popup)
1879                 popup->menuretval= UI_RETURN_UPDATE;
1880 }
1881
1882 static void close_popup_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1883 {
1884         uiBut *but= (uiBut *)bt1;
1885         uiPopupBlockHandle *popup= but->block->handle;
1886         
1887         if(popup)
1888                 popup->menuretval= UI_RETURN_OK;
1889 }
1890
1891 static void picker_new_hide_reveal(uiBlock *block, short colormode)
1892 {
1893         uiBut *bt;
1894         
1895         /* tag buttons */
1896         for(bt= block->buttons.first; bt; bt= bt->next) {
1897                 
1898                 if (bt->type == LABEL) {
1899                         if( bt->str[1]=='G') {
1900                                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1901                                 else bt->flag |= UI_HIDDEN;
1902                         }
1903                 }
1904                 
1905                 if(bt->type==NUMSLI || bt->type==TEX) {
1906                         if( bt->str[1]=='e') {
1907                                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1908                                 else bt->flag |= UI_HIDDEN;
1909                         }
1910                         else if( ELEM3(bt->str[0], 'R', 'G', 'B')) {
1911                                 if(colormode==0) bt->flag &= ~UI_HIDDEN;
1912                                 else bt->flag |= UI_HIDDEN;
1913                         }
1914                         else if( ELEM3(bt->str[0], 'H', 'S', 'V')) {
1915                                 if(colormode==1) bt->flag &= ~UI_HIDDEN;
1916                                 else bt->flag |= UI_HIDDEN;
1917                         }
1918                 }
1919         }
1920 }
1921
1922 static void do_picker_new_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1923 {
1924         uiBut *bt= bt1;
1925         short colormode= ui_get_but_val(bt);
1926         picker_new_hide_reveal(bt->block, colormode);
1927 }
1928
1929 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1930 #define SPICK1  150.0
1931 #define DPICK1  6.0
1932
1933 #define PICKER_H        150
1934 #define PICKER_W        150
1935 #define PICKER_SPACE    6
1936 #define PICKER_BAR              14
1937
1938 #define PICKER_TOTAL_W  (PICKER_W+PICKER_SPACE+PICKER_BAR)
1939
1940 static void circle_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop)
1941 {
1942         uiBut *bt;
1943         
1944         /* HS circle */
1945         bt= uiDefButR_prop(block, HSVCIRCLE, 0, "",     0, 0, PICKER_H, PICKER_W, ptr, prop, 0, 0.0, 0.0, 0, 0, "Color");
1946         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1947         
1948         /* value */
1949         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");
1950         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1951 }
1952
1953
1954 static void square_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int type)
1955 {
1956         uiBut *bt;
1957         int bartype = type + 3;
1958         
1959         /* HS square */
1960         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");
1961         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1962         
1963         /* value */
1964         bt= uiDefButR_prop(block, HSVCUBE, 0, "",               0, 0, PICKER_TOTAL_W, PICKER_BAR, ptr, prop, 0, 0.0, 0.0, bartype, 0, "Value");
1965         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1966 }
1967
1968
1969 /* a HS circle, V slider, rgb/hsv/hex sliders */
1970 static void uiBlockPicker(uiBlock *block, float *rgb, PointerRNA *ptr, PropertyRNA *prop)
1971 {
1972         static short colormode= 0;      /* temp? 0=rgb, 1=hsv, 2=hex */
1973         uiBut *bt;
1974         int width, butwidth;
1975         static char tip[50];
1976         static char hexcol[128];
1977         float rgb_gamma[3];
1978         float min, max, step, precision;
1979         float *hsv= ui_block_hsv_get(block);
1980         
1981         ui_block_hsv_get(block);
1982         
1983         width= PICKER_TOTAL_W;
1984         butwidth = width - UI_UNIT_X - 10;
1985         
1986         /* existence of profile means storage is in linear color space, with display correction */
1987         if (block->color_profile == BLI_PR_NONE) {
1988                 BLI_strncpy(tip, "Value in Display Color Space", sizeof(tip));
1989                 copy_v3_v3(rgb_gamma, rgb);
1990         } else {
1991                 BLI_strncpy(tip, "Value in Linear RGB Color Space", sizeof(tip));
1992                 /* make an sRGB version, for Hex code */
1993                 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1994         }
1995         
1996         /* sneaky way to check for alpha */
1997         rgb[3]= FLT_MAX;
1998
1999         RNA_property_float_ui_range(ptr, prop, &min, &max, &step, &precision);
2000         RNA_property_float_get_array(ptr, prop, rgb);
2001
2002         switch (U.color_picker_type) {
2003                 case USER_CP_CIRCLE:
2004                         circle_picker(block, ptr, prop);
2005                         break;
2006                 case USER_CP_SQUARE_SV:
2007                         square_picker(block, ptr, prop, UI_GRAD_SV);
2008                         break;
2009                 case USER_CP_SQUARE_HS:
2010                         square_picker(block, ptr, prop, UI_GRAD_HS);
2011                         break;
2012                 case USER_CP_SQUARE_HV:
2013                         square_picker(block, ptr, prop, UI_GRAD_HV);
2014                         break;
2015         }
2016         
2017         /* mode */
2018         uiBlockBeginAlign(block);
2019         bt= uiDefButS(block, ROW, 0, "RGB",     0, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 0.0, 0, 0, "");
2020         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2021         bt= uiDefButS(block, ROW, 0, "HSV",     width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 1.0, 0, 0, "");
2022         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2023         bt= uiDefButS(block, ROW, 0, "Hex",     2*width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 2.0, 0, 0, "");
2024         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2025         uiBlockEndAlign(block);
2026
2027         bt= uiDefIconButO(block, BUT, "UI_OT_eyedropper", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, butwidth+10, -60, UI_UNIT_X, UI_UNIT_Y, NULL);
2028         uiButSetFunc(bt, close_popup_cb, bt, NULL);
2029         
2030         /* RGB values */
2031         uiBlockBeginAlign(block);
2032         bt= uiDefButR_prop(block, NUMSLI, 0, "R ",      0, -60, butwidth, UI_UNIT_Y, ptr, prop, 0, 0.0, 0.0, 0, 3, "Red");
2033         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2034         bt= uiDefButR_prop(block, NUMSLI, 0, "G ",      0, -80, butwidth, UI_UNIT_Y, ptr, prop, 1, 0.0, 0.0, 0, 3, "Green");
2035         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2036         bt= uiDefButR_prop(block, NUMSLI, 0, "B ",      0, -100, butwidth, UI_UNIT_Y, ptr, prop, 2, 0.0, 0.0, 0, 3, "Blue");
2037         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2038
2039         // could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", ICON_NONE);
2040         // but need to use uiButSetFunc for updating other fake buttons
2041         
2042         /* HSV values */
2043         uiBlockBeginAlign(block);
2044         bt= uiDefButF(block, NUMSLI, 0, "H ",   0, -60, butwidth, UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, "Hue");
2045         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2046         bt= uiDefButF(block, NUMSLI, 0, "S ",   0, -80, butwidth, UI_UNIT_Y, hsv+1, 0.0, 1.0, 10, 3, "Saturation");
2047         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2048         bt= uiDefButF(block, NUMSLI, 0, "V ",   0, -100, butwidth, UI_UNIT_Y, hsv+2, 0.0, max, 10, 3, "Value");
2049         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2050         uiBlockEndAlign(block);
2051
2052         if(rgb[3] != FLT_MAX) {
2053                 bt= uiDefButR_prop(block, NUMSLI, 0, "A ",      0, -120, butwidth, UI_UNIT_Y, ptr, prop, 3, 0.0, 0.0, 0, 0, "Alpha");
2054                 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2055         }
2056         else {
2057                 rgb[3]= 1.0f;
2058         }
2059
2060         BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
2061
2062         bt= uiDefBut(block, TEX, 0, "Hex: ", 0, -60, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
2063         uiButSetFunc(bt, do_hex_rna_cb, bt, hexcol);
2064         uiDefBut(block, LABEL, 0, "(Gamma Corrected)", 0, -80, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2065
2066         rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
2067
2068         picker_new_hide_reveal(block, colormode);
2069 }
2070
2071
2072 static int ui_picker_small_wheel_cb(const bContext *UNUSED(C), uiBlock *block, wmEvent *event)
2073 {
2074         float add= 0.0f;
2075         
2076         if(event->type==WHEELUPMOUSE)
2077                 add= 0.05f;
2078         else if(event->type==WHEELDOWNMOUSE)
2079                 add= -0.05f;
2080         
2081         if(add!=0.0f) {
2082                 uiBut *but;
2083                 
2084                 for(but= block->buttons.first; but; but= but->next) {
2085                         if(but->type==HSVCUBE && but->active==NULL) {
2086                                 uiPopupBlockHandle *popup= block->handle;
2087                                 float col[3];
2088                                 float *hsv= ui_block_hsv_get(block);
2089                                 
2090                                 ui_get_but_vectorf(but, col);
2091                                 
2092                                 rgb_to_hsv_compat(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
2093                                 hsv[2]= CLAMPIS(hsv[2]+add, 0.0f, 1.0f);
2094                                 hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
2095
2096                                 ui_set_but_vectorf(but, col);
2097                                 
2098                                 ui_update_block_buts_rgb(block, col);
2099                                 if(popup)
2100                                         popup->menuretval= UI_RETURN_UPDATE;
2101                                 
2102                                 return 1;
2103                         }
2104                 }
2105         }
2106         return 0;
2107 }
2108
2109 uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
2110 {
2111         uiBut *but= arg_but;
2112         uiBlock *block;
2113         
2114         block= uiBeginBlock(C, handle->region, __func__, UI_EMBOSS);
2115         
2116         if (but->rnaprop) {
2117                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2118                         block->color_profile = BLI_PR_NONE;
2119                 }
2120         }
2121         
2122         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2123         
2124         copy_v3_v3(handle->retvec, but->editvec);
2125         
2126         uiBlockPicker(block, handle->retvec, &but->rnapoin, but->rnaprop);
2127         
2128         block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN|UI_BLOCK_OUT_1;
2129         uiBoundsBlock(block, 10);
2130         
2131         block->block_event_func= ui_picker_small_wheel_cb;
2132         
2133         /* and lets go */
2134         block->direction= UI_TOP;
2135         
2136         return block;
2137 }
2138
2139 /************************ Popup Menu Memory ****************************/
2140
2141 static int ui_popup_string_hash(char *str)
2142 {
2143         /* sometimes button contains hotkey, sometimes not, strip for proper compare */
2144         int hash;
2145         char *delimit= strchr(str, '|');
2146
2147         if(delimit) *delimit= 0;
2148         hash= BLI_ghashutil_strhash(str);
2149         if(delimit) *delimit= '|';
2150
2151         return hash;
2152 }
2153
2154 static int ui_popup_menu_hash(const char *str)
2155 {
2156         return BLI_ghashutil_strhash(str);
2157 }
2158
2159 /* but == NULL read, otherwise set */
2160 uiBut *ui_popup_menu_memory(uiBlock *block, uiBut *but)
2161 {
2162         static int mem[256], first=1;
2163         int hash= block->puphash;
2164         
2165         if(first) {
2166                 /* init */
2167                 memset(mem, -1, sizeof(mem));
2168                 first= 0;
2169         }
2170
2171         if(but) {
2172                 /* set */
2173                 mem[hash & 255 ]= ui_popup_string_hash(but->str);
2174                 return NULL;
2175         }
2176         else {
2177                 /* get */
2178                 for(but=block->buttons.first; but; but=but->next)
2179                         if(ui_popup_string_hash(but->str) == mem[hash & 255])
2180                                 return but;
2181
2182                 return NULL;
2183         }
2184 }
2185
2186 /******************** Popup Menu with callback or string **********************/
2187
2188 struct uiPopupMenu {
2189         uiBlock *block;
2190         uiLayout *layout;
2191         uiBut *but;
2192
2193         int mx, my, popup, slideout;
2194         int startx, starty, maxrow;
2195
2196         uiMenuCreateFunc menu_func;
2197         void *menu_arg;
2198 };
2199
2200 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
2201 {
2202         uiBlock *block;
2203         uiBut *bt;
2204         uiPopupMenu *pup= arg_pup;
2205         int offset[2], direction, minwidth, width, height, flip;
2206
2207         if(pup->menu_func) {
2208                 pup->block->handle= handle;
2209                 pup->menu_func(C, pup->layout, pup->menu_arg);
2210                 pup->block->handle= NULL;
2211         }
2212
2213         if(pup->but) {
2214                 /* minimum width to enforece */
2215                 minwidth= pup->but->x2 - pup->but->x1;
2216
2217                 if(pup->but->type == PULLDOWN || pup->but->menu_create_func) {
2218                         direction= UI_DOWN;
2219                         flip= 1;
2220                 }
2221                 else {
2222                         direction= UI_TOP;
2223                         flip= 0;
2224                 }
2225         }
2226         else {
2227                 minwidth= 50;
2228                 direction= UI_DOWN;
2229                 flip= 1;
2230         }
2231
2232         block= pup->block;
2233         
2234         /* in some cases we create the block before the region,
2235            so we set it delayed here if necessary */
2236         if(BLI_findindex(&handle->region->uiblocks, block) == -1)
2237                 uiBlockSetRegion(block, handle->region);
2238
2239         block->direction= direction;
2240
2241         uiBlockLayoutResolve(block, &width, &height);
2242
2243         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2244         
2245         if(pup->popup) {
2246                 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
2247                 uiBlockSetDirection(block, direction);
2248
2249                 /* offset the mouse position, possibly based on earlier selection */
2250                 if((block->flag & UI_BLOCK_POPUP_MEMORY) &&
2251                         (bt= ui_popup_menu_memory(block, NULL))) {
2252                         /* position mouse on last clicked item, at 0.8*width of the
2253                            button, so it doesn't overlap the text too much, also note
2254                            the offset is negative because we are inverse moving the
2255                            block to be under the mouse */
2256                         offset[0]= -(bt->x1 + 0.8f*(bt->x2 - bt->x1));
2257                         offset[1]= -(bt->y1 + 0.5f*UI_UNIT_Y);
2258                 }
2259                 else {
2260                         /* position mouse at 0.8*width of the button and below the tile
2261                            on the first item */
2262                         offset[0]= 0;
2263                         for(bt=block->buttons.first; bt; bt=bt->next)
2264                                 offset[0]= MIN2(offset[0], -(bt->x1 + 0.8f*(bt->x2 - bt->x1)));
2265
2266                         offset[1]= 1.5*UI_UNIT_Y;
2267                 }
2268
2269                 block->minbounds= minwidth;
2270                 uiMenuPopupBoundsBlock(block, 1, offset[0], offset[1]);
2271         }
2272         else {
2273                 /* for a header menu we set the direction automatic */
2274                 if(!pup->slideout && flip) {
2275                         ScrArea *sa= CTX_wm_area(C);
2276                         if(sa && sa->headertype==HEADERDOWN) {
2277                                 ARegion *ar= CTX_wm_region(C);
2278                                 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
2279                                         uiBlockSetDirection(block, UI_TOP);
2280                                         uiBlockFlipOrder(block);
2281                                 }
2282                         }
2283                 }
2284
2285                 block->minbounds= minwidth;
2286                 uiTextBoundsBlock(block, 50);
2287         }
2288
2289         /* if menu slides out of other menu, override direction */
2290         if(pup->slideout)
2291                 uiBlockSetDirection(block, UI_RIGHT);
2292
2293         uiEndBlock(C, block);
2294
2295         return pup->block;
2296 }
2297
2298 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg, char *str)
2299 {
2300         wmWindow *window= CTX_wm_window(C);
2301         uiStyle *style= UI_GetStyle();
2302         uiPopupBlockHandle *handle;
2303         uiPopupMenu *pup;
2304         pup= MEM_callocN(sizeof(uiPopupMenu), __func__);
2305         pup->block= uiBeginBlock(C, NULL, __func__, UI_EMBOSSP);
2306         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2307         pup->slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
2308         pup->but= but;
2309         uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
2310
2311         if(!but) {
2312                 /* no button to start from, means we are a popup */
2313                 pup->mx= window->eventstate->x;
2314                 pup->my= window->eventstate->y;
2315                 pup->popup= 1;
2316                 pup->block->flag |= UI_BLOCK_NO_FLIP;
2317         }
2318         /* some enums reversing is strange, currently we have no good way to
2319          * reverse some enum's but not others, so reverse all so the first menu
2320          * items are always close to the mouse cursor */
2321 #if 0
2322         else {
2323                 /* if this is an rna button then we can assume its an enum
2324                  * flipping enums is generally not good since the order can be
2325                  * important [#28786] */
2326                 if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
2327                         pup->block->flag |= UI_BLOCK_NO_FLIP;
2328                 }
2329         }
2330 #endif
2331
2332         if(str) {
2333                 /* menu is created from a string */
2334                 pup->menu_func= ui_block_func_MENUSTR;
2335                 pup->menu_arg= str;
2336         }
2337         else {
2338                 /* menu is created from a callback */
2339                 pup->menu_func= menu_func;
2340                 pup->menu_arg= arg;
2341         }
2342         
2343         handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
2344
2345         if(!but) {
2346                 handle->popup= 1;
2347
2348                 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2349                 WM_event_add_mousemove(C);
2350         }
2351         
2352         MEM_freeN(pup);
2353
2354         return handle;
2355 }
2356
2357 /******************** Popup Menu API with begin and end ***********************/
2358
2359 /* only return handler, and set optional title */
2360 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
2361 {
2362         uiStyle *style= UI_GetStyle();
2363         uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "popup menu");
2364         uiBut *but;
2365         
2366         pup->block= uiBeginBlock(C, NULL, __func__, UI_EMBOSSP);
2367         pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
2368         pup->block->puphash= ui_popup_menu_hash(title);
2369         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2370         uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
2371
2372         /* create in advance so we can let buttons point to retval already */
2373         pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2374         
2375         /* create title button */
2376         if(title && title[0]) {
2377                 char titlestr[256];
2378                 
2379                 if(icon) {
2380                         BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
2381                         uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2382                 }
2383                 else {
2384                         but= uiDefBut(pup->block, LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2385                         but->flag= UI_TEXT_LEFT;
2386                 }
2387         }
2388
2389         return pup;
2390 }
2391
2392 /* set the whole structure to work */
2393 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
2394 {
2395         wmWindow *window= CTX_wm_window(C);
2396         uiPopupBlockHandle *menu;
2397         
2398         pup->popup= 1;
2399         pup->mx= window->eventstate->x;
2400         pup->my= window->eventstate->y;
2401         
2402         menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
2403         menu->popup= 1;
2404         
2405         UI_add_popup_handlers(C, &window->modalhandlers, menu);
2406         WM_event_add_mousemove(C);
2407         
2408         MEM_freeN(pup);
2409 }
2410
2411 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
2412 {
2413         return pup->layout;
2414 }
2415
2416 /*************************** Standard Popup Menus ****************************/
2417
2418 static void operator_name_cb(bContext *C, void *arg, int retval)
2419 {
2420         const char *opname= arg;
2421
2422         if(opname && retval > 0)
2423                 WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
2424 }
2425
2426 static void operator_cb(bContext *C, void *arg, int retval)
2427 {
2428         wmOperator *op= arg;
2429         
2430         if(op && retval > 0)
2431                 WM_operator_call(C, op);
2432         else
2433                 WM_operator_free(op);
2434 }
2435
2436 static void confirm_cancel_operator(void *opv)
2437 {
2438         WM_operator_free(opv);
2439 }
2440
2441 static void vconfirm_opname(bContext *C, const char *opname, const char *title, const char *itemfmt, va_list ap)
2442 {
2443         uiPopupBlockHandle *handle;
2444         char *s, buf[512];
2445
2446         s= buf;
2447         if (title) s+= sprintf(s, "%s%%t|", title);
2448         vsnprintf(s, sizeof(buf) - (s - buf), itemfmt, ap);
2449         buf[sizeof(buf) - 1]= '\0';
2450
2451         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2452
2453         handle->popup_func= operator_name_cb;
2454         handle->popup_arg= (void *)opname;
2455 }
2456
2457 static void confirm_operator(bContext *C, wmOperator *op, const char *title, const char *item)
2458 {
2459         uiPopupBlockHandle *handle;
2460         char *s, buf[512];
2461         
2462         s= buf;
2463         if (title) s+= sprintf(s, "%s%%t|%s", title, item);
2464         (void)s;
2465         
2466         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2467
2468         handle->popup_func= operator_cb;
2469         handle->popup_arg= op;
2470         handle->cancel_func= confirm_cancel_operator;
2471 }
2472
2473 void uiPupMenuOkee(bContext *C, const char *opname, const char *str, ...)
2474 {
2475         va_list ap;
2476         char titlestr[256];
2477
2478         BLI_snprintf(titlestr, sizeof(titlestr), "OK? %%i%d", ICON_QUESTION);
2479
2480         va_start(ap, str);
2481         vconfirm_opname(C, opname, titlestr, str, ap);
2482         va_end(ap);
2483 }
2484
2485 /* note, only call this is the file exists,
2486  * the case where the file does not exist so can be saved without a
2487  * popup must be checked for already, since saving from here
2488  * will free the operator which will break invoke().
2489  * The operator state for this is implicitly OPERATOR_RUNNING_MODAL */
2490 void uiPupMenuSaveOver(bContext *C, wmOperator *op, const char *filename)
2491 {
2492         confirm_operator(C, op, "Save Over", filename);
2493 }
2494
2495 void uiPupMenuNotice(bContext *C, const char *str, ...)
2496 {
2497         va_list ap;
2498
2499         va_start(ap, str);
2500         vconfirm_opname(C, NULL, NULL, str, ap);
2501         va_end(ap);
2502 }
2503
2504 void uiPupMenuError(bContext *C, const char *str, ...)
2505 {
2506         va_list ap;
2507         char nfmt[256];
2508         char titlestr[256];
2509
2510         BLI_snprintf(titlestr, sizeof(titlestr), "Error %%i%d", ICON_ERROR);
2511
2512         BLI_strncpy(nfmt, str, sizeof(nfmt));
2513
2514         va_start(ap, str);
2515         vconfirm_opname(C, NULL, titlestr, nfmt, ap);
2516         va_end(ap);
2517 }
2518
2519 void uiPupMenuReports(bContext *C, ReportList *reports)
2520 {
2521         Report *report;
2522         DynStr *ds;
2523         char *str;
2524
2525         if(!reports || !reports->list.first)
2526                 return;
2527         if(!CTX_wm_window(C))
2528                 return;
2529
2530         ds= BLI_dynstr_new();
2531
2532         for(report=reports->list.first; report; report=report->next) {
2533                 if(report->type < reports->printlevel)
2534                         ; /* pass */
2535                 else if(report->type >= RPT_ERROR)
2536                         BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
2537                 else if(report->type >= RPT_WARNING)
2538                         BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
2539                 else if(report->type >= RPT_INFO)
2540                         BLI_dynstr_appendf(ds, "Info %%i%d%%t|%s", ICON_INFO, report->message);
2541         }
2542
2543         str= BLI_dynstr_get_cstring(ds);
2544         if(str[0] != '\0')
2545                 ui_popup_menu_create(C, NULL, NULL, NULL, NULL, str);
2546         MEM_freeN(str);
2547
2548         BLI_dynstr_free(ds);
2549 }
2550
2551 void uiPupMenuInvoke(bContext *C, const char *idname)
2552 {
2553         uiPopupMenu *pup;
2554         uiLayout *layout;
2555         Menu menu;
2556         MenuType *mt= WM_menutype_find(idname, TRUE);
2557
2558         if(mt==NULL) {
2559                 printf("%s: named menu \"%s\" not found\n", __func__, idname);
2560                 return;
2561         }
2562
2563         if(mt->poll && mt->poll(C, mt)==0)
2564                 return;
2565
2566         pup= uiPupMenuBegin(C, mt->label, ICON_NONE);
2567         layout= uiPupMenuLayout(pup);
2568
2569         menu.layout= layout;
2570         menu.type= mt;
2571
2572         mt->draw(C, &menu);
2573
2574         uiPupMenuEnd(C, pup);
2575 }
2576
2577
2578 /*************************** Popup Block API **************************/
2579
2580 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext)
2581 {
2582         wmWindow *window= CTX_wm_window(C);
2583         uiPopupBlockHandle *handle;
2584         
2585         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2586         handle->popup= 1;
2587         handle->optype= (opname)? WM_operatortype_find(opname, 0): NULL;
2588         handle->opcontext= opcontext;
2589         
2590         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2591         WM_event_add_mousemove(C);
2592 }
2593
2594 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2595 {
2596         uiPupBlockO(C, func, arg, NULL, 0);
2597 }
2598
2599 void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg)
2600 {
2601         wmWindow *window= CTX_wm_window(C);
2602         uiPopupBlockHandle *handle;
2603         
2604         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2605         handle->popup= 1;
2606         handle->retvalue= 1;
2607
2608         handle->popup_arg= arg;
2609         handle->popup_func= popup_func;
2610         handle->cancel_func= cancel_func;
2611         // handle->opcontext= opcontext;
2612         
2613         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2614         WM_event_add_mousemove(C);
2615 }
2616
2617 #if 0 /* UNUSED */
2618 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2619 {
2620         wmWindow *window= CTX_wm_window(C);
2621         uiPopupBlockHandle *handle;
2622         
2623         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2624         handle->popup= 1;
2625         handle->retvalue= 1;
2626
2627         handle->popup_arg= op;
2628         handle->popup_func= operator_cb;
2629         handle->cancel_func= confirm_cancel_operator;
2630         handle->opcontext= opcontext;
2631         
2632         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2633         WM_event_add_mousemove(C);
2634 }
2635 #endif
2636
2637 void uiPupBlockClose(bContext *C, uiBlock *block)
2638 {
2639         if(block->handle) {
2640                 UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, block->handle);
2641                 ui_popup_block_free(C, block->handle);
2642         }
2643 }
2644
2645 float *ui_block_hsv_get(uiBlock *block)
2646 {
2647         return block->_hsv;
2648 }