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