diff -r 136d8764c7e6 -r 1111b4d36e35 src/widget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widget.cpp Wed Jan 10 18:12:09 2007 +0000 @@ -0,0 +1,700 @@ +/* $Id$ */ + +#include "stdafx.h" +#include "openttd.h" +#include "functions.h" +#include "player.h" +#include "table/sprites.h" +#include "table/strings.h" +#include "window.h" +#include "gfx.h" +#include "viewport.h" + +static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom) +{ + Point pt; + int height, count, pos, cap; + + top += 10; + bottom -= 9; + + height = (bottom - top); + + pos = sb->pos; + count = sb->count; + cap = sb->cap; + + if (count != 0) top += height * pos / count; + + if (cap > count) cap = count; + if (count != 0) bottom -= (count - pos - cap) * height / count; + + pt.x = top; + pt.y = bottom - 1; + return pt; +} + +/***************************************************** + * Special handling for the scrollbar widget type. + * Handles the special scrolling buttons and other + * scrolling. + * Parameters: + * w - Window. + * wi - Pointer to the scrollbar widget. + * x - The X coordinate of the mouse click. + * y - The Y coordinate of the mouse click. + */ + +void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y) +{ + int mi, ma, pos; + Scrollbar *sb; + + switch (wi->type) { + case WWT_SCROLLBAR: { + // vertical scroller + w->flags4 &= ~WF_HSCROLL; + w->flags4 &= ~WF_SCROLL2; + mi = wi->top; + ma = wi->bottom; + pos = y; + sb = &w->vscroll; + break; + } + case WWT_SCROLL2BAR: { + // 2nd vertical scroller + w->flags4 &= ~WF_HSCROLL; + w->flags4 |= WF_SCROLL2; + mi = wi->top; + ma = wi->bottom; + pos = y; + sb = &w->vscroll2; + break; + } + case WWT_HSCROLLBAR: { + // horizontal scroller + w->flags4 &= ~WF_SCROLL2; + w->flags4 |= WF_HSCROLL; + mi = wi->left; + ma = wi->right; + pos = x; + sb = &w->hscroll; + break; + } + default: return; //this should never happen + } + if (pos <= mi+9) { + // Pressing the upper button? + w->flags4 |= WF_SCROLL_UP; + if (_scroller_click_timeout == 0) { + _scroller_click_timeout = 6; + if (sb->pos != 0) sb->pos--; + } + _left_button_clicked = false; + } else if (pos >= ma-10) { + // Pressing the lower button? + w->flags4 |= WF_SCROLL_DOWN; + + if (_scroller_click_timeout == 0) { + _scroller_click_timeout = 6; + if ((byte)(sb->pos + sb->cap) < sb->count) + sb->pos++; + } + _left_button_clicked = false; + } else { + // + Point pt = HandleScrollbarHittest(sb, mi, ma); + + if (pos < pt.x) { + sb->pos = max(sb->pos - sb->cap, 0); + } else if (pos > pt.y) { + sb->pos = min( + sb->pos + sb->cap, + max(sb->count - sb->cap, 0) + ); + } else { + _scrollbar_start_pos = pt.x - mi - 9; + _scrollbar_size = ma - mi - 23; + w->flags4 |= WF_SCROLL_MIDDLE; + _scrolling_scrollbar = true; + _cursorpos_drag_start = _cursor.pos; + } + } + + SetWindowDirty(w); +} + +/** Returns the index for the widget located at the given position + * relative to the window. It includes all widget-corner pixels as well. + * @param *w Window to look inside + * @param x,y Window client coordinates + * @return A widget index, or -1 if no widget was found. + */ +int GetWidgetFromPos(const Window *w, int x, int y) +{ + uint index; + int found_index = -1; + + // Go through the widgets and check if we find the widget that the coordinate is + // inside. + for (index = 0; index < w->widget_count; index++) { + const Widget *wi = &w->widget[index]; + if (wi->type == WWT_EMPTY || wi->type == WWT_FRAME) continue; + + if (x >= wi->left && x <= wi->right && y >= wi->top && y <= wi->bottom && + !IsWindowWidgetHidden(w, index)) { + found_index = index; + } + } + + return found_index; +} + + +void DrawFrameRect(int left, int top, int right, int bottom, int ctab, FrameFlags flags) +{ + uint dark = _colour_gradient[ctab][3]; + uint medium_dark = _colour_gradient[ctab][5]; + uint medium_light = _colour_gradient[ctab][6]; + uint light = _colour_gradient[ctab][7]; + + if (flags & FR_TRANSPARENT) { + GfxFillRect(left, top, right, bottom, 0x322 | USE_COLORTABLE); + } else { + uint interior; + + if (flags & FR_LOWERED) { + GfxFillRect(left, top, left, bottom, dark); + GfxFillRect(left + 1, top, right, top, dark); + GfxFillRect(right, top + 1, right, bottom - 1, light); + GfxFillRect(left + 1, bottom, right, bottom, light); + interior = (flags & FR_DARKENED ? medium_dark : medium_light); + } else { + GfxFillRect(left, top, left, bottom - 1, light); + GfxFillRect(left + 1, top, right - 1, top, light); + GfxFillRect(right, top, right, bottom - 1, dark); + GfxFillRect(left, bottom, right, bottom, dark); + interior = medium_dark; + } + if (!(flags & FR_BORDERONLY)) { + GfxFillRect(left + 1, top + 1, right - 1, bottom - 1, interior); + } + } +} + + +void DrawWindowWidgets(const Window *w) +{ + const DrawPixelInfo* dpi = _cur_dpi; + Rect r; + uint i; + + for (i = 0; i < w->widget_count; i++) { + const Widget *wi = &w->widget[i]; + bool clicked = IsWindowWidgetLowered(w, i); + + if (dpi->left > (r.right=/*w->left + */wi->right) || + dpi->left + dpi->width <= (r.left=wi->left/* + w->left*/) || + dpi->top > (r.bottom=/*w->top +*/ wi->bottom) || + dpi->top + dpi->height <= (r.top = /*w->top +*/ wi->top) || + IsWindowWidgetHidden(w, i)) { + continue; + } + + switch (wi->type & WWT_MASK) { + case WWT_IMGBTN: + case WWT_IMGBTN_2: { + int img = wi->data; + assert(img != 0); + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + + /* show different image when clicked for WWT_IMGBTN_2 */ + if ((wi->type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; + DrawSprite(img, r.left + 1 + clicked, r.top + 1 + clicked); + goto draw_default; + } + + case WWT_PANEL: { + assert(wi->data == 0); + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + goto draw_default; + } + + case WWT_TEXTBTN: + case WWT_TEXTBTN_2: { + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + } + /* fall through */ + + case WWT_LABEL: { + StringID str = wi->data; + + if ((wi->type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++; + + DrawStringCentered(((r.left + r.right + 1) >> 1) + clicked, ((r.top + r.bottom + 1) >> 1) - 5 + clicked, str, 0); + goto draw_default; + } + + case WWT_INSET: { + StringID str = wi->data; + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED); + + if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, 0, r.right - r.left - 10); + goto draw_default; + } + + case WWT_MATRIX: { + int c, d, ctr; + int x, amt1, amt2; + int color; + + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + + c = GB(wi->data, 0, 8); + amt1 = (wi->right - wi->left + 1) / c; + + d = GB(wi->data, 8, 8); + amt2 = (wi->bottom - wi->top + 1) / d; + + color = _colour_gradient[wi->color & 0xF][6]; + + x = r.left; + for (ctr = c; ctr > 1; ctr--) { + x += amt1; + GfxFillRect(x, r.top + 1, x, r.bottom - 1, color); + } + + x = r.top; + for (ctr = d; ctr > 1; ctr--) { + x += amt2; + GfxFillRect(r.left + 1, x, r.right - 1, x, color); + } + + color = _colour_gradient[wi->color&0xF][4]; + + x = r.left - 1; + for (ctr = c; ctr > 1; ctr--) { + x += amt1; + GfxFillRect(x, r.top + 1, x, r.bottom - 1, color); + } + + x = r.top - 1; + for (ctr = d; ctr > 1; ctr--) { + x += amt2; + GfxFillRect(r.left+1, x, r.right-1, x, color); + } + + goto draw_default; + } + + // vertical scrollbar + case WWT_SCROLLBAR: { + Point pt; + int c1,c2; + + assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! + + // draw up/down buttons + clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP); + DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : 0); + DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10); + + clicked = (((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN)); + DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10); + + c1 = _colour_gradient[wi->color&0xF][3]; + c2 = _colour_gradient[wi->color&0xF][7]; + + // draw "shaded" background + GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2); + GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT); + + // draw shaded lines + GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1); + GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2); + GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1); + GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2); + + pt = HandleScrollbarHittest(&w->vscroll, r.top, r.bottom); + DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_MIDDLE ? FR_LOWERED : 0); + break; + } + case WWT_SCROLL2BAR: { + Point pt; + int c1,c2; + + assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! + + // draw up/down buttons + clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2)); + DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : 0); + DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10); + + clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2)); + DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10); + + c1 = _colour_gradient[wi->color&0xF][3]; + c2 = _colour_gradient[wi->color&0xF][7]; + + // draw "shaded" background + GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2); + GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT); + + // draw shaded lines + GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1); + GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2); + GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1); + GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2); + + pt = HandleScrollbarHittest(&w->vscroll2, r.top, r.bottom); + DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_MIDDLE | WF_SCROLL2) ? FR_LOWERED : 0); + break; + } + + // horizontal scrollbar + case WWT_HSCROLLBAR: { + Point pt; + int c1,c2; + + assert(r.bottom - r.top == 11); // XXX - to ensure the same sizes are used everywhere! + + clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL)); + DrawFrameRect(r.left, r.top, r.left + 9, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + DrawSprite(SPR_ARROW_LEFT, r.left + 1 + clicked, r.top + 1 + clicked); + + clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL)); + DrawFrameRect(r.right-9, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + DrawSprite(SPR_ARROW_RIGHT, r.right - 8 + clicked, r.top + 1 + clicked); + + c1 = _colour_gradient[wi->color&0xF][3]; + c2 = _colour_gradient[wi->color&0xF][7]; + + // draw "shaded" background + GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c2); + GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c1 | PALETTE_MODIFIER_GREYOUT); + + // draw shaded lines + GfxFillRect(r.left+10, r.top+2, r.right-10, r.top+2, c1); + GfxFillRect(r.left+10, r.top+3, r.right-10, r.top+3, c2); + GfxFillRect(r.left+10, r.top+7, r.right-10, r.top+7, c1); + GfxFillRect(r.left+10, r.top+8, r.right-10, r.top+8, c2); + + // draw actual scrollbar + pt = HandleScrollbarHittest(&w->hscroll, r.left, r.right); + DrawFrameRect(pt.x, r.top, pt.y, r.bottom, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL)) == (WF_SCROLL_MIDDLE | WF_HSCROLL) ? FR_LOWERED : 0); + + break; + } + + case WWT_FRAME: { + int c1,c2; + int x2 = r.left; // by default the left side is the left side of the widget + + if (wi->data != 0) x2 = DrawString(r.left + 6, r.top, wi->data, 0); + + c1 = _colour_gradient[wi->color][3]; + c2 = _colour_gradient[wi->color][7]; + + //Line from upper left corner to start of text + GfxFillRect(r.left, r.top+4, r.left+4,r.top+4, c1); + GfxFillRect(r.left+1, r.top+5, r.left+4,r.top+5, c2); + + // Line from end of text to upper right corner + GfxFillRect(x2, r.top+4, r.right-1,r.top+4,c1); + GfxFillRect(x2, r.top+5, r.right-2,r.top+5,c2); + + // Line from upper left corner to bottom left corner + GfxFillRect(r.left, r.top+5, r.left, r.bottom-1, c1); + GfxFillRect(r.left+1, r.top+6, r.left+1, r.bottom-2, c2); + + //Line from upper right corner to bottom right corner + GfxFillRect(r.right-1, r.top+5, r.right-1, r.bottom-2, c1); + GfxFillRect(r.right, r.top+4, r.right, r.bottom-1, c2); + + GfxFillRect(r.left+1, r.bottom-1, r.right-1, r.bottom-1, c1); + GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2); + + goto draw_default; + } + + case WWT_STICKYBOX: { + assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! + + clicked = !!(w->flags4 & WF_STICKY); + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + DrawSprite((clicked) ? SPR_PIN_UP : SPR_PIN_DOWN, r.left + 2 + clicked, r.top + 3 + clicked); + break; + } + + case WWT_RESIZEBOX: { + assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! + + clicked = !!(w->flags4 & WF_SIZING); + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); + DrawSprite(SPR_WINDOW_RESIZE, r.left + 3 + clicked, r.top + 3 + clicked); + break; + } + + case WWT_CLOSEBOX: { + assert(r.right - r.left == 10); // ensure the same sizes are used everywhere + + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, 0); + DrawString(r.left + 2, r.top + 2, STR_00C5, 0); + break; + } + + case WWT_CAPTION: { + assert(r.bottom - r.top == 13); // XXX - to ensure the same sizes are used everywhere! + DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_BORDERONLY); + DrawFrameRect(r.left+1, r.top+1, r.right-1, r.bottom-1, wi->color, (w->caption_color == 0xFF) ? FR_LOWERED | FR_DARKENED : FR_LOWERED | FR_DARKENED | FR_BORDERONLY); + + if (w->caption_color != 0xFF) { + GfxFillRect(r.left+2, r.top+2, r.right-2, r.bottom-2, _colour_gradient[_player_colors[w->caption_color]][4]); + } + + DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top+2, wi->data, 0x84); +draw_default:; + if (IsWindowWidgetDisabled(w, i)) { + GfxFillRect(r.left+1, r.top+1, r.right-1, r.bottom-1, _colour_gradient[wi->color&0xF][2] | PALETTE_MODIFIER_GREYOUT); + } + } + } + } + + + if (w->flags4 & WF_WHITE_BORDER_MASK) { + //DrawFrameRect(w->left, w->top, w->left + w->width-1, w->top+w->height-1, 0xF, 0x10); + DrawFrameRect(0, 0, w->width-1, w->height-1, 0xF, FR_BORDERONLY); + } + +} + +static const Widget _dropdown_menu_widgets[] = { +{ WWT_PANEL, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL}, +{ WWT_SCROLLBAR, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WIDGETS_END}, +}; + +static int GetDropdownItem(const Window *w) +{ + byte item, counter; + int y; + + if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0) + return -1; + + y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10; + + if (y < 0) + return - 1; + + item = y / 10; + if (item >= WP(w,dropdown_d).num_items || (HASBIT(WP(w,dropdown_d).disabled_state, item) && !HASBIT(WP(w,dropdown_d).hidden_state, item)) || WP(w,dropdown_d).items[item] == 0) + return - 1; + + // Skip hidden items -- +1 for each hidden item before the clicked item. + for (counter = 0; item >= counter; ++counter) + if (HASBIT(WP(w,dropdown_d).hidden_state, counter)) item++; + + return item; +} + +static void DropdownMenuWndProc(Window *w, WindowEvent *e) +{ + int item; + + switch (e->event) { + case WE_PAINT: { + int x,y,i,sel; + int width, height; + + DrawWindowWidgets(w); + + x = 1; + y = 2 - w->vscroll.pos * 10; + + sel = WP(w,dropdown_d).selected_index; + width = w->widget[0].right - 3; + height = w->widget[0].bottom - 3; + + for (i = 0; WP(w,dropdown_d).items[i] != INVALID_STRING_ID; i++, sel--) { + if (HASBIT(WP(w,dropdown_d).hidden_state, i)) continue; + + if (y >= 0 && y <= height) { + if (WP(w,dropdown_d).items[i] != STR_NULL) { + if (sel == 0) GfxFillRect(x + 1, y, x + width, y + 9, 0); + DrawStringTruncated(x + 2, y, WP(w,dropdown_d).items[i], sel == 0 ? 12 : 16, x + width); + + if (HASBIT(WP(w,dropdown_d).disabled_state, i)) { + GfxFillRect(x, y, x + width, y + 9, + PALETTE_MODIFIER_GREYOUT | _colour_gradient[_dropdown_menu_widgets[0].color][5] + ); + } + } else { + int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3]; + int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7]; + + GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1); + GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2); + } + } + y += 10; + } + } break; + + case WE_CLICK: { + if (e->we.click.widget != 0) break; + item = GetDropdownItem(w); + if (item >= 0) { + WP(w,dropdown_d).click_delay = 4; + WP(w,dropdown_d).selected_index = item; + SetWindowDirty(w); + } + } break; + + case WE_MOUSELOOP: { + Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num); + if (w2 == NULL) { + DeleteWindow(w); + return; + } + + if (WP(w,dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) { + WindowEvent e; + e.event = WE_DROPDOWN_SELECT; + e.we.dropdown.button = WP(w,dropdown_d).parent_button; + e.we.dropdown.index = WP(w,dropdown_d).selected_index; + w2->wndproc(w2, &e); + DeleteWindow(w); + return; + } + + if (WP(w,dropdown_d).drag_mode) { + item = GetDropdownItem(w); + + if (!_left_button_clicked) { + WP(w,dropdown_d).drag_mode = false; + if (item < 0) return; + WP(w,dropdown_d).click_delay = 2; + } else { + if (item < 0) return; + } + + WP(w,dropdown_d).selected_index = item; + SetWindowDirty(w); + } + } break; + + case WE_DESTROY: { + Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num); + if (w2 != NULL) { + RaiseWindowWidget(w2, WP(w,dropdown_d).parent_button); + InvalidateWidget(w2, WP(w,dropdown_d).parent_button); + } + } break; + } +} + +void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask) +{ + int i; + const Widget *wi; + Window *w2; + const Window *w3; + bool is_dropdown_menu_shown = IsWindowWidgetLowered(w, button); + int top, height; + int screen_top, screen_bottom; + bool scroll = false; + + DeleteWindowById(WC_DROPDOWN_MENU, 0); + + if (is_dropdown_menu_shown) return; + + LowerWindowWidget(w, button); + + InvalidateWidget(w, button); + + for (i = 0; strings[i] != INVALID_STRING_ID; i++) {} + if (i == 0) return; + + wi = &w->widget[button]; + + if (hidden_mask != 0) { + uint j; + + for (j = 0; strings[j] != INVALID_STRING_ID; j++) { + if (HASBIT(hidden_mask, j)) i--; + } + } + + /* The preferred position is just below the dropdown calling widget */ + top = w->top + wi->bottom + 2; + height = i * 10 + 4; + + w3 = FindWindowById(WC_STATUS_BAR, 0); + screen_bottom = w3 == NULL ? _screen.height : w3->top; + + /* Check if the dropdown will fully fit below the widget */ + if (top + height >= screen_bottom) { + w3 = FindWindowById(WC_MAIN_TOOLBAR, 0); + screen_top = w3 == NULL ? 0 : w3->top + w3->height; + + /* If not, check if it will fit above the widget */ + if (w->top + wi->top - height - 1 > screen_top) { + top = w->top + wi->top - height - 1; + } else { + /* ... and lastly if it won't, enable the scroll bar and fit the + * list in below the widget */ + int rows = (screen_bottom - 4 - top) / 10; + height = rows * 10 + 4; + scroll = true; + } + } + + w2 = AllocateWindow( + w->left + wi[-1].left + 1, + top, + wi->right - wi[-1].left + 1, + height, + DropdownMenuWndProc, + WC_DROPDOWN_MENU, + _dropdown_menu_widgets); + + w2->widget[0].color = wi->color; + w2->widget[0].right = wi->right - wi[-1].left; + w2->widget[0].bottom = height - 1; + + SetWindowWidgetHiddenState(w2, 1, !scroll); + + if (scroll) { + /* We're scrolling, so enable the scroll bar and shrink the list by + * the scrollbar's width */ + w2->widget[1].color = wi->color; + w2->widget[1].right = w2->widget[0].right; + w2->widget[1].left = w2->widget[1].right - 11; + w2->widget[1].bottom = height - 1; + w2->widget[0].right -= 12; + + w2->vscroll.cap = (height - 4) / 10; + w2->vscroll.count = i; + } + + w2->desc_flags = WDF_DEF_WIDGET; + w2->flags4 &= ~WF_WHITE_BORDER_MASK; + + WP(w2,dropdown_d).disabled_state = disabled_mask; + WP(w2,dropdown_d).hidden_state = hidden_mask; + + WP(w2,dropdown_d).parent_wnd_class = w->window_class; + WP(w2,dropdown_d).parent_wnd_num = w->window_number; + WP(w2,dropdown_d).parent_button = button; + + WP(w2,dropdown_d).num_items = i; + WP(w2,dropdown_d).selected_index = selected; + WP(w2,dropdown_d).items = strings; + + WP(w2,dropdown_d).click_delay = 0; + WP(w2,dropdown_d).drag_mode = true; +}