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