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