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