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