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