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