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