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