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