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