Fix [#29018] Problem with multi-column dorpdown lists, when scrolling is enabled...
[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
383                         RNA_property_enum_items_gettexted(C, &but->rnapoin, but->rnaprop, &item, &totitem, &free);
384
385                         for(i=0; i<totitem; i++) {
386                                 if(item[i].identifier[0] && item[i].value == (int)but->hardmax) {
387                                         if(item[i].description[0]) {
388                                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), "%s: %s", item[i].name, item[i].description);
389                                                 data->color[data->totline]= 0xDDDDDD;
390                                                 data->totline++;
391                                         }
392                                         break;
393                                 }
394                         }
395
396                         if(free)
397                                 MEM_freeN(item);
398                 }
399         }
400         
401         if(but->tip && but->tip[0] != '\0') {
402                 BLI_strncpy(data->lines[data->totline], but->tip, sizeof(data->lines[0]));
403                 data->color[data->totline]= 0xFFFFFF;
404                 data->totline++;
405         }
406
407         if(but->optype && !(but->block->flag & UI_BLOCK_LOOP)) {
408                 /* operator keymap (not menus, they already have it) */
409                 prop= (but->opptr)? but->opptr->data: NULL;
410
411                 if(WM_key_event_operator_string(C, but->optype->idname, but->opcontext, prop, TRUE, buf, sizeof(buf))) {
412                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), buf);
413                         data->color[data->totline]= 0x888888;
414                         data->totline++;
415                 }
416         }
417
418         if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
419                 /* full string */
420                 ui_get_but_string(but, buf, sizeof(buf));
421                 if(buf[0]) {
422                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf);
423                         data->color[data->totline]= 0x888888;
424                         data->totline++;
425                 }
426         }
427
428         if(but->rnaprop) {
429                 int unit_type= uiButGetUnitType(but);
430                 
431                 if (unit_type == PROP_UNIT_ROTATION) {
432                         if (RNA_property_type(but->rnaprop) == PROP_FLOAT) {
433                                 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);
434                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value);
435                                 data->color[data->totline]= 0x888888;
436                                 data->totline++;
437                         }
438                 }
439                 
440                 if(but->flag & UI_BUT_DRIVEN) {
441                         if(ui_but_anim_expression_get(but, buf, sizeof(buf))) {
442                                 /* expression */
443                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf);
444                                 data->color[data->totline]= 0x888888;
445                                 data->totline++;
446                         }
447                 }
448
449                 /* rna info */
450                 if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
451                         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));
452                         data->color[data->totline]= 0x888888;
453                         data->totline++;
454                 }
455                 
456                 if(but->rnapoin.id.data) {
457                         ID *id= but->rnapoin.id.data;
458                         if(id->lib && id->lib->name) {
459                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name);
460                                 data->color[data->totline]= 0x888888;
461                                 data->totline++;
462                         }
463                 }
464         }
465         else if (but->optype) {
466                 PointerRNA *opptr;
467                 char *str;
468                 opptr= uiButGetOperatorPtrRNA(but); /* allocated when needed, the button owns it */
469
470                 str= WM_operator_pystring(C, but->optype, opptr, 0);
471
472                 /* operator info */
473                 if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
474                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str);
475                         data->color[data->totline]= 0x888888;
476                         data->totline++;
477                 }
478
479                 MEM_freeN(str);
480
481                 /* second check if we are disabled - why */
482                 if(but->flag & UI_BUT_DISABLED) {
483                         const char *poll_msg;
484                         CTX_wm_operator_poll_msg_set(C, NULL);
485                         WM_operator_poll_context(C, but->optype, but->opcontext);
486                         poll_msg= CTX_wm_operator_poll_msg_get(C);
487                         if(poll_msg) {
488                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), poll_msg);
489                                 data->color[data->totline]= 0x6666ff; /* alert */
490                                 data->totline++;                        
491                         }
492                 }
493         }
494         else if (ELEM(but->type, MENU, PULLDOWN)) {
495                 if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
496                         MenuType *mt= uiButGetMenuType(but);
497                         if (mt) {
498                                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), mt->idname);
499                                 data->color[data->totline]= 0x888888;
500                                 data->totline++;
501                         }
502                 }
503
504         }
505
506         assert(data->totline < MAX_TOOLTIP_LINES);
507         
508         if(data->totline == 0) {
509                 MEM_freeN(data);
510                 return NULL;
511         }
512
513         /* create area region */
514         ar= ui_add_temporary_region(CTX_wm_screen(C));
515
516         memset(&type, 0, sizeof(ARegionType));
517         type.draw= ui_tooltip_region_draw_cb;
518         type.free= ui_tooltip_region_free_cb;
519         ar->type= &type;
520         
521         /* set font, get bb */
522         data->fstyle= style->widget; /* copy struct */
523         data->fstyle.align= UI_STYLE_TEXT_CENTER;
524         uiStyleFontSet(&data->fstyle);
525
526         /* these defines may need to be tweaked depending on font */
527 #define TIP_MARGIN_Y 2
528 #define TIP_BORDER_X 16.0f
529 #define TIP_BORDER_Y 6.0f
530
531         h= BLF_height_max(data->fstyle.uifont_id);
532
533         for(a=0, fontw=0, fonth=0; a<data->totline; a++) {
534                 w= BLF_width(data->fstyle.uifont_id, data->lines[a]);
535                 fontw= MAX2(fontw, w);
536                 fonth += (a == 0)? h: h+TIP_MARGIN_Y;
537         }
538
539         fontw *= aspect;
540
541         ar->regiondata= data;
542
543         data->toth= fonth;
544         data->lineh= h;
545         data->spaceh= TIP_MARGIN_Y;
546
547
548         /* compute position */
549         ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
550         ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
551
552         x1f= (but->x1 + but->x2) * 0.5f + ofsx - (TIP_BORDER_X * aspect);
553         x2f= x1f + fontw + (TIP_BORDER_X * aspect);
554         y2f= but->y1 + ofsy - (TIP_BORDER_Y * aspect);
555         y1f= y2f - fonth*aspect - (TIP_BORDER_Y * aspect);
556         
557 #undef TIP_MARGIN_Y
558 #undef TIP_BORDER_X
559 #undef TIP_BORDER_Y
560
561         /* copy to int, gets projected if possible too */
562         x1= x1f; y1= y1f; x2= x2f; y2= y2f; 
563         
564         if(butregion) {
565                 /* XXX temp, region v2ds can be empty still */
566                 if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
567                         UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
568                         UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
569                 }
570
571                 x1 += butregion->winrct.xmin;
572                 x2 += butregion->winrct.xmin;
573                 y1 += butregion->winrct.ymin;
574                 y2 += butregion->winrct.ymin;
575         }
576
577         wm_window_get_size(CTX_wm_window(C), &winx, &winy);
578
579         if(x2 > winx) {
580                 /* super size */
581                 if(x2 > winx + x1) {
582                         x2= winx;
583                         x1= 0;
584                 }
585                 else {
586                         x1 -= x2-winx;
587                         x2= winx;
588                 }
589         }
590         /* ensure at least 5 px above screen bounds
591          * 25 is just a guess to be above the menu item */
592         if(y1 < 5) {
593                 y2 += (-y1) + 30;
594                 y1 = 30;
595         }
596
597         /* widget rect, in region coords */
598         data->bbox.xmin= MENU_SHADOW_SIDE;
599         data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
600         data->bbox.ymin= MENU_SHADOW_BOTTOM;
601         data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
602         
603         /* region bigger for shadow */
604         ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
605         ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
606         ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
607         ar->winrct.ymax= y2 + MENU_TOP;
608
609         /* adds subwindow */
610         ED_region_init(C, ar);
611         
612         /* notify change and redraw */
613         ED_region_tag_redraw(ar);
614
615         return ar;
616 }
617
618 void ui_tooltip_free(bContext *C, ARegion *ar)
619 {
620         ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
621 }
622
623
624 /************************* Creating Search Box **********************/
625
626 struct uiSearchItems {
627         int maxitem, totitem, maxstrlen;
628         
629         int offset, offset_i; /* offset for inserting in array */
630         int more;  /* flag indicating there are more items */
631         
632         char **names;
633         void **pointers;
634         int *icons;
635
636         AutoComplete *autocpl;
637         void *active;
638 };
639
640 typedef struct uiSearchboxData {
641         rcti bbox;
642         uiFontStyle fstyle;
643         uiSearchItems items;
644         int active;             /* index in items array */
645         int noback;             /* when menu opened with enough space for this */
646         int preview;    /* draw thumbnail previews, rather than list */
647         int prv_rows, prv_cols;
648 } uiSearchboxData;
649
650 #define SEARCH_ITEMS    10
651
652 /* exported for use by search callbacks */
653 /* returns zero if nothing to add */
654 int uiSearchItemAdd(uiSearchItems *items, const char *name, void *poin, int iconid)
655 {
656         /* hijack for autocomplete */
657         if(items->autocpl) {
658                 autocomplete_do_name(items->autocpl, name);
659                 return 1;
660         }
661         
662         /* hijack for finding active item */
663         if(items->active) {
664                 if(poin==items->active)
665                         items->offset_i= items->totitem;
666                 items->totitem++;
667                 return 1;
668         }
669         
670         if(items->totitem>=items->maxitem) {
671                 items->more= 1;
672                 return 0;
673         }
674         
675         /* skip first items in list */
676         if(items->offset_i > 0) {
677                 items->offset_i--;
678                 return 1;
679         }
680         
681         if(items->names)
682                 BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
683         if(items->pointers)
684                 items->pointers[items->totitem]= poin;
685         if(items->icons)
686                 items->icons[items->totitem]= iconid;
687         
688         items->totitem++;
689         
690         return 1;
691 }
692
693 int uiSearchBoxhHeight(void)
694 {
695         return SEARCH_ITEMS*UI_UNIT_Y + 2*MENU_TOP;
696 }
697
698 /* ar is the search box itself */
699 static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
700 {
701         uiSearchboxData *data= ar->regiondata;
702         
703         /* apply step */
704         data->active+= step;
705         
706         if(data->items.totitem==0)
707                 data->active= 0;
708         else if(data->active > data->items.totitem) {
709                 if(data->items.more) {
710                         data->items.offset++;
711                         data->active= data->items.totitem;
712                         ui_searchbox_update(C, ar, but, 0);
713                 }
714                 else
715                         data->active= data->items.totitem;
716         }
717         else if(data->active < 1) {
718                 if(data->items.offset) {
719                         data->items.offset--;
720                         data->active= 1;
721                         ui_searchbox_update(C, ar, but, 0);
722                 }
723                 else if(data->active < 0)
724                         data->active= 0;
725         }
726         
727         ED_region_tag_redraw(ar);
728 }
729
730 static void ui_searchbox_butrect(rcti *rect, uiSearchboxData *data, int itemnr)
731 {
732         /* thumbnail preview */
733         if (data->preview) {
734                 int buth = (data->bbox.ymax - data->bbox.ymin - 2*MENU_TOP) / data->prv_rows;
735                 int butw = (data->bbox.xmax - data->bbox.xmin) / data->prv_cols;
736                 int row, col;
737                 
738                 *rect= data->bbox;
739                 
740                 col = itemnr % data->prv_cols;
741                 row = itemnr / data->prv_cols;
742                 
743                 rect->xmin += col * butw;
744                 rect->xmax = rect->xmin + butw;
745                 
746                 rect->ymax = data->bbox.ymax - MENU_TOP - (row * buth);
747                 rect->ymin = rect->ymax - buth;
748         }
749         /* list view */
750         else {
751                 int buth= (data->bbox.ymax-data->bbox.ymin - 2*MENU_TOP)/SEARCH_ITEMS;
752                 
753                 *rect= data->bbox;
754                 rect->xmin= data->bbox.xmin + 3.0f;
755                 rect->xmax= data->bbox.xmax - 3.0f;
756                 
757                 rect->ymax= data->bbox.ymax - MENU_TOP - itemnr*buth;
758                 rect->ymin= rect->ymax - buth;
759         }
760         
761 }
762
763 /* x and y in screencoords */
764 int ui_searchbox_inside(ARegion *ar, int x, int y)
765 {
766         uiSearchboxData *data= ar->regiondata;
767         
768         return(BLI_in_rcti(&data->bbox, x-ar->winrct.xmin, y-ar->winrct.ymin));
769 }
770
771 /* string validated to be of correct length (but->hardmax) */
772 void ui_searchbox_apply(uiBut *but, ARegion *ar)
773 {
774         uiSearchboxData *data= ar->regiondata;
775
776         but->func_arg2= NULL;
777         
778         if(data->active) {
779                 char *name= data->items.names[data->active-1];
780                 char *cpoin= strchr(name, '|');
781                 
782                 if(cpoin) cpoin[0]= 0;
783                 BLI_strncpy(but->editstr, name, data->items.maxstrlen);
784                 if(cpoin) cpoin[0]= '|';
785                 
786                 but->func_arg2= data->items.pointers[data->active-1];
787         }
788 }
789
790 void ui_searchbox_event(bContext *C, ARegion *ar, uiBut *but, wmEvent *event)
791 {
792         uiSearchboxData *data= ar->regiondata;
793         
794         switch(event->type) {
795                 case WHEELUPMOUSE:
796                 case UPARROWKEY:
797                         ui_searchbox_select(C, ar, but, -1);
798                         break;
799                 case WHEELDOWNMOUSE:
800                 case DOWNARROWKEY:
801                         ui_searchbox_select(C, ar, but, 1);
802                         break;
803                 case MOUSEMOVE:
804                         if(BLI_in_rcti(&ar->winrct, event->x, event->y)) {
805                                 rcti rect;
806                                 int a;
807                                 
808                                 for(a=0; a<data->items.totitem; a++) {
809                                         ui_searchbox_butrect(&rect, data, a);
810                                         if(BLI_in_rcti(&rect, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
811                                                 if( data->active!= a+1) {
812                                                         data->active= a+1;
813                                                         ui_searchbox_select(C, ar, but, 0);
814                                                         break;
815                                                 }
816                                         }
817                                 }
818                         }
819                         break;
820         }
821 }
822
823 /* ar is the search box itself */
824 void ui_searchbox_update(bContext *C, ARegion *ar, uiBut *but, int reset)
825 {
826         uiSearchboxData *data= ar->regiondata;
827         
828         /* reset vars */
829         data->items.totitem= 0;
830         data->items.more= 0;
831         if(reset==0) {
832                 data->items.offset_i= data->items.offset;
833         }
834         else {
835                 data->items.offset_i= data->items.offset= 0;
836                 data->active= 0;
837                 
838                 /* handle active */
839                 if(but->search_func && but->func_arg2) {
840                         data->items.active= but->func_arg2;
841                         but->search_func(C, but->search_arg, but->editstr, &data->items);
842                         data->items.active= NULL;
843                         
844                         /* found active item, calculate real offset by centering it */
845                         if(data->items.totitem) {
846                                 /* first case, begin of list */
847                                 if(data->items.offset_i < data->items.maxitem) {
848                                         data->active= data->items.offset_i+1;
849                                         data->items.offset_i= 0;
850                                 }
851                                 else {
852                                         /* second case, end of list */
853                                         if(data->items.totitem - data->items.offset_i <= data->items.maxitem) {
854                                                 data->active= 1 + data->items.offset_i - data->items.totitem + data->items.maxitem;
855                                                 data->items.offset_i= data->items.totitem - data->items.maxitem;
856                                         }
857                                         else {
858                                                 /* center active item */
859                                                 data->items.offset_i -= data->items.maxitem/2;
860                                                 data->active= 1 + data->items.maxitem/2;
861                                         }
862                                 }
863                         }
864                         data->items.offset= data->items.offset_i;
865                         data->items.totitem= 0;
866                 }
867         }
868         
869         /* callback */
870         if(but->search_func)
871                 but->search_func(C, but->search_arg, but->editstr, &data->items);
872         
873         /* handle case where editstr is equal to one of items */
874         if(reset && data->active==0) {
875                 int a;
876                 
877                 for(a=0; a<data->items.totitem; a++) {
878                         char *cpoin= strchr(data->items.names[a], '|');
879                         
880                         if(cpoin) cpoin[0]= 0;
881                         if(0==strcmp(but->editstr, data->items.names[a]))
882                                 data->active= a+1;
883                         if(cpoin) cpoin[0]= '|';
884                 }
885                 if(data->items.totitem==1 && but->editstr[0])
886                         data->active= 1;
887         }
888
889         /* validate selected item */
890         ui_searchbox_select(C, ar, but, 0);
891         
892         ED_region_tag_redraw(ar);
893 }
894
895 void ui_searchbox_autocomplete(bContext *C, ARegion *ar, uiBut *but, char *str)
896 {
897         uiSearchboxData *data= ar->regiondata;
898
899         if(str[0]) {
900                 data->items.autocpl= autocomplete_begin(str, ui_get_but_string_max_length(but));
901
902                 but->search_func(C, but->search_arg, but->editstr, &data->items);
903
904                 autocomplete_end(data->items.autocpl, str);
905                 data->items.autocpl= NULL;
906         }
907 }
908
909 static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
910 {
911         uiSearchboxData *data= ar->regiondata;
912         
913         /* pixel space */
914         wmOrtho2(-0.01f, ar->winx-0.01f, -0.01f, ar->winy-0.01f);
915
916         if(!data->noback)
917                 ui_draw_search_back(NULL, NULL, &data->bbox); /* style not used yet */
918         
919         /* draw text */
920         if(data->items.totitem) {
921                 rcti rect;
922                 int a;
923                 
924                 if (data->preview) {
925                         /* draw items */
926                         for(a=0; a<data->items.totitem; a++) {
927                                 ui_searchbox_butrect(&rect, data, a);
928                                 
929                                 /* widget itself */
930                                 if (data->preview)
931                                         ui_draw_preview_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
932                                 else 
933                                         ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
934                         }
935                         
936                         /* indicate more */
937                         if(data->items.more) {
938                                 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
939                                 glEnable(GL_BLEND);
940                                 UI_icon_draw(rect.xmax-18, rect.ymin-7, ICON_TRIA_DOWN);
941                                 glDisable(GL_BLEND);
942                         }
943                         if(data->items.offset) {
944                                 ui_searchbox_butrect(&rect, data, 0);
945                                 glEnable(GL_BLEND);
946                                 UI_icon_draw(rect.xmin, rect.ymax-9, ICON_TRIA_UP);
947                                 glDisable(GL_BLEND);
948                         }
949                         
950                 } else {
951                         /* draw items */
952                         for(a=0; a<data->items.totitem; a++) {
953                                 ui_searchbox_butrect(&rect, data, a);
954                                 
955                                 /* widget itself */
956                                 ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
957                                 
958                         }
959                         /* indicate more */
960                         if(data->items.more) {
961                                 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
962                                 glEnable(GL_BLEND);
963                                 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymin-9, ICON_TRIA_DOWN);
964                                 glDisable(GL_BLEND);
965                         }
966                         if(data->items.offset) {
967                                 ui_searchbox_butrect(&rect, data, 0);
968                                 glEnable(GL_BLEND);
969                                 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymax-7, ICON_TRIA_UP);
970                                 glDisable(GL_BLEND);
971                         }
972                 }
973         }
974 }
975
976 static void ui_searchbox_region_free_cb(ARegion *ar)
977 {
978         uiSearchboxData *data= ar->regiondata;
979         int a;
980
981         /* free search data */
982         for(a=0; a<data->items.maxitem; a++)
983                 MEM_freeN(data->items.names[a]);
984         MEM_freeN(data->items.names);
985         MEM_freeN(data->items.pointers);
986         MEM_freeN(data->items.icons);
987         
988         MEM_freeN(data);
989         ar->regiondata= NULL;
990 }
991
992 ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but)
993 {
994         uiStyle *style= UI_GetStyle();
995         static ARegionType type;
996         ARegion *ar;
997         uiSearchboxData *data;
998         float aspect= but->block->aspect;
999         float x1f, x2f, y1f, y2f;
1000         int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
1001         
1002         /* create area region */
1003         ar= ui_add_temporary_region(CTX_wm_screen(C));
1004         
1005         memset(&type, 0, sizeof(ARegionType));
1006         type.draw= ui_searchbox_region_draw_cb;
1007         type.free= ui_searchbox_region_free_cb;
1008         ar->type= &type;
1009         
1010         /* create searchbox data */
1011         data= MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
1012
1013         /* set font, get bb */
1014         data->fstyle= style->widget; /* copy struct */
1015         data->fstyle.align= UI_STYLE_TEXT_CENTER;
1016         ui_fontscale(&data->fstyle.points, aspect);
1017         uiStyleFontSet(&data->fstyle);
1018         
1019         ar->regiondata= data;
1020         
1021         /* special case, hardcoded feature, not draw backdrop when called from menus,
1022            assume for design that popup already added it */
1023         if(but->block->flag & UI_BLOCK_LOOP)
1024                 data->noback= 1;
1025         
1026         if (but->a1 > 0 && but->a2 > 0) {
1027                 data->preview = 1;
1028                 data->prv_rows = but->a1;
1029                 data->prv_cols = but->a2;
1030         }
1031         
1032         /* compute position */
1033         if(but->block->flag & UI_BLOCK_LOOP) {
1034                 /* this case is search menu inside other menu */
1035                 /* we copy region size */
1036
1037                 ar->winrct= butregion->winrct;
1038                 
1039                 /* widget rect, in region coords */
1040                 data->bbox.xmin= MENU_SHADOW_SIDE;
1041                 data->bbox.xmax= (ar->winrct.xmax-ar->winrct.xmin) - MENU_SHADOW_SIDE;
1042                 data->bbox.ymin= MENU_SHADOW_BOTTOM;
1043                 data->bbox.ymax= (ar->winrct.ymax-ar->winrct.ymin) - MENU_SHADOW_BOTTOM;
1044                 
1045                 /* check if button is lower half */
1046                 if( but->y2 < (but->block->miny+but->block->maxy)/2 ) {
1047                         data->bbox.ymin += (but->y2-but->y1);
1048                 }
1049                 else {
1050                         data->bbox.ymax -= (but->y2-but->y1);
1051                 }
1052         }
1053         else {
1054                 x1f= but->x1 - 5;       /* align text with button */
1055                 x2f= but->x2 + 5;       /* symmetrical */
1056                 y2f= but->y1;
1057                 y1f= y2f - uiSearchBoxhHeight();
1058
1059                 ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
1060                 ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
1061
1062                 x1f += ofsx;
1063                 x2f += ofsx;
1064                 y1f += ofsy;
1065                 y2f += ofsy;
1066         
1067                 /* minimal width */
1068                 if(x2f - x1f < 150) x2f= x1f+150; // XXX arbitrary
1069                 
1070                 /* copy to int, gets projected if possible too */
1071                 x1= x1f; y1= y1f; x2= x2f; y2= y2f; 
1072                 
1073                 if(butregion) {
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                 
1085                 wm_window_get_size(CTX_wm_window(C), &winx, &winy);
1086                 
1087                 if(x2 > winx) {
1088                         /* super size */
1089                         if(x2 > winx + x1) {
1090                                 x2= winx;
1091                                 x1= 0;
1092                         }
1093                         else {
1094                                 x1 -= x2-winx;
1095                                 x2= winx;
1096                         }
1097                 }
1098                 if(y1 < 0) { /* XXX butregion NULL check?, there is one above */
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 void uiPupMenuSaveOver(bContext *C, wmOperator *op, const char *filename)
2484 {
2485         size_t len= strlen(filename);
2486
2487         if(len==0)
2488                 return;
2489
2490         if(filename[len-1]=='/' || filename[len-1]=='\\') {
2491                 uiPupMenuError(C, "Cannot overwrite a directory");
2492                 WM_operator_free(op);
2493                 return;
2494         }
2495         if(BLI_exists(filename)==0)
2496                 operator_cb(C, op, 1);
2497         else
2498                 confirm_operator(C, op, "Save Over", filename);
2499 }
2500
2501 void uiPupMenuNotice(bContext *C, const char *str, ...)
2502 {
2503         va_list ap;
2504
2505         va_start(ap, str);
2506         vconfirm_opname(C, NULL, NULL, str, ap);
2507         va_end(ap);
2508 }
2509
2510 void uiPupMenuError(bContext *C, const char *str, ...)
2511 {
2512         va_list ap;
2513         char nfmt[256];
2514         char titlestr[256];
2515
2516         BLI_snprintf(titlestr, sizeof(titlestr), "Error %%i%d", ICON_ERROR);
2517
2518         BLI_strncpy(nfmt, str, sizeof(nfmt));
2519
2520         va_start(ap, str);
2521         vconfirm_opname(C, NULL, titlestr, nfmt, ap);
2522         va_end(ap);
2523 }
2524
2525 void uiPupMenuReports(bContext *C, ReportList *reports)
2526 {
2527         Report *report;
2528         DynStr *ds;
2529         char *str;
2530
2531         if(!reports || !reports->list.first)
2532                 return;
2533         if(!CTX_wm_window(C))
2534                 return;
2535
2536         ds= BLI_dynstr_new();
2537
2538         for(report=reports->list.first; report; report=report->next) {
2539                 if(report->type < reports->printlevel)
2540                         ; /* pass */
2541                 else if(report->type >= RPT_ERROR)
2542                         BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
2543                 else if(report->type >= RPT_WARNING)
2544                         BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
2545                 else if(report->type >= RPT_INFO)
2546                         BLI_dynstr_appendf(ds, "Info %%i%d%%t|%s", ICON_INFO, report->message);
2547         }
2548
2549         str= BLI_dynstr_get_cstring(ds);
2550         if(str[0] != '\0')
2551                 ui_popup_menu_create(C, NULL, NULL, NULL, NULL, str);
2552         MEM_freeN(str);
2553
2554         BLI_dynstr_free(ds);
2555 }
2556
2557 void uiPupMenuInvoke(bContext *C, const char *idname)
2558 {
2559         uiPopupMenu *pup;
2560         uiLayout *layout;
2561         Menu menu;
2562         MenuType *mt= WM_menutype_find(idname, TRUE);
2563
2564         if(mt==NULL) {
2565                 printf("%s: named menu \"%s\" not found\n", __func__, idname);
2566                 return;
2567         }
2568
2569         if(mt->poll && mt->poll(C, mt)==0)
2570                 return;
2571
2572         pup= uiPupMenuBegin(C, mt->label, ICON_NONE);
2573         layout= uiPupMenuLayout(pup);
2574
2575         menu.layout= layout;
2576         menu.type= mt;
2577
2578         mt->draw(C, &menu);
2579
2580         uiPupMenuEnd(C, pup);
2581 }
2582
2583
2584 /*************************** Popup Block API **************************/
2585
2586 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext)
2587 {
2588         wmWindow *window= CTX_wm_window(C);
2589         uiPopupBlockHandle *handle;
2590         
2591         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2592         handle->popup= 1;
2593         handle->optype= (opname)? WM_operatortype_find(opname, 0): NULL;
2594         handle->opcontext= opcontext;
2595         
2596         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2597         WM_event_add_mousemove(C);
2598 }
2599
2600 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2601 {
2602         uiPupBlockO(C, func, arg, NULL, 0);
2603 }
2604
2605 void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockCancelFunc cancel_func, void *arg)
2606 {
2607         wmWindow *window= CTX_wm_window(C);
2608         uiPopupBlockHandle *handle;
2609         
2610         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2611         handle->popup= 1;
2612         handle->retvalue= 1;
2613
2614         handle->popup_arg= arg;
2615         // handle->popup_func= operator_cb;
2616         handle->cancel_func= cancel_func;
2617         // handle->opcontext= opcontext;
2618         
2619         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2620         WM_event_add_mousemove(C);
2621 }
2622
2623 #if 0 /* UNUSED */
2624 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2625 {
2626         wmWindow *window= CTX_wm_window(C);
2627         uiPopupBlockHandle *handle;
2628         
2629         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2630         handle->popup= 1;
2631         handle->retvalue= 1;
2632
2633         handle->popup_arg= op;
2634         handle->popup_func= operator_cb;
2635         handle->cancel_func= confirm_cancel_operator;
2636         handle->opcontext= opcontext;
2637         
2638         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2639         WM_event_add_mousemove(C);
2640 }
2641 #endif
2642
2643 void uiPupBlockClose(bContext *C, uiBlock *block)
2644 {
2645         if(block->handle) {
2646                 UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, block->handle);
2647                 ui_popup_block_free(C, block->handle);
2648         }
2649 }
2650
2651 float *ui_block_hsv_get(uiBlock *block)
2652 {
2653         return block->_hsv;
2654 }