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