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