svn merge ^/trunk/blender -r42680:42722
[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 ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
353 {
354         uiStyle *style= UI_GetStyle();
355         static ARegionType type;
356         ARegion *ar;
357         uiTooltipData *data;
358         IDProperty *prop;
359         char buf[512];
360         float fonth, fontw, aspect= but->block->aspect;
361         float x1f, x2f, y1f, y2f;
362         int x1, x2, y1, y2, winx, winy, ofsx, ofsy, w, h, a;
363
364         if(but->flag & UI_BUT_NO_TOOLTIP)
365                 return NULL;
366
367         /* create tooltip data */
368         data= MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
369
370         /* special case, enum rna buttons only have enum item description, use general enum description too before the specific one */
371         if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
372                 const char *descr= RNA_property_description(but->rnaprop);
373                 if(descr && descr[0]) {
374                         BLI_strncpy(data->lines[data->totline], descr, sizeof(data->lines[0]));
375                         data->color[data->totline]= 0xFFFFFF;
376                         data->totline++;
377                 }
378
379                 if(ELEM(but->type, ROW, MENU)) {
380                         EnumPropertyItem *item;
381                         int i, totitem, free;
382                         int value = (but->type == ROW)? but->hardmax: ui_get_but_val(but);
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 == value) {
388                                         if(item[i].description && 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]= 0xDDDDDD;
391                                                 data->totline++;
392                                         }
393                                         break;
394                                 }
395                         }
396
397                         if(free)
398                                 MEM_freeN(item);
399                 }
400         }
401         
402         if(but->tip && but->tip[0] != '\0') {
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, TRUE, buf, sizeof(buf))) {
413                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("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_("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_("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_("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_("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_("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_("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_("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                         MenuType *mt= uiButGetMenuType(but);
498                         if (mt) {
499                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("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
619 void ui_tooltip_free(bContext *C, ARegion *ar)
620 {
621         ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
622 }
623
624
625 /************************* Creating Search Box **********************/
626
627 struct uiSearchItems {
628         int maxitem, totitem, maxstrlen;
629         
630         int offset, offset_i; /* offset for inserting in array */
631         int more;  /* flag indicating there are more items */
632         
633         char **names;
634         void **pointers;
635         int *icons;
636
637         AutoComplete *autocpl;
638         void *active;
639 };
640
641 typedef struct uiSearchboxData {
642         rcti bbox;
643         uiFontStyle fstyle;
644         uiSearchItems items;
645         int active;             /* index in items array */
646         int noback;             /* when menu opened with enough space for this */
647         int preview;    /* draw thumbnail previews, rather than list */
648         int prv_rows, prv_cols;
649 } uiSearchboxData;
650
651 #define SEARCH_ITEMS    10
652
653 /* exported for use by search callbacks */
654 /* returns zero if nothing to add */
655 int uiSearchItemAdd(uiSearchItems *items, const char *name, void *poin, int iconid)
656 {
657         /* hijack for autocomplete */
658         if(items->autocpl) {
659                 autocomplete_do_name(items->autocpl, name);
660                 return 1;
661         }
662         
663         /* hijack for finding active item */
664         if(items->active) {
665                 if(poin==items->active)
666                         items->offset_i= items->totitem;
667                 items->totitem++;
668                 return 1;
669         }
670         
671         if(items->totitem>=items->maxitem) {
672                 items->more= 1;
673                 return 0;
674         }
675         
676         /* skip first items in list */
677         if(items->offset_i > 0) {
678                 items->offset_i--;
679                 return 1;
680         }
681         
682         if(items->names)
683                 BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
684         if(items->pointers)
685                 items->pointers[items->totitem]= poin;
686         if(items->icons)
687                 items->icons[items->totitem]= iconid;
688         
689         items->totitem++;
690         
691         return 1;
692 }
693
694 int uiSearchBoxhHeight(void)
695 {
696         return SEARCH_ITEMS*UI_UNIT_Y + 2*MENU_TOP;
697 }
698
699 /* ar is the search box itself */
700 static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
701 {
702         uiSearchboxData *data= ar->regiondata;
703         
704         /* apply step */
705         data->active+= step;
706         
707         if(data->items.totitem==0)
708                 data->active= 0;
709         else if(data->active > data->items.totitem) {
710                 if(data->items.more) {
711                         data->items.offset++;
712                         data->active= data->items.totitem;
713                         ui_searchbox_update(C, ar, but, 0);
714                 }
715                 else
716                         data->active= data->items.totitem;
717         }
718         else if(data->active < 1) {
719                 if(data->items.offset) {
720                         data->items.offset--;
721                         data->active= 1;
722                         ui_searchbox_update(C, ar, but, 0);
723                 }
724                 else if(data->active < 0)
725                         data->active= 0;
726         }
727         
728         ED_region_tag_redraw(ar);
729 }
730
731 static void ui_searchbox_butrect(rcti *rect, uiSearchboxData *data, int itemnr)
732 {
733         /* thumbnail preview */
734         if (data->preview) {
735                 int buth = (data->bbox.ymax - data->bbox.ymin - 2*MENU_TOP) / data->prv_rows;
736                 int butw = (data->bbox.xmax - data->bbox.xmin) / data->prv_cols;
737                 int row, col;
738                 
739                 *rect= data->bbox;
740                 
741                 col = itemnr % data->prv_cols;
742                 row = itemnr / data->prv_cols;
743                 
744                 rect->xmin += col * butw;
745                 rect->xmax = rect->xmin + butw;
746                 
747                 rect->ymax = data->bbox.ymax - MENU_TOP - (row * buth);
748                 rect->ymin = rect->ymax - buth;
749         }
750         /* list view */
751         else {
752                 int buth= (data->bbox.ymax-data->bbox.ymin - 2*MENU_TOP)/SEARCH_ITEMS;
753                 
754                 *rect= data->bbox;
755                 rect->xmin= data->bbox.xmin + 3.0f;
756                 rect->xmax= data->bbox.xmax - 3.0f;
757                 
758                 rect->ymax= data->bbox.ymax - MENU_TOP - itemnr*buth;
759                 rect->ymin= rect->ymax - buth;
760         }
761         
762 }
763
764 /* x and y in screencoords */
765 int ui_searchbox_inside(ARegion *ar, int x, int y)
766 {
767         uiSearchboxData *data= ar->regiondata;
768         
769         return(BLI_in_rcti(&data->bbox, x-ar->winrct.xmin, y-ar->winrct.ymin));
770 }
771
772 /* string validated to be of correct length (but->hardmax) */
773 void ui_searchbox_apply(uiBut *but, ARegion *ar)
774 {
775         uiSearchboxData *data= ar->regiondata;
776
777         but->func_arg2= NULL;
778         
779         if(data->active) {
780                 char *name= data->items.names[data->active-1];
781                 char *cpoin= strchr(name, '|');
782                 
783                 if(cpoin) cpoin[0]= 0;
784                 BLI_strncpy(but->editstr, name, data->items.maxstrlen);
785                 if(cpoin) cpoin[0]= '|';
786                 
787                 but->func_arg2= data->items.pointers[data->active-1];
788         }
789 }
790
791 void ui_searchbox_event(bContext *C, ARegion *ar, uiBut *but, wmEvent *event)
792 {
793         uiSearchboxData *data= ar->regiondata;
794         
795         switch(event->type) {
796                 case WHEELUPMOUSE:
797                 case UPARROWKEY:
798                         ui_searchbox_select(C, ar, but, -1);
799                         break;
800                 case WHEELDOWNMOUSE:
801                 case DOWNARROWKEY:
802                         ui_searchbox_select(C, ar, but, 1);
803                         break;
804                 case MOUSEMOVE:
805                         if(BLI_in_rcti(&ar->winrct, event->x, event->y)) {
806                                 rcti rect;
807                                 int a;
808                                 
809                                 for(a=0; a<data->items.totitem; a++) {
810                                         ui_searchbox_butrect(&rect, data, a);
811                                         if(BLI_in_rcti(&rect, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
812                                                 if( data->active!= a+1) {
813                                                         data->active= a+1;
814                                                         ui_searchbox_select(C, ar, but, 0);
815                                                         break;
816                                                 }
817                                         }
818                                 }
819                         }
820                         break;
821         }
822 }
823
824 /* ar is the search box itself */
825 void ui_searchbox_update(bContext *C, ARegion *ar, uiBut *but, int reset)
826 {
827         uiSearchboxData *data= ar->regiondata;
828         
829         /* reset vars */
830         data->items.totitem= 0;
831         data->items.more= 0;
832         if(reset==0) {
833                 data->items.offset_i= data->items.offset;
834         }
835         else {
836                 data->items.offset_i= data->items.offset= 0;
837                 data->active= 0;
838                 
839                 /* handle active */
840                 if(but->search_func && but->func_arg2) {
841                         data->items.active= but->func_arg2;
842                         but->search_func(C, but->search_arg, but->editstr, &data->items);
843                         data->items.active= NULL;
844                         
845                         /* found active item, calculate real offset by centering it */
846                         if(data->items.totitem) {
847                                 /* first case, begin of list */
848                                 if(data->items.offset_i < data->items.maxitem) {
849                                         data->active= data->items.offset_i+1;
850                                         data->items.offset_i= 0;
851                                 }
852                                 else {
853                                         /* second case, end of list */
854                                         if(data->items.totitem - data->items.offset_i <= data->items.maxitem) {
855                                                 data->active= 1 + data->items.offset_i - data->items.totitem + data->items.maxitem;
856                                                 data->items.offset_i= data->items.totitem - data->items.maxitem;
857                                         }
858                                         else {
859                                                 /* center active item */
860                                                 data->items.offset_i -= data->items.maxitem/2;
861                                                 data->active= 1 + data->items.maxitem/2;
862                                         }
863                                 }
864                         }
865                         data->items.offset= data->items.offset_i;
866                         data->items.totitem= 0;
867                 }
868         }
869         
870         /* callback */
871         if(but->search_func)
872                 but->search_func(C, but->search_arg, but->editstr, &data->items);
873         
874         /* handle case where editstr is equal to one of items */
875         if(reset && data->active==0) {
876                 int a;
877                 
878                 for(a=0; a<data->items.totitem; a++) {
879                         char *cpoin= strchr(data->items.names[a], '|');
880                         
881                         if(cpoin) cpoin[0]= 0;
882                         if(0==strcmp(but->editstr, data->items.names[a]))
883                                 data->active= a+1;
884                         if(cpoin) cpoin[0]= '|';
885                 }
886                 if(data->items.totitem==1 && but->editstr[0])
887                         data->active= 1;
888         }
889
890         /* validate selected item */
891         ui_searchbox_select(C, ar, but, 0);
892         
893         ED_region_tag_redraw(ar);
894 }
895
896 void ui_searchbox_autocomplete(bContext *C, ARegion *ar, uiBut *but, char *str)
897 {
898         uiSearchboxData *data= ar->regiondata;
899
900         if(str[0]) {
901                 data->items.autocpl= autocomplete_begin(str, ui_get_but_string_max_length(but));
902
903                 but->search_func(C, but->search_arg, but->editstr, &data->items);
904
905                 autocomplete_end(data->items.autocpl, str);
906                 data->items.autocpl= NULL;
907         }
908 }
909
910 static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
911 {
912         uiSearchboxData *data= ar->regiondata;
913         
914         /* pixel space */
915         wmOrtho2(-0.01f, ar->winx-0.01f, -0.01f, ar->winy-0.01f);
916
917         if(!data->noback)
918                 ui_draw_search_back(NULL, NULL, &data->bbox); /* style not used yet */
919         
920         /* draw text */
921         if(data->items.totitem) {
922                 rcti rect;
923                 int a;
924                 
925                 if (data->preview) {
926                         /* draw items */
927                         for(a=0; a<data->items.totitem; a++) {
928                                 ui_searchbox_butrect(&rect, data, a);
929                                 
930                                 /* widget itself */
931                                 if (data->preview)
932                                         ui_draw_preview_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
933                                 else 
934                                         ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
935                         }
936                         
937                         /* indicate more */
938                         if(data->items.more) {
939                                 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
940                                 glEnable(GL_BLEND);
941                                 UI_icon_draw(rect.xmax-18, rect.ymin-7, ICON_TRIA_DOWN);
942                                 glDisable(GL_BLEND);
943                         }
944                         if(data->items.offset) {
945                                 ui_searchbox_butrect(&rect, data, 0);
946                                 glEnable(GL_BLEND);
947                                 UI_icon_draw(rect.xmin, rect.ymax-9, ICON_TRIA_UP);
948                                 glDisable(GL_BLEND);
949                         }
950                         
951                 } else {
952                         /* draw items */
953                         for(a=0; a<data->items.totitem; a++) {
954                                 ui_searchbox_butrect(&rect, data, a);
955                                 
956                                 /* widget itself */
957                                 ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
958                                 
959                         }
960                         /* indicate more */
961                         if(data->items.more) {
962                                 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
963                                 glEnable(GL_BLEND);
964                                 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymin-9, ICON_TRIA_DOWN);
965                                 glDisable(GL_BLEND);
966                         }
967                         if(data->items.offset) {
968                                 ui_searchbox_butrect(&rect, data, 0);
969                                 glEnable(GL_BLEND);
970                                 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymax-7, ICON_TRIA_UP);
971                                 glDisable(GL_BLEND);
972                         }
973                 }
974         }
975 }
976
977 static void ui_searchbox_region_free_cb(ARegion *ar)
978 {
979         uiSearchboxData *data= ar->regiondata;
980         int a;
981
982         /* free search data */
983         for(a=0; a<data->items.maxitem; a++)
984                 MEM_freeN(data->items.names[a]);
985         MEM_freeN(data->items.names);
986         MEM_freeN(data->items.pointers);
987         MEM_freeN(data->items.icons);
988         
989         MEM_freeN(data);
990         ar->regiondata= NULL;
991 }
992
993 ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but)
994 {
995         uiStyle *style= UI_GetStyle();
996         static ARegionType type;
997         ARegion *ar;
998         uiSearchboxData *data;
999         float aspect= but->block->aspect;
1000         float x1f, x2f, y1f, y2f;
1001         int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
1002         
1003         /* create area region */
1004         ar= ui_add_temporary_region(CTX_wm_screen(C));
1005         
1006         memset(&type, 0, sizeof(ARegionType));
1007         type.draw= ui_searchbox_region_draw_cb;
1008         type.free= ui_searchbox_region_free_cb;
1009         ar->type= &type;
1010         
1011         /* create searchbox data */
1012         data= MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
1013
1014         /* set font, get bb */
1015         data->fstyle= style->widget; /* copy struct */
1016         data->fstyle.align= UI_STYLE_TEXT_CENTER;
1017         ui_fontscale(&data->fstyle.points, aspect);
1018         uiStyleFontSet(&data->fstyle);
1019         
1020         ar->regiondata= data;
1021         
1022         /* special case, hardcoded feature, not draw backdrop when called from menus,
1023            assume for design that popup already added it */
1024         if(but->block->flag & UI_BLOCK_LOOP)
1025                 data->noback= 1;
1026         
1027         if (but->a1 > 0 && but->a2 > 0) {
1028                 data->preview = 1;
1029                 data->prv_rows = but->a1;
1030                 data->prv_cols = but->a2;
1031         }
1032         
1033         /* compute position */
1034         if(but->block->flag & UI_BLOCK_LOOP) {
1035                 /* this case is search menu inside other menu */
1036                 /* we copy region size */
1037
1038                 ar->winrct= butregion->winrct;
1039                 
1040                 /* widget rect, in region coords */
1041                 data->bbox.xmin= MENU_SHADOW_SIDE;
1042                 data->bbox.xmax= (ar->winrct.xmax-ar->winrct.xmin) - MENU_SHADOW_SIDE;
1043                 data->bbox.ymin= MENU_SHADOW_BOTTOM;
1044                 data->bbox.ymax= (ar->winrct.ymax-ar->winrct.ymin) - MENU_SHADOW_BOTTOM;
1045                 
1046                 /* check if button is lower half */
1047                 if( but->y2 < (but->block->miny+but->block->maxy)/2 ) {
1048                         data->bbox.ymin += (but->y2-but->y1);
1049                 }
1050                 else {
1051                         data->bbox.ymax -= (but->y2-but->y1);
1052                 }
1053         }
1054         else {
1055                 x1f= but->x1 - 5;       /* align text with button */
1056                 x2f= but->x2 + 5;       /* symmetrical */
1057                 y2f= but->y1;
1058                 y1f= y2f - uiSearchBoxhHeight();
1059
1060                 ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
1061                 ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
1062
1063                 x1f += ofsx;
1064                 x2f += ofsx;
1065                 y1f += ofsy;
1066                 y2f += ofsy;
1067         
1068                 /* minimal width */
1069                 if(x2f - x1f < 150) x2f= x1f+150; // XXX arbitrary
1070                 
1071                 /* copy to int, gets projected if possible too */
1072                 x1= x1f; y1= y1f; x2= x2f; y2= y2f; 
1073                 
1074                 if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
1075                         UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
1076                         UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
1077                 }
1078
1079                 x1 += butregion->winrct.xmin;
1080                 x2 += butregion->winrct.xmin;
1081                 y1 += butregion->winrct.ymin;
1082                 y2 += butregion->winrct.ymin;
1083
1084                 wm_window_get_size(CTX_wm_window(C), &winx, &winy);
1085                 
1086                 if(x2 > winx) {
1087                         /* super size */
1088                         if(x2 > winx + x1) {
1089                                 x2= winx;
1090                                 x1= 0;
1091                         }
1092                         else {
1093                                 x1 -= x2-winx;
1094                                 x2= winx;
1095                         }
1096                 }
1097
1098                 if(y1 < 0) {
1099                         int newy1;
1100                         UI_view2d_to_region_no_clip(&butregion->v2d, 0, but->y2 + ofsy, NULL, &newy1);
1101                         newy1 += butregion->winrct.ymin;
1102
1103                         y2= y2-y1 + newy1;
1104                         y1= newy1;
1105                 }
1106
1107                 /* widget rect, in region coords */
1108                 data->bbox.xmin= MENU_SHADOW_SIDE;
1109                 data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
1110                 data->bbox.ymin= MENU_SHADOW_BOTTOM;
1111                 data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
1112                 
1113                 /* region bigger for shadow */
1114                 ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
1115                 ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
1116                 ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
1117                 ar->winrct.ymax= y2;
1118         }
1119         
1120         /* adds subwindow */
1121         ED_region_init(C, ar);
1122         
1123         /* notify change and redraw */
1124         ED_region_tag_redraw(ar);
1125         
1126         /* prepare search data */
1127         if (data->preview) {
1128                 data->items.maxitem= data->prv_rows * data->prv_cols;
1129         } else {
1130                 data->items.maxitem= SEARCH_ITEMS;
1131         }
1132         data->items.maxstrlen= but->hardmax;
1133         data->items.totitem= 0;
1134         data->items.names= MEM_callocN(data->items.maxitem*sizeof(void *), "search names");
1135         data->items.pointers= MEM_callocN(data->items.maxitem*sizeof(void *), "search pointers");
1136         data->items.icons= MEM_callocN(data->items.maxitem*sizeof(int), "search icons");
1137         for(x1=0; x1<data->items.maxitem; x1++)
1138                 data->items.names[x1]= MEM_callocN(but->hardmax+1, "search pointers");
1139         
1140         return ar;
1141 }
1142
1143 void ui_searchbox_free(bContext *C, ARegion *ar)
1144 {
1145         ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
1146 }
1147
1148 /* sets red alert if button holds a string it can't find */
1149 /* XXX weak: search_func adds all partial matches... */
1150 void ui_but_search_test(uiBut *but)
1151 {
1152         uiSearchItems *items;
1153         int x1;
1154
1155         /* possibly very large lists (such as ID datablocks) only
1156          * only validate string RNA buts (not pointers) */
1157         if(but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
1158                 return;
1159         }
1160
1161         items= MEM_callocN(sizeof(uiSearchItems), "search items");
1162
1163         /* setup search struct */
1164         items->maxitem= 10;
1165         items->maxstrlen= 256;
1166         items->names= MEM_callocN(items->maxitem*sizeof(void *), "search names");
1167         for(x1=0; x1<items->maxitem; x1++)
1168                 items->names[x1]= MEM_callocN(but->hardmax+1, "search names");
1169         
1170         but->search_func(but->block->evil_C, but->search_arg, but->drawstr, items);
1171         
1172         /* only redalert when we are sure of it, this can miss cases when >10 matches */
1173         if(items->totitem==0)
1174                 uiButSetFlag(but, UI_BUT_REDALERT);
1175         else if(items->more==0) {
1176                 for(x1= 0; x1<items->totitem; x1++)
1177                         if(strcmp(but->drawstr, items->names[x1])==0)
1178                                 break;
1179                 if(x1==items->totitem)
1180                         uiButSetFlag(but, UI_BUT_REDALERT);
1181         }
1182         
1183         for(x1=0; x1<items->maxitem; x1++)
1184                 MEM_freeN(items->names[x1]);
1185         MEM_freeN(items->names);
1186         MEM_freeN(items);
1187 }
1188
1189
1190 /************************* Creating Menu Blocks **********************/
1191
1192 /* position block relative to but, result is in window space */
1193 static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
1194 {
1195         uiBut *bt;
1196         uiSafetyRct *saferct;
1197         rctf butrct;
1198         /*float aspect;*/ /*UNUSED*/
1199         int xsize, ysize, xof=0, yof=0, center;
1200         short dir1= 0, dir2=0;
1201         
1202         /* transform to window coordinates, using the source button region/block */
1203         butrct.xmin= but->x1; butrct.xmax= but->x2;
1204         butrct.ymin= but->y1; butrct.ymax= but->y2;
1205
1206         ui_block_to_window_fl(butregion, but->block, &butrct.xmin, &butrct.ymin);
1207         ui_block_to_window_fl(butregion, but->block, &butrct.xmax, &butrct.ymax);
1208
1209         /* calc block rect */
1210         if(block->minx == 0.0f && block->maxx == 0.0f) {
1211                 if(block->buttons.first) {
1212                         block->minx= block->miny= 10000;
1213                         block->maxx= block->maxy= -10000;
1214                         
1215                         bt= block->buttons.first;
1216                         while(bt) {
1217                                 if(bt->x1 < block->minx) block->minx= bt->x1;
1218                                 if(bt->y1 < block->miny) block->miny= bt->y1;
1219
1220                                 if(bt->x2 > block->maxx) block->maxx= bt->x2;
1221                                 if(bt->y2 > block->maxy) block->maxy= bt->y2;
1222                                 
1223                                 bt= bt->next;
1224                         }
1225                 }
1226                 else {
1227                         /* we're nice and allow empty blocks too */
1228                         block->minx= block->miny= 0;
1229                         block->maxx= block->maxy= 20;
1230                 }
1231         }
1232         
1233         /*aspect= (float)(block->maxx - block->minx + 4);*/ /*UNUSED*/
1234         ui_block_to_window_fl(butregion, but->block, &block->minx, &block->miny);
1235         ui_block_to_window_fl(butregion, but->block, &block->maxx, &block->maxy);
1236
1237         //block->minx-= 2.0; block->miny-= 2.0;
1238         //block->maxx+= 2.0; block->maxy+= 2.0;
1239         
1240         xsize= block->maxx - block->minx+4; // 4 for shadow
1241         ysize= block->maxy - block->miny+4;
1242         /*aspect/= (float)xsize;*/ /*UNUSED*/
1243
1244         {
1245                 int left=0, right=0, top=0, down=0;
1246                 int winx, winy;
1247                 // int offscreen;
1248
1249                 wm_window_get_size(window, &winx, &winy);
1250
1251                 if(block->direction & UI_CENTER) center= ysize/2;
1252                 else center= 0;
1253                 
1254                 /* check if there's space at all */
1255                 if( butrct.xmin-xsize > 0.0f) left= 1;
1256                 if( butrct.xmax+xsize < winx) right= 1;
1257                 if( butrct.ymin-ysize+center > 0.0f) down= 1;
1258                 if( butrct.ymax+ysize-center < winy) top= 1;
1259                 
1260                 if(top==0 && down==0) {
1261                         if (butrct.ymin-ysize < winy-butrct.ymax-ysize)
1262                                 top= 1;
1263                         else
1264                                 down= 1;
1265                 }
1266                 
1267                 dir1= block->direction & UI_DIRECTION;
1268
1269                 /* secundary directions */
1270                 if(dir1 & (UI_TOP|UI_DOWN)) {
1271                         if(dir1 & UI_LEFT) dir2= UI_LEFT;
1272                         else if(dir1 & UI_RIGHT) dir2= UI_RIGHT;
1273                         dir1 &= (UI_TOP|UI_DOWN);
1274                 }
1275
1276                 if(dir2==0) if(dir1==UI_LEFT || dir1==UI_RIGHT) dir2= UI_DOWN;
1277                 if(dir2==0) if(dir1==UI_TOP || dir1==UI_DOWN) dir2= UI_LEFT;
1278                 
1279                 /* no space at all? dont change */
1280                 if(left || right) {
1281                         if(dir1==UI_LEFT && left==0) dir1= UI_RIGHT;
1282                         if(dir1==UI_RIGHT && right==0) dir1= UI_LEFT;
1283                         /* this is aligning, not append! */
1284                         if(dir2==UI_LEFT && right==0) dir2= UI_RIGHT;
1285                         if(dir2==UI_RIGHT && left==0) dir2= UI_LEFT;
1286                 }
1287                 if(down || top) {
1288                         if(dir1==UI_TOP && top==0) dir1= UI_DOWN;
1289                         if(dir1==UI_DOWN && down==0) dir1= UI_TOP;
1290                         if(dir2==UI_TOP && top==0) dir2= UI_DOWN;
1291                         if(dir2==UI_DOWN && down==0) dir2= UI_TOP;
1292                 }
1293                 
1294                 if(dir1==UI_LEFT) {
1295                         xof= butrct.xmin - block->maxx;
1296                         if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
1297                         else yof= butrct.ymax - block->maxy+center;
1298                 }
1299                 else if(dir1==UI_RIGHT) {
1300                         xof= butrct.xmax - block->minx;
1301                         if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
1302                         else yof= butrct.ymax - block->maxy+center;
1303                 }
1304                 else if(dir1==UI_TOP) {
1305                         yof= butrct.ymax - block->miny;
1306                         if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
1307                         else xof= butrct.xmin - block->minx;
1308                         // changed direction? 
1309                         if((dir1 & block->direction)==0) {
1310                                 if(block->direction & UI_SHIFT_FLIPPED)
1311                                         xof+= dir2==UI_LEFT?25:-25;
1312                                 uiBlockFlipOrder(block);
1313                         }
1314                 }
1315                 else if(dir1==UI_DOWN) {
1316                         yof= butrct.ymin - block->maxy;
1317                         if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
1318                         else xof= butrct.xmin - block->minx;
1319                         // changed direction?
1320                         if((dir1 & block->direction)==0) {
1321                                 if(block->direction & UI_SHIFT_FLIPPED)
1322                                         xof+= dir2==UI_LEFT?25:-25;
1323                                 uiBlockFlipOrder(block);
1324                         }
1325                 }
1326
1327                 /* and now we handle the exception; no space below or to top */
1328                 if(top==0 && down==0) {
1329                         if(dir1==UI_LEFT || dir1==UI_RIGHT) {
1330                                 // align with bottom of screen 
1331                                 // yof= ysize; (not with menu scrolls)
1332                         }
1333                 }
1334                 
1335                 /* or no space left or right */
1336                 if(left==0 && right==0) {
1337                         if(dir1==UI_TOP || dir1==UI_DOWN) {
1338                                 // align with left size of screen 
1339                                 xof= -block->minx+5;
1340                         }
1341                 }
1342                 
1343                 // apply requested offset in the block
1344                 xof += block->xofs/block->aspect;
1345                 yof += block->yofs/block->aspect;
1346 #if 0
1347                 /* clamp to window bounds, could be made into an option if its ever annoying */
1348                 if(     (offscreen= (block->miny+yof)) < 0)      yof -= offscreen; /* bottom */
1349                 else if((offscreen= (block->maxy+yof)-winy) > 0) yof -= offscreen; /* top */
1350                 if(     (offscreen= (block->minx+xof)) < 0)      xof -= offscreen; /* left */
1351                 else if((offscreen= (block->maxx+xof)-winx) > 0) xof -= offscreen; /* right */
1352 #endif
1353         }
1354         
1355         /* apply offset, buttons in window coords */
1356         
1357         for(bt= block->buttons.first; bt; bt= bt->next) {
1358                 ui_block_to_window_fl(butregion, but->block, &bt->x1, &bt->y1);
1359                 ui_block_to_window_fl(butregion, but->block, &bt->x2, &bt->y2);
1360
1361                 bt->x1 += xof;
1362                 bt->x2 += xof;
1363                 bt->y1 += yof;
1364                 bt->y2 += yof;
1365
1366                 bt->aspect= 1.0;
1367                 // ui_check_but recalculates drawstring size in pixels
1368                 ui_check_but(bt);
1369         }
1370         
1371         block->minx += xof;
1372         block->miny += yof;
1373         block->maxx += xof;
1374         block->maxy += yof;
1375
1376         /* safety calculus */
1377         if(but) {
1378                 float midx= (butrct.xmin+butrct.xmax)/2.0f;
1379                 float midy= (butrct.ymin+butrct.ymax)/2.0f;
1380                 
1381                 /* when you are outside parent button, safety there should be smaller */
1382                 
1383                 // parent button to left
1384                 if( midx < block->minx ) block->safety.xmin= block->minx-3; 
1385                 else block->safety.xmin= block->minx-40;
1386                 // parent button to right
1387                 if( midx > block->maxx ) block->safety.xmax= block->maxx+3; 
1388                 else block->safety.xmax= block->maxx+40;
1389                 
1390                 // parent button on bottom
1391                 if( midy < block->miny ) block->safety.ymin= block->miny-3; 
1392                 else block->safety.ymin= block->miny-40;
1393                 // parent button on top
1394                 if( midy > block->maxy ) block->safety.ymax= block->maxy+3; 
1395                 else block->safety.ymax= block->maxy+40;
1396                 
1397                 // exception for switched pulldowns...
1398                 if(dir1 && (dir1 & block->direction)==0) {
1399                         if(dir2==UI_RIGHT) block->safety.xmax= block->maxx+3; 
1400                         if(dir2==UI_LEFT) block->safety.xmin= block->minx-3; 
1401                 }
1402                 block->direction= dir1;
1403         }
1404         else {
1405                 block->safety.xmin= block->minx-40;
1406                 block->safety.ymin= block->miny-40;
1407                 block->safety.xmax= block->maxx+40;
1408                 block->safety.ymax= block->maxy+40;
1409         }
1410
1411         /* keep a list of these, needed for pulldown menus */
1412         saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1413         saferct->parent= butrct;
1414         saferct->safety= block->safety;
1415         BLI_freelistN(&block->saferct);
1416         if(but)
1417                 BLI_duplicatelist(&block->saferct, &but->block->saferct);
1418         BLI_addhead(&block->saferct, saferct);
1419 }
1420
1421 static void ui_block_region_draw(const bContext *C, ARegion *ar)
1422 {
1423         uiBlock *block;
1424
1425         for(block=ar->uiblocks.first; block; block=block->next)
1426                 uiDrawBlock(C, block);
1427 }
1428
1429 static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
1430 {
1431         int winx, winy;
1432         
1433         wm_window_get_size(window, &winx, &winy);
1434         
1435         if(block->minx < MENU_SHADOW_SIDE)
1436                 block->minx= MENU_SHADOW_SIDE;
1437         if(block->maxx > winx-MENU_SHADOW_SIDE)
1438                 block->maxx= winx-MENU_SHADOW_SIDE;
1439         
1440         if(block->miny < MENU_SHADOW_BOTTOM)
1441                 block->miny= MENU_SHADOW_BOTTOM;
1442         if(block->maxy > winy-MENU_TOP)
1443                 block->maxy= winy-MENU_TOP;
1444 }
1445
1446 void ui_popup_block_scrolltest(uiBlock *block)
1447 {
1448         uiBut *bt;
1449         /* Knowing direction is necessary for multi-column menus... */
1450         int is_flip = (block->direction & UI_TOP) && !(block->flag & UI_BLOCK_NO_FLIP);
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(is_flip && bt->next && bt->next->y1 > bt->y1)
1468                                         bt->next->flag |= UI_SCROLLED;
1469                                 else if(!is_flip && 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(!is_flip && bt->next && bt->next->y2 < bt->y2)
1479                                         bt->next->flag |= UI_SCROLLED;
1480                                 else if(is_flip && 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                 ui_block_position(window, butregion, but, block);
1539         }
1540         else {
1541                 /* keep a list of these, needed for pulldown menus */
1542                 saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1543                 saferct->safety= block->safety;
1544                 BLI_addhead(&block->saferct, saferct);
1545                 block->flag |= UI_BLOCK_POPUP|UI_BLOCK_NUMSELECT;
1546         }
1547
1548         /* clip block with window boundary */
1549         ui_popup_block_clip(window, block);
1550         
1551         /* the block and buttons were positioned in window space as in 2.4x, now
1552          * these menu blocks are regions so we bring it back to region space.
1553          * additionally we add some padding for the menu shadow or rounded menus */
1554         ar->winrct.xmin= block->minx - MENU_SHADOW_SIDE;
1555         ar->winrct.xmax= block->maxx + MENU_SHADOW_SIDE;
1556         ar->winrct.ymin= block->miny - MENU_SHADOW_BOTTOM;
1557         ar->winrct.ymax= block->maxy + MENU_TOP;
1558         
1559         block->minx -= ar->winrct.xmin;
1560         block->maxx -= ar->winrct.xmin;
1561         block->miny -= ar->winrct.ymin;
1562         block->maxy -= ar->winrct.ymin;
1563
1564         for(bt= block->buttons.first; bt; bt= bt->next) {
1565                 bt->x1 -= ar->winrct.xmin;
1566                 bt->x2 -= ar->winrct.xmin;
1567                 bt->y1 -= ar->winrct.ymin;
1568                 bt->y2 -= ar->winrct.ymin;
1569         }
1570         
1571         block->flag |= UI_BLOCK_LOOP;
1572
1573         /* adds subwindow */
1574         ED_region_init(C, ar);
1575
1576         /* checks which buttons are visible, sets flags to prevent draw (do after region init) */
1577         ui_popup_block_scrolltest(block);
1578         
1579         /* get winmat now that we actually have the subwindow */
1580         wmSubWindowSet(window, ar->swinid);
1581         
1582         wm_subwindow_getmatrix(window, ar->swinid, block->winmat);
1583         
1584         /* notify change and redraw */
1585         ED_region_tag_redraw(ar);
1586
1587         return handle;
1588 }
1589
1590 void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
1591 {
1592         ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
1593         
1594         if(handle->scrolltimer)
1595                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
1596         
1597         MEM_freeN(handle);
1598 }
1599
1600 /***************************** Menu Button ***************************/
1601
1602 static void ui_block_func_MENUSTR(bContext *UNUSED(C), uiLayout *layout, void *arg_str)
1603 {
1604         uiBlock *block= uiLayoutGetBlock(layout);
1605         uiPopupBlockHandle *handle= block->handle;
1606         uiLayout *split, *column=NULL;
1607         uiBut *bt;
1608         MenuData *md;
1609         MenuEntry *entry;
1610         const char *instr= arg_str;
1611         int columns, rows, a, b;
1612
1613         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1614         
1615         /* compute menu data */
1616         md= decompose_menu_string(instr);
1617
1618         /* columns and row estimation */
1619         columns= (md->nitems+20)/20;
1620         if(columns<1)
1621                 columns= 1;
1622         if(columns>8)
1623                 columns= (md->nitems+25)/25;
1624         
1625         rows= md->nitems/columns;
1626         if(rows<1)
1627                 rows= 1;
1628         while(rows*columns<md->nitems)
1629                 rows++;
1630
1631         /* create title */
1632         if(md->title) {
1633                 if(md->titleicon) {
1634                         uiItemL(layout, md->title, md->titleicon);
1635                 }
1636                 else {
1637                         uiItemL(layout, md->title, ICON_NONE);
1638                         bt= block->buttons.last;
1639                         bt->flag= UI_TEXT_LEFT;
1640                 }
1641         }
1642
1643         /* inconsistent, but menus with labels do not look good flipped */
1644         entry= md->items;
1645         for(a=0; a<md->nitems; a++, entry++) {
1646                 if(entry->sepr && entry->str[0]) {
1647                         block->flag |= UI_BLOCK_NO_FLIP;
1648                         break;
1649                 }
1650         }
1651
1652         /* create items */
1653         split= uiLayoutSplit(layout, 0, 0);
1654
1655         for(a=0, b=0; a<md->nitems; a++, b++) {
1656                 if(block->flag & UI_BLOCK_NO_FLIP)
1657                         entry= &md->items[a];
1658                 else
1659                         entry= &md->items[md->nitems-a-1];
1660                 
1661                 /* new column on N rows or on separation label */
1662                 if((b % rows == 0) || (entry->sepr && entry->str[0])) {
1663                         column= uiLayoutColumn(split, 0);
1664                         b= 0;
1665                 }
1666
1667                 if(entry->sepr) {
1668                         uiItemL(column, entry->str, entry->icon);
1669                         bt= block->buttons.last;
1670                         bt->flag= UI_TEXT_LEFT;
1671                 }
1672                 else if(entry->icon) {
1673                         uiDefIconTextButF(block, BUTM|FLO, B_NOP, entry->icon, entry->str, 0, 0,
1674                                 UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1675                 }
1676                 else {
1677                         uiDefButF(block, BUTM|FLO, B_NOP, entry->str, 0, 0,
1678                                 UI_UNIT_X*5, UI_UNIT_X, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1679                 }
1680         }
1681         
1682         menudata_free(md);
1683 }
1684
1685 void ui_block_func_ICONROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1686 {
1687         uiBlock *block= uiLayoutGetBlock(layout);
1688         uiPopupBlockHandle *handle= block->handle;
1689         uiBut *but= arg_but;
1690         int a;
1691         
1692         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1693         
1694         for(a=(int)but->hardmin; a<=(int)but->hardmax; a++)
1695                 uiDefIconButF(block, BUTM|FLO, B_NOP, but->icon+(a-but->hardmin), 0, 0, UI_UNIT_X*5, UI_UNIT_Y,
1696                         &handle->retvalue, (float)a, 0.0, 0, 0, "");
1697 }
1698
1699 void ui_block_func_ICONTEXTROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1700 {
1701         uiBlock *block= uiLayoutGetBlock(layout);
1702         uiPopupBlockHandle *handle= block->handle;
1703         uiBut *but= arg_but, *bt;
1704         MenuData *md;
1705         MenuEntry *entry;
1706         int a;
1707         
1708         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1709
1710         md= decompose_menu_string(but->str);
1711
1712         /* title */
1713         if(md->title) {
1714                 bt= uiDefBut(block, LABEL, 0, md->title, 0, 0, UI_UNIT_X*5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1715                 bt->flag= UI_TEXT_LEFT;
1716         }
1717
1718         /* loop through the menu options and draw them out with icons & text labels */
1719         for(a=0; a<md->nitems; a++) {
1720                 entry= &md->items[md->nitems-a-1];
1721
1722                 if(entry->sepr)
1723                         uiItemS(layout);
1724                 else
1725                         uiDefIconTextButF(block, BUTM|FLO, B_NOP, (short)((but->icon)+(entry->retval-but->hardmin)), entry->str,
1726                                 0, 0, UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1727         }
1728
1729         menudata_free(md);
1730 }
1731
1732 #if 0
1733 static void ui_warp_pointer(int x, int y)
1734 {
1735         /* XXX 2.50 which function to use for this? */
1736         /* OSX has very poor mousewarp support, it sends events;
1737            this causes a menu being pressed immediately ... */
1738         #ifndef __APPLE__
1739         warp_pointer(x, y);
1740         #endif
1741 }
1742 #endif
1743
1744 /********************* Color Button ****************/
1745
1746 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1747 #define SPICK   110.0
1748 #define FPICK   180.0
1749 #define DPICK   6.0
1750 #define BPICK   24.0
1751
1752 /* for picker, while editing hsv */
1753 void ui_set_but_hsv(uiBut *but)
1754 {
1755         float col[3];
1756         float *hsv= ui_block_hsv_get(but->block);
1757         
1758         hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
1759         ui_set_but_vectorf(but, col);
1760 }
1761
1762 /* also used by small picker, be careful with name checks below... */
1763 static void ui_update_block_buts_rgb(uiBlock *block, float *rgb)
1764 {
1765         uiBut *bt;
1766         float *hsv= ui_block_hsv_get(block);
1767         
1768         /* this is to keep the H and S value when V is equal to zero
1769          * and we are working in HSV mode, of course!
1770          */
1771         rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
1772         
1773         // this updates button strings, is hackish... but button pointers are on stack of caller function
1774         for(bt= block->buttons.first; bt; bt= bt->next) {
1775                 if (bt->rnaprop) {
1776                         
1777                         ui_set_but_vectorf(bt, rgb);
1778                         
1779                 }
1780                 else if(strcmp(bt->str, "Hex: ")==0) {
1781                         float rgb_gamma[3];
1782                         double intpart;
1783                         char col[16];
1784                         
1785                         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1786                         
1787                         if (block->color_profile == BLI_PR_NONE) {
1788                                 copy_v3_v3(rgb_gamma, rgb);
1789                         } else {
1790                                 /* make an sRGB version, for Hex code */
1791                                 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1792                         }
1793                         
1794                         if (rgb_gamma[0] > 1.0f) rgb_gamma[0] = modf(rgb_gamma[0], &intpart);
1795                         if (rgb_gamma[1] > 1.0f) rgb_gamma[1] = modf(rgb_gamma[1], &intpart);
1796                         if (rgb_gamma[2] > 1.0f) rgb_gamma[2] = modf(rgb_gamma[2], &intpart);
1797
1798                         BLI_snprintf(col, sizeof(col), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
1799                         
1800                         strcpy(bt->poin, col);
1801                 }
1802                 else if(bt->str[1]==' ') {
1803                         if(bt->str[0]=='R') {
1804                                 ui_set_but_val(bt, rgb[0]);
1805                         }
1806                         else if(bt->str[0]=='G') {
1807                                 ui_set_but_val(bt, rgb[1]);
1808                         }
1809                         else if(bt->str[0]=='B') {
1810                                 ui_set_but_val(bt, rgb[2]);
1811                         }
1812                         else if(bt->str[0]=='H') {
1813                                 ui_set_but_val(bt, hsv[0]);
1814                         }
1815                         else if(bt->str[0]=='S') {
1816                                 ui_set_but_val(bt, hsv[1]);
1817                         }
1818                         else if(bt->str[0]=='V') {
1819                                 ui_set_but_val(bt, hsv[2]);
1820                         }
1821                 }               
1822
1823                 ui_check_but(bt);
1824         }
1825 }
1826
1827 static void do_picker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1828 {
1829         uiBut *but= (uiBut *)bt1;
1830         uiPopupBlockHandle *popup= but->block->handle;
1831         PropertyRNA *prop = but->rnaprop;
1832         PointerRNA ptr = but->rnapoin;
1833         float rgb[4];
1834         
1835         if (prop) {
1836                 RNA_property_float_get_array(&ptr, prop, rgb);
1837                 ui_update_block_buts_rgb(but->block, rgb);
1838         }
1839         
1840         if(popup)
1841                 popup->menuretval= UI_RETURN_UPDATE;
1842 }
1843
1844 static void do_hsv_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1845 {
1846         uiBut *but= (uiBut *)bt1;
1847         uiPopupBlockHandle *popup= but->block->handle;
1848         float rgb[3];
1849         float *hsv= ui_block_hsv_get(but->block);
1850         
1851         hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
1852         
1853         ui_update_block_buts_rgb(but->block, rgb);
1854         
1855         if(popup)
1856                 popup->menuretval= UI_RETURN_UPDATE;
1857 }
1858
1859 static void do_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
1860 {
1861         uiBut *but= (uiBut *)bt1;
1862         uiPopupBlockHandle *popup= but->block->handle;
1863         char *hexcol= (char *)hexcl;
1864         float rgb[3];
1865         
1866         hex_to_rgb(hexcol, rgb, rgb+1, rgb+2);
1867         
1868         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1869         if (but->block->color_profile != BLI_PR_NONE) {
1870                 /* so we need to linearise it for Blender */
1871                 srgb_to_linearrgb_v3_v3(rgb, rgb);
1872         }
1873         
1874         ui_update_block_buts_rgb(but->block, rgb);
1875         
1876         if(popup)
1877                 popup->menuretval= UI_RETURN_UPDATE;
1878 }
1879
1880 static void close_popup_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1881 {
1882         uiBut *but= (uiBut *)bt1;
1883         uiPopupBlockHandle *popup= but->block->handle;
1884         
1885         if(popup)
1886                 popup->menuretval= UI_RETURN_OK;
1887 }
1888
1889 static void picker_new_hide_reveal(uiBlock *block, short colormode)
1890 {
1891         uiBut *bt;
1892         
1893         /* tag buttons */
1894         for(bt= block->buttons.first; bt; bt= bt->next) {
1895                 
1896                 if (bt->type == LABEL) {
1897                         if( bt->str[1]=='G') {
1898                                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1899                                 else bt->flag |= UI_HIDDEN;
1900                         }
1901                 }
1902                 
1903                 if(bt->type==NUMSLI || bt->type==TEX) {
1904                         if( bt->str[1]=='e') {
1905                                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1906                                 else bt->flag |= UI_HIDDEN;
1907                         }
1908                         else if( ELEM3(bt->str[0], 'R', 'G', 'B')) {
1909                                 if(colormode==0) bt->flag &= ~UI_HIDDEN;
1910                                 else bt->flag |= UI_HIDDEN;
1911                         }
1912                         else if( ELEM3(bt->str[0], 'H', 'S', 'V')) {
1913                                 if(colormode==1) bt->flag &= ~UI_HIDDEN;
1914                                 else bt->flag |= UI_HIDDEN;
1915                         }
1916                 }
1917         }
1918 }
1919
1920 static void do_picker_new_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1921 {
1922         uiBut *bt= bt1;
1923         short colormode= ui_get_but_val(bt);
1924         picker_new_hide_reveal(bt->block, colormode);
1925 }
1926
1927 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1928 #define SPICK1  150.0
1929 #define DPICK1  6.0
1930
1931 #define PICKER_H        150
1932 #define PICKER_W        150
1933 #define PICKER_SPACE    6
1934 #define PICKER_BAR              14
1935
1936 #define PICKER_TOTAL_W  (PICKER_W+PICKER_SPACE+PICKER_BAR)
1937
1938 static void circle_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop)
1939 {
1940         uiBut *bt;
1941         
1942         /* HS circle */
1943         bt= uiDefButR_prop(block, HSVCIRCLE, 0, "",     0, 0, PICKER_H, PICKER_W, ptr, prop, 0, 0.0, 0.0, 0, 0, "Color");
1944         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1945         
1946         /* value */
1947         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");
1948         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1949 }
1950
1951
1952 static void square_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int type)
1953 {
1954         uiBut *bt;
1955         int bartype = type + 3;
1956         
1957         /* HS square */
1958         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");
1959         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1960         
1961         /* value */
1962         bt= uiDefButR_prop(block, HSVCUBE, 0, "",               0, 0, PICKER_TOTAL_W, PICKER_BAR, ptr, prop, 0, 0.0, 0.0, bartype, 0, "Value");
1963         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1964 }
1965
1966
1967 /* a HS circle, V slider, rgb/hsv/hex sliders */
1968 static void uiBlockPicker(uiBlock *block, float *rgb, PointerRNA *ptr, PropertyRNA *prop)
1969 {
1970         static short colormode= 0;      /* temp? 0=rgb, 1=hsv, 2=hex */
1971         uiBut *bt;
1972         int width, butwidth;
1973         static char tip[50];
1974         static char hexcol[128];
1975         float rgb_gamma[3];
1976         float min, max, step, precision;
1977         float *hsv= ui_block_hsv_get(block);
1978         
1979         ui_block_hsv_get(block);
1980         
1981         width= PICKER_TOTAL_W;
1982         butwidth = width - UI_UNIT_X - 10;
1983         
1984         /* existence of profile means storage is in linear color space, with display correction */
1985         if (block->color_profile == BLI_PR_NONE) {
1986                 BLI_strncpy(tip, "Value in Display Color Space", sizeof(tip));
1987                 copy_v3_v3(rgb_gamma, rgb);
1988         } else {
1989                 BLI_strncpy(tip, "Value in Linear RGB Color Space", sizeof(tip));
1990                 /* make an sRGB version, for Hex code */
1991                 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1992         }
1993         
1994         /* sneaky way to check for alpha */
1995         rgb[3]= FLT_MAX;
1996
1997         RNA_property_float_ui_range(ptr, prop, &min, &max, &step, &precision);
1998         RNA_property_float_get_array(ptr, prop, rgb);
1999
2000         switch (U.color_picker_type) {
2001                 case USER_CP_CIRCLE:
2002                         circle_picker(block, ptr, prop);
2003                         break;
2004                 case USER_CP_SQUARE_SV:
2005                         square_picker(block, ptr, prop, UI_GRAD_SV);
2006                         break;
2007                 case USER_CP_SQUARE_HS:
2008                         square_picker(block, ptr, prop, UI_GRAD_HS);
2009                         break;
2010                 case USER_CP_SQUARE_HV:
2011                         square_picker(block, ptr, prop, UI_GRAD_HV);
2012                         break;
2013         }
2014         
2015         /* mode */
2016         uiBlockBeginAlign(block);
2017         bt= uiDefButS(block, ROW, 0, "RGB",     0, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 0.0, 0, 0, "");
2018         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2019         bt= uiDefButS(block, ROW, 0, "HSV",     width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 1.0, 0, 0, "");
2020         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2021         bt= uiDefButS(block, ROW, 0, "Hex",     2*width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 2.0, 0, 0, "");
2022         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2023         uiBlockEndAlign(block);
2024
2025         bt= uiDefIconButO(block, BUT, "UI_OT_eyedropper", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, butwidth+10, -60, UI_UNIT_X, UI_UNIT_Y, NULL);
2026         uiButSetFunc(bt, close_popup_cb, bt, NULL);
2027         
2028         /* RGB values */
2029         uiBlockBeginAlign(block);
2030         bt= uiDefButR_prop(block, NUMSLI, 0, "R ",      0, -60, butwidth, UI_UNIT_Y, ptr, prop, 0, 0.0, 0.0, 0, 3, "Red");
2031         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2032         bt= uiDefButR_prop(block, NUMSLI, 0, "G ",      0, -80, butwidth, UI_UNIT_Y, ptr, prop, 1, 0.0, 0.0, 0, 3, "Green");
2033         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2034         bt= uiDefButR_prop(block, NUMSLI, 0, "B ",      0, -100, butwidth, UI_UNIT_Y, ptr, prop, 2, 0.0, 0.0, 0, 3, "Blue");
2035         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2036
2037         // could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", ICON_NONE);
2038         // but need to use uiButSetFunc for updating other fake buttons
2039         
2040         /* HSV values */
2041         uiBlockBeginAlign(block);
2042         bt= uiDefButF(block, NUMSLI, 0, "H ",   0, -60, butwidth, UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, "Hue");
2043         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2044         bt= uiDefButF(block, NUMSLI, 0, "S ",   0, -80, butwidth, UI_UNIT_Y, hsv+1, 0.0, 1.0, 10, 3, "Saturation");
2045         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2046         bt= uiDefButF(block, NUMSLI, 0, "V ",   0, -100, butwidth, UI_UNIT_Y, hsv+2, 0.0, max, 10, 3, "Value");
2047         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2048         uiBlockEndAlign(block);
2049
2050         if(rgb[3] != FLT_MAX) {
2051                 bt= uiDefButR_prop(block, NUMSLI, 0, "A ",      0, -120, butwidth, UI_UNIT_Y, ptr, prop, 3, 0.0, 0.0, 0, 0, "Alpha");
2052                 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2053         }
2054         else {
2055                 rgb[3]= 1.0f;
2056         }
2057
2058         BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
2059
2060         bt= uiDefBut(block, TEX, 0, "Hex: ", 0, -60, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
2061         uiButSetFunc(bt, do_hex_rna_cb, bt, hexcol);
2062         uiDefBut(block, LABEL, 0, "(Gamma Corrected)", 0, -80, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2063
2064         rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
2065
2066         picker_new_hide_reveal(block, colormode);
2067 }
2068
2069
2070 static int ui_picker_small_wheel_cb(const bContext *UNUSED(C), uiBlock *block, wmEvent *event)
2071 {
2072         float add= 0.0f;
2073         
2074         if(event->type==WHEELUPMOUSE)
2075                 add= 0.05f;
2076         else if(event->type==WHEELDOWNMOUSE)
2077                 add= -0.05f;
2078         
2079         if(add!=0.0f) {
2080                 uiBut *but;
2081                 
2082                 for(but= block->buttons.first; but; but= but->next) {
2083                         if(but->type==HSVCUBE && but->active==NULL) {
2084                                 uiPopupBlockHandle *popup= block->handle;
2085                                 float col[3];
2086                                 float *hsv= ui_block_hsv_get(block);
2087                                 
2088                                 ui_get_but_vectorf(but, col);
2089                                 
2090                                 rgb_to_hsv_compat(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
2091                                 hsv[2]= CLAMPIS(hsv[2]+add, 0.0f, 1.0f);
2092                                 hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
2093
2094                                 ui_set_but_vectorf(but, col);
2095                                 
2096                                 ui_update_block_buts_rgb(block, col);
2097                                 if(popup)
2098                                         popup->menuretval= UI_RETURN_UPDATE;
2099                                 
2100                                 return 1;
2101                         }
2102                 }
2103         }
2104         return 0;
2105 }
2106
2107 uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
2108 {
2109         uiBut *but= arg_but;
2110         uiBlock *block;
2111         
2112         block= uiBeginBlock(C, handle->region, "colorpicker", UI_EMBOSS);
2113         
2114         if (but->rnaprop) {
2115                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2116                         block->color_profile = BLI_PR_NONE;
2117                 }
2118         }
2119         
2120         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2121         
2122         copy_v3_v3(handle->retvec, but->editvec);
2123         
2124         uiBlockPicker(block, handle->retvec, &but->rnapoin, but->rnaprop);
2125         
2126         block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN|UI_BLOCK_OUT_1;
2127         uiBoundsBlock(block, 10);
2128         
2129         block->block_event_func= ui_picker_small_wheel_cb;
2130         
2131         /* and lets go */
2132         block->direction= UI_TOP;
2133         
2134         return block;
2135 }
2136
2137 /************************ Popup Menu Memory ****************************/
2138
2139 static int ui_popup_string_hash(char *str)
2140 {
2141         /* sometimes button contains hotkey, sometimes not, strip for proper compare */
2142         int hash;
2143         char *delimit= strchr(str, '|');
2144
2145         if(delimit) *delimit= 0;
2146         hash= BLI_ghashutil_strhash(str);
2147         if(delimit) *delimit= '|';
2148
2149         return hash;
2150 }
2151
2152 static int ui_popup_menu_hash(const char *str)
2153 {
2154         return BLI_ghashutil_strhash(str);
2155 }
2156
2157 /* but == NULL read, otherwise set */
2158 uiBut *ui_popup_menu_memory(uiBlock *block, uiBut *but)
2159 {
2160         static int mem[256], first=1;
2161         int hash= block->puphash;
2162         
2163         if(first) {
2164                 /* init */
2165                 memset(mem, -1, sizeof(mem));
2166                 first= 0;
2167         }
2168
2169         if(but) {
2170                 /* set */
2171                 mem[hash & 255 ]= ui_popup_string_hash(but->str);
2172                 return NULL;
2173         }
2174         else {
2175                 /* get */
2176                 for(but=block->buttons.first; but; but=but->next)
2177                         if(ui_popup_string_hash(but->str) == mem[hash & 255])
2178                                 return but;
2179
2180                 return NULL;
2181         }
2182 }
2183
2184 /******************** Popup Menu with callback or string **********************/
2185
2186 struct uiPopupMenu {
2187         uiBlock *block;
2188         uiLayout *layout;
2189         uiBut *but;
2190
2191         int mx, my, popup, slideout;
2192         int startx, starty, maxrow;
2193
2194         uiMenuCreateFunc menu_func;
2195         void *menu_arg;
2196 };
2197
2198 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
2199 {
2200         uiBlock *block;
2201         uiBut *bt;
2202         uiPopupMenu *pup= arg_pup;
2203         int offset[2], direction, minwidth, width, height, flip;
2204
2205         if(pup->menu_func) {
2206                 pup->block->handle= handle;
2207                 pup->menu_func(C, pup->layout, pup->menu_arg);
2208                 pup->block->handle= NULL;
2209         }
2210
2211         if(pup->but) {
2212                 /* minimum width to enforece */
2213                 minwidth= pup->but->x2 - pup->but->x1;
2214
2215                 if(pup->but->type == PULLDOWN || pup->but->menu_create_func) {
2216                         direction= UI_DOWN;
2217                         flip= 1;
2218                 }
2219                 else {
2220                         direction= UI_TOP;
2221                         flip= 0;
2222                 }
2223         }
2224         else {
2225                 minwidth= 50;
2226                 direction= UI_DOWN;
2227                 flip= 1;
2228         }
2229
2230         block= pup->block;
2231         
2232         /* in some cases we create the block before the region,
2233            so we set it delayed here if necessary */
2234         if(BLI_findindex(&handle->region->uiblocks, block) == -1)
2235                 uiBlockSetRegion(block, handle->region);
2236
2237         block->direction= direction;
2238
2239         uiBlockLayoutResolve(block, &width, &height);
2240
2241         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2242         
2243         if(pup->popup) {
2244                 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
2245                 uiBlockSetDirection(block, direction);
2246
2247                 /* offset the mouse position, possibly based on earlier selection */
2248                 if((block->flag & UI_BLOCK_POPUP_MEMORY) &&
2249                         (bt= ui_popup_menu_memory(block, NULL))) {
2250                         /* position mouse on last clicked item, at 0.8*width of the
2251                            button, so it doesn't overlap the text too much, also note
2252                            the offset is negative because we are inverse moving the
2253                            block to be under the mouse */
2254                         offset[0]= -(bt->x1 + 0.8f*(bt->x2 - bt->x1));
2255                         offset[1]= -(bt->y1 + 0.5f*UI_UNIT_Y);
2256                 }
2257                 else {
2258                         /* position mouse at 0.8*width of the button and below the tile
2259                            on the first item */
2260                         offset[0]= 0;
2261                         for(bt=block->buttons.first; bt; bt=bt->next)
2262                                 offset[0]= MIN2(offset[0], -(bt->x1 + 0.8f*(bt->x2 - bt->x1)));
2263
2264                         offset[1]= 1.5*UI_UNIT_Y;
2265                 }
2266
2267                 block->minbounds= minwidth;
2268                 uiMenuPopupBoundsBlock(block, 1, offset[0], offset[1]);
2269         }
2270         else {
2271                 /* for a header menu we set the direction automatic */
2272                 if(!pup->slideout && flip) {
2273                         ScrArea *sa= CTX_wm_area(C);
2274                         if(sa && sa->headertype==HEADERDOWN) {
2275                                 ARegion *ar= CTX_wm_region(C);
2276                                 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
2277                                         uiBlockSetDirection(block, UI_TOP);
2278                                         uiBlockFlipOrder(block);
2279                                 }
2280                         }
2281                 }
2282
2283                 block->minbounds= minwidth;
2284                 uiTextBoundsBlock(block, 50);
2285         }
2286
2287         /* if menu slides out of other menu, override direction */
2288         if(pup->slideout)
2289                 uiBlockSetDirection(block, UI_RIGHT);
2290
2291         uiEndBlock(C, block);
2292
2293         return pup->block;
2294 }
2295
2296 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg, char *str)
2297 {
2298         wmWindow *window= CTX_wm_window(C);
2299         uiStyle *style= UI_GetStyle();
2300         uiPopupBlockHandle *handle;
2301         uiPopupMenu *pup;
2302         pup= MEM_callocN(sizeof(uiPopupMenu), "menu dummy");
2303         pup->block= uiBeginBlock(C, NULL, "ui_button_menu_create", UI_EMBOSSP);
2304         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2305         pup->slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
2306         pup->but= but;
2307         uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
2308
2309         if(!but) {
2310                 /* no button to start from, means we are a popup */
2311                 pup->mx= window->eventstate->x;
2312                 pup->my= window->eventstate->y;
2313                 pup->popup= 1;
2314                 pup->block->flag |= UI_BLOCK_NO_FLIP;
2315         }
2316         /* some enums reversing is strange, currently we have no good way to
2317          * reverse some enum's but not others, so reverse all so the first menu
2318          * items are always close to the mouse cursor */
2319 #if 0
2320         else {
2321                 /* if this is an rna button then we can assume its an enum
2322                  * flipping enums is generally not good since the order can be
2323                  * important [#28786] */
2324                 if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
2325                         pup->block->flag |= UI_BLOCK_NO_FLIP;
2326                 }
2327         }
2328 #endif
2329
2330         if(str) {
2331                 /* menu is created from a string */
2332                 pup->menu_func= ui_block_func_MENUSTR;
2333                 pup->menu_arg= str;
2334         }
2335         else {
2336                 /* menu is created from a callback */
2337                 pup->menu_func= menu_func;
2338                 pup->menu_arg= arg;
2339         }
2340         
2341         handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
2342
2343         if(!but) {
2344                 handle->popup= 1;
2345
2346                 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2347                 WM_event_add_mousemove(C);
2348         }
2349         
2350         MEM_freeN(pup);
2351
2352         return handle;
2353 }
2354
2355 /******************** Popup Menu API with begin and end ***********************/
2356
2357 /* only return handler, and set optional title */
2358 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
2359 {
2360         uiStyle *style= UI_GetStyle();
2361         uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "popup menu");
2362         uiBut *but;
2363         
2364         pup->block= uiBeginBlock(C, NULL, "uiPupMenuBegin", UI_EMBOSSP);
2365         pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
2366         pup->block->puphash= ui_popup_menu_hash(title);
2367         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2368         uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
2369
2370         /* create in advance so we can let buttons point to retval already */
2371         pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2372         
2373         /* create title button */
2374         if(title && title[0]) {
2375                 char titlestr[256];
2376                 
2377                 if(icon) {
2378                         BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
2379                         uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2380                 }
2381                 else {
2382                         but= uiDefBut(pup->block, LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2383                         but->flag= UI_TEXT_LEFT;
2384                 }
2385         }
2386
2387         return pup;
2388 }
2389
2390 /* set the whole structure to work */
2391 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
2392 {
2393         wmWindow *window= CTX_wm_window(C);
2394         uiPopupBlockHandle *menu;
2395         
2396         pup->popup= 1;
2397         pup->mx= window->eventstate->x;
2398         pup->my= window->eventstate->y;
2399         
2400         menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
2401         menu->popup= 1;
2402         
2403         UI_add_popup_handlers(C, &window->modalhandlers, menu);
2404         WM_event_add_mousemove(C);
2405         
2406         MEM_freeN(pup);
2407 }
2408
2409 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
2410 {
2411         return pup->layout;
2412 }
2413
2414 /*************************** Standard Popup Menus ****************************/
2415
2416 static void operator_name_cb(bContext *C, void *arg, int retval)
2417 {
2418         const char *opname= arg;
2419
2420         if(opname && retval > 0)
2421                 WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
2422 }
2423
2424 static void operator_cb(bContext *C, void *arg, int retval)
2425 {
2426         wmOperator *op= arg;
2427         
2428         if(op && retval > 0)
2429                 WM_operator_call(C, op);
2430         else
2431                 WM_operator_free(op);
2432 }
2433
2434 static void confirm_cancel_operator(void *opv)
2435 {
2436         WM_operator_free(opv);
2437 }
2438
2439 static void vconfirm_opname(bContext *C, const char *opname, const char *title, const char *itemfmt, va_list ap)
2440 {
2441         uiPopupBlockHandle *handle;
2442         char *s, buf[512];
2443
2444         s= buf;
2445         if (title) s+= sprintf(s, "%s%%t|", title);
2446         vsnprintf(s, sizeof(buf) - (s - buf), itemfmt, ap);
2447         buf[sizeof(buf) - 1]= '\0';
2448
2449         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2450
2451         handle->popup_func= operator_name_cb;
2452         handle->popup_arg= (void *)opname;
2453 }
2454
2455 static void confirm_operator(bContext *C, wmOperator *op, const char *title, const char *item)
2456 {
2457         uiPopupBlockHandle *handle;
2458         char *s, buf[512];
2459         
2460         s= buf;
2461         if (title) s+= sprintf(s, "%s%%t|%s", title, item);
2462         (void)s;
2463         
2464         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2465
2466         handle->popup_func= operator_cb;
2467         handle->popup_arg= op;
2468         handle->cancel_func= confirm_cancel_operator;
2469 }
2470
2471 void uiPupMenuOkee(bContext *C, const char *opname, const char *str, ...)
2472 {
2473         va_list ap;
2474         char titlestr[256];
2475
2476         BLI_snprintf(titlestr, sizeof(titlestr), "OK? %%i%d", ICON_QUESTION);
2477
2478         va_start(ap, str);
2479         vconfirm_opname(C, opname, titlestr, str, ap);
2480         va_end(ap);
2481 }
2482
2483 /* note, only call this is the file exists,
2484  * the case where the file does not exist so can be saved without a
2485  * popup must be checked for already, since saving from here
2486  * will free the operator which will break invoke().
2487  * The operator state for this is implicitly OPERATOR_RUNNING_MODAL */
2488 void uiPupMenuSaveOver(bContext *C, wmOperator *op, const char *filename)
2489 {
2490         confirm_operator(C, op, "Save Over", filename);
2491 }
2492
2493 void uiPupMenuNotice(bContext *C, const char *str, ...)
2494 {
2495         va_list ap;
2496
2497         va_start(ap, str);
2498         vconfirm_opname(C, NULL, NULL, str, ap);
2499         va_end(ap);
2500 }
2501
2502 void uiPupMenuError(bContext *C, const char *str, ...)
2503 {
2504         va_list ap;
2505         char nfmt[256];
2506         char titlestr[256];
2507
2508         BLI_snprintf(titlestr, sizeof(titlestr), "Error %%i%d", ICON_ERROR);
2509
2510         BLI_strncpy(nfmt, str, sizeof(nfmt));
2511
2512         va_start(ap, str);
2513         vconfirm_opname(C, NULL, titlestr, nfmt, ap);
2514         va_end(ap);
2515 }
2516
2517 void uiPupMenuReports(bContext *C, ReportList *reports)
2518 {
2519         Report *report;
2520         DynStr *ds;
2521         char *str;
2522
2523         if(!reports || !reports->list.first)
2524                 return;
2525         if(!CTX_wm_window(C))
2526                 return;
2527
2528         ds= BLI_dynstr_new();
2529
2530         for(report=reports->list.first; report; report=report->next) {
2531                 if(report->type < reports->printlevel)
2532                         ; /* pass */
2533                 else if(report->type >= RPT_ERROR)
2534                         BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
2535                 else if(report->type >= RPT_WARNING)
2536                         BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
2537                 else if(report->type >= RPT_INFO)
2538                         BLI_dynstr_appendf(ds, "Info %%i%d%%t|%s", ICON_INFO, report->message);
2539         }
2540
2541         str= BLI_dynstr_get_cstring(ds);
2542         if(str[0] != '\0')
2543                 ui_popup_menu_create(C, NULL, NULL, NULL, NULL, str);
2544         MEM_freeN(str);
2545
2546         BLI_dynstr_free(ds);
2547 }
2548
2549 void uiPupMenuInvoke(bContext *C, const char *idname)
2550 {
2551         uiPopupMenu *pup;
2552         uiLayout *layout;
2553         Menu menu;
2554         MenuType *mt= WM_menutype_find(idname, TRUE);
2555
2556         if(mt==NULL) {
2557                 printf("%s: named menu \"%s\" not found\n", __func__, idname);
2558                 return;
2559         }
2560
2561         if(mt->poll && mt->poll(C, mt)==0)
2562                 return;
2563
2564         pup= uiPupMenuBegin(C, mt->label, ICON_NONE);
2565         layout= uiPupMenuLayout(pup);
2566
2567         menu.layout= layout;
2568         menu.type= mt;
2569
2570         mt->draw(C, &menu);
2571
2572         uiPupMenuEnd(C, pup);
2573 }
2574
2575
2576 /*************************** Popup Block API **************************/
2577
2578 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext)
2579 {
2580         wmWindow *window= CTX_wm_window(C);
2581         uiPopupBlockHandle *handle;
2582         
2583         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2584         handle->popup= 1;
2585         handle->optype= (opname)? WM_operatortype_find(opname, 0): NULL;
2586         handle->opcontext= opcontext;
2587         
2588         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2589         WM_event_add_mousemove(C);
2590 }
2591
2592 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2593 {
2594         uiPupBlockO(C, func, arg, NULL, 0);
2595 }
2596
2597 void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg)
2598 {
2599         wmWindow *window= CTX_wm_window(C);
2600         uiPopupBlockHandle *handle;
2601         
2602         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2603         handle->popup= 1;
2604         handle->retvalue= 1;
2605
2606         handle->popup_arg= arg;
2607         handle->popup_func= popup_func;
2608         handle->cancel_func= cancel_func;
2609         // handle->opcontext= opcontext;
2610         
2611         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2612         WM_event_add_mousemove(C);
2613 }
2614
2615 #if 0 /* UNUSED */
2616 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2617 {
2618         wmWindow *window= CTX_wm_window(C);
2619         uiPopupBlockHandle *handle;
2620         
2621         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2622         handle->popup= 1;
2623         handle->retvalue= 1;
2624
2625         handle->popup_arg= op;
2626         handle->popup_func= operator_cb;
2627         handle->cancel_func= confirm_cancel_operator;
2628         handle->opcontext= opcontext;
2629         
2630         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2631         WM_event_add_mousemove(C);
2632 }
2633 #endif
2634
2635 void uiPupBlockClose(bContext *C, uiBlock *block)
2636 {
2637         if(block->handle) {
2638                 UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, block->handle);
2639                 ui_popup_block_free(C, block->handle);
2640         }
2641 }
2642
2643 float *ui_block_hsv_get(uiBlock *block)
2644 {
2645         return block->_hsv;
2646 }