tron@2186: /* $Id$ */ tron@2186: belugas@6928: /** @file widget.cpp */ belugas@6928: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@2163: #include "functions.h" tron@2154: #include "player.h" tron@1363: #include "table/sprites.h" tron@507: #include "table/strings.h" truelight@0: #include "viewport.h" rubidium@8602: #include "window_gui.h" truelight@0: Darkvater@2436: static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom) truelight@0: { truelight@0: Point pt; truelight@0: int height, count, pos, cap; truelight@0: truelight@0: top += 10; truelight@0: bottom -= 9; truelight@193: truelight@0: height = (bottom - top); truelight@0: truelight@0: pos = sb->pos; truelight@0: count = sb->count; truelight@0: cap = sb->cap; truelight@0: tron@2026: if (count != 0) top += height * pos / count; truelight@193: truelight@0: if (cap > count) cap = count; tron@2639: if (count != 0) bottom -= (count - pos - cap) * height / count; truelight@0: truelight@0: pt.x = top; truelight@0: pt.y = bottom - 1; truelight@0: return pt; truelight@0: } truelight@0: belugas@6977: /** Special handling for the scrollbar widget type. truelight@0: * Handles the special scrolling buttons and other truelight@0: * scrolling. belugas@6977: * @param w Window on which a scroll was performed. belugas@6977: * @param wi Pointer to the scrollbar widget. belugas@6977: * @param x The X coordinate of the mouse click. belugas@6977: * @param y The Y coordinate of the mouse click. */ truelight@0: void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y) truelight@0: { truelight@0: int mi, ma, pos; truelight@0: Scrollbar *sb; truelight@0: bjarni@842: switch (wi->type) { bjarni@842: case WWT_SCROLLBAR: { belugas@6928: /* vertical scroller */ bjarni@842: w->flags4 &= ~WF_HSCROLL; bjarni@842: w->flags4 &= ~WF_SCROLL2; bjarni@842: mi = wi->top; bjarni@842: ma = wi->bottom; bjarni@842: pos = y; bjarni@842: sb = &w->vscroll; bjarni@842: break; bjarni@842: } bjarni@842: case WWT_SCROLL2BAR: { belugas@6928: /* 2nd vertical scroller */ bjarni@842: w->flags4 &= ~WF_HSCROLL; bjarni@842: w->flags4 |= WF_SCROLL2; bjarni@842: mi = wi->top; bjarni@842: ma = wi->bottom; bjarni@842: pos = y; bjarni@842: sb = &w->vscroll2; bjarni@842: break; bjarni@842: } bjarni@842: case WWT_HSCROLLBAR: { belugas@6928: /* horizontal scroller */ truelight@867: w->flags4 &= ~WF_SCROLL2; bjarni@842: w->flags4 |= WF_HSCROLL; bjarni@842: mi = wi->left; bjarni@842: ma = wi->right; bjarni@842: pos = x; bjarni@842: sb = &w->hscroll; truelight@867: break; bjarni@842: } bjarni@845: default: return; //this should never happen truelight@0: } truelight@0: if (pos <= mi+9) { belugas@6928: /* Pressing the upper button? */ tron@2597: w->flags4 |= WF_SCROLL_UP; tron@2597: if (_scroller_click_timeout == 0) { tron@2597: _scroller_click_timeout = 6; tron@2597: if (sb->pos != 0) sb->pos--; truelight@0: } tron@2597: _left_button_clicked = false; truelight@0: } else if (pos >= ma-10) { belugas@6928: /* Pressing the lower button? */ tron@2597: w->flags4 |= WF_SCROLL_DOWN; truelight@193: tron@2597: if (_scroller_click_timeout == 0) { tron@2597: _scroller_click_timeout = 6; tron@2597: if ((byte)(sb->pos + sb->cap) < sb->count) tron@2597: sb->pos++; truelight@0: } tron@2597: _left_button_clicked = false; truelight@0: } else { truelight@0: Point pt = HandleScrollbarHittest(sb, mi, ma); truelight@0: truelight@0: if (pos < pt.x) { truelight@0: sb->pos = max(sb->pos - sb->cap, 0); truelight@0: } else if (pos > pt.y) { truelight@0: sb->pos = min( truelight@193: sb->pos + sb->cap, truelight@0: max(sb->count - sb->cap, 0) truelight@0: ); truelight@0: } else { truelight@0: _scrollbar_start_pos = pt.x - mi - 9; truelight@0: _scrollbar_size = ma - mi - 23; truelight@0: w->flags4 |= WF_SCROLL_MIDDLE; truelight@0: _scrolling_scrollbar = true; truelight@0: _cursorpos_drag_start = _cursor.pos; truelight@0: } truelight@0: } truelight@0: truelight@0: SetWindowDirty(w); truelight@0: } truelight@0: Darkvater@2021: /** Returns the index for the widget located at the given position Darkvater@2021: * relative to the window. It includes all widget-corner pixels as well. Darkvater@2021: * @param *w Window to look inside belugas@6977: * @param x The Window client X coordinate belugas@6977: * @param y The Window client y coordinate Darkvater@2021: * @return A widget index, or -1 if no widget was found. truelight@0: */ Darkvater@2436: int GetWidgetFromPos(const Window *w, int x, int y) truelight@0: { rubidium@5236: uint index; rubidium@5236: int found_index = -1; truelight@0: belugas@6928: /* Go through the widgets and check if we find the widget that the coordinate is belugas@6928: * inside. */ rubidium@5236: for (index = 0; index < w->widget_count; index++) { rubidium@5236: const Widget *wi = &w->widget[index]; tron@2639: if (wi->type == WWT_EMPTY || wi->type == WWT_FRAME) continue; truelight@0: Darkvater@2021: if (x >= wi->left && x <= wi->right && y >= wi->top && y <= wi->bottom && rubidium@8493: !w->IsWidgetHidden(index)) { tron@2639: found_index = index; truelight@0: } truelight@0: } truelight@0: truelight@0: return found_index; truelight@0: } truelight@0: truelight@0: tron@4437: void DrawFrameRect(int left, int top, int right, int bottom, int ctab, FrameFlags flags) tron@4437: { tron@4444: uint dark = _colour_gradient[ctab][3]; tron@4444: uint medium_dark = _colour_gradient[ctab][5]; tron@4444: uint medium_light = _colour_gradient[ctab][6]; tron@4444: uint light = _colour_gradient[ctab][7]; tron@4437: tron@4438: if (flags & FR_TRANSPARENT) { peter1138@5919: GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT | (1 << USE_COLORTABLE)); tron@4437: } else { tron@4437: uint interior; tron@4437: tron@4437: if (flags & FR_LOWERED) { tron@4437: GfxFillRect(left, top, left, bottom, dark); tron@4437: GfxFillRect(left + 1, top, right, top, dark); tron@4437: GfxFillRect(right, top + 1, right, bottom - 1, light); tron@4437: GfxFillRect(left + 1, bottom, right, bottom, light); tron@4437: interior = (flags & FR_DARKENED ? medium_dark : medium_light); tron@4437: } else { tron@4437: GfxFillRect(left, top, left, bottom - 1, light); tron@4437: GfxFillRect(left + 1, top, right - 1, top, light); tron@4437: GfxFillRect(right, top, right, bottom - 1, dark); tron@4437: GfxFillRect(left, bottom, right, bottom, dark); tron@4437: interior = medium_dark; tron@4437: } tron@4437: if (!(flags & FR_BORDERONLY)) { tron@4437: GfxFillRect(left + 1, top + 1, right - 1, bottom - 1, interior); tron@4437: } tron@4437: } tron@4437: } tron@4437: tron@4437: Darkvater@2436: void DrawWindowWidgets(const Window *w) truelight@0: { tron@2548: const DrawPixelInfo* dpi = _cur_dpi; truelight@193: smatz@8458: for (uint i = 0; i < w->widget_count; i++) { rubidium@5236: const Widget *wi = &w->widget[i]; rubidium@8493: bool clicked = w->IsWidgetLowered(i); smatz@8458: Rect r; Darkvater@1657: smatz@8458: if (dpi->left > (r.right = wi->right) || smatz@8458: dpi->left + dpi->width <= (r.left = wi->left) || smatz@8458: dpi->top > (r.bottom = wi->bottom) || smatz@8458: dpi->top + dpi->height <= (r.top = wi->top) || rubidium@8493: w->IsWidgetHidden(i)) { tron@2639: continue; tron@2639: } truelight@0: Darkvater@1657: switch (wi->type & WWT_MASK) { Darkvater@4938: case WWT_IMGBTN: Darkvater@4938: case WWT_IMGBTN_2: { smatz@8458: SpriteID img = wi->data; Darkvater@4938: assert(img != 0); rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); truelight@0: Darkvater@4938: /* show different image when clicked for WWT_IMGBTN_2 */ Darkvater@4938: if ((wi->type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; peter1138@5919: DrawSprite(img, PAL_NONE, r.left + 1 + clicked, r.top + 1 + clicked); Darkvater@4938: goto draw_default; Darkvater@4938: } Darkvater@1657: Darkvater@4938: case WWT_PANEL: { Darkvater@4938: assert(wi->data == 0); rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); truelight@0: goto draw_default; truelight@0: } truelight@0: Darkvater@4939: case WWT_TEXTBTN: Darkvater@4939: case WWT_TEXTBTN_2: { rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); truelight@0: } truelight@0: /* fall through */ truelight@0: belugas@4345: case WWT_LABEL: { Darkvater@4547: StringID str = wi->data; truelight@0: Darkvater@4939: if ((wi->type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++; truelight@193: belugas@8320: DrawStringCentered(((r.left + r.right + 1) >> 1) + clicked, ((r.top + r.bottom + 1) >> 1) - 5 + clicked, str, TC_FROMSTRING); ludde@2064: goto draw_default; truelight@0: } truelight@0: belugas@6604: case WWT_TEXT: { smatz@8458: const StringID str = wi->data; belugas@6604: belugas@6604: if (str != STR_NULL) DrawStringTruncated(r.left, r.top, str, wi->color, r.right - r.left); belugas@6604: break; belugas@6604: } belugas@6604: Darkvater@4939: case WWT_INSET: { smatz@8458: const StringID str = wi->data; hackykid@1938: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED); truelight@0: belugas@8320: if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, TC_FROMSTRING, r.right - r.left - 10); truelight@0: goto draw_default; truelight@0: } truelight@0: truelight@0: case WWT_MATRIX: { truelight@193: int c, d, ctr; truelight@0: int x, amt1, amt2; truelight@0: int color; truelight@193: rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); truelight@193: Darkvater@4547: c = GB(wi->data, 0, 8); truelight@0: amt1 = (wi->right - wi->left + 1) / c; truelight@0: Darkvater@4547: d = GB(wi->data, 8, 8); truelight@0: amt2 = (wi->bottom - wi->top + 1) / d; truelight@0: tron@4444: color = _colour_gradient[wi->color & 0xF][6]; truelight@193: truelight@0: x = r.left; peter1138@2801: for (ctr = c; ctr > 1; ctr--) { truelight@0: x += amt1; tron@2639: GfxFillRect(x, r.top + 1, x, r.bottom - 1, color); truelight@0: } truelight@0: truelight@0: x = r.top; peter1138@2801: for (ctr = d; ctr > 1; ctr--) { truelight@0: x += amt2; tron@2639: GfxFillRect(r.left + 1, x, r.right - 1, x, color); truelight@0: } truelight@0: smatz@8458: color = _colour_gradient[wi->color & 0xF][4]; truelight@0: tron@2639: x = r.left - 1; peter1138@2801: for (ctr = c; ctr > 1; ctr--) { truelight@0: x += amt1; tron@2639: GfxFillRect(x, r.top + 1, x, r.bottom - 1, color); truelight@0: } truelight@0: tron@2639: x = r.top - 1; peter1138@2801: for (ctr = d; ctr > 1; ctr--) { truelight@0: x += amt2; smatz@8458: GfxFillRect(r.left + 1, x, r.right - 1, x, color); truelight@0: } truelight@0: truelight@193: goto draw_default; truelight@0: } truelight@0: belugas@6928: /* vertical scrollbar */ truelight@0: case WWT_SCROLLBAR: { truelight@0: Point pt; smatz@8458: int c1, c2; truelight@0: smatz@8458: assert(wi->data == 0); darkvater@893: assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! darkvater@893: belugas@6928: /* draw up/down buttons */ Darkvater@4940: clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP); rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : FR_NONE); belugas@8320: DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, TC_BLACK); truelight@0: Darkvater@4940: clicked = (((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN)); rubidium@5838: DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); belugas@8320: DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, TC_BLACK); truelight@0: smatz@8458: c1 = _colour_gradient[wi->color & 0xF][3]; smatz@8458: c2 = _colour_gradient[wi->color & 0xF][7]; truelight@0: belugas@6928: /* draw "shaded" background */ smatz@8458: GfxFillRect(r.left, r.top + 10, r.right, r.bottom - 10, c2); smatz@8458: GfxFillRect(r.left, r.top + 10, r.right, r.bottom - 10, c1 | (1 << PALETTE_MODIFIER_GREYOUT)); truelight@0: belugas@6928: /* draw shaded lines */ smatz@8458: GfxFillRect(r.left + 2, r.top + 10, r.left + 2, r.bottom - 10, c1); smatz@8458: GfxFillRect(r.left + 3, r.top + 10, r.left + 3, r.bottom - 10, c2); smatz@8458: GfxFillRect(r.left + 7, r.top + 10, r.left + 7, r.bottom - 10, c1); smatz@8458: GfxFillRect(r.left + 8, r.top + 10, r.left + 8, r.bottom - 10, c2); truelight@193: truelight@0: pt = HandleScrollbarHittest(&w->vscroll, r.top, r.bottom); rubidium@5838: 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 : FR_NONE); bjarni@842: break; bjarni@842: } bjarni@842: case WWT_SCROLL2BAR: { bjarni@842: Point pt; smatz@8458: int c1, c2; bjarni@842: smatz@8458: assert(wi->data == 0); darkvater@893: assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! darkvater@893: belugas@6928: /* draw up/down buttons */ Darkvater@4940: clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2)); rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : FR_NONE); belugas@8320: DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, TC_BLACK); bjarni@842: Darkvater@4940: clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2)); rubidium@5838: DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); belugas@8320: DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, TC_BLACK); bjarni@842: smatz@8458: c1 = _colour_gradient[wi->color & 0xF][3]; smatz@8458: c2 = _colour_gradient[wi->color & 0xF][7]; bjarni@842: belugas@6928: /* draw "shaded" background */ smatz@8458: GfxFillRect(r.left, r.top + 10, r.right, r.bottom - 10, c2); smatz@8458: GfxFillRect(r.left, r.top + 10, r.right, r.bottom - 10, c1 | (1 << PALETTE_MODIFIER_GREYOUT)); bjarni@842: belugas@6928: /* draw shaded lines */ smatz@8458: GfxFillRect(r.left + 2, r.top + 10, r.left + 2, r.bottom - 10, c1); smatz@8458: GfxFillRect(r.left + 3, r.top + 10, r.left + 3, r.bottom - 10, c2); smatz@8458: GfxFillRect(r.left + 7, r.top + 10, r.left + 7, r.bottom - 10, c1); smatz@8458: GfxFillRect(r.left + 8, r.top + 10, r.left + 8, r.bottom - 10, c2); bjarni@842: bjarni@842: pt = HandleScrollbarHittest(&w->vscroll2, r.top, r.bottom); rubidium@5838: 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 : FR_NONE); truelight@0: break; truelight@0: } truelight@0: belugas@6928: /* horizontal scrollbar */ truelight@0: case WWT_HSCROLLBAR: { truelight@0: Point pt; smatz@8458: int c1, c2; truelight@0: smatz@8458: assert(wi->data == 0); darkvater@894: assert(r.bottom - r.top == 11); // XXX - to ensure the same sizes are used everywhere! darkvater@893: Darkvater@4940: clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL)); rubidium@5838: DrawFrameRect(r.left, r.top, r.left + 9, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); peter1138@5919: DrawSprite(SPR_ARROW_LEFT, PAL_NONE, r.left + 1 + clicked, r.top + 1 + clicked); truelight@0: Darkvater@4940: clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL)); smatz@8458: DrawFrameRect(r.right - 9, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); peter1138@5919: DrawSprite(SPR_ARROW_RIGHT, PAL_NONE, r.right - 8 + clicked, r.top + 1 + clicked); truelight@0: smatz@8458: c1 = _colour_gradient[wi->color & 0xF][3]; smatz@8458: c2 = _colour_gradient[wi->color & 0xF][7]; truelight@0: belugas@6928: /* draw "shaded" background */ smatz@8458: GfxFillRect(r.left + 10, r.top, r.right - 10, r.bottom, c2); smatz@8458: GfxFillRect(r.left + 10, r.top, r.right - 10, r.bottom, c1 | (1 << PALETTE_MODIFIER_GREYOUT)); truelight@0: belugas@6928: /* draw shaded lines */ smatz@8458: GfxFillRect(r.left + 10, r.top + 2, r.right - 10, r.top + 2, c1); smatz@8458: GfxFillRect(r.left + 10, r.top + 3, r.right - 10, r.top + 3, c2); smatz@8458: GfxFillRect(r.left + 10, r.top + 7, r.right - 10, r.top + 7, c1); smatz@8458: GfxFillRect(r.left + 10, r.top + 8, r.right - 10, r.top + 8, c2); truelight@0: belugas@6928: /* draw actual scrollbar */ truelight@0: pt = HandleScrollbarHittest(&w->hscroll, r.left, r.right); rubidium@5838: 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 : FR_NONE); truelight@0: truelight@0: break; truelight@0: } truelight@0: truelight@0: case WWT_FRAME: { smatz@8458: const StringID str = wi->data; smatz@8458: int c1, c2; darkvater@860: int x2 = r.left; // by default the left side is the left side of the widget truelight@0: smatz@8458: if (str != STR_NULL) x2 = DrawString(r.left + 6, r.top, str, TC_FROMSTRING); truelight@0: tron@4444: c1 = _colour_gradient[wi->color][3]; tron@4444: c2 = _colour_gradient[wi->color][7]; truelight@0: smatz@8458: /* Line from upper left corner to start of text */ smatz@8458: GfxFillRect(r.left, r.top + 4, r.left + 4, r.top + 4, c1); smatz@8458: GfxFillRect(r.left + 1, r.top + 5, r.left + 4, r.top + 5, c2); truelight@0: belugas@6928: /* Line from end of text to upper right corner */ smatz@8458: GfxFillRect(x2, r.top + 4, r.right - 1, r.top + 4, c1); smatz@8458: GfxFillRect(x2, r.top + 5, r.right - 2, r.top + 5, c2); truelight@0: belugas@6928: /* Line from upper left corner to bottom left corner */ smatz@8458: GfxFillRect(r.left, r.top + 5, r.left, r.bottom - 1, c1); smatz@8458: GfxFillRect(r.left + 1, r.top + 6, r.left + 1, r.bottom - 2, c2); truelight@0: belugas@6928: /*Line from upper right corner to bottom right corner */ smatz@8458: GfxFillRect(r.right - 1, r.top + 5, r.right - 1, r.bottom - 2, c1); smatz@8458: GfxFillRect(r.right, r.top + 4, r.right, r.bottom - 1, c2); truelight@0: smatz@8458: GfxFillRect(r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1, c1); truelight@0: GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2); truelight@0: ludde@2064: goto draw_default; truelight@0: } truelight@0: darkvater@682: case WWT_STICKYBOX: { smatz@8458: assert(wi->data == 0); darkvater@893: assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! peter1138@2703: peter1138@2703: clicked = !!(w->flags4 & WF_STICKY); rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); peter1138@5919: DrawSprite((clicked) ? SPR_PIN_UP : SPR_PIN_DOWN, PAL_NONE, r.left + 2 + clicked, r.top + 3 + clicked); darkvater@682: break; darkvater@682: } truelight@867: truelight@867: case WWT_RESIZEBOX: { smatz@8458: assert(wi->data == 0); darkvater@893: assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! tron@915: Darkvater@1657: clicked = !!(w->flags4 & WF_SIZING); rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); peter1138@5919: DrawSprite(SPR_WINDOW_RESIZE, PAL_NONE, r.left + 3 + clicked, r.top + 3 + clicked); truelight@867: break; truelight@867: } truelight@867: peter1138@2757: case WWT_CLOSEBOX: { smatz@8458: const StringID str = wi->data; smatz@8458: smatz@8458: assert(str == STR_00C5 || str == STR_00C6); // black or silver cross peter1138@2757: assert(r.right - r.left == 10); // ensure the same sizes are used everywhere peter1138@2757: rubidium@5838: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_NONE); smatz@8458: DrawString(r.left + 2, r.top + 2, str, TC_FROMSTRING); peter1138@2757: break; peter1138@2757: } peter1138@2757: truelight@0: case WWT_CAPTION: { darkvater@893: assert(r.bottom - r.top == 13); // XXX - to ensure the same sizes are used everywhere! hackykid@1938: DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_BORDERONLY); smatz@8458: 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); truelight@193: truelight@0: if (w->caption_color != 0xFF) { smatz@8458: GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, _colour_gradient[_player_colors[w->caption_color]][4]); truelight@0: } truelight@0: smatz@8458: DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top + 2, wi->data, 0x84); truelight@0: draw_default:; rubidium@8493: if (w->IsWidgetDisabled(i)) { smatz@8458: GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[wi->color & 0xF][2] | (1 << PALETTE_MODIFIER_GREYOUT)); truelight@0: } truelight@0: } truelight@0: } rubidium@5236: } truelight@193: truelight@0: truelight@0: if (w->flags4 & WF_WHITE_BORDER_MASK) { smatz@8458: DrawFrameRect(0, 0, w->width - 1, w->height - 1, 0xF, FR_BORDERONLY); truelight@0: } truelight@0: truelight@0: } darkvater@164: truelight@883: static const Widget _dropdown_menu_widgets[] = { Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL}, peter1138@5041: { WWT_SCROLLBAR, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, darkvater@176: { WIDGETS_END}, darkvater@164: }; darkvater@164: Darkvater@2436: static int GetDropdownItem(const Window *w) darkvater@164: { peter1138@2448: byte item, counter; darkvater@164: int y; darkvater@164: darkvater@164: if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0) darkvater@164: return -1; truelight@193: peter1138@5196: y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10; darkvater@164: darkvater@164: if (y < 0) darkvater@164: return - 1; darkvater@164: darkvater@164: item = y / 10; rubidium@8578: 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) darkvater@164: return - 1; darkvater@164: belugas@6928: /* Skip hidden items -- +1 for each hidden item before the clicked item. */ peter1138@2448: for (counter = 0; item >= counter; ++counter) rubidium@8578: if (HasBit(WP(w, dropdown_d).hidden_state, counter)) item++; peter1138@2448: darkvater@164: return item; darkvater@164: } darkvater@164: tron@1095: static void DropdownMenuWndProc(Window *w, WindowEvent *e) darkvater@164: { darkvater@164: int item; darkvater@164: tron@2952: switch (e->event) { bjarni@842: case WE_PAINT: { bjarni@842: int x,y,i,sel; peter1138@5196: int width, height; darkvater@164: bjarni@842: DrawWindowWidgets(w); darkvater@164: bjarni@842: x = 1; peter1138@5196: y = 2 - w->vscroll.pos * 10; darkvater@164: rubidium@8578: sel = WP(w, dropdown_d).selected_index; peter1138@5196: width = w->widget[0].right - 3; peter1138@5196: height = w->widget[0].bottom - 3; darkvater@164: rubidium@8578: for (i = 0; WP(w, dropdown_d).items[i] != INVALID_STRING_ID; i++, sel--) { rubidium@8578: if (HasBit(WP(w, dropdown_d).hidden_state, i)) continue; peter1138@5196: peter1138@5196: if (y >= 0 && y <= height) { rubidium@8578: if (WP(w, dropdown_d).items[i] != STR_NULL) { peter1138@5196: if (sel == 0) GfxFillRect(x + 1, y, x + width, y + 9, 0); rubidium@8578: DrawStringTruncated(x + 2, y, WP(w, dropdown_d).items[i], sel == 0 ? TC_WHITE : TC_BLACK, x + width); peter1138@5196: rubidium@8578: if (HasBit(WP(w, dropdown_d).disabled_state, i)) { peter1138@5196: GfxFillRect(x, y, x + width, y + 9, peter1138@5919: (1 << PALETTE_MODIFIER_GREYOUT) | _colour_gradient[_dropdown_menu_widgets[0].color][5] peter1138@5196: ); peter1138@5196: } peter1138@5196: } else { peter1138@5196: int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3]; peter1138@5196: int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7]; peter1138@5196: peter1138@5196: GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1); peter1138@5196: GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2); bjarni@842: } bjarni@842: } bjarni@1177: y += 10; bjarni@842: } bjarni@842: } break; bjarni@842: bjarni@842: case WE_CLICK: { peter1138@5041: if (e->we.click.widget != 0) break; bjarni@842: item = GetDropdownItem(w); bjarni@842: if (item >= 0) { rubidium@8578: WP(w, dropdown_d).click_delay = 4; rubidium@8578: WP(w, dropdown_d).selected_index = item; bjarni@842: SetWindowDirty(w); bjarni@842: } bjarni@842: } break; bjarni@842: bjarni@842: case WE_MOUSELOOP: { rubidium@8578: Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num); bjarni@842: if (w2 == NULL) { bjarni@842: DeleteWindow(w); bjarni@842: return; darkvater@164: } darkvater@164: rubidium@8578: if (WP(w, dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) { bjarni@842: WindowEvent e; bjarni@842: e.event = WE_DROPDOWN_SELECT; rubidium@8578: e.we.dropdown.button = WP(w, dropdown_d).parent_button; rubidium@8578: e.we.dropdown.index = WP(w, dropdown_d).selected_index; bjarni@842: w2->wndproc(w2, &e); bjarni@842: DeleteWindow(w); bjarni@842: return; bjarni@842: } truelight@193: rubidium@8578: if (WP(w, dropdown_d).drag_mode) { bjarni@842: item = GetDropdownItem(w); bjarni@842: bjarni@842: if (!_left_button_clicked) { rubidium@8578: WP(w, dropdown_d).drag_mode = false; tron@2639: if (item < 0) return; rubidium@8578: WP(w, dropdown_d).click_delay = 2; bjarni@842: } else { tron@2639: if (item < 0) return; bjarni@842: } bjarni@842: rubidium@8578: WP(w, dropdown_d).selected_index = item; bjarni@842: SetWindowDirty(w); bjarni@842: } bjarni@842: } break; bjarni@842: bjarni@842: case WE_DESTROY: { rubidium@8578: Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num); bjarni@842: if (w2 != NULL) { rubidium@8578: w2->RaiseWidget(WP(w, dropdown_d).parent_button); rubidium@8578: w2->InvalidateWidget(WP(w, dropdown_d).parent_button); bjarni@842: } bjarni@842: } break; darkvater@164: } darkvater@164: } darkvater@164: peter1138@2448: void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask) darkvater@164: { truelight@867: int i; darkvater@164: const Widget *wi; darkvater@164: Window *w2; peter1138@5041: const Window *w3; rubidium@8493: bool is_dropdown_menu_shown = w->IsWidgetLowered(button); peter1138@5041: int top, height; peter1138@5041: int screen_top, screen_bottom; peter1138@5041: bool scroll = false; truelight@193: darkvater@164: DeleteWindowById(WC_DROPDOWN_MENU, 0); darkvater@164: belugas@4719: if (is_dropdown_menu_shown) return; darkvater@164: rubidium@8493: w->LowerWidget(button); darkvater@164: glx@8524: w->InvalidateWidget(button); truelight@193: tron@2639: for (i = 0; strings[i] != INVALID_STRING_ID; i++) {} tron@2639: if (i == 0) return; darkvater@164: darkvater@164: wi = &w->widget[button]; darkvater@164: peter1138@2448: if (hidden_mask != 0) { tron@2639: uint j; tron@2639: peter1138@2636: for (j = 0; strings[j] != INVALID_STRING_ID; j++) { skidd13@8424: if (HasBit(hidden_mask, j)) i--; bjarni@842: } bjarni@842: } bjarni@842: peter1138@5041: /* The preferred position is just below the dropdown calling widget */ peter1138@5041: top = w->top + wi->bottom + 2; peter1138@5041: height = i * 10 + 4; peter1138@5041: peter1138@5041: w3 = FindWindowById(WC_STATUS_BAR, 0); peter1138@5041: screen_bottom = w3 == NULL ? _screen.height : w3->top; peter1138@5041: peter1138@5041: /* Check if the dropdown will fully fit below the widget */ peter1138@5041: if (top + height >= screen_bottom) { peter1138@5041: w3 = FindWindowById(WC_MAIN_TOOLBAR, 0); peter1138@5041: screen_top = w3 == NULL ? 0 : w3->top + w3->height; peter1138@5041: peter1138@5041: /* If not, check if it will fit above the widget */ peter1138@5041: if (w->top + wi->top - height - 1 > screen_top) { peter1138@5041: top = w->top + wi->top - height - 1; peter1138@5041: } else { peter1138@5041: /* ... and lastly if it won't, enable the scroll bar and fit the peter1138@5041: * list in below the widget */ peter1138@5196: int rows = (screen_bottom - 4 - top) / 10; peter1138@5196: height = rows * 10 + 4; peter1138@5041: scroll = true; peter1138@5041: } peter1138@5041: } peter1138@5041: darkvater@164: w2 = AllocateWindow( darkvater@164: w->left + wi[-1].left + 1, peter1138@5041: top, truelight@867: wi->right - wi[-1].left + 1, peter1138@5041: height, darkvater@164: DropdownMenuWndProc, belugas@4573: WC_DROPDOWN_MENU, darkvater@164: _dropdown_menu_widgets); darkvater@164: truelight@867: w2->widget[0].color = wi->color; truelight@867: w2->widget[0].right = wi->right - wi[-1].left; peter1138@5041: w2->widget[0].bottom = height - 1; darkvater@164: rubidium@8493: w2->SetWidgetHiddenState(1, !scroll); peter1138@5041: peter1138@5041: if (scroll) { peter1138@5041: /* We're scrolling, so enable the scroll bar and shrink the list by peter1138@5041: * the scrollbar's width */ peter1138@5041: w2->widget[1].color = wi->color; peter1138@5041: w2->widget[1].right = w2->widget[0].right; peter1138@5041: w2->widget[1].left = w2->widget[1].right - 11; peter1138@5041: w2->widget[1].bottom = height - 1; peter1138@5041: w2->widget[0].right -= 12; peter1138@5041: peter1138@5196: w2->vscroll.cap = (height - 4) / 10; peter1138@5196: w2->vscroll.count = i; peter1138@5041: } peter1138@5041: peter1138@5041: w2->desc_flags = WDF_DEF_WIDGET; darkvater@164: w2->flags4 &= ~WF_WHITE_BORDER_MASK; peter1138@2629: rubidium@8578: WP(w2, dropdown_d).disabled_state = disabled_mask; rubidium@8578: WP(w2, dropdown_d).hidden_state = hidden_mask; peter1138@2636: rubidium@8578: WP(w2, dropdown_d).parent_wnd_class = w->window_class; rubidium@8578: WP(w2, dropdown_d).parent_wnd_num = w->window_number; rubidium@8578: WP(w2, dropdown_d).parent_button = button; peter1138@2636: rubidium@8578: WP(w2, dropdown_d).num_items = i; rubidium@8578: WP(w2, dropdown_d).selected_index = selected; rubidium@8578: WP(w2, dropdown_d).items = strings; peter1138@2636: rubidium@8578: WP(w2, dropdown_d).click_delay = 0; rubidium@8578: WP(w2, dropdown_d).drag_mode = true; darkvater@164: } bjarni@6073: bjarni@6073: bjarni@6075: static void ResizeWidgets(Window *w, byte a, byte b) bjarni@6075: { bjarni@6075: int16 offset = w->widget[a].left; bjarni@6075: int16 length = w->widget[b].right - offset; bjarni@6075: bjarni@6075: w->widget[a].right = (length / 2) + offset; bjarni@6075: bjarni@6073: w->widget[b].left = w->widget[a].right + 1; bjarni@6073: } bjarni@6073: bjarni@6075: static void ResizeWidgets(Window *w, byte a, byte b, byte c) bjarni@6073: { bjarni@6075: int16 offset = w->widget[a].left; bjarni@6075: int16 length = w->widget[c].right - offset; bjarni@6073: bjarni@6075: w->widget[a].right = length / 3; bjarni@6073: w->widget[b].right = w->widget[a].right * 2; bjarni@6073: bjarni@6075: w->widget[a].right += offset; bjarni@6075: w->widget[b].right += offset; bjarni@6075: bjarni@6073: /* Now the right side of the buttons are set. We will now set the left sides next to them */ bjarni@6073: w->widget[b].left = w->widget[a].right + 1; bjarni@6073: w->widget[c].left = w->widget[b].right + 1; bjarni@6073: } bjarni@6073: bjarni@6075: /** Evenly distribute some widgets when resizing horizontally (often a button row) bjarni@6075: * When only two arguments are given, the widgets are presumed to be on a line and only the ends are given belugas@6977: * @param w Window to modify bjarni@6075: * @param left The leftmost widget to resize bjarni@6075: * @param right The rightmost widget to resize. Since right side of it is used, remember to set it to RESIZE_RIGHT bjarni@6075: */ bjarni@6073: void ResizeButtons(Window *w, byte left, byte right) bjarni@6073: { bjarni@6075: int16 num_widgets = right - left + 1; bjarni@6075: bjarni@6075: if (num_widgets < 2) NOT_REACHED(); bjarni@6075: bjarni@6075: switch (num_widgets) { bjarni@6075: case 2: ResizeWidgets(w, left, right); break; bjarni@6075: case 3: ResizeWidgets(w, left, left + 1, right); break; bjarni@6075: default: { bjarni@6075: /* Looks like we got more than 3 widgets to resize bjarni@6075: * Now we will find the middle of the space desinated for the widgets bjarni@6075: * and place half of the widgets on each side of it and call recursively. bjarni@6075: * Eventually we will get down to blocks of 2-3 widgets and we got code to handle those cases */ bjarni@6075: int16 offset = w->widget[left].left; bjarni@6075: int16 length = w->widget[right].right - offset; bjarni@6075: byte widget = ((num_widgets - 1)/ 2) + left; // rightmost widget of the left side bjarni@6075: bjarni@6075: /* Now we need to find the middle of the widgets. bjarni@6075: * It will not always be the middle because if we got an uneven number of widgets, bjarni@6075: * we will need it to be 2/5, 3/7 and so on bjarni@6075: * To get this, we multiply with num_widgets/num_widgets. Since we calculate in int, we will get: bjarni@6075: * bjarni@6075: * num_widgets/2 (rounding down) bjarni@6075: * --------------- bjarni@6075: * num_widgets bjarni@6075: * bjarni@6075: * as multiplier to length. We just multiply before divide to that we stay in the int area though */ bjarni@6075: int16 middle = ((length * num_widgets) / (2 * num_widgets)) + offset; bjarni@6075: bjarni@6075: /* Set left and right on the widgets, that's next to our "middle" */ bjarni@6075: w->widget[widget].right = middle; bjarni@6075: w->widget[widget + 1].left = w->widget[widget].right + 1; bjarni@6075: /* Now resize the left and right of the middle */ bjarni@6075: ResizeButtons(w, left, widget); bjarni@6075: ResizeButtons(w, widget + 1, right); bjarni@6075: } bjarni@6073: } bjarni@6073: }