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