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