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