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