svn merge -r41779:41847 ^/trunk/blender
[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(but->type == ROW) {
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]= 0xFFFFFF;
390                                                 data->totline++;
391                                         }
392                                         break;
393                                 }
394                         }
395
396                         if(free)
397                                 MEM_freeN(item);
398                 }
399         }
400         
401         if(but->tip && strlen(but->tip)) {
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         
1450         block->flag &= ~(UI_BLOCK_CLIPBOTTOM|UI_BLOCK_CLIPTOP);
1451         
1452         for(bt= block->buttons.first; bt; bt= bt->next)
1453                 bt->flag &= ~UI_SCROLLED;
1454         
1455         if(block->buttons.first==block->buttons.last)
1456                 return;
1457         
1458         /* mark buttons that are outside boundary and the ones next to it for arrow(s) */
1459         for(bt= block->buttons.first; bt; bt= bt->next) {
1460                 if(bt->y1 < block->miny) {
1461                         bt->flag |= UI_SCROLLED;
1462                         block->flag |= UI_BLOCK_CLIPBOTTOM;
1463                         /* make space for arrow */
1464                         if(bt->y2 < block->miny +10) {
1465                                 if(bt->next && bt->next->y1 > bt->y1)
1466                                         bt->next->flag |= UI_SCROLLED;
1467                                 if(bt->prev && bt->prev->y1 > bt->y1)
1468                                         bt->prev->flag |= UI_SCROLLED;
1469                         }
1470                 }
1471                 if(bt->y2 > block->maxy) {
1472                         bt->flag |= UI_SCROLLED;
1473                         block->flag |= UI_BLOCK_CLIPTOP;
1474                         /* make space for arrow */
1475                         if(bt->y1 > block->maxy -10) {
1476                                 if(bt->next && bt->next->y2 < bt->y2)
1477                                         bt->next->flag |= UI_SCROLLED;
1478                                 if(bt->prev && bt->prev->y2 < bt->y2)
1479                                         bt->prev->flag |= UI_SCROLLED;
1480                         }
1481                 }
1482         }
1483 }
1484
1485 uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg)
1486 {
1487         wmWindow *window= CTX_wm_window(C);
1488         static ARegionType type;
1489         ARegion *ar;
1490         uiBlock *block;
1491         uiBut *bt;
1492         uiPopupBlockHandle *handle;
1493         uiSafetyRct *saferct;
1494
1495         /* create handle */
1496         handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
1497
1498         /* store context for operator */
1499         handle->ctx_area= CTX_wm_area(C);
1500         handle->ctx_region= CTX_wm_region(C);
1501         
1502         /* create area region */
1503         ar= ui_add_temporary_region(CTX_wm_screen(C));
1504         handle->region= ar;
1505
1506         memset(&type, 0, sizeof(ARegionType));
1507         type.draw= ui_block_region_draw;
1508         ar->type= &type;
1509
1510         UI_add_region_handlers(&ar->handlers);
1511
1512         /* create ui block */
1513         if(create_func)
1514                 block= create_func(C, handle->region, arg);
1515         else
1516                 block= handle_create_func(C, handle, arg);
1517         
1518         if(block->handle) {
1519                 memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
1520                 MEM_freeN(handle);
1521                 handle= block->handle;
1522         }
1523         else
1524                 block->handle= handle;
1525
1526         ar->regiondata= handle;
1527
1528         if(!block->endblock)
1529                 uiEndBlock(C, block);
1530
1531         /* if this is being created from a button */
1532         if(but) {
1533                 if(ELEM(but->type, BLOCK, PULLDOWN))
1534                         block->xofs = -2;       /* for proper alignment */
1535
1536                 ui_block_position(window, butregion, but, block);
1537         }
1538         else {
1539                 /* keep a list of these, needed for pulldown menus */
1540                 saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1541                 saferct->safety= block->safety;
1542                 BLI_addhead(&block->saferct, saferct);
1543                 block->flag |= UI_BLOCK_POPUP|UI_BLOCK_NUMSELECT;
1544         }
1545
1546         /* clip block with window boundary */
1547         ui_popup_block_clip(window, block);
1548         
1549         /* the block and buttons were positioned in window space as in 2.4x, now
1550          * these menu blocks are regions so we bring it back to region space.
1551          * additionally we add some padding for the menu shadow or rounded menus */
1552         ar->winrct.xmin= block->minx - MENU_SHADOW_SIDE;
1553         ar->winrct.xmax= block->maxx + MENU_SHADOW_SIDE;
1554         ar->winrct.ymin= block->miny - MENU_SHADOW_BOTTOM;
1555         ar->winrct.ymax= block->maxy + MENU_TOP;
1556         
1557         block->minx -= ar->winrct.xmin;
1558         block->maxx -= ar->winrct.xmin;
1559         block->miny -= ar->winrct.ymin;
1560         block->maxy -= ar->winrct.ymin;
1561
1562         for(bt= block->buttons.first; bt; bt= bt->next) {
1563                 bt->x1 -= ar->winrct.xmin;
1564                 bt->x2 -= ar->winrct.xmin;
1565                 bt->y1 -= ar->winrct.ymin;
1566                 bt->y2 -= ar->winrct.ymin;
1567         }
1568         
1569         block->flag |= UI_BLOCK_LOOP;
1570
1571         /* adds subwindow */
1572         ED_region_init(C, ar);
1573
1574         /* checks which buttons are visible, sets flags to prevent draw (do after region init) */
1575         ui_popup_block_scrolltest(block);
1576         
1577         /* get winmat now that we actually have the subwindow */
1578         wmSubWindowSet(window, ar->swinid);
1579         
1580         wm_subwindow_getmatrix(window, ar->swinid, block->winmat);
1581         
1582         /* notify change and redraw */
1583         ED_region_tag_redraw(ar);
1584
1585         return handle;
1586 }
1587
1588 void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
1589 {
1590         ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
1591         
1592         if(handle->scrolltimer)
1593                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
1594         
1595         MEM_freeN(handle);
1596 }
1597
1598 /***************************** Menu Button ***************************/
1599
1600 static void ui_block_func_MENUSTR(bContext *UNUSED(C), uiLayout *layout, void *arg_str)
1601 {
1602         uiBlock *block= uiLayoutGetBlock(layout);
1603         uiPopupBlockHandle *handle= block->handle;
1604         uiLayout *split, *column=NULL;
1605         uiBut *bt;
1606         MenuData *md;
1607         MenuEntry *entry;
1608         const char *instr= arg_str;
1609         int columns, rows, a, b;
1610
1611         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1612         
1613         /* compute menu data */
1614         md= decompose_menu_string(instr);
1615
1616         /* columns and row estimation */
1617         columns= (md->nitems+20)/20;
1618         if(columns<1)
1619                 columns= 1;
1620         if(columns>8)
1621                 columns= (md->nitems+25)/25;
1622         
1623         rows= md->nitems/columns;
1624         if(rows<1)
1625                 rows= 1;
1626         while(rows*columns<md->nitems)
1627                 rows++;
1628
1629         /* create title */
1630         if(md->title) {
1631                 if(md->titleicon) {
1632                         uiItemL(layout, md->title, md->titleicon);
1633                 }
1634                 else {
1635                         uiItemL(layout, md->title, ICON_NONE);
1636                         bt= block->buttons.last;
1637                         bt->flag= UI_TEXT_LEFT;
1638                 }
1639         }
1640
1641         /* inconsistent, but menus with labels do not look good flipped */
1642         entry= md->items;
1643         for(a=0; a<md->nitems; a++, entry++) {
1644                 if(entry->sepr && entry->str[0]) {
1645                         block->flag |= UI_BLOCK_NO_FLIP;
1646                         break;
1647                 }
1648         }
1649
1650         /* create items */
1651         split= uiLayoutSplit(layout, 0, 0);
1652
1653         for(a=0, b=0; a<md->nitems; a++, b++) {
1654                 if(block->flag & UI_BLOCK_NO_FLIP)
1655                         entry= &md->items[a];
1656                 else
1657                         entry= &md->items[md->nitems-a-1];
1658                 
1659                 /* new column on N rows or on separation label */
1660                 if((b % rows == 0) || (entry->sepr && entry->str[0])) {
1661                         column= uiLayoutColumn(split, 0);
1662                         b= 0;
1663                 }
1664
1665                 if(entry->sepr) {
1666                         uiItemL(column, entry->str, entry->icon);
1667                         bt= block->buttons.last;
1668                         bt->flag= UI_TEXT_LEFT;
1669                 }
1670                 else if(entry->icon) {
1671                         uiDefIconTextButF(block, BUTM|FLO, B_NOP, entry->icon, entry->str, 0, 0,
1672                                 UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1673                 }
1674                 else {
1675                         uiDefButF(block, BUTM|FLO, B_NOP, entry->str, 0, 0,
1676                                 UI_UNIT_X*5, UI_UNIT_X, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1677                 }
1678         }
1679         
1680         menudata_free(md);
1681 }
1682
1683 void ui_block_func_ICONROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1684 {
1685         uiBlock *block= uiLayoutGetBlock(layout);
1686         uiPopupBlockHandle *handle= block->handle;
1687         uiBut *but= arg_but;
1688         int a;
1689         
1690         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1691         
1692         for(a=(int)but->hardmin; a<=(int)but->hardmax; a++)
1693                 uiDefIconButF(block, BUTM|FLO, B_NOP, but->icon+(a-but->hardmin), 0, 0, UI_UNIT_X*5, UI_UNIT_Y,
1694                         &handle->retvalue, (float)a, 0.0, 0, 0, "");
1695 }
1696
1697 void ui_block_func_ICONTEXTROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1698 {
1699         uiBlock *block= uiLayoutGetBlock(layout);
1700         uiPopupBlockHandle *handle= block->handle;
1701         uiBut *but= arg_but, *bt;
1702         MenuData *md;
1703         MenuEntry *entry;
1704         int a;
1705         
1706         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1707
1708         md= decompose_menu_string(but->str);
1709
1710         /* title */
1711         if(md->title) {
1712                 bt= uiDefBut(block, LABEL, 0, md->title, 0, 0, UI_UNIT_X*5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1713                 bt->flag= UI_TEXT_LEFT;
1714         }
1715
1716         /* loop through the menu options and draw them out with icons & text labels */
1717         for(a=0; a<md->nitems; a++) {
1718                 entry= &md->items[md->nitems-a-1];
1719
1720                 if(entry->sepr)
1721                         uiItemS(layout);
1722                 else
1723                         uiDefIconTextButF(block, BUTM|FLO, B_NOP, (short)((but->icon)+(entry->retval-but->hardmin)), entry->str,
1724                                 0, 0, UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1725         }
1726
1727         menudata_free(md);
1728 }
1729
1730 #if 0
1731 static void ui_warp_pointer(int x, int y)
1732 {
1733         /* XXX 2.50 which function to use for this? */
1734         /* OSX has very poor mousewarp support, it sends events;
1735            this causes a menu being pressed immediately ... */
1736         #ifndef __APPLE__
1737         warp_pointer(x, y);
1738         #endif
1739 }
1740 #endif
1741
1742 /********************* Color Button ****************/
1743
1744 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1745 #define SPICK   110.0
1746 #define FPICK   180.0
1747 #define DPICK   6.0
1748 #define BPICK   24.0
1749
1750 /* for picker, while editing hsv */
1751 void ui_set_but_hsv(uiBut *but)
1752 {
1753         float col[3];
1754         float *hsv= ui_block_hsv_get(but->block);
1755         
1756         hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
1757         ui_set_but_vectorf(but, col);
1758 }
1759
1760 /* also used by small picker, be careful with name checks below... */
1761 static void ui_update_block_buts_rgb(uiBlock *block, float *rgb)
1762 {
1763         uiBut *bt;
1764         float *hsv= ui_block_hsv_get(block);
1765         
1766         /* this is to keep the H and S value when V is equal to zero
1767          * and we are working in HSV mode, of course!
1768          */
1769         rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
1770         
1771         // this updates button strings, is hackish... but button pointers are on stack of caller function
1772         for(bt= block->buttons.first; bt; bt= bt->next) {
1773                 if (bt->rnaprop) {
1774                         
1775                         ui_set_but_vectorf(bt, rgb);
1776                         
1777                 }
1778                 else if(strcmp(bt->str, "Hex: ")==0) {
1779                         float rgb_gamma[3];
1780                         double intpart;
1781                         char col[16];
1782                         
1783                         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1784                         
1785                         if (block->color_profile == BLI_PR_NONE) {
1786                                 copy_v3_v3(rgb_gamma, rgb);
1787                         } else {
1788                                 /* make an sRGB version, for Hex code */
1789                                 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1790                         }
1791                         
1792                         if (rgb_gamma[0] > 1.0f) rgb_gamma[0] = modf(rgb_gamma[0], &intpart);
1793                         if (rgb_gamma[1] > 1.0f) rgb_gamma[1] = modf(rgb_gamma[1], &intpart);
1794                         if (rgb_gamma[2] > 1.0f) rgb_gamma[2] = modf(rgb_gamma[2], &intpart);
1795
1796                         BLI_snprintf(col, sizeof(col), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
1797                         
1798                         strcpy(bt->poin, col);
1799                 }
1800                 else if(bt->str[1]==' ') {
1801                         if(bt->str[0]=='R') {
1802                                 ui_set_but_val(bt, rgb[0]);
1803                         }
1804                         else if(bt->str[0]=='G') {
1805                                 ui_set_but_val(bt, rgb[1]);
1806                         }
1807                         else if(bt->str[0]=='B') {
1808                                 ui_set_but_val(bt, rgb[2]);
1809                         }
1810                         else if(bt->str[0]=='H') {
1811                                 ui_set_but_val(bt, hsv[0]);
1812                         }
1813                         else if(bt->str[0]=='S') {
1814                                 ui_set_but_val(bt, hsv[1]);
1815                         }
1816                         else if(bt->str[0]=='V') {
1817                                 ui_set_but_val(bt, hsv[2]);
1818                         }
1819                 }               
1820
1821                 ui_check_but(bt);
1822         }
1823 }
1824
1825 static void do_picker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1826 {
1827         uiBut *but= (uiBut *)bt1;
1828         uiPopupBlockHandle *popup= but->block->handle;
1829         PropertyRNA *prop = but->rnaprop;
1830         PointerRNA ptr = but->rnapoin;
1831         float rgb[4];
1832         
1833         if (prop) {
1834                 RNA_property_float_get_array(&ptr, prop, rgb);
1835                 ui_update_block_buts_rgb(but->block, rgb);
1836         }
1837         
1838         if(popup)
1839                 popup->menuretval= UI_RETURN_UPDATE;
1840 }
1841
1842 static void do_hsv_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1843 {
1844         uiBut *but= (uiBut *)bt1;
1845         uiPopupBlockHandle *popup= but->block->handle;
1846         float rgb[3];
1847         float *hsv= ui_block_hsv_get(but->block);
1848         
1849         hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
1850         
1851         ui_update_block_buts_rgb(but->block, rgb);
1852         
1853         if(popup)
1854                 popup->menuretval= UI_RETURN_UPDATE;
1855 }
1856
1857 static void do_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
1858 {
1859         uiBut *but= (uiBut *)bt1;
1860         uiPopupBlockHandle *popup= but->block->handle;
1861         char *hexcol= (char *)hexcl;
1862         float rgb[3];
1863         
1864         hex_to_rgb(hexcol, rgb, rgb+1, rgb+2);
1865         
1866         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1867         if (but->block->color_profile != BLI_PR_NONE) {
1868                 /* so we need to linearise it for Blender */
1869                 srgb_to_linearrgb_v3_v3(rgb, rgb);
1870         }
1871         
1872         ui_update_block_buts_rgb(but->block, rgb);
1873         
1874         if(popup)
1875                 popup->menuretval= UI_RETURN_UPDATE;
1876 }
1877
1878 static void close_popup_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1879 {
1880         uiBut *but= (uiBut *)bt1;
1881         uiPopupBlockHandle *popup= but->block->handle;
1882         
1883         if(popup)
1884                 popup->menuretval= UI_RETURN_OK;
1885 }
1886
1887 static void picker_new_hide_reveal(uiBlock *block, short colormode)
1888 {
1889         uiBut *bt;
1890         
1891         /* tag buttons */
1892         for(bt= block->buttons.first; bt; bt= bt->next) {
1893                 
1894                 if (bt->type == LABEL) {
1895                         if( bt->str[1]=='G') {
1896                                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1897                                 else bt->flag |= UI_HIDDEN;
1898                         }
1899                 }
1900                 
1901                 if(bt->type==NUMSLI || bt->type==TEX) {
1902                         if( bt->str[1]=='e') {
1903                                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1904                                 else bt->flag |= UI_HIDDEN;
1905                         }
1906                         else if( ELEM3(bt->str[0], 'R', 'G', 'B')) {
1907                                 if(colormode==0) bt->flag &= ~UI_HIDDEN;
1908                                 else bt->flag |= UI_HIDDEN;
1909                         }
1910                         else if( ELEM3(bt->str[0], 'H', 'S', 'V')) {
1911                                 if(colormode==1) bt->flag &= ~UI_HIDDEN;
1912                                 else bt->flag |= UI_HIDDEN;
1913                         }
1914                 }
1915         }
1916 }
1917
1918 static void do_picker_new_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1919 {
1920         uiBut *bt= bt1;
1921         short colormode= ui_get_but_val(bt);
1922         picker_new_hide_reveal(bt->block, colormode);
1923 }
1924
1925 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1926 #define SPICK1  150.0
1927 #define DPICK1  6.0
1928
1929 #define PICKER_H        150
1930 #define PICKER_W        150
1931 #define PICKER_SPACE    6
1932 #define PICKER_BAR              14
1933
1934 #define PICKER_TOTAL_W  (PICKER_W+PICKER_SPACE+PICKER_BAR)
1935
1936 static void circle_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop)
1937 {
1938         uiBut *bt;
1939         
1940         /* HS circle */
1941         bt= uiDefButR_prop(block, HSVCIRCLE, 0, "",     0, 0, PICKER_H, PICKER_W, ptr, prop, 0, 0.0, 0.0, 0, 0, "Color");
1942         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1943         
1944         /* value */
1945         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");
1946         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1947 }
1948
1949
1950 static void square_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int type)
1951 {
1952         uiBut *bt;
1953         int bartype = type + 3;
1954         
1955         /* HS square */
1956         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");
1957         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1958         
1959         /* value */
1960         bt= uiDefButR_prop(block, HSVCUBE, 0, "",               0, 0, PICKER_TOTAL_W, PICKER_BAR, ptr, prop, 0, 0.0, 0.0, bartype, 0, "Value");
1961         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1962 }
1963
1964
1965 /* a HS circle, V slider, rgb/hsv/hex sliders */
1966 static void uiBlockPicker(uiBlock *block, float *rgb, PointerRNA *ptr, PropertyRNA *prop)
1967 {
1968         static short colormode= 0;      /* temp? 0=rgb, 1=hsv, 2=hex */
1969         uiBut *bt;
1970         int width, butwidth;
1971         static char tip[50];
1972         static char hexcol[128];
1973         float rgb_gamma[3];
1974         float min, max, step, precision;
1975         float *hsv= ui_block_hsv_get(block);
1976         
1977         ui_block_hsv_get(block);
1978         
1979         width= PICKER_TOTAL_W;
1980         butwidth = width - UI_UNIT_X - 10;
1981         
1982         /* existence of profile means storage is in linear color space, with display correction */
1983         if (block->color_profile == BLI_PR_NONE) {
1984                 BLI_strncpy(tip, "Value in Display Color Space", sizeof(tip));
1985                 copy_v3_v3(rgb_gamma, rgb);
1986         } else {
1987                 BLI_strncpy(tip, "Value in Linear RGB Color Space", sizeof(tip));
1988                 /* make an sRGB version, for Hex code */
1989                 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1990         }
1991         
1992         /* sneaky way to check for alpha */
1993         rgb[3]= FLT_MAX;
1994
1995         RNA_property_float_ui_range(ptr, prop, &min, &max, &step, &precision);
1996         RNA_property_float_get_array(ptr, prop, rgb);
1997
1998         switch (U.color_picker_type) {
1999                 case USER_CP_CIRCLE:
2000                         circle_picker(block, ptr, prop);
2001                         break;
2002                 case USER_CP_SQUARE_SV:
2003                         square_picker(block, ptr, prop, UI_GRAD_SV);
2004                         break;
2005                 case USER_CP_SQUARE_HS:
2006                         square_picker(block, ptr, prop, UI_GRAD_HS);
2007                         break;
2008                 case USER_CP_SQUARE_HV:
2009                         square_picker(block, ptr, prop, UI_GRAD_HV);
2010                         break;
2011         }
2012         
2013         /* mode */
2014         uiBlockBeginAlign(block);
2015         bt= uiDefButS(block, ROW, 0, "RGB",     0, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 0.0, 0, 0, "");
2016         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2017         bt= uiDefButS(block, ROW, 0, "HSV",     width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 1.0, 0, 0, "");
2018         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2019         bt= uiDefButS(block, ROW, 0, "Hex",     2*width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 2.0, 0, 0, "");
2020         uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2021         uiBlockEndAlign(block);
2022
2023         bt= uiDefIconButO(block, BUT, "UI_OT_eyedropper", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, butwidth+10, -60, UI_UNIT_X, UI_UNIT_Y, NULL);
2024         uiButSetFunc(bt, close_popup_cb, bt, NULL);
2025         
2026         /* RGB values */
2027         uiBlockBeginAlign(block);
2028         bt= uiDefButR_prop(block, NUMSLI, 0, "R ",      0, -60, butwidth, UI_UNIT_Y, ptr, prop, 0, 0.0, 0.0, 0, 3, "Red");
2029         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2030         bt= uiDefButR_prop(block, NUMSLI, 0, "G ",      0, -80, butwidth, UI_UNIT_Y, ptr, prop, 1, 0.0, 0.0, 0, 3, "Green");
2031         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2032         bt= uiDefButR_prop(block, NUMSLI, 0, "B ",      0, -100, butwidth, UI_UNIT_Y, ptr, prop, 2, 0.0, 0.0, 0, 3, "Blue");
2033         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2034
2035         // could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", ICON_NONE);
2036         // but need to use uiButSetFunc for updating other fake buttons
2037         
2038         /* HSV values */
2039         uiBlockBeginAlign(block);
2040         bt= uiDefButF(block, NUMSLI, 0, "H ",   0, -60, butwidth, UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, "Hue");
2041         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2042         bt= uiDefButF(block, NUMSLI, 0, "S ",   0, -80, butwidth, UI_UNIT_Y, hsv+1, 0.0, 1.0, 10, 3, "Saturation");
2043         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2044         bt= uiDefButF(block, NUMSLI, 0, "V ",   0, -100, butwidth, UI_UNIT_Y, hsv+2, 0.0, max, 10, 3, "Value");
2045         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2046         uiBlockEndAlign(block);
2047
2048         if(rgb[3] != FLT_MAX) {
2049                 bt= uiDefButR_prop(block, NUMSLI, 0, "A ",      0, -120, butwidth, UI_UNIT_Y, ptr, prop, 3, 0.0, 0.0, 0, 0, "Alpha");
2050                 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2051         }
2052         else {
2053                 rgb[3]= 1.0f;
2054         }
2055
2056         BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
2057
2058         bt= uiDefBut(block, TEX, 0, "Hex: ", 0, -60, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
2059         uiButSetFunc(bt, do_hex_rna_cb, bt, hexcol);
2060         uiDefBut(block, LABEL, 0, "(Gamma Corrected)", 0, -80, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2061
2062         rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
2063
2064         picker_new_hide_reveal(block, colormode);
2065 }
2066
2067
2068 static int ui_picker_small_wheel_cb(const bContext *UNUSED(C), uiBlock *block, wmEvent *event)
2069 {
2070         float add= 0.0f;
2071         
2072         if(event->type==WHEELUPMOUSE)
2073                 add= 0.05f;
2074         else if(event->type==WHEELDOWNMOUSE)
2075                 add= -0.05f;
2076         
2077         if(add!=0.0f) {
2078                 uiBut *but;
2079                 
2080                 for(but= block->buttons.first; but; but= but->next) {
2081                         if(but->type==HSVCUBE && but->active==NULL) {
2082                                 uiPopupBlockHandle *popup= block->handle;
2083                                 float col[3];
2084                                 float *hsv= ui_block_hsv_get(block);
2085                                 
2086                                 ui_get_but_vectorf(but, col);
2087                                 
2088                                 rgb_to_hsv_compat(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
2089                                 hsv[2]= CLAMPIS(hsv[2]+add, 0.0f, 1.0f);
2090                                 hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
2091
2092                                 ui_set_but_vectorf(but, col);
2093                                 
2094                                 ui_update_block_buts_rgb(block, col);
2095                                 if(popup)
2096                                         popup->menuretval= UI_RETURN_UPDATE;
2097                                 
2098                                 return 1;
2099                         }
2100                 }
2101         }
2102         return 0;
2103 }
2104
2105 uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
2106 {
2107         uiBut *but= arg_but;
2108         uiBlock *block;
2109         
2110         block= uiBeginBlock(C, handle->region, "colorpicker", UI_EMBOSS);
2111         
2112         if (but->rnaprop) {
2113                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2114                         block->color_profile = BLI_PR_NONE;
2115                 }
2116         }
2117         
2118         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2119         
2120         copy_v3_v3(handle->retvec, but->editvec);
2121         
2122         uiBlockPicker(block, handle->retvec, &but->rnapoin, but->rnaprop);
2123         
2124         block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN|UI_BLOCK_OUT_1;
2125         uiBoundsBlock(block, 10);
2126         
2127         block->block_event_func= ui_picker_small_wheel_cb;
2128         
2129         /* and lets go */
2130         block->direction= UI_TOP;
2131         
2132         return block;
2133 }
2134
2135 /************************ Popup Menu Memory ****************************/
2136
2137 static int ui_popup_string_hash(char *str)
2138 {
2139         /* sometimes button contains hotkey, sometimes not, strip for proper compare */
2140         int hash;
2141         char *delimit= strchr(str, '|');
2142
2143         if(delimit) *delimit= 0;
2144         hash= BLI_ghashutil_strhash(str);
2145         if(delimit) *delimit= '|';
2146
2147         return hash;
2148 }
2149
2150 static int ui_popup_menu_hash(const char *str)
2151 {
2152         return BLI_ghashutil_strhash(str);
2153 }
2154
2155 /* but == NULL read, otherwise set */
2156 uiBut *ui_popup_menu_memory(uiBlock *block, uiBut *but)
2157 {
2158         static int mem[256], first=1;
2159         int hash= block->puphash;
2160         
2161         if(first) {
2162                 /* init */
2163                 memset(mem, -1, sizeof(mem));
2164                 first= 0;
2165         }
2166
2167         if(but) {
2168                 /* set */
2169                 mem[hash & 255 ]= ui_popup_string_hash(but->str);
2170                 return NULL;
2171         }
2172         else {
2173                 /* get */
2174                 for(but=block->buttons.first; but; but=but->next)
2175                         if(ui_popup_string_hash(but->str) == mem[hash & 255])
2176                                 return but;
2177
2178                 return NULL;
2179         }
2180 }
2181
2182 /******************** Popup Menu with callback or string **********************/
2183
2184 struct uiPopupMenu {
2185         uiBlock *block;
2186         uiLayout *layout;
2187         uiBut *but;
2188
2189         int mx, my, popup, slideout;
2190         int startx, starty, maxrow;
2191
2192         uiMenuCreateFunc menu_func;
2193         void *menu_arg;
2194 };
2195
2196 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
2197 {
2198         uiBlock *block;
2199         uiBut *bt;
2200         uiPopupMenu *pup= arg_pup;
2201         int offset[2], direction, minwidth, width, height, flip;
2202
2203         if(pup->menu_func) {
2204                 pup->block->handle= handle;
2205                 pup->menu_func(C, pup->layout, pup->menu_arg);
2206                 pup->block->handle= NULL;
2207         }
2208
2209         if(pup->but) {
2210                 /* minimum width to enforece */
2211                 minwidth= pup->but->x2 - pup->but->x1;
2212
2213                 if(pup->but->type == PULLDOWN || pup->but->menu_create_func) {
2214                         direction= UI_DOWN;
2215                         flip= 1;
2216                 }
2217                 else {
2218                         direction= UI_TOP;
2219                         flip= 0;
2220                 }
2221         }
2222         else {
2223                 minwidth= 50;
2224                 direction= UI_DOWN;
2225                 flip= 1;
2226         }
2227
2228         block= pup->block;
2229         
2230         /* in some cases we create the block before the region,
2231            so we set it delayed here if necessary */
2232         if(BLI_findindex(&handle->region->uiblocks, block) == -1)
2233                 uiBlockSetRegion(block, handle->region);
2234
2235         block->direction= direction;
2236
2237         uiBlockLayoutResolve(block, &width, &height);
2238
2239         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2240         
2241         if(pup->popup) {
2242                 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
2243                 uiBlockSetDirection(block, direction);
2244
2245                 /* offset the mouse position, possibly based on earlier selection */
2246                 if((block->flag & UI_BLOCK_POPUP_MEMORY) &&
2247                         (bt= ui_popup_menu_memory(block, NULL))) {
2248                         /* position mouse on last clicked item, at 0.8*width of the
2249                            button, so it doesn't overlap the text too much, also note
2250                            the offset is negative because we are inverse moving the
2251                            block to be under the mouse */
2252                         offset[0]= -(bt->x1 + 0.8f*(bt->x2 - bt->x1));
2253                         offset[1]= -(bt->y1 + 0.5f*UI_UNIT_Y);
2254                 }
2255                 else {
2256                         /* position mouse at 0.8*width of the button and below the tile
2257                            on the first item */
2258                         offset[0]= 0;
2259                         for(bt=block->buttons.first; bt; bt=bt->next)
2260                                 offset[0]= MIN2(offset[0], -(bt->x1 + 0.8f*(bt->x2 - bt->x1)));
2261
2262                         offset[1]= 1.5*UI_UNIT_Y;
2263                 }
2264
2265                 block->minbounds= minwidth;
2266                 uiMenuPopupBoundsBlock(block, 1, offset[0], offset[1]);
2267         }
2268         else {
2269                 /* for a header menu we set the direction automatic */
2270                 if(!pup->slideout && flip) {
2271                         ScrArea *sa= CTX_wm_area(C);
2272                         if(sa && sa->headertype==HEADERDOWN) {
2273                                 ARegion *ar= CTX_wm_region(C);
2274                                 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
2275                                         uiBlockSetDirection(block, UI_TOP);
2276                                         uiBlockFlipOrder(block);
2277                                 }
2278                         }
2279                 }
2280
2281                 block->minbounds= minwidth;
2282                 uiTextBoundsBlock(block, 50);
2283         }
2284
2285         /* if menu slides out of other menu, override direction */
2286         if(pup->slideout)
2287                 uiBlockSetDirection(block, UI_RIGHT);
2288
2289         uiEndBlock(C, block);
2290
2291         return pup->block;
2292 }
2293
2294 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg, char *str)
2295 {
2296         wmWindow *window= CTX_wm_window(C);
2297         uiStyle *style= UI_GetStyle();
2298         uiPopupBlockHandle *handle;
2299         uiPopupMenu *pup;
2300         pup= MEM_callocN(sizeof(uiPopupMenu), "menu dummy");
2301         pup->block= uiBeginBlock(C, NULL, "ui_button_menu_create", UI_EMBOSSP);
2302         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2303         pup->slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
2304         pup->but= but;
2305         uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
2306
2307         if(!but) {
2308                 /* no button to start from, means we are a popup */
2309                 pup->mx= window->eventstate->x;
2310                 pup->my= window->eventstate->y;
2311                 pup->popup= 1;
2312                 pup->block->flag |= UI_BLOCK_NO_FLIP;
2313         }
2314         /* some enums reversing is strange, currently we have no good way to
2315          * reverse some enum's but not others, so reverse all so the first menu
2316          * items are always close to the mouse cursor */
2317 #if 0
2318         else {
2319                 /* if this is an rna button then we can assume its an enum
2320                  * flipping enums is generally not good since the order can be
2321                  * important [#28786] */
2322                 if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
2323                         pup->block->flag |= UI_BLOCK_NO_FLIP;
2324                 }
2325         }
2326 #endif
2327
2328         if(str) {
2329                 /* menu is created from a string */
2330                 pup->menu_func= ui_block_func_MENUSTR;
2331                 pup->menu_arg= str;
2332         }
2333         else {
2334                 /* menu is created from a callback */
2335                 pup->menu_func= menu_func;
2336                 pup->menu_arg= arg;
2337         }
2338         
2339         handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
2340
2341         if(!but) {
2342                 handle->popup= 1;
2343
2344                 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2345                 WM_event_add_mousemove(C);
2346         }
2347         
2348         MEM_freeN(pup);
2349
2350         return handle;
2351 }
2352
2353 /******************** Popup Menu API with begin and end ***********************/
2354
2355 /* only return handler, and set optional title */
2356 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
2357 {
2358         uiStyle *style= UI_GetStyle();
2359         uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "popup menu");
2360         uiBut *but;
2361         
2362         pup->block= uiBeginBlock(C, NULL, "uiPupMenuBegin", UI_EMBOSSP);
2363         pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
2364         pup->block->puphash= ui_popup_menu_hash(title);
2365         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2366         uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
2367
2368         /* create in advance so we can let buttons point to retval already */
2369         pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2370         
2371         /* create title button */
2372         if(title && title[0]) {
2373                 char titlestr[256];
2374                 
2375                 if(icon) {
2376                         BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
2377                         uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2378                 }
2379                 else {
2380                         but= uiDefBut(pup->block, LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2381                         but->flag= UI_TEXT_LEFT;
2382                 }
2383         }
2384
2385         return pup;
2386 }
2387
2388 /* set the whole structure to work */
2389 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
2390 {
2391         wmWindow *window= CTX_wm_window(C);
2392         uiPopupBlockHandle *menu;
2393         
2394         pup->popup= 1;
2395         pup->mx= window->eventstate->x;
2396         pup->my= window->eventstate->y;
2397         
2398         menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
2399         menu->popup= 1;
2400         
2401         UI_add_popup_handlers(C, &window->modalhandlers, menu);
2402         WM_event_add_mousemove(C);
2403         
2404         MEM_freeN(pup);
2405 }
2406
2407 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
2408 {
2409         return pup->layout;
2410 }
2411
2412 /*************************** Standard Popup Menus ****************************/
2413
2414 static void operator_name_cb(bContext *C, void *arg, int retval)
2415 {
2416         const char *opname= arg;
2417
2418         if(opname && retval > 0)
2419                 WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
2420 }
2421
2422 static void operator_cb(bContext *C, void *arg, int retval)
2423 {
2424         wmOperator *op= arg;
2425         
2426         if(op && retval > 0)
2427                 WM_operator_call(C, op);
2428         else
2429                 WM_operator_free(op);
2430 }
2431
2432 static void confirm_cancel_operator(void *opv)
2433 {
2434         WM_operator_free(opv);
2435 }
2436
2437 static void vconfirm_opname(bContext *C, const char *opname, const char *title, const char *itemfmt, va_list ap)
2438 {
2439         uiPopupBlockHandle *handle;
2440         char *s, buf[512];
2441
2442         s= buf;
2443         if (title) s+= sprintf(s, "%s%%t|", title);
2444         vsnprintf(s, sizeof(buf) - (s - buf), itemfmt, ap);
2445         buf[sizeof(buf) - 1]= '\0';
2446
2447         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2448
2449         handle->popup_func= operator_name_cb;
2450         handle->popup_arg= (void *)opname;
2451 }
2452
2453 static void confirm_operator(bContext *C, wmOperator *op, const char *title, const char *item)
2454 {
2455         uiPopupBlockHandle *handle;
2456         char *s, buf[512];
2457         
2458         s= buf;
2459         if (title) s+= sprintf(s, "%s%%t|%s", title, item);
2460         (void)s;
2461         
2462         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2463
2464         handle->popup_func= operator_cb;
2465         handle->popup_arg= op;
2466         handle->cancel_func= confirm_cancel_operator;
2467 }
2468
2469 void uiPupMenuOkee(bContext *C, const char *opname, const char *str, ...)
2470 {
2471         va_list ap;
2472         char titlestr[256];
2473
2474         BLI_snprintf(titlestr, sizeof(titlestr), "OK? %%i%d", ICON_QUESTION);
2475
2476         va_start(ap, str);
2477         vconfirm_opname(C, opname, titlestr, str, ap);
2478         va_end(ap);
2479 }
2480
2481 void uiPupMenuSaveOver(bContext *C, wmOperator *op, const char *filename)
2482 {
2483         size_t len= strlen(filename);
2484
2485         if(len==0)
2486                 return;
2487
2488         if(filename[len-1]=='/' || filename[len-1]=='\\') {
2489                 uiPupMenuError(C, "Cannot overwrite a directory");
2490                 WM_operator_free(op);
2491                 return;
2492         }
2493         if(BLI_exists(filename)==0)
2494                 operator_cb(C, op, 1);
2495         else
2496                 confirm_operator(C, op, "Save Over", filename);
2497 }
2498
2499 void uiPupMenuNotice(bContext *C, const char *str, ...)
2500 {
2501         va_list ap;
2502
2503         va_start(ap, str);
2504         vconfirm_opname(C, NULL, NULL, str, ap);
2505         va_end(ap);
2506 }
2507
2508 void uiPupMenuError(bContext *C, const char *str, ...)
2509 {
2510         va_list ap;
2511         char nfmt[256];
2512         char titlestr[256];
2513
2514         BLI_snprintf(titlestr, sizeof(titlestr), "Error %%i%d", ICON_ERROR);
2515
2516         BLI_strncpy(nfmt, str, sizeof(nfmt));
2517
2518         va_start(ap, str);
2519         vconfirm_opname(C, NULL, titlestr, nfmt, ap);
2520         va_end(ap);
2521 }
2522
2523 void uiPupMenuReports(bContext *C, ReportList *reports)
2524 {
2525         Report *report;
2526         DynStr *ds;
2527         char *str;
2528
2529         if(!reports || !reports->list.first)
2530                 return;
2531         if(!CTX_wm_window(C))
2532                 return;
2533
2534         ds= BLI_dynstr_new();
2535
2536         for(report=reports->list.first; report; report=report->next) {
2537                 if(report->type < reports->printlevel)
2538                         ; /* pass */
2539                 else if(report->type >= RPT_ERROR)
2540                         BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
2541                 else if(report->type >= RPT_WARNING)
2542                         BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
2543                 else if(report->type >= RPT_INFO)
2544                         BLI_dynstr_appendf(ds, "Info %%i%d%%t|%s", ICON_INFO, report->message);
2545         }
2546
2547         str= BLI_dynstr_get_cstring(ds);
2548         if(str[0] != '\0')
2549                 ui_popup_menu_create(C, NULL, NULL, NULL, NULL, str);
2550         MEM_freeN(str);
2551
2552         BLI_dynstr_free(ds);
2553 }
2554
2555 void uiPupMenuInvoke(bContext *C, const char *idname)
2556 {
2557         uiPopupMenu *pup;
2558         uiLayout *layout;
2559         Menu menu;
2560         MenuType *mt= WM_menutype_find(idname, TRUE);
2561
2562         if(mt==NULL) {
2563                 printf("%s: named menu \"%s\" not found\n", __func__, idname);
2564                 return;
2565         }
2566
2567         if(mt->poll && mt->poll(C, mt)==0)
2568                 return;
2569
2570         pup= uiPupMenuBegin(C, mt->label, ICON_NONE);
2571         layout= uiPupMenuLayout(pup);
2572
2573         menu.layout= layout;
2574         menu.type= mt;
2575
2576         mt->draw(C, &menu);
2577
2578         uiPupMenuEnd(C, pup);
2579 }
2580
2581
2582 /*************************** Popup Block API **************************/
2583
2584 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext)
2585 {
2586         wmWindow *window= CTX_wm_window(C);
2587         uiPopupBlockHandle *handle;
2588         
2589         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2590         handle->popup= 1;
2591         handle->optype= (opname)? WM_operatortype_find(opname, 0): NULL;
2592         handle->opcontext= opcontext;
2593         
2594         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2595         WM_event_add_mousemove(C);
2596 }
2597
2598 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2599 {
2600         uiPupBlockO(C, func, arg, NULL, 0);
2601 }
2602
2603 void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockCancelFunc cancel_func, void *arg)
2604 {
2605         wmWindow *window= CTX_wm_window(C);
2606         uiPopupBlockHandle *handle;
2607         
2608         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2609         handle->popup= 1;
2610         handle->retvalue= 1;
2611
2612         handle->popup_arg= arg;
2613         // handle->popup_func= operator_cb;
2614         handle->cancel_func= cancel_func;
2615         // handle->opcontext= opcontext;
2616         
2617         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2618         WM_event_add_mousemove(C);
2619 }
2620
2621 #if 0 /* UNUSED */
2622 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2623 {
2624         wmWindow *window= CTX_wm_window(C);
2625         uiPopupBlockHandle *handle;
2626         
2627         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2628         handle->popup= 1;
2629         handle->retvalue= 1;
2630
2631         handle->popup_arg= op;
2632         handle->popup_func= operator_cb;
2633         handle->cancel_func= confirm_cancel_operator;
2634         handle->opcontext= opcontext;
2635         
2636         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2637         WM_event_add_mousemove(C);
2638 }
2639 #endif
2640
2641 void uiPupBlockClose(bContext *C, uiBlock *block)
2642 {
2643         if(block->handle) {
2644                 UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, block->handle);
2645                 ui_popup_block_free(C, block->handle);
2646         }
2647 }
2648
2649 float *ui_block_hsv_get(uiBlock *block)
2650 {
2651         return block->_hsv;
2652 }