NLA SoC: Merge from 2.5
[blender.git] / source / blender / editors / interface / interface_regions.c
1 /**
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 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_arithb.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_dynstr.h"
41
42 #include "BKE_context.h"
43 #include "BKE_icons.h"
44 #include "BKE_report.h"
45 #include "BKE_screen.h"
46 #include "BKE_texture.h"
47 #include "BKE_utildefines.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51 #include "wm_draw.h"
52 #include "wm_subwindow.h"
53 #include "wm_window.h"
54
55 #include "RNA_access.h"
56
57 #include "BIF_gl.h"
58
59 #include "UI_interface.h"
60 #include "UI_interface_icons.h"
61 #include "UI_view2d.h"
62
63 #include "BLF_api.h"
64
65 #include "ED_screen.h"
66
67 #include "interface_intern.h"
68
69 #define MENU_BUTTON_HEIGHT      20
70 #define MENU_SEPR_HEIGHT        6
71 #define B_NOP                   -1
72 #define MENU_SHADOW_SIDE        8
73 #define MENU_SHADOW_BOTTOM      10
74 #define MENU_TOP                        8
75
76 /*********************** Menu Data Parsing ********************* */
77
78 typedef struct {
79         char *str;
80         int retval;
81         int icon;
82 } MenuEntry;
83
84 typedef struct {
85         char *instr;
86         char *title;
87         int titleicon;
88         
89         MenuEntry *items;
90         int nitems, itemssize;
91 } MenuData;
92
93 static MenuData *menudata_new(char *instr)
94 {
95         MenuData *md= MEM_mallocN(sizeof(*md), "MenuData");
96
97         md->instr= instr;
98         md->title= NULL;
99         md->titleicon= 0;
100         md->items= NULL;
101         md->nitems= md->itemssize= 0;
102         
103         return md;
104 }
105
106 static void menudata_set_title(MenuData *md, char *title, int titleicon)
107 {
108         if (!md->title)
109                 md->title= title;
110         if (!md->titleicon)
111                 md->titleicon= titleicon;
112 }
113
114 static void menudata_add_item(MenuData *md, char *str, int retval, int icon)
115 {
116         if (md->nitems==md->itemssize) {
117                 int nsize= md->itemssize?(md->itemssize<<1):1;
118                 MenuEntry *oitems= md->items;
119                 
120                 md->items= MEM_mallocN(nsize*sizeof(*md->items), "md->items");
121                 if (oitems) {
122                         memcpy(md->items, oitems, md->nitems*sizeof(*md->items));
123                         MEM_freeN(oitems);
124                 }
125                 
126                 md->itemssize= nsize;
127         }
128         
129         md->items[md->nitems].str= str;
130         md->items[md->nitems].retval= retval;
131         md->items[md->nitems].icon= icon;
132         md->nitems++;
133 }
134
135 void menudata_free(MenuData *md)
136 {
137         MEM_freeN(md->instr);
138         if (md->items)
139                 MEM_freeN(md->items);
140         MEM_freeN(md);
141 }
142
143         /**
144          * Parse menu description strings, string is of the
145          * form "[sss%t|]{(sss[%xNN]|), (%l|)}", ssss%t indicates the
146          * menu title, sss or sss%xNN indicates an option, 
147          * if %xNN is given then NN is the return value if
148          * that option is selected otherwise the return value
149          * is the index of the option (starting with 1). %l
150          * indicates a seperator.
151          * 
152          * @param str String to be parsed.
153          * @retval new menudata structure, free with menudata_free()
154          */
155 MenuData *decompose_menu_string(char *str) 
156 {
157         char *instr= BLI_strdup(str);
158         MenuData *md= menudata_new(instr);
159         char *nitem= NULL, *s= instr;
160         int nicon=0, nretval= 1, nitem_is_title= 0;
161         
162         while (1) {
163                 char c= *s;
164
165                 if (c=='%') {
166                         if (s[1]=='x') {
167                                 nretval= atoi(s+2);
168
169                                 *s= '\0';
170                                 s++;
171                         } else if (s[1]=='t') {
172                                 nitem_is_title= 1;
173
174                                 *s= '\0';
175                                 s++;
176                         } else if (s[1]=='l') {
177                                 nitem= "%l";
178                                 s++;
179                         } else if (s[1]=='i') {
180                                 nicon= atoi(s+2);
181                                 
182                                 *s= '\0';
183                                 s++;
184                         }
185                 } else if (c=='|' || c == '\n' || c=='\0') {
186                         if (nitem) {
187                                 *s= '\0';
188
189                                 if (nitem_is_title) {
190                                         menudata_set_title(md, nitem, nicon);
191                                         nitem_is_title= 0;
192                                 } else {
193                                         /* prevent separator to get a value */
194                                         if(nitem[0]=='%' && nitem[1]=='l')
195                                                 menudata_add_item(md, nitem, -1, nicon);
196                                         else
197                                                 menudata_add_item(md, nitem, nretval, nicon);
198                                         nretval= md->nitems+1;
199                                 } 
200                                 
201                                 nitem= NULL;
202                                 nicon= 0;
203                         }
204                         
205                         if (c=='\0')
206                                 break;
207                 } else if (!nitem)
208                         nitem= s;
209                 
210                 s++;
211         }
212         
213         return md;
214 }
215
216 void ui_set_name_menu(uiBut *but, int value)
217 {
218         MenuData *md;
219         int i;
220         
221         md= decompose_menu_string(but->str);
222         for (i=0; i<md->nitems; i++)
223                 if (md->items[i].retval==value)
224                         strcpy(but->drawstr, md->items[i].str);
225         
226         menudata_free(md);
227 }
228
229 int ui_step_name_menu(uiBut *but, int step)
230 {
231         MenuData *md;
232         int value= ui_get_but_val(but);
233         int i;
234         
235         md= decompose_menu_string(but->str);
236         for (i=0; i<md->nitems; i++)
237                 if (md->items[i].retval==value)
238                         break;
239         
240         if(step==1) {
241                 /* skip separators */
242                 for(; i<md->nitems-1; i++) {
243                         if(md->items[i+1].retval != -1) {
244                                 value= md->items[i+1].retval;
245                                 break;
246                         }
247                 }
248         }
249         else {
250                 if(i>0) {
251                         /* skip separators */
252                         for(; i>0; i--) {
253                                 if(md->items[i-1].retval != -1) {
254                                         value= md->items[i-1].retval;
255                                         break;
256                                 }
257                         }
258                 }
259         }
260         
261         menudata_free(md);
262                 
263         return value;
264 }
265
266
267 /******************** Creating Temporary regions ******************/
268
269 ARegion *ui_add_temporary_region(bScreen *sc)
270 {
271         ARegion *ar;
272
273         ar= MEM_callocN(sizeof(ARegion), "area region");
274         BLI_addtail(&sc->regionbase, ar);
275
276         ar->regiontype= RGN_TYPE_TEMPORARY;
277         ar->alignment= RGN_ALIGN_FLOAT;
278
279         return ar;
280 }
281
282 void ui_remove_temporary_region(bContext *C, bScreen *sc, ARegion *ar)
283 {
284         if(CTX_wm_window(C))
285                 wm_draw_region_clear(CTX_wm_window(C), ar);
286
287         ED_region_exit(C, ar);
288         BKE_area_region_free(NULL, ar);         /* NULL: no spacetype */
289         BLI_freelinkN(&sc->regionbase, ar);
290 }
291
292 /************************* Creating Tooltips **********************/
293
294 typedef struct uiTooltipData {
295         rcti bbox;
296         uiFontStyle fstyle;
297         char *tip;
298 } uiTooltipData;
299
300 static void ui_tooltip_region_draw(const bContext *C, ARegion *ar)
301 {
302         uiTooltipData *data= ar->regiondata;
303         
304         ui_draw_menu_back(U.uistyles.first, NULL, &data->bbox);
305         
306         /* draw text */
307         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
308         uiStyleFontSet(&data->fstyle);
309         uiStyleFontDraw(&data->fstyle, &data->bbox, data->tip);
310 }
311
312 static void ui_tooltip_region_free(ARegion *ar)
313 {
314         uiTooltipData *data;
315
316         data= ar->regiondata;
317         MEM_freeN(data->tip);
318         MEM_freeN(data);
319         ar->regiondata= NULL;
320 }
321
322 ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
323 {
324         uiStyle *style= U.uistyles.first;       // XXX pass on as arg
325         static ARegionType type;
326         ARegion *ar;
327         uiTooltipData *data;
328         float fonth, fontw, aspect= but->block->aspect;
329         float x1f, x2f, y1f, y2f;
330         int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
331
332         if(!but->tip || strlen(but->tip)==0)
333                 return NULL;
334
335         /* create area region */
336         ar= ui_add_temporary_region(CTX_wm_screen(C));
337
338         memset(&type, 0, sizeof(ARegionType));
339         type.draw= ui_tooltip_region_draw;
340         type.free= ui_tooltip_region_free;
341         ar->type= &type;
342
343         /* create tooltip data */
344         data= MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
345         data->tip= BLI_strdup(but->tip);
346         
347         /* set font, get bb */
348         data->fstyle= style->widget; /* copy struct */
349         data->fstyle.align= UI_STYLE_TEXT_CENTER;
350         ui_fontscale(&data->fstyle.points, aspect);
351         uiStyleFontSet(&data->fstyle);
352         fontw= aspect * BLF_width(data->tip);
353         fonth= aspect * BLF_height(data->tip);
354
355         ar->regiondata= data;
356
357         /* compute position */
358         ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
359         ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
360
361         x1f= (but->x1+but->x2)/2.0f + ofsx - 16.0f*aspect;
362         x2f= x1f + fontw + 16.0f*aspect;
363         y2f= but->y1 + ofsy - 15.0f*aspect;
364         y1f= y2f - fonth - 10.0f*aspect;
365         
366         /* copy to int, gets projected if possible too */
367         x1= x1f; y1= y1f; x2= x2f; y2= y2f; 
368         
369         if(butregion) {
370                 /* XXX temp, region v2ds can be empty still */
371                 if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
372                         UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
373                         UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
374                 }
375
376                 x1 += butregion->winrct.xmin;
377                 x2 += butregion->winrct.xmin;
378                 y1 += butregion->winrct.ymin;
379                 y2 += butregion->winrct.ymin;
380         }
381
382         wm_window_get_size(CTX_wm_window(C), &winx, &winy);
383
384         if(x2 > winx) {
385                 /* super size */
386                 if(x2 > winx + x1) {
387                         x2= winx;
388                         x1= 0;
389                 }
390                 else {
391                         x1 -= x2-winx;
392                         x2= winx;
393                 }
394         }
395         if(y1 < 0) {
396                 y1 += 56*aspect;
397                 y2 += 56*aspect;
398         }
399
400         /* widget rect, in region coords */
401         data->bbox.xmin= MENU_SHADOW_SIDE;
402         data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
403         data->bbox.ymin= MENU_SHADOW_BOTTOM;
404         data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
405         
406         /* region bigger for shadow */
407         ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
408         ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
409         ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
410         ar->winrct.ymax= y2 + MENU_TOP;
411
412         /* adds subwindow */
413         ED_region_init(C, ar);
414         
415         /* notify change and redraw */
416         ED_region_tag_redraw(ar);
417
418         return ar;
419 }
420
421 void ui_tooltip_free(bContext *C, ARegion *ar)
422 {
423         ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
424 }
425
426
427 /************************* Creating Search Box **********************/
428
429 struct uiSearchItems {
430         int maxitem, totitem, maxstrlen;
431         
432         int offset, offset_i; /* offset for inserting in array */
433         int more;  /* flag indicating there are more items */
434         
435         char **names;
436         void **pointers;
437         int *icons;
438
439         AutoComplete *autocpl;
440         void *active;
441 };
442
443 typedef struct uiSearchboxData {
444         rcti bbox;
445         uiFontStyle fstyle;
446         uiSearchItems items;
447         int active;             /* index in items array */
448         int noback;             /* when menu opened with enough space for this */
449 } uiSearchboxData;
450
451 #define SEARCH_ITEMS    10
452
453 /* exported for use by search callbacks */
454 /* returns zero if nothing to add */
455 int uiSearchItemAdd(uiSearchItems *items, const char *name, void *poin, int iconid)
456 {
457         /* hijack for autocomplete */
458         if(items->autocpl) {
459                 autocomplete_do_name(items->autocpl, name);
460                 return 1;
461         }
462         
463         /* hijack for finding active item */
464         if(items->active) {
465                 if(poin==items->active)
466                         items->offset_i= items->totitem;
467                 items->totitem++;
468                 return 1;
469         }
470         
471         if(items->totitem>=items->maxitem) {
472                 items->more= 1;
473                 return 0;
474         }
475         
476         /* skip first items in list */
477         if(items->offset_i > 0) {
478                 items->offset_i--;
479                 return 1;
480         }
481         
482         BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
483         items->pointers[items->totitem]= poin;
484         items->icons[items->totitem]= iconid;
485         
486         items->totitem++;
487         
488         return 1;
489 }
490
491 int uiSearchBoxhHeight(void)
492 {
493         return SEARCH_ITEMS*MENU_BUTTON_HEIGHT + 2*MENU_TOP;
494 }
495
496 /* ar is the search box itself */
497 static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
498 {
499         uiSearchboxData *data= ar->regiondata;
500         
501         /* apply step */
502         data->active+= step;
503         
504         if(data->items.totitem==0)
505                 data->active= 0;
506         else if(data->active > data->items.totitem) {
507                 if(data->items.more) {
508                         data->items.offset++;
509                         data->active= data->items.totitem;
510                         ui_searchbox_update(C, ar, but, 0);
511                 }
512                 else
513                         data->active= data->items.totitem;
514         }
515         else if(data->active < 1) {
516                 if(data->items.offset) {
517                         data->items.offset--;
518                         data->active= 1;
519                         ui_searchbox_update(C, ar, but, 0);
520                 }
521                 else if(data->active < 0)
522                         data->active= 0;
523         }
524         
525         ED_region_tag_redraw(ar);
526 }
527
528 static void ui_searchbox_butrect(rcti *rect, uiSearchboxData *data, int itemnr)
529 {
530         int buth= (data->bbox.ymax-data->bbox.ymin - 2*MENU_TOP)/SEARCH_ITEMS;
531         
532         *rect= data->bbox;
533         rect->xmin= data->bbox.xmin + 3.0f;
534         rect->xmax= data->bbox.xmax - 3.0f;
535         
536         rect->ymax= data->bbox.ymax - MENU_TOP - itemnr*buth;
537         rect->ymin= rect->ymax - buth;
538         
539 }
540
541 /* x and y in screencoords */
542 int ui_searchbox_inside(ARegion *ar, int x, int y)
543 {
544         uiSearchboxData *data= ar->regiondata;
545         
546         return(BLI_in_rcti(&data->bbox, x-ar->winrct.xmin, y-ar->winrct.ymin));
547 }
548
549 /* string validated to be of correct length (but->hardmax) */
550 void ui_searchbox_apply(uiBut *but, ARegion *ar)
551 {
552         uiSearchboxData *data= ar->regiondata;
553
554         but->func_arg2= NULL;
555         
556         if(data->active) {
557                 char *name= data->items.names[data->active-1];
558                 char *cpoin= strchr(name, '|');
559                 
560                 if(cpoin) cpoin[0]= 0;
561                 BLI_strncpy(but->editstr, name, data->items.maxstrlen);
562                 if(cpoin) cpoin[0]= '|';
563                 
564                 but->func_arg2= data->items.pointers[data->active-1];
565         }
566 }
567
568 void ui_searchbox_event(bContext *C, ARegion *ar, uiBut *but, wmEvent *event)
569 {
570         uiSearchboxData *data= ar->regiondata;
571         
572         switch(event->type) {
573                 case WHEELUPMOUSE:
574                 case UPARROWKEY:
575                         ui_searchbox_select(C, ar, but, -1);
576                         break;
577                 case WHEELDOWNMOUSE:
578                 case DOWNARROWKEY:
579                         ui_searchbox_select(C, ar, but, 1);
580                         break;
581                 case MOUSEMOVE:
582                         if(BLI_in_rcti(&ar->winrct, event->x, event->y)) {
583                                 rcti rect;
584                                 int a;
585                                 
586                                 for(a=0; a<data->items.totitem; a++) {
587                                         ui_searchbox_butrect(&rect, data, a);
588                                         if(BLI_in_rcti(&rect, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
589                                                 if( data->active!= a+1) {
590                                                         data->active= a+1;
591                                                         ui_searchbox_select(C, ar, but, 0);
592                                                         break;
593                                                 }
594                                         }
595                                 }
596                         }
597                         break;
598         }
599 }
600
601 /* ar is the search box itself */
602 void ui_searchbox_update(bContext *C, ARegion *ar, uiBut *but, int reset)
603 {
604         uiSearchboxData *data= ar->regiondata;
605         
606         /* reset vars */
607         data->items.totitem= 0;
608         data->items.more= 0;
609         if(reset==0) {
610                 data->items.offset_i= data->items.offset;
611         }
612         else {
613                 data->items.offset_i= data->items.offset= 0;
614                 data->active= 0;
615                 
616                 /* handle active */
617                 if(but->search_func && but->func_arg2) {
618                         data->items.active= but->func_arg2;
619                         but->search_func(C, but->search_arg, but->editstr, &data->items);
620                         data->items.active= NULL;
621                         
622                         /* found active item, calculate real offset by centering it */
623                         if(data->items.totitem) {
624                                 /* first case, begin of list */
625                                 if(data->items.offset_i < data->items.maxitem) {
626                                         data->active= data->items.offset_i+1;
627                                         data->items.offset_i= 0;
628                                 }
629                                 else {
630                                         /* second case, end of list */
631                                         if(data->items.totitem - data->items.offset_i <= data->items.maxitem) {
632                                                 data->active= 1 + data->items.offset_i - data->items.totitem + data->items.maxitem;
633                                                 data->items.offset_i= data->items.totitem - data->items.maxitem;
634                                         }
635                                         else {
636                                                 /* center active item */
637                                                 data->items.offset_i -= data->items.maxitem/2;
638                                                 data->active= 1 + data->items.maxitem/2;
639                                         }
640                                 }
641                         }
642                         data->items.offset= data->items.offset_i;
643                         data->items.totitem= 0;
644                 }
645         }
646         
647         /* callback */
648         if(but->search_func)
649                 but->search_func(C, but->search_arg, but->editstr, &data->items);
650         
651         /* handle case where editstr is equal to one of items */
652         if(reset && data->active==0) {
653                 int a;
654                 
655                 for(a=0; a<data->items.totitem; a++) {
656                         char *cpoin= strchr(data->items.names[a], '|');
657                         
658                         if(cpoin) cpoin[0]= 0;
659                         if(0==strcmp(but->editstr, data->items.names[a]))
660                                 data->active= a+1;
661                         if(cpoin) cpoin[0]= '|';
662                 }
663                 if(data->items.totitem==1)
664                         data->active= 1;
665         }
666
667         /* validate selected item */
668         ui_searchbox_select(C, ar, but, 0);
669         
670         ED_region_tag_redraw(ar);
671 }
672
673 void ui_searchbox_autocomplete(bContext *C, ARegion *ar, uiBut *but, char *str)
674 {
675         uiSearchboxData *data= ar->regiondata;
676
677         data->items.autocpl= autocomplete_begin(str, ui_get_but_string_max_length(but));
678
679         but->search_func(C, but->search_arg, but->editstr, &data->items);
680
681         autocomplete_end(data->items.autocpl, str);
682         data->items.autocpl= NULL;
683 }
684
685 static void ui_searchbox_region_draw(const bContext *C, ARegion *ar)
686 {
687         uiSearchboxData *data= ar->regiondata;
688         
689         /* pixel space */
690         wmOrtho2(-0.01f, ar->winx-0.01f, -0.01f, ar->winy-0.01f);
691
692         if(!data->noback)
693                 ui_draw_search_back(U.uistyles.first, NULL, &data->bbox);
694         
695         /* draw text */
696         if(data->items.totitem) {
697                 rcti rect;
698                 int a;
699                                 
700                 /* draw items */
701                 for(a=0; a<data->items.totitem; a++) {
702                         ui_searchbox_butrect(&rect, data, a);
703                         
704                         /* widget itself */
705                         ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
706                         
707                 }
708                 /* indicate more */
709                 if(data->items.more) {
710                         ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
711                         glEnable(GL_BLEND);
712                         UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymin-9, ICON_TRIA_DOWN);
713                         glDisable(GL_BLEND);
714                 }
715                 if(data->items.offset) {
716                         ui_searchbox_butrect(&rect, data, 0);
717                         glEnable(GL_BLEND);
718                         UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymax-7, ICON_TRIA_UP);
719                         glDisable(GL_BLEND);
720                 }
721         }
722 }
723
724 static void ui_searchbox_region_free(ARegion *ar)
725 {
726         uiSearchboxData *data= ar->regiondata;
727         int a;
728
729         /* free search data */
730         for(a=0; a<SEARCH_ITEMS; a++)
731                 MEM_freeN(data->items.names[a]);
732         MEM_freeN(data->items.names);
733         MEM_freeN(data->items.pointers);
734         MEM_freeN(data->items.icons);
735         
736         MEM_freeN(data);
737         ar->regiondata= NULL;
738 }
739
740 ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but)
741 {
742         uiStyle *style= U.uistyles.first;       // XXX pass on as arg
743         static ARegionType type;
744         ARegion *ar;
745         uiSearchboxData *data;
746         float aspect= but->block->aspect;
747         float x1f, x2f, y1f, y2f;
748         int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
749         
750         /* create area region */
751         ar= ui_add_temporary_region(CTX_wm_screen(C));
752         
753         memset(&type, 0, sizeof(ARegionType));
754         type.draw= ui_searchbox_region_draw;
755         type.free= ui_searchbox_region_free;
756         ar->type= &type;
757         
758         /* create searchbox data */
759         data= MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
760         
761         /* set font, get bb */
762         data->fstyle= style->widget; /* copy struct */
763         data->fstyle.align= UI_STYLE_TEXT_CENTER;
764         ui_fontscale(&data->fstyle.points, aspect);
765         uiStyleFontSet(&data->fstyle);
766         
767         ar->regiondata= data;
768         
769         /* special case, hardcoded feature, not draw backdrop when called from menus,
770            assume for design that popup already added it */
771         if(but->block->flag & UI_BLOCK_LOOP)
772                 data->noback= 1;
773         
774         /* compute position */
775         
776         if(but->block->flag & UI_BLOCK_LOOP) {
777                 /* this case is search menu inside other menu */
778                 /* we copy region size */
779
780                 ar->winrct= butregion->winrct;
781                 
782                 /* widget rect, in region coords */
783                 data->bbox.xmin= MENU_SHADOW_SIDE;
784                 data->bbox.xmax= (ar->winrct.xmax-ar->winrct.xmin) - MENU_SHADOW_SIDE;
785                 data->bbox.ymin= MENU_SHADOW_BOTTOM;
786                 data->bbox.ymax= (ar->winrct.ymax-ar->winrct.ymin) - MENU_SHADOW_BOTTOM;
787                 
788                 /* check if button is lower half */
789                 if( but->y2 < (but->block->minx+but->block->maxx)/2 ) {
790                         data->bbox.ymin += (but->y2-but->y1);
791                 }
792                 else {
793                         data->bbox.ymax -= (but->y2-but->y1);
794                 }
795         }
796         else {
797                 x1f= but->x1 - 5;       /* align text with button */
798                 x2f= but->x2 + 5;       /* symmetrical */
799                 y2f= but->y1;
800                 y1f= y2f - uiSearchBoxhHeight();
801
802                 ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
803                 ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
804
805                 x1f += ofsx;
806                 x2f += ofsx;
807                 y1f += ofsy;
808                 y2f += ofsy;
809         
810                 /* minimal width */
811                 if(x2f - x1f < 150) x2f= x1f+150; // XXX arbitrary
812                 
813                 /* copy to int, gets projected if possible too */
814                 x1= x1f; y1= y1f; x2= x2f; y2= y2f; 
815                 
816                 if(butregion) {
817                         if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
818                                 UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
819                                 UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
820                         }
821                         
822                         x1 += butregion->winrct.xmin;
823                         x2 += butregion->winrct.xmin;
824                         y1 += butregion->winrct.ymin;
825                         y2 += butregion->winrct.ymin;
826                 }
827                 
828                 wm_window_get_size(CTX_wm_window(C), &winx, &winy);
829                 
830                 if(x2 > winx) {
831                         /* super size */
832                         if(x2 > winx + x1) {
833                                 x2= winx;
834                                 x1= 0;
835                         }
836                         else {
837                                 x1 -= x2-winx;
838                                 x2= winx;
839                         }
840                 }
841                 if(y1 < 0) {
842                         y1 += 36;
843                         y2 += 36;
844                 }
845                 
846                 /* widget rect, in region coords */
847                 data->bbox.xmin= MENU_SHADOW_SIDE;
848                 data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
849                 data->bbox.ymin= MENU_SHADOW_BOTTOM;
850                 data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
851                 
852                 /* region bigger for shadow */
853                 ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
854                 ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
855                 ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
856                 ar->winrct.ymax= y2;
857         }
858         
859         /* adds subwindow */
860         ED_region_init(C, ar);
861         
862         /* notify change and redraw */
863         ED_region_tag_redraw(ar);
864         
865         /* prepare search data */
866         data->items.maxitem= SEARCH_ITEMS;
867         data->items.maxstrlen= but->hardmax;
868         data->items.totitem= 0;
869         data->items.names= MEM_callocN(SEARCH_ITEMS*sizeof(void *), "search names");
870         data->items.pointers= MEM_callocN(SEARCH_ITEMS*sizeof(void *), "search pointers");
871         data->items.icons= MEM_callocN(SEARCH_ITEMS*sizeof(int), "search icons");
872         for(x1=0; x1<SEARCH_ITEMS; x1++)
873                 data->items.names[x1]= MEM_callocN(but->hardmax+1, "search pointers");
874         
875         return ar;
876 }
877
878 void ui_searchbox_free(bContext *C, ARegion *ar)
879 {
880         ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
881 }
882
883
884 /************************* Creating Menu Blocks **********************/
885
886 /* position block relative to but, result is in window space */
887 static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
888 {
889         uiBut *bt;
890         uiSafetyRct *saferct;
891         rctf butrct;
892         float aspect;
893         int xsize, ysize, xof=0, yof=0, center;
894         short dir1= 0, dir2=0;
895         
896         /* transform to window coordinates, using the source button region/block */
897         butrct.xmin= but->x1; butrct.xmax= but->x2;
898         butrct.ymin= but->y1; butrct.ymax= but->y2;
899
900         ui_block_to_window_fl(butregion, but->block, &butrct.xmin, &butrct.ymin);
901         ui_block_to_window_fl(butregion, but->block, &butrct.xmax, &butrct.ymax);
902
903         /* calc block rect */
904         if(block->minx == 0.0f && block->maxx == 0.0f) {
905                 if(block->buttons.first) {
906                         block->minx= block->miny= 10000;
907                         block->maxx= block->maxy= -10000;
908                         
909                         bt= block->buttons.first;
910                         while(bt) {
911                                 if(bt->x1 < block->minx) block->minx= bt->x1;
912                                 if(bt->y1 < block->miny) block->miny= bt->y1;
913
914                                 if(bt->x2 > block->maxx) block->maxx= bt->x2;
915                                 if(bt->y2 > block->maxy) block->maxy= bt->y2;
916                                 
917                                 bt= bt->next;
918                         }
919                 }
920                 else {
921                         /* we're nice and allow empty blocks too */
922                         block->minx= block->miny= 0;
923                         block->maxx= block->maxy= 20;
924                 }
925         }
926         
927         aspect= (float)(block->maxx - block->minx + 4);
928         ui_block_to_window_fl(butregion, but->block, &block->minx, &block->miny);
929         ui_block_to_window_fl(butregion, but->block, &block->maxx, &block->maxy);
930
931         //block->minx-= 2.0; block->miny-= 2.0;
932         //block->maxx+= 2.0; block->maxy+= 2.0;
933         
934         xsize= block->maxx - block->minx+4; // 4 for shadow
935         ysize= block->maxy - block->miny+4;
936         aspect/= (float)xsize;
937
938         if(but) {
939                 int left=0, right=0, top=0, down=0;
940                 int winx, winy;
941
942                 wm_window_get_size(window, &winx, &winy);
943
944                 if(block->direction & UI_CENTER) center= ysize/2;
945                 else center= 0;
946
947                 if( butrct.xmin-xsize > 0.0) left= 1;
948                 if( butrct.xmax+xsize < winx) right= 1;
949                 if( butrct.ymin-ysize+center > 0.0) down= 1;
950                 if( butrct.ymax+ysize-center < winy) top= 1;
951                 
952                 dir1= block->direction & UI_DIRECTION;
953
954                 /* secundary directions */
955                 if(dir1 & (UI_TOP|UI_DOWN)) {
956                         if(dir1 & UI_LEFT) dir2= UI_LEFT;
957                         else if(dir1 & UI_RIGHT) dir2= UI_RIGHT;
958                         dir1 &= (UI_TOP|UI_DOWN);
959                 }
960
961                 if(dir2==0) if(dir1==UI_LEFT || dir1==UI_RIGHT) dir2= UI_DOWN;
962                 if(dir2==0) if(dir1==UI_TOP || dir1==UI_DOWN) dir2= UI_LEFT;
963                 
964                 /* no space at all? dont change */
965                 if(left || right) {
966                         if(dir1==UI_LEFT && left==0) dir1= UI_RIGHT;
967                         if(dir1==UI_RIGHT && right==0) dir1= UI_LEFT;
968                         /* this is aligning, not append! */
969                         if(dir2==UI_LEFT && right==0) dir2= UI_RIGHT;
970                         if(dir2==UI_RIGHT && left==0) dir2= UI_LEFT;
971                 }
972                 if(down || top) {
973                         if(dir1==UI_TOP && top==0) dir1= UI_DOWN;
974                         if(dir1==UI_DOWN && down==0) dir1= UI_TOP;
975                         if(dir2==UI_TOP && top==0) dir2= UI_DOWN;
976                         if(dir2==UI_DOWN && down==0) dir2= UI_TOP;
977                 }
978                 
979                 if(dir1==UI_LEFT) {
980                         xof= butrct.xmin - block->maxx;
981                         if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
982                         else yof= butrct.ymax - block->maxy+center;
983                 }
984                 else if(dir1==UI_RIGHT) {
985                         xof= butrct.xmax - block->minx;
986                         if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
987                         else yof= butrct.ymax - block->maxy+center;
988                 }
989                 else if(dir1==UI_TOP) {
990                         yof= butrct.ymax - block->miny;
991                         if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
992                         else xof= butrct.xmin - block->minx;
993                         // changed direction? 
994                         if((dir1 & block->direction)==0) {
995                                 if(block->direction & UI_SHIFT_FLIPPED)
996                                         xof+= dir2==UI_LEFT?25:-25;
997                                 uiBlockFlipOrder(block);
998                         }
999                 }
1000                 else if(dir1==UI_DOWN) {
1001                         yof= butrct.ymin - block->maxy;
1002                         if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
1003                         else xof= butrct.xmin - block->minx;
1004                         // changed direction?
1005                         if((dir1 & block->direction)==0) {
1006                                 if(block->direction & UI_SHIFT_FLIPPED)
1007                                         xof+= dir2==UI_LEFT?25:-25;
1008                                 uiBlockFlipOrder(block);
1009                         }
1010                 }
1011
1012                 /* and now we handle the exception; no space below or to top */
1013                 if(top==0 && down==0) {
1014                         if(dir1==UI_LEFT || dir1==UI_RIGHT) {
1015                                 // align with bottom of screen 
1016                                 yof= ysize;
1017                         }
1018                 }
1019                 
1020                 /* or no space left or right */
1021                 if(left==0 && right==0) {
1022                         if(dir1==UI_TOP || dir1==UI_DOWN) {
1023                                 // align with left size of screen 
1024                                 xof= -block->minx+5;
1025                         }
1026                 }
1027                 
1028                 // apply requested offset in the block
1029                 xof += block->xofs/block->aspect;
1030                 yof += block->yofs/block->aspect;
1031         }
1032         
1033         /* apply */
1034         
1035         for(bt= block->buttons.first; bt; bt= bt->next) {
1036                 ui_block_to_window_fl(butregion, but->block, &bt->x1, &bt->y1);
1037                 ui_block_to_window_fl(butregion, but->block, &bt->x2, &bt->y2);
1038
1039                 bt->x1 += xof;
1040                 bt->x2 += xof;
1041                 bt->y1 += yof;
1042                 bt->y2 += yof;
1043
1044                 bt->aspect= 1.0;
1045                 // ui_check_but recalculates drawstring size in pixels
1046                 ui_check_but(bt);
1047         }
1048         
1049         block->minx += xof;
1050         block->miny += yof;
1051         block->maxx += xof;
1052         block->maxy += yof;
1053
1054         /* safety calculus */
1055         if(but) {
1056                 float midx= (butrct.xmin+butrct.xmax)/2.0;
1057                 float midy= (butrct.ymin+butrct.ymax)/2.0;
1058                 
1059                 /* when you are outside parent button, safety there should be smaller */
1060                 
1061                 // parent button to left
1062                 if( midx < block->minx ) block->safety.xmin= block->minx-3; 
1063                 else block->safety.xmin= block->minx-40;
1064                 // parent button to right
1065                 if( midx > block->maxx ) block->safety.xmax= block->maxx+3; 
1066                 else block->safety.xmax= block->maxx+40;
1067                 
1068                 // parent button on bottom
1069                 if( midy < block->miny ) block->safety.ymin= block->miny-3; 
1070                 else block->safety.ymin= block->miny-40;
1071                 // parent button on top
1072                 if( midy > block->maxy ) block->safety.ymax= block->maxy+3; 
1073                 else block->safety.ymax= block->maxy+40;
1074                 
1075                 // exception for switched pulldowns...
1076                 if(dir1 && (dir1 & block->direction)==0) {
1077                         if(dir2==UI_RIGHT) block->safety.xmax= block->maxx+3; 
1078                         if(dir2==UI_LEFT) block->safety.xmin= block->minx-3; 
1079                 }
1080                 block->direction= dir1;
1081         }
1082         else {
1083                 block->safety.xmin= block->minx-40;
1084                 block->safety.ymin= block->miny-40;
1085                 block->safety.xmax= block->maxx+40;
1086                 block->safety.ymax= block->maxy+40;
1087         }
1088
1089         /* keep a list of these, needed for pulldown menus */
1090         saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1091         saferct->parent= butrct;
1092         saferct->safety= block->safety;
1093         BLI_freelistN(&block->saferct);
1094         if(but)
1095                 BLI_duplicatelist(&block->saferct, &but->block->saferct);
1096         BLI_addhead(&block->saferct, saferct);
1097 }
1098
1099 static void ui_block_region_draw(const bContext *C, ARegion *ar)
1100 {
1101         uiBlock *block;
1102
1103         for(block=ar->uiblocks.first; block; block=block->next)
1104                 uiDrawBlock(C, block);
1105 }
1106
1107 uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg)
1108 {
1109         wmWindow *window= CTX_wm_window(C);
1110         static ARegionType type;
1111         ARegion *ar;
1112         uiBlock *block;
1113         uiBut *bt;
1114         uiPopupBlockHandle *handle;
1115         uiSafetyRct *saferct;
1116
1117         /* create handle */
1118         handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
1119
1120         /* store context for operator */
1121         handle->ctx_area= CTX_wm_area(C);
1122         handle->ctx_region= CTX_wm_region(C);
1123         
1124         /* create area region */
1125         ar= ui_add_temporary_region(CTX_wm_screen(C));
1126         handle->region= ar;
1127
1128         memset(&type, 0, sizeof(ARegionType));
1129         type.draw= ui_block_region_draw;
1130         ar->type= &type;
1131
1132         UI_add_region_handlers(&ar->handlers);
1133
1134         /* create ui block */
1135         if(create_func)
1136                 block= create_func(C, handle->region, arg);
1137         else
1138                 block= handle_create_func(C, handle, arg);
1139         
1140         if(block->handle) {
1141                 memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
1142                 MEM_freeN(handle);
1143                 handle= block->handle;
1144         }
1145         else
1146                 block->handle= handle;
1147
1148         ar->regiondata= handle;
1149
1150         if(!block->endblock)
1151                 uiEndBlock(C, block);
1152
1153         /* if this is being created from a button */
1154         if(but) {
1155                 if(ELEM(but->type, BLOCK, PULLDOWN))
1156                         block->xofs = -2;       /* for proper alignment */
1157
1158                 /* only used for automatic toolbox, so can set the shift flag */
1159                 if(but->flag & UI_MAKE_TOP) {
1160                         block->direction= UI_TOP|UI_SHIFT_FLIPPED;
1161                         uiBlockFlipOrder(block);
1162                 }
1163                 if(but->flag & UI_MAKE_DOWN) block->direction= UI_DOWN|UI_SHIFT_FLIPPED;
1164                 if(but->flag & UI_MAKE_LEFT) block->direction |= UI_LEFT;
1165                 if(but->flag & UI_MAKE_RIGHT) block->direction |= UI_RIGHT;
1166
1167                 ui_block_position(window, butregion, but, block);
1168         }
1169         else {
1170                 /* keep a list of these, needed for pulldown menus */
1171                 saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1172                 saferct->safety= block->safety;
1173                 BLI_addhead(&block->saferct, saferct);
1174                 block->flag |= UI_BLOCK_POPUP;
1175         }
1176
1177         /* the block and buttons were positioned in window space as in 2.4x, now
1178          * these menu blocks are regions so we bring it back to region space.
1179          * additionally we add some padding for the menu shadow or rounded menus */
1180         ar->winrct.xmin= block->minx - MENU_SHADOW_SIDE;
1181         ar->winrct.xmax= block->maxx + MENU_SHADOW_SIDE;
1182         ar->winrct.ymin= block->miny - MENU_SHADOW_BOTTOM;
1183         ar->winrct.ymax= block->maxy + MENU_TOP;
1184
1185         block->minx -= ar->winrct.xmin;
1186         block->maxx -= ar->winrct.xmin;
1187         block->miny -= ar->winrct.ymin;
1188         block->maxy -= ar->winrct.ymin;
1189
1190         for(bt= block->buttons.first; bt; bt= bt->next) {
1191                 bt->x1 -= ar->winrct.xmin;
1192                 bt->x2 -= ar->winrct.xmin;
1193                 bt->y1 -= ar->winrct.ymin;
1194                 bt->y2 -= ar->winrct.ymin;
1195         }
1196
1197         block->flag |= UI_BLOCK_LOOP|UI_BLOCK_MOVEMOUSE_QUIT;
1198
1199         /* adds subwindow */
1200         ED_region_init(C, ar);
1201
1202         /* get winmat now that we actually have the subwindow */
1203         wmSubWindowSet(window, ar->swinid);
1204         
1205         wm_subwindow_getmatrix(window, ar->swinid, block->winmat);
1206         
1207         /* notify change and redraw */
1208         ED_region_tag_redraw(ar);
1209
1210         return handle;
1211 }
1212
1213 void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
1214 {
1215         /* XXX ton added, chrash on load file with popup open... need investigate */
1216         if(CTX_wm_screen(C))
1217                 ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
1218         MEM_freeN(handle);
1219 }
1220
1221 /***************************** Menu Button ***************************/
1222
1223 uiBlock *ui_block_func_MENU(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
1224 {
1225         uiBut *but= arg_but;
1226         uiBlock *block;
1227         uiBut *bt;
1228         MenuData *md;
1229         ListBase lb;
1230         float aspect;
1231         int width, height, boxh, columns, rows, startx, starty, x1, y1, xmax, a;
1232
1233         /* create the block */
1234         block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
1235         block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT;
1236
1237         /* compute menu data */
1238         md= decompose_menu_string(but->str);
1239
1240         /* columns and row calculation */
1241         columns= (md->nitems+20)/20;
1242         if(columns<1)
1243                 columns= 1;
1244         if(columns>8)
1245                 columns= (md->nitems+25)/25;
1246         
1247         rows= md->nitems/columns;
1248         if(rows<1)
1249                 rows= 1;
1250         while(rows*columns<md->nitems)
1251                 rows++;
1252                 
1253         /* prevent scaling up of pupmenu */
1254         aspect= but->block->aspect;
1255         if(aspect < 1.0f)
1256                 aspect = 1.0f;
1257
1258         /* size and location */
1259         if(md->title)
1260                 width= 1.5*aspect*strlen(md->title)+UI_GetStringWidth(md->title);
1261         else
1262                 width= 0;
1263
1264         for(a=0; a<md->nitems; a++) {
1265                 xmax= aspect*UI_GetStringWidth(md->items[a].str);
1266                 if(md->items[a].icon)
1267                         xmax += 20*aspect;
1268                 if(xmax>width)
1269                         width= xmax;
1270         }
1271
1272         width+= 10;
1273         if(width < (but->x2 - but->x1))
1274                 width = (but->x2 - but->x1);
1275         if(width<50)
1276                 width=50;
1277         
1278         boxh= MENU_BUTTON_HEIGHT;
1279         
1280         height= rows*boxh;
1281         if(md->title)
1282                 height+= boxh;
1283
1284         /* here we go! */
1285         startx= but->x1;
1286         starty= but->y1;
1287         
1288         if(md->title) {
1289                 uiBut *bt;
1290
1291                 if (md->titleicon) {
1292                         bt= uiDefIconTextBut(block, LABEL, 0, md->titleicon, md->title, startx, (short)(starty+rows*boxh), (short)width, (short)boxh, NULL, 0.0, 0.0, 0, 0, "");
1293                 } else {
1294                         bt= uiDefBut(block, LABEL, 0, md->title, startx, (short)(starty+rows*boxh), (short)width, (short)boxh, NULL, 0.0, 0.0, 0, 0, "");
1295                         bt->flag= UI_TEXT_LEFT;
1296                 }
1297         }
1298
1299         for(a=0; a<md->nitems; a++) {
1300                 
1301                 x1= startx + width*((int)(md->nitems-a-1)/rows);
1302                 y1= starty - boxh*(rows - ((md->nitems - a - 1)%rows)) + (rows*boxh);
1303
1304                 if (strcmp(md->items[md->nitems-a-1].str, "%l")==0) {
1305                         bt= uiDefBut(block, SEPR, B_NOP, "", x1, y1,(short)(width-(rows>1)), (short)(boxh-1), NULL, 0.0, 0.0, 0, 0, "");
1306                 }
1307                 else if(md->items[md->nitems-a-1].icon) {
1308                         bt= uiDefIconTextButF(block, BUTM|FLO, B_NOP, md->items[md->nitems-a-1].icon ,md->items[md->nitems-a-1].str, x1, y1,(short)(width-(rows>1)), (short)(boxh-1), &handle->retvalue, (float) md->items[md->nitems-a-1].retval, 0.0, 0, 0, "");
1309                 }
1310                 else {
1311                         bt= uiDefButF(block, BUTM|FLO, B_NOP, md->items[md->nitems-a-1].str, x1, y1,(short)(width-(rows>1)), (short)(boxh-1), &handle->retvalue, (float) md->items[md->nitems-a-1].retval, 0.0, 0, 0, "");
1312                 }
1313         }
1314         
1315         menudata_free(md);
1316
1317         /* the code up here has flipped locations, because of change of preferred order */
1318         /* thats why we have to switch list order too, to make arrowkeys work */
1319         
1320         lb.first= lb.last= NULL;
1321         bt= block->buttons.first;
1322         while(bt) {
1323                 uiBut *next= bt->next;
1324                 BLI_remlink(&block->buttons, bt);
1325                 BLI_addhead(&lb, bt);
1326                 bt= next;
1327         }
1328         block->buttons= lb;
1329
1330         block->direction= UI_TOP;
1331         uiEndBlock(C, block);
1332
1333         return block;
1334 }
1335
1336 uiBlock *ui_block_func_ICONROW(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
1337 {
1338         uiBut *but= arg_but;
1339         uiBlock *block;
1340         int a;
1341         
1342         block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
1343         block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT;
1344         
1345         for(a=(int)but->hardmin; a<=(int)but->hardmax; a++) {
1346                 uiDefIconButF(block, BUTM|FLO, B_NOP, but->icon+(a-but->hardmin), 0, (short)(18*a), (short)(but->x2-but->x1-4), 18, &handle->retvalue, (float)a, 0.0, 0, 0, "");
1347         }
1348
1349         block->direction= UI_TOP;       
1350
1351         uiEndBlock(C, block);
1352
1353         return block;
1354 }
1355
1356 uiBlock *ui_block_func_ICONTEXTROW(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
1357 {
1358         uiBut *but= arg_but;
1359         uiBlock *block;
1360         MenuData *md;
1361         int width, xmax, ypos, a;
1362
1363         block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
1364         block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT;
1365
1366         md= decompose_menu_string(but->str);
1367
1368         /* size and location */
1369         /* expand menu width to fit labels */
1370         if(md->title)
1371                 width= 2*strlen(md->title)+UI_GetStringWidth(md->title);
1372         else
1373                 width= 0;
1374
1375         for(a=0; a<md->nitems; a++) {
1376                 xmax= UI_GetStringWidth(md->items[a].str);
1377                 if(xmax>width) width= xmax;
1378         }
1379
1380         width+= 30;
1381         if (width<50) width=50;
1382
1383         ypos = 1;
1384
1385         /* loop through the menu options and draw them out with icons & text labels */
1386         for(a=0; a<md->nitems; a++) {
1387
1388                 /* add a space if there's a separator (%l) */
1389                 if (strcmp(md->items[a].str, "%l")==0) {
1390                         ypos +=3;
1391                 }
1392                 else {
1393                         uiDefIconTextButF(block, BUTM|FLO, B_NOP, (short)((but->icon)+(md->items[a].retval-but->hardmin)), md->items[a].str, 0, ypos,(short)width, 19, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
1394                         ypos += 20;
1395                 }
1396         }
1397         
1398         if(md->title) {
1399                 uiBut *bt;
1400
1401                 bt= uiDefBut(block, LABEL, 0, md->title, 0, ypos, (short)width, 19, NULL, 0.0, 0.0, 0, 0, "");
1402                 bt->flag= UI_TEXT_LEFT;
1403         }
1404         
1405         menudata_free(md);
1406
1407         block->direction= UI_TOP;
1408
1409         uiBoundsBlock(block, 3);
1410         uiEndBlock(C, block);
1411
1412         return block;
1413 }
1414
1415 static void ui_warp_pointer(short x, short y)
1416 {
1417         /* XXX 2.50 which function to use for this? */
1418 #if 0
1419         /* OSX has very poor mousewarp support, it sends events;
1420            this causes a menu being pressed immediately ... */
1421         #ifndef __APPLE__
1422         warp_pointer(x, y);
1423         #endif
1424 #endif
1425 }
1426
1427 /********************* Color Button ****************/
1428
1429 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1430 #define SPICK   110.0
1431 #define FPICK   180.0
1432 #define DPICK   6.0
1433 #define BPICK   24.0
1434
1435 #define UI_PALETTE_TOT 16
1436 /* note; in tot+1 the old color is stored */
1437 static float palette[UI_PALETTE_TOT+1][3]= {
1438 {0.93, 0.83, 0.81}, {0.88, 0.89, 0.73}, {0.69, 0.81, 0.57}, {0.51, 0.76, 0.64}, 
1439 {0.37, 0.56, 0.61}, {0.33, 0.29, 0.55}, {0.46, 0.21, 0.51}, {0.40, 0.12, 0.18}, 
1440 {1.0, 1.0, 1.0}, {0.85, 0.85, 0.85}, {0.7, 0.7, 0.7}, {0.56, 0.56, 0.56}, 
1441 {0.42, 0.42, 0.42}, {0.28, 0.28, 0.28}, {0.14, 0.14, 0.14}, {0.0, 0.0, 0.0}
1442 };  
1443
1444 /* for picker, while editing hsv */
1445 void ui_set_but_hsv(uiBut *but)
1446 {
1447         float col[3];
1448         
1449         hsv_to_rgb(but->hsv[0], but->hsv[1], but->hsv[2], col, col+1, col+2);
1450         ui_set_but_vectorf(but, col);
1451 }
1452
1453 static void update_picker_hex(uiBlock *block, float *rgb)
1454 {
1455         uiBut *bt;
1456         char col[16];
1457         
1458         sprintf(col, "%02X%02X%02X", (unsigned int)(rgb[0]*255.0), (unsigned int)(rgb[1]*255.0), (unsigned int)(rgb[2]*255.0));
1459         
1460         // this updates button strings, is hackish... but button pointers are on stack of caller function
1461
1462         for(bt= block->buttons.first; bt; bt= bt->next) {
1463                 if(strcmp(bt->str, "Hex: ")==0) {
1464                         strcpy(bt->poin, col);
1465                         ui_check_but(bt);
1466                         break;
1467                 }
1468         }
1469 }
1470
1471 /* also used by small picker, be careful with name checks below... */
1472 void ui_update_block_buts_hsv(uiBlock *block, float *hsv)
1473 {
1474         uiBut *bt;
1475         float r, g, b;
1476         float rgb[3];
1477         
1478         // this updates button strings, is hackish... but button pointers are on stack of caller function
1479         hsv_to_rgb(hsv[0], hsv[1], hsv[2], &r, &g, &b);
1480         
1481         rgb[0] = r; rgb[1] = g; rgb[2] = b;
1482         update_picker_hex(block, rgb);
1483
1484         for(bt= block->buttons.first; bt; bt= bt->next) {
1485                 if(ELEM(bt->type, HSVCUBE, HSVCIRCLE)) {
1486                         VECCOPY(bt->hsv, hsv);
1487                         ui_set_but_hsv(bt);
1488                 }
1489                 else if(bt->str[1]==' ') {
1490                         if(bt->str[0]=='R') {
1491                                 ui_set_but_val(bt, r);
1492                         }
1493                         else if(bt->str[0]=='G') {
1494                                 ui_set_but_val(bt, g);
1495                         }
1496                         else if(bt->str[0]=='B') {
1497                                 ui_set_but_val(bt, b);
1498                         }
1499                         else if(bt->str[0]=='H') {
1500                                 ui_set_but_val(bt, hsv[0]);
1501                         }
1502                         else if(bt->str[0]=='S') {
1503                                 ui_set_but_val(bt, hsv[1]);
1504                         }
1505                         else if(bt->str[0]=='V') {
1506                                 ui_set_but_val(bt, hsv[2]);
1507                         }
1508                 }               
1509         }
1510 }
1511
1512 static void ui_update_block_buts_hex(uiBlock *block, char *hexcol)
1513 {
1514         uiBut *bt;
1515         float r=0, g=0, b=0;
1516         float h, s, v;
1517         
1518         
1519         // this updates button strings, is hackish... but button pointers are on stack of caller function
1520         hex_to_rgb(hexcol, &r, &g, &b);
1521         rgb_to_hsv(r, g, b, &h, &s, &v);
1522
1523         for(bt= block->buttons.first; bt; bt= bt->next) {
1524                 if(bt->type==HSVCUBE) {
1525                         bt->hsv[0] = h;
1526                         bt->hsv[1] = s;                 
1527                         bt->hsv[2] = v;
1528                         ui_set_but_hsv(bt);
1529                 }
1530                 else if(bt->str[1]==' ') {
1531                         if(bt->str[0]=='R') {
1532                                 ui_set_but_val(bt, r);
1533                         }
1534                         else if(bt->str[0]=='G') {
1535                                 ui_set_but_val(bt, g);
1536                         }
1537                         else if(bt->str[0]=='B') {
1538                                 ui_set_but_val(bt, b);
1539                         }
1540                         else if(bt->str[0]=='H') {
1541                                 ui_set_but_val(bt, h);
1542                         }
1543                         else if(bt->str[0]=='S') {
1544                                 ui_set_but_val(bt, s);
1545                         }
1546                         else if(bt->str[0]=='V') {
1547                                 ui_set_but_val(bt, v);
1548                         }
1549                 }
1550         }
1551 }
1552
1553 /* bt1 is palette but, col1 is original color */
1554 /* callback to copy from/to palette */
1555 static void do_palette_cb(bContext *C, void *bt1, void *col1)
1556 {
1557         wmWindow *win= CTX_wm_window(C);
1558         uiBut *but1= (uiBut *)bt1;
1559         uiPopupBlockHandle *popup= but1->block->handle;
1560         float *col= (float *)col1;
1561         float *fp, hsv[3];
1562         
1563         fp= (float *)but1->poin;
1564         
1565         if(win->eventstate->ctrl) {
1566                 VECCOPY(fp, col);
1567         }
1568         else {
1569                 VECCOPY(col, fp);
1570         }
1571         
1572         rgb_to_hsv(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
1573         ui_update_block_buts_hsv(but1->block, hsv);
1574         update_picker_hex(but1->block, col);
1575
1576         if(popup)
1577                 popup->menuretval= UI_RETURN_UPDATE;
1578 }
1579
1580 static void do_hsv_cb(bContext *C, void *bt1, void *unused)
1581 {
1582         uiBut *but1= (uiBut *)bt1;
1583         uiPopupBlockHandle *popup= but1->block->handle;
1584
1585         if(popup)
1586                 popup->menuretval= UI_RETURN_UPDATE;
1587 }
1588
1589 /* bt1 is num but, hsv1 is pointer to original color in hsv space*/
1590 /* callback to handle changes in num-buts in picker */
1591 static void do_palette1_cb(bContext *C, void *bt1, void *hsv1)
1592 {
1593         uiBut *but1= (uiBut *)bt1;
1594         uiPopupBlockHandle *popup= but1->block->handle;
1595         float *hsv= (float *)hsv1;
1596         float *fp= NULL;
1597         
1598         if(but1->str[1]==' ') {
1599                 if(but1->str[0]=='R') fp= (float *)but1->poin;
1600                 else if(but1->str[0]=='G') fp= ((float *)but1->poin)-1;
1601                 else if(but1->str[0]=='B') fp= ((float *)but1->poin)-2;
1602         }
1603         if(fp) {
1604                 rgb_to_hsv(fp[0], fp[1], fp[2], hsv, hsv+1, hsv+2);
1605         } 
1606         ui_update_block_buts_hsv(but1->block, hsv);
1607
1608         if(popup)
1609                 popup->menuretval= UI_RETURN_UPDATE;
1610 }
1611
1612 /* bt1 is num but, col1 is pointer to original color */
1613 /* callback to handle changes in num-buts in picker */
1614 static void do_palette2_cb(bContext *C, void *bt1, void *col1)
1615 {
1616         uiBut *but1= (uiBut *)bt1;
1617         uiPopupBlockHandle *popup= but1->block->handle;
1618         float *rgb= (float *)col1;
1619         float *fp= NULL;
1620         
1621         if(but1->str[1]==' ') {
1622                 if(but1->str[0]=='H') fp= (float *)but1->poin;
1623                 else if(but1->str[0]=='S') fp= ((float *)but1->poin)-1;
1624                 else if(but1->str[0]=='V') fp= ((float *)but1->poin)-2;
1625         }
1626         if(fp) {
1627                 hsv_to_rgb(fp[0], fp[1], fp[2], rgb, rgb+1, rgb+2);
1628         } 
1629         ui_update_block_buts_hsv(but1->block, fp);
1630
1631         if(popup)
1632                 popup->menuretval= UI_RETURN_UPDATE;
1633 }
1634
1635 static void do_palette_hex_cb(bContext *C, void *bt1, void *hexcl)
1636 {
1637         uiBut *but1= (uiBut *)bt1;
1638         uiPopupBlockHandle *popup= but1->block->handle;
1639         char *hexcol= (char *)hexcl;
1640         
1641         ui_update_block_buts_hex(but1->block, hexcol);  
1642
1643         if(popup)
1644                 popup->menuretval= UI_RETURN_UPDATE;
1645 }
1646
1647 /* used for both 3d view and image window */
1648 static void do_palette_sample_cb(bContext *C, void *bt1, void *col1)    /* frontbuf */
1649 {
1650         /* XXX 2.50 this should become an operator? */
1651 #if 0
1652         uiBut *but1= (uiBut *)bt1;
1653         uiBut *but;
1654         float tempcol[4];
1655         int x=0, y=0;
1656         short mval[2];
1657         float hsv[3];
1658         short capturing;
1659         int oldcursor;
1660         Window *win;
1661         unsigned short dev;
1662         
1663         oldcursor=get_cursor();
1664         win=winlay_get_active_window();
1665         
1666         while (get_mbut() & L_MOUSE) UI_wait_for_statechange();
1667         
1668         SetBlenderCursor(BC_EYEDROPPER_CURSOR);
1669         
1670         /* loop and wait for a mouse click */
1671         capturing = TRUE;
1672         while(capturing) {
1673                 char ascii;
1674                 short val;
1675                 
1676                 dev = extern_qread_ext(&val, &ascii);
1677                 
1678                 if(dev==INPUTCHANGE) break;
1679                 if(get_mbut() & R_MOUSE) break;
1680                 else if(get_mbut() & L_MOUSE) {
1681                         uiGetMouse(mywinget(), mval);
1682                         x= mval[0]; y= mval[1];
1683                         
1684                         capturing = FALSE;
1685                         break;
1686                 }
1687                 else if(dev==ESCKEY) break;
1688         }
1689         window_set_cursor(win, oldcursor);
1690         
1691         if(capturing) return;
1692         
1693         if(x<0 || y<0) return;
1694         
1695         /* if we've got a glick, use OpenGL to sample the color under the mouse pointer */
1696         glReadBuffer(GL_FRONT);
1697         glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, tempcol);
1698         glReadBuffer(GL_BACK);
1699         
1700         /* and send that color back to the picker */
1701         rgb_to_hsv(tempcol[0], tempcol[1], tempcol[2], hsv, hsv+1, hsv+2);
1702         ui_update_block_buts_hsv(but1->block, hsv);
1703         update_picker_hex(but1->block, tempcol);
1704         
1705         for (but= but1->block->buttons.first; but; but= but->next) {
1706                 ui_check_but(but);
1707                 ui_draw_but(but);
1708         }
1709         
1710         but= but1->block->buttons.first;
1711         ui_block_flush_back(but->block);
1712 #endif
1713 }
1714
1715 /* color picker, Gimp version. mode: 'f' = floating panel, 'p' =  popup */
1716 /* col = read/write to, hsv/old/hexcol = memory for temporal use */
1717 void uiBlockPickerButtons(uiBlock *block, float *col, float *hsv, float *old, char *hexcol, char mode, short retval)
1718 {
1719         uiBut *bt;
1720         float h, offs;
1721         int a;
1722
1723         VECCOPY(old, col);      // old color stored there, for palette_cb to work
1724         
1725         // the cube intersection
1726         bt= uiDefButF(block, HSVCUBE, retval, "",       0,DPICK+BPICK,FPICK,FPICK, col, 0.0, 0.0, 2, 0, "");
1727         uiButSetFunc(bt, do_hsv_cb, bt, NULL);
1728
1729         bt= uiDefButF(block, HSVCUBE, retval, "",       0,0,FPICK,BPICK, col, 0.0, 0.0, 3, 0, "");
1730         uiButSetFunc(bt, do_hsv_cb, bt, NULL);
1731
1732         // palette
1733         
1734         bt=uiDefButF(block, COL, retval, "",            FPICK+DPICK, 0, BPICK,BPICK, old, 0.0, 0.0, -1, 0, "Old color, click to restore");
1735         uiButSetFunc(bt, do_palette_cb, bt, col);
1736         uiDefButF(block, COL, retval, "",               FPICK+DPICK, BPICK+DPICK, BPICK,60-BPICK-DPICK, col, 0.0, 0.0, -1, 0, "Active color");
1737
1738         h= (DPICK+BPICK+FPICK-64)/(UI_PALETTE_TOT/2.0);
1739         uiBlockBeginAlign(block);
1740         for(a= -1+UI_PALETTE_TOT/2; a>=0; a--) {
1741                 bt= uiDefButF(block, COL, retval, "",   FPICK+DPICK, 65.0+(float)a*h, BPICK/2, h, palette[a+UI_PALETTE_TOT/2], 0.0, 0.0, -1, 0, "Click to choose, hold CTRL to store in palette");
1742                 uiButSetFunc(bt, do_palette_cb, bt, col);
1743                 bt= uiDefButF(block, COL, retval, "",   FPICK+DPICK+BPICK/2, 65.0+(float)a*h, BPICK/2, h, palette[a], 0.0, 0.0, -1, 0, "Click to choose, hold CTRL to store in palette");               
1744                 uiButSetFunc(bt, do_palette_cb, bt, col);
1745         }
1746         uiBlockEndAlign(block);
1747         
1748         // buttons
1749         rgb_to_hsv(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
1750         sprintf(hexcol, "%02X%02X%02X", (unsigned int)(col[0]*255.0), (unsigned int)(col[1]*255.0), (unsigned int)(col[2]*255.0));      
1751
1752         offs= FPICK+2*DPICK+BPICK;
1753
1754         /* note; made this a TOG now, with NULL pointer. Is because BUT now gets handled with a afterfunc */
1755         bt= uiDefIconTextBut(block, TOG, UI_RETURN_OK, ICON_EYEDROPPER, "Sample", offs+55, 170, 85, 20, NULL, 0, 0, 0, 0, "Sample the color underneath the following mouse click (ESC or RMB to cancel)");
1756         uiButSetFunc(bt, do_palette_sample_cb, bt, col);
1757         uiButSetFlag(bt, UI_TEXT_LEFT);
1758         
1759         bt= uiDefBut(block, TEX, retval, "Hex: ", offs, 140, 140, 20, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
1760         uiButSetFunc(bt, do_palette_hex_cb, bt, hexcol);
1761
1762         uiBlockBeginAlign(block);
1763         bt= uiDefButF(block, NUMSLI, retval, "R ",      offs, 110, 140,20, col, 0.0, 1.0, 10, 3, "");
1764         uiButSetFunc(bt, do_palette1_cb, bt, hsv);
1765         bt= uiDefButF(block, NUMSLI, retval, "G ",      offs, 90, 140,20, col+1, 0.0, 1.0, 10, 3, "");
1766         uiButSetFunc(bt, do_palette1_cb, bt, hsv);
1767         bt= uiDefButF(block, NUMSLI, retval, "B ",      offs, 70, 140,20, col+2, 0.0, 1.0, 10, 3, "");
1768         uiButSetFunc(bt, do_palette1_cb, bt, hsv);
1769         
1770         uiBlockBeginAlign(block);
1771         bt= uiDefButF(block, NUMSLI, retval, "H ",      offs, 40, 140,20, hsv, 0.0, 1.0, 10, 3, "");
1772         uiButSetFunc(bt, do_palette2_cb, bt, col);
1773         bt= uiDefButF(block, NUMSLI, retval, "S ",      offs, 20, 140,20, hsv+1, 0.0, 1.0, 10, 3, "");
1774         uiButSetFunc(bt, do_palette2_cb, bt, col);
1775         bt= uiDefButF(block, NUMSLI, retval, "V ",      offs, 0, 140,20, hsv+2, 0.0, 1.0, 10, 3, "");
1776         uiButSetFunc(bt, do_palette2_cb, bt, col);
1777         uiBlockEndAlign(block);
1778 }
1779
1780 /* bt1 is num but, hsv1 is pointer to original color in hsv space*/
1781 /* callback to handle changes */
1782 static void do_picker_small_cb(bContext *C, void *bt1, void *hsv1)
1783 {
1784         uiBut *but1= (uiBut *)bt1;
1785         uiPopupBlockHandle *popup= but1->block->handle;
1786         float *hsv= (float *)hsv1;
1787         float *fp= NULL;
1788         
1789         fp= (float *)but1->poin;
1790         rgb_to_hsv(fp[0], fp[1], fp[2], hsv, hsv+1, hsv+2);
1791
1792         ui_update_block_buts_hsv(but1->block, hsv);
1793         
1794         if(popup)
1795                 popup->menuretval= UI_RETURN_UPDATE;
1796 }
1797
1798 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
1799 #define SPICK1  150.0
1800 #define DPICK1  6.0
1801
1802 /* only the color, a HS circle and V slider */
1803 static void uiBlockPickerSmall(uiBlock *block, float *col, float *hsv, float *old, char *hexcol, char mode, short retval)
1804 {
1805         uiBut *bt;
1806         
1807         VECCOPY(old, col);      // old color stored there, for palette_cb to work
1808         
1809         /* HS circle */
1810         bt= uiDefButF(block, HSVCIRCLE, retval, "",     0, 0,SPICK1,SPICK1, col, 0.0, 0.0, 0, 0, "");
1811         uiButSetFunc(bt, do_picker_small_cb, bt, hsv);
1812
1813         /* value */
1814         bt= uiDefButF(block, HSVCUBE, retval, "",       SPICK1+DPICK1,0,14,SPICK1, col, 0.0, 0.0, 4, 0, "");
1815         uiButSetFunc(bt, do_picker_small_cb, bt, hsv);
1816 }
1817
1818
1819 static void picker_new_hide_reveal(uiBlock *block, short colormode)
1820 {
1821         uiBut *bt;
1822         
1823         /* tag buttons */
1824         for(bt= block->buttons.first; bt; bt= bt->next) {
1825                 
1826                 if(bt->type==NUMSLI || bt->type==TEX) {
1827                         if( bt->str[1]=='e') {
1828                                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1829                                 else bt->flag |= UI_HIDDEN;
1830                         }
1831                         else if( ELEM3(bt->str[0], 'R', 'G', 'B')) {
1832                                 if(colormode==0) bt->flag &= ~UI_HIDDEN;
1833                                 else bt->flag |= UI_HIDDEN;
1834                         }
1835                         else if( ELEM3(bt->str[0], 'H', 'S', 'V')) {
1836                                 if(colormode==1) bt->flag &= ~UI_HIDDEN;
1837                                 else bt->flag |= UI_HIDDEN;
1838                         }
1839                 }
1840         }
1841 }
1842
1843 static void do_picker_new_mode_cb(bContext *C, void *bt1, void *colv)
1844 {
1845         uiBut *bt= bt1;
1846         short colormode= ui_get_but_val(bt);
1847
1848         picker_new_hide_reveal(bt->block, colormode);
1849 }
1850
1851
1852 /* a HS circle, V slider, rgb/hsv/hex sliders */
1853 static void uiBlockPickerNew(uiBlock *block, float *col, float *hsv, float *old, char *hexcol, char mode, short retval)
1854 {
1855         static short colormode= 0;      /* temp? 0=rgb, 1=hsv, 2=hex */
1856         uiBut *bt;
1857         int width;
1858         
1859         VECCOPY(old, col);      // old color stored there, for palette_cb to work
1860         
1861         /* HS circle */
1862         bt= uiDefButF(block, HSVCIRCLE, retval, "",     0, 0,SPICK1,SPICK1, col, 0.0, 0.0, 0, 0, "");
1863         uiButSetFunc(bt, do_picker_small_cb, bt, hsv);
1864         
1865         /* value */
1866         bt= uiDefButF(block, HSVCUBE, retval, "",       SPICK1+DPICK1,0,14,SPICK1, col, 0.0, 0.0, 4, 0, "");
1867         uiButSetFunc(bt, do_picker_small_cb, bt, hsv);
1868         
1869         /* mode */
1870         width= (SPICK1+DPICK1+14)/3;
1871         uiBlockBeginAlign(block);
1872         bt= uiDefButS(block, ROW, retval, "RGB",        0, -30, width, 19, &colormode, 0.0, 0.0, 0, 0, "");
1873         uiButSetFunc(bt, do_picker_new_mode_cb, bt, col);
1874         bt= uiDefButS(block, ROW, retval, "HSV",        width, -30, width, 19, &colormode, 0.0, 1.0, 0, 0, "");
1875         uiButSetFunc(bt, do_picker_new_mode_cb, bt, hsv);
1876         bt= uiDefButS(block, ROW, retval, "Hex",        2*width, -30, width, 19, &colormode, 0.0, 2.0, 0, 0, "");
1877         uiButSetFunc(bt, do_picker_new_mode_cb, bt, hexcol);
1878         uiBlockEndAlign(block);
1879         
1880         /* sliders or hex */
1881         width= (SPICK1+DPICK1+14);
1882         rgb_to_hsv(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
1883         sprintf(hexcol, "%02X%02X%02X", (unsigned int)(col[0]*255.0), (unsigned int)(col[1]*255.0), (unsigned int)(col[2]*255.0));      
1884
1885         uiBlockBeginAlign(block);
1886         bt= uiDefButF(block, NUMSLI, 0, "R ",   0, -60, width, 19, col, 0.0, 1.0, 10, 3, "");
1887         uiButSetFunc(bt, do_palette1_cb, bt, hsv);
1888         bt= uiDefButF(block, NUMSLI, 0, "G ",   0, -80, width, 19, col+1, 0.0, 1.0, 10, 3, "");
1889         uiButSetFunc(bt, do_palette1_cb, bt, hsv);
1890         bt= uiDefButF(block, NUMSLI, 0, "B ",   0, -100, width, 19, col+2, 0.0, 1.0, 10, 3, "");
1891         uiButSetFunc(bt, do_palette1_cb, bt, hsv);
1892         uiBlockEndAlign(block);
1893
1894         uiBlockBeginAlign(block);
1895         bt= uiDefButF(block, NUMSLI, 0, "H ",   0, -60, width, 19, hsv, 0.0, 1.0, 10, 3, "");
1896         uiButSetFunc(bt, do_palette2_cb, bt, col);
1897         bt= uiDefButF(block, NUMSLI, 0, "S ",   0, -80, width, 19, hsv+1, 0.0, 1.0, 10, 3, "");
1898         uiButSetFunc(bt, do_palette2_cb, bt, col);
1899         bt= uiDefButF(block, NUMSLI, 0, "V ",   0, -100, width, 19, hsv+2, 0.0, 1.0, 10, 3, "");
1900         uiButSetFunc(bt, do_palette2_cb, bt, col);
1901         uiBlockEndAlign(block);
1902
1903         bt= uiDefBut(block, TEX, 0, "Hex: ", 0, -80, width, 19, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
1904         uiButSetFunc(bt, do_palette_hex_cb, bt, hexcol);
1905
1906         picker_new_hide_reveal(block, colormode);
1907 }
1908
1909
1910 static int ui_picker_small_wheel(const bContext *C, uiBlock *block, wmEvent *event)
1911 {
1912         float add= 0.0f;
1913         
1914         if(event->type==WHEELUPMOUSE)
1915                 add= 0.05f;
1916         else if(event->type==WHEELDOWNMOUSE)
1917                 add= -0.05f;
1918         
1919         if(add!=0.0f) {
1920                 uiBut *but;
1921                 
1922                 for(but= block->buttons.first; but; but= but->next) {
1923                         if(but->type==HSVCUBE && but->active==NULL) {
1924                                 uiPopupBlockHandle *popup= block->handle;
1925                                 float col[3];
1926                                 
1927                                 ui_get_but_vectorf(but, col);
1928                                 
1929                                 rgb_to_hsv(col[0], col[1], col[2], but->hsv, but->hsv+1, but->hsv+2);
1930                                 but->hsv[2]= CLAMPIS(but->hsv[2]+add, 0.0f, 1.0f);
1931                                 hsv_to_rgb(but->hsv[0], but->hsv[1], but->hsv[2], col, col+1, col+2);
1932
1933                                 ui_set_but_vectorf(but, col);
1934                                 
1935                                 ui_update_block_buts_hsv(block, but->hsv);
1936                                 if(popup)
1937                                         popup->menuretval= UI_RETURN_UPDATE;
1938                                 
1939                                 return 1;
1940                         }
1941                 }
1942         }
1943         return 0;
1944 }
1945
1946 uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
1947 {
1948         wmWindow *win= CTX_wm_window(C); // XXX temp, needs to become keymap to detect type?
1949         uiBut *but= arg_but;
1950         uiBlock *block;
1951         static float hsvcol[3], oldcol[3];
1952         static char hexcol[128];
1953         
1954         block= uiBeginBlock(C, handle->region, "colorpicker", UI_EMBOSS);
1955         
1956         VECCOPY(handle->retvec, but->editvec);
1957         if(win->eventstate->shift) {
1958                 uiBlockPickerButtons(block, handle->retvec, hsvcol, oldcol, hexcol, 'p', 0);
1959                 block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN;
1960                 uiBoundsBlock(block, 3);
1961         }
1962         else if(win->eventstate->alt) {
1963                 uiBlockPickerSmall(block, handle->retvec, hsvcol, oldcol, hexcol, 'p', 0);
1964                 block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_RET_1|UI_BLOCK_OUT_1;
1965                 uiBoundsBlock(block, 10);
1966                 
1967                 block->block_event_func= ui_picker_small_wheel;
1968         }
1969         else {
1970                 uiBlockPickerNew(block, handle->retvec, hsvcol, oldcol, hexcol, 'p', 0);
1971                 block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN;
1972                 uiBoundsBlock(block, 10);
1973                 
1974                 block->block_event_func= ui_picker_small_wheel;
1975         }
1976         
1977         
1978         /* and lets go */
1979         block->direction= UI_TOP;
1980         
1981         return block;
1982 }
1983
1984 /* ******************** Color band *************** */
1985
1986 static int vergcband(const void *a1, const void *a2)
1987 {
1988         const CBData *x1=a1, *x2=a2;
1989         
1990         if( x1->pos > x2->pos ) return 1;
1991         else if( x1->pos < x2->pos) return -1;
1992         return 0;
1993 }
1994
1995 static void colorband_pos_cb(bContext *C, void *coba_v, void *unused_v)
1996 {
1997         ColorBand *coba= coba_v;
1998         int a;
1999         
2000         if(coba->tot<2) return;
2001         
2002         for(a=0; a<coba->tot; a++) coba->data[a].cur= a;
2003         qsort(coba->data, coba->tot, sizeof(CBData), vergcband);
2004         for(a=0; a<coba->tot; a++) {
2005                 if(coba->data[a].cur==coba->cur) {
2006                         /* if(coba->cur!=a) addqueue(curarea->win, REDRAW, 0); */       /* button cur */
2007                         coba->cur= a;
2008                         break;
2009                 }
2010         }
2011 }
2012
2013 static void colorband_add_cb(bContext *C, void *coba_v, void *unused_v)
2014 {
2015         ColorBand *coba= coba_v;
2016         
2017         if(coba->tot < MAXCOLORBAND-1) coba->tot++;
2018         coba->cur= coba->tot-1;
2019         
2020         colorband_pos_cb(C, coba, NULL);
2021 //      BIF_undo_push("Add colorband");
2022         
2023 }
2024
2025 static void colorband_del_cb(bContext *C, void *coba_v, void *unused_v)
2026 {
2027         ColorBand *coba= coba_v;
2028         int a;
2029         
2030         if(coba->tot<2) return;
2031         
2032         for(a=coba->cur; a<coba->tot; a++) {
2033                 coba->data[a]= coba->data[a+1];
2034         }
2035         if(coba->cur) coba->cur--;
2036         coba->tot--;
2037         
2038 //      BIF_undo_push("Delete colorband");
2039 //      BIF_preview_changed(ID_TE);
2040 }
2041
2042 void uiBlockColorbandButtons(uiBlock *block, ColorBand *coba, rctf *butr, int event)
2043 {
2044         CBData *cbd;
2045         uiBut *bt;
2046         float unit= (butr->xmax-butr->xmin)/14.0f;
2047         float xs= butr->xmin;
2048         
2049         cbd= coba->data + coba->cur;
2050         
2051         uiBlockBeginAlign(block);
2052         uiDefButF(block, COL, event,            "",                     xs,butr->ymin+20.0f,2.0f*unit,20,                               &(cbd->r), 0, 0, 0, 0, "");
2053         uiDefButF(block, NUM, event,            "A:",           xs+2.0f*unit,butr->ymin+20.0f,4.0f*unit,20,     &(cbd->a), 0.0f, 1.0f, 10, 2, "");
2054         bt= uiDefBut(block, BUT, event, "Add",          xs+6.0f*unit,butr->ymin+20.0f,2.0f*unit,20,     NULL, 0, 0, 0, 0, "Adds a new color position to the colorband");
2055         uiButSetFunc(bt, colorband_add_cb, coba, NULL);
2056         bt= uiDefBut(block, BUT, event, "Del",          xs+8.0f*unit, butr->ymin+20.0f, 2.0f*unit, 20,  NULL, 0, 0, 0, 0, "Deletes the active position");
2057         uiButSetFunc(bt, colorband_del_cb, coba, NULL);
2058         
2059         uiDefButS(block, MENU, event,           "Interpolation %t|Ease %x1|Cardinal %x3|Linear %x0|B-Spline %x2|Constant %x4",
2060                           xs + 10.0f*unit, butr->ymin+20.0f, unit*4.0f, 20,             &coba->ipotype, 0.0, 0.0, 0, 0, "Sets interpolation type");
2061         
2062         uiDefBut(block, BUT_COLORBAND, event, "",               xs, butr->ymin, butr->xmax-butr->xmin, 20.0f, coba, 0, 0, 0, 0, "");
2063         uiBlockEndAlign(block);
2064         
2065 }
2066
2067
2068 /* ******************** PUPmenu ****************** */
2069
2070 static int pupmenu_set= 0;
2071
2072 void uiPupMenuSetActive(int val)
2073 {
2074         pupmenu_set= val;
2075 }
2076
2077 /* value== -1 read, otherwise set */
2078 static int pupmenu_memory(char *str, int value)
2079 {
2080         static char mem[256], first=1;
2081         int val=0, nr=0;
2082         
2083         if(first) {
2084                 memset(mem, 0, 256);
2085                 first= 0;
2086         }
2087         while(str[nr]) {
2088                 val+= str[nr];
2089                 nr++;
2090         }
2091
2092         if(value >= 0) mem[ val & 255 ]= value;
2093         else return mem[ val & 255 ];
2094         
2095         return 0;
2096 }
2097
2098 #define PUP_LABELH      6
2099
2100 typedef struct uiPupMenuInfo {
2101         char *instr;
2102         int mx, my;
2103         int startx, starty;
2104         int maxrow;
2105 } uiPupMenuInfo;
2106
2107 uiBlock *ui_block_func_PUPMENU(bContext *C, uiPopupBlockHandle *handle, void *arg_info)
2108 {
2109         uiBlock *block;
2110         uiPupMenuInfo *info;
2111         int columns, rows, mousemove[2]= {0, 0}, mousewarp= 0;
2112         int width, height, xmax, ymax, maxrow;
2113         int a, startx, starty, endx, endy, x1, y1;
2114         int lastselected;
2115         MenuData *md;
2116
2117         info= arg_info;
2118         maxrow= info->maxrow;
2119         height= 0;
2120
2121         /* block stuff first, need to know the font */
2122         block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
2123         uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_RET_1|UI_BLOCK_NUMSELECT);
2124         block->direction= UI_DOWN;
2125         
2126         md= decompose_menu_string(info->instr);
2127
2128         rows= md->nitems;
2129         if(rows<1)
2130                 rows= 1;
2131         
2132         columns= 1;
2133
2134         /* size and location, title slightly bigger for bold */
2135         if(md->title) {
2136                 width= 2*strlen(md->title)+UI_GetStringWidth(md->title);
2137                 width /= columns;
2138         }
2139         else width= 0;
2140
2141         for(a=0; a<md->nitems; a++) {
2142                 xmax= UI_GetStringWidth(md->items[a].str);
2143                 if(xmax>width) width= xmax;
2144
2145                 if(strcmp(md->items[a].str, "%l")==0) height+= PUP_LABELH;
2146                 else height+= MENU_BUTTON_HEIGHT;
2147         }
2148
2149         width+= 10;
2150         if (width<50) width=50;
2151         
2152         wm_window_get_size(CTX_wm_window(C), &xmax, &ymax);
2153
2154         /* set first item */
2155         lastselected= 0;
2156         if(pupmenu_set) {
2157                 lastselected= pupmenu_set-1;
2158                 pupmenu_set= 0;
2159         }
2160         else if(md->nitems>1) {
2161                 lastselected= pupmenu_memory(info->instr, -1);
2162         }
2163
2164         startx= info->mx-(0.8*(width));
2165         starty= info->my-height+MENU_BUTTON_HEIGHT/2;
2166         if(lastselected>=0 && lastselected<md->nitems) {
2167                 for(a=0; a<md->nitems; a++) {
2168                         if(a==lastselected) break;
2169                         if( strcmp(md->items[a].str, "%l")==0) starty+= PUP_LABELH;
2170                         else starty+=MENU_BUTTON_HEIGHT;
2171                 }
2172                 
2173                 //starty= info->my-height+MENU_BUTTON_HEIGHT/2+lastselected*MENU_BUTTON_HEIGHT;
2174         }
2175         
2176         if(startx<10) {
2177                 startx= 10;
2178         }
2179         if(starty<10) {
2180                 mousemove[1]= 10-starty;
2181                 starty= 10;
2182         }
2183         
2184         endx= startx+width*columns;
2185         endy= starty+height;
2186
2187         if(endx>xmax) {
2188                 endx= xmax-10;
2189                 startx= endx-width*columns;
2190         }
2191         if(endy>ymax-20) {
2192                 mousemove[1]= ymax-endy-20;
2193                 endy= ymax-20;
2194                 starty= endy-height;
2195         }
2196
2197         if(mousemove[0] || mousemove[1]) {
2198                 ui_warp_pointer(info->mx+mousemove[0], info->my+mousemove[1]);
2199                 mousemove[0]= info->mx;
2200                 mousemove[1]= info->my;
2201                 mousewarp= 1;
2202         }
2203
2204         /* here we go! */
2205         if(md->title) {
2206                 uiBut *bt;
2207                 char titlestr[256];
2208
2209                 if(md->titleicon) {
2210                         width+= 20;
2211                         sprintf(titlestr, " %s", md->title);
2212                         uiDefIconTextBut(block, LABEL, 0, md->titleicon, titlestr, startx, (short)(starty+height), width, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2213                 }
2214                 else {
2215                         bt= uiDefBut(block, LABEL, 0, md->title, startx, (short)(starty+height), columns*width, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2216                         bt->flag= UI_TEXT_LEFT;
2217                 }
2218                 
2219                 //uiDefBut(block, SEPR, 0, "", startx, (short)(starty+height)-MENU_SEPR_HEIGHT, width, MENU_SEPR_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2220         }
2221
2222         x1= startx + width*((int)a/rows);
2223         y1= starty + height - MENU_BUTTON_HEIGHT; // - MENU_SEPR_HEIGHT;
2224                 
2225         for(a=0; a<md->nitems; a++) {
2226                 char *name= md->items[a].str;
2227                 int icon = md->items[a].icon;
2228
2229                 if(strcmp(name, "%l")==0) {
2230                         uiDefBut(block, SEPR, B_NOP, "", x1, y1, width, PUP_LABELH, NULL, 0, 0.0, 0, 0, "");
2231                         y1 -= PUP_LABELH;
2232                 }
2233                 else if (icon) {
2234                         uiDefIconButF(block, BUTM, B_NOP, icon, x1, y1, width+16, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
2235                         y1 -= MENU_BUTTON_HEIGHT;
2236                 }
2237                 else {
2238                         uiDefButF(block, BUTM, B_NOP, name, x1, y1, width, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
2239                         y1 -= MENU_BUTTON_HEIGHT;
2240                 }
2241         }
2242         
2243         uiBoundsBlock(block, 1);
2244         uiEndBlock(C, block);
2245
2246         menudata_free(md);
2247
2248         /* XXX 2.5 need to store last selected */
2249 #if 0
2250         /* calculate last selected */
2251         if(event & ui_return_ok) {
2252                 lastselected= 0;
2253                 for(a=0; a<md->nitems; a++) {
2254                         if(val==md->items[a].retval) lastselected= a;
2255                 }
2256                 
2257                 pupmenu_memory(info->instr, lastselected);
2258         }
2259 #endif
2260         
2261         /* XXX 2.5 need to warp back */
2262 #if 0
2263         if(mousemove[1] && (event & ui_return_out)==0)
2264                 ui_warp_pointer(mousemove[0], mousemove[1]);
2265         return val;
2266 #endif
2267
2268         return block;
2269 }
2270
2271 uiBlock *ui_block_func_PUPMENUCOL(bContext *C, uiPopupBlockHandle *handle, void *arg_info)
2272 {
2273         uiBlock *block;
2274         uiPupMenuInfo *info;
2275         int columns, rows, mousemove[2]= {0, 0}, mousewarp;
2276         int width, height, xmax, ymax, maxrow;
2277         int a, startx, starty, endx, endy, x1, y1;
2278         float fvalue;
2279         MenuData *md;
2280
2281         info= arg_info;
2282         maxrow= info->maxrow;
2283         height= 0;
2284
2285         /* block stuff first, need to know the font */
2286         block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
2287         uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_RET_1|UI_BLOCK_NUMSELECT);
2288         block->direction= UI_DOWN;
2289         
2290         md= decompose_menu_string(info->instr);
2291
2292         /* columns and row calculation */
2293         columns= (md->nitems+maxrow)/maxrow;
2294         if (columns<1) columns= 1;
2295         
2296         if(columns > 8) {
2297                 maxrow += 5;
2298                 columns= (md->nitems+maxrow)/maxrow;
2299         }
2300         
2301         rows= (int) md->nitems/columns;
2302         if (rows<1) rows= 1;
2303         
2304         while (rows*columns<(md->nitems+columns) ) rows++;
2305
2306         /* size and location, title slightly bigger for bold */
2307         if(md->title) {
2308                 width= 2*strlen(md->title)+UI_GetStringWidth(md->title);
2309                 width /= columns;
2310         }
2311         else width= 0;
2312
2313         for(a=0; a<md->nitems; a++) {
2314                 xmax= UI_GetStringWidth(md->items[a].str);
2315                 if(xmax>width) width= xmax;
2316         }
2317
2318         width+= 10;
2319         if (width<50) width=50;
2320         
2321         height= rows*MENU_BUTTON_HEIGHT;
2322         if (md->title) height+= MENU_BUTTON_HEIGHT;
2323         
2324         wm_window_get_size(CTX_wm_window(C), &xmax, &ymax);
2325
2326         /* find active item */
2327         fvalue= handle->retvalue;
2328         for(a=0; a<md->nitems; a++) {
2329                 if( md->items[a].retval== (int)fvalue ) break;
2330         }
2331
2332         /* no active item? */
2333         if(a==md->nitems) {
2334                 if(md->title) a= -1;
2335                 else a= 0;
2336         }
2337
2338         if(a>0)
2339                 startx = info->mx-width/2 - ((int)(a)/rows)*width;
2340         else
2341                 startx= info->mx-width/2;
2342         starty = info->my-height + MENU_BUTTON_HEIGHT/2 + ((a)%rows)*MENU_BUTTON_HEIGHT;
2343
2344         if (md->title) starty+= MENU_BUTTON_HEIGHT;
2345         
2346         if(startx<10) {
2347                 mousemove[0]= 10-startx;
2348                 startx= 10;
2349         }
2350         if(starty<10) {
2351                 mousemove[1]= 10-starty;
2352                 starty= 10;
2353         }
2354         
2355         endx= startx+width*columns;
2356         endy= starty+height;
2357
2358         if(endx>xmax) {
2359                 mousemove[0]= xmax-endx-10;
2360                 endx= xmax-10;
2361                 startx= endx-width*columns;
2362         }
2363         if(endy>ymax) {
2364                 mousemove[1]= ymax-endy-10;
2365                 endy= ymax-10;
2366                 starty= endy-height;
2367         }
2368
2369         if(mousemove[0] || mousemove[1]) {
2370                 ui_warp_pointer(info->mx+mousemove[0], info->my+mousemove[1]);
2371                 mousemove[0]= info->mx;
2372                 mousemove[1]= info->my;
2373                 mousewarp= 1;
2374         }
2375
2376         /* here we go! */
2377         if(md->title) {
2378                 uiBut *bt;
2379
2380                 if(md->titleicon) {
2381                 }
2382                 else {
2383                         bt= uiDefBut(block, LABEL, 0, md->title, startx, (short)(starty+rows*MENU_BUTTON_HEIGHT), columns*width, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2384                         bt->flag= UI_TEXT_LEFT;
2385                 }
2386         }
2387
2388         for(a=0; a<md->nitems; a++) {
2389                 char *name= md->items[a].str;
2390                 int icon = md->items[a].icon;
2391
2392                 x1= startx + width*((int)a/rows);
2393                 y1= starty - MENU_BUTTON_HEIGHT*(a%rows) + (rows-1)*MENU_BUTTON_HEIGHT; 
2394                 
2395                 if(strcmp(name, "%l")==0) {
2396                         uiDefBut(block, SEPR, B_NOP, "", x1, y1, width, PUP_LABELH, NULL, 0, 0.0, 0, 0, "");
2397                         y1 -= PUP_LABELH;
2398                 }
2399                 else if (icon) {
2400                         uiDefIconButF(block, BUTM, B_NOP, icon, x1, y1, width+16, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
2401                         y1 -= MENU_BUTTON_HEIGHT;
2402                 }
2403                 else {
2404                         uiDefButF(block, BUTM, B_NOP, name, x1, y1, width, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
2405                         y1 -= MENU_BUTTON_HEIGHT;
2406                 }
2407         }
2408         
2409         uiBoundsBlock(block, 1);
2410         uiEndBlock(C, block);
2411         
2412         menudata_free(md);
2413         
2414         /* XXX 2.5 need to warp back */
2415 #if 0
2416         if((event & UI_RETURN_OUT)==0)
2417                 ui_warp_pointer(mousemove[0], mousemove[1]);
2418 #endif
2419
2420         return block;
2421 }
2422
2423 /************************** Menu Definitions ***************************/
2424
2425 /* prototype */
2426 static uiBlock *ui_block_func_MENU_ITEM(bContext *C, uiPopupBlockHandle *handle, void *arg_info);
2427
2428 struct uiPopupMenu {
2429         uiBlock *block;
2430         uiLayout *layout;
2431 };
2432
2433 typedef struct uiMenuInfo {
2434         uiPopupMenu *pup;
2435         int mx, my, popup, slideout;
2436         int startx, starty;
2437 } uiMenuInfo;
2438
2439 /************************ Menu Definitions to uiBlocks ***********************/
2440
2441 const char *ui_menu_enumpropname(char *opname, const char *propname, int retval)
2442 {
2443         wmOperatorType *ot= WM_operatortype_find(opname);
2444         PointerRNA ptr;
2445         PropertyRNA *prop;
2446
2447         if(!ot || !ot->srna)
2448                 return "";
2449         
2450         RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
2451         prop= RNA_struct_find_property(&ptr, propname);
2452         
2453         if(prop) {
2454                 const EnumPropertyItem *item;
2455                 int totitem, i;
2456                 
2457                 RNA_property_enum_items(&ptr, prop, &item, &totitem);
2458                 
2459                 for (i=0; i<totitem; i++) {
2460                         if(item[i].value==retval)
2461                                 return item[i].name;
2462                 }
2463         }
2464
2465         return "";
2466 }
2467
2468 typedef struct MenuItemLevel {
2469         int opcontext;
2470         char *opname;
2471         char *propname;
2472         PointerRNA rnapoin;
2473 } MenuItemLevel;
2474
2475 static uiBlock *ui_block_func_MENU_ITEM(bContext *C, uiPopupBlockHandle *handle, void *arg_info)
2476 {
2477         uiBlock *block;
2478         uiMenuInfo *info= arg_info;
2479         uiPopupMenu *pup;
2480         ScrArea *sa;
2481         ARegion *ar;
2482         
2483         pup= info->pup;
2484         block= pup->block;
2485         
2486         /* block stuff first, need to know the font */
2487         uiBlockSetRegion(block, handle->region);
2488         block->direction= UI_DOWN;
2489
2490         uiBlockLayoutResolve(C, block, NULL, NULL);
2491
2492         if(info->popup) {
2493                 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
2494                 uiBlockSetDirection(block, UI_DOWN);
2495
2496                 /* here we set an offset for the mouse position */
2497                 uiMenuPopupBoundsBlock(block, 1, 0, 1.5*MENU_BUTTON_HEIGHT);
2498         }
2499         else {
2500                 /* for a header menu we set the direction automatic */
2501                 if(!info->slideout) {
2502                         sa= CTX_wm_area(C);
2503                         ar= CTX_wm_region(C);
2504
2505                         if(sa && sa->headertype==HEADERDOWN) {
2506                                 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
2507                                         uiBlockSetDirection(block, UI_TOP);
2508                                         uiBlockFlipOrder(block);
2509                                 }
2510                         }
2511                 }
2512
2513                 uiTextBoundsBlock(block, 50);
2514         }
2515
2516         /* if menu slides out of other menu, override direction */
2517         if(info->slideout)
2518                 uiBlockSetDirection(block, UI_RIGHT);
2519
2520         uiEndBlock(C, block);
2521         
2522         return block;
2523 }
2524
2525 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
2526 {
2527         uiStyle *style= U.uistyles.first;
2528         uiPopupBlockHandle *handle;
2529         uiPopupMenu *pup;
2530         uiMenuInfo info;
2531         
2532         pup= MEM_callocN(sizeof(uiPopupMenu), "menu dummy");
2533         pup->block= uiBeginBlock(C, NULL, "ui_popup_menu_create", UI_EMBOSSP);
2534         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2535         uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
2536
2537         /* create in advance so we can let buttons point to retval already */
2538         pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2539
2540         menu_func(C, pup->layout, arg);
2541         
2542         memset(&info, 0, sizeof(info));
2543         info.pup= pup;
2544         info.slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
2545         
2546         handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_MENU_ITEM, &info);
2547         
2548         MEM_freeN(pup);
2549
2550         return handle;
2551 }
2552
2553 /*************************** Menu Creating API **************************/
2554
2555
2556 /*************************** Popup Menu API **************************/
2557
2558 /* only return handler, and set optional title */
2559 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
2560 {
2561         uiStyle *style= U.uistyles.first;
2562         uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "menu start");
2563         uiBut *but;
2564         
2565         pup->block= uiBeginBlock(C, NULL, "uiPupMenuBegin", UI_EMBOSSP);
2566         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2567         uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
2568
2569         /* create in advance so we can let buttons point to retval already */
2570         pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2571         
2572         /* create title button */
2573         if(title && title[0]) {
2574                 char titlestr[256];
2575                 
2576                 if(icon) {
2577                         sprintf(titlestr, " %s", title);
2578                         uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2579                 }
2580                 else {
2581                         but= uiDefBut(pup->block, LABEL, 0, (char*)title, 0, 0, 200, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2582                         but->flag= UI_TEXT_LEFT;
2583                 }
2584                 
2585                 //uiDefBut(block, SEPR, 0, "", startx, (short)(starty+height)-MENU_SEPR_HEIGHT, width, MENU_SEPR_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2586         }
2587
2588         return pup;
2589 }
2590
2591 /* set the whole structure to work */
2592 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
2593 {
2594         wmWindow *window= CTX_wm_window(C);
2595         uiMenuInfo info;
2596         uiPopupBlockHandle *menu;
2597         
2598         memset(&info, 0, sizeof(info));
2599         info.popup= 1;
2600         info.mx= window->eventstate->x;
2601         info.my= window->eventstate->y;
2602         info.pup= pup;
2603         
2604         menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_MENU_ITEM, &info);
2605         menu->popup= 1;
2606         
2607         UI_add_popup_handlers(C, &window->handlers, menu);
2608         WM_event_add_mousemove(C);
2609         
2610         MEM_freeN(pup);
2611 }
2612
2613 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
2614 {
2615         return pup->layout;
2616 }
2617
2618 /* ************** standard pupmenus *************** */
2619
2620 /* this one can called with operatortype name and operators */
2621 static uiPopupBlockHandle *ui_pup_menu(bContext *C, int maxrow, uiMenuHandleFunc func, void *arg, char *str, ...)
2622 {
2623         wmWindow *window= CTX_wm_window(C);
2624         uiPupMenuInfo info;
2625         uiPopupBlockHandle *menu;
2626
2627         memset(&info, 0, sizeof(info));
2628         info.mx= window->eventstate->x;
2629         info.my= window->eventstate->y;
2630         info.maxrow= maxrow;
2631         info.instr= str;
2632
2633         menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_PUPMENU, &info);
2634         menu->popup= 1;
2635
2636         UI_add_popup_handlers(C, &window->handlers, menu);
2637         WM_event_add_mousemove(C);
2638
2639         menu->popup_func= func;
2640         menu->popup_arg= arg;
2641         
2642         return menu;
2643 }
2644
2645 static void operator_name_cb(bContext *C, void *arg, int retval)
2646 {
2647         const char *opname= arg;
2648
2649         if(opname && retval > 0)
2650                 WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
2651 }
2652
2653 static void vconfirm_opname(bContext *C, char *opname, char *title, char *itemfmt, va_list ap)
2654 {
2655         char *s, buf[512];
2656
2657         s= buf;
2658         if (title) s+= sprintf(s, "%s%%t|", title);
2659         vsprintf(s, itemfmt, ap);
2660
2661         ui_pup_menu(C, 0, operator_name_cb, opname, buf);
2662 }
2663
2664 static void operator_cb(bContext *C, void *arg, int retval)
2665 {
2666         wmOperator *op= arg;
2667         
2668         if(op && retval > 0)
2669                 WM_operator_call(C, op);
2670         else
2671                 WM_operator_free(op);
2672 }
2673
2674 static void confirm_cancel_operator(void *opv)
2675 {
2676         WM_operator_free(opv);
2677 }
2678
2679 static void confirm_operator(bContext *C, wmOperator *op, char *title, char *item)
2680 {
2681         uiPopupBlockHandle *handle;
2682         char *s, buf[512];
2683         
2684         s= buf;
2685         if (title) s+= sprintf(s, "%s%%t|%s", title, item);
2686         
2687         handle= ui_pup_menu(C, 0, operator_cb, op, buf);
2688         handle->cancel_func= confirm_cancel_operator;
2689 }
2690
2691
2692 void uiPupMenuOkee(bContext *C, char *opname, char *str, ...)
2693 {
2694         va_list ap;
2695         char titlestr[256];
2696
2697         sprintf(titlestr, "OK? %%i%d", ICON_QUESTION);
2698
2699         va_start(ap, str);
2700         vconfirm_opname(C, opname, titlestr, str, ap);
2701         va_end(ap);
2702 }
2703
2704
2705 void uiPupMenuSaveOver(bContext *C, wmOperator *op, char *filename)
2706 {
2707         size_t len= strlen(filename);
2708
2709         if(len==0)
2710                 return;
2711
2712         if(filename[len-1]=='/' || filename[len-1]=='\\') {
2713                 uiPupMenuError(C, "Cannot overwrite a directory");
2714                 WM_operator_free(op);
2715                 return;
2716         }
2717         if(BLI_exists(filename)==0)
2718                 operator_cb(C, op, 1);
2719         else
2720                 confirm_operator(C, op, "Save over", filename);
2721 }
2722
2723 void uiPupMenuNotice(bContext *C, char *str, ...)
2724 {
2725         va_list ap;
2726
2727         va_start(ap, str);
2728         vconfirm_opname(C, NULL, NULL, str, ap);
2729         va_end(ap);
2730 }
2731
2732 void uiPupMenuError(bContext *C, char *str, ...)
2733 {
2734         va_list ap;
2735         char nfmt[256];
2736         char titlestr[256];
2737
2738         sprintf(titlestr, "Error %%i%d", ICON_ERROR);
2739
2740         sprintf(nfmt, "%s", str);
2741
2742         va_start(ap, str);
2743         vconfirm_opname(C, NULL, titlestr, nfmt, ap);
2744         va_end(ap);
2745 }
2746
2747 void uiPupMenuReports(bContext *C, ReportList *reports)
2748 {
2749         Report *report;
2750         DynStr *ds;
2751         char *str;
2752
2753         if(!reports || !reports->list.first)
2754                 return;
2755         if(!CTX_wm_window(C))
2756                 return;
2757
2758         ds= BLI_dynstr_new();
2759
2760         for(report=reports->list.first; report; report=report->next) {
2761                 if(report->type >= RPT_ERROR)
2762                         BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
2763                 else if(report->type >= RPT_WARNING)
2764                         BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
2765                 else if(report->type >= RPT_INFO)
2766                         BLI_dynstr_appendf(ds, "Info %%t|%s", report->message);
2767         }
2768
2769         str= BLI_dynstr_get_cstring(ds);
2770         ui_pup_menu(C, 0, NULL, NULL, str);
2771         MEM_freeN(str);
2772
2773         BLI_dynstr_free(ds);
2774 }
2775
2776 /*************************** Popup Block API **************************/
2777
2778 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, char *opname, int opcontext)
2779 {
2780         wmWindow *window= CTX_wm_window(C);
2781         uiPopupBlockHandle *handle;
2782         
2783         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2784         handle->popup= 1;
2785         handle->optype= (opname)? WM_operatortype_find(opname): NULL;
2786         handle->opcontext= opcontext;
2787         
2788         UI_add_popup_handlers(C, &window->handlers, handle);
2789         WM_event_add_mousemove(C);
2790 }
2791
2792 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2793 {
2794         uiPupBlockO(C, func, arg, NULL, 0);
2795 }
2796
2797 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2798 {
2799         wmWindow *window= CTX_wm_window(C);
2800         uiPopupBlockHandle *handle;
2801         
2802         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2803         handle->popup= 1;
2804         handle->retvalue= 1;
2805
2806         handle->popup_arg= op;
2807         handle->popup_func= operator_cb;
2808         handle->cancel_func= confirm_cancel_operator;
2809         handle->opcontext= opcontext;
2810         
2811         UI_add_popup_handlers(C, &window->handlers, handle);
2812         WM_event_add_mousemove(C);
2813 }