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