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