2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
21 * Contributor(s): Blender Foundation
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/interface/interface_regions.c
27 * \ingroup edinterface
37 #include "MEM_guardedalloc.h"
39 #include "DNA_userdef_types.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_dynstr.h"
45 #include "BLI_ghash.h"
47 #include "BKE_context.h"
48 #include "BKE_screen.h"
53 #include "wm_subwindow.h"
54 #include "wm_window.h"
56 #include "RNA_access.h"
60 #include "UI_interface.h"
61 #include "UI_interface_icons.h"
62 #include "UI_view2d.h"
65 #include "BLF_translation.h"
67 #include "ED_screen.h"
69 #include "interface_intern.h"
71 #define MENU_SEPR_HEIGHT 6
73 #define MENU_SHADOW_SIDE 8
74 #define MENU_SHADOW_BOTTOM 10
77 /*********************** Menu Data Parsing ********************* */
79 typedef struct MenuEntry {
86 typedef struct MenuData {
92 int nitems, itemssize;
95 static MenuData *menudata_new(const char *instr)
97 MenuData *md= MEM_mallocN(sizeof(*md), "MenuData");
103 md->nitems= md->itemssize= 0;
108 static void menudata_set_title(MenuData *md, const char *title, int titleicon)
113 md->titleicon= titleicon;
116 static void menudata_add_item(MenuData *md, const char *str, int retval, int icon, int sepr)
118 if (md->nitems==md->itemssize) {
119 int nsize= md->itemssize?(md->itemssize<<1):1;
120 MenuEntry *oitems= md->items;
122 md->items= MEM_mallocN(nsize*sizeof(*md->items), "md->items");
124 memcpy(md->items, oitems, md->nitems*sizeof(*md->items));
128 md->itemssize= nsize;
131 md->items[md->nitems].str= str;
132 md->items[md->nitems].retval= retval;
133 md->items[md->nitems].icon= icon;
134 md->items[md->nitems].sepr= sepr;
138 static void menudata_free(MenuData *md)
140 MEM_freeN((void *)md->instr);
142 MEM_freeN(md->items);
147 * Parse menu description strings, string is of the
148 * form "[sss%t|]{(sss[%xNN]|), (%l|), (sss%l|)}", ssss%t indicates the
149 * menu title, sss or sss%xNN indicates an option,
150 * if %xNN is given then NN is the return value if
151 * that option is selected otherwise the return value
152 * is the index of the option (starting with 1). %l
153 * indicates a seperator, sss%l indicates a label and
156 * @param str String to be parsed.
157 * @retval new menudata structure, free with menudata_free()
159 static MenuData *decompose_menu_string(const char *str)
161 char *instr= BLI_strdup(str);
162 MenuData *md= menudata_new(instr);
163 const char *nitem= NULL;
165 int nicon=0, nretval= 1, nitem_is_title= 0, nitem_is_sepr= 0;
176 } else if (s[1]=='t') {
177 nitem_is_title= (s != instr); /* check for empty title */
181 } else if (s[1]=='l') {
183 if(!nitem) nitem= "";
187 } else if (s[1]=='i') {
193 } else if (c=='|' || c == '\n' || c=='\0') {
198 menudata_set_title(md, nitem, nicon);
201 else if(nitem_is_sepr) {
202 /* prevent separator to get a value */
203 menudata_add_item(md, nitem, -1, nicon, 1);
204 nretval= md->nitems+1;
208 menudata_add_item(md, nitem, nretval, nicon, 0);
209 nretval= md->nitems+1;
229 void ui_set_name_menu(uiBut *but, int value)
234 md= decompose_menu_string(but->str);
235 for (i=0; i<md->nitems; i++) {
236 if (md->items[i].retval==value) {
237 BLI_strncpy(but->drawstr, md->items[i].str, sizeof(but->drawstr));
245 int ui_step_name_menu(uiBut *but, int step)
248 int value= ui_get_but_val(but);
251 md= decompose_menu_string(but->str);
252 for (i=0; i<md->nitems; i++)
253 if (md->items[i].retval==value)
257 /* skip separators */
258 for(; i<md->nitems-1; i++) {
259 if(md->items[i+1].retval != -1) {
260 value= md->items[i+1].retval;
267 /* skip separators */
269 if(md->items[i-1].retval != -1) {
270 value= md->items[i-1].retval;
283 /******************** Creating Temporary regions ******************/
285 static ARegion *ui_add_temporary_region(bScreen *sc)
289 ar= MEM_callocN(sizeof(ARegion), "area region");
290 BLI_addtail(&sc->regionbase, ar);
292 ar->regiontype= RGN_TYPE_TEMPORARY;
293 ar->alignment= RGN_ALIGN_FLOAT;
298 static void ui_remove_temporary_region(bContext *C, bScreen *sc, ARegion *ar)
301 wm_draw_region_clear(CTX_wm_window(C), ar);
303 ED_region_exit(C, ar);
304 BKE_area_region_free(NULL, ar); /* NULL: no spacetype */
305 BLI_freelinkN(&sc->regionbase, ar);
308 /************************* Creating Tooltips **********************/
310 #define MAX_TOOLTIP_LINES 8
312 typedef struct uiTooltipData {
315 char lines[MAX_TOOLTIP_LINES][512];
316 unsigned int color[MAX_TOOLTIP_LINES];
318 int toth, spaceh, lineh;
321 static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
323 uiTooltipData *data= ar->regiondata;
324 rcti bbox= data->bbox;
327 ui_draw_menu_back(UI_GetStyle(), NULL, &data->bbox);
330 uiStyleFontSet(&data->fstyle);
332 bbox.ymax= bbox.ymax - 0.5f*((bbox.ymax - bbox.ymin) - data->toth);
333 bbox.ymin= bbox.ymax - data->lineh;
335 for(a=0; a<data->totline; a++) {
336 cpack(data->color[a]);
337 uiStyleFontDraw(&data->fstyle, &bbox, data->lines[a]);
338 bbox.ymin -= data->lineh + data->spaceh;
339 bbox.ymax -= data->lineh + data->spaceh;
343 static void ui_tooltip_region_free_cb(ARegion *ar)
347 data= ar->regiondata;
349 ar->regiondata= NULL;
352 ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
354 uiStyle *style= UI_GetStyle();
355 static ARegionType type;
360 float fonth, fontw, aspect= but->block->aspect;
361 float x1f, x2f, y1f, y2f;
362 int x1, x2, y1, y2, winx, winy, ofsx, ofsy, w, h, a;
364 if(but->flag & UI_BUT_NO_TOOLTIP)
367 /* create tooltip data */
368 data= MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
370 /* special case, enum rna buttons only have enum item description, use general enum description too before the specific one */
371 if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
372 const char *descr= RNA_property_description(but->rnaprop);
373 if(descr && descr[0]) {
374 BLI_strncpy(data->lines[data->totline], descr, sizeof(data->lines[0]));
375 data->color[data->totline]= 0xFFFFFF;
379 if(ELEM(but->type, ROW, MENU)) {
380 EnumPropertyItem *item;
381 int i, totitem, free;
383 RNA_property_enum_items_gettexted(C, &but->rnapoin, but->rnaprop, &item, &totitem, &free);
385 for(i=0; i<totitem; i++) {
386 if(item[i].identifier[0] && item[i].value == (int)but->hardmax) {
387 if(item[i].description[0]) {
388 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), "%s: %s", item[i].name, item[i].description);
389 data->color[data->totline]= 0xDDDDDD;
401 if(but->tip && but->tip[0] != '\0') {
402 BLI_strncpy(data->lines[data->totline], but->tip, sizeof(data->lines[0]));
403 data->color[data->totline]= 0xFFFFFF;
407 if(but->optype && !(but->block->flag & UI_BLOCK_LOOP)) {
408 /* operator keymap (not menus, they already have it) */
409 prop= (but->opptr)? but->opptr->data: NULL;
411 if(WM_key_event_operator_string(C, but->optype->idname, but->opcontext, prop, TRUE, buf, sizeof(buf))) {
412 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), buf);
413 data->color[data->totline]= 0x888888;
418 if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
420 ui_get_but_string(but, buf, sizeof(buf));
422 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf);
423 data->color[data->totline]= 0x888888;
429 int unit_type= uiButGetUnitType(but);
431 if (unit_type == PROP_UNIT_ROTATION) {
432 if (RNA_property_type(but->rnaprop) == PROP_FLOAT) {
433 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);
434 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value);
435 data->color[data->totline]= 0x888888;
440 if(but->flag & UI_BUT_DRIVEN) {
441 if(ui_but_anim_expression_get(but, buf, sizeof(buf))) {
443 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf);
444 data->color[data->totline]= 0x888888;
450 if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
451 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));
452 data->color[data->totline]= 0x888888;
456 if(but->rnapoin.id.data) {
457 ID *id= but->rnapoin.id.data;
458 if(id->lib && id->lib->name) {
459 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name);
460 data->color[data->totline]= 0x888888;
465 else if (but->optype) {
468 opptr= uiButGetOperatorPtrRNA(but); /* allocated when needed, the button owns it */
470 str= WM_operator_pystring(C, but->optype, opptr, 0);
473 if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
474 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str);
475 data->color[data->totline]= 0x888888;
481 /* second check if we are disabled - why */
482 if(but->flag & UI_BUT_DISABLED) {
483 const char *poll_msg;
484 CTX_wm_operator_poll_msg_set(C, NULL);
485 WM_operator_poll_context(C, but->optype, but->opcontext);
486 poll_msg= CTX_wm_operator_poll_msg_get(C);
488 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), poll_msg);
489 data->color[data->totline]= 0x6666ff; /* alert */
494 else if (ELEM(but->type, MENU, PULLDOWN)) {
495 if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
496 MenuType *mt= uiButGetMenuType(but);
498 BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), mt->idname);
499 data->color[data->totline]= 0x888888;
506 assert(data->totline < MAX_TOOLTIP_LINES);
508 if(data->totline == 0) {
513 /* create area region */
514 ar= ui_add_temporary_region(CTX_wm_screen(C));
516 memset(&type, 0, sizeof(ARegionType));
517 type.draw= ui_tooltip_region_draw_cb;
518 type.free= ui_tooltip_region_free_cb;
521 /* set font, get bb */
522 data->fstyle= style->widget; /* copy struct */
523 data->fstyle.align= UI_STYLE_TEXT_CENTER;
524 uiStyleFontSet(&data->fstyle);
526 /* these defines may need to be tweaked depending on font */
527 #define TIP_MARGIN_Y 2
528 #define TIP_BORDER_X 16.0f
529 #define TIP_BORDER_Y 6.0f
531 h= BLF_height_max(data->fstyle.uifont_id);
533 for(a=0, fontw=0, fonth=0; a<data->totline; a++) {
534 w= BLF_width(data->fstyle.uifont_id, data->lines[a]);
535 fontw= MAX2(fontw, w);
536 fonth += (a == 0)? h: h+TIP_MARGIN_Y;
541 ar->regiondata= data;
545 data->spaceh= TIP_MARGIN_Y;
548 /* compute position */
549 ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
550 ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
552 x1f= (but->x1 + but->x2) * 0.5f + ofsx - (TIP_BORDER_X * aspect);
553 x2f= x1f + fontw + (TIP_BORDER_X * aspect);
554 y2f= but->y1 + ofsy - (TIP_BORDER_Y * aspect);
555 y1f= y2f - fonth*aspect - (TIP_BORDER_Y * aspect);
561 /* copy to int, gets projected if possible too */
562 x1= x1f; y1= y1f; x2= x2f; y2= y2f;
565 /* XXX temp, region v2ds can be empty still */
566 if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
567 UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
568 UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
571 x1 += butregion->winrct.xmin;
572 x2 += butregion->winrct.xmin;
573 y1 += butregion->winrct.ymin;
574 y2 += butregion->winrct.ymin;
577 wm_window_get_size(CTX_wm_window(C), &winx, &winy);
590 /* ensure at least 5 px above screen bounds
591 * 25 is just a guess to be above the menu item */
597 /* widget rect, in region coords */
598 data->bbox.xmin= MENU_SHADOW_SIDE;
599 data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
600 data->bbox.ymin= MENU_SHADOW_BOTTOM;
601 data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
603 /* region bigger for shadow */
604 ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
605 ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
606 ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
607 ar->winrct.ymax= y2 + MENU_TOP;
610 ED_region_init(C, ar);
612 /* notify change and redraw */
613 ED_region_tag_redraw(ar);
618 void ui_tooltip_free(bContext *C, ARegion *ar)
620 ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
624 /************************* Creating Search Box **********************/
626 struct uiSearchItems {
627 int maxitem, totitem, maxstrlen;
629 int offset, offset_i; /* offset for inserting in array */
630 int more; /* flag indicating there are more items */
636 AutoComplete *autocpl;
640 typedef struct uiSearchboxData {
644 int active; /* index in items array */
645 int noback; /* when menu opened with enough space for this */
646 int preview; /* draw thumbnail previews, rather than list */
647 int prv_rows, prv_cols;
650 #define SEARCH_ITEMS 10
652 /* exported for use by search callbacks */
653 /* returns zero if nothing to add */
654 int uiSearchItemAdd(uiSearchItems *items, const char *name, void *poin, int iconid)
656 /* hijack for autocomplete */
658 autocomplete_do_name(items->autocpl, name);
662 /* hijack for finding active item */
664 if(poin==items->active)
665 items->offset_i= items->totitem;
670 if(items->totitem>=items->maxitem) {
675 /* skip first items in list */
676 if(items->offset_i > 0) {
682 BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
684 items->pointers[items->totitem]= poin;
686 items->icons[items->totitem]= iconid;
693 int uiSearchBoxhHeight(void)
695 return SEARCH_ITEMS*UI_UNIT_Y + 2*MENU_TOP;
698 /* ar is the search box itself */
699 static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
701 uiSearchboxData *data= ar->regiondata;
706 if(data->items.totitem==0)
708 else if(data->active > data->items.totitem) {
709 if(data->items.more) {
710 data->items.offset++;
711 data->active= data->items.totitem;
712 ui_searchbox_update(C, ar, but, 0);
715 data->active= data->items.totitem;
717 else if(data->active < 1) {
718 if(data->items.offset) {
719 data->items.offset--;
721 ui_searchbox_update(C, ar, but, 0);
723 else if(data->active < 0)
727 ED_region_tag_redraw(ar);
730 static void ui_searchbox_butrect(rcti *rect, uiSearchboxData *data, int itemnr)
732 /* thumbnail preview */
734 int buth = (data->bbox.ymax - data->bbox.ymin - 2*MENU_TOP) / data->prv_rows;
735 int butw = (data->bbox.xmax - data->bbox.xmin) / data->prv_cols;
740 col = itemnr % data->prv_cols;
741 row = itemnr / data->prv_cols;
743 rect->xmin += col * butw;
744 rect->xmax = rect->xmin + butw;
746 rect->ymax = data->bbox.ymax - MENU_TOP - (row * buth);
747 rect->ymin = rect->ymax - buth;
751 int buth= (data->bbox.ymax-data->bbox.ymin - 2*MENU_TOP)/SEARCH_ITEMS;
754 rect->xmin= data->bbox.xmin + 3.0f;
755 rect->xmax= data->bbox.xmax - 3.0f;
757 rect->ymax= data->bbox.ymax - MENU_TOP - itemnr*buth;
758 rect->ymin= rect->ymax - buth;
763 /* x and y in screencoords */
764 int ui_searchbox_inside(ARegion *ar, int x, int y)
766 uiSearchboxData *data= ar->regiondata;
768 return(BLI_in_rcti(&data->bbox, x-ar->winrct.xmin, y-ar->winrct.ymin));
771 /* string validated to be of correct length (but->hardmax) */
772 void ui_searchbox_apply(uiBut *but, ARegion *ar)
774 uiSearchboxData *data= ar->regiondata;
776 but->func_arg2= NULL;
779 char *name= data->items.names[data->active-1];
780 char *cpoin= strchr(name, '|');
782 if(cpoin) cpoin[0]= 0;
783 BLI_strncpy(but->editstr, name, data->items.maxstrlen);
784 if(cpoin) cpoin[0]= '|';
786 but->func_arg2= data->items.pointers[data->active-1];
790 void ui_searchbox_event(bContext *C, ARegion *ar, uiBut *but, wmEvent *event)
792 uiSearchboxData *data= ar->regiondata;
794 switch(event->type) {
797 ui_searchbox_select(C, ar, but, -1);
801 ui_searchbox_select(C, ar, but, 1);
804 if(BLI_in_rcti(&ar->winrct, event->x, event->y)) {
808 for(a=0; a<data->items.totitem; a++) {
809 ui_searchbox_butrect(&rect, data, a);
810 if(BLI_in_rcti(&rect, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
811 if( data->active!= a+1) {
813 ui_searchbox_select(C, ar, but, 0);
823 /* ar is the search box itself */
824 void ui_searchbox_update(bContext *C, ARegion *ar, uiBut *but, int reset)
826 uiSearchboxData *data= ar->regiondata;
829 data->items.totitem= 0;
832 data->items.offset_i= data->items.offset;
835 data->items.offset_i= data->items.offset= 0;
839 if(but->search_func && but->func_arg2) {
840 data->items.active= but->func_arg2;
841 but->search_func(C, but->search_arg, but->editstr, &data->items);
842 data->items.active= NULL;
844 /* found active item, calculate real offset by centering it */
845 if(data->items.totitem) {
846 /* first case, begin of list */
847 if(data->items.offset_i < data->items.maxitem) {
848 data->active= data->items.offset_i+1;
849 data->items.offset_i= 0;
852 /* second case, end of list */
853 if(data->items.totitem - data->items.offset_i <= data->items.maxitem) {
854 data->active= 1 + data->items.offset_i - data->items.totitem + data->items.maxitem;
855 data->items.offset_i= data->items.totitem - data->items.maxitem;
858 /* center active item */
859 data->items.offset_i -= data->items.maxitem/2;
860 data->active= 1 + data->items.maxitem/2;
864 data->items.offset= data->items.offset_i;
865 data->items.totitem= 0;
871 but->search_func(C, but->search_arg, but->editstr, &data->items);
873 /* handle case where editstr is equal to one of items */
874 if(reset && data->active==0) {
877 for(a=0; a<data->items.totitem; a++) {
878 char *cpoin= strchr(data->items.names[a], '|');
880 if(cpoin) cpoin[0]= 0;
881 if(0==strcmp(but->editstr, data->items.names[a]))
883 if(cpoin) cpoin[0]= '|';
885 if(data->items.totitem==1 && but->editstr[0])
889 /* validate selected item */
890 ui_searchbox_select(C, ar, but, 0);
892 ED_region_tag_redraw(ar);
895 void ui_searchbox_autocomplete(bContext *C, ARegion *ar, uiBut *but, char *str)
897 uiSearchboxData *data= ar->regiondata;
900 data->items.autocpl= autocomplete_begin(str, ui_get_but_string_max_length(but));
902 but->search_func(C, but->search_arg, but->editstr, &data->items);
904 autocomplete_end(data->items.autocpl, str);
905 data->items.autocpl= NULL;
909 static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
911 uiSearchboxData *data= ar->regiondata;
914 wmOrtho2(-0.01f, ar->winx-0.01f, -0.01f, ar->winy-0.01f);
917 ui_draw_search_back(NULL, NULL, &data->bbox); /* style not used yet */
920 if(data->items.totitem) {
926 for(a=0; a<data->items.totitem; a++) {
927 ui_searchbox_butrect(&rect, data, a);
931 ui_draw_preview_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
933 ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
937 if(data->items.more) {
938 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
940 UI_icon_draw(rect.xmax-18, rect.ymin-7, ICON_TRIA_DOWN);
943 if(data->items.offset) {
944 ui_searchbox_butrect(&rect, data, 0);
946 UI_icon_draw(rect.xmin, rect.ymax-9, ICON_TRIA_UP);
952 for(a=0; a<data->items.totitem; a++) {
953 ui_searchbox_butrect(&rect, data, a);
956 ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], (a+1)==data->active?UI_ACTIVE:0);
960 if(data->items.more) {
961 ui_searchbox_butrect(&rect, data, data->items.maxitem-1);
963 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymin-9, ICON_TRIA_DOWN);
966 if(data->items.offset) {
967 ui_searchbox_butrect(&rect, data, 0);
969 UI_icon_draw((rect.xmax-rect.xmin)/2, rect.ymax-7, ICON_TRIA_UP);
976 static void ui_searchbox_region_free_cb(ARegion *ar)
978 uiSearchboxData *data= ar->regiondata;
981 /* free search data */
982 for(a=0; a<data->items.maxitem; a++)
983 MEM_freeN(data->items.names[a]);
984 MEM_freeN(data->items.names);
985 MEM_freeN(data->items.pointers);
986 MEM_freeN(data->items.icons);
989 ar->regiondata= NULL;
992 ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but)
994 uiStyle *style= UI_GetStyle();
995 static ARegionType type;
997 uiSearchboxData *data;
998 float aspect= but->block->aspect;
999 float x1f, x2f, y1f, y2f;
1000 int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
1002 /* create area region */
1003 ar= ui_add_temporary_region(CTX_wm_screen(C));
1005 memset(&type, 0, sizeof(ARegionType));
1006 type.draw= ui_searchbox_region_draw_cb;
1007 type.free= ui_searchbox_region_free_cb;
1010 /* create searchbox data */
1011 data= MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
1013 /* set font, get bb */
1014 data->fstyle= style->widget; /* copy struct */
1015 data->fstyle.align= UI_STYLE_TEXT_CENTER;
1016 ui_fontscale(&data->fstyle.points, aspect);
1017 uiStyleFontSet(&data->fstyle);
1019 ar->regiondata= data;
1021 /* special case, hardcoded feature, not draw backdrop when called from menus,
1022 assume for design that popup already added it */
1023 if(but->block->flag & UI_BLOCK_LOOP)
1026 if (but->a1 > 0 && but->a2 > 0) {
1028 data->prv_rows = but->a1;
1029 data->prv_cols = but->a2;
1032 /* compute position */
1033 if(but->block->flag & UI_BLOCK_LOOP) {
1034 /* this case is search menu inside other menu */
1035 /* we copy region size */
1037 ar->winrct= butregion->winrct;
1039 /* widget rect, in region coords */
1040 data->bbox.xmin= MENU_SHADOW_SIDE;
1041 data->bbox.xmax= (ar->winrct.xmax-ar->winrct.xmin) - MENU_SHADOW_SIDE;
1042 data->bbox.ymin= MENU_SHADOW_BOTTOM;
1043 data->bbox.ymax= (ar->winrct.ymax-ar->winrct.ymin) - MENU_SHADOW_BOTTOM;
1045 /* check if button is lower half */
1046 if( but->y2 < (but->block->miny+but->block->maxy)/2 ) {
1047 data->bbox.ymin += (but->y2-but->y1);
1050 data->bbox.ymax -= (but->y2-but->y1);
1054 x1f= but->x1 - 5; /* align text with button */
1055 x2f= but->x2 + 5; /* symmetrical */
1057 y1f= y2f - uiSearchBoxhHeight();
1059 ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
1060 ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
1068 if(x2f - x1f < 150) x2f= x1f+150; // XXX arbitrary
1070 /* copy to int, gets projected if possible too */
1071 x1= x1f; y1= y1f; x2= x2f; y2= y2f;
1074 if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
1075 UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
1076 UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
1079 x1 += butregion->winrct.xmin;
1080 x2 += butregion->winrct.xmin;
1081 y1 += butregion->winrct.ymin;
1082 y2 += butregion->winrct.ymin;
1085 wm_window_get_size(CTX_wm_window(C), &winx, &winy);
1089 if(x2 > winx + x1) {
1098 if(y1 < 0) { /* XXX butregion NULL check?, there is one above */
1100 UI_view2d_to_region_no_clip(&butregion->v2d, 0, but->y2 + ofsy, NULL, &newy1);
1101 newy1 += butregion->winrct.ymin;
1107 /* widget rect, in region coords */
1108 data->bbox.xmin= MENU_SHADOW_SIDE;
1109 data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
1110 data->bbox.ymin= MENU_SHADOW_BOTTOM;
1111 data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
1113 /* region bigger for shadow */
1114 ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
1115 ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
1116 ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
1117 ar->winrct.ymax= y2;
1120 /* adds subwindow */
1121 ED_region_init(C, ar);
1123 /* notify change and redraw */
1124 ED_region_tag_redraw(ar);
1126 /* prepare search data */
1127 if (data->preview) {
1128 data->items.maxitem= data->prv_rows * data->prv_cols;
1130 data->items.maxitem= SEARCH_ITEMS;
1132 data->items.maxstrlen= but->hardmax;
1133 data->items.totitem= 0;
1134 data->items.names= MEM_callocN(data->items.maxitem*sizeof(void *), "search names");
1135 data->items.pointers= MEM_callocN(data->items.maxitem*sizeof(void *), "search pointers");
1136 data->items.icons= MEM_callocN(data->items.maxitem*sizeof(int), "search icons");
1137 for(x1=0; x1<data->items.maxitem; x1++)
1138 data->items.names[x1]= MEM_callocN(but->hardmax+1, "search pointers");
1143 void ui_searchbox_free(bContext *C, ARegion *ar)
1145 ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
1148 /* sets red alert if button holds a string it can't find */
1149 /* XXX weak: search_func adds all partial matches... */
1150 void ui_but_search_test(uiBut *but)
1152 uiSearchItems *items;
1155 /* possibly very large lists (such as ID datablocks) only
1156 * only validate string RNA buts (not pointers) */
1157 if(but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
1161 items= MEM_callocN(sizeof(uiSearchItems), "search items");
1163 /* setup search struct */
1165 items->maxstrlen= 256;
1166 items->names= MEM_callocN(items->maxitem*sizeof(void *), "search names");
1167 for(x1=0; x1<items->maxitem; x1++)
1168 items->names[x1]= MEM_callocN(but->hardmax+1, "search names");
1170 but->search_func(but->block->evil_C, but->search_arg, but->drawstr, items);
1172 /* only redalert when we are sure of it, this can miss cases when >10 matches */
1173 if(items->totitem==0)
1174 uiButSetFlag(but, UI_BUT_REDALERT);
1175 else if(items->more==0) {
1176 for(x1= 0; x1<items->totitem; x1++)
1177 if(strcmp(but->drawstr, items->names[x1])==0)
1179 if(x1==items->totitem)
1180 uiButSetFlag(but, UI_BUT_REDALERT);
1183 for(x1=0; x1<items->maxitem; x1++)
1184 MEM_freeN(items->names[x1]);
1185 MEM_freeN(items->names);
1190 /************************* Creating Menu Blocks **********************/
1192 /* position block relative to but, result is in window space */
1193 static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
1196 uiSafetyRct *saferct;
1198 /*float aspect;*/ /*UNUSED*/
1199 int xsize, ysize, xof=0, yof=0, center;
1200 short dir1= 0, dir2=0;
1202 /* transform to window coordinates, using the source button region/block */
1203 butrct.xmin= but->x1; butrct.xmax= but->x2;
1204 butrct.ymin= but->y1; butrct.ymax= but->y2;
1206 ui_block_to_window_fl(butregion, but->block, &butrct.xmin, &butrct.ymin);
1207 ui_block_to_window_fl(butregion, but->block, &butrct.xmax, &butrct.ymax);
1209 /* calc block rect */
1210 if(block->minx == 0.0f && block->maxx == 0.0f) {
1211 if(block->buttons.first) {
1212 block->minx= block->miny= 10000;
1213 block->maxx= block->maxy= -10000;
1215 bt= block->buttons.first;
1217 if(bt->x1 < block->minx) block->minx= bt->x1;
1218 if(bt->y1 < block->miny) block->miny= bt->y1;
1220 if(bt->x2 > block->maxx) block->maxx= bt->x2;
1221 if(bt->y2 > block->maxy) block->maxy= bt->y2;
1227 /* we're nice and allow empty blocks too */
1228 block->minx= block->miny= 0;
1229 block->maxx= block->maxy= 20;
1233 /*aspect= (float)(block->maxx - block->minx + 4);*/ /*UNUSED*/
1234 ui_block_to_window_fl(butregion, but->block, &block->minx, &block->miny);
1235 ui_block_to_window_fl(butregion, but->block, &block->maxx, &block->maxy);
1237 //block->minx-= 2.0; block->miny-= 2.0;
1238 //block->maxx+= 2.0; block->maxy+= 2.0;
1240 xsize= block->maxx - block->minx+4; // 4 for shadow
1241 ysize= block->maxy - block->miny+4;
1242 /*aspect/= (float)xsize;*/ /*UNUSED*/
1245 int left=0, right=0, top=0, down=0;
1249 wm_window_get_size(window, &winx, &winy);
1251 if(block->direction & UI_CENTER) center= ysize/2;
1254 /* check if there's space at all */
1255 if( butrct.xmin-xsize > 0.0f) left= 1;
1256 if( butrct.xmax+xsize < winx) right= 1;
1257 if( butrct.ymin-ysize+center > 0.0f) down= 1;
1258 if( butrct.ymax+ysize-center < winy) top= 1;
1260 if(top==0 && down==0) {
1261 if (butrct.ymin-ysize < winy-butrct.ymax-ysize)
1267 dir1= block->direction & UI_DIRECTION;
1269 /* secundary directions */
1270 if(dir1 & (UI_TOP|UI_DOWN)) {
1271 if(dir1 & UI_LEFT) dir2= UI_LEFT;
1272 else if(dir1 & UI_RIGHT) dir2= UI_RIGHT;
1273 dir1 &= (UI_TOP|UI_DOWN);
1276 if(dir2==0) if(dir1==UI_LEFT || dir1==UI_RIGHT) dir2= UI_DOWN;
1277 if(dir2==0) if(dir1==UI_TOP || dir1==UI_DOWN) dir2= UI_LEFT;
1279 /* no space at all? dont change */
1281 if(dir1==UI_LEFT && left==0) dir1= UI_RIGHT;
1282 if(dir1==UI_RIGHT && right==0) dir1= UI_LEFT;
1283 /* this is aligning, not append! */
1284 if(dir2==UI_LEFT && right==0) dir2= UI_RIGHT;
1285 if(dir2==UI_RIGHT && left==0) dir2= UI_LEFT;
1288 if(dir1==UI_TOP && top==0) dir1= UI_DOWN;
1289 if(dir1==UI_DOWN && down==0) dir1= UI_TOP;
1290 if(dir2==UI_TOP && top==0) dir2= UI_DOWN;
1291 if(dir2==UI_DOWN && down==0) dir2= UI_TOP;
1295 xof= butrct.xmin - block->maxx;
1296 if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
1297 else yof= butrct.ymax - block->maxy+center;
1299 else if(dir1==UI_RIGHT) {
1300 xof= butrct.xmax - block->minx;
1301 if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
1302 else yof= butrct.ymax - block->maxy+center;
1304 else if(dir1==UI_TOP) {
1305 yof= butrct.ymax - block->miny;
1306 if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
1307 else xof= butrct.xmin - block->minx;
1308 // changed direction?
1309 if((dir1 & block->direction)==0) {
1310 if(block->direction & UI_SHIFT_FLIPPED)
1311 xof+= dir2==UI_LEFT?25:-25;
1312 uiBlockFlipOrder(block);
1315 else if(dir1==UI_DOWN) {
1316 yof= butrct.ymin - block->maxy;
1317 if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
1318 else xof= butrct.xmin - block->minx;
1319 // changed direction?
1320 if((dir1 & block->direction)==0) {
1321 if(block->direction & UI_SHIFT_FLIPPED)
1322 xof+= dir2==UI_LEFT?25:-25;
1323 uiBlockFlipOrder(block);
1327 /* and now we handle the exception; no space below or to top */
1328 if(top==0 && down==0) {
1329 if(dir1==UI_LEFT || dir1==UI_RIGHT) {
1330 // align with bottom of screen
1331 // yof= ysize; (not with menu scrolls)
1335 /* or no space left or right */
1336 if(left==0 && right==0) {
1337 if(dir1==UI_TOP || dir1==UI_DOWN) {
1338 // align with left size of screen
1339 xof= -block->minx+5;
1343 // apply requested offset in the block
1344 xof += block->xofs/block->aspect;
1345 yof += block->yofs/block->aspect;
1347 /* clamp to window bounds, could be made into an option if its ever annoying */
1348 if( (offscreen= (block->miny+yof)) < 0) yof -= offscreen; /* bottom */
1349 else if((offscreen= (block->maxy+yof)-winy) > 0) yof -= offscreen; /* top */
1350 if( (offscreen= (block->minx+xof)) < 0) xof -= offscreen; /* left */
1351 else if((offscreen= (block->maxx+xof)-winx) > 0) xof -= offscreen; /* right */
1355 /* apply offset, buttons in window coords */
1357 for(bt= block->buttons.first; bt; bt= bt->next) {
1358 ui_block_to_window_fl(butregion, but->block, &bt->x1, &bt->y1);
1359 ui_block_to_window_fl(butregion, but->block, &bt->x2, &bt->y2);
1367 // ui_check_but recalculates drawstring size in pixels
1376 /* safety calculus */
1378 float midx= (butrct.xmin+butrct.xmax)/2.0f;
1379 float midy= (butrct.ymin+butrct.ymax)/2.0f;
1381 /* when you are outside parent button, safety there should be smaller */
1383 // parent button to left
1384 if( midx < block->minx ) block->safety.xmin= block->minx-3;
1385 else block->safety.xmin= block->minx-40;
1386 // parent button to right
1387 if( midx > block->maxx ) block->safety.xmax= block->maxx+3;
1388 else block->safety.xmax= block->maxx+40;
1390 // parent button on bottom
1391 if( midy < block->miny ) block->safety.ymin= block->miny-3;
1392 else block->safety.ymin= block->miny-40;
1393 // parent button on top
1394 if( midy > block->maxy ) block->safety.ymax= block->maxy+3;
1395 else block->safety.ymax= block->maxy+40;
1397 // exception for switched pulldowns...
1398 if(dir1 && (dir1 & block->direction)==0) {
1399 if(dir2==UI_RIGHT) block->safety.xmax= block->maxx+3;
1400 if(dir2==UI_LEFT) block->safety.xmin= block->minx-3;
1402 block->direction= dir1;
1405 block->safety.xmin= block->minx-40;
1406 block->safety.ymin= block->miny-40;
1407 block->safety.xmax= block->maxx+40;
1408 block->safety.ymax= block->maxy+40;
1411 /* keep a list of these, needed for pulldown menus */
1412 saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1413 saferct->parent= butrct;
1414 saferct->safety= block->safety;
1415 BLI_freelistN(&block->saferct);
1417 BLI_duplicatelist(&block->saferct, &but->block->saferct);
1418 BLI_addhead(&block->saferct, saferct);
1421 static void ui_block_region_draw(const bContext *C, ARegion *ar)
1425 for(block=ar->uiblocks.first; block; block=block->next)
1426 uiDrawBlock(C, block);
1429 static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
1433 wm_window_get_size(window, &winx, &winy);
1435 if(block->minx < MENU_SHADOW_SIDE)
1436 block->minx= MENU_SHADOW_SIDE;
1437 if(block->maxx > winx-MENU_SHADOW_SIDE)
1438 block->maxx= winx-MENU_SHADOW_SIDE;
1440 if(block->miny < MENU_SHADOW_BOTTOM)
1441 block->miny= MENU_SHADOW_BOTTOM;
1442 if(block->maxy > winy-MENU_TOP)
1443 block->maxy= winy-MENU_TOP;
1446 void ui_popup_block_scrolltest(uiBlock *block)
1450 block->flag &= ~(UI_BLOCK_CLIPBOTTOM|UI_BLOCK_CLIPTOP);
1452 for(bt= block->buttons.first; bt; bt= bt->next)
1453 bt->flag &= ~UI_SCROLLED;
1455 if(block->buttons.first==block->buttons.last)
1458 /* mark buttons that are outside boundary and the ones next to it for arrow(s) */
1459 for(bt= block->buttons.first; bt; bt= bt->next) {
1460 if(bt->y1 < block->miny) {
1461 bt->flag |= UI_SCROLLED;
1462 block->flag |= UI_BLOCK_CLIPBOTTOM;
1463 /* make space for arrow */
1464 if(bt->y2 < block->miny +10) {
1465 if(bt->next && bt->next->y1 > bt->y1)
1466 bt->next->flag |= UI_SCROLLED;
1467 if(bt->prev && bt->prev->y1 > bt->y1)
1468 bt->prev->flag |= UI_SCROLLED;
1471 if(bt->y2 > block->maxy) {
1472 bt->flag |= UI_SCROLLED;
1473 block->flag |= UI_BLOCK_CLIPTOP;
1474 /* make space for arrow */
1475 if(bt->y1 > block->maxy -10) {
1476 if(bt->next && bt->next->y2 < bt->y2)
1477 bt->next->flag |= UI_SCROLLED;
1478 if(bt->prev && bt->prev->y2 < bt->y2)
1479 bt->prev->flag |= UI_SCROLLED;
1485 uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg)
1487 wmWindow *window= CTX_wm_window(C);
1488 static ARegionType type;
1492 uiPopupBlockHandle *handle;
1493 uiSafetyRct *saferct;
1496 handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
1498 /* store context for operator */
1499 handle->ctx_area= CTX_wm_area(C);
1500 handle->ctx_region= CTX_wm_region(C);
1502 /* create area region */
1503 ar= ui_add_temporary_region(CTX_wm_screen(C));
1506 memset(&type, 0, sizeof(ARegionType));
1507 type.draw= ui_block_region_draw;
1510 UI_add_region_handlers(&ar->handlers);
1512 /* create ui block */
1514 block= create_func(C, handle->region, arg);
1516 block= handle_create_func(C, handle, arg);
1519 memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
1521 handle= block->handle;
1524 block->handle= handle;
1526 ar->regiondata= handle;
1528 if(!block->endblock)
1529 uiEndBlock(C, block);
1531 /* if this is being created from a button */
1533 if(ELEM(but->type, BLOCK, PULLDOWN))
1534 block->xofs = -2; /* for proper alignment */
1536 ui_block_position(window, butregion, but, block);
1539 /* keep a list of these, needed for pulldown menus */
1540 saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
1541 saferct->safety= block->safety;
1542 BLI_addhead(&block->saferct, saferct);
1543 block->flag |= UI_BLOCK_POPUP|UI_BLOCK_NUMSELECT;
1546 /* clip block with window boundary */
1547 ui_popup_block_clip(window, block);
1549 /* the block and buttons were positioned in window space as in 2.4x, now
1550 * these menu blocks are regions so we bring it back to region space.
1551 * additionally we add some padding for the menu shadow or rounded menus */
1552 ar->winrct.xmin= block->minx - MENU_SHADOW_SIDE;
1553 ar->winrct.xmax= block->maxx + MENU_SHADOW_SIDE;
1554 ar->winrct.ymin= block->miny - MENU_SHADOW_BOTTOM;
1555 ar->winrct.ymax= block->maxy + MENU_TOP;
1557 block->minx -= ar->winrct.xmin;
1558 block->maxx -= ar->winrct.xmin;
1559 block->miny -= ar->winrct.ymin;
1560 block->maxy -= ar->winrct.ymin;
1562 for(bt= block->buttons.first; bt; bt= bt->next) {
1563 bt->x1 -= ar->winrct.xmin;
1564 bt->x2 -= ar->winrct.xmin;
1565 bt->y1 -= ar->winrct.ymin;
1566 bt->y2 -= ar->winrct.ymin;
1569 block->flag |= UI_BLOCK_LOOP;
1571 /* adds subwindow */
1572 ED_region_init(C, ar);
1574 /* checks which buttons are visible, sets flags to prevent draw (do after region init) */
1575 ui_popup_block_scrolltest(block);
1577 /* get winmat now that we actually have the subwindow */
1578 wmSubWindowSet(window, ar->swinid);
1580 wm_subwindow_getmatrix(window, ar->swinid, block->winmat);
1582 /* notify change and redraw */
1583 ED_region_tag_redraw(ar);
1588 void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
1590 ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
1592 if(handle->scrolltimer)
1593 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
1598 /***************************** Menu Button ***************************/
1600 static void ui_block_func_MENUSTR(bContext *UNUSED(C), uiLayout *layout, void *arg_str)
1602 uiBlock *block= uiLayoutGetBlock(layout);
1603 uiPopupBlockHandle *handle= block->handle;
1604 uiLayout *split, *column=NULL;
1608 const char *instr= arg_str;
1609 int columns, rows, a, b;
1611 uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1613 /* compute menu data */
1614 md= decompose_menu_string(instr);
1616 /* columns and row estimation */
1617 columns= (md->nitems+20)/20;
1621 columns= (md->nitems+25)/25;
1623 rows= md->nitems/columns;
1626 while(rows*columns<md->nitems)
1632 uiItemL(layout, md->title, md->titleicon);
1635 uiItemL(layout, md->title, ICON_NONE);
1636 bt= block->buttons.last;
1637 bt->flag= UI_TEXT_LEFT;
1641 /* inconsistent, but menus with labels do not look good flipped */
1643 for(a=0; a<md->nitems; a++, entry++) {
1644 if(entry->sepr && entry->str[0]) {
1645 block->flag |= UI_BLOCK_NO_FLIP;
1651 split= uiLayoutSplit(layout, 0, 0);
1653 for(a=0, b=0; a<md->nitems; a++, b++) {
1654 if(block->flag & UI_BLOCK_NO_FLIP)
1655 entry= &md->items[a];
1657 entry= &md->items[md->nitems-a-1];
1659 /* new column on N rows or on separation label */
1660 if((b % rows == 0) || (entry->sepr && entry->str[0])) {
1661 column= uiLayoutColumn(split, 0);
1666 uiItemL(column, entry->str, entry->icon);
1667 bt= block->buttons.last;
1668 bt->flag= UI_TEXT_LEFT;
1670 else if(entry->icon) {
1671 uiDefIconTextButF(block, BUTM|FLO, B_NOP, entry->icon, entry->str, 0, 0,
1672 UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1675 uiDefButF(block, BUTM|FLO, B_NOP, entry->str, 0, 0,
1676 UI_UNIT_X*5, UI_UNIT_X, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1683 void ui_block_func_ICONROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1685 uiBlock *block= uiLayoutGetBlock(layout);
1686 uiPopupBlockHandle *handle= block->handle;
1687 uiBut *but= arg_but;
1690 uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1692 for(a=(int)but->hardmin; a<=(int)but->hardmax; a++)
1693 uiDefIconButF(block, BUTM|FLO, B_NOP, but->icon+(a-but->hardmin), 0, 0, UI_UNIT_X*5, UI_UNIT_Y,
1694 &handle->retvalue, (float)a, 0.0, 0, 0, "");
1697 void ui_block_func_ICONTEXTROW(bContext *UNUSED(C), uiLayout *layout, void *arg_but)
1699 uiBlock *block= uiLayoutGetBlock(layout);
1700 uiPopupBlockHandle *handle= block->handle;
1701 uiBut *but= arg_but, *bt;
1706 uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
1708 md= decompose_menu_string(but->str);
1712 bt= uiDefBut(block, LABEL, 0, md->title, 0, 0, UI_UNIT_X*5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1713 bt->flag= UI_TEXT_LEFT;
1716 /* loop through the menu options and draw them out with icons & text labels */
1717 for(a=0; a<md->nitems; a++) {
1718 entry= &md->items[md->nitems-a-1];
1723 uiDefIconTextButF(block, BUTM|FLO, B_NOP, (short)((but->icon)+(entry->retval-but->hardmin)), entry->str,
1724 0, 0, UI_UNIT_X*5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, 0, "");
1731 static void ui_warp_pointer(int x, int y)
1733 /* XXX 2.50 which function to use for this? */
1734 /* OSX has very poor mousewarp support, it sends events;
1735 this causes a menu being pressed immediately ... */
1742 /********************* Color Button ****************/
1744 /* picker sizes S hsize, F full size, D spacer, B button/pallette height */
1750 /* for picker, while editing hsv */
1751 void ui_set_but_hsv(uiBut *but)
1754 float *hsv= ui_block_hsv_get(but->block);
1756 hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
1757 ui_set_but_vectorf(but, col);
1760 /* also used by small picker, be careful with name checks below... */
1761 static void ui_update_block_buts_rgb(uiBlock *block, float *rgb)
1764 float *hsv= ui_block_hsv_get(block);
1766 /* this is to keep the H and S value when V is equal to zero
1767 * and we are working in HSV mode, of course!
1769 rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
1771 // this updates button strings, is hackish... but button pointers are on stack of caller function
1772 for(bt= block->buttons.first; bt; bt= bt->next) {
1775 ui_set_but_vectorf(bt, rgb);
1778 else if(strcmp(bt->str, "Hex: ")==0) {
1783 /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1785 if (block->color_profile == BLI_PR_NONE) {
1786 copy_v3_v3(rgb_gamma, rgb);
1788 /* make an sRGB version, for Hex code */
1789 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1792 if (rgb_gamma[0] > 1.0f) rgb_gamma[0] = modf(rgb_gamma[0], &intpart);
1793 if (rgb_gamma[1] > 1.0f) rgb_gamma[1] = modf(rgb_gamma[1], &intpart);
1794 if (rgb_gamma[2] > 1.0f) rgb_gamma[2] = modf(rgb_gamma[2], &intpart);
1796 BLI_snprintf(col, sizeof(col), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
1798 strcpy(bt->poin, col);
1800 else if(bt->str[1]==' ') {
1801 if(bt->str[0]=='R') {
1802 ui_set_but_val(bt, rgb[0]);
1804 else if(bt->str[0]=='G') {
1805 ui_set_but_val(bt, rgb[1]);
1807 else if(bt->str[0]=='B') {
1808 ui_set_but_val(bt, rgb[2]);
1810 else if(bt->str[0]=='H') {
1811 ui_set_but_val(bt, hsv[0]);
1813 else if(bt->str[0]=='S') {
1814 ui_set_but_val(bt, hsv[1]);
1816 else if(bt->str[0]=='V') {
1817 ui_set_but_val(bt, hsv[2]);
1825 static void do_picker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1827 uiBut *but= (uiBut *)bt1;
1828 uiPopupBlockHandle *popup= but->block->handle;
1829 PropertyRNA *prop = but->rnaprop;
1830 PointerRNA ptr = but->rnapoin;
1834 RNA_property_float_get_array(&ptr, prop, rgb);
1835 ui_update_block_buts_rgb(but->block, rgb);
1839 popup->menuretval= UI_RETURN_UPDATE;
1842 static void do_hsv_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1844 uiBut *but= (uiBut *)bt1;
1845 uiPopupBlockHandle *popup= but->block->handle;
1847 float *hsv= ui_block_hsv_get(but->block);
1849 hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
1851 ui_update_block_buts_rgb(but->block, rgb);
1854 popup->menuretval= UI_RETURN_UPDATE;
1857 static void do_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
1859 uiBut *but= (uiBut *)bt1;
1860 uiPopupBlockHandle *popup= but->block->handle;
1861 char *hexcol= (char *)hexcl;
1864 hex_to_rgb(hexcol, rgb, rgb+1, rgb+2);
1866 /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
1867 if (but->block->color_profile != BLI_PR_NONE) {
1868 /* so we need to linearise it for Blender */
1869 srgb_to_linearrgb_v3_v3(rgb, rgb);
1872 ui_update_block_buts_rgb(but->block, rgb);
1875 popup->menuretval= UI_RETURN_UPDATE;
1878 static void close_popup_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1880 uiBut *but= (uiBut *)bt1;
1881 uiPopupBlockHandle *popup= but->block->handle;
1884 popup->menuretval= UI_RETURN_OK;
1887 static void picker_new_hide_reveal(uiBlock *block, short colormode)
1892 for(bt= block->buttons.first; bt; bt= bt->next) {
1894 if (bt->type == LABEL) {
1895 if( bt->str[1]=='G') {
1896 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1897 else bt->flag |= UI_HIDDEN;
1901 if(bt->type==NUMSLI || bt->type==TEX) {
1902 if( bt->str[1]=='e') {
1903 if(colormode==2) bt->flag &= ~UI_HIDDEN;
1904 else bt->flag |= UI_HIDDEN;
1906 else if( ELEM3(bt->str[0], 'R', 'G', 'B')) {
1907 if(colormode==0) bt->flag &= ~UI_HIDDEN;
1908 else bt->flag |= UI_HIDDEN;
1910 else if( ELEM3(bt->str[0], 'H', 'S', 'V')) {
1911 if(colormode==1) bt->flag &= ~UI_HIDDEN;
1912 else bt->flag |= UI_HIDDEN;
1918 static void do_picker_new_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
1921 short colormode= ui_get_but_val(bt);
1922 picker_new_hide_reveal(bt->block, colormode);
1925 /* picker sizes S hsize, F full size, D spacer, B button/pallette height */
1926 #define SPICK1 150.0
1929 #define PICKER_H 150
1930 #define PICKER_W 150
1931 #define PICKER_SPACE 6
1932 #define PICKER_BAR 14
1934 #define PICKER_TOTAL_W (PICKER_W+PICKER_SPACE+PICKER_BAR)
1936 static void circle_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop)
1941 bt= uiDefButR_prop(block, HSVCIRCLE, 0, "", 0, 0, PICKER_H, PICKER_W, ptr, prop, 0, 0.0, 0.0, 0, 0, "Color");
1942 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1945 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");
1946 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1950 static void square_picker(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int type)
1953 int bartype = type + 3;
1956 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");
1957 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1960 bt= uiDefButR_prop(block, HSVCUBE, 0, "", 0, 0, PICKER_TOTAL_W, PICKER_BAR, ptr, prop, 0, 0.0, 0.0, bartype, 0, "Value");
1961 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
1965 /* a HS circle, V slider, rgb/hsv/hex sliders */
1966 static void uiBlockPicker(uiBlock *block, float *rgb, PointerRNA *ptr, PropertyRNA *prop)
1968 static short colormode= 0; /* temp? 0=rgb, 1=hsv, 2=hex */
1970 int width, butwidth;
1971 static char tip[50];
1972 static char hexcol[128];
1974 float min, max, step, precision;
1975 float *hsv= ui_block_hsv_get(block);
1977 ui_block_hsv_get(block);
1979 width= PICKER_TOTAL_W;
1980 butwidth = width - UI_UNIT_X - 10;
1982 /* existence of profile means storage is in linear color space, with display correction */
1983 if (block->color_profile == BLI_PR_NONE) {
1984 BLI_strncpy(tip, "Value in Display Color Space", sizeof(tip));
1985 copy_v3_v3(rgb_gamma, rgb);
1987 BLI_strncpy(tip, "Value in Linear RGB Color Space", sizeof(tip));
1988 /* make an sRGB version, for Hex code */
1989 linearrgb_to_srgb_v3_v3(rgb_gamma, rgb);
1992 /* sneaky way to check for alpha */
1995 RNA_property_float_ui_range(ptr, prop, &min, &max, &step, &precision);
1996 RNA_property_float_get_array(ptr, prop, rgb);
1998 switch (U.color_picker_type) {
1999 case USER_CP_CIRCLE:
2000 circle_picker(block, ptr, prop);
2002 case USER_CP_SQUARE_SV:
2003 square_picker(block, ptr, prop, UI_GRAD_SV);
2005 case USER_CP_SQUARE_HS:
2006 square_picker(block, ptr, prop, UI_GRAD_HS);
2008 case USER_CP_SQUARE_HV:
2009 square_picker(block, ptr, prop, UI_GRAD_HV);
2014 uiBlockBeginAlign(block);
2015 bt= uiDefButS(block, ROW, 0, "RGB", 0, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 0.0, 0, 0, "");
2016 uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2017 bt= uiDefButS(block, ROW, 0, "HSV", width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 1.0, 0, 0, "");
2018 uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2019 bt= uiDefButS(block, ROW, 0, "Hex", 2*width/3, -30, width/3, UI_UNIT_Y, &colormode, 0.0, 2.0, 0, 0, "");
2020 uiButSetFunc(bt, do_picker_new_mode_cb, bt, NULL);
2021 uiBlockEndAlign(block);
2023 bt= uiDefIconButO(block, BUT, "UI_OT_eyedropper", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, butwidth+10, -60, UI_UNIT_X, UI_UNIT_Y, NULL);
2024 uiButSetFunc(bt, close_popup_cb, bt, NULL);
2027 uiBlockBeginAlign(block);
2028 bt= uiDefButR_prop(block, NUMSLI, 0, "R ", 0, -60, butwidth, UI_UNIT_Y, ptr, prop, 0, 0.0, 0.0, 0, 3, "Red");
2029 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2030 bt= uiDefButR_prop(block, NUMSLI, 0, "G ", 0, -80, butwidth, UI_UNIT_Y, ptr, prop, 1, 0.0, 0.0, 0, 3, "Green");
2031 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2032 bt= uiDefButR_prop(block, NUMSLI, 0, "B ", 0, -100, butwidth, UI_UNIT_Y, ptr, prop, 2, 0.0, 0.0, 0, 3, "Blue");
2033 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2035 // could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", ICON_NONE);
2036 // but need to use uiButSetFunc for updating other fake buttons
2039 uiBlockBeginAlign(block);
2040 bt= uiDefButF(block, NUMSLI, 0, "H ", 0, -60, butwidth, UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, "Hue");
2041 uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2042 bt= uiDefButF(block, NUMSLI, 0, "S ", 0, -80, butwidth, UI_UNIT_Y, hsv+1, 0.0, 1.0, 10, 3, "Saturation");
2043 uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2044 bt= uiDefButF(block, NUMSLI, 0, "V ", 0, -100, butwidth, UI_UNIT_Y, hsv+2, 0.0, max, 10, 3, "Value");
2045 uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv);
2046 uiBlockEndAlign(block);
2048 if(rgb[3] != FLT_MAX) {
2049 bt= uiDefButR_prop(block, NUMSLI, 0, "A ", 0, -120, butwidth, UI_UNIT_Y, ptr, prop, 3, 0.0, 0.0, 0, 0, "Alpha");
2050 uiButSetFunc(bt, do_picker_rna_cb, bt, NULL);
2056 BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", FTOCHAR(rgb_gamma[0]), FTOCHAR(rgb_gamma[1]), FTOCHAR(rgb_gamma[2]));
2058 bt= uiDefBut(block, TEX, 0, "Hex: ", 0, -60, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
2059 uiButSetFunc(bt, do_hex_rna_cb, bt, hexcol);
2060 uiDefBut(block, LABEL, 0, "(Gamma Corrected)", 0, -80, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2062 rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
2064 picker_new_hide_reveal(block, colormode);
2068 static int ui_picker_small_wheel_cb(const bContext *UNUSED(C), uiBlock *block, wmEvent *event)
2072 if(event->type==WHEELUPMOUSE)
2074 else if(event->type==WHEELDOWNMOUSE)
2080 for(but= block->buttons.first; but; but= but->next) {
2081 if(but->type==HSVCUBE && but->active==NULL) {
2082 uiPopupBlockHandle *popup= block->handle;
2084 float *hsv= ui_block_hsv_get(block);
2086 ui_get_but_vectorf(but, col);
2088 rgb_to_hsv_compat(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
2089 hsv[2]= CLAMPIS(hsv[2]+add, 0.0f, 1.0f);
2090 hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
2092 ui_set_but_vectorf(but, col);
2094 ui_update_block_buts_rgb(block, col);
2096 popup->menuretval= UI_RETURN_UPDATE;
2105 uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
2107 uiBut *but= arg_but;
2110 block= uiBeginBlock(C, handle->region, "colorpicker", UI_EMBOSS);
2113 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2114 block->color_profile = BLI_PR_NONE;
2118 uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2120 copy_v3_v3(handle->retvec, but->editvec);
2122 uiBlockPicker(block, handle->retvec, &but->rnapoin, but->rnaprop);
2124 block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN|UI_BLOCK_OUT_1;
2125 uiBoundsBlock(block, 10);
2127 block->block_event_func= ui_picker_small_wheel_cb;
2130 block->direction= UI_TOP;
2135 /************************ Popup Menu Memory ****************************/
2137 static int ui_popup_string_hash(char *str)
2139 /* sometimes button contains hotkey, sometimes not, strip for proper compare */
2141 char *delimit= strchr(str, '|');
2143 if(delimit) *delimit= 0;
2144 hash= BLI_ghashutil_strhash(str);
2145 if(delimit) *delimit= '|';
2150 static int ui_popup_menu_hash(const char *str)
2152 return BLI_ghashutil_strhash(str);
2155 /* but == NULL read, otherwise set */
2156 uiBut *ui_popup_menu_memory(uiBlock *block, uiBut *but)
2158 static int mem[256], first=1;
2159 int hash= block->puphash;
2163 memset(mem, -1, sizeof(mem));
2169 mem[hash & 255 ]= ui_popup_string_hash(but->str);
2174 for(but=block->buttons.first; but; but=but->next)
2175 if(ui_popup_string_hash(but->str) == mem[hash & 255])
2182 /******************** Popup Menu with callback or string **********************/
2184 struct uiPopupMenu {
2189 int mx, my, popup, slideout;
2190 int startx, starty, maxrow;
2192 uiMenuCreateFunc menu_func;
2196 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
2200 uiPopupMenu *pup= arg_pup;
2201 int offset[2], direction, minwidth, width, height, flip;
2203 if(pup->menu_func) {
2204 pup->block->handle= handle;
2205 pup->menu_func(C, pup->layout, pup->menu_arg);
2206 pup->block->handle= NULL;
2210 /* minimum width to enforece */
2211 minwidth= pup->but->x2 - pup->but->x1;
2213 if(pup->but->type == PULLDOWN || pup->but->menu_create_func) {
2230 /* in some cases we create the block before the region,
2231 so we set it delayed here if necessary */
2232 if(BLI_findindex(&handle->region->uiblocks, block) == -1)
2233 uiBlockSetRegion(block, handle->region);
2235 block->direction= direction;
2237 uiBlockLayoutResolve(block, &width, &height);
2239 uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
2242 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
2243 uiBlockSetDirection(block, direction);
2245 /* offset the mouse position, possibly based on earlier selection */
2246 if((block->flag & UI_BLOCK_POPUP_MEMORY) &&
2247 (bt= ui_popup_menu_memory(block, NULL))) {
2248 /* position mouse on last clicked item, at 0.8*width of the
2249 button, so it doesn't overlap the text too much, also note
2250 the offset is negative because we are inverse moving the
2251 block to be under the mouse */
2252 offset[0]= -(bt->x1 + 0.8f*(bt->x2 - bt->x1));
2253 offset[1]= -(bt->y1 + 0.5f*UI_UNIT_Y);
2256 /* position mouse at 0.8*width of the button and below the tile
2257 on the first item */
2259 for(bt=block->buttons.first; bt; bt=bt->next)
2260 offset[0]= MIN2(offset[0], -(bt->x1 + 0.8f*(bt->x2 - bt->x1)));
2262 offset[1]= 1.5*UI_UNIT_Y;
2265 block->minbounds= minwidth;
2266 uiMenuPopupBoundsBlock(block, 1, offset[0], offset[1]);
2269 /* for a header menu we set the direction automatic */
2270 if(!pup->slideout && flip) {
2271 ScrArea *sa= CTX_wm_area(C);
2272 if(sa && sa->headertype==HEADERDOWN) {
2273 ARegion *ar= CTX_wm_region(C);
2274 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
2275 uiBlockSetDirection(block, UI_TOP);
2276 uiBlockFlipOrder(block);
2281 block->minbounds= minwidth;
2282 uiTextBoundsBlock(block, 50);
2285 /* if menu slides out of other menu, override direction */
2287 uiBlockSetDirection(block, UI_RIGHT);
2289 uiEndBlock(C, block);
2294 uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg, char *str)
2296 wmWindow *window= CTX_wm_window(C);
2297 uiStyle *style= UI_GetStyle();
2298 uiPopupBlockHandle *handle;
2300 pup= MEM_callocN(sizeof(uiPopupMenu), "menu dummy");
2301 pup->block= uiBeginBlock(C, NULL, "ui_button_menu_create", UI_EMBOSSP);
2302 pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2303 pup->slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
2305 uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
2308 /* no button to start from, means we are a popup */
2309 pup->mx= window->eventstate->x;
2310 pup->my= window->eventstate->y;
2312 pup->block->flag |= UI_BLOCK_NO_FLIP;
2314 /* some enums reversing is strange, currently we have no good way to
2315 * reverse some enum's but not others, so reverse all so the first menu
2316 * items are always close to the mouse cursor */
2319 /* if this is an rna button then we can assume its an enum
2320 * flipping enums is generally not good since the order can be
2321 * important [#28786] */
2322 if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
2323 pup->block->flag |= UI_BLOCK_NO_FLIP;
2329 /* menu is created from a string */
2330 pup->menu_func= ui_block_func_MENUSTR;
2334 /* menu is created from a callback */
2335 pup->menu_func= menu_func;
2339 handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
2344 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2345 WM_event_add_mousemove(C);
2353 /******************** Popup Menu API with begin and end ***********************/
2355 /* only return handler, and set optional title */
2356 uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
2358 uiStyle *style= UI_GetStyle();
2359 uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "popup menu");
2362 pup->block= uiBeginBlock(C, NULL, "uiPupMenuBegin", UI_EMBOSSP);
2363 pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
2364 pup->block->puphash= ui_popup_menu_hash(title);
2365 pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
2366 uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
2368 /* create in advance so we can let buttons point to retval already */
2369 pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
2371 /* create title button */
2372 if(title && title[0]) {
2376 BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
2377 uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2380 but= uiDefBut(pup->block, LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
2381 but->flag= UI_TEXT_LEFT;
2388 /* set the whole structure to work */
2389 void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
2391 wmWindow *window= CTX_wm_window(C);
2392 uiPopupBlockHandle *menu;
2395 pup->mx= window->eventstate->x;
2396 pup->my= window->eventstate->y;
2398 menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
2401 UI_add_popup_handlers(C, &window->modalhandlers, menu);
2402 WM_event_add_mousemove(C);
2407 uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
2412 /*************************** Standard Popup Menus ****************************/
2414 static void operator_name_cb(bContext *C, void *arg, int retval)
2416 const char *opname= arg;
2418 if(opname && retval > 0)
2419 WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
2422 static void operator_cb(bContext *C, void *arg, int retval)
2424 wmOperator *op= arg;
2426 if(op && retval > 0)
2427 WM_operator_call(C, op);
2429 WM_operator_free(op);
2432 static void confirm_cancel_operator(void *opv)
2434 WM_operator_free(opv);
2437 static void vconfirm_opname(bContext *C, const char *opname, const char *title, const char *itemfmt, va_list ap)
2439 uiPopupBlockHandle *handle;
2443 if (title) s+= sprintf(s, "%s%%t|", title);
2444 vsnprintf(s, sizeof(buf) - (s - buf), itemfmt, ap);
2445 buf[sizeof(buf) - 1]= '\0';
2447 handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2449 handle->popup_func= operator_name_cb;
2450 handle->popup_arg= (void *)opname;
2453 static void confirm_operator(bContext *C, wmOperator *op, const char *title, const char *item)
2455 uiPopupBlockHandle *handle;
2459 if (title) s+= sprintf(s, "%s%%t|%s", title, item);
2462 handle= ui_popup_menu_create(C, NULL, NULL, NULL, NULL, buf);
2464 handle->popup_func= operator_cb;
2465 handle->popup_arg= op;
2466 handle->cancel_func= confirm_cancel_operator;
2469 void uiPupMenuOkee(bContext *C, const char *opname, const char *str, ...)
2474 BLI_snprintf(titlestr, sizeof(titlestr), "OK? %%i%d", ICON_QUESTION);
2477 vconfirm_opname(C, opname, titlestr, str, ap);
2481 void uiPupMenuSaveOver(bContext *C, wmOperator *op, const char *filename)
2483 size_t len= strlen(filename);
2488 if(filename[len-1]=='/' || filename[len-1]=='\\') {
2489 uiPupMenuError(C, "Cannot overwrite a directory");
2490 WM_operator_free(op);
2493 if(BLI_exists(filename)==0)
2494 operator_cb(C, op, 1);
2496 confirm_operator(C, op, "Save Over", filename);
2499 void uiPupMenuNotice(bContext *C, const char *str, ...)
2504 vconfirm_opname(C, NULL, NULL, str, ap);
2508 void uiPupMenuError(bContext *C, const char *str, ...)
2514 BLI_snprintf(titlestr, sizeof(titlestr), "Error %%i%d", ICON_ERROR);
2516 BLI_strncpy(nfmt, str, sizeof(nfmt));
2519 vconfirm_opname(C, NULL, titlestr, nfmt, ap);
2523 void uiPupMenuReports(bContext *C, ReportList *reports)
2529 if(!reports || !reports->list.first)
2531 if(!CTX_wm_window(C))
2534 ds= BLI_dynstr_new();
2536 for(report=reports->list.first; report; report=report->next) {
2537 if(report->type < reports->printlevel)
2539 else if(report->type >= RPT_ERROR)
2540 BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
2541 else if(report->type >= RPT_WARNING)
2542 BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
2543 else if(report->type >= RPT_INFO)
2544 BLI_dynstr_appendf(ds, "Info %%i%d%%t|%s", ICON_INFO, report->message);
2547 str= BLI_dynstr_get_cstring(ds);
2549 ui_popup_menu_create(C, NULL, NULL, NULL, NULL, str);
2552 BLI_dynstr_free(ds);
2555 void uiPupMenuInvoke(bContext *C, const char *idname)
2560 MenuType *mt= WM_menutype_find(idname, TRUE);
2563 printf("%s: named menu \"%s\" not found\n", __func__, idname);
2567 if(mt->poll && mt->poll(C, mt)==0)
2570 pup= uiPupMenuBegin(C, mt->label, ICON_NONE);
2571 layout= uiPupMenuLayout(pup);
2573 menu.layout= layout;
2578 uiPupMenuEnd(C, pup);
2582 /*************************** Popup Block API **************************/
2584 void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext)
2586 wmWindow *window= CTX_wm_window(C);
2587 uiPopupBlockHandle *handle;
2589 handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2591 handle->optype= (opname)? WM_operatortype_find(opname, 0): NULL;
2592 handle->opcontext= opcontext;
2594 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2595 WM_event_add_mousemove(C);
2598 void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
2600 uiPupBlockO(C, func, arg, NULL, 0);
2603 void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockCancelFunc cancel_func, void *arg)
2605 wmWindow *window= CTX_wm_window(C);
2606 uiPopupBlockHandle *handle;
2608 handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
2610 handle->retvalue= 1;
2612 handle->popup_arg= arg;
2613 // handle->popup_func= operator_cb;
2614 handle->cancel_func= cancel_func;
2615 // handle->opcontext= opcontext;
2617 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2618 WM_event_add_mousemove(C);
2622 void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
2624 wmWindow *window= CTX_wm_window(C);
2625 uiPopupBlockHandle *handle;
2627 handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
2629 handle->retvalue= 1;
2631 handle->popup_arg= op;
2632 handle->popup_func= operator_cb;
2633 handle->cancel_func= confirm_cancel_operator;
2634 handle->opcontext= opcontext;
2636 UI_add_popup_handlers(C, &window->modalhandlers, handle);
2637 WM_event_add_mousemove(C);
2641 void uiPupBlockClose(bContext *C, uiBlock *block)
2644 UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, block->handle);
2645 ui_popup_block_free(C, block->handle);
2649 float *ui_block_hsv_get(uiBlock *block)