|
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 |