00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../window_gui.h"
00014 #include "../string_func.h"
00015 #include "../strings_func.h"
00016 #include "../window_func.h"
00017 #include "dropdown_type.h"
00018
00019
00020 void DropDownListItem::Draw(int left, int right, int top, int bottom, 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 int mid = top + this->Height(0) / 2;
00026 GfxFillRect(left + 1, mid - 2, right - 1, mid - 2, c1);
00027 GfxFillRect(left + 1, mid - 1, right - 1, mid - 1, c2);
00028 }
00029
00030 uint DropDownListStringItem::Width() const
00031 {
00032 char buffer[512];
00033 GetString(buffer, this->String(), lastof(buffer));
00034 return GetStringBoundingBox(buffer).width;
00035 }
00036
00037 void DropDownListStringItem::Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
00038 {
00039 DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, this->String(), sel ? TC_WHITE : TC_BLACK);
00040 }
00041
00049 bool DropDownListStringItem::NatSortFunc(const DropDownListItem *first, const DropDownListItem *second)
00050 {
00051 char buffer1[512], buffer2[512];
00052 GetString(buffer1, static_cast<const DropDownListStringItem*>(first)->String(), lastof(buffer1));
00053 GetString(buffer2, static_cast<const DropDownListStringItem*>(second)->String(), lastof(buffer2));
00054 return strnatcmp(buffer1, buffer2) < 0;
00055 }
00056
00057 StringID DropDownListParamStringItem::String() const
00058 {
00059 for (uint i = 0; i < lengthof(this->decode_params); i++) SetDParam(i, this->decode_params[i]);
00060 return this->string;
00061 }
00062
00063 StringID DropDownListCharStringItem::String() const
00064 {
00065 SetDParamStr(0, this->raw_string);
00066 return this->string;
00067 }
00068
00073 static void DeleteDropDownList(DropDownList *list)
00074 {
00075 for (DropDownList::iterator it = list->begin(); it != list->end(); ++it) {
00076 DropDownListItem *item = *it;
00077 delete item;
00078 }
00079 delete list;
00080 }
00081
00083 enum DropdownMenuWidgets {
00084 DDM_ITEMS,
00085 DDM_SHOW_SCROLL,
00086 DDM_SCROLL,
00087 };
00088
00089 static const NWidgetPart _nested_dropdown_menu_widgets[] = {
00090 NWidget(NWID_HORIZONTAL),
00091 NWidget(WWT_PANEL, COLOUR_END, DDM_ITEMS), SetMinimalSize(1, 1), SetScrollbar(DDM_SCROLL), EndContainer(),
00092 NWidget(NWID_SELECTION, INVALID_COLOUR, DDM_SHOW_SCROLL),
00093 NWidget(NWID_VSCROLLBAR, COLOUR_END, DDM_SCROLL),
00094 EndContainer(),
00095 EndContainer(),
00096 };
00097
00098 const WindowDesc _dropdown_desc(
00099 WDP_MANUAL, 0, 0,
00100 WC_DROPDOWN_MENU, WC_NONE,
00101 0,
00102 _nested_dropdown_menu_widgets, lengthof(_nested_dropdown_menu_widgets)
00103 );
00104
00106 struct DropdownWindow : Window {
00107 WindowClass parent_wnd_class;
00108 WindowNumber parent_wnd_num;
00109 byte parent_button;
00110 DropDownList *list;
00111 int selected_index;
00112 byte click_delay;
00113 bool drag_mode;
00114 bool instant_close;
00115 int scrolling;
00116 Point position;
00117 Scrollbar *vscroll;
00118
00132 DropdownWindow(Window *parent, DropDownList *list, int selected, int button, bool instant_close, const Point &position, const Dimension &size, Colours wi_colour, bool scroll) : Window()
00133 {
00134 this->position = position;
00135
00136 this->CreateNestedTree(&_dropdown_desc);
00137
00138 this->vscroll = this->GetScrollbar(DDM_SCROLL);
00139
00140 uint items_width = size.width - (scroll ? NWidgetScrollbar::GetVerticalDimension().width : 0);
00141 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(DDM_ITEMS);
00142 nwi->SetMinimalSize(items_width, size.height + 4);
00143 nwi->colour = wi_colour;
00144
00145 nwi = this->GetWidget<NWidgetCore>(DDM_SCROLL);
00146 nwi->colour = wi_colour;
00147
00148 this->GetWidget<NWidgetStacked>(DDM_SHOW_SCROLL)->SetDisplayedPlane(scroll ? 0 : SZSP_NONE);
00149
00150 this->FinishInitNested(&_dropdown_desc, 0);
00151 CLRBITS(this->flags4, WF_WHITE_BORDER_MASK);
00152
00153
00154 int list_height = 0;
00155 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00156 DropDownListItem *item = *it;
00157 list_height += item->Height(items_width);
00158 }
00159
00160
00161 this->vscroll->SetCapacity(size.height * (uint16)list->size() / list_height);
00162 this->vscroll->SetCount((uint16)list->size());
00163
00164 this->parent_wnd_class = parent->window_class;
00165 this->parent_wnd_num = parent->window_number;
00166 this->parent_button = button;
00167 this->list = list;
00168 this->selected_index = selected;
00169 this->click_delay = 0;
00170 this->drag_mode = true;
00171 this->instant_close = instant_close;
00172 }
00173
00174 ~DropdownWindow()
00175 {
00176 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00177 if (w2 != NULL) {
00178 if (w2->nested_array != NULL) {
00179 NWidgetCore *nwi2 = w2->GetWidget<NWidgetCore>(this->parent_button);
00180 if (nwi2->type == NWID_BUTTON_DROPDOWN) {
00181 nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
00182 } else {
00183 w2->RaiseWidget(this->parent_button);
00184 }
00185 } else {
00186 w2->RaiseWidget(this->parent_button);
00187 }
00188 w2->SetWidgetDirty(this->parent_button);
00189 }
00190
00191 DeleteDropDownList(this->list);
00192 }
00193
00194 virtual Point OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
00195 {
00196 return this->position;
00197 }
00198
00204 bool GetDropDownItem(int &value)
00205 {
00206 if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return false;
00207
00208 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(DDM_ITEMS);
00209 int y = _cursor.pos.y - this->top - nwi->pos_y - 2;
00210 int width = nwi->current_x - 4;
00211 int pos = this->vscroll->GetPosition();
00212
00213 const DropDownList *list = this->list;
00214
00215 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00216
00217 if (--pos >= 0) continue;
00218
00219 const DropDownListItem *item = *it;
00220 int item_height = item->Height(width);
00221
00222 if (y < item_height) {
00223 if (item->masked || !item->Selectable()) return false;
00224 value = item->result;
00225 return true;
00226 }
00227
00228 y -= item_height;
00229 }
00230
00231 return false;
00232 }
00233
00234 virtual void DrawWidget(const Rect &r, int widget) const
00235 {
00236 if (widget != DDM_ITEMS) return;
00237
00238 TextColour colour = (TextColour)this->GetWidget<NWidgetCore>(widget)->colour;
00239
00240 int y = r.top + 2;
00241 int pos = this->vscroll->GetPosition();
00242 for (DropDownList::const_iterator it = this->list->begin(); it != this->list->end(); ++it) {
00243 const DropDownListItem *item = *it;
00244 int item_height = item->Height(r.right - r.left + 1);
00245
00246
00247 if (--pos >= 0) continue;
00248
00249 if (y + item_height < r.bottom) {
00250 bool selected = (this->selected_index == item->result);
00251 if (selected) GfxFillRect(r.left + 2, y, r.right - 1, y + item_height - 1, PC_BLACK);
00252
00253 item->Draw(r.left, r.right, y, y + item_height, selected, colour);
00254
00255 if (item->masked) {
00256 GfxFillRect(r.left + 1, y, r.right - 1, y + item_height - 1, _colour_gradient[colour][5], FILLRECT_CHECKER);
00257 }
00258 }
00259 y += item_height;
00260 }
00261 }
00262
00263 virtual void OnClick(Point pt, int widget, int click_count)
00264 {
00265 if (widget != DDM_ITEMS) return;
00266 int item;
00267 if (this->GetDropDownItem(item)) {
00268 this->click_delay = 4;
00269 this->selected_index = item;
00270 this->SetDirty();
00271 }
00272 }
00273
00274 virtual void OnTick()
00275 {
00276 if (this->scrolling != 0) {
00277 int pos = this->vscroll->GetPosition();
00278
00279 this->vscroll->UpdatePosition(this->scrolling);
00280 this->scrolling = 0;
00281
00282 if (pos != this->vscroll->GetPosition()) {
00283 this->SetDirty();
00284 }
00285 }
00286 }
00287
00288 virtual void OnMouseLoop()
00289 {
00290 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00291 if (w2 == NULL) {
00292 delete this;
00293 return;
00294 }
00295
00296 if (this->click_delay != 0 && --this->click_delay == 0) {
00297
00298
00299 this->window_class = WC_INVALID;
00300 this->SetDirty();
00301
00302 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00303 delete this;
00304 return;
00305 }
00306
00307 if (this->drag_mode) {
00308 int item;
00309
00310 if (!_left_button_clicked) {
00311 this->drag_mode = false;
00312 if (!this->GetDropDownItem(item)) {
00313 if (this->instant_close) {
00314
00315
00316 this->window_class = WC_INVALID;
00317 this->SetDirty();
00318
00319 if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == this->parent_button) {
00320
00321
00322 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00323 }
00324 delete this;
00325 }
00326 return;
00327 }
00328 this->click_delay = 2;
00329 } else {
00330 if (_cursor.pos.y <= this->top + 2) {
00331
00332 this->scrolling = -1;
00333 return;
00334 } else if (_cursor.pos.y >= this->top + this->height - 2) {
00335
00336 this->scrolling = 1;
00337 return;
00338 }
00339
00340 if (!this->GetDropDownItem(item)) return;
00341 }
00342
00343 if (this->selected_index != item) {
00344 this->selected_index = item;
00345 this->SetDirty();
00346 }
00347 }
00348 }
00349 };
00350
00351 void ShowDropDownList(Window *w, DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close)
00352 {
00353 DeleteWindowById(WC_DROPDOWN_MENU, 0);
00354
00355
00356
00357 Rect wi_rect;
00358 Colours wi_colour;
00359 NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button);
00360 wi_rect.left = nwi->pos_x;
00361 wi_rect.right = nwi->pos_x + nwi->current_x - 1;
00362 wi_rect.top = nwi->pos_y;
00363 wi_rect.bottom = nwi->pos_y + nwi->current_y - 1;
00364 wi_colour = nwi->colour;
00365
00366 if (nwi->type == NWID_BUTTON_DROPDOWN) {
00367 nwi->disp_flags |= ND_DROPDOWN_ACTIVE;
00368 } else {
00369 w->LowerWidget(button);
00370 }
00371 w->SetWidgetDirty(button);
00372
00373
00374 int top = w->top + wi_rect.bottom + 1;
00375
00376 if (width == 0) width = wi_rect.right - wi_rect.left + 1;
00377
00378 uint max_item_width = 0;
00379
00380 if (auto_width) {
00381
00382 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00383 const DropDownListItem *item = *it;
00384 max_item_width = max(max_item_width, item->Width() + 5);
00385 }
00386 }
00387
00388
00389 int list_height = 0;
00390
00391 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00392 DropDownListItem *item = *it;
00393 list_height += item->Height(width);
00394 }
00395
00396
00397 int height = list_height;
00398
00399
00400 int screen_bottom = GetMainViewBottom();
00401 bool scroll = false;
00402
00403
00404 if (top + height + 4 >= screen_bottom) {
00405
00406 if (w->top + wi_rect.top - height > GetMainViewTop()) {
00407 top = w->top + wi_rect.top - height - 4;
00408 } else {
00409
00410
00411 int avg_height = list_height / (int)list->size();
00412 int rows = (screen_bottom - 4 - top) / avg_height;
00413 height = rows * avg_height;
00414 scroll = true;
00415
00416
00417 max_item_width += NWidgetScrollbar::GetVerticalDimension().width;
00418 }
00419 }
00420
00421 if (auto_width) width = max(width, max_item_width);
00422
00423 Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - width : wi_rect.left), top};
00424 Dimension dw_size = {width, height};
00425 new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll);
00426 }
00427
00439 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width)
00440 {
00441 DropDownList *list = new DropDownList();
00442
00443 for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
00444 if (!HasBit(hidden_mask, i)) {
00445 list->push_back(new DropDownListStringItem(strings[i], i, HasBit(disabled_mask, i)));
00446 }
00447 }
00448
00449
00450 if (list->size() == 0) {
00451 DeleteDropDownList(list);
00452 return;
00453 }
00454
00455 ShowDropDownList(w, list, selected, button, width);
00456 }
00457
00463 int HideDropDownMenu(Window *pw)
00464 {
00465 Window *w;
00466 FOR_ALL_WINDOWS_FROM_BACK(w) {
00467 if (w->window_class != WC_DROPDOWN_MENU) continue;
00468
00469 DropdownWindow *dw = dynamic_cast<DropdownWindow*>(w);
00470 if (pw->window_class == dw->parent_wnd_class &&
00471 pw->window_number == dw->parent_wnd_num) {
00472 int parent_button = dw->parent_button;
00473 delete dw;
00474 return parent_button;
00475 }
00476 }
00477
00478 return -1;
00479 }
00480