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