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