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