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