Avoid reset the H and S value in the color picker when V is equal to zero!
[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", (unsigned int)(rgb_gamma[0]*255.0), (unsigned int)(rgb_gamma[1]*255.0), (unsigned int)(rgb_gamma[2]*255.0));
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, "");
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, "");
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, "");
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, "");
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         RNA_property_float_ui_range(ptr, prop, &min, &max, &step, &precision);
1799         RNA_property_float_get_array(ptr, prop, rgb);
1800         rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
1801
1802         switch (U.color_picker_type) {
1803                 case USER_CP_CIRCLE:
1804                         circle_picker(block, ptr, propname);
1805                         break;
1806                 case USER_CP_SQUARE_SV:
1807                         square_picker(block, ptr, propname, UI_GRAD_SV);
1808                         break;
1809                 case USER_CP_SQUARE_HS:
1810                         square_picker(block, ptr, propname, UI_GRAD_HS);
1811                         break;
1812                 case USER_CP_SQUARE_HV:
1813                         square_picker(block, ptr, propname, UI_GRAD_HV);
1814                         break;
1815         }
1816         
1817         /* mode */
1818         uiBlockBeginAlign(block);
1819         bt= uiDefButS(block, ROW, 0, "RGB",     0, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 0.0, 0, 0, "");
1820         uiButSetFunc(bt, do_picker_new_mode_cb, bt, rgb);
1821         bt= uiDefButS(block, ROW, 0, "HSV",     width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 1.0, 0, 0, "");
1822         uiButSetFunc(bt, do_picker_new_mode_cb, bt, hsv);
1823         bt= uiDefButS(block, ROW, 0, "Hex",     2*width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 2.0, 0, 0, "");
1824         uiButSetFunc(bt, do_picker_new_mode_cb, bt, hexcol);
1825         uiBlockEndAlign(block);
1826
1827         bt= uiDefIconButO(block, BUT, "UI_OT_eyedropper", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, butwidth+10, -60, UI_UNIT_X, UI_UNIT_Y, NULL);
1828         uiButSetFunc(bt, close_popup_cb, bt, NULL);
1829         
1830         /* RGB values */
1831         uiBlockBeginAlign(block);
1832         bt= uiDefButR(block, NUMSLI, 0, "R ",   0, -60, butwidth, UI_UNIT_Y, ptr, propname, 0, 0.0, 0.0, 0, 0, "");
1833         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1834         bt= uiDefButR(block, NUMSLI, 0, "G ",   0, -80, butwidth, UI_UNIT_Y, ptr, propname, 1, 0.0, 0.0, 0, 0, "");
1835         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1836         bt= uiDefButR(block, NUMSLI, 0, "B ",   0, -100, butwidth, UI_UNIT_Y, ptr, propname, 2, 0.0, 0.0, 0, 0, "");
1837         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1838         // could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", 0);
1839         // but need to use uiButSetFunc for updating other fake buttons
1840         
1841         /* HSV values */
1842         uiBlockBeginAlign(block);
1843         bt= uiDefButF(block, NUMSLI, 0, "H ",   0, -60, butwidth, UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, "");
1844         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
1845         bt= uiDefButF(block, NUMSLI, 0, "S ",   0, -80, butwidth, UI_UNIT_Y, hsv+1, 0.0, 1.0, 10, 3, "");
1846         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
1847         bt= uiDefButF(block, NUMSLI, 0, "V ",   0, -100, butwidth, UI_UNIT_Y, hsv+2, 0.0, max, 10, 3, "");
1848         uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
1849         uiBlockEndAlign(block);
1850         
1851         rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
1852         
1853         sprintf(hexcol, "%02X%02X%02X", (unsigned int)(rgb_gamma[0]*255.0), (unsigned int)(rgb_gamma[1]*255.0), (unsigned int)(rgb_gamma[2]*255.0));    
1854
1855         bt= uiDefBut(block, TEX, 0, "Hex: ", 0, -60, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
1856         uiButSetFunc(bt, do_hex_rna_cb, bt, hexcol);
1857         uiDefBut(block, LABEL, 0, "(Gamma Corrected)", 0, -80, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1858
1859         picker_new_hide_reveal(block, colormode);
1860 }
1861
1862
1863 static int ui_picker_small_wheel(const bContext *C, uiBlock *block, wmEvent *event)
1864 {
1865         float add= 0.0f;
1866         
1867         if(event->type==WHEELUPMOUSE)
1868                 add= 0.05f;
1869         else if(event->type==WHEELDOWNMOUSE)
1870                 add= -0.05f;
1871         
1872         if(add!=0.0f) {
1873                 uiBut *but;
1874                 
1875                 for(but= block->buttons.first; but; but= but->next) {
1876                         if(but->type==HSVCUBE && but->active==NULL) {
1877                                 uiPopupBlockHandle *popup= block->handle;
1878                                 float col[3];
1879                                 
1880                                 ui_get_but_vectorf(but, col);
1881                                 
1882                                 rgb_to_hsv(col[0], col[1], col[2], but->hsv, but->hsv+1, but->hsv+2);
1883                                 but->hsv[2]= CLAMPIS(but->hsv[2]+add, 0.0f, 1.0f);
1884                                 hsv_to_rgb(but->hsv[0], but->hsv[1], but->hsv[2], col, col+1, col+2);
1885
1886                                 ui_set_but_vectorf(but, col);
1887                                 
1888                                 ui_update_block_buts_rgb(block, col, NULL);
1889                                 if(popup)
1890                                         popup->menuretval= UI_RETURN_UPDATE;
1891                                 
1892                                 return 1;
1893                         }
1894                 }
1895         }
1896         return 0;
1897 }
1898
1899 uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
1900 {
1901         uiBut *but= arg_but;
1902         uiBlock *block;
1903         
1904         block= uiBeginBlock(C, handle->region, "colorpicker", UI_EMBOSS);
1905         
1906         if (but->rnaprop) {
1907                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
1908                         block->color_profile = BLI_PR_NONE;
1909                 }
1910         }
1911         
1912         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1913         
1914         VECCOPY(handle->retvec, but->editvec);
1915
1916         uiBlockPicker(block, handle->retvec, &but->rnapoin, but->rnaprop);
1917         block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN;
1918         uiBoundsBlock(block, 10);
1919         
1920         block->block_event_func= ui_picker_small_wheel;
1921         
1922         /* and lets go */
1923         block->direction= UI_TOP;
1924         
1925         return block;
1926 }
1927
1928 /************************ Popup Menu Memory ****************************/
1929
1930 static int ui_popup_string_hash(char *str)
1931 {
1932         /* sometimes button contains hotkey, sometimes not, strip for proper compare */
1933         int hash;
1934         char *delimit= strchr(str, '|');
1935
1936         if(delimit) *delimit= 0;
1937         hash= BLI_ghashutil_strhash(str);
1938         if(delimit) *delimit= '|';
1939
1940         return hash;
1941 }
1942
1943 static int ui_popup_menu_hash(char *str)
1944 {
1945         return BLI_ghashutil_strhash(str);
1946 }
1947
1948 /* but == NULL read, otherwise set */
1949 uiBut *ui_popup_menu_memory(uiBlock *block, uiBut *but)
1950 {
1951         static int mem[256], first=1;
1952         int hash= block->puphash;
1953         
1954         if(first) {
1955                 /* init */
1956                 memset(mem, -1, sizeof(mem));
1957                 first= 0;
1958         }
1959
1960         if(but) {
1961                 /* set */
1962                 mem[hash & 255 ]= ui_popup_string_hash(but->str);
1963                 return NULL;
1964         }
1965         else {
1966                 /* get */
1967                 for(but=block->buttons.first; but; but=but->next)
1968                         if(ui_popup_string_hash(but->str) == mem[hash & 255])
1969                                 return but;
1970
1971                 return NULL;
1972         }
1973 }
1974
1975 /******************** Popup Menu with callback or string **********************/
1976
1977 struct uiPopupMenu {
1978         uiBlock *block;
1979         uiLayout *layout;
1980         uiBut *but;
1981
1982         int mx, my, popup, slideout;
1983         int startx, starty, maxrow;
1984
1985         uiMenuCreateFunc menu_func;
1986         void *menu_arg;
1987 };
1988
1989 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
1990 {
1991         uiBlock *block;
1992         uiBut *bt;
1993         ScrArea *sa;
1994         ARegion *ar;
1995         uiPopupMenu *pup= arg_pup;
1996         int offset[2], direction, minwidth, width, height, flip;
1997
1998         if(pup->menu_func) {
1999                 pup->block->handle= handle;
2000                 pup->menu_func(C, pup->layout, pup->menu_arg);
2001                 pup->block->handle= NULL;
2002         }
2003
2004         if(pup->but) {
2005                 /* minimum width to enforece */
2006                 minwidth= pup->but->x2 - pup->but->x1;
2007
2008                 if(pup->but->type == PULLDOWN || pup->but->menu_create_func) {
2009                         direction= UI_DOWN;
2010                         flip= 1;
2011                 }
2012                 else {
2013                         direction= UI_TOP;
2014                         flip= 0;
2015                 }
2016         }
2017         else {
2018                 minwidth= 50;
2019                 direction= UI_DOWN;
2020                 flip= 1;
2021         }
2022
2023         block= pup->block;
2024         
2025         /* in some cases we create the block before the region,
2026            so we set it delayed here if necessary */
2027         if(BLI_findindex(&handle->region->uiblocks, block) == -1)
2028                 uiBlockSetRegion(block, handle->region);
2029
2030         block->direction= direction;
2031
2032         uiBlockLayoutResolve(block, &width, &height);
2033
2034         uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2035         
2036         if(pup->popup) {
2037                 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
2038                 uiBlockSetDirection(block, direction);
2039
2040                 /* offset the mouse position, possibly based on earlier selection */
2041                 if((block->flag & UI_BLOCK_POPUP_MEMORY) &&
2042                         (bt= ui_popup_menu_memory(block, NULL))) {
2043                         /* position mouse on last clicked item, at 0.8*width of the
2044                            button, so it doesn't overlap the text too much, also note
2045                            the offset is negative because we are inverse moving the
2046                            block to be under the mouse */
2047                         offset[0]= -(bt->x1 + 0.8f*(bt->x2 - bt->x1));
2048                         offset[1]= -(bt->y1 + 0.5f*MENU_BUTTON_HEIGHT);
2049                 }
2050                 else {
2051                         /* position mouse at 0.8*width of the button and below the tile
2052                            on the first item */
2053                         offset[0]= 0;
2054                         for(bt=block->buttons.first; bt; bt=bt->next)
2055                                 offset[0]= MIN2(offset[0], -(bt->x1 + 0.8f*(bt->x2 - bt->x1)));
2056
2057                         offset[1]= 1.5*MENU_BUTTON_HEIGHT;
2058                 }
2059
2060                 block->minbounds= minwidth;
2061                 uiMenuPopupBoundsBlock(block, 1, offset[0], offset[1]);
2062         }
2063         else {
2064                 /* for a header menu we set the direction automatic */
2065                 if(!pup->slideout && flip) {
2066                         sa= CTX_wm_area(C);
2067                         ar= CTX_wm_region(C);
2068
2069                         if(sa && sa->headertype==HEADERDOWN) {
2070                                 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
2071                                         uiBlockSetDirection(block, UI_TOP);
2072                                         uiBlockFlipOrder(block);
2073                                 }
2074                         }
2075                 }
2076
2077                 block->minbounds= minwidth;
2078                 uiTextBoundsBlock(block, 50);
2079         }
2080
2081         /* if menu slides out of other menu, override direction */
2082         if(pup->slideout)
2083                 uiBlockSetDirection(block, UI_RIGHT);
2084
2085         uiEndBlock(C, block);
2086
2087         return pup->block;
2088 }
2089
2090 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg, char *str)
2091 {
2092         wmWindow *window= CTX_wm_window(C);
2093         uiStyle *style= U.uistyles.first;
2094         uiPopupBlockHandle *handle;
2095         uiPopupMenu *pup;
2096         
2097         pup= MEM_callocN(sizeof(uiPopupMenu), "menu dummy");
2098         pup->block= uiBeginBlock(C, NULL, "ui_button_menu_create", UI_EMBOSSP);
2099         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2100         pup->slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
2101         pup->but= but;
2102         uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
2103
2104         if(!but) {
2105                 /* no button to start from, means we are a popup */
2106                 pup->mx= window->eventstate->x;
2107                 pup->my= window->eventstate->y;
2108                 pup->popup= 1;
2109                 pup->block->flag |= UI_BLOCK_NO_FLIP;
2110         }
2111
2112         if(str) {
2113                 /* menu is created from a string */
2114                 pup->menu_func= ui_block_func_MENUSTR;
2115                 pup->menu_arg= str;
2116         }
2117         else {
2118                 /* menu is created from a callback */
2119                 pup->menu_func= menu_func;
2120                 pup->menu_arg= arg;
2121         }
2122         
2123         handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
2124
2125         if(!but) {
2126                 handle->popup= 1;
2127
2128                 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2129                 WM_event_add_mousemove(C);
2130         }
2131         
2132         MEM_freeN(pup);
2133
2134         return handle;
2135 }
2136
2137 /******************** Popup Menu API with begin and end ***********************/
2138
2139 /* only return handler, and set optional title */
2140 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
2141 {
2142         uiStyle *style= U.uistyles.first;
2143         uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "popup menu");
2144         uiBut *but;
2145         
2146         pup->block= uiBeginBlock(C, NULL, "uiPupMenuBegin", UI_EMBOSSP);
2147         pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
2148         pup->block->puphash= ui_popup_menu_hash((char*)title);
2149         pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2150         uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
2151
2152         /* create in advance so we can let buttons point to retval already */
2153         pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2154         
2155         /* create title button */
2156         if(title && title[0]) {
2157                 char titlestr[256];
2158                 
2159                 if(icon) {
2160                         sprintf(titlestr, " %s", title);
2161                         uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2162                 }
2163                 else {
2164                         but= uiDefBut(pup->block, LABEL, 0, (char*)title, 0, 0, 200, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
2165                         but->flag= UI_TEXT_LEFT;
2166                 }
2167         }
2168
2169         return pup;
2170 }
2171
2172 /* set the whole structure to work */
2173 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
2174 {
2175         wmWindow *window= CTX_wm_window(C);
2176         uiPopupBlockHandle *menu;
2177         
2178         pup->popup= 1;
2179         pup->mx= window->eventstate->x;
2180         pup->my= window->eventstate->y;
2181         
2182         menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
2183         menu->popup= 1;
2184         
2185         UI_add_popup_handlers(C, &window->modalhandlers, menu);
2186         WM_event_add_mousemove(C);
2187         
2188         MEM_freeN(pup);
2189 }
2190
2191 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
2192 {
2193         return pup->layout;
2194 }
2195
2196 /*************************** Standard Popup Menus ****************************/
2197
2198 static void operator_name_cb(bContext *C, void *arg, int retval)
2199 {
2200         const char *opname= arg;
2201
2202         if(opname && retval > 0)
2203                 WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
2204 }
2205
2206 static void operator_cb(bContext *C, void *arg, int retval)
2207 {
2208         wmOperator *op= arg;
2209         
2210         if(op && retval > 0)
2211                 WM_operator_call(C, op);
2212         else
2213                 WM_operator_free(op);
2214 }
2215
2216 static void confirm_cancel_operator(void *opv)
2217 {
2218         WM_operator_free(opv);
2219 }
2220
2221 static void vconfirm_opname(bContext *C, char *opname, char *title, char *itemfmt, va_list ap)
2222 {
2223         uiPopupBlockHandle *handle;
2224         char *s, buf[512];
2225
2226         s= buf;
2227         if (title) s+= sprintf(s, "%s%%t|", title);
2228         vsprintf(s, itemfmt, ap);
2229
2230         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2231
2232         handle->popup_func= operator_name_cb;
2233         handle->popup_arg= opname;
2234 }
2235
2236 static void confirm_operator(bContext *C, wmOperator *op, char *title, char *item)
2237 {
2238         uiPopupBlockHandle *handle;
2239         char *s, buf[512];
2240         
2241         s= buf;
2242         if (title) s+= sprintf(s, "%s%%t|%s", title, item);
2243         
2244         handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2245
2246         handle->popup_func= operator_cb;
2247         handle->popup_arg= op;
2248         handle->cancel_func= confirm_cancel_operator;
2249 }
2250
2251 void uiPupMenuOkee(bContext *C, char *opname, char *str, ...)
2252 {
2253         va_list ap;
2254         char titlestr[256];
2255
2256         sprintf(titlestr, "OK? %%i%d", ICON_QUESTION);
2257
2258         va_start(ap, str);
2259         vconfirm_opname(C, opname, titlestr, str, ap);
2260         va_end(ap);
2261 }
2262
2263 void uiPupMenuSaveOver(bContext *C, wmOperator *op, char *filename)
2264 {
2265         size_t len= strlen(filename);
2266
2267         if(len==0)
2268                 return;
2269
2270         if(filename[len-1]=='/' || filename[len-1]=='\\') {
2271                 uiPupMenuError(C, "Cannot overwrite a directory");
2272                 WM_operator_free(op);
2273                 return;
2274         }
2275         if(BLI_exists(filename)==0)
2276                 operator_cb(C, op, 1);
2277         else
2278                 confirm_operator(C, op, "Save Over", filename);
2279 }
2280
2281 void uiPupMenuNotice(bContext *C, char *str, ...)
2282 {
2283         va_list ap;
2284
2285         va_start(ap, str);
2286         vconfirm_opname(C, NULL, NULL, str, ap);
2287         va_end(ap);
2288 }
2289
2290 void uiPupMenuError(bContext *C, char *str, ...)
2291 {
2292         va_list ap;
2293         char nfmt[256];
2294         char titlestr[256];
2295
2296         sprintf(titlestr, "Error %%i%d", ICON_ERROR);
2297
2298         sprintf(nfmt, "%s", str);
2299
2300         va_start(ap, str);
2301         vconfirm_opname(C, NULL, titlestr, nfmt, ap);
2302         va_end(ap);
2303 }
2304
2305 void uiPupMenuReports(bContext *C, ReportList *reports)
2306 {
2307         Report *report;
2308         DynStr *ds;
2309         char *str;
2310
2311         if(!reports || !reports->list.first)
2312                 return;
2313         if(!CTX_wm_window(C))
2314                 return;
2315
2316         ds= BLI_dynstr_new();
2317
2318         for(report=reports->list.first; report; report=report->next) {
2319                 if(report->type <= reports->printlevel)
2320                         ; /* pass */
2321                 else if(report->type >= RPT_ERROR)
2322                         BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
2323                 else if(report->type >= RPT_WARNING)
2324                         BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
2325                 else if(report->type >= RPT_INFO)
2326                         BLI_dynstr_appendf(ds, "Info %%i%d%%t|%s", ICON_INFO, report->message);
2327         }
2328
2329         str= BLI_dynstr_get_cstring(ds);
2330         if(str[0] != '\0')
2331                 ui_popup_menu_create(C, NULL, NULL, NULL, NULL, str);
2332         MEM_freeN(str);
2333
2334         BLI_dynstr_free(ds);
2335 }
2336
2337 void uiPupMenuInvoke(bContext *C, const char *idname)
2338 {
2339         uiPopupMenu *pup;
2340         uiLayout *layout;
2341         Menu menu;
2342         MenuType *mt= WM_menutype_find(idname, TRUE);
2343
2344         if(mt==NULL) {
2345                 printf("uiPupMenuInvoke: named menu \"%s\" not found\n", idname);
2346                 return;
2347         }
2348
2349         if(mt->poll && mt->poll(C, mt)==0)
2350                 return;
2351
2352         pup= uiPupMenuBegin(C, mt->label, 0);
2353         layout= uiPupMenuLayout(pup);
2354
2355         menu.layout= layout;
2356         menu.type= mt;
2357
2358         mt->draw(C, &menu);
2359
2360         uiPupMenuEnd(C, pup);
2361 }
2362
2363
2364 /*************************** Popup Block API **************************/
2365
2366 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, char *opname, int opcontext)
2367 {
2368         wmWindow *window= CTX_wm_window(C);
2369         uiPopupBlockHandle *handle;
2370         
2371         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2372         handle->popup= 1;
2373         handle->optype= (opname)? WM_operatortype_find(opname, 0): NULL;
2374         handle->opcontext= opcontext;
2375         
2376         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2377         WM_event_add_mousemove(C);
2378 }
2379
2380 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2381 {
2382         uiPupBlockO(C, func, arg, NULL, 0);
2383 }
2384
2385 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2386 {
2387         wmWindow *window= CTX_wm_window(C);
2388         uiPopupBlockHandle *handle;
2389         
2390         handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2391         handle->popup= 1;
2392         handle->retvalue= 1;
2393
2394         handle->popup_arg= op;
2395         handle->popup_func= operator_cb;
2396         handle->cancel_func= confirm_cancel_operator;
2397         handle->opcontext= opcontext;
2398         
2399         UI_add_popup_handlers(C, &window->modalhandlers, handle);
2400         WM_event_add_mousemove(C);
2401 }
2402
2403 void uiPupBlockClose(bContext *C, uiBlock *block)
2404 {
2405         if(block->handle) {
2406                 UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, block->handle);
2407                 ui_popup_block_free(C, block->handle);
2408         }
2409 }
2410