OpenTTD-patches/widget.c
Darkvater 53f954edd3 (svn r7206) -Codechange: Remove (some of) the magic needed for windows that could be invalid after
a call to DeleteWindow() somewhere else. Also do some cleanup with AllowWindowDescFront()
2006-11-18 17:04:44 +00:00

717 lines
21 KiB
C

/* $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)
{
const Widget *wi;
int index, found_index = -1;
// Go through the widgets and check if we find the widget that the coordinate is
// inside.
for (index = 0,wi = w->widget; wi->type != WWT_LAST; index++, wi++) {
if (wi->type == WWT_EMPTY || wi->type == WWT_FRAME) continue;
if (x >= wi->left && x <= wi->right && y >= wi->top && y <= wi->bottom &&
!IsWidgetHidden(wi)) {
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 Widget *wi;
const DrawPixelInfo* dpi = _cur_dpi;
Rect r;
int i = 0;
wi = w->widget;
do {
bool clicked = IsWindowWidgetLowered((Window*)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) ||
IsWidgetHidden(wi)) {
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 (IsWidgetDisabled(wi)) {
GfxFillRect(r.left+1, r.top+1, r.right-1, r.bottom-1, _colour_gradient[wi->color&0xF][2] | PALETTE_MODIFIER_GREYOUT);
}
}
}
} while (i++, (++wi)->type != WWT_LAST);
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;
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;
bool scroll = w->vscroll.count > 0;
DrawPixelInfo tmp_dpi, *old_dpi = NULL;
DrawWindowWidgets(w);
x = 1;
y = 2 - w->vscroll.pos;
if (scroll) {
/* Set up the bounding box for drawing the list content */
if (!FillDrawPixelInfo(&tmp_dpi, w->widget[0].left + 1, w->widget[0].top + 1, w->widget[0].right - 1, w->widget[0].bottom - 1)) return;
old_dpi = _cur_dpi;
_cur_dpi = &tmp_dpi;
/* Adjust x and y for the 1 pixel offset of the bounding box */
x--;
y--;
}
sel = WP(w,dropdown_d).selected_index;
width = w->widget[0].right - 3;
for (i = 0; WP(w,dropdown_d).items[i] != INVALID_STRING_ID; i++) {
if (HASBIT(WP(w,dropdown_d).hidden_state, i)) {
sel--;
continue;
}
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;
sel--;
}
/* Reset the bounding box if we had set it up */
if (scroll) _cur_dpi = old_dpi;
} 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 */
height = screen_bottom - top;
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 - 1;
w2->vscroll.count = i * 10 + 3;
}
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;
}