src/widget.cpp
changeset 5584 1111b4d36e35
parent 5475 2e6990a8c7c4
child 5587 167d9a91ef02
equal deleted inserted replaced
5583:136d8764c7e6 5584:1111b4d36e35
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "functions.h"
       
     6 #include "player.h"
       
     7 #include "table/sprites.h"
       
     8 #include "table/strings.h"
       
     9 #include "window.h"
       
    10 #include "gfx.h"
       
    11 #include "viewport.h"
       
    12 
       
    13 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom)
       
    14 {
       
    15 	Point pt;
       
    16 	int height, count, pos, cap;
       
    17 
       
    18 	top += 10;
       
    19 	bottom -= 9;
       
    20 
       
    21 	height = (bottom - top);
       
    22 
       
    23 	pos = sb->pos;
       
    24 	count = sb->count;
       
    25 	cap = sb->cap;
       
    26 
       
    27 	if (count != 0) top += height * pos / count;
       
    28 
       
    29 	if (cap > count) cap = count;
       
    30 	if (count != 0) bottom -= (count - pos - cap) * height / count;
       
    31 
       
    32 	pt.x = top;
       
    33 	pt.y = bottom - 1;
       
    34 	return pt;
       
    35 }
       
    36 
       
    37 /*****************************************************
       
    38  * Special handling for the scrollbar widget type.
       
    39  * Handles the special scrolling buttons and other
       
    40  * scrolling.
       
    41  * Parameters:
       
    42  *   w   - Window.
       
    43  *   wi  - Pointer to the scrollbar widget.
       
    44  *   x   - The X coordinate of the mouse click.
       
    45  *   y   - The Y coordinate of the mouse click.
       
    46  */
       
    47 
       
    48 void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y)
       
    49 {
       
    50 	int mi, ma, pos;
       
    51 	Scrollbar *sb;
       
    52 
       
    53 	switch (wi->type) {
       
    54 		case WWT_SCROLLBAR: {
       
    55 			// vertical scroller
       
    56 			w->flags4 &= ~WF_HSCROLL;
       
    57 			w->flags4 &= ~WF_SCROLL2;
       
    58 			mi = wi->top;
       
    59 			ma = wi->bottom;
       
    60 			pos = y;
       
    61 			sb = &w->vscroll;
       
    62 			break;
       
    63 		}
       
    64 		case WWT_SCROLL2BAR: {
       
    65 			// 2nd vertical scroller
       
    66 			w->flags4 &= ~WF_HSCROLL;
       
    67 			w->flags4 |= WF_SCROLL2;
       
    68 			mi = wi->top;
       
    69 			ma = wi->bottom;
       
    70 			pos = y;
       
    71 			sb = &w->vscroll2;
       
    72 			break;
       
    73 		}
       
    74 		case  WWT_HSCROLLBAR: {
       
    75 			// horizontal scroller
       
    76 			w->flags4 &= ~WF_SCROLL2;
       
    77 			w->flags4 |= WF_HSCROLL;
       
    78 			mi = wi->left;
       
    79 			ma = wi->right;
       
    80 			pos = x;
       
    81 			sb = &w->hscroll;
       
    82 			break;
       
    83 		}
       
    84 		default: return; //this should never happen
       
    85 	}
       
    86 	if (pos <= mi+9) {
       
    87 		// Pressing the upper button?
       
    88 		w->flags4 |= WF_SCROLL_UP;
       
    89 		if (_scroller_click_timeout == 0) {
       
    90 			_scroller_click_timeout = 6;
       
    91 			if (sb->pos != 0) sb->pos--;
       
    92 		}
       
    93 		_left_button_clicked = false;
       
    94 	} else if (pos >= ma-10) {
       
    95 		// Pressing the lower button?
       
    96 		w->flags4 |= WF_SCROLL_DOWN;
       
    97 
       
    98 		if (_scroller_click_timeout == 0) {
       
    99 			_scroller_click_timeout = 6;
       
   100 			if ((byte)(sb->pos + sb->cap) < sb->count)
       
   101 				sb->pos++;
       
   102 		}
       
   103 		_left_button_clicked = false;
       
   104 	} else {
       
   105 		//
       
   106 		Point pt = HandleScrollbarHittest(sb, mi, ma);
       
   107 
       
   108 		if (pos < pt.x) {
       
   109 			sb->pos = max(sb->pos - sb->cap, 0);
       
   110 		} else if (pos > pt.y) {
       
   111 			sb->pos = min(
       
   112 				sb->pos + sb->cap,
       
   113 				max(sb->count - sb->cap, 0)
       
   114 			);
       
   115 		} else {
       
   116 			_scrollbar_start_pos = pt.x - mi - 9;
       
   117 			_scrollbar_size = ma - mi - 23;
       
   118 			w->flags4 |= WF_SCROLL_MIDDLE;
       
   119 			_scrolling_scrollbar = true;
       
   120 			_cursorpos_drag_start = _cursor.pos;
       
   121 		}
       
   122 	}
       
   123 
       
   124 	SetWindowDirty(w);
       
   125 }
       
   126 
       
   127 /** Returns the index for the widget located at the given position
       
   128  * relative to the window. It includes all widget-corner pixels as well.
       
   129  * @param *w Window to look inside
       
   130  * @param  x,y Window client coordinates
       
   131  * @return A widget index, or -1 if no widget was found.
       
   132  */
       
   133 int GetWidgetFromPos(const Window *w, int x, int y)
       
   134 {
       
   135 	uint index;
       
   136 	int found_index = -1;
       
   137 
       
   138 	// Go through the widgets and check if we find the widget that the coordinate is
       
   139 	// inside.
       
   140 	for (index = 0; index < w->widget_count; index++) {
       
   141 		const Widget *wi = &w->widget[index];
       
   142 		if (wi->type == WWT_EMPTY || wi->type == WWT_FRAME) continue;
       
   143 
       
   144 		if (x >= wi->left && x <= wi->right && y >= wi->top &&  y <= wi->bottom &&
       
   145 				!IsWindowWidgetHidden(w, index)) {
       
   146 			found_index = index;
       
   147 		}
       
   148 	}
       
   149 
       
   150 	return found_index;
       
   151 }
       
   152 
       
   153 
       
   154 void DrawFrameRect(int left, int top, int right, int bottom, int ctab, FrameFlags flags)
       
   155 {
       
   156 	uint dark         = _colour_gradient[ctab][3];
       
   157 	uint medium_dark  = _colour_gradient[ctab][5];
       
   158 	uint medium_light = _colour_gradient[ctab][6];
       
   159 	uint light        = _colour_gradient[ctab][7];
       
   160 
       
   161 	if (flags & FR_TRANSPARENT) {
       
   162 		GfxFillRect(left, top, right, bottom, 0x322 | USE_COLORTABLE);
       
   163 	} else {
       
   164 		uint interior;
       
   165 
       
   166 		if (flags & FR_LOWERED) {
       
   167 			GfxFillRect(left,     top,     left,  bottom,     dark);
       
   168 			GfxFillRect(left + 1, top,     right, top,        dark);
       
   169 			GfxFillRect(right,    top + 1, right, bottom - 1, light);
       
   170 			GfxFillRect(left + 1, bottom,  right, bottom,     light);
       
   171 			interior = (flags & FR_DARKENED ? medium_dark : medium_light);
       
   172 		} else {
       
   173 			GfxFillRect(left,     top,    left,      bottom - 1, light);
       
   174 			GfxFillRect(left + 1, top,    right - 1, top,        light);
       
   175 			GfxFillRect(right,    top,    right,     bottom - 1, dark);
       
   176 			GfxFillRect(left,     bottom, right,     bottom,     dark);
       
   177 			interior = medium_dark;
       
   178 		}
       
   179 		if (!(flags & FR_BORDERONLY)) {
       
   180 			GfxFillRect(left + 1, top + 1, right - 1, bottom - 1, interior);
       
   181 		}
       
   182 	}
       
   183 }
       
   184 
       
   185 
       
   186 void DrawWindowWidgets(const Window *w)
       
   187 {
       
   188 	const DrawPixelInfo* dpi = _cur_dpi;
       
   189 	Rect r;
       
   190 	uint i;
       
   191 
       
   192 	for (i = 0; i < w->widget_count; i++) {
       
   193 		const Widget *wi = &w->widget[i];
       
   194 		bool clicked = IsWindowWidgetLowered(w, i);
       
   195 
       
   196 		if (dpi->left > (r.right=/*w->left + */wi->right) ||
       
   197 				dpi->left + dpi->width <= (r.left=wi->left/* + w->left*/) ||
       
   198 				dpi->top > (r.bottom=/*w->top +*/ wi->bottom) ||
       
   199 				dpi->top + dpi->height <= (r.top = /*w->top +*/ wi->top) ||
       
   200 				IsWindowWidgetHidden(w, i)) {
       
   201 			continue;
       
   202 		}
       
   203 
       
   204 		switch (wi->type & WWT_MASK) {
       
   205 		case WWT_IMGBTN:
       
   206 		case WWT_IMGBTN_2: {
       
   207 			int img = wi->data;
       
   208 			assert(img != 0);
       
   209 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   210 
       
   211 			/* show different image when clicked for WWT_IMGBTN_2 */
       
   212 			if ((wi->type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++;
       
   213 			DrawSprite(img, r.left + 1 + clicked, r.top + 1 + clicked);
       
   214 			goto draw_default;
       
   215 		}
       
   216 
       
   217 		case WWT_PANEL: {
       
   218 			assert(wi->data == 0);
       
   219 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   220 			goto draw_default;
       
   221 		}
       
   222 
       
   223 		case WWT_TEXTBTN:
       
   224 		case WWT_TEXTBTN_2: {
       
   225 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   226 			}
       
   227 		/* fall through */
       
   228 
       
   229 		case WWT_LABEL: {
       
   230 			StringID str = wi->data;
       
   231 
       
   232 			if ((wi->type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
       
   233 
       
   234 			DrawStringCentered(((r.left + r.right + 1) >> 1) + clicked, ((r.top + r.bottom + 1) >> 1) - 5 + clicked, str, 0);
       
   235 			goto draw_default;
       
   236 		}
       
   237 
       
   238 		case WWT_INSET: {
       
   239 			StringID str = wi->data;
       
   240 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED);
       
   241 
       
   242 			if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, 0, r.right - r.left - 10);
       
   243 			goto draw_default;
       
   244 		}
       
   245 
       
   246 		case WWT_MATRIX: {
       
   247 			int c, d, ctr;
       
   248 			int x, amt1, amt2;
       
   249 			int color;
       
   250 
       
   251 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   252 
       
   253 			c = GB(wi->data, 0, 8);
       
   254 			amt1 = (wi->right - wi->left + 1) / c;
       
   255 
       
   256 			d = GB(wi->data, 8, 8);
       
   257 			amt2 = (wi->bottom - wi->top + 1) / d;
       
   258 
       
   259 			color = _colour_gradient[wi->color & 0xF][6];
       
   260 
       
   261 			x = r.left;
       
   262 			for (ctr = c; ctr > 1; ctr--) {
       
   263 				x += amt1;
       
   264 				GfxFillRect(x, r.top + 1, x, r.bottom - 1, color);
       
   265 			}
       
   266 
       
   267 			x = r.top;
       
   268 			for (ctr = d; ctr > 1; ctr--) {
       
   269 				x += amt2;
       
   270 				GfxFillRect(r.left + 1, x, r.right - 1, x, color);
       
   271 			}
       
   272 
       
   273 			color = _colour_gradient[wi->color&0xF][4];
       
   274 
       
   275 			x = r.left - 1;
       
   276 			for (ctr = c; ctr > 1; ctr--) {
       
   277 				x += amt1;
       
   278 				GfxFillRect(x, r.top + 1, x, r.bottom - 1, color);
       
   279 			}
       
   280 
       
   281 			x = r.top - 1;
       
   282 			for (ctr = d; ctr > 1; ctr--) {
       
   283 				x += amt2;
       
   284 				GfxFillRect(r.left+1, x, r.right-1, x, color);
       
   285 			}
       
   286 
       
   287 			goto draw_default;
       
   288 		}
       
   289 
       
   290 		// vertical scrollbar
       
   291 		case WWT_SCROLLBAR: {
       
   292 			Point pt;
       
   293 			int c1,c2;
       
   294 
       
   295 			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
       
   296 
       
   297 			// draw up/down buttons
       
   298 			clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP);
       
   299 			DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : 0);
       
   300 			DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10);
       
   301 
       
   302 			clicked = (((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN));
       
   303 			DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   304 			DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10);
       
   305 
       
   306 			c1 = _colour_gradient[wi->color&0xF][3];
       
   307 			c2 = _colour_gradient[wi->color&0xF][7];
       
   308 
       
   309 			// draw "shaded" background
       
   310 			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2);
       
   311 			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT);
       
   312 
       
   313 			// draw shaded lines
       
   314 			GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1);
       
   315 			GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2);
       
   316 			GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1);
       
   317 			GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2);
       
   318 
       
   319 			pt = HandleScrollbarHittest(&w->vscroll, r.top, r.bottom);
       
   320 			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);
       
   321 			break;
       
   322 		}
       
   323 		case WWT_SCROLL2BAR: {
       
   324 			Point pt;
       
   325 			int c1,c2;
       
   326 
       
   327 			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
       
   328 
       
   329 			// draw up/down buttons
       
   330 			clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2));
       
   331 			DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color,  (clicked) ? FR_LOWERED : 0);
       
   332 			DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10);
       
   333 
       
   334 			clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2));
       
   335 			DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color,  (clicked) ? FR_LOWERED : 0);
       
   336 			DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10);
       
   337 
       
   338 			c1 = _colour_gradient[wi->color&0xF][3];
       
   339 			c2 = _colour_gradient[wi->color&0xF][7];
       
   340 
       
   341 			// draw "shaded" background
       
   342 			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2);
       
   343 			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT);
       
   344 
       
   345 			// draw shaded lines
       
   346 			GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1);
       
   347 			GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2);
       
   348 			GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1);
       
   349 			GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2);
       
   350 
       
   351 			pt = HandleScrollbarHittest(&w->vscroll2, r.top, r.bottom);
       
   352 			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);
       
   353 			break;
       
   354 		}
       
   355 
       
   356 		// horizontal scrollbar
       
   357 		case WWT_HSCROLLBAR: {
       
   358 			Point pt;
       
   359 			int c1,c2;
       
   360 
       
   361 			assert(r.bottom - r.top == 11); // XXX - to ensure the same sizes are used everywhere!
       
   362 
       
   363 			clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL));
       
   364 			DrawFrameRect(r.left, r.top, r.left + 9, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   365 			DrawSprite(SPR_ARROW_LEFT, r.left + 1 + clicked, r.top + 1 + clicked);
       
   366 
       
   367 			clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL));
       
   368 			DrawFrameRect(r.right-9, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   369 			DrawSprite(SPR_ARROW_RIGHT, r.right - 8 + clicked, r.top + 1 + clicked);
       
   370 
       
   371 			c1 = _colour_gradient[wi->color&0xF][3];
       
   372 			c2 = _colour_gradient[wi->color&0xF][7];
       
   373 
       
   374 			// draw "shaded" background
       
   375 			GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c2);
       
   376 			GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c1 | PALETTE_MODIFIER_GREYOUT);
       
   377 
       
   378 			// draw shaded lines
       
   379 			GfxFillRect(r.left+10, r.top+2, r.right-10, r.top+2, c1);
       
   380 			GfxFillRect(r.left+10, r.top+3, r.right-10, r.top+3, c2);
       
   381 			GfxFillRect(r.left+10, r.top+7, r.right-10, r.top+7, c1);
       
   382 			GfxFillRect(r.left+10, r.top+8, r.right-10, r.top+8, c2);
       
   383 
       
   384 			// draw actual scrollbar
       
   385 			pt = HandleScrollbarHittest(&w->hscroll, r.left, r.right);
       
   386 			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);
       
   387 
       
   388 			break;
       
   389 		}
       
   390 
       
   391 		case WWT_FRAME: {
       
   392 			int c1,c2;
       
   393 			int x2 = r.left; // by default the left side is the left side of the widget
       
   394 
       
   395 			if (wi->data != 0) x2 = DrawString(r.left + 6, r.top, wi->data, 0);
       
   396 
       
   397 			c1 = _colour_gradient[wi->color][3];
       
   398 			c2 = _colour_gradient[wi->color][7];
       
   399 
       
   400 			//Line from upper left corner to start of text
       
   401 			GfxFillRect(r.left, r.top+4, r.left+4,r.top+4, c1);
       
   402 			GfxFillRect(r.left+1, r.top+5, r.left+4,r.top+5, c2);
       
   403 
       
   404 			// Line from end of text to upper right corner
       
   405 			GfxFillRect(x2, r.top+4, r.right-1,r.top+4,c1);
       
   406 			GfxFillRect(x2, r.top+5, r.right-2,r.top+5,c2);
       
   407 
       
   408 			// Line from upper left corner to bottom left corner
       
   409 			GfxFillRect(r.left, r.top+5, r.left, r.bottom-1, c1);
       
   410 			GfxFillRect(r.left+1, r.top+6, r.left+1, r.bottom-2, c2);
       
   411 
       
   412 			//Line from upper right corner to bottom right corner
       
   413 			GfxFillRect(r.right-1, r.top+5, r.right-1, r.bottom-2, c1);
       
   414 			GfxFillRect(r.right, r.top+4, r.right, r.bottom-1, c2);
       
   415 
       
   416 			GfxFillRect(r.left+1, r.bottom-1, r.right-1, r.bottom-1, c1);
       
   417 			GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2);
       
   418 
       
   419 			goto draw_default;
       
   420 		}
       
   421 
       
   422 		case WWT_STICKYBOX: {
       
   423 			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
       
   424 
       
   425 			clicked = !!(w->flags4 & WF_STICKY);
       
   426 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   427 			DrawSprite((clicked) ? SPR_PIN_UP : SPR_PIN_DOWN, r.left + 2 + clicked, r.top + 3 + clicked);
       
   428 			break;
       
   429 		}
       
   430 
       
   431 		case WWT_RESIZEBOX: {
       
   432 			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
       
   433 
       
   434 			clicked = !!(w->flags4 & WF_SIZING);
       
   435 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
       
   436 			DrawSprite(SPR_WINDOW_RESIZE, r.left + 3 + clicked, r.top + 3 + clicked);
       
   437 			break;
       
   438 		}
       
   439 
       
   440 		case WWT_CLOSEBOX: {
       
   441 			assert(r.right - r.left == 10); // ensure the same sizes are used everywhere
       
   442 
       
   443 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, 0);
       
   444 			DrawString(r.left + 2, r.top + 2, STR_00C5, 0);
       
   445 			break;
       
   446 		}
       
   447 
       
   448 		case WWT_CAPTION: {
       
   449 			assert(r.bottom - r.top == 13); // XXX - to ensure the same sizes are used everywhere!
       
   450 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_BORDERONLY);
       
   451 			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);
       
   452 
       
   453 			if (w->caption_color != 0xFF) {
       
   454 				GfxFillRect(r.left+2, r.top+2, r.right-2, r.bottom-2, _colour_gradient[_player_colors[w->caption_color]][4]);
       
   455 			}
       
   456 
       
   457 			DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top+2, wi->data, 0x84);
       
   458 draw_default:;
       
   459 			if (IsWindowWidgetDisabled(w, i)) {
       
   460 				GfxFillRect(r.left+1, r.top+1, r.right-1, r.bottom-1, _colour_gradient[wi->color&0xF][2] | PALETTE_MODIFIER_GREYOUT);
       
   461 			}
       
   462 		}
       
   463 		}
       
   464 	}
       
   465 
       
   466 
       
   467 	if (w->flags4 & WF_WHITE_BORDER_MASK) {
       
   468 		//DrawFrameRect(w->left, w->top, w->left + w->width-1, w->top+w->height-1, 0xF, 0x10);
       
   469 		DrawFrameRect(0, 0, w->width-1, w->height-1, 0xF, FR_BORDERONLY);
       
   470 	}
       
   471 
       
   472 }
       
   473 
       
   474 static const Widget _dropdown_menu_widgets[] = {
       
   475 {      WWT_PANEL,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_NULL},
       
   476 {  WWT_SCROLLBAR,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   477 {   WIDGETS_END},
       
   478 };
       
   479 
       
   480 static int GetDropdownItem(const Window *w)
       
   481 {
       
   482 	byte item, counter;
       
   483 	int y;
       
   484 
       
   485 	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0)
       
   486 		return -1;
       
   487 
       
   488 	y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10;
       
   489 
       
   490 	if (y < 0)
       
   491 		return - 1;
       
   492 
       
   493 	item = y / 10;
       
   494 	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)
       
   495 		return - 1;
       
   496 
       
   497 	// Skip hidden items -- +1 for each hidden item before the clicked item.
       
   498 	for (counter = 0; item >= counter; ++counter)
       
   499 		if (HASBIT(WP(w,dropdown_d).hidden_state, counter)) item++;
       
   500 
       
   501 	return item;
       
   502 }
       
   503 
       
   504 static void DropdownMenuWndProc(Window *w, WindowEvent *e)
       
   505 {
       
   506 	int item;
       
   507 
       
   508 	switch (e->event) {
       
   509 		case WE_PAINT: {
       
   510 			int x,y,i,sel;
       
   511 			int width, height;
       
   512 
       
   513 			DrawWindowWidgets(w);
       
   514 
       
   515 			x = 1;
       
   516 			y = 2 - w->vscroll.pos * 10;
       
   517 
       
   518 			sel    = WP(w,dropdown_d).selected_index;
       
   519 			width  = w->widget[0].right - 3;
       
   520 			height = w->widget[0].bottom - 3;
       
   521 
       
   522 			for (i = 0; WP(w,dropdown_d).items[i] != INVALID_STRING_ID; i++, sel--) {
       
   523 				if (HASBIT(WP(w,dropdown_d).hidden_state, i)) continue;
       
   524 
       
   525 				if (y >= 0 && y <= height) {
       
   526 					if (WP(w,dropdown_d).items[i] != STR_NULL) {
       
   527 						if (sel == 0) GfxFillRect(x + 1, y, x + width, y + 9, 0);
       
   528 						DrawStringTruncated(x + 2, y, WP(w,dropdown_d).items[i], sel == 0 ? 12 : 16, x + width);
       
   529 
       
   530 						if (HASBIT(WP(w,dropdown_d).disabled_state, i)) {
       
   531 							GfxFillRect(x, y, x + width, y + 9,
       
   532 								PALETTE_MODIFIER_GREYOUT | _colour_gradient[_dropdown_menu_widgets[0].color][5]
       
   533 							);
       
   534 						}
       
   535 					} else {
       
   536 						int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3];
       
   537 						int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7];
       
   538 
       
   539 						GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1);
       
   540 						GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2);
       
   541 					}
       
   542 				}
       
   543 				y += 10;
       
   544 			}
       
   545 		} break;
       
   546 
       
   547 		case WE_CLICK: {
       
   548 			if (e->we.click.widget != 0) break;
       
   549 			item = GetDropdownItem(w);
       
   550 			if (item >= 0) {
       
   551 				WP(w,dropdown_d).click_delay = 4;
       
   552 				WP(w,dropdown_d).selected_index = item;
       
   553 				SetWindowDirty(w);
       
   554 			}
       
   555 		} break;
       
   556 
       
   557 		case WE_MOUSELOOP: {
       
   558 			Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   559 			if (w2 == NULL) {
       
   560 				DeleteWindow(w);
       
   561 				return;
       
   562 			}
       
   563 
       
   564 			if (WP(w,dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) {
       
   565 				WindowEvent e;
       
   566 				e.event = WE_DROPDOWN_SELECT;
       
   567 				e.we.dropdown.button = WP(w,dropdown_d).parent_button;
       
   568 				e.we.dropdown.index  = WP(w,dropdown_d).selected_index;
       
   569 				w2->wndproc(w2, &e);
       
   570 				DeleteWindow(w);
       
   571 				return;
       
   572 			}
       
   573 
       
   574 			if (WP(w,dropdown_d).drag_mode) {
       
   575 				item = GetDropdownItem(w);
       
   576 
       
   577 				if (!_left_button_clicked) {
       
   578 					WP(w,dropdown_d).drag_mode = false;
       
   579 					if (item < 0) return;
       
   580 					WP(w,dropdown_d).click_delay = 2;
       
   581 				} else {
       
   582 					if (item < 0) return;
       
   583 				}
       
   584 
       
   585 				WP(w,dropdown_d).selected_index = item;
       
   586 				SetWindowDirty(w);
       
   587 			}
       
   588 		} break;
       
   589 
       
   590 		case WE_DESTROY: {
       
   591 			Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   592 			if (w2 != NULL) {
       
   593 				RaiseWindowWidget(w2, WP(w,dropdown_d).parent_button);
       
   594 				InvalidateWidget(w2, WP(w,dropdown_d).parent_button);
       
   595 			}
       
   596 		} break;
       
   597 	}
       
   598 }
       
   599 
       
   600 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask)
       
   601 {
       
   602 	int i;
       
   603 	const Widget *wi;
       
   604 	Window *w2;
       
   605 	const Window *w3;
       
   606 	bool is_dropdown_menu_shown = IsWindowWidgetLowered(w, button);
       
   607 	int top, height;
       
   608 	int screen_top, screen_bottom;
       
   609 	bool scroll = false;
       
   610 
       
   611 	DeleteWindowById(WC_DROPDOWN_MENU, 0);
       
   612 
       
   613 	if (is_dropdown_menu_shown) return;
       
   614 
       
   615 	LowerWindowWidget(w, button);
       
   616 
       
   617 	InvalidateWidget(w, button);
       
   618 
       
   619 	for (i = 0; strings[i] != INVALID_STRING_ID; i++) {}
       
   620 	if (i == 0) return;
       
   621 
       
   622 	wi = &w->widget[button];
       
   623 
       
   624 	if (hidden_mask != 0) {
       
   625 		uint j;
       
   626 
       
   627 		for (j = 0; strings[j] != INVALID_STRING_ID; j++) {
       
   628 			if (HASBIT(hidden_mask, j)) i--;
       
   629 		}
       
   630 	}
       
   631 
       
   632 	/* The preferred position is just below the dropdown calling widget */
       
   633 	top = w->top + wi->bottom + 2;
       
   634 	height = i * 10 + 4;
       
   635 
       
   636 	w3 = FindWindowById(WC_STATUS_BAR, 0);
       
   637 	screen_bottom = w3 == NULL ? _screen.height : w3->top;
       
   638 
       
   639 	/* Check if the dropdown will fully fit below the widget */
       
   640 	if (top + height >= screen_bottom) {
       
   641 		w3 = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
   642 		screen_top = w3 == NULL ? 0 : w3->top + w3->height;
       
   643 
       
   644 		/* If not, check if it will fit above the widget */
       
   645 		if (w->top + wi->top - height - 1 > screen_top) {
       
   646 			top = w->top + wi->top - height - 1;
       
   647 		} else {
       
   648 			/* ... and lastly if it won't, enable the scroll bar and fit the
       
   649 			 * list in below the widget */
       
   650 			int rows = (screen_bottom - 4 - top) / 10;
       
   651 			height = rows * 10 + 4;
       
   652 			scroll = true;
       
   653 		}
       
   654 	}
       
   655 
       
   656 	w2 = AllocateWindow(
       
   657 		w->left + wi[-1].left + 1,
       
   658 		top,
       
   659 		wi->right - wi[-1].left + 1,
       
   660 		height,
       
   661 		DropdownMenuWndProc,
       
   662 		WC_DROPDOWN_MENU,
       
   663 		_dropdown_menu_widgets);
       
   664 
       
   665 	w2->widget[0].color = wi->color;
       
   666 	w2->widget[0].right = wi->right - wi[-1].left;
       
   667 	w2->widget[0].bottom = height - 1;
       
   668 
       
   669 	SetWindowWidgetHiddenState(w2, 1, !scroll);
       
   670 
       
   671 	if (scroll) {
       
   672 		/* We're scrolling, so enable the scroll bar and shrink the list by
       
   673 		 * the scrollbar's width */
       
   674 		w2->widget[1].color  = wi->color;
       
   675 		w2->widget[1].right  = w2->widget[0].right;
       
   676 		w2->widget[1].left   = w2->widget[1].right - 11;
       
   677 		w2->widget[1].bottom = height - 1;
       
   678 		w2->widget[0].right -= 12;
       
   679 
       
   680 		w2->vscroll.cap   = (height - 4) / 10;
       
   681 		w2->vscroll.count = i;
       
   682 	}
       
   683 
       
   684 	w2->desc_flags = WDF_DEF_WIDGET;
       
   685 	w2->flags4 &= ~WF_WHITE_BORDER_MASK;
       
   686 
       
   687 	WP(w2,dropdown_d).disabled_state = disabled_mask;
       
   688 	WP(w2,dropdown_d).hidden_state = hidden_mask;
       
   689 
       
   690 	WP(w2,dropdown_d).parent_wnd_class = w->window_class;
       
   691 	WP(w2,dropdown_d).parent_wnd_num = w->window_number;
       
   692 	WP(w2,dropdown_d).parent_button = button;
       
   693 
       
   694 	WP(w2,dropdown_d).num_items = i;
       
   695 	WP(w2,dropdown_d).selected_index = selected;
       
   696 	WP(w2,dropdown_d).items = strings;
       
   697 
       
   698 	WP(w2,dropdown_d).click_delay = 0;
       
   699 	WP(w2,dropdown_d).drag_mode = true;
       
   700 }