src/widgets/dropdown.cpp
changeset 8780 6f3f3ec6c69a
child 8790 7eae7a27e2f6
equal deleted inserted replaced
8779:efb41d093a39 8780:6f3f3ec6c69a
       
     1 /* $Id$ */
       
     2 
       
     3 #include "../stdafx.h"
       
     4 #include "../openttd.h"
       
     5 #include "../strings_type.h"
       
     6 #include "../window_gui.h"
       
     7 #include "../strings_func.h"
       
     8 #include "../strings_type.h"
       
     9 #include "../gfx_func.h"
       
    10 #include "../window_func.h"
       
    11 #include "dropdown_type.h"
       
    12 #include "dropdown_func.h"
       
    13 
       
    14 #include "../table/sprites.h"
       
    15 #include "table/strings.h"
       
    16 
       
    17 StringID DropDownListItem::String() const
       
    18 {
       
    19 	return STR_NULL;
       
    20 }
       
    21 
       
    22 StringID DropDownListStringItem::String() const
       
    23 {
       
    24 	return this->string;
       
    25 }
       
    26 
       
    27 StringID DropDownListParamStringItem::String() const
       
    28 {
       
    29 	for (uint i = 0; i < lengthof(this->decode_params); i++) SetDParam(i, this->decode_params[i]);
       
    30 	return this->string;
       
    31 }
       
    32 
       
    33 struct dropdown_d {
       
    34 	WindowClass parent_wnd_class;
       
    35 	WindowNumber parent_wnd_num;
       
    36 	byte parent_button;
       
    37 	DropDownList *list;
       
    38 	byte selected_index;
       
    39 	byte click_delay;
       
    40 	bool drag_mode;
       
    41 };
       
    42 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(dropdown_d));
       
    43 
       
    44 static const Widget _dropdown_menu_widgets[] = {
       
    45 {      WWT_PANEL,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_NULL},
       
    46 {  WWT_SCROLLBAR,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
    47 {   WIDGETS_END},
       
    48 };
       
    49 
       
    50 static int GetDropDownItem(const Window *w)
       
    51 {
       
    52 	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0) return -1;
       
    53 
       
    54 	int y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10;
       
    55 	if (y < 0) return -1;
       
    56 
       
    57 	uint selected_row = y / 10;
       
    58 	const DropDownList *list = WP(w, dropdown_d).list;
       
    59 
       
    60 	if (selected_row >= list->size()) return -1;
       
    61 
       
    62 	for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it, selected_row--) {
       
    63 		if (selected_row == 0) {
       
    64 			const DropDownListItem *item = *it;
       
    65 			if (item->masked || item->String() == STR_NULL) return -1;
       
    66 			return item->result;
       
    67 		}
       
    68 	}
       
    69 
       
    70 	return -1;
       
    71 }
       
    72 
       
    73 static void DropDownMenuWndProc(Window *w, WindowEvent *e)
       
    74 {
       
    75 	switch (e->event) {
       
    76 		case WE_PAINT: {
       
    77 			DrawWindowWidgets(w);
       
    78 
       
    79 			int x = 1;
       
    80 			int y = 2 - w->vscroll.pos * 10;
       
    81 
       
    82 			int sel    = WP(w, dropdown_d).selected_index;
       
    83 			int width  = w->widget[0].right - 3;
       
    84 			int height = w->widget[0].bottom - 3;
       
    85 
       
    86 			DropDownList *list = WP(w, dropdown_d).list;
       
    87 
       
    88 			for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
       
    89 				if (y >= 0 && y <= height) {
       
    90 					const DropDownListItem *item = *it;
       
    91 					if (item->String() != STR_NULL) {
       
    92 						if (sel == item->result) GfxFillRect(x + 1, y, x + width, y + 9, 0);
       
    93 
       
    94 						DrawStringTruncated(x + 2, y, item->String(), sel == item->result ? TC_WHITE : TC_BLACK, x + width);
       
    95 
       
    96 						if (item->masked) {
       
    97 							GfxFillRect(x, y, x + width, y + 9,
       
    98 								(1 << PALETTE_MODIFIER_GREYOUT) | _colour_gradient[_dropdown_menu_widgets[0].color][5]
       
    99 							);
       
   100 						}
       
   101 					} else {
       
   102 						int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3];
       
   103 						int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7];
       
   104 
       
   105 						GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1);
       
   106 						GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2);
       
   107 					}
       
   108 				}
       
   109 				y += 10;
       
   110 			}
       
   111 		} break;
       
   112 
       
   113 		case WE_CLICK: {
       
   114 			if (e->we.click.widget != 0) break;
       
   115 			int item = GetDropDownItem(w);
       
   116 			if (item >= 0) {
       
   117 				WP(w, dropdown_d).click_delay = 4;
       
   118 				WP(w, dropdown_d).selected_index = item;
       
   119 				SetWindowDirty(w);
       
   120 			}
       
   121 		} break;
       
   122 
       
   123 		case WE_MOUSELOOP: {
       
   124 			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   125 			if (w2 == NULL) {
       
   126 				DeleteWindow(w);
       
   127 				return;
       
   128 			}
       
   129 
       
   130 			if (WP(w, dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) {
       
   131 				WindowEvent e;
       
   132 				e.event = WE_DROPDOWN_SELECT;
       
   133 				e.we.dropdown.button = WP(w, dropdown_d).parent_button;
       
   134 				e.we.dropdown.index  = WP(w, dropdown_d).selected_index;
       
   135 				w2->wndproc(w2, &e);
       
   136 				DeleteWindow(w);
       
   137 				return;
       
   138 			}
       
   139 
       
   140 			if (WP(w, dropdown_d).drag_mode) {
       
   141 				int item = GetDropDownItem(w);
       
   142 
       
   143 				if (!_left_button_clicked) {
       
   144 					WP(w, dropdown_d).drag_mode = false;
       
   145 					if (item < 0) return;
       
   146 					WP(w, dropdown_d).click_delay = 2;
       
   147 				} else {
       
   148 					if (item < 0) return;
       
   149 				}
       
   150 
       
   151 				WP(w, dropdown_d).selected_index = item;
       
   152 				SetWindowDirty(w);
       
   153 			}
       
   154 		} break;
       
   155 
       
   156 		case WE_DESTROY: {
       
   157 			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   158 			if (w2 != NULL) {
       
   159 				w2->RaiseWidget(WP(w, dropdown_d).parent_button);
       
   160 				w2->InvalidateWidget(WP(w, dropdown_d).parent_button);
       
   161 			}
       
   162 
       
   163 			delete WP(w, dropdown_d).list;
       
   164 		} break;
       
   165 	}
       
   166 }
       
   167 
       
   168 void ShowDropDownList(Window *w, DropDownList *list, int selected, int button)
       
   169 {
       
   170 	bool is_dropdown_menu_shown = w->IsWidgetLowered(button);
       
   171 
       
   172 	DeleteWindowById(WC_DROPDOWN_MENU, 0);
       
   173 
       
   174 	if (is_dropdown_menu_shown) {
       
   175 		delete list;
       
   176 		return;
       
   177 	}
       
   178 
       
   179 	w->LowerWidget(button);
       
   180 	w->InvalidateWidget(button);
       
   181 
       
   182 	/* Our parent's button widget is used to determine where to place the drop
       
   183 	 * down list window. */
       
   184 	const Widget *wi = &w->widget[button];
       
   185 
       
   186 	/* The preferred position is just below the dropdown calling widget */
       
   187 	int top = w->top + wi->bottom + 2;
       
   188 	int height = list->size() * 10 + 4;
       
   189 
       
   190 	/* Check if the status bar is visible, as we don't want to draw over it */
       
   191 	Window *w3 = FindWindowById(WC_STATUS_BAR, 0);
       
   192 	int screen_bottom = w3 == NULL ? _screen.height : w3->top;
       
   193 
       
   194 	bool scroll = false;
       
   195 
       
   196 	/* Check if the dropdown will fully fit below the widget */
       
   197 	if (top + height >= screen_bottom) {
       
   198 		w3 = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
   199 		int screen_top = w3 == NULL ? 0 : w3->top + w3->height;
       
   200 
       
   201 		/* If not, check if it will fit above the widget */
       
   202 		if (w->top + wi->top - height - 1 > screen_top) {
       
   203 			top = w->top + wi->top - height - 1;
       
   204 		} else {
       
   205 			/* ... and lastly if it won't, enable the scroll bar and fit the
       
   206 			 * list in below the widget */
       
   207 			int rows = (screen_bottom - 4 - top) / 10;
       
   208 			height = rows * 10 + 4;
       
   209 			scroll = true;
       
   210 		}
       
   211 	}
       
   212 
       
   213 	Window *dw = AllocateWindow(
       
   214 		w->left + wi[-1].left + 1,
       
   215 		top,
       
   216 		wi->right - wi[-1].left + 1,
       
   217 		height,
       
   218 		DropDownMenuWndProc,
       
   219 		WC_DROPDOWN_MENU,
       
   220 		_dropdown_menu_widgets);
       
   221 
       
   222 	dw->widget[0].color = wi->color;
       
   223 	dw->widget[0].right = wi->right - wi[-1].left;
       
   224 	dw->widget[0].bottom = height - 1;
       
   225 
       
   226 	dw->SetWidgetHiddenState(1, !scroll);
       
   227 
       
   228 	if (scroll) {
       
   229 		/* We're scrolling, so enable the scroll bar and shrink the list by
       
   230 		 * the scrollbar's width */
       
   231 		dw->widget[1].color  = wi->color;
       
   232 		dw->widget[1].right  = dw->widget[0].right;
       
   233 		dw->widget[1].left   = dw->widget[1].right - 11;
       
   234 		dw->widget[1].bottom = height - 1;
       
   235 		dw->widget[0].right -= 12;
       
   236 
       
   237 		dw->vscroll.cap   = (height - 4) / 10;
       
   238 		dw->vscroll.count = list->size();
       
   239 	}
       
   240 
       
   241 	dw->desc_flags = WDF_DEF_WIDGET;
       
   242 	dw->flags4 &= ~WF_WHITE_BORDER_MASK;
       
   243 
       
   244 	WP(dw, dropdown_d).parent_wnd_class = w->window_class;
       
   245 	WP(dw, dropdown_d).parent_wnd_num   = w->window_number;
       
   246 	WP(dw, dropdown_d).parent_button    = button;
       
   247 	WP(dw, dropdown_d).list             = list;
       
   248 	WP(dw, dropdown_d).selected_index   = selected;
       
   249 	WP(dw, dropdown_d).click_delay      = 0;
       
   250 	WP(dw, dropdown_d).drag_mode        = true;
       
   251 }
       
   252 
       
   253 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask)
       
   254 {
       
   255 	uint result = 0;
       
   256 	DropDownList *list = new DropDownList();
       
   257 
       
   258 	for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
       
   259 		if (!HasBit(hidden_mask, i)) {
       
   260 			list->push_back(new DropDownListStringItem(strings[i], result, HasBit(disabled_mask, i)));
       
   261 		}
       
   262 		result++;
       
   263 	}
       
   264 
       
   265 	/* No entries in the list? */
       
   266 	if (list->size() == 0) {
       
   267 		delete list;
       
   268 		return;
       
   269 	}
       
   270 
       
   271 	ShowDropDownList(w, list, selected, button);
       
   272 }
       
   273 
       
   274 void HideDropDownMenu(Window *pw)
       
   275 {
       
   276 	Window **wz;
       
   277 	FOR_ALL_WINDOWS(wz) {
       
   278 		if ((*wz)->window_class != WC_DROPDOWN_MENU) continue;
       
   279 
       
   280 		if (pw->window_class == WP(*wz, dropdown_d).parent_wnd_class &&
       
   281 				pw->window_number == WP(*wz, dropdown_d).parent_wnd_num) {
       
   282 			DeleteWindow(*wz);
       
   283 			break;
       
   284 		}
       
   285 	}
       
   286 }
       
   287