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