00001
00002
00005 #include "../stdafx.h"
00006 #include "../openttd.h"
00007 #include "../strings_type.h"
00008 #include "../window_gui.h"
00009 #include "../strings_func.h"
00010 #include "../strings_type.h"
00011 #include "../gfx_func.h"
00012 #include "../window_func.h"
00013 #include "../core/math_func.hpp"
00014 #include "dropdown_type.h"
00015 #include "dropdown_func.h"
00016
00017 #include "../table/sprites.h"
00018 #include "table/strings.h"
00019
00020 void DropDownListItem::Draw(int x, int y, uint width, uint height, bool sel, int bg_colour) const
00021 {
00022 int c1 = _colour_gradient[bg_colour][3];
00023 int c2 = _colour_gradient[bg_colour][7];
00024
00025 GfxFillRect(x + 1, y + 3, x + width - 2, y + 3, c1);
00026 GfxFillRect(x + 1, y + 4, x + width - 2, y + 4, c2);
00027 }
00028
00029 uint DropDownListStringItem::Width() const
00030 {
00031 char buffer[512];
00032 GetString(buffer, this->String(), lastof(buffer));
00033 return GetStringBoundingBox(buffer).width;
00034 }
00035
00036 void DropDownListStringItem::Draw(int x, int y, uint width, uint height, bool sel, int bg_colour) const
00037 {
00038 DrawStringTruncated(x + 2, y, this->String(), sel ? TC_WHITE : TC_BLACK, width);
00039 }
00040
00041 StringID DropDownListParamStringItem::String() const
00042 {
00043 for (uint i = 0; i < lengthof(this->decode_params); i++) SetDParam(i, this->decode_params[i]);
00044 return this->string;
00045 }
00046
00051 static void DeleteDropDownList(DropDownList *list)
00052 {
00053 for (DropDownList::iterator it = list->begin(); it != list->end(); ++it) {
00054 DropDownListItem *item = *it;
00055 delete item;
00056 }
00057 delete list;
00058 }
00059
00060 static const Widget _dropdown_menu_widgets[] = {
00061 { WWT_PANEL, RESIZE_NONE, COLOUR_END, 0, 0, 0, 0, 0x0, STR_NULL},
00062 { WWT_SCROLLBAR, RESIZE_NONE, COLOUR_END, 0, 0, 0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00063 { WIDGETS_END},
00064 };
00065
00066 struct DropdownWindow : Window {
00067 WindowClass parent_wnd_class;
00068 WindowNumber parent_wnd_num;
00069 byte parent_button;
00070 DropDownList *list;
00071 int selected_index;
00072 byte click_delay;
00073 bool drag_mode;
00074 bool instant_close;
00075 int scrolling;
00076
00077 DropdownWindow(int x, int y, int width, int height, const Widget *widget) : Window(x, y, width, height, WC_DROPDOWN_MENU, widget)
00078 {
00079 this->FindWindowPlacementAndResize(width, height);
00080 }
00081
00082 ~DropdownWindow()
00083 {
00084 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00085 if (w2 != NULL) {
00086 w2->RaiseWidget(this->parent_button);
00087 w2->InvalidateWidget(this->parent_button);
00088 }
00089
00090 DeleteDropDownList(this->list);
00091 }
00092
00093 bool GetDropDownItem(int &value)
00094 {
00095 if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return false;
00096
00097 int y = _cursor.pos.y - this->top - 2;
00098 int width = this->widget[0].right - 3;
00099 int pos = this->vscroll.pos;
00100
00101 const DropDownList *list = this->list;
00102
00103 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00104
00105 if (--pos >= 0) continue;
00106
00107 const DropDownListItem *item = *it;
00108 int item_height = item->Height(width);
00109
00110 if (y < item_height) {
00111 if (item->masked || !item->Selectable()) return false;
00112 value = item->result;
00113 return true;
00114 }
00115
00116 y -= item_height;
00117 }
00118
00119 return false;
00120 }
00121
00122 virtual void OnPaint()
00123 {
00124 this->DrawWidgets();
00125
00126 int x = 1;
00127 int y = 2;
00128
00129 int sel = this->selected_index;
00130 int width = this->widget[0].right - 2;
00131 int height = this->widget[0].bottom;
00132 int pos = this->vscroll.pos;
00133
00134 DropDownList *list = this->list;
00135
00136 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00137 const DropDownListItem *item = *it;
00138 int item_height = item->Height(width);
00139
00140
00141 if (--pos >= 0) continue;
00142
00143 if (y + item_height < height) {
00144 if (sel == item->result) GfxFillRect(x + 1, y, x + width - 1, y + item_height - 1, 0);
00145
00146 item->Draw(x, y, width, height, sel == item->result, (TextColour)this->widget[0].color);
00147
00148 if (item->masked) {
00149 GfxFillRect(x, y, x + width - 1, y + item_height - 1,
00150 _colour_gradient[this->widget[0].color][5], FILLRECT_CHECKER
00151 );
00152 }
00153 }
00154 y += item_height;
00155 }
00156 };
00157
00158 virtual void OnClick(Point pt, int widget)
00159 {
00160 if (widget != 0) return;
00161 int item;
00162 if (this->GetDropDownItem(item)) {
00163 this->click_delay = 4;
00164 this->selected_index = item;
00165 this->SetDirty();
00166 }
00167 }
00168
00169 virtual void OnTick()
00170 {
00171 if (this->scrolling == -1) {
00172 this->vscroll.pos = max(0, this->vscroll.pos - 1);
00173 this->SetDirty();
00174 } else if (this->scrolling == 1) {
00175 this->vscroll.pos = min(this->vscroll.count - this->vscroll.cap, this->vscroll.pos + 1);
00176 this->SetDirty();
00177 }
00178 this->scrolling = 0;
00179 }
00180
00181 virtual void OnMouseLoop()
00182 {
00183 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00184 if (w2 == NULL) {
00185 delete this;
00186 return;
00187 }
00188
00189 if (this->click_delay != 0 && --this->click_delay == 0) {
00190 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00191 delete this;
00192 return;
00193 }
00194
00195 if (this->drag_mode) {
00196 int item;
00197
00198 if (!_left_button_clicked) {
00199 this->drag_mode = false;
00200 if (!this->GetDropDownItem(item)) {
00201 if (this->instant_close) {
00202 if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == this->parent_button) {
00203
00204
00205 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00206 }
00207 delete this;
00208 }
00209 return;
00210 }
00211 this->click_delay = 2;
00212 } else {
00213 if (_cursor.pos.y <= this->top + 2) {
00214
00215 this->scrolling = -1;
00216 return;
00217 } else if (_cursor.pos.y >= this->top + this->height - 2) {
00218
00219 this->scrolling = 1;
00220 return;
00221 }
00222
00223 if (!this->GetDropDownItem(item)) return;
00224 }
00225
00226 this->selected_index = item;
00227 this->SetDirty();
00228 }
00229 }
00230 };
00231
00232 void ShowDropDownList(Window *w, DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close)
00233 {
00234 bool is_dropdown_menu_shown = w->IsWidgetLowered(button);
00235
00236 DeleteWindowById(WC_DROPDOWN_MENU, 0);
00237
00238 if (is_dropdown_menu_shown) {
00239 DeleteDropDownList(list);
00240 return;
00241 }
00242
00243 w->LowerWidget(button);
00244 w->InvalidateWidget(button);
00245
00246
00247
00248 const Widget *wi = &w->widget[button];
00249
00250
00251 int top = w->top + wi->bottom + 1;
00252
00253 if (width == 0) width = wi->right - wi->left + 1;
00254
00255 uint max_item_width = 0;
00256
00257 if (auto_width) {
00258
00259 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00260 const DropDownListItem *item = *it;
00261 max_item_width = max(max_item_width, item->Width() + 5);
00262 }
00263 }
00264
00265
00266 int list_height = 0;
00267
00268 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00269 DropDownListItem *item = *it;
00270 list_height += item->Height(width);
00271 }
00272
00273
00274 int height = list_height;
00275
00276
00277 Window *w3 = FindWindowById(WC_STATUS_BAR, 0);
00278 int screen_bottom = w3 == NULL ? _screen.height : w3->top;
00279
00280 bool scroll = false;
00281
00282
00283 if (top + height + 4 >= screen_bottom) {
00284 w3 = FindWindowById(WC_MAIN_TOOLBAR, 0);
00285 int screen_top = w3 == NULL ? 0 : w3->top + w3->height;
00286
00287
00288 if (w->top + wi->top - height > screen_top) {
00289 top = w->top + wi->top - height - 4;
00290 } else {
00291
00292
00293 int avg_height = list_height / (int)list->size();
00294 int rows = (screen_bottom - 4 - top) / avg_height;
00295 height = rows * avg_height;
00296 scroll = true;
00297
00298
00299 max_item_width += 12;
00300 }
00301 }
00302
00303 if (auto_width) width = max(width, max_item_width);
00304
00305 DropdownWindow *dw = new DropdownWindow(
00306 w->left + wi->left,
00307 top,
00308 width,
00309 height + 4,
00310 _dropdown_menu_widgets);
00311
00312 dw->widget[0].color = wi->color;
00313 dw->widget[0].right = width - 1;
00314 dw->widget[0].bottom = height + 3;
00315
00316 dw->SetWidgetHiddenState(1, !scroll);
00317
00318 if (scroll) {
00319
00320
00321 dw->widget[1].color = wi->color;
00322 dw->widget[1].right = dw->widget[0].right;
00323 dw->widget[1].left = dw->widget[1].right - 11;
00324 dw->widget[1].bottom = dw->widget[0].bottom;
00325 dw->widget[0].right -= 12;
00326
00327
00328 dw->vscroll.cap = height * (uint16)list->size() / list_height;
00329 dw->vscroll.count = (uint16)list->size();
00330 }
00331
00332 dw->desc_flags = WDF_DEF_WIDGET;
00333 dw->flags4 &= ~WF_WHITE_BORDER_MASK;
00334
00335 dw->parent_wnd_class = w->window_class;
00336 dw->parent_wnd_num = w->window_number;
00337 dw->parent_button = button;
00338 dw->list = list;
00339 dw->selected_index = selected;
00340 dw->click_delay = 0;
00341 dw->drag_mode = true;
00342 dw->instant_close = instant_close;
00343 }
00344
00345 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width)
00346 {
00347
00348 if (w->IsWidgetLowered(button)) {
00349 DeleteWindowById(WC_DROPDOWN_MENU, 0);
00350 return;
00351 }
00352
00353 uint result = 0;
00354 DropDownList *list = new DropDownList();
00355
00356 for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
00357 if (!HasBit(hidden_mask, i)) {
00358 list->push_back(new DropDownListStringItem(strings[i], result, HasBit(disabled_mask, i)));
00359 }
00360 result++;
00361 }
00362
00363
00364 if (list->size() == 0) {
00365 DeleteDropDownList(list);
00366 return;
00367 }
00368
00369 ShowDropDownList(w, list, selected, button, width);
00370 }
00371
00376 void HideDropDownMenu(Window *pw)
00377 {
00378 Window **wz;
00379 FOR_ALL_WINDOWS(wz) {
00380 if ((*wz)->window_class != WC_DROPDOWN_MENU) continue;
00381
00382 DropdownWindow *dw = dynamic_cast<DropdownWindow*>(*wz);
00383 if (pw->window_class == dw->parent_wnd_class &&
00384 pw->window_number == dw->parent_wnd_num) {
00385 delete dw;
00386 break;
00387 }
00388 }
00389 }
00390