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