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