dropdown.cpp

Go to the documentation of this file.
00001 /* $Id: dropdown.cpp 14168 2008-08-25 16:07:10Z peter1138 $ */
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       /* Skip items that are scrolled up */
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       /* Skip items that are scrolled up */
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               /* Send event for selected option if we're still
00204                * on the parent button of the list. */
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           /* Cursor is above the list, set scroll up */
00215           this->scrolling = -1;
00216           return;
00217         } else if (_cursor.pos.y >= this->top + this->height - 2) {
00218           /* Cursor is below list, set scroll down */
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   /* Our parent's button widget is used to determine where to place the drop
00247    * down list window. */
00248   const Widget *wi = &w->widget[button];
00249 
00250   /* The preferred position is just below the dropdown calling widget */
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     /* Find the longest item in the list */
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   /* Total length of list */
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   /* Height of window visible */
00274   int height = list_height;
00275 
00276   /* Check if the status bar is visible, as we don't want to draw over it */
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   /* Check if the dropdown will fully fit below the widget */
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     /* If not, check if it will fit above the widget */
00288     if (w->top + wi->top - height > screen_top) {
00289       top = w->top + wi->top - height - 4;
00290     } else {
00291       /* ... and lastly if it won't, enable the scroll bar and fit the
00292        * list in below the widget */
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       /* Add space for the scroll bar if we automatically determined
00298        * the width of the list. */
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     /* We're scrolling, so enable the scroll bar and shrink the list by
00320      * the scrollbar's width */
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     /* Capacity is the average number of items visible */
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   /* Don't create a new list if we're just closing an existing menu */
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   /* No entries in the list? */
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 

Generated on Fri Nov 21 19:01:38 2008 for openttd by  doxygen 1.5.6