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