Blender V2.61 - r43446

interface_regions.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008 Blender Foundation.
00019  * All rights reserved.
00020  * 
00021  * Contributor(s): Blender Foundation
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00032 #include <stdarg.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <assert.h>
00036 
00037 #include "MEM_guardedalloc.h"
00038 
00039 #include "DNA_userdef_types.h"
00040 
00041 #include "BLI_math.h"
00042 #include "BLI_blenlib.h"
00043 #include "BLI_utildefines.h"
00044 #include "BLI_dynstr.h"
00045 #include "BLI_ghash.h"
00046 
00047 #include "BKE_context.h"
00048 #include "BKE_screen.h"
00049 
00050 #include "WM_api.h"
00051 #include "WM_types.h"
00052 #include "wm_draw.h"
00053 #include "wm_subwindow.h"
00054 #include "wm_window.h"
00055 
00056 #include "RNA_access.h"
00057 
00058 #include "BIF_gl.h"
00059 
00060 #include "UI_interface.h"
00061 #include "UI_interface_icons.h"
00062 #include "UI_view2d.h"
00063 
00064 #include "BLF_api.h"
00065 #include "BLF_translation.h"
00066 
00067 #include "ED_screen.h"
00068 
00069 #include "interface_intern.h"
00070 
00071 #define MENU_SEPR_HEIGHT    6
00072 #define B_NOP               -1
00073 #define MENU_SHADOW_SIDE    8
00074 #define MENU_SHADOW_BOTTOM  10
00075 #define MENU_TOP            8
00076 
00077 /*********************** Menu Data Parsing ********************* */
00078 
00079 typedef struct MenuEntry {
00080     const char *str;
00081     int retval;
00082     int icon;
00083     int sepr;
00084 } MenuEntry;
00085 
00086 typedef struct MenuData {
00087     const char *instr;
00088     const char *title;
00089     int titleicon;
00090     
00091     MenuEntry *items;
00092     int nitems, itemssize;
00093 } MenuData;
00094 
00095 static MenuData *menudata_new(const char *instr)
00096 {
00097     MenuData *md= MEM_mallocN(sizeof(*md), "MenuData");
00098 
00099     md->instr= instr;
00100     md->title= NULL;
00101     md->titleicon= 0;
00102     md->items= NULL;
00103     md->nitems= md->itemssize= 0;
00104     
00105     return md;
00106 }
00107 
00108 static void menudata_set_title(MenuData *md, const char *title, int titleicon)
00109 {
00110     if (!md->title)
00111         md->title= title;
00112     if (!md->titleicon)
00113         md->titleicon= titleicon;
00114 }
00115 
00116 static void menudata_add_item(MenuData *md, const char *str, int retval, int icon, int sepr)
00117 {
00118     if (md->nitems==md->itemssize) {
00119         int nsize= md->itemssize?(md->itemssize<<1):1;
00120         MenuEntry *oitems= md->items;
00121         
00122         md->items= MEM_mallocN(nsize*sizeof(*md->items), "md->items");
00123         if (oitems) {
00124             memcpy(md->items, oitems, md->nitems*sizeof(*md->items));
00125             MEM_freeN(oitems);
00126         }
00127         
00128         md->itemssize= nsize;
00129     }
00130     
00131     md->items[md->nitems].str= str;
00132     md->items[md->nitems].retval= retval;
00133     md->items[md->nitems].icon= icon;
00134     md->items[md->nitems].sepr= sepr;
00135     md->nitems++;
00136 }
00137 
00138 static void menudata_free(MenuData *md)
00139 {
00140     MEM_freeN((void *)md->instr);
00141     if (md->items)
00142         MEM_freeN(md->items);
00143     MEM_freeN(md);
00144 }
00145 
00159 static MenuData *decompose_menu_string(const char *str)
00160 {
00161     char *instr= BLI_strdup(str);
00162     MenuData *md= menudata_new(instr);
00163     const char *nitem= NULL;
00164     char *s= instr;
00165     int nicon=0, nretval= 1, nitem_is_title= 0, nitem_is_sepr= 0;
00166     
00167     while (1) {
00168         char c= *s;
00169 
00170         if (c=='%') {
00171             if (s[1]=='x') {
00172                 nretval= atoi(s+2);
00173 
00174                 *s= '\0';
00175                 s++;
00176             } else if (s[1]=='t') {
00177                 nitem_is_title= (s != instr); /* check for empty title */
00178 
00179                 *s= '\0';
00180                 s++;
00181             } else if (s[1]=='l') {
00182                 nitem_is_sepr= 1;
00183                 if(!nitem) nitem= "";
00184 
00185                 *s= '\0';
00186                 s++;
00187             } else if (s[1]=='i') {
00188                 nicon= atoi(s+2);
00189                 
00190                 *s= '\0';
00191                 s++;
00192             }
00193         } else if (c=='|' || c == '\n' || c=='\0') {
00194             if (nitem) {
00195                 *s= '\0';
00196 
00197                 if(nitem_is_title) {
00198                     menudata_set_title(md, nitem, nicon);
00199                     nitem_is_title= 0;
00200                 }
00201                 else if(nitem_is_sepr) {
00202                     /* prevent separator to get a value */
00203                     menudata_add_item(md, nitem, -1, nicon, 1);
00204                     nretval= md->nitems+1;
00205                     nitem_is_sepr= 0;
00206                 }
00207                 else {
00208                     menudata_add_item(md, nitem, nretval, nicon, 0);
00209                     nretval= md->nitems+1;
00210                 } 
00211                 
00212                 nitem= NULL;
00213                 nicon= 0;
00214             }
00215             
00216             if (c=='\0') {
00217                 break;
00218             }
00219         } else if (!nitem) {
00220             nitem= s;
00221         }
00222 
00223         s++;
00224     }
00225     
00226     return md;
00227 }
00228 
00229 void ui_set_name_menu(uiBut *but, int value)
00230 {
00231     MenuData *md;
00232     int i;
00233     
00234     md= decompose_menu_string(but->str);
00235     for (i=0; i<md->nitems; i++) {
00236         if (md->items[i].retval==value) {
00237             BLI_strncpy(but->drawstr, md->items[i].str, sizeof(but->drawstr));
00238             break;
00239         }
00240     }
00241     
00242     menudata_free(md);
00243 }
00244 
00245 int ui_step_name_menu(uiBut *but, int step)
00246 {
00247     MenuData *md;
00248     int value= ui_get_but_val(but);
00249     int i;
00250     
00251     md= decompose_menu_string(but->str);
00252     for (i=0; i<md->nitems; i++)
00253         if (md->items[i].retval==value)
00254             break;
00255     
00256     if(step==1) {
00257         /* skip separators */
00258         for(; i<md->nitems-1; i++) {
00259             if(md->items[i+1].retval != -1) {
00260                 value= md->items[i+1].retval;
00261                 break;
00262             }
00263         }
00264     }
00265     else {
00266         if(i>0) {
00267             /* skip separators */
00268             for(; i>0; i--) {
00269                 if(md->items[i-1].retval != -1) {
00270                     value= md->items[i-1].retval;
00271                     break;
00272                 }
00273             }
00274         }
00275     }
00276     
00277     menudata_free(md);
00278         
00279     return value;
00280 }
00281 
00282 
00283 /******************** Creating Temporary regions ******************/
00284 
00285 static ARegion *ui_add_temporary_region(bScreen *sc)
00286 {
00287     ARegion *ar;
00288 
00289     ar= MEM_callocN(sizeof(ARegion), "area region");
00290     BLI_addtail(&sc->regionbase, ar);
00291 
00292     ar->regiontype= RGN_TYPE_TEMPORARY;
00293     ar->alignment= RGN_ALIGN_FLOAT;
00294 
00295     return ar;
00296 }
00297 
00298 static void ui_remove_temporary_region(bContext *C, bScreen *sc, ARegion *ar)
00299 {
00300     if(CTX_wm_window(C))
00301         wm_draw_region_clear(CTX_wm_window(C), ar);
00302 
00303     ED_region_exit(C, ar);
00304     BKE_area_region_free(NULL, ar);     /* NULL: no spacetype */
00305     BLI_freelinkN(&sc->regionbase, ar);
00306 }
00307 
00308 /************************* Creating Tooltips **********************/
00309 
00310 #define MAX_TOOLTIP_LINES 8
00311 
00312 typedef struct uiTooltipData {
00313     rcti bbox;
00314     uiFontStyle fstyle;
00315     char lines[MAX_TOOLTIP_LINES][512];
00316     unsigned int color[MAX_TOOLTIP_LINES];
00317     int totline;
00318     int toth, spaceh, lineh;
00319 } uiTooltipData;
00320 
00321 static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
00322 {
00323     uiTooltipData *data= ar->regiondata;
00324     rcti bbox= data->bbox;
00325     int a;
00326     
00327     ui_draw_menu_back(UI_GetStyle(), NULL, &data->bbox);
00328     
00329     /* draw text */
00330     uiStyleFontSet(&data->fstyle);
00331 
00332     bbox.ymax= bbox.ymax - 0.5f*((bbox.ymax - bbox.ymin) - data->toth);
00333     bbox.ymin= bbox.ymax - data->lineh;
00334 
00335     for(a=0; a<data->totline; a++) {
00336         cpack(data->color[a]);
00337         uiStyleFontDraw(&data->fstyle, &bbox, data->lines[a]);
00338         bbox.ymin -= data->lineh + data->spaceh;
00339         bbox.ymax -= data->lineh + data->spaceh;
00340     }
00341 }
00342 
00343 static void ui_tooltip_region_free_cb(ARegion *ar)
00344 {
00345     uiTooltipData *data;
00346 
00347     data= ar->regiondata;
00348     MEM_freeN(data);
00349     ar->regiondata= NULL;
00350 }
00351 
00352 ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
00353 {
00354     uiStyle *style= UI_GetStyle();
00355     static ARegionType type;
00356     ARegion *ar;
00357     uiTooltipData *data;
00358     IDProperty *prop;
00359     char buf[512];
00360     float fonth, fontw, aspect= but->block->aspect;
00361     float x1f, x2f, y1f, y2f;
00362     int x1, x2, y1, y2, winx, winy, ofsx, ofsy, w, h, a;
00363 
00364     if(but->flag & UI_BUT_NO_TOOLTIP)
00365         return NULL;
00366 
00367     /* create tooltip data */
00368     data= MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
00369 
00370     /* special case, enum rna buttons only have enum item description, use general enum description too before the specific one */
00371     if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
00372         const char *descr= RNA_property_description(but->rnaprop);
00373         if(descr && descr[0]) {
00374             BLI_strncpy(data->lines[data->totline], descr, sizeof(data->lines[0]));
00375             data->color[data->totline]= 0xFFFFFF;
00376             data->totline++;
00377         }
00378 
00379         if(ELEM(but->type, ROW, MENU)) {
00380             EnumPropertyItem *item;
00381             int i, totitem, free;
00382             int value = (but->type == ROW)? but->hardmax: ui_get_but_val(but);
00383 
00384             RNA_property_enum_items_gettexted(C, &but->rnapoin, but->rnaprop, &item, &totitem, &free);
00385 
00386             for(i=0; i<totitem; i++) {
00387                 if(item[i].identifier[0] && item[i].value == value) {
00388                     if(item[i].description && item[i].description[0]) {
00389                         BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), "%s: %s", item[i].name, item[i].description);
00390                         data->color[data->totline]= 0xDDDDDD;
00391                         data->totline++;
00392                     }
00393                     break;
00394                 }
00395             }
00396 
00397             if(free)
00398                 MEM_freeN(item);
00399         }
00400     }
00401     
00402     if(but->tip && but->tip[0] != '\0') {
00403         BLI_strncpy(data->lines[data->totline], but->tip, sizeof(data->lines[0]));
00404         data->color[data->totline]= 0xFFFFFF;
00405         data->totline++;
00406     }
00407 
00408     if(but->optype && !(but->block->flag & UI_BLOCK_LOOP)) {
00409         /* operator keymap (not menus, they already have it) */
00410         prop= (but->opptr)? but->opptr->data: NULL;
00411 
00412         if(WM_key_event_operator_string(C, but->optype->idname, but->opcontext, prop, TRUE,
00413                                         buf, sizeof(buf)))
00414         {
00415             BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), buf);
00416             data->color[data->totline]= 0x888888;
00417             data->totline++;
00418         }
00419     }
00420 
00421     if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
00422         /* full string */
00423         ui_get_but_string(but, buf, sizeof(buf));
00424         if(buf[0]) {
00425             BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf);
00426             data->color[data->totline]= 0x888888;
00427             data->totline++;
00428         }
00429     }
00430 
00431     if(but->rnaprop) {
00432         int unit_type= uiButGetUnitType(but);
00433         
00434         if (unit_type == PROP_UNIT_ROTATION) {
00435             if (RNA_property_type(but->rnaprop) == PROP_FLOAT) {
00436                 float value= RNA_property_array_check(but->rnaprop) ? RNA_property_float_get_index(&but->rnapoin, but->rnaprop, but->rnaindex) : RNA_property_float_get(&but->rnapoin, but->rnaprop);
00437                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value);
00438                 data->color[data->totline]= 0x888888;
00439                 data->totline++;
00440             }
00441         }
00442         
00443         if(but->flag & UI_BUT_DRIVEN) {
00444             if(ui_but_anim_expression_get(but, buf, sizeof(buf))) {
00445                 /* expression */
00446                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf);
00447                 data->color[data->totline]= 0x888888;
00448                 data->totline++;
00449             }
00450         }
00451 
00452         /* rna info */
00453         if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
00454             BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s.%s"), RNA_struct_identifier(but->rnapoin.type), RNA_property_identifier(but->rnaprop));
00455             data->color[data->totline]= 0x888888;
00456             data->totline++;
00457         }
00458         
00459         if(but->rnapoin.id.data) {
00460             ID *id= but->rnapoin.id.data;
00461             if(id->lib && id->lib->name) {
00462                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name);
00463                 data->color[data->totline]= 0x888888;
00464                 data->totline++;
00465             }
00466         }
00467     }
00468     else if (but->optype) {
00469         PointerRNA *opptr;
00470         char *str;
00471         opptr= uiButGetOperatorPtrRNA(but); /* allocated when needed, the button owns it */
00472 
00473         str= WM_operator_pystring(C, but->optype, opptr, 0);
00474 
00475         /* operator info */
00476         if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
00477             BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str);
00478             data->color[data->totline]= 0x888888;
00479             data->totline++;
00480         }
00481 
00482         MEM_freeN(str);
00483 
00484         /* second check if we are disabled - why */
00485         if(but->flag & UI_BUT_DISABLED) {
00486             const char *poll_msg;
00487             CTX_wm_operator_poll_msg_set(C, NULL);
00488             WM_operator_poll_context(C, but->optype, but->opcontext);
00489             poll_msg= CTX_wm_operator_poll_msg_get(C);
00490             if(poll_msg) {
00491                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), poll_msg);
00492                 data->color[data->totline]= 0x6666ff; /* alert */
00493                 data->totline++;            
00494             }
00495         }
00496     }
00497     else if (ELEM(but->type, MENU, PULLDOWN)) {
00498         if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
00499             MenuType *mt= uiButGetMenuType(but);
00500             if (mt) {
00501                 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), mt->idname);
00502                 data->color[data->totline]= 0x888888;
00503                 data->totline++;
00504             }
00505         }
00506 
00507     }
00508 
00509     assert(data->totline < MAX_TOOLTIP_LINES);
00510     
00511     if(data->totline == 0) {
00512         MEM_freeN(data);
00513         return NULL;
00514     }
00515 
00516     /* create area region */
00517     ar= ui_add_temporary_region(CTX_wm_screen(C));
00518 
00519     memset(&type, 0, sizeof(ARegionType));
00520     type.draw= ui_tooltip_region_draw_cb;
00521     type.free= ui_tooltip_region_free_cb;
00522     ar->type= &type;
00523     
00524     /* set font, get bb */
00525     data->fstyle= style->widget; /* copy struct */
00526     data->fstyle.align= UI_STYLE_TEXT_CENTER;
00527     uiStyleFontSet(&data->fstyle);
00528 
00529     /* these defines may need to be tweaked depending on font */
00530 #define TIP_MARGIN_Y 2
00531 #define TIP_BORDER_X 16.0f
00532 #define TIP_BORDER_Y 6.0f
00533 
00534     h= BLF_height_max(data->fstyle.uifont_id);
00535 
00536     for(a=0, fontw=0, fonth=0; a<data->totline; a++) {
00537         w= BLF_width(data->fstyle.uifont_id, data->lines[a]);
00538         fontw= MAX2(fontw, w);
00539         fonth += (a == 0)? h: h+TIP_MARGIN_Y;
00540     }
00541 
00542     fontw *= aspect;
00543 
00544     ar->regiondata= data;
00545 
00546     data->toth= fonth;
00547     data->lineh= h;
00548     data->spaceh= TIP_MARGIN_Y;
00549 
00550 
00551     /* compute position */
00552     ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
00553     ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
00554 
00555     x1f= (but->x1 + but->x2) * 0.5f + ofsx - (TIP_BORDER_X * aspect);
00556     x2f= x1f + fontw + (TIP_BORDER_X * aspect);
00557     y2f= but->y1 + ofsy - (TIP_BORDER_Y * aspect);
00558     y1f= y2f - fonth*aspect - (TIP_BORDER_Y * aspect);
00559     
00560 #undef TIP_MARGIN_Y
00561 #undef TIP_BORDER_X
00562 #undef TIP_BORDER_Y
00563 
00564     /* copy to int, gets projected if possible too */
00565     x1= x1f; y1= y1f; x2= x2f; y2= y2f; 
00566     
00567     if(butregion) {
00568         /* XXX temp, region v2ds can be empty still */
00569         if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
00570             UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
00571             UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
00572         }
00573 
00574         x1 += butregion->winrct.xmin;
00575         x2 += butregion->winrct.xmin;
00576         y1 += butregion->winrct.ymin;
00577         y2 += butregion->winrct.ymin;
00578     }
00579 
00580     wm_window_get_size(CTX_wm_window(C), &winx, &winy);
00581 
00582     if(x2 > winx) {
00583         /* super size */
00584         if(x2 > winx + x1) {
00585             x2= winx;
00586             x1= 0;
00587         }
00588         else {
00589             x1 -= x2-winx;
00590             x2= winx;
00591         }
00592     }
00593     /* ensure at least 5 px above screen bounds
00594      * 25 is just a guess to be above the menu item */
00595     if(y1 < 5) {
00596         y2 += (-y1) + 30;
00597         y1 = 30;
00598     }
00599 
00600     /* widget rect, in region coords */
00601     data->bbox.xmin= MENU_SHADOW_SIDE;
00602     data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
00603     data->bbox.ymin= MENU_SHADOW_BOTTOM;
00604     data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
00605     
00606     /* region bigger for shadow */
00607     ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
00608     ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
00609     ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
00610     ar->winrct.ymax= y2 + MENU_TOP;
00611 
00612     /* adds subwindow */
00613     ED_region_init(C, ar);
00614     
00615     /* notify change and redraw */
00616     ED_region_tag_redraw(ar);
00617 
00618     return ar;
00619 }
00620 
00621 void ui_tooltip_free(bContext *C, ARegion *ar)
00622 {
00623     ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
00624 }
00625 
00626 
00627 /************************* Creating Search Box **********************/
00628 
00629 struct uiSearchItems {
00630     int maxitem, totitem, maxstrlen;
00631     
00632     int offset, offset_i; /* offset for inserting in array */
00633     int more;  /* flag indicating there are more items */
00634     
00635     char **names;
00636     void **pointers;
00637     int *icons;
00638 
00639     AutoComplete *autocpl;
00640     void *active;
00641 };
00642 
00643 typedef struct uiSearchboxData {
00644     rcti bbox;
00645     uiFontStyle fstyle;
00646     uiSearchItems items;
00647     int active;     /* index in items array */
00648     int noback;     /* when menu opened with enough space for this */
00649     int preview;    /* draw thumbnail previews, rather than list */
00650     int prv_rows, prv_cols;
00651 } uiSearchboxData;
00652 
00653 #define SEARCH_ITEMS    10
00654 
00655 /* exported for use by search callbacks */
00656 /* returns zero if nothing to add */
00657 int uiSearchItemAdd(uiSearchItems *items, const char *name, void *poin, int iconid)
00658 {
00659     /* hijack for autocomplete */
00660     if(items->autocpl) {
00661         autocomplete_do_name(items->autocpl, name);
00662         return 1;
00663     }
00664     
00665     /* hijack for finding active item */
00666     if(items->active) {
00667         if(poin==items->active)
00668             items->offset_i= items->totitem;
00669         items->totitem++;
00670         return 1;
00671     }
00672     
00673     if(items->totitem>=items->maxitem) {
00674         items->more= 1;
00675         return 0;
00676     }
00677     
00678     /* skip first items in list */
00679     if(items->offset_i > 0) {
00680         items->offset_i--;
00681         return 1;
00682     }
00683     
00684     if(items->names)
00685         BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
00686     if(items->pointers)
00687         items->pointers[items->totitem]= poin;
00688     if(items->icons)
00689         items->icons[items->totitem]= iconid;
00690     
00691     items->totitem++;
00692     
00693     return 1;
00694 }
00695 
00696 int uiSearchBoxhHeight(void)
00697 {
00698     return SEARCH_ITEMS*UI_UNIT_Y + 2*MENU_TOP;
00699 }
00700 
00701 /* ar is the search box itself */
00702 static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
00703 {
00704     uiSearchboxData *data= ar->regiondata;
00705     
00706     /* apply step */
00707     data->active+= step;
00708     
00709     if(data->items.totitem==0)
00710         data->active= 0;
00711     else if(data->active > data->items.totitem) {
00712         if(data->items.more) {
00713             data->items.offset++;
00714             data->active= data->items.totitem;
00715             ui_searchbox_update(C, ar, but, 0);
00716         }
00717         else
00718             data->active= data->items.totitem;
00719     }
00720     else if(data->active < 1) {
00721         if(data->items.offset) {
00722             data->items.offset--;
00723             data->active= 1;
00724             ui_searchbox_update(C, ar, but, 0);
00725         }
00726         else if(data->active < 0)
00727             data->active= 0;
00728     }
00729     
00730     ED_region_tag_redraw(ar);
00731 }
00732 
00733 static void ui_searchbox_butrect(rcti *rect, uiSearchboxData *data, int itemnr)
00734 {
00735     /* thumbnail preview */
00736     if (data->preview) {
00737         int buth = (data->bbox.ymax - data->bbox.ymin - 2*MENU_TOP) / data->prv_rows;
00738         int butw = (data->bbox.xmax - data->bbox.xmin) / data->prv_cols;
00739         int row, col;
00740         
00741         *rect= data->bbox;
00742         
00743         col = itemnr % data->prv_cols;
00744         row = itemnr / data->prv_cols;
00745         
00746         rect->xmin += col * butw;
00747         rect->xmax = rect->xmin + butw;
00748         
00749         rect->ymax = data->bbox.ymax - MENU_TOP - (row * buth);
00750         rect->ymin = rect->ymax - buth;
00751     }
00752     /* list view */
00753     else {
00754         int buth= (data->bbox.ymax-data->bbox.ymin - 2*MENU_TOP)/SEARCH_ITEMS;
00755         
00756         *rect= data->bbox;
00757         rect->xmin= data->bbox.xmin + 3.0f;
00758         rect->xmax= data->bbox.xmax - 3.0f;
00759         
00760         rect->ymax= data->bbox.ymax - MENU_TOP - itemnr*buth;
00761         rect->ymin= rect->ymax - buth;
00762     }
00763     
00764 }
00765 
00766 /* x and y in screencoords */
00767 int ui_searchbox_inside(ARegion *ar, int x, int y)
00768 {
00769     uiSearchboxData *data= ar->regiondata;
00770     
00771     return(BLI_in_rcti(&data->bbox, x-ar->winrct.xmin, y-ar->winrct.ymin));
00772 }
00773 
00774 /* string validated to be of correct length (but->hardmax) */
00775 void ui_searchbox_apply(uiBut *but, ARegion *ar)
00776 {
00777     uiSearchboxData *data= ar->regiondata;
00778 
00779     but->func_arg2= NULL;
00780     
00781     if(data->active) {
00782         char *name= data->items.names[data->active-1];
00783         char *cpoin= strchr(name, '|');
00784         
00785         if(cpoin) cpoin[0]= 0;
00786         BLI_strncpy(but->editstr, name, data->items.maxstrlen);
00787         if(cpoin) cpoin[0]= '|';
00788         
00789         but->func_arg2= data->items.pointers[data->active-1];
00790     }
00791 }
00792 
00793 void ui_searchbox_event(bContext *C, ARegion *ar, uiBut *but, wmEvent *event)
00794 {
00795     uiSearchboxData *data= ar->regiondata;
00796     
00797     switch(event->type) {
00798         case WHEELUPMOUSE:
00799         case UPARROWKEY:
00800             ui_searchbox_select(C, ar, but, -1);
00801             break;
00802         case WHEELDOWNMOUSE:
00803         case DOWNARROWKEY:
00804             ui_searchbox_select(C, ar, but, 1);
00805             break;
00806         case MOUSEMOVE:
00807             if(BLI_in_rcti(&ar->winrct, event->x, event->y)) {
00808                 rcti rect;
00809                 int a;
00810                 
00811                 for(a=0; a<data->items.totitem; a++) {
00812                     ui_searchbox_butrect(&rect, data, a);
00813                     if(BLI_in_rcti(&rect, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
00814                         if( data->active!= a+1) {
00815                             data->active= a+1;
00816                             ui_searchbox_select(C, ar, but, 0);
00817                             break;
00818                         }
00819                     }
00820                 }
00821             }
00822             break;
00823     }
00824 }
00825 
00826 /* ar is the search box itself */
00827 void ui_searchbox_update(bContext *C, ARegion *ar, uiBut *but, int reset)
00828 {
00829     uiSearchboxData *data= ar->regiondata;
00830     
00831     /* reset vars */
00832     data->items.totitem= 0;
00833     data->items.more= 0;
00834     if(reset==0) {
00835         data->items.offset_i= data->items.offset;
00836     }
00837     else {
00838         data->items.offset_i= data->items.offset= 0;
00839         data->active= 0;
00840         
00841         /* handle active */
00842         if(but->search_func && but->func_arg2) {
00843             data->items.active= but->func_arg2;
00844             but->search_func(C, but->search_arg, but->editstr, &data->items);
00845             data->items.active= NULL;
00846             
00847             /* found active item, calculate real offset by centering it */
00848             if(data->items.totitem) {
00849                 /* first case, begin of list */
00850                 if(data->items.offset_i < data->items.maxitem) {
00851                     data->active= data->items.offset_i+1;
00852                     data->items.offset_i= 0;
00853                 }
00854                 else {
00855                     /* second case, end of list */
00856                     if(data->items.totitem - data->items.offset_i <= data->items.maxitem) {
00857                         data->active= 1 + data->items.offset_i - data->items.totitem + data->items.maxitem;
00858                         data->items.offset_i= data->items.totitem - data->items.maxitem;
00859                     }
00860                     else {
00861                         /* center active item */
00862                         data->items.offset_i -= data->items.maxitem/2;
00863                         data->active= 1 + data->items.maxitem/2;
00864                     }
00865                 }
00866             }
00867             data->items.offset= data->items.offset_i;
00868             data->items.totitem= 0;
00869         }
00870     }
00871     
00872     /* callback */
00873     if(but->search_func)
00874         but->search_func(C, but->search_arg, but->editstr, &data->items);
00875     
00876     /* handle case where editstr is equal to one of items */
00877     if(reset && data->active==0) {
00878         int a;
00879         
00880         for(a=0; a<data->items.totitem; a++) {
00881             char *cpoin= strchr(data->items.names[a], '|');
00882             
00883             if(cpoin) cpoin[0]= 0;
00884             if(0==strcmp(but->editstr, data->items.names[a]))
00885                 data->active= a+1;
00886             if(cpoin) cpoin[0]= '|';
00887         }
00888         if(data->items.totitem==1 && but->editstr[0])
00889             data->active= 1;
00890     }
00891 
00892     /* validate selected item */
00893     ui_searchbox_select(C, ar, but, 0);
00894     
00895     ED_region_tag_redraw(ar);
00896 }
00897 
00898 void ui_searchbox_autocomplete(bContext *C, ARegion *ar, uiBut *but, char *str)
00899 {
00900     uiSearchboxData *data= ar->regiondata;
00901 
00902     if(str[0]) {
00903         data->items.autocpl= autocomplete_begin(str, ui_get_but_string_max_length(but));
00904 
00905         but->search_func(C, but->search_arg, but->editstr, &data->items);
00906 
00907         autocomplete_end(data->items.autocpl, str);
00908         data->items.autocpl= NULL;
00909     }
00910 }
00911 
00912 static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
00913 {
00914     uiSearchboxData *data= ar->regiondata;
00915     
00916     /* pixel space */
00917     wmOrtho2(-0.01f, ar->winx-0.01f, -0.01f, ar->winy-0.01f);
00918 
00919     if(!data->noback)
00920         ui_draw_search_back(NULL, NULL, &data->bbox); /* style not used yet */
00921     
00922     /* draw text */
00923     if(data->items.totitem) {
00924         rcti rect;
00925         int a;
00926         
00927         if (data->preview) {
00928             /* draw items */
00929             for(a=0; a<data->items.totitem; a++) {
00930                 ui_searchbox_butrect(&rect, data, a);
00931                 
00932                 /* widget itself */
00933                 if (data->preview)
00934                     ui_draw_preview_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
00935                 else 
00936                     ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
00937             }
00938             
00939             /* indicate more */
00940             if(data->items.more) {
00941                 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
00942                 glEnable(GL_BLEND);
00943                 UI_icon_draw(rect.xmax-18, rect.ymin-7, ICON_TRIA_DOWN);
00944                 glDisable(GL_BLEND);
00945             }
00946             if(data->items.offset) {
00947                 ui_searchbox_butrect(&rect, data, 0);
00948                 glEnable(GL_BLEND);
00949                 UI_icon_draw(rect.xmin, rect.ymax-9, ICON_TRIA_UP);
00950                 glDisable(GL_BLEND);
00951             }
00952             
00953         } else {
00954             /* draw items */
00955             for(a=0; a<data->items.totitem; a++) {
00956                 ui_searchbox_butrect(&rect, data, a);
00957                 
00958                 /* widget itself */
00959                 ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
00960                 
00961             }
00962             /* indicate more */
00963             if(data->items.more) {
00964                 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
00965                 glEnable(GL_BLEND);
00966                 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymin-9, ICON_TRIA_DOWN);
00967                 glDisable(GL_BLEND);
00968             }
00969             if(data->items.offset) {
00970                 ui_searchbox_butrect(&rect, data, 0);
00971                 glEnable(GL_BLEND);
00972                 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymax-7, ICON_TRIA_UP);
00973                 glDisable(GL_BLEND);
00974             }
00975         }
00976     }
00977 }
00978 
00979 static void ui_searchbox_region_free_cb(ARegion *ar)
00980 {
00981     uiSearchboxData *data= ar->regiondata;
00982     int a;
00983 
00984     /* free search data */
00985     for(a=0; a<data->items.maxitem; a++)
00986         MEM_freeN(data->items.names[a]);
00987     MEM_freeN(data->items.names);
00988     MEM_freeN(data->items.pointers);
00989     MEM_freeN(data->items.icons);
00990     
00991     MEM_freeN(data);
00992     ar->regiondata= NULL;
00993 }
00994 
00995 ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but)
00996 {
00997     uiStyle *style= UI_GetStyle();
00998     static ARegionType type;
00999     ARegion *ar;
01000     uiSearchboxData *data;
01001     float aspect= but->block->aspect;
01002     float x1f, x2f, y1f, y2f;
01003     int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
01004     
01005     /* create area region */
01006     ar= ui_add_temporary_region(CTX_wm_screen(C));
01007     
01008     memset(&type, 0, sizeof(ARegionType));
01009     type.draw= ui_searchbox_region_draw_cb;
01010     type.free= ui_searchbox_region_free_cb;
01011     ar->type= &type;
01012     
01013     /* create searchbox data */
01014     data= MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
01015 
01016     /* set font, get bb */
01017     data->fstyle= style->widget; /* copy struct */
01018     data->fstyle.align= UI_STYLE_TEXT_CENTER;
01019     ui_fontscale(&data->fstyle.points, aspect);
01020     uiStyleFontSet(&data->fstyle);
01021     
01022     ar->regiondata= data;
01023     
01024     /* special case, hardcoded feature, not draw backdrop when called from menus,
01025        assume for design that popup already added it */
01026     if(but->block->flag & UI_BLOCK_LOOP)
01027         data->noback= 1;
01028     
01029     if (but->a1 > 0 && but->a2 > 0) {
01030         data->preview = 1;
01031         data->prv_rows = but->a1;
01032         data->prv_cols = but->a2;
01033     }
01034     
01035     /* compute position */
01036     if(but->block->flag & UI_BLOCK_LOOP) {
01037         /* this case is search menu inside other menu */
01038         /* we copy region size */
01039 
01040         ar->winrct= butregion->winrct;
01041         
01042         /* widget rect, in region coords */
01043         data->bbox.xmin= MENU_SHADOW_SIDE;
01044         data->bbox.xmax= (ar->winrct.xmax-ar->winrct.xmin) - MENU_SHADOW_SIDE;
01045         data->bbox.ymin= MENU_SHADOW_BOTTOM;
01046         data->bbox.ymax= (ar->winrct.ymax-ar->winrct.ymin) - MENU_SHADOW_BOTTOM;
01047         
01048         /* check if button is lower half */
01049         if( but->y2 < (but->block->miny+but->block->maxy)/2 ) {
01050             data->bbox.ymin += (but->y2-but->y1);
01051         }
01052         else {
01053             data->bbox.ymax -= (but->y2-but->y1);
01054         }
01055     }
01056     else {
01057         x1f= but->x1 - 5;   /* align text with button */
01058         x2f= but->x2 + 5;   /* symmetrical */
01059         y2f= but->y1;
01060         y1f= y2f - uiSearchBoxhHeight();
01061 
01062         ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
01063         ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
01064 
01065         x1f += ofsx;
01066         x2f += ofsx;
01067         y1f += ofsy;
01068         y2f += ofsy;
01069     
01070         /* minimal width */
01071         if(x2f - x1f < 150) x2f= x1f+150; // XXX arbitrary
01072         
01073         /* copy to int, gets projected if possible too */
01074         x1= x1f; y1= y1f; x2= x2f; y2= y2f; 
01075         
01076         if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
01077             UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
01078             UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
01079         }
01080 
01081         x1 += butregion->winrct.xmin;
01082         x2 += butregion->winrct.xmin;
01083         y1 += butregion->winrct.ymin;
01084         y2 += butregion->winrct.ymin;
01085 
01086         wm_window_get_size(CTX_wm_window(C), &winx, &winy);
01087         
01088         if(x2 > winx) {
01089             /* super size */
01090             if(x2 > winx + x1) {
01091                 x2= winx;
01092                 x1= 0;
01093             }
01094             else {
01095                 x1 -= x2-winx;
01096                 x2= winx;
01097             }
01098         }
01099 
01100         if(y1 < 0) {
01101             int newy1;
01102             UI_view2d_to_region_no_clip(&butregion->v2d, 0, but->y2 + ofsy, NULL, &newy1);
01103             newy1 += butregion->winrct.ymin;
01104 
01105             y2= y2-y1 + newy1;
01106             y1= newy1;
01107         }
01108 
01109         /* widget rect, in region coords */
01110         data->bbox.xmin= MENU_SHADOW_SIDE;
01111         data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
01112         data->bbox.ymin= MENU_SHADOW_BOTTOM;
01113         data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
01114         
01115         /* region bigger for shadow */
01116         ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
01117         ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
01118         ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
01119         ar->winrct.ymax= y2;
01120     }
01121     
01122     /* adds subwindow */
01123     ED_region_init(C, ar);
01124     
01125     /* notify change and redraw */
01126     ED_region_tag_redraw(ar);
01127     
01128     /* prepare search data */
01129     if (data->preview) {
01130         data->items.maxitem= data->prv_rows * data->prv_cols;
01131     } else {
01132         data->items.maxitem= SEARCH_ITEMS;
01133     }
01134     data->items.maxstrlen= but->hardmax;
01135     data->items.totitem= 0;
01136     data->items.names= MEM_callocN(data->items.maxitem*sizeof(void *), "search names");
01137     data->items.pointers= MEM_callocN(data->items.maxitem*sizeof(void *), "search pointers");
01138     data->items.icons= MEM_callocN(data->items.maxitem*sizeof(int), "search icons");
01139     for(x1=0; x1<data->items.maxitem; x1++)
01140         data->items.names[x1]= MEM_callocN(but->hardmax+1, "search pointers");
01141     
01142     return ar;
01143 }
01144 
01145 void ui_searchbox_free(bContext *C, ARegion *ar)
01146 {
01147     ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
01148 }
01149 
01150 /* sets red alert if button holds a string it can't find */
01151 /* XXX weak: search_func adds all partial matches... */
01152 void ui_but_search_test(uiBut *but)
01153 {
01154     uiSearchItems *items;
01155     int x1;
01156 
01157     /* possibly very large lists (such as ID datablocks) only
01158      * only validate string RNA buts (not pointers) */
01159     if(but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
01160         return;
01161     }
01162 
01163     items= MEM_callocN(sizeof(uiSearchItems), "search items");
01164 
01165     /* setup search struct */
01166     items->maxitem= 10;
01167     items->maxstrlen= 256;
01168     items->names= MEM_callocN(items->maxitem*sizeof(void *), "search names");
01169     for(x1=0; x1<items->maxitem; x1++)
01170         items->names[x1]= MEM_callocN(but->hardmax+1, "search names");
01171     
01172     but->search_func(but->block->evil_C, but->search_arg, but->drawstr, items);
01173     
01174     /* only redalert when we are sure of it, this can miss cases when >10 matches */
01175     if(items->totitem==0)
01176         uiButSetFlag(but, UI_BUT_REDALERT);
01177     else if(items->more==0) {
01178         for(x1= 0; x1<items->totitem; x1++)
01179             if(strcmp(but->drawstr, items->names[x1])==0)
01180                 break;
01181         if(x1==items->totitem)
01182             uiButSetFlag(but, UI_BUT_REDALERT);
01183     }
01184     
01185     for(x1=0; x1<items->maxitem; x1++)
01186         MEM_freeN(items->names[x1]);
01187     MEM_freeN(items->names);
01188     MEM_freeN(items);
01189 }
01190 
01191 
01192 /************************* Creating Menu Blocks **********************/
01193 
01194 /* position block relative to but, result is in window space */
01195 static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
01196 {
01197     uiBut *bt;
01198     uiSafetyRct *saferct;
01199     rctf butrct;
01200     /*float aspect;*/ /*UNUSED*/
01201     int xsize, ysize, xof=0, yof=0, center;
01202     short dir1= 0, dir2=0;
01203     
01204     /* transform to window coordinates, using the source button region/block */
01205     butrct.xmin= but->x1; butrct.xmax= but->x2;
01206     butrct.ymin= but->y1; butrct.ymax= but->y2;
01207 
01208     ui_block_to_window_fl(butregion, but->block, &butrct.xmin, &butrct.ymin);
01209     ui_block_to_window_fl(butregion, but->block, &butrct.xmax, &butrct.ymax);
01210 
01211     /* calc block rect */
01212     if(block->minx == 0.0f && block->maxx == 0.0f) {
01213         if(block->buttons.first) {
01214             block->minx= block->miny= 10000;
01215             block->maxx= block->maxy= -10000;
01216             
01217             bt= block->buttons.first;
01218             while(bt) {
01219                 if(bt->x1 < block->minx) block->minx= bt->x1;
01220                 if(bt->y1 < block->miny) block->miny= bt->y1;
01221 
01222                 if(bt->x2 > block->maxx) block->maxx= bt->x2;
01223                 if(bt->y2 > block->maxy) block->maxy= bt->y2;
01224                 
01225                 bt= bt->next;
01226             }
01227         }
01228         else {
01229             /* we're nice and allow empty blocks too */
01230             block->minx= block->miny= 0;
01231             block->maxx= block->maxy= 20;
01232         }
01233     }
01234     
01235     /*aspect= (float)(block->maxx - block->minx + 4);*/ /*UNUSED*/
01236     ui_block_to_window_fl(butregion, but->block, &block->minx, &block->miny);
01237     ui_block_to_window_fl(butregion, but->block, &block->maxx, &block->maxy);
01238 
01239     //block->minx-= 2.0; block->miny-= 2.0;
01240     //block->maxx+= 2.0; block->maxy+= 2.0;
01241     
01242     xsize= block->maxx - block->minx+4; // 4 for shadow
01243     ysize= block->maxy - block->miny+4;
01244     /*aspect/= (float)xsize;*/ /*UNUSED*/
01245 
01246     {
01247         int left=0, right=0, top=0, down=0;
01248         int winx, winy;
01249         // int offscreen;
01250 
01251         wm_window_get_size(window, &winx, &winy);
01252 
01253         if(block->direction & UI_CENTER) center= ysize/2;
01254         else center= 0;
01255         
01256         /* check if there's space at all */
01257         if( butrct.xmin-xsize > 0.0f) left= 1;
01258         if( butrct.xmax+xsize < winx) right= 1;
01259         if( butrct.ymin-ysize+center > 0.0f) down= 1;
01260         if( butrct.ymax+ysize-center < winy) top= 1;
01261         
01262         if(top==0 && down==0) {
01263             if (butrct.ymin-ysize < winy-butrct.ymax-ysize)
01264                 top= 1;
01265             else
01266                 down= 1;
01267         }
01268         
01269         dir1= block->direction & UI_DIRECTION;
01270 
01271         /* secundary directions */
01272         if(dir1 & (UI_TOP|UI_DOWN)) {
01273             if(dir1 & UI_LEFT) dir2= UI_LEFT;
01274             else if(dir1 & UI_RIGHT) dir2= UI_RIGHT;
01275             dir1 &= (UI_TOP|UI_DOWN);
01276         }
01277 
01278         if(dir2==0) if(dir1==UI_LEFT || dir1==UI_RIGHT) dir2= UI_DOWN;
01279         if(dir2==0) if(dir1==UI_TOP || dir1==UI_DOWN) dir2= UI_LEFT;
01280         
01281         /* no space at all? dont change */
01282         if(left || right) {
01283             if(dir1==UI_LEFT && left==0) dir1= UI_RIGHT;
01284             if(dir1==UI_RIGHT && right==0) dir1= UI_LEFT;
01285             /* this is aligning, not append! */
01286             if(dir2==UI_LEFT && right==0) dir2= UI_RIGHT;
01287             if(dir2==UI_RIGHT && left==0) dir2= UI_LEFT;
01288         }
01289         if(down || top) {
01290             if(dir1==UI_TOP && top==0) dir1= UI_DOWN;
01291             if(dir1==UI_DOWN && down==0) dir1= UI_TOP;
01292             if(dir2==UI_TOP && top==0) dir2= UI_DOWN;
01293             if(dir2==UI_DOWN && down==0) dir2= UI_TOP;
01294         }
01295         
01296         if(dir1==UI_LEFT) {
01297             xof= butrct.xmin - block->maxx;
01298             if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
01299             else yof= butrct.ymax - block->maxy+center;
01300         }
01301         else if(dir1==UI_RIGHT) {
01302             xof= butrct.xmax - block->minx;
01303             if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
01304             else yof= butrct.ymax - block->maxy+center;
01305         }
01306         else if(dir1==UI_TOP) {
01307             yof= butrct.ymax - block->miny;
01308             if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
01309             else xof= butrct.xmin - block->minx;
01310             // changed direction? 
01311             if((dir1 & block->direction)==0) {
01312                 if(block->direction & UI_SHIFT_FLIPPED)
01313                     xof+= dir2==UI_LEFT?25:-25;
01314                 uiBlockFlipOrder(block);
01315             }
01316         }
01317         else if(dir1==UI_DOWN) {
01318             yof= butrct.ymin - block->maxy;
01319             if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
01320             else xof= butrct.xmin - block->minx;
01321             // changed direction?
01322             if((dir1 & block->direction)==0) {
01323                 if(block->direction & UI_SHIFT_FLIPPED)
01324                     xof+= dir2==UI_LEFT?25:-25;
01325                 uiBlockFlipOrder(block);
01326             }
01327         }
01328 
01329         /* and now we handle the exception; no space below or to top */
01330         if(top==0 && down==0) {
01331             if(dir1==UI_LEFT || dir1==UI_RIGHT) {
01332                 // align with bottom of screen 
01333                 // yof= ysize; (not with menu scrolls)
01334             }
01335         }
01336         
01337         /* or no space left or right */
01338         if(left==0 && right==0) {
01339             if(dir1==UI_TOP || dir1==UI_DOWN) {
01340                 // align with left size of screen 
01341                 xof= -block->minx+5;
01342             }
01343         }
01344         
01345         // apply requested offset in the block
01346         xof += block->xofs/block->aspect;
01347         yof += block->yofs/block->aspect;
01348 #if 0
01349         /* clamp to window bounds, could be made into an option if its ever annoying */
01350         if(     (offscreen= (block->miny+yof)) < 0)      yof -= offscreen; /* bottom */
01351         else if((offscreen= (block->maxy+yof)-winy) > 0) yof -= offscreen; /* top */
01352         if(     (offscreen= (block->minx+xof)) < 0)      xof -= offscreen; /* left */
01353         else if((offscreen= (block->maxx+xof)-winx) > 0) xof -= offscreen; /* right */
01354 #endif
01355     }
01356     
01357     /* apply offset, buttons in window coords */
01358     
01359     for(bt= block->buttons.first; bt; bt= bt->next) {
01360         ui_block_to_window_fl(butregion, but->block, &bt->x1, &bt->y1);
01361         ui_block_to_window_fl(butregion, but->block, &bt->x2, &bt->y2);
01362 
01363         bt->x1 += xof;
01364         bt->x2 += xof;
01365         bt->y1 += yof;
01366         bt->y2 += yof;
01367 
01368         bt->aspect= 1.0;
01369         // ui_check_but recalculates drawstring size in pixels
01370         ui_check_but(bt);
01371     }
01372     
01373     block->minx += xof;
01374     block->miny += yof;
01375     block->maxx += xof;
01376     block->maxy += yof;
01377 
01378     /* safety calculus */
01379     if(but) {
01380         float midx= (butrct.xmin+butrct.xmax)/2.0f;
01381         float midy= (butrct.ymin+butrct.ymax)/2.0f;
01382         
01383         /* when you are outside parent button, safety there should be smaller */
01384         
01385         // parent button to left
01386         if( midx < block->minx ) block->safety.xmin= block->minx-3; 
01387         else block->safety.xmin= block->minx-40;
01388         // parent button to right
01389         if( midx > block->maxx ) block->safety.xmax= block->maxx+3; 
01390         else block->safety.xmax= block->maxx+40;
01391         
01392         // parent button on bottom
01393         if( midy < block->miny ) block->safety.ymin= block->miny-3; 
01394         else block->safety.ymin= block->miny-40;
01395         // parent button on top
01396         if( midy > block->maxy ) block->safety.ymax= block->maxy+3; 
01397         else block->safety.ymax= block->maxy+40;
01398         
01399         // exception for switched pulldowns...
01400         if(dir1 && (dir1 & block->direction)==0) {
01401             if(dir2==UI_RIGHT) block->safety.xmax= block->maxx+3; 
01402             if(dir2==UI_LEFT) block->safety.xmin= block->minx-3; 
01403         }
01404         block->direction= dir1;
01405     }
01406     else {
01407         block->safety.xmin= block->minx-40;
01408         block->safety.ymin= block->miny-40;
01409         block->safety.xmax= block->maxx+40;
01410         block->safety.ymax= block->maxy+40;
01411     }
01412 
01413     /* keep a list of these, needed for pulldown menus */
01414     saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
01415     saferct->parent= butrct;
01416     saferct->safety= block->safety;
01417     BLI_freelistN(&block->saferct);
01418     if(but)
01419         BLI_duplicatelist(&block->saferct, &but->block->saferct);
01420     BLI_addhead(&block->saferct, saferct);
01421 }
01422 
01423 static void ui_block_region_draw(const bContext *C, ARegion *ar)
01424 {
01425     uiBlock *block;
01426 
01427     for(block=ar->uiblocks.first; block; block=block->next)
01428         uiDrawBlock(C, block);
01429 }
01430 
01431 static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
01432 {
01433     int winx, winy;
01434     
01435     wm_window_get_size(window, &winx, &winy);
01436     
01437     if(block->minx < MENU_SHADOW_SIDE)
01438         block->minx= MENU_SHADOW_SIDE;
01439     if(block->maxx > winx-MENU_SHADOW_SIDE)
01440         block->maxx= winx-MENU_SHADOW_SIDE;
01441     
01442     if(block->miny < MENU_SHADOW_BOTTOM)
01443         block->miny= MENU_SHADOW_BOTTOM;
01444     if(block->maxy > winy-MENU_TOP)
01445         block->maxy= winy-MENU_TOP;
01446 }
01447 
01448 void ui_popup_block_scrolltest(uiBlock *block)
01449 {
01450     uiBut *bt;
01451     /* Knowing direction is necessary for multi-column menus... */
01452     int is_flip = (block->direction & UI_TOP) && !(block->flag & UI_BLOCK_NO_FLIP);
01453     
01454     block->flag &= ~(UI_BLOCK_CLIPBOTTOM|UI_BLOCK_CLIPTOP);
01455     
01456     for(bt= block->buttons.first; bt; bt= bt->next)
01457         bt->flag &= ~UI_SCROLLED;
01458     
01459     if(block->buttons.first==block->buttons.last)
01460         return;
01461     
01462     /* mark buttons that are outside boundary and the ones next to it for arrow(s) */
01463     for(bt= block->buttons.first; bt; bt= bt->next) {
01464         if(bt->y1 < block->miny) {
01465             bt->flag |= UI_SCROLLED;
01466             block->flag |= UI_BLOCK_CLIPBOTTOM;
01467             /* make space for arrow */
01468             if(bt->y2 < block->miny +10) {
01469                 if(is_flip && bt->next && bt->next->y1 > bt->y1)
01470                     bt->next->flag |= UI_SCROLLED;
01471                 else if(!is_flip && bt->prev && bt->prev->y1 > bt->y1)
01472                     bt->prev->flag |= UI_SCROLLED;
01473             }
01474         }
01475         if(bt->y2 > block->maxy) {
01476             bt->flag |= UI_SCROLLED;
01477             block->flag |= UI_BLOCK_CLIPTOP;
01478             /* make space for arrow */
01479             if(bt->y1 > block->maxy -10) {
01480                 if(!is_flip && bt->next && bt->next->y2 < bt->y2)
01481                     bt->next->flag |= UI_SCROLLED;
01482                 else if(is_flip && bt->prev && bt->prev->y2 < bt->y2)
01483                     bt->prev->flag |= UI_SCROLLED;
01484             }
01485         }
01486     }
01487 }
01488 
01489 uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg)
01490 {
01491     wmWindow *window= CTX_wm_window(C);
01492     static ARegionType type;
01493     ARegion *ar;
01494     uiBlock *block;
01495     uiBut *bt;
01496     uiPopupBlockHandle *handle;
01497     uiSafetyRct *saferct;
01498 
01499     /* create handle */
01500     handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
01501 
01502     /* store context for operator */
01503     handle->ctx_area= CTX_wm_area(C);
01504     handle->ctx_region= CTX_wm_region(C);
01505     
01506     /* create area region */
01507     ar= ui_add_temporary_region(CTX_wm_screen(C));
01508     handle->region= ar;
01509 
01510     memset(&type, 0, sizeof(ARegionType));
01511     type.draw= ui_block_region_draw;
01512     ar->type= &type;
01513 
01514     UI_add_region_handlers(&ar->handlers);
01515 
01516     /* create ui block */
01517     if(create_func)
01518         block= create_func(C, handle->region, arg);
01519     else
01520         block= handle_create_func(C, handle, arg);
01521     
01522     if(block->handle) {
01523         memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
01524         MEM_freeN(handle);
01525         handle= block->handle;
01526     }
01527     else
01528         block->handle= handle;
01529 
01530     ar->regiondata= handle;
01531 
01532     if(!block->endblock)
01533         uiEndBlock(C, block);
01534 
01535     /* if this is being created from a button */
01536     if(but) {
01537         if(ELEM(but->type, BLOCK, PULLDOWN))
01538             block->xofs = -2;   /* for proper alignment */
01539 
01540         ui_block_position(window, butregion, but, block);
01541     }
01542     else {
01543         /* keep a list of these, needed for pulldown menus */
01544         saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
01545         saferct->safety= block->safety;
01546         BLI_addhead(&block->saferct, saferct);
01547         block->flag |= UI_BLOCK_POPUP|UI_BLOCK_NUMSELECT;
01548     }
01549 
01550     /* clip block with window boundary */
01551     ui_popup_block_clip(window, block);
01552     
01553     /* the block and buttons were positioned in window space as in 2.4x, now
01554      * these menu blocks are regions so we bring it back to region space.
01555      * additionally we add some padding for the menu shadow or rounded menus */
01556     ar->winrct.xmin= block->minx - MENU_SHADOW_SIDE;
01557     ar->winrct.xmax= block->maxx + MENU_SHADOW_SIDE;
01558     ar->winrct.ymin= block->miny - MENU_SHADOW_BOTTOM;
01559     ar->winrct.ymax= block->maxy + MENU_TOP;
01560     
01561     block->minx -= ar->winrct.xmin;
01562     block->maxx -= ar->winrct.xmin;
01563     block->miny -= ar->winrct.ymin;
01564     block->maxy -= ar->winrct.ymin;
01565 
01566     for(bt= block->buttons.first; bt; bt= bt->next) {
01567         bt->x1 -= ar->winrct.xmin;
01568         bt->x2 -= ar->winrct.xmin;
01569         bt->y1 -= ar->winrct.ymin;
01570         bt->y2 -= ar->winrct.ymin;
01571     }
01572     
01573     block->flag |= UI_BLOCK_LOOP;
01574 
01575     /* adds subwindow */
01576     ED_region_init(C, ar);
01577 
01578     /* checks which buttons are visible, sets flags to prevent draw (do after region init) */
01579     ui_popup_block_scrolltest(block);
01580     
01581     /* get winmat now that we actually have the subwindow */
01582     wmSubWindowSet(window, ar->swinid);
01583     
01584     wm_subwindow_getmatrix(window, ar->swinid, block->winmat);
01585     
01586     /* notify change and redraw */
01587     ED_region_tag_redraw(ar);
01588 
01589     return handle;
01590 }
01591 
01592 void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
01593 {
01594     ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
01595     
01596     if(handle->scrolltimer)
01597         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
01598     
01599     MEM_freeN(handle);
01600 }
01601 
01602 /***************************** Menu Button ***************************/
01603 
01604 static void ui_block_func_MENUSTR(bContext *UNUSED(C), uiLayout *layout, void *arg_str)
01605 {
01606     uiBlock *block= uiLayoutGetBlock(layout);
01607     uiPopupBlockHandle *handle= block->handle;
01608     uiLayout *split, *column=NULL;
01609     uiBut *bt;
01610     MenuData *md;
01611     MenuEntry *entry;
01612     const char *instr= arg_str;
01613     int columns, rows, a, b;
01614 
01615     uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
01616     
01617     /* compute menu data */
01618     md= decompose_menu_string(instr);
01619 
01620     /* columns and row estimation */
01621     columns= (md->nitems+20)/20;
01622     if(columns<1)
01623         columns= 1;
01624     if(columns>8)
01625         columns= (md->nitems+25)/25;
01626     
01627     rows= md->nitems/columns;
01628     if(rows<1)
01629         rows= 1;
01630     while(rows*columns<md->nitems)
01631         rows++;
01632 
01633     /* create title */
01634     if(md->title) {
01635         if(md->titleicon) {
01636             uiItemL(layout, md->title, md->titleicon);
01637         }
01638         else {
01639             uiItemL(layout, md->title, ICON_NONE);
01640             bt= block->buttons.last;
01641             bt->flag= UI_TEXT_LEFT;
01642         }
01643     }
01644 
01645     /* inconsistent, but menus with labels do not look good flipped */
01646     entry= md->items;
01647     for(a=0; a<md->nitems; a++, entry++) {
01648         if(entry->sepr && entry->str[0]) {
01649             block->flag |= UI_BLOCK_NO_FLIP;
01650             break;
01651         }
01652     }
01653 
01654     /* create items */
01655     split= uiLayoutSplit(layout, 0, 0);
01656 
01657     for(a=0, b=0; a<md->nitems; a++, b++) {
01658         if(block->flag & UI_BLOCK_NO_FLIP)
01659             entry= &md->items[a];
01660         else
01661             entry= &md->items[md->nitems-a-1];
01662         
01663         /* new column on N rows or on separation label */
01664         if((b % rows == 0) || (entry->sepr && entry->str[0])) {
01665             column= uiLayoutColumn(split, 0);
01666             b= 0;
01667         }
01668 
01669         if(entry->sepr) {
01670             uiItemL(column, entry->str, entry->icon);
01671             bt= block->buttons.last;
01672             bt->flag= UI_TEXT_LEFT;
01673         }
01674         else if(entry->icon) {
01675             uiDefIconTextButF(block, BUTM|FLO, B_NOP, entry->icon, entry->str, 0, 0,
01676                 UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
01677         }
01678         else {
01679             uiDefButF(block, BUTM|FLO, B_NOP, entry->str, 0, 0,
01680                 UI_UNIT_X*5, UI_UNIT_X, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
01681         }
01682     }
01683     
01684     menudata_free(md);
01685 }
01686 
01687 void ui_block_func_ICONROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
01688 {
01689     uiBlock *block= uiLayoutGetBlock(layout);
01690     uiPopupBlockHandle *handle= block->handle;
01691     uiBut *but= arg_but;
01692     int a;
01693     
01694     uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
01695     
01696     for(a=(int)but->hardmin; a<=(int)but->hardmax; a++)
01697         uiDefIconButF(block, BUTM|FLO, B_NOP, but->icon+(a-but->hardmin), 0, 0, UI_UNIT_X*5, UI_UNIT_Y,
01698             &handle->retvalue, (float)a, 0.0, 0, 0, "");
01699 }
01700 
01701 void ui_block_func_ICONTEXTROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
01702 {
01703     uiBlock *block= uiLayoutGetBlock(layout);
01704     uiPopupBlockHandle *handle= block->handle;
01705     uiBut *but= arg_but, *bt;
01706     MenuData *md;
01707     MenuEntry *entry;
01708     int a;
01709     
01710     uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
01711 
01712     md= decompose_menu_string(but->str);
01713 
01714     /* title */
01715     if(md->title) {
01716         bt= uiDefBut(block, LABEL, 0, md->title, 0, 0, UI_UNIT_X*5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
01717         bt->flag= UI_TEXT_LEFT;
01718     }
01719 
01720     /* loop through the menu options and draw them out with icons & text labels */
01721     for(a=0; a<md->nitems; a++) {
01722         entry= &md->items[md->nitems-a-1];
01723 
01724         if(entry->sepr)
01725             uiItemS(layout);
01726         else
01727             uiDefIconTextButF(block, BUTM|FLO, B_NOP, (short)((but->icon)+(entry->retval-but->hardmin)), entry->str,
01728                 0, 0, UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
01729     }
01730 
01731     menudata_free(md);
01732 }
01733 
01734 #if 0
01735 static void ui_warp_pointer(int x, int y)
01736 {
01737     /* XXX 2.50 which function to use for this? */
01738     /* OSX has very poor mousewarp support, it sends events;
01739        this causes a menu being pressed immediately ... */
01740     #ifndef __APPLE__
01741     warp_pointer(x, y);
01742     #endif
01743 }
01744 #endif
01745 
01746 /********************* Color Button ****************/
01747 
01748 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
01749 #define SPICK   110.0
01750 #define FPICK   180.0
01751 #define DPICK   6.0
01752 #define BPICK   24.0
01753 
01754 /* for picker, while editing hsv */
01755 void ui_set_but_hsv(uiBut *but)
01756 {
01757     float col[3];
01758     float *hsv= ui_block_hsv_get(but->block);
01759     
01760     hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
01761     ui_set_but_vectorf(but, col);
01762 }
01763 
01764 /* also used by small picker, be careful with name checks below... */
01765 static void ui_update_block_buts_rgb(uiBlock *block, float *rgb)
01766 {
01767     uiBut *bt;
01768     float *hsv= ui_block_hsv_get(block);
01769     
01770     /* this is to keep the H and S value when V is equal to zero
01771      * and we are working in HSV mode, of course!
01772      */
01773     rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
01774     
01775     // this updates button strings, is hackish... but button pointers are on stack of caller function
01776     for(bt= block->buttons.first; bt; bt= bt->next) {
01777         if (bt->rnaprop) {
01778             
01779             ui_set_but_vectorf(bt, rgb);
01780             
01781         }
01782         else if(strcmp(bt->str, "Hex: ")==0) {
01783             float rgb_gamma[3];
01784             double intpart;
01785             char col[16];
01786             
01787             /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
01788             
01789             if (block->color_profile == BLI_PR_NONE) {
01790                 copy_v3_v3(rgb_gamma, rgb);
01791             } else {
01792                 /* make an sRGB version, for Hex code */
01793                 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
01794             }
01795             
01796             if (rgb_gamma[0] > 1.0f) rgb_gamma[0] = modf(rgb_gamma[0], &intpart);
01797             if (rgb_gamma[1] > 1.0f) rgb_gamma[1] = modf(rgb_gamma[1], &intpart);
01798             if (rgb_gamma[2] > 1.0f) rgb_gamma[2] = modf(rgb_gamma[2], &intpart);
01799 
01800             BLI_snprintf(col, sizeof(col), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
01801             
01802             strcpy(bt->poin, col);
01803         }
01804         else if(bt->str[1]==' ') {
01805             if(bt->str[0]=='R') {
01806                 ui_set_but_val(bt, rgb[0]);
01807             }
01808             else if(bt->str[0]=='G') {
01809                 ui_set_but_val(bt, rgb[1]);
01810             }
01811             else if(bt->str[0]=='B') {
01812                 ui_set_but_val(bt, rgb[2]);
01813             }
01814             else if(bt->str[0]=='H') {
01815                 ui_set_but_val(bt, hsv[0]);
01816             }
01817             else if(bt->str[0]=='S') {
01818                 ui_set_but_val(bt, hsv[1]);
01819             }
01820             else if(bt->str[0]=='V') {
01821                 ui_set_but_val(bt, hsv[2]);
01822             }
01823         }       
01824 
01825         ui_check_but(bt);
01826     }
01827 }
01828 
01829 static void do_picker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
01830 {
01831     uiBut *but= (uiBut *)bt1;
01832     uiPopupBlockHandle *popup= but->block->handle;
01833     PropertyRNA *prop = but->rnaprop;
01834     PointerRNA ptr = but->rnapoin;
01835     float rgb[4];
01836     
01837     if (prop) {
01838         RNA_property_float_get_array(&ptr, prop, rgb);
01839         ui_update_block_buts_rgb(but->block, rgb);
01840     }
01841     
01842     if(popup)
01843         popup->menuretval= UI_RETURN_UPDATE;
01844 }
01845 
01846 static void do_hsv_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
01847 {
01848     uiBut *but= (uiBut *)bt1;
01849     uiPopupBlockHandle *popup= but->block->handle;
01850     float rgb[3];
01851     float *hsv= ui_block_hsv_get(but->block);
01852     
01853     hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
01854     
01855     ui_update_block_buts_rgb(but->block, rgb);
01856     
01857     if(popup)
01858         popup->menuretval= UI_RETURN_UPDATE;
01859 }
01860 
01861 static void do_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
01862 {
01863     uiBut *but= (uiBut *)bt1;
01864     uiPopupBlockHandle *popup= but->block->handle;
01865     char *hexcol= (char *)hexcl;
01866     float rgb[3];
01867     
01868     hex_to_rgb(hexcol, rgb, rgb+1, rgb+2);
01869     
01870     /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
01871     if (but->block->color_profile != BLI_PR_NONE) {
01872         /* so we need to linearise it for Blender */
01873         srgb_to_linearrgb_v3_v3(rgb, rgb);
01874     }
01875     
01876     ui_update_block_buts_rgb(but->block, rgb);
01877     
01878     if(popup)
01879         popup->menuretval= UI_RETURN_UPDATE;
01880 }
01881 
01882 static void close_popup_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
01883 {
01884     uiBut *but= (uiBut *)bt1;
01885     uiPopupBlockHandle *popup= but->block->handle;
01886     
01887     if(popup)
01888         popup->menuretval= UI_RETURN_OK;
01889 }
01890 
01891 static void picker_new_hide_reveal(uiBlock *block, short colormode)
01892 {
01893     uiBut *bt;
01894     
01895     /* tag buttons */
01896     for(bt= block->buttons.first; bt; bt= bt->next) {
01897         
01898         if (bt->type == LABEL) {
01899             if( bt->str[1]=='G') {
01900                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
01901                 else bt->flag |= UI_HIDDEN;
01902             }
01903         }
01904         
01905         if(bt->type==NUMSLI || bt->type==TEX) {
01906             if( bt->str[1]=='e') {
01907                 if(colormode==2) bt->flag &= ~UI_HIDDEN;
01908                 else bt->flag |= UI_HIDDEN;
01909             }
01910             else if( ELEM3(bt->str[0], 'R', 'G', 'B')) {
01911                 if(colormode==0) bt->flag &= ~UI_HIDDEN;
01912                 else bt->flag |= UI_HIDDEN;
01913             }
01914             else if( ELEM3(bt->str[0], 'H', 'S', 'V')) {
01915                 if(colormode==1) bt->flag &= ~UI_HIDDEN;
01916                 else bt->flag |= UI_HIDDEN;
01917             }
01918         }
01919     }
01920 }
01921 
01922 static void do_picker_new_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
01923 {
01924     uiBut *bt= bt1;
01925     short colormode= ui_get_but_val(bt);
01926     picker_new_hide_reveal(bt->block, colormode);
01927 }
01928 
01929 /* picker sizes S hsize, F full size, D spacer, B button/pallette height  */
01930 #define SPICK1  150.0
01931 #define DPICK1  6.0
01932 
01933 #define PICKER_H    150
01934 #define PICKER_W    150
01935 #define PICKER_SPACE    6
01936 #define PICKER_BAR      14
01937 
01938 #define PICKER_TOTAL_W  (PICKER_W+PICKER_SPACE+PICKER_BAR)
01939 
01940 static void circle_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop)
01941 {
01942     uiBut *bt;
01943     
01944     /* HS circle */
01945     bt= uiDefButR_prop(block, HSVCIRCLE, 0, "", 0, 0, PICKER_H, PICKER_W, ptr, prop, 0, 0.0, 0.0, 0, 0, "Color");
01946     uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
01947     
01948     /* value */
01949     bt= uiDefButR_prop(block, HSVCUBE, 0, "", PICKER_W+PICKER_SPACE,0,PICKER_BAR,PICKER_H, ptr, prop, 0, 0.0, 0.0, UI_GRAD_V_ALT, 0, "Value");
01950     uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
01951 }
01952 
01953 
01954 static void square_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int type)
01955 {
01956     uiBut *bt;
01957     int bartype = type + 3;
01958     
01959     /* HS square */
01960     bt= uiDefButR_prop(block, HSVCUBE, 0, "",   0, PICKER_BAR+PICKER_SPACE, PICKER_TOTAL_W, PICKER_H, ptr, prop, 0, 0.0, 0.0, type, 0, "Color");
01961     uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
01962     
01963     /* value */
01964     bt= uiDefButR_prop(block, HSVCUBE, 0, "",       0, 0, PICKER_TOTAL_W, PICKER_BAR, ptr, prop, 0, 0.0, 0.0, bartype, 0, "Value");
01965     uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
01966 }
01967 
01968 
01969 /* a HS circle, V slider, rgb/hsv/hex sliders */
01970 static void uiBlockPicker(uiBlock *block, float *rgb, PointerRNA *ptr, PropertyRNA *prop)
01971 {
01972     static short colormode= 0;  /* temp? 0=rgb, 1=hsv, 2=hex */
01973     uiBut *bt;
01974     int width, butwidth;
01975     static char tip[50];
01976     static char hexcol[128];
01977     float rgb_gamma[3];
01978     float min, max, step, precision;
01979     float *hsv= ui_block_hsv_get(block);
01980     
01981     ui_block_hsv_get(block);
01982     
01983     width= PICKER_TOTAL_W;
01984     butwidth = width - UI_UNIT_X - 10;
01985     
01986     /* existence of profile means storage is in linear color space, with display correction */
01987     if (block->color_profile == BLI_PR_NONE) {
01988         BLI_strncpy(tip, "Value in Display Color Space", sizeof(tip));
01989         copy_v3_v3(rgb_gamma, rgb);
01990     } else {
01991         BLI_strncpy(tip, "Value in Linear RGB Color Space", sizeof(tip));
01992         /* make an sRGB version, for Hex code */
01993         linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
01994     }
01995     
01996     /* sneaky way to check for alpha */
01997     rgb[3]= FLT_MAX;
01998 
01999     RNA_property_float_ui_range(ptr, prop, &min, &max, &step, &precision);
02000     RNA_property_float_get_array(ptr, prop, rgb);
02001 
02002     switch (U.color_picker_type) {
02003         case USER_CP_CIRCLE:
02004             circle_picker(block, ptr, prop);
02005             break;
02006         case USER_CP_SQUARE_SV:
02007             square_picker(block, ptr, prop, UI_GRAD_SV);
02008             break;
02009         case USER_CP_SQUARE_HS:
02010             square_picker(block, ptr, prop, UI_GRAD_HS);
02011             break;
02012         case USER_CP_SQUARE_HV:
02013             square_picker(block, ptr, prop, UI_GRAD_HV);
02014             break;
02015     }
02016     
02017     /* mode */
02018     uiBlockBeginAlign(block);
02019     bt= uiDefButS(block, ROW, 0, "RGB", 0, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 0.0, 0, 0, "");
02020     uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
02021     bt= uiDefButS(block, ROW, 0, "HSV", width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 1.0, 0, 0, "");
02022     uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
02023     bt= uiDefButS(block, ROW, 0, "Hex", 2*width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 2.0, 0, 0, "");
02024     uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
02025     uiBlockEndAlign(block);
02026 
02027     bt= uiDefIconButO(block, BUT, "UI_OT_eyedropper", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, butwidth+10, -60, UI_UNIT_X, UI_UNIT_Y, NULL);
02028     uiButSetFunc(bt, close_popup_cb, bt, NULL);
02029     
02030     /* RGB values */
02031     uiBlockBeginAlign(block);
02032     bt= uiDefButR_prop(block, NUMSLI, 0, "R ",  0, -60, butwidth, UI_UNIT_Y, ptr, prop, 0, 0.0, 0.0, 0, 3, "Red");
02033     uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
02034     bt= uiDefButR_prop(block, NUMSLI, 0, "G ",  0, -80, butwidth, UI_UNIT_Y, ptr, prop, 1, 0.0, 0.0, 0, 3, "Green");
02035     uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
02036     bt= uiDefButR_prop(block, NUMSLI, 0, "B ",  0, -100, butwidth, UI_UNIT_Y, ptr, prop, 2, 0.0, 0.0, 0, 3, "Blue");
02037     uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
02038 
02039     // could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", ICON_NONE);
02040     // but need to use uiButSetFunc for updating other fake buttons
02041     
02042     /* HSV values */
02043     uiBlockBeginAlign(block);
02044     bt= uiDefButF(block, NUMSLI, 0, "H ",   0, -60, butwidth, UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, "Hue");
02045     uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
02046     bt= uiDefButF(block, NUMSLI, 0, "S ",   0, -80, butwidth, UI_UNIT_Y, hsv+1, 0.0, 1.0, 10, 3, "Saturation");
02047     uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
02048     bt= uiDefButF(block, NUMSLI, 0, "V ",   0, -100, butwidth, UI_UNIT_Y, hsv+2, 0.0, max, 10, 3, "Value");
02049     uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
02050     uiBlockEndAlign(block);
02051 
02052     if(rgb[3] != FLT_MAX) {
02053         bt= uiDefButR_prop(block, NUMSLI, 0, "A ",  0, -120, butwidth, UI_UNIT_Y, ptr, prop, 3, 0.0, 0.0, 0, 0, "Alpha");
02054         uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
02055     }
02056     else {
02057         rgb[3]= 1.0f;
02058     }
02059 
02060     BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
02061 
02062     bt= uiDefBut(block, TEX, 0, "Hex: ", 0, -60, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
02063     uiButSetFunc(bt, do_hex_rna_cb, bt, hexcol);
02064     uiDefBut(block, LABEL, 0, "(Gamma Corrected)", 0, -80, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
02065 
02066     rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
02067 
02068     picker_new_hide_reveal(block, colormode);
02069 }
02070 
02071 
02072 static int ui_picker_small_wheel_cb(const bContext *UNUSED(C), uiBlock *block, wmEvent *event)
02073 {
02074     float add= 0.0f;
02075     
02076     if(event->type==WHEELUPMOUSE)
02077         add= 0.05f;
02078     else if(event->type==WHEELDOWNMOUSE)
02079         add= -0.05f;
02080     
02081     if(add!=0.0f) {
02082         uiBut *but;
02083         
02084         for(but= block->buttons.first; but; but= but->next) {
02085             if(but->type==HSVCUBE && but->active==NULL) {
02086                 uiPopupBlockHandle *popup= block->handle;
02087                 float col[3];
02088                 float *hsv= ui_block_hsv_get(block);
02089                 
02090                 ui_get_but_vectorf(but, col);
02091                 
02092                 rgb_to_hsv_compat(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
02093                 hsv[2]= CLAMPIS(hsv[2]+add, 0.0f, 1.0f);
02094                 hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
02095 
02096                 ui_set_but_vectorf(but, col);
02097                 
02098                 ui_update_block_buts_rgb(block, col);
02099                 if(popup)
02100                     popup->menuretval= UI_RETURN_UPDATE;
02101                 
02102                 return 1;
02103             }
02104         }
02105     }
02106     return 0;
02107 }
02108 
02109 uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
02110 {
02111     uiBut *but= arg_but;
02112     uiBlock *block;
02113     
02114     block= uiBeginBlock(C, handle->region, __func__, UI_EMBOSS);
02115     
02116     if (but->rnaprop) {
02117         if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
02118             block->color_profile = BLI_PR_NONE;
02119         }
02120     }
02121     
02122     uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
02123     
02124     copy_v3_v3(handle->retvec, but->editvec);
02125     
02126     uiBlockPicker(block, handle->retvec, &but->rnapoin, but->rnaprop);
02127     
02128     block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN|UI_BLOCK_OUT_1;
02129     uiBoundsBlock(block, 10);
02130     
02131     block->block_event_func= ui_picker_small_wheel_cb;
02132     
02133     /* and lets go */
02134     block->direction= UI_TOP;
02135     
02136     return block;
02137 }
02138 
02139 /************************ Popup Menu Memory ****************************/
02140 
02141 static int ui_popup_string_hash(char *str)
02142 {
02143     /* sometimes button contains hotkey, sometimes not, strip for proper compare */
02144     int hash;
02145     char *delimit= strchr(str, '|');
02146 
02147     if(delimit) *delimit= 0;
02148     hash= BLI_ghashutil_strhash(str);
02149     if(delimit) *delimit= '|';
02150 
02151     return hash;
02152 }
02153 
02154 static int ui_popup_menu_hash(const char *str)
02155 {
02156     return BLI_ghashutil_strhash(str);
02157 }
02158 
02159 /* but == NULL read, otherwise set */
02160 uiBut *ui_popup_menu_memory(uiBlock *block, uiBut *but)
02161 {
02162     static int mem[256], first=1;
02163     int hash= block->puphash;
02164     
02165     if(first) {
02166         /* init */
02167         memset(mem, -1, sizeof(mem));
02168         first= 0;
02169     }
02170 
02171     if(but) {
02172         /* set */
02173         mem[hash & 255 ]= ui_popup_string_hash(but->str);
02174         return NULL;
02175     }
02176     else {
02177         /* get */
02178         for(but=block->buttons.first; but; but=but->next)
02179             if(ui_popup_string_hash(but->str) == mem[hash & 255])
02180                 return but;
02181 
02182         return NULL;
02183     }
02184 }
02185 
02186 /******************** Popup Menu with callback or string **********************/
02187 
02188 struct uiPopupMenu {
02189     uiBlock *block;
02190     uiLayout *layout;
02191     uiBut *but;
02192 
02193     int mx, my, popup, slideout;
02194     int startx, starty, maxrow;
02195 
02196     uiMenuCreateFunc menu_func;
02197     void *menu_arg;
02198 };
02199 
02200 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
02201 {
02202     uiBlock *block;
02203     uiBut *bt;
02204     uiPopupMenu *pup= arg_pup;
02205     int offset[2], direction, minwidth, width, height, flip;
02206 
02207     if(pup->menu_func) {
02208         pup->block->handle= handle;
02209         pup->menu_func(C, pup->layout, pup->menu_arg);
02210         pup->block->handle= NULL;
02211     }
02212 
02213     if(pup->but) {
02214         /* minimum width to enforece */
02215         minwidth= pup->but->x2 - pup->but->x1;
02216 
02217         if(pup->but->type == PULLDOWN || pup->but->menu_create_func) {
02218             direction= UI_DOWN;
02219             flip= 1;
02220         }
02221         else {
02222             direction= UI_TOP;
02223             flip= 0;
02224         }
02225     }
02226     else {
02227         minwidth= 50;
02228         direction= UI_DOWN;
02229         flip= 1;
02230     }
02231 
02232     block= pup->block;
02233     
02234     /* in some cases we create the block before the region,
02235        so we set it delayed here if necessary */
02236     if(BLI_findindex(&handle->region->uiblocks, block) == -1)
02237         uiBlockSetRegion(block, handle->region);
02238 
02239     block->direction= direction;
02240 
02241     uiBlockLayoutResolve(block, &width, &height);
02242 
02243     uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
02244     
02245     if(pup->popup) {
02246         uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
02247         uiBlockSetDirection(block, direction);
02248 
02249         /* offset the mouse position, possibly based on earlier selection */
02250         if((block->flag & UI_BLOCK_POPUP_MEMORY) &&
02251             (bt= ui_popup_menu_memory(block, NULL))) {
02252             /* position mouse on last clicked item, at 0.8*width of the
02253                button, so it doesn't overlap the text too much, also note
02254                the offset is negative because we are inverse moving the
02255                block to be under the mouse */
02256             offset[0]= -(bt->x1 + 0.8f*(bt->x2 - bt->x1));
02257             offset[1]= -(bt->y1 + 0.5f*UI_UNIT_Y);
02258         }
02259         else {
02260             /* position mouse at 0.8*width of the button and below the tile
02261                on the first item */
02262             offset[0]= 0;
02263             for(bt=block->buttons.first; bt; bt=bt->next)
02264                 offset[0]= MIN2(offset[0], -(bt->x1 + 0.8f*(bt->x2 - bt->x1)));
02265 
02266             offset[1]= 1.5*UI_UNIT_Y;
02267         }
02268 
02269         block->minbounds= minwidth;
02270         uiMenuPopupBoundsBlock(block, 1, offset[0], offset[1]);
02271     }
02272     else {
02273         /* for a header menu we set the direction automatic */
02274         if(!pup->slideout && flip) {
02275             ScrArea *sa= CTX_wm_area(C);
02276             if(sa && sa->headertype==HEADERDOWN) {
02277                 ARegion *ar= CTX_wm_region(C);
02278                 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
02279                     uiBlockSetDirection(block, UI_TOP);
02280                     uiBlockFlipOrder(block);
02281                 }
02282             }
02283         }
02284 
02285         block->minbounds= minwidth;
02286         uiTextBoundsBlock(block, 50);
02287     }
02288 
02289     /* if menu slides out of other menu, override direction */
02290     if(pup->slideout)
02291         uiBlockSetDirection(block, UI_RIGHT);
02292 
02293     uiEndBlock(C, block);
02294 
02295     return pup->block;
02296 }
02297 
02298 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg, char *str)
02299 {
02300     wmWindow *window= CTX_wm_window(C);
02301     uiStyle *style= UI_GetStyle();
02302     uiPopupBlockHandle *handle;
02303     uiPopupMenu *pup;
02304     pup= MEM_callocN(sizeof(uiPopupMenu), __func__);
02305     pup->block= uiBeginBlock(C, NULL, __func__, UI_EMBOSSP);
02306     pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
02307     pup->slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
02308     pup->but= but;
02309     uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
02310 
02311     if(!but) {
02312         /* no button to start from, means we are a popup */
02313         pup->mx= window->eventstate->x;
02314         pup->my= window->eventstate->y;
02315         pup->popup= 1;
02316         pup->block->flag |= UI_BLOCK_NO_FLIP;
02317     }
02318     /* some enums reversing is strange, currently we have no good way to
02319      * reverse some enum's but not others, so reverse all so the first menu
02320      * items are always close to the mouse cursor */
02321 #if 0
02322     else {
02323         /* if this is an rna button then we can assume its an enum
02324          * flipping enums is generally not good since the order can be
02325          * important [#28786] */
02326         if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
02327             pup->block->flag |= UI_BLOCK_NO_FLIP;
02328         }
02329     }
02330 #endif
02331 
02332     if(str) {
02333         /* menu is created from a string */
02334         pup->menu_func= ui_block_func_MENUSTR;
02335         pup->menu_arg= str;
02336     }
02337     else {
02338         /* menu is created from a callback */
02339         pup->menu_func= menu_func;
02340         pup->menu_arg= arg;
02341     }
02342     
02343     handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
02344 
02345     if(!but) {
02346         handle->popup= 1;
02347 
02348         UI_add_popup_handlers(C, &window->modalhandlers, handle);
02349         WM_event_add_mousemove(C);
02350     }
02351     
02352     MEM_freeN(pup);
02353 
02354     return handle;
02355 }
02356 
02357 /******************** Popup Menu API with begin and end ***********************/
02358 
02359 /* only return handler, and set optional title */
02360 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
02361 {
02362     uiStyle *style= UI_GetStyle();
02363     uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "popup menu");
02364     uiBut *but;
02365     
02366     pup->block= uiBeginBlock(C, NULL, __func__, UI_EMBOSSP);
02367     pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
02368     pup->block->puphash= ui_popup_menu_hash(title);
02369     pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
02370     uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
02371 
02372     /* create in advance so we can let buttons point to retval already */
02373     pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
02374     
02375     /* create title button */
02376     if(title && title[0]) {
02377         char titlestr[256];
02378         
02379         if(icon) {
02380             BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
02381             uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
02382         }
02383         else {
02384             but= uiDefBut(pup->block, LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
02385             but->flag= UI_TEXT_LEFT;
02386         }
02387     }
02388 
02389     return pup;
02390 }
02391 
02392 /* set the whole structure to work */
02393 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
02394 {
02395     wmWindow *window= CTX_wm_window(C);
02396     uiPopupBlockHandle *menu;
02397     
02398     pup->popup= 1;
02399     pup->mx= window->eventstate->x;
02400     pup->my= window->eventstate->y;
02401     
02402     menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
02403     menu->popup= 1;
02404     
02405     UI_add_popup_handlers(C, &window->modalhandlers, menu);
02406     WM_event_add_mousemove(C);
02407     
02408     MEM_freeN(pup);
02409 }
02410 
02411 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
02412 {
02413     return pup->layout;
02414 }
02415 
02416 /*************************** Standard Popup Menus ****************************/
02417 
02418 static void operator_name_cb(bContext *C, void *arg, int retval)
02419 {
02420     const char *opname= arg;
02421 
02422     if(opname && retval > 0)
02423         WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
02424 }
02425 
02426 static void operator_cb(bContext *C, void *arg, int retval)
02427 {
02428     wmOperator *op= arg;
02429     
02430     if(op && retval > 0)
02431         WM_operator_call(C, op);
02432     else
02433         WM_operator_free(op);
02434 }
02435 
02436 static void confirm_cancel_operator(void *opv)
02437 {
02438     WM_operator_free(opv);
02439 }
02440 
02441 static void vconfirm_opname(bContext *C, const char *opname, const char *title, const char *itemfmt, va_list ap)
02442 {
02443     uiPopupBlockHandle *handle;
02444     char *s, buf[512];
02445 
02446     s= buf;
02447     if (title) s+= sprintf(s, "%s%%t|", title);
02448     vsnprintf(s, sizeof(buf) - (s - buf), itemfmt, ap);
02449     buf[sizeof(buf) - 1]= '\0';
02450 
02451     handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
02452 
02453     handle->popup_func= operator_name_cb;
02454     handle->popup_arg= (void *)opname;
02455 }
02456 
02457 static void confirm_operator(bContext *C, wmOperator *op, const char *title, const char *item)
02458 {
02459     uiPopupBlockHandle *handle;
02460     char *s, buf[512];
02461     
02462     s= buf;
02463     if (title) s+= sprintf(s, "%s%%t|%s", title, item);
02464     (void)s;
02465     
02466     handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
02467 
02468     handle->popup_func= operator_cb;
02469     handle->popup_arg= op;
02470     handle->cancel_func= confirm_cancel_operator;
02471 }
02472 
02473 void uiPupMenuOkee(bContext *C, const char *opname, const char *str, ...)
02474 {
02475     va_list ap;
02476     char titlestr[256];
02477 
02478     BLI_snprintf(titlestr, sizeof(titlestr), "OK? %%i%d", ICON_QUESTION);
02479 
02480     va_start(ap, str);
02481     vconfirm_opname(C, opname, titlestr, str, ap);
02482     va_end(ap);
02483 }
02484 
02485 /* note, only call this is the file exists,
02486  * the case where the file does not exist so can be saved without a
02487  * popup must be checked for already, since saving from here
02488  * will free the operator which will break invoke().
02489  * The operator state for this is implicitly OPERATOR_RUNNING_MODAL */
02490 void uiPupMenuSaveOver(bContext *C, wmOperator *op, const char *filename)
02491 {
02492     confirm_operator(C, op, "Save Over", filename);
02493 }
02494 
02495 void uiPupMenuNotice(bContext *C, const char *str, ...)
02496 {
02497     va_list ap;
02498 
02499     va_start(ap, str);
02500     vconfirm_opname(C, NULL, NULL, str, ap);
02501     va_end(ap);
02502 }
02503 
02504 void uiPupMenuError(bContext *C, const char *str, ...)
02505 {
02506     va_list ap;
02507     char nfmt[256];
02508     char titlestr[256];
02509 
02510     BLI_snprintf(titlestr, sizeof(titlestr), "Error %%i%d", ICON_ERROR);
02511 
02512     BLI_strncpy(nfmt, str, sizeof(nfmt));
02513 
02514     va_start(ap, str);
02515     vconfirm_opname(C, NULL, titlestr, nfmt, ap);
02516     va_end(ap);
02517 }
02518 
02519 void uiPupMenuReports(bContext *C, ReportList *reports)
02520 {
02521     Report *report;
02522     DynStr *ds;
02523     char *str;
02524 
02525     if(!reports || !reports->list.first)
02526         return;
02527     if(!CTX_wm_window(C))
02528         return;
02529 
02530     ds= BLI_dynstr_new();
02531 
02532     for(report=reports->list.first; report; report=report->next) {
02533         if(report->type < reports->printlevel)
02534             ; /* pass */
02535         else if(report->type >= RPT_ERROR)
02536             BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
02537         else if(report->type >= RPT_WARNING)
02538             BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
02539         else if(report->type >= RPT_INFO)
02540             BLI_dynstr_appendf(ds, "Info %%i%d%%t|%s", ICON_INFO, report->message);
02541     }
02542 
02543     str= BLI_dynstr_get_cstring(ds);
02544     if(str[0] != '\0')
02545         ui_popup_menu_create(C, NULL, NULL, NULL, NULL, str);
02546     MEM_freeN(str);
02547 
02548     BLI_dynstr_free(ds);
02549 }
02550 
02551 void uiPupMenuInvoke(bContext *C, const char *idname)
02552 {
02553     uiPopupMenu *pup;
02554     uiLayout *layout;
02555     Menu menu;
02556     MenuType *mt= WM_menutype_find(idname, TRUE);
02557 
02558     if(mt==NULL) {
02559         printf("%s: named menu \"%s\" not found\n", __func__, idname);
02560         return;
02561     }
02562 
02563     if(mt->poll && mt->poll(C, mt)==0)
02564         return;
02565 
02566     pup= uiPupMenuBegin(C, mt->label, ICON_NONE);
02567     layout= uiPupMenuLayout(pup);
02568 
02569     menu.layout= layout;
02570     menu.type= mt;
02571 
02572     mt->draw(C, &menu);
02573 
02574     uiPupMenuEnd(C, pup);
02575 }
02576 
02577 
02578 /*************************** Popup Block API **************************/
02579 
02580 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext)
02581 {
02582     wmWindow *window= CTX_wm_window(C);
02583     uiPopupBlockHandle *handle;
02584     
02585     handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
02586     handle->popup= 1;
02587     handle->optype= (opname)? WM_operatortype_find(opname, 0): NULL;
02588     handle->opcontext= opcontext;
02589     
02590     UI_add_popup_handlers(C, &window->modalhandlers, handle);
02591     WM_event_add_mousemove(C);
02592 }
02593 
02594 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
02595 {
02596     uiPupBlockO(C, func, arg, NULL, 0);
02597 }
02598 
02599 void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg)
02600 {
02601     wmWindow *window= CTX_wm_window(C);
02602     uiPopupBlockHandle *handle;
02603     
02604     handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
02605     handle->popup= 1;
02606     handle->retvalue= 1;
02607 
02608     handle->popup_arg= arg;
02609     handle->popup_func= popup_func;
02610     handle->cancel_func= cancel_func;
02611     // handle->opcontext= opcontext;
02612     
02613     UI_add_popup_handlers(C, &window->modalhandlers, handle);
02614     WM_event_add_mousemove(C);
02615 }
02616 
02617 #if 0 /* UNUSED */
02618 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
02619 {
02620     wmWindow *window= CTX_wm_window(C);
02621     uiPopupBlockHandle *handle;
02622     
02623     handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
02624     handle->popup= 1;
02625     handle->retvalue= 1;
02626 
02627     handle->popup_arg= op;
02628     handle->popup_func= operator_cb;
02629     handle->cancel_func= confirm_cancel_operator;
02630     handle->opcontext= opcontext;
02631     
02632     UI_add_popup_handlers(C, &window->modalhandlers, handle);
02633     WM_event_add_mousemove(C);
02634 }
02635 #endif
02636 
02637 void uiPupBlockClose(bContext *C, uiBlock *block)
02638 {
02639     if(block->handle) {
02640         UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, block->handle);
02641         ui_popup_block_free(C, block->handle);
02642     }
02643 }
02644 
02645 float *ui_block_hsv_get(uiBlock *block)
02646 {
02647     return block->_hsv;
02648 }