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