widget.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "company_func.h"
00014 #include "window_gui.h"
00015 #include "viewport_func.h"
00016 #include "zoom_func.h"
00017 #include "strings_func.h"
00018 #include "transparency.h"
00019 #include "core/geometry_func.hpp"
00020 #include "settings_type.h"
00021 #include "querystring_gui.h"
00022 
00023 #include "table/sprites.h"
00024 #include "table/strings.h"
00025 #include "table/palettes.h"
00026 
00027 static const char *UPARROW   = "\xEE\x8A\xA0"; 
00028 static const char *DOWNARROW = "\xEE\x8A\xAA"; 
00029 
00039 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom, bool horizontal)
00040 {
00041   /* Base for reversion */
00042   int rev_base = top + bottom;
00043   int button_size;
00044   if (horizontal) {
00045     button_size = NWidgetScrollbar::GetHorizontalDimension().width;
00046   } else {
00047     button_size = NWidgetScrollbar::GetVerticalDimension().height;
00048   }
00049   top += button_size;    // top    points to just below the up-button
00050   bottom -= button_size; // bottom points to top of the down-button
00051 
00052   int height = (bottom - top);
00053   int pos = sb->GetPosition();
00054   int count = sb->GetCount();
00055   int cap = sb->GetCapacity();
00056 
00057   if (count != 0) top += height * pos / count;
00058 
00059   if (cap > count) cap = count;
00060   if (count != 0) bottom -= (count - pos - cap) * height / count;
00061 
00062   Point pt;
00063   if (horizontal && _current_text_dir == TD_RTL) {
00064     pt.x = rev_base - bottom;
00065     pt.y = rev_base - top;
00066   } else {
00067     pt.x = top;
00068     pt.y = bottom;
00069   }
00070   return pt;
00071 }
00072 
00082 static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
00083 {
00084   int pos;
00085   int button_size;
00086   bool rtl = false;
00087 
00088   if (sb->type == NWID_HSCROLLBAR) {
00089     pos = x;
00090     rtl = _current_text_dir == TD_RTL;
00091     button_size = NWidgetScrollbar::GetHorizontalDimension().width;
00092   } else {
00093     pos = y;
00094     button_size = NWidgetScrollbar::GetVerticalDimension().height;
00095   }
00096   if (pos < mi + button_size) {
00097     /* Pressing the upper button? */
00098     SetBit(sb->disp_flags, NDB_SCROLLBAR_UP);
00099     if (_scroller_click_timeout <= 1) {
00100       _scroller_click_timeout = 3;
00101       sb->UpdatePosition(rtl ? 1 : -1);
00102     }
00103     w->scrolling_scrollbar = sb->index;
00104   } else if (pos >= ma - button_size) {
00105     /* Pressing the lower button? */
00106     SetBit(sb->disp_flags, NDB_SCROLLBAR_DOWN);
00107 
00108     if (_scroller_click_timeout <= 1) {
00109       _scroller_click_timeout = 3;
00110       sb->UpdatePosition(rtl ? -1 : 1);
00111     }
00112     w->scrolling_scrollbar = sb->index;
00113   } else {
00114     Point pt = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR);
00115 
00116     if (pos < pt.x) {
00117       sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::SS_BIG);
00118     } else if (pos > pt.y) {
00119       sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG);
00120     } else {
00121       _scrollbar_start_pos = pt.x - mi - button_size;
00122       _scrollbar_size = ma - mi - button_size * 2;
00123       w->scrolling_scrollbar = sb->index;
00124       _cursorpos_drag_start = _cursor.pos;
00125     }
00126   }
00127 
00128   w->SetDirty();
00129 }
00130 
00139 void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
00140 {
00141   int mi, ma;
00142 
00143   if (nw->type == NWID_HSCROLLBAR) {
00144     mi = nw->pos_x;
00145     ma = nw->pos_x + nw->current_x;
00146   } else {
00147     mi = nw->pos_y;
00148     ma = nw->pos_y + nw->current_y;
00149   }
00150   ScrollbarClickPositioning(w, dynamic_cast<NWidgetScrollbar*>(nw), x, y, mi, ma);
00151 }
00152 
00161 int GetWidgetFromPos(const Window *w, int x, int y)
00162 {
00163   NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00164   return (nw != NULL) ? nw->index : -1;
00165 }
00166 
00176 void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
00177 {
00178   uint dark         = _colour_gradient[colour][3];
00179   uint medium_dark  = _colour_gradient[colour][5];
00180   uint medium_light = _colour_gradient[colour][6];
00181   uint light        = _colour_gradient[colour][7];
00182 
00183   if (flags & FR_TRANSPARENT) {
00184     GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
00185   } else {
00186     uint interior;
00187 
00188     if (flags & FR_LOWERED) {
00189       GfxFillRect(left,                 top,                left,                   bottom,                   dark);
00190       GfxFillRect(left + WD_BEVEL_LEFT, top,                right,                  top,                      dark);
00191       GfxFillRect(right,                top + WD_BEVEL_TOP, right,                  bottom - WD_BEVEL_BOTTOM, light);
00192       GfxFillRect(left + WD_BEVEL_LEFT, bottom,             right,                  bottom,                   light);
00193       interior = (flags & FR_DARKENED ? medium_dark : medium_light);
00194     } else {
00195       GfxFillRect(left,                 top,                left,                   bottom - WD_BEVEL_BOTTOM, light);
00196       GfxFillRect(left + WD_BEVEL_LEFT, top,                right - WD_BEVEL_RIGHT, top,                      light);
00197       GfxFillRect(right,                top,                right,                  bottom - WD_BEVEL_BOTTOM, dark);
00198       GfxFillRect(left,                 bottom,             right,                  bottom,                   dark);
00199       interior = medium_dark;
00200     }
00201     if (!(flags & FR_BORDERONLY)) {
00202       GfxFillRect(left + WD_BEVEL_LEFT, top + WD_BEVEL_TOP, right - WD_BEVEL_RIGHT, bottom - WD_BEVEL_BOTTOM, interior);
00203     }
00204   }
00205 }
00206 
00215 static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img)
00216 {
00217   assert(img != 0);
00218   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
00219 
00220   if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
00221   DrawSprite(img, PAL_NONE, r.left + WD_IMGBTN_LEFT + clicked, r.top + WD_IMGBTN_TOP + clicked);
00222 }
00223 
00231 static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, StringID str)
00232 {
00233   if (str == STR_NULL) return;
00234   if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
00235   Dimension d = GetStringBoundingBox(str);
00236   int offset = max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered
00237   DrawString(r.left + clicked, r.right + clicked, r.top + offset + clicked, str, TC_FROMSTRING, SA_HOR_CENTER);
00238 }
00239 
00246 static inline void DrawText(const Rect &r, TextColour colour, StringID str)
00247 {
00248   Dimension d = GetStringBoundingBox(str);
00249   int offset = max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered
00250   if (str != STR_NULL) DrawString(r.left, r.right, r.top + offset, str, colour);
00251 }
00252 
00259 static inline void DrawInset(const Rect &r, Colours colour, StringID str)
00260 {
00261   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED);
00262   if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str);
00263 }
00264 
00272 static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint16 data)
00273 {
00274   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
00275 
00276   int num_columns = GB(data, MAT_COL_START, MAT_COL_BITS);  // Lower 8 bits of the widget data: Number of columns in the matrix.
00277   int column_width = (r.right - r.left + 1) / num_columns; // Width of a single column in the matrix.
00278 
00279   int num_rows = GB(data, MAT_ROW_START, MAT_ROW_BITS); // Upper 8 bits of the widget data: Number of rows in the matrix.
00280   int row_height = (r.bottom - r.top + 1) / num_rows; // Height of a single row in the matrix.
00281 
00282   int col = _colour_gradient[colour & 0xF][6];
00283 
00284   int x = r.left;
00285   for (int ctr = num_columns; ctr > 1; ctr--) {
00286     x += column_width;
00287     GfxFillRect(x, r.top + 1, x, r.bottom - 1, col);
00288   }
00289 
00290   x = r.top;
00291   for (int ctr = num_rows; ctr > 1; ctr--) {
00292     x += row_height;
00293     GfxFillRect(r.left + 1, x, r.right - 1, x, col);
00294   }
00295 
00296   col = _colour_gradient[colour & 0xF][4];
00297 
00298   x = r.left - 1;
00299   for (int ctr = num_columns; ctr > 1; ctr--) {
00300     x += column_width;
00301     GfxFillRect(x, r.top + 1, x, r.bottom - 1, col);
00302   }
00303 
00304   x = r.top - 1;
00305   for (int ctr = num_rows; ctr > 1; ctr--) {
00306     x += row_height;
00307     GfxFillRect(r.left + 1, x, r.right - 1, x, col);
00308   }
00309 }
00310 
00320 static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
00321 {
00322   int centre = (r.right - r.left) / 2;
00323   int height = NWidgetScrollbar::GetVerticalDimension().height;
00324 
00325   /* draw up/down buttons */
00326   DrawFrameRect(r.left, r.top, r.right, r.top + height - 1, colour, (up_clicked) ? FR_LOWERED : FR_NONE);
00327   DrawString(r.left + up_clicked, r.right + up_clicked, r.top + up_clicked, UPARROW, TC_BLACK, SA_HOR_CENTER);
00328 
00329   DrawFrameRect(r.left, r.bottom - (height - 1), r.right, r.bottom, colour, (down_clicked) ? FR_LOWERED : FR_NONE);
00330   DrawString(r.left + down_clicked, r.right + down_clicked, r.bottom - (height - 1) + down_clicked, DOWNARROW, TC_BLACK, SA_HOR_CENTER);
00331 
00332   int c1 = _colour_gradient[colour & 0xF][3];
00333   int c2 = _colour_gradient[colour & 0xF][7];
00334 
00335   /* draw "shaded" background */
00336   GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
00337   GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c1, FILLRECT_CHECKER);
00338 
00339   /* draw shaded lines */
00340   GfxFillRect(r.left + centre - 3, r.top + height, r.left + centre - 3, r.bottom - height, c1);
00341   GfxFillRect(r.left + centre - 2, r.top + height, r.left + centre - 2, r.bottom - height, c2);
00342   GfxFillRect(r.left + centre + 2, r.top + height, r.left + centre + 2, r.bottom - height, c1);
00343   GfxFillRect(r.left + centre + 3, r.top + height, r.left + centre + 3, r.bottom - height, c2);
00344 
00345   Point pt = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
00346   DrawFrameRect(r.left, pt.x, r.right, pt.y, colour, bar_dragged ? FR_LOWERED : FR_NONE);
00347 }
00348 
00358 static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
00359 {
00360   int centre = (r.bottom - r.top) / 2;
00361   int width = NWidgetScrollbar::GetHorizontalDimension().width;
00362 
00363   DrawFrameRect(r.left, r.top, r.left + width - 1, r.bottom, colour, left_clicked ? FR_LOWERED : FR_NONE);
00364   DrawSprite(SPR_ARROW_LEFT, PAL_NONE, r.left + 1 + left_clicked, r.top + 1 + left_clicked);
00365 
00366   DrawFrameRect(r.right - (width - 1), r.top, r.right, r.bottom, colour, right_clicked ? FR_LOWERED : FR_NONE);
00367   DrawSprite(SPR_ARROW_RIGHT, PAL_NONE, r.right - (width - 2) + right_clicked, r.top + 1 + right_clicked);
00368 
00369   int c1 = _colour_gradient[colour & 0xF][3];
00370   int c2 = _colour_gradient[colour & 0xF][7];
00371 
00372   /* draw "shaded" background */
00373   GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
00374   GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c1, FILLRECT_CHECKER);
00375 
00376   /* draw shaded lines */
00377   GfxFillRect(r.left + width, r.top + centre - 3, r.right - width, r.top + centre - 3, c1);
00378   GfxFillRect(r.left + width, r.top + centre - 2, r.right - width, r.top + centre - 2, c2);
00379   GfxFillRect(r.left + width, r.top + centre + 2, r.right - width, r.top + centre + 2, c1);
00380   GfxFillRect(r.left + width, r.top + centre + 3, r.right - width, r.top + centre + 3, c2);
00381 
00382   /* draw actual scrollbar */
00383   Point pt = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
00384   DrawFrameRect(pt.x, r.top, pt.y, r.bottom, colour, bar_dragged ? FR_LOWERED : FR_NONE);
00385 }
00386 
00393 static inline void DrawFrame(const Rect &r, Colours colour, StringID str)
00394 {
00395   int x2 = r.left; // by default the left side is the left side of the widget
00396 
00397   if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str);
00398 
00399   int c1 = _colour_gradient[colour][3];
00400   int c2 = _colour_gradient[colour][7];
00401 
00402   /* If the frame has text, adjust the top bar to fit half-way through */
00403   int dy1 = 4;
00404   if (str != STR_NULL) dy1 = FONT_HEIGHT_NORMAL / 2 - 1;
00405   int dy2 = dy1 + 1;
00406 
00407   if (_current_text_dir == TD_LTR) {
00408     /* Line from upper left corner to start of text */
00409     GfxFillRect(r.left, r.top + dy1, r.left + 4, r.top + dy1, c1);
00410     GfxFillRect(r.left + 1, r.top + dy2, r.left + 4, r.top + dy2, c2);
00411 
00412     /* Line from end of text to upper right corner */
00413     GfxFillRect(x2, r.top + dy1, r.right - 1, r.top + dy1, c1);
00414     GfxFillRect(x2, r.top + dy2, r.right - 2, r.top + dy2, c2);
00415   } else {
00416     /* Line from upper left corner to start of text */
00417     GfxFillRect(r.left, r.top + dy1, x2 - 2, r.top + dy1, c1);
00418     GfxFillRect(r.left + 1, r.top + dy2, x2 - 2, r.top + dy2, c2);
00419 
00420     /* Line from end of text to upper right corner */
00421     GfxFillRect(r.right - 5, r.top + dy1, r.right - 1, r.top + dy1, c1);
00422     GfxFillRect(r.right - 5, r.top + dy2, r.right - 2, r.top + dy2, c2);
00423   }
00424 
00425   /* Line from upper left corner to bottom left corner */
00426   GfxFillRect(r.left, r.top + dy2, r.left, r.bottom - 1, c1);
00427   GfxFillRect(r.left + 1, r.top + dy2 + 1, r.left + 1, r.bottom - 2, c2);
00428 
00429   /* Line from upper right corner to bottom right corner */
00430   GfxFillRect(r.right - 1, r.top + dy2, r.right - 1, r.bottom - 2, c1);
00431   GfxFillRect(r.right, r.top + dy1, r.right, r.bottom - 1, c2);
00432 
00433   GfxFillRect(r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1, c1);
00434   GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2);
00435 }
00436 
00443 static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
00444 {
00445   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
00446   DrawSprite((clicked) ? SPR_WINDOW_SHADE : SPR_WINDOW_UNSHADE, PAL_NONE, r.left + WD_SHADEBOX_LEFT + clicked, r.top + WD_SHADEBOX_TOP + clicked);
00447 }
00448 
00455 static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
00456 {
00457   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
00458   DrawSprite((clicked) ? SPR_PIN_UP : SPR_PIN_DOWN, PAL_NONE, r.left + WD_STICKYBOX_LEFT + clicked, r.top + WD_STICKYBOX_TOP + clicked);
00459 }
00460 
00467 static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
00468 {
00469   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
00470   DrawSprite(SPR_WINDOW_DEBUG, PAL_NONE, r.left + WD_DEBUGBOX_LEFT + clicked, r.top + WD_DEBUGBOX_TOP + clicked);
00471 }
00472 
00480 static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked)
00481 {
00482   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
00483   if (at_left) {
00484     DrawSprite(SPR_WINDOW_RESIZE_LEFT, PAL_NONE, r.left + WD_RESIZEBOX_RIGHT + clicked,
00485          r.bottom - WD_RESIZEBOX_BOTTOM - GetSpriteSize(SPR_WINDOW_RESIZE_LEFT).height + clicked);
00486   } else {
00487     DrawSprite(SPR_WINDOW_RESIZE_RIGHT, PAL_NONE, r.left + WD_RESIZEBOX_LEFT + clicked,
00488          r.bottom - WD_RESIZEBOX_BOTTOM - GetSpriteSize(SPR_WINDOW_RESIZE_RIGHT).height + clicked);
00489   }
00490 }
00491 
00498 static inline void DrawCloseBox(const Rect &r, Colours colour, StringID str)
00499 {
00500   assert(str == STR_BLACK_CROSS || str == STR_SILVER_CROSS); // black or silver cross
00501   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_NONE);
00502   DrawString(r.left + WD_CLOSEBOX_LEFT, r.right - WD_CLOSEBOX_RIGHT, r.top + WD_CLOSEBOX_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
00503 }
00504 
00512 void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str)
00513 {
00514   DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_BORDERONLY);
00515   DrawFrameRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, colour, (owner == INVALID_OWNER) ? FR_LOWERED | FR_DARKENED : FR_LOWERED | FR_DARKENED | FR_BORDERONLY);
00516 
00517   if (owner != INVALID_OWNER) {
00518     GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, _colour_gradient[_company_colours[owner]][4]);
00519   }
00520 
00521   if (str != STR_NULL) {
00522     Dimension d = GetStringBoundingBox(str);
00523     int offset = max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered
00524     DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, r.top + offset, str, TC_FROMSTRING, SA_HOR_CENTER);
00525   }
00526 }
00527 
00538 static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str)
00539 {
00540   int text_offset = max(0, ((int)(r.bottom - r.top + 1) - FONT_HEIGHT_NORMAL) / 2); // Offset for rendering the text vertically centered
00541 
00542   if (_current_text_dir == TD_LTR) {
00543     DrawFrameRect(r.left, r.top, r.right - 12, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
00544     DrawFrameRect(r.right - 11, r.top, r.right, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE);
00545     DrawString(r.right - (clicked_dropdown ? 10 : 11), r.right, r.top + (clicked_dropdown ? 2 : 1), DOWNARROW, TC_BLACK, SA_HOR_CENTER);
00546     if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK);
00547   } else {
00548     DrawFrameRect(r.left + 12, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
00549     DrawFrameRect(r.left, r.top, r.left + 11, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE);
00550     DrawString(r.left + clicked_dropdown, r.left + 11, r.top + (clicked_dropdown ? 2 : 1), DOWNARROW, TC_BLACK, SA_HOR_CENTER);
00551     if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_RIGHT + clicked_button, r.right - WD_DROPDOWNTEXT_LEFT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK);
00552   }
00553 }
00554 
00562 static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str)
00563 {
00564   DrawButtonDropdown(r, colour, false, clicked, str);
00565 }
00566 
00570 void Window::DrawWidgets() const
00571 {
00572   this->nested_root->Draw(this);
00573 
00574   if (this->flags & WF_WHITE_BORDER) {
00575     DrawFrameRect(0, 0, this->width - 1, this->height - 1, COLOUR_WHITE, FR_BORDERONLY);
00576   }
00577 
00578   if (this->flags & WF_HIGHLIGHTED) {
00579     extern bool _window_highlight_colour;
00580     for (uint i = 0; i < this->nested_array_size; i++) {
00581       const NWidgetBase *widget = this->GetWidget<NWidgetBase>(i);
00582       if (widget == NULL || !widget->IsHighlighted()) continue;
00583 
00584       int left = widget->pos_x;
00585       int top  = widget->pos_y;
00586       int right  = left + widget->current_x - 1;
00587       int bottom = top  + widget->current_y - 1;
00588 
00589       int colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
00590 
00591       GfxFillRect(left,                 top,    left,                   bottom - WD_BEVEL_BOTTOM, colour);
00592       GfxFillRect(left + WD_BEVEL_LEFT, top,    right - WD_BEVEL_RIGHT, top,                      colour);
00593       GfxFillRect(right,                top,    right,                  bottom - WD_BEVEL_BOTTOM, colour);
00594       GfxFillRect(left,                 bottom, right,                  bottom,                   colour);
00595     }
00596   }
00597 }
00598 
00604 void Window::DrawSortButtonState(int widget, SortButtonState state) const
00605 {
00606   if (state == SBS_OFF) return;
00607 
00608   assert(this->nested_array != NULL);
00609   const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget);
00610 
00611   int offset = this->IsWidgetLowered(widget) ? 1 : 0;
00612   int base = offset + nwid->pos_x + (_current_text_dir == TD_LTR ? nwid->current_x - WD_SORTBUTTON_ARROW_WIDTH : 0);
00613   int top = nwid->pos_y;
00614 
00615   DrawString(base, base + WD_SORTBUTTON_ARROW_WIDTH, top + 1 + offset, state == SBS_DOWN ? DOWNARROW : UPARROW, TC_BLACK, SA_HOR_CENTER);
00616 }
00617 
00618 
00676 NWidgetBase::NWidgetBase(WidgetType tp) : ZeroedMemoryAllocator()
00677 {
00678   this->type = tp;
00679 }
00680 
00681 /* ~NWidgetContainer() takes care of #next and #prev data members. */
00682 
00730 void NWidgetBase::SetDirty(const Window *w) const
00731 {
00732   int abs_left = w->left + this->pos_x;
00733   int abs_top = w->top + this->pos_y;
00734   SetDirtyBlocks(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
00735 }
00736 
00750 NWidgetBase *NWidgetBase::GetWidgetOfType(WidgetType tp)
00751 {
00752   return (this->type == tp) ? this : NULL;
00753 }
00754 
00761 NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y) : NWidgetBase(tp)
00762 {
00763   this->fill_x = fill_x;
00764   this->fill_y = fill_y;
00765 }
00766 
00772 void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y)
00773 {
00774   this->min_x = min_x;
00775   this->min_y = min_y;
00776 }
00777 
00784 void NWidgetResizeBase::SetMinimalTextLines(uint8 min_lines, uint8 spacing, FontSize size)
00785 {
00786   this->min_y = min_lines * GetCharacterHeight(size) + spacing;
00787 }
00788 
00794 void NWidgetResizeBase::SetFill(uint fill_x, uint fill_y)
00795 {
00796   this->fill_x = fill_x;
00797   this->fill_y = fill_y;
00798 }
00799 
00805 void NWidgetResizeBase::SetResize(uint resize_x, uint resize_y)
00806 {
00807   this->resize_x = resize_x;
00808   this->resize_y = resize_y;
00809 }
00810 
00811 void NWidgetResizeBase::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
00812 {
00813   this->StoreSizePosition(sizing, x, y, given_width, given_height);
00814 }
00815 
00825 NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, uint fill_x, uint fill_y, uint16 widget_data, StringID tool_tip) : NWidgetResizeBase(tp, fill_x, fill_y)
00826 {
00827   this->colour = colour;
00828   this->index = -1;
00829   this->widget_data = widget_data;
00830   this->tool_tip = tool_tip;
00831   this->scrollbar_index = -1;
00832 }
00833 
00838 void NWidgetCore::SetIndex(int index)
00839 {
00840   assert(index >= 0);
00841   this->index = index;
00842 }
00843 
00849 void NWidgetCore::SetDataTip(uint16 widget_data, StringID tool_tip)
00850 {
00851   this->widget_data = widget_data;
00852   this->tool_tip = tool_tip;
00853 }
00854 
00855 void NWidgetCore::FillNestedArray(NWidgetBase **array, uint length)
00856 {
00857   if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this;
00858 }
00859 
00860 NWidgetCore *NWidgetCore::GetWidgetFromPos(int x, int y)
00861 {
00862   return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : NULL;
00863 }
00864 
00869 NWidgetContainer::NWidgetContainer(WidgetType tp) : NWidgetBase(tp)
00870 {
00871   this->head = NULL;
00872   this->tail = NULL;
00873 }
00874 
00875 NWidgetContainer::~NWidgetContainer()
00876 {
00877   while (this->head != NULL) {
00878     NWidgetBase *wid = this->head->next;
00879     delete this->head;
00880     this->head = wid;
00881   }
00882   this->tail = NULL;
00883 }
00884 
00885 NWidgetBase *NWidgetContainer::GetWidgetOfType(WidgetType tp)
00886 {
00887   if (this->type == tp) return this;
00888   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
00889     NWidgetBase *nwid = child_wid->GetWidgetOfType(tp);
00890     if (nwid != NULL) return nwid;
00891   }
00892   return NULL;
00893 }
00894 
00899 void NWidgetContainer::Add(NWidgetBase *wid)
00900 {
00901   assert(wid->next == NULL && wid->prev == NULL);
00902 
00903   if (this->head == NULL) {
00904     this->head = wid;
00905     this->tail = wid;
00906   } else {
00907     assert(this->tail != NULL);
00908     assert(this->tail->next == NULL);
00909 
00910     this->tail->next = wid;
00911     wid->prev = this->tail;
00912     this->tail = wid;
00913   }
00914 }
00915 
00916 void NWidgetContainer::FillNestedArray(NWidgetBase **array, uint length)
00917 {
00918   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
00919     child_wid->FillNestedArray(array, length);
00920   }
00921 }
00922 
00926 NWidgetStacked::NWidgetStacked() : NWidgetContainer(NWID_SELECTION)
00927 {
00928   this->index = -1;
00929 }
00930 
00931 void NWidgetStacked::SetIndex(int index)
00932 {
00933   this->index = index;
00934 }
00935 
00936 void NWidgetStacked::SetupSmallestSize(Window *w, bool init_array)
00937 {
00938   if (this->index >= 0 && init_array) { // Fill w->nested_array[]
00939     assert(w->nested_array_size > (uint)this->index);
00940     w->nested_array[this->index] = this;
00941   }
00942 
00943   /* Zero size plane selected */
00944   if (this->shown_plane >= SZSP_BEGIN) {
00945     Dimension size    = {0, 0};
00946     Dimension padding = {0, 0};
00947     Dimension fill    = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
00948     Dimension resize  = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
00949     /* Here we're primarily interested in the value of resize */
00950     if (this->index >= 0) w->UpdateWidgetSize(this->index, &size, padding, &fill, &resize);
00951 
00952     this->smallest_x = size.width;
00953     this->smallest_y = size.height;
00954     this->fill_x = fill.width;
00955     this->fill_y = fill.height;
00956     this->resize_x = resize.width;
00957     this->resize_y = resize.height;
00958     return;
00959   }
00960 
00961   /* First sweep, recurse down and compute minimal size and filling. */
00962   this->smallest_x = 0;
00963   this->smallest_y = 0;
00964   this->fill_x = (this->head != NULL) ? 1 : 0;
00965   this->fill_y = (this->head != NULL) ? 1 : 0;
00966   this->resize_x = (this->head != NULL) ? 1 : 0;
00967   this->resize_y = (this->head != NULL) ? 1 : 0;
00968   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
00969     child_wid->SetupSmallestSize(w, init_array);
00970 
00971     this->smallest_x = max(this->smallest_x, child_wid->smallest_x + child_wid->padding_left + child_wid->padding_right);
00972     this->smallest_y = max(this->smallest_y, child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom);
00973     this->fill_x = LeastCommonMultiple(this->fill_x, child_wid->fill_x);
00974     this->fill_y = LeastCommonMultiple(this->fill_y, child_wid->fill_y);
00975     this->resize_x = LeastCommonMultiple(this->resize_x, child_wid->resize_x);
00976     this->resize_y = LeastCommonMultiple(this->resize_y, child_wid->resize_y);
00977   }
00978 }
00979 
00980 void NWidgetStacked::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
00981 {
00982   assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
00983   this->StoreSizePosition(sizing, x, y, given_width, given_height);
00984 
00985   if (this->shown_plane >= SZSP_BEGIN) return;
00986 
00987   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
00988     uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
00989     uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding_left - child_wid->padding_right, hor_step);
00990     uint child_pos_x = (rtl ? child_wid->padding_right : child_wid->padding_left);
00991 
00992     uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
00993     uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding_top - child_wid->padding_bottom, vert_step);
00994     uint child_pos_y = child_wid->padding_top;
00995 
00996     child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
00997   }
00998 }
00999 
01000 void NWidgetStacked::FillNestedArray(NWidgetBase **array, uint length)
01001 {
01002   if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this;
01003   NWidgetContainer::FillNestedArray(array, length);
01004 }
01005 
01006 void NWidgetStacked::Draw(const Window *w)
01007 {
01008   if (this->shown_plane >= SZSP_BEGIN) return;
01009 
01010   int plane = 0;
01011   for (NWidgetBase *child_wid = this->head; child_wid != NULL; plane++, child_wid = child_wid->next) {
01012     if (plane == this->shown_plane) {
01013       child_wid->Draw(w);
01014       return;
01015     }
01016   }
01017 
01018   NOT_REACHED();
01019 }
01020 
01021 NWidgetCore *NWidgetStacked::GetWidgetFromPos(int x, int y)
01022 {
01023   if (this->shown_plane >= SZSP_BEGIN) return NULL;
01024 
01025   if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01026   int plane = 0;
01027   for (NWidgetBase *child_wid = this->head; child_wid != NULL; plane++, child_wid = child_wid->next) {
01028     if (plane == this->shown_plane) {
01029       return child_wid->GetWidgetFromPos(x, y);
01030     }
01031   }
01032   return NULL;
01033 }
01034 
01039 void NWidgetStacked::SetDisplayedPlane(int plane)
01040 {
01041   this->shown_plane = plane;
01042 }
01043 
01044 NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags) : NWidgetContainer(tp)
01045 {
01046   this->flags = flags;
01047 }
01048 
01058 void NWidgetPIPContainer::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post)
01059 {
01060   this->pip_pre = pip_pre;
01061   this->pip_inter = pip_inter;
01062   this->pip_post = pip_post;
01063 }
01064 
01065 void NWidgetPIPContainer::Draw(const Window *w)
01066 {
01067   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01068     child_wid->Draw(w);
01069   }
01070 }
01071 
01072 NWidgetCore *NWidgetPIPContainer::GetWidgetFromPos(int x, int y)
01073 {
01074   if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01075 
01076   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01077     NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
01078     if (nwid != NULL) return nwid;
01079   }
01080   return NULL;
01081 }
01082 
01084 NWidgetHorizontal::NWidgetHorizontal(NWidContainerFlags flags) : NWidgetPIPContainer(NWID_HORIZONTAL, flags)
01085 {
01086 }
01087 
01088 void NWidgetHorizontal::SetupSmallestSize(Window *w, bool init_array)
01089 {
01090   this->smallest_x = 0; // Sum of minimal size of all childs.
01091   this->smallest_y = 0; // Biggest child.
01092   this->fill_x = 0;     // smallest non-zero child widget fill step.
01093   this->fill_y = 1;     // smallest common child fill step.
01094   this->resize_x = 0;   // smallest non-zero child widget resize step.
01095   this->resize_y = 1;   // smallest common child resize step.
01096 
01097   /* 1a. Forward call, collect biggest nested array index, and longest/widest child length. */
01098   uint longest = 0; // Longest child found.
01099   uint max_vert_fill = 0; // Biggest vertical fill step.
01100   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01101     child_wid->SetupSmallestSize(w, init_array);
01102     longest = max(longest, child_wid->smallest_x);
01103     max_vert_fill = max(max_vert_fill, child_wid->GetVerticalStepSize(ST_SMALLEST));
01104     this->smallest_y = max(this->smallest_y, child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom);
01105   }
01106   /* 1b. Make the container higher if needed to accomadate all childs nicely. */
01107   uint max_smallest = this->smallest_y + 3 * max_vert_fill; // Upper limit to computing smallest height.
01108   uint cur_height = this->smallest_y;
01109   for (;;) {
01110     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01111       uint step_size = child_wid->GetVerticalStepSize(ST_SMALLEST);
01112       uint child_height = child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom;
01113       if (step_size > 1 && child_height < cur_height) { // Small step sizes or already fitting childs are not interesting.
01114         uint remainder = (cur_height - child_height) % step_size;
01115         if (remainder > 0) { // Child did not fit entirely, widen the container.
01116           cur_height += step_size - remainder;
01117           assert(cur_height < max_smallest); // Safeguard against infinite height expansion.
01118           /* Remaining childs will adapt to the new cur_height, thus speeding up the computation. */
01119         }
01120       }
01121     }
01122     if (this->smallest_y == cur_height) break;
01123     this->smallest_y = cur_height; // Smallest height got changed, try again.
01124   }
01125   /* 2. For containers that must maintain equal width, extend child minimal size. */
01126   if (this->flags & NC_EQUALSIZE) {
01127     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01128       if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
01129     }
01130   }
01131   /* 3. Move PIP space to the childs, compute smallest, fill, and resize values of the container. */
01132   if (this->head != NULL) this->head->padding_left += this->pip_pre;
01133   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01134     if (child_wid->next != NULL) {
01135       child_wid->padding_right += this->pip_inter;
01136     } else {
01137       child_wid->padding_right += this->pip_post;
01138     }
01139 
01140     this->smallest_x += child_wid->smallest_x + child_wid->padding_left + child_wid->padding_right;
01141     if (child_wid->fill_x > 0) {
01142       if (this->fill_x == 0 || this->fill_x > child_wid->fill_x) this->fill_x = child_wid->fill_x;
01143     }
01144     this->fill_y = LeastCommonMultiple(this->fill_y, child_wid->fill_y);
01145 
01146     if (child_wid->resize_x > 0) {
01147       if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
01148     }
01149     this->resize_y = LeastCommonMultiple(this->resize_y, child_wid->resize_y);
01150   }
01151   /* We need to zero the PIP settings so we can re-initialize the tree. */
01152   this->pip_pre = this->pip_inter = this->pip_post = 0;
01153 }
01154 
01155 void NWidgetHorizontal::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01156 {
01157   assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
01158 
01159   /* Compute additional width given to us. */
01160   uint additional_length = given_width;
01161   if (sizing == ST_SMALLEST && (this->flags & NC_EQUALSIZE)) {
01162     /* For EQUALSIZE containers this does not sum to smallest_x during initialisation */
01163     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01164       additional_length -= child_wid->smallest_x + child_wid->padding_right + child_wid->padding_left;
01165     }
01166   } else {
01167     additional_length -= this->smallest_x;
01168   }
01169 
01170   this->StoreSizePosition(sizing, x, y, given_width, given_height);
01171 
01172   /* In principle, the additional horizontal space is distributed evenly over the available resizable childs. Due to step sizes, this may not always be feasible.
01173    * To make resizing work as good as possible, first childs with biggest step sizes are done. These may get less due to rounding down.
01174    * This additional space is then given to childs with smaller step sizes. This will give a good result when resize steps of each child is a multiple
01175    * of the child with the smallest non-zero stepsize.
01176    *
01177    * Since child sizes are computed out of order, positions cannot be calculated until all sizes are known. That means it is not possible to compute the child
01178    * size and position, and directly call child->AssignSizePosition() with the computed values.
01179    * Instead, computed child widths and heights are stored in child->current_x and child->current_y values. That is allowed, since this method overwrites those values
01180    * then we call the child.
01181    */
01182 
01183   /* First loop: Find biggest stepsize, find number of childs that want a piece of the pie, handle vertical size for all childs, handle horizontal size for non-resizing childs. */
01184   int num_changing_childs = 0; // Number of childs that can change size.
01185   uint biggest_stepsize = 0;
01186   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01187     uint hor_step = child_wid->GetHorizontalStepSize(sizing);
01188     if (hor_step > 0) {
01189       num_changing_childs++;
01190       biggest_stepsize = max(biggest_stepsize, hor_step);
01191     } else {
01192       child_wid->current_x = child_wid->smallest_x;
01193     }
01194 
01195     uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
01196     child_wid->current_y = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding_top - child_wid->padding_bottom, vert_step);
01197   }
01198 
01199   /* Second loop: Allocate the additional horizontal space over the resizing childs, starting with the biggest resize steps. */
01200   while (biggest_stepsize > 0) {
01201     uint next_biggest_stepsize = 0;
01202     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01203       uint hor_step = child_wid->GetHorizontalStepSize(sizing);
01204       if (hor_step > biggest_stepsize) continue; // Already done
01205       if (hor_step == biggest_stepsize) {
01206         uint increment = additional_length / num_changing_childs;
01207         num_changing_childs--;
01208         if (hor_step > 1) increment -= increment % hor_step;
01209         child_wid->current_x = child_wid->smallest_x + increment;
01210         additional_length -= increment;
01211         continue;
01212       }
01213       next_biggest_stepsize = max(next_biggest_stepsize, hor_step);
01214     }
01215     biggest_stepsize = next_biggest_stepsize;
01216   }
01217   assert(num_changing_childs == 0);
01218 
01219   /* Third loop: Compute position and call the child. */
01220   uint position = 0; // Place to put next child relative to origin of the container.
01221   NWidgetBase *child_wid = rtl ? this->tail : this->head;
01222   while (child_wid != NULL) {
01223     uint child_width = child_wid->current_x;
01224     uint child_x = x + position + (rtl ? child_wid->padding_right : child_wid->padding_left);
01225     uint child_y = y + child_wid->padding_top;
01226 
01227     child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
01228     position += child_width + child_wid->padding_right + child_wid->padding_left;
01229 
01230     child_wid = rtl ? child_wid->prev : child_wid->next;
01231   }
01232 }
01233 
01235 NWidgetHorizontalLTR::NWidgetHorizontalLTR(NWidContainerFlags flags) : NWidgetHorizontal(flags)
01236 {
01237   this->type = NWID_HORIZONTAL_LTR;
01238 }
01239 
01240 void NWidgetHorizontalLTR::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01241 {
01242   NWidgetHorizontal::AssignSizePosition(sizing, x, y, given_width, given_height, false);
01243 }
01244 
01246 NWidgetVertical::NWidgetVertical(NWidContainerFlags flags) : NWidgetPIPContainer(NWID_VERTICAL, flags)
01247 {
01248 }
01249 
01250 void NWidgetVertical::SetupSmallestSize(Window *w, bool init_array)
01251 {
01252   this->smallest_x = 0; // Biggest child.
01253   this->smallest_y = 0; // Sum of minimal size of all childs.
01254   this->fill_x = 1;     // smallest common child fill step.
01255   this->fill_y = 0;     // smallest non-zero child widget fill step.
01256   this->resize_x = 1;   // smallest common child resize step.
01257   this->resize_y = 0;   // smallest non-zero child widget resize step.
01258 
01259   /* 1a. Forward call, collect biggest nested array index, and longest/widest child length. */
01260   uint highest = 0; // Highest child found.
01261   uint max_hor_fill = 0; // Biggest horizontal fill step.
01262   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01263     child_wid->SetupSmallestSize(w, init_array);
01264     highest = max(highest, child_wid->smallest_y);
01265     max_hor_fill = max(max_hor_fill, child_wid->GetHorizontalStepSize(ST_SMALLEST));
01266     this->smallest_x = max(this->smallest_x, child_wid->smallest_x + child_wid->padding_left + child_wid->padding_right);
01267   }
01268   /* 1b. Make the container wider if needed to accomadate all childs nicely. */
01269   uint max_smallest = this->smallest_x + 3 * max_hor_fill; // Upper limit to computing smallest height.
01270   uint cur_width = this->smallest_x;
01271   for (;;) {
01272     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01273       uint step_size = child_wid->GetHorizontalStepSize(ST_SMALLEST);
01274       uint child_width = child_wid->smallest_x + child_wid->padding_left + child_wid->padding_right;
01275       if (step_size > 1 && child_width < cur_width) { // Small step sizes or already fitting childs are not interesting.
01276         uint remainder = (cur_width - child_width) % step_size;
01277         if (remainder > 0) { // Child did not fit entirely, widen the container.
01278           cur_width += step_size - remainder;
01279           assert(cur_width < max_smallest); // Safeguard against infinite width expansion.
01280           /* Remaining childs will adapt to the new cur_width, thus speeding up the computation. */
01281         }
01282       }
01283     }
01284     if (this->smallest_x == cur_width) break;
01285     this->smallest_x = cur_width; // Smallest width got changed, try again.
01286   }
01287   /* 2. For containers that must maintain equal width, extend child minimal size. */
01288   if (this->flags & NC_EQUALSIZE) {
01289     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01290       if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
01291     }
01292   }
01293   /* 3. Move PIP space to the childs, compute smallest, fill, and resize values of the container. */
01294   if (this->head != NULL) this->head->padding_top += this->pip_pre;
01295   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01296     if (child_wid->next != NULL) {
01297       child_wid->padding_bottom += this->pip_inter;
01298     } else {
01299       child_wid->padding_bottom += this->pip_post;
01300     }
01301 
01302     this->smallest_y += child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom;
01303     if (child_wid->fill_y > 0) {
01304       if (this->fill_y == 0 || this->fill_y > child_wid->fill_y) this->fill_y = child_wid->fill_y;
01305     }
01306     this->fill_x = LeastCommonMultiple(this->fill_x, child_wid->fill_x);
01307 
01308     if (child_wid->resize_y > 0) {
01309       if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
01310     }
01311     this->resize_x = LeastCommonMultiple(this->resize_x, child_wid->resize_x);
01312   }
01313   /* We need to zero the PIP settings so we can re-initialize the tree. */
01314   this->pip_pre = this->pip_inter = this->pip_post = 0;
01315 }
01316 
01317 void NWidgetVertical::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01318 {
01319   assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
01320 
01321   /* Compute additional height given to us. */
01322   uint additional_length = given_height;
01323   if (sizing == ST_SMALLEST && (this->flags & NC_EQUALSIZE)) {
01324     /* For EQUALSIZE containers this does not sum to smallest_y during initialisation */
01325     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01326       additional_length -= child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom;
01327     }
01328   } else {
01329     additional_length -= this->smallest_y;
01330   }
01331 
01332   this->StoreSizePosition(sizing, x, y, given_width, given_height);
01333 
01334   /* Like the horizontal container, the vertical container also distributes additional height evenly, starting with the childs with the biggest resize steps.
01335    * It also stores computed widths and heights into current_x and current_y values of the child.
01336    */
01337 
01338   /* First loop: Find biggest stepsize, find number of childs that want a piece of the pie, handle horizontal size for all childs, handle vertical size for non-resizing childs. */
01339   int num_changing_childs = 0; // Number of childs that can change size.
01340   uint biggest_stepsize = 0;
01341   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01342     uint vert_step = child_wid->GetVerticalStepSize(sizing);
01343     if (vert_step > 0) {
01344       num_changing_childs++;
01345       biggest_stepsize = max(biggest_stepsize, vert_step);
01346     } else {
01347       child_wid->current_y = child_wid->smallest_y;
01348     }
01349 
01350     uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
01351     child_wid->current_x = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding_left - child_wid->padding_right, hor_step);
01352   }
01353 
01354   /* Second loop: Allocate the additional vertical space over the resizing childs, starting with the biggest resize steps. */
01355   while (biggest_stepsize > 0) {
01356     uint next_biggest_stepsize = 0;
01357     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01358       uint vert_step = child_wid->GetVerticalStepSize(sizing);
01359       if (vert_step > biggest_stepsize) continue; // Already done
01360       if (vert_step == biggest_stepsize) {
01361         uint increment = additional_length / num_changing_childs;
01362         num_changing_childs--;
01363         if (vert_step > 1) increment -= increment % vert_step;
01364         child_wid->current_y = child_wid->smallest_y + increment;
01365         additional_length -= increment;
01366         continue;
01367       }
01368       next_biggest_stepsize = max(next_biggest_stepsize, vert_step);
01369     }
01370     biggest_stepsize = next_biggest_stepsize;
01371   }
01372   assert(num_changing_childs == 0);
01373 
01374   /* Third loop: Compute position and call the child. */
01375   uint position = 0; // Place to put next child relative to origin of the container.
01376   for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01377     uint child_x = x + (rtl ? child_wid->padding_right : child_wid->padding_left);
01378     uint child_height = child_wid->current_y;
01379 
01380     child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding_top, child_wid->current_x, child_height, rtl);
01381     position += child_height + child_wid->padding_top + child_wid->padding_bottom;
01382   }
01383 }
01384 
01390 NWidgetSpacer::NWidgetSpacer(int length, int height) : NWidgetResizeBase(NWID_SPACER, 0, 0)
01391 {
01392   this->SetMinimalSize(length, height);
01393   this->SetResize(0, 0);
01394 }
01395 
01396 void NWidgetSpacer::SetupSmallestSize(Window *w, bool init_array)
01397 {
01398   this->smallest_x = this->min_x;
01399   this->smallest_y = this->min_y;
01400 }
01401 
01402 void NWidgetSpacer::FillNestedArray(NWidgetBase **array, uint length)
01403 {
01404 }
01405 
01406 void NWidgetSpacer::Draw(const Window *w)
01407 {
01408   /* Spacer widget is never visible. */
01409 }
01410 
01411 void NWidgetSpacer::SetDirty(const Window *w) const
01412 {
01413   /* Spacer widget never need repainting. */
01414 }
01415 
01416 NWidgetCore *NWidgetSpacer::GetWidgetFromPos(int x, int y)
01417 {
01418   return NULL;
01419 }
01420 
01421 NWidgetMatrix::NWidgetMatrix() : NWidgetPIPContainer(NWID_MATRIX, NC_EQUALSIZE), index(-1), clicked(-1), count(-1)
01422 {
01423 }
01424 
01425 void NWidgetMatrix::SetIndex(int index)
01426 {
01427   this->index = index;
01428 }
01429 
01430 void NWidgetMatrix::SetColour(Colours colour)
01431 {
01432   this->colour = colour;
01433 }
01434 
01439 void NWidgetMatrix::SetClicked(int clicked)
01440 {
01441   this->clicked = clicked;
01442   if (this->clicked >= 0 && this->sb != NULL && this->widgets_x != 0) {
01443     int vpos = (this->clicked / this->widgets_x) * this->widget_h; // Vertical position of the top.
01444     /* Need to scroll down -> Scroll to the bottom.
01445      * However, last entry has no 'this->pip_inter' underneath, and we must stay below this->sb->GetCount() */
01446     if (this->sb->GetPosition() < vpos) vpos += this->widget_h - this->pip_inter - 1;
01447     this->sb->ScrollTowards(vpos);
01448   }
01449 }
01450 
01456 void NWidgetMatrix::SetCount(int count)
01457 {
01458   this->count = count;
01459 
01460   if (this->sb == NULL || this->widgets_x == 0) return;
01461 
01462   /* We need to get the number of pixels the matrix is high/wide.
01463    * So, determine the number of rows/columns based on the number of
01464    * columns/rows (one is constant/unscrollable).
01465    * Then multiply that by the height of a widget, and add the pre
01466    * and post spacing "offsets". */
01467   count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
01468   count *= (this->sb->IsVertical() ? this->head->smallest_y : this->head->smallest_x) + this->pip_inter;
01469   count += -this->pip_inter + this->pip_pre + this->pip_post; // We counted an inter too much in the multiplication above
01470   this->sb->SetCount(count);
01471   this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
01472   this->sb->SetStepSize(this->sb->IsVertical() ? this->widget_h  : this->widget_w);
01473 }
01474 
01479 void NWidgetMatrix::SetScrollbar(Scrollbar *sb)
01480 {
01481   this->sb = sb;
01482 }
01483 
01484 void NWidgetMatrix::SetupSmallestSize(Window *w, bool init_array)
01485 {
01486   assert(this->head != NULL);
01487   assert(this->head->next == NULL);
01488 
01489   if (this->index >= 0 && init_array) { // Fill w->nested_array[]
01490     assert(w->nested_array_size > (uint)this->index);
01491     w->nested_array[this->index] = this;
01492   }
01493 
01494   /* Reset the widget number. */
01495   SB(dynamic_cast<NWidgetCore *>(this->head)->index, 16, 16, 0);
01496   this->head->SetupSmallestSize(w, init_array);
01497 
01498   Dimension padding = {this->pip_pre + this->pip_post, this->pip_pre + this->pip_post};
01499   Dimension size    = {this->head->smallest_x + padding.width, this->head->smallest_y + padding.height};
01500   Dimension fill    = {0, 0};
01501   Dimension resize  = {this->pip_inter + this->head->smallest_x, this->pip_inter + this->head->smallest_y};
01502 
01503   if (this->index >= 0) w->UpdateWidgetSize(this->index, &size, padding, &fill, &resize);
01504 
01505   this->smallest_x = size.width;
01506   this->smallest_y = size.height;
01507   this->fill_x = fill.width;
01508   this->fill_y = fill.height;
01509   this->resize_x = resize.width;
01510   this->resize_y = resize.height;
01511 }
01512 
01513 void NWidgetMatrix::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01514 {
01515   assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
01516 
01517   this->pos_x = x;
01518   this->pos_y = y;
01519   this->current_x = given_width;
01520   this->current_y = given_height;
01521 
01522   /* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
01523   this->widget_w = this->head->smallest_x + this->pip_inter;
01524   this->widget_h = this->head->smallest_y + this->pip_inter;
01525 
01526   /* Account for the pip_inter is between widgets, so we need to account for that when
01527    * the division assumes pip_inter is used for all widgets. */
01528   this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
01529   this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
01530 
01531   /* When resizing, update the scrollbar's count. E.g. with a vertical
01532    * scrollbar becoming wider or narrower means the amount of rows in
01533    * the scrollbar becomes respectively smaller or higher. */
01534   this->SetCount(this->count);
01535 }
01536 
01537 void NWidgetMatrix::FillNestedArray(NWidgetBase **array, uint length)
01538 {
01539   if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this;
01540   NWidgetContainer::FillNestedArray(array, length);
01541 }
01542 
01543 NWidgetCore *NWidgetMatrix::GetWidgetFromPos(int x, int y)
01544 {
01545   /* Falls outside of the matrix widget. */
01546   if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01547 
01548   int start_x, start_y, base_offs_x, base_offs_y;
01549   this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
01550 
01551   /* Swap the x offset around for RTL, so it'll behave like LTR for RTL as well. */
01552   bool rtl = _current_text_dir == TD_RTL;
01553   if (rtl) base_offs_x -= (this->widgets_x - 1) * this->widget_w;
01554 
01555   int widget_col = (x - base_offs_x - (int)this->pip_pre - (int)this->pos_x) / this->widget_w;
01556   int widget_row = (y - base_offs_y - (int)this->pip_pre - (int)this->pos_y) / this->widget_h;
01557 
01558   if (widget_row * this->widgets_x + widget_col >= this->count) return NULL;
01559 
01560   NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->head);
01561   child->AssignSizePosition(ST_RESIZE,
01562       this->pos_x + this->pip_pre + widget_col * this->widget_w + base_offs_x,
01563       this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
01564       child->smallest_x, child->smallest_y, rtl);
01565 
01566   /* "Undo" the RTL swap here to get the right widget index. */
01567   if (rtl) widget_col = this->widgets_x - widget_col - 1;
01568   SB(child->index, 16, 16, (widget_row + start_y) * this->widgets_x + widget_col + start_x);
01569 
01570   return child->GetWidgetFromPos(x, y);
01571 }
01572 
01573 /* virtual */ void NWidgetMatrix::Draw(const Window *w)
01574 {
01575   /* Fill the background. */
01576   GfxFillRect(this->pos_x, this->pos_y, this->pos_x + this->current_x - 1, this->pos_y + this->current_y - 1, _colour_gradient[this->colour & 0xF][5]);
01577 
01578   /* Set up a clipping area for the previews. */
01579   DrawPixelInfo tmp_dpi;
01580   if (!FillDrawPixelInfo(&tmp_dpi, this->pos_x + this->pip_pre, this->pos_y + this->pip_pre, this->current_x - this->pip_pre - this->pip_post, this->current_y - this->pip_pre - this->pip_post)) return;
01581   DrawPixelInfo *old_dpi = _cur_dpi;
01582   _cur_dpi = &tmp_dpi;
01583 
01584   /* Get the appropriate offsets so we can draw the right widgets. */
01585   NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->head);
01586   bool rtl = _current_text_dir == TD_RTL;
01587   int start_x, start_y, base_offs_x, base_offs_y;
01588   this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
01589 
01590   int offs_y = base_offs_y;
01591   for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
01592     /* Are we within bounds? */
01593     if (offs_y + child->smallest_y <= 0) continue;
01594     if (offs_y >= (int)this->current_y) break;
01595 
01596     /* We've passed our amount of widgets. */
01597     if (y * this->widgets_x >= this->count) break;
01598 
01599     int offs_x = base_offs_x;
01600     for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
01601       /* Are we within bounds? */
01602       if (offs_x + child->smallest_x <= 0) continue;
01603       if (offs_x >= (int)this->current_x) continue;
01604 
01605       /* Do we have this many widgets? */
01606       int sub_wid = y * this->widgets_x + x;
01607       if (sub_wid >= this->count) break;
01608 
01609       child->AssignSizePosition(ST_RESIZE, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
01610       child->SetLowered(this->clicked == sub_wid);
01611       SB(child->index, 16, 16, sub_wid);
01612       child->Draw(w);
01613     }
01614   }
01615 
01616   /* Restore the clipping area. */
01617   _cur_dpi = old_dpi;
01618 }
01619 
01627 void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
01628 {
01629   base_offs_x = 0;
01630   base_offs_y = 0;
01631   start_x = 0;
01632   start_y = 0;
01633   if (this->sb != NULL) {
01634     if (this->sb->IsVertical()) {
01635       start_y = this->sb->GetPosition() / this->widget_h;
01636       base_offs_y = -this->sb->GetPosition() + start_y * this->widget_h;
01637       if (_current_text_dir == TD_RTL) base_offs_x = this->pip_pre + this->widget_w * (this->widgets_x - 1) - this->pip_inter;
01638     } else {
01639       start_x = this->sb->GetPosition() / this->widget_w;
01640       if (_current_text_dir == TD_RTL) {
01641         base_offs_x = this->sb->GetCapacity() + this->sb->GetPosition() - (start_x + 1) * this->widget_w + this->pip_inter - this->pip_post - this->pip_pre;
01642       } else {
01643         base_offs_x = -this->sb->GetPosition() + start_x * this->widget_w;
01644       }
01645     }
01646   }
01647 }
01648 
01658 NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, int index, NWidgetPIPContainer *child) : NWidgetCore(tp, colour, 1, 1, 0x0, STR_NULL)
01659 {
01660   assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
01661   if (index >= 0) this->SetIndex(index);
01662   this->child = child;
01663 }
01664 
01665 NWidgetBackground::~NWidgetBackground()
01666 {
01667   if (this->child != NULL) delete this->child;
01668 }
01669 
01677 void NWidgetBackground::Add(NWidgetBase *nwid)
01678 {
01679   if (this->child == NULL) {
01680     this->child = new NWidgetVertical();
01681   }
01682   this->child->Add(nwid);
01683 }
01684 
01695 void NWidgetBackground::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post)
01696 {
01697   if (this->child == NULL) {
01698     this->child = new NWidgetVertical();
01699   }
01700   this->child->SetPIP(pip_pre, pip_inter, pip_post);
01701 }
01702 
01703 void NWidgetBackground::SetupSmallestSize(Window *w, bool init_array)
01704 {
01705   if (init_array && this->index >= 0) {
01706     assert(w->nested_array_size > (uint)this->index);
01707     w->nested_array[this->index] = this;
01708   }
01709   if (this->child != NULL) {
01710     this->child->SetupSmallestSize(w, init_array);
01711 
01712     this->smallest_x = this->child->smallest_x;
01713     this->smallest_y = this->child->smallest_y;
01714     this->fill_x = this->child->fill_x;
01715     this->fill_y = this->child->fill_y;
01716     this->resize_x = this->child->resize_x;
01717     this->resize_y = this->child->resize_y;
01718 
01719     /* Account for the size of the frame's text if that exists */
01720     if (w != NULL && this->type == WWT_FRAME) {
01721       this->child->padding_left   = WD_FRAMETEXT_LEFT;
01722       this->child->padding_right  = WD_FRAMETEXT_RIGHT;
01723       this->child->padding_top    = max((int)WD_FRAMETEXT_TOP, this->widget_data != STR_NULL ? FONT_HEIGHT_NORMAL + WD_FRAMETEXT_TOP / 2 : 0);
01724       this->child->padding_bottom = WD_FRAMETEXT_BOTTOM;
01725 
01726       this->smallest_x += this->child->padding_left + this->child->padding_right;
01727       this->smallest_y += this->child->padding_top + this->child->padding_bottom;
01728 
01729       if (this->index >= 0) w->SetStringParameters(this->index);
01730       this->smallest_x = max(this->smallest_x, GetStringBoundingBox(this->widget_data).width + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT);
01731     }
01732   } else {
01733     Dimension d = {this->min_x, this->min_y};
01734     Dimension fill = {this->fill_x, this->fill_y};
01735     Dimension resize  = {this->resize_x, this->resize_y};
01736     if (w != NULL) { // A non-NULL window pointer acts as switch to turn dynamic widget size on.
01737       if (this->type == WWT_FRAME || this->type == WWT_INSET) {
01738         if (this->index >= 0) w->SetStringParameters(this->index);
01739         Dimension background = GetStringBoundingBox(this->widget_data);
01740         background.width += (this->type == WWT_FRAME) ? (WD_FRAMETEXT_LEFT + WD_FRAMERECT_RIGHT) : (WD_INSET_LEFT + WD_INSET_RIGHT);
01741         d = maxdim(d, background);
01742       }
01743       if (this->index >= 0) {
01744         static const Dimension padding = {0, 0};
01745         w->UpdateWidgetSize(this->index, &d, padding, &fill, &resize);
01746       }
01747     }
01748     this->smallest_x = d.width;
01749     this->smallest_y = d.height;
01750     this->fill_x = fill.width;
01751     this->fill_y = fill.height;
01752     this->resize_x = resize.width;
01753     this->resize_y = resize.height;
01754   }
01755 }
01756 
01757 void NWidgetBackground::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01758 {
01759   this->StoreSizePosition(sizing, x, y, given_width, given_height);
01760 
01761   if (this->child != NULL) {
01762     uint x_offset = (rtl ? this->child->padding_right : this->child->padding_left);
01763     uint width = given_width - this->child->padding_right - this->child->padding_left;
01764     uint height = given_height - this->child->padding_top - this->child->padding_bottom;
01765     this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding_top, width, height, rtl);
01766   }
01767 }
01768 
01769 void NWidgetBackground::FillNestedArray(NWidgetBase **array, uint length)
01770 {
01771   if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this;
01772   if (this->child != NULL) this->child->FillNestedArray(array, length);
01773 }
01774 
01775 void NWidgetBackground::Draw(const Window *w)
01776 {
01777   if (this->current_x == 0 || this->current_y == 0) return;
01778 
01779   Rect r;
01780   r.left = this->pos_x;
01781   r.right = this->pos_x + this->current_x - 1;
01782   r.top = this->pos_y;
01783   r.bottom = this->pos_y + this->current_y - 1;
01784 
01785   const DrawPixelInfo *dpi = _cur_dpi;
01786   if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
01787 
01788   switch (this->type) {
01789     case WWT_PANEL:
01790       assert(this->widget_data == 0);
01791       DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, this->IsLowered() ? FR_LOWERED : FR_NONE);
01792       break;
01793 
01794     case WWT_FRAME:
01795       if (this->index >= 0) w->SetStringParameters(this->index);
01796       DrawFrame(r, this->colour, this->widget_data);
01797       break;
01798 
01799     case WWT_INSET:
01800       if (this->index >= 0) w->SetStringParameters(this->index);
01801       DrawInset(r, this->colour, this->widget_data);
01802       break;
01803 
01804     default:
01805       NOT_REACHED();
01806   }
01807 
01808   if (this->index >= 0) w->DrawWidget(r, this->index);
01809   if (this->child != NULL) this->child->Draw(w);
01810 
01811   if (this->IsDisabled()) {
01812     GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER);
01813   }
01814 }
01815 
01816 NWidgetCore *NWidgetBackground::GetWidgetFromPos(int x, int y)
01817 {
01818   NWidgetCore *nwid = NULL;
01819   if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
01820     if (this->child != NULL) nwid = this->child->GetWidgetFromPos(x, y);
01821     if (nwid == NULL) nwid = this;
01822   }
01823   return nwid;
01824 }
01825 
01826 NWidgetBase *NWidgetBackground::GetWidgetOfType(WidgetType tp)
01827 {
01828   NWidgetBase *nwid = NULL;
01829   if (this->child != NULL) nwid = this->child->GetWidgetOfType(tp);
01830   if (nwid == NULL && this->type == tp) nwid = this;
01831   return nwid;
01832 }
01833 
01834 NWidgetViewport::NWidgetViewport(int index) : NWidgetCore(NWID_VIEWPORT, INVALID_COLOUR, 1, 1, 0x0, STR_NULL)
01835 {
01836   this->SetIndex(index);
01837 }
01838 
01839 void NWidgetViewport::SetupSmallestSize(Window *w, bool init_array)
01840 {
01841   if (init_array && this->index >= 0) {
01842     assert(w->nested_array_size > (uint)this->index);
01843     w->nested_array[this->index] = this;
01844   }
01845   this->smallest_x = this->min_x;
01846   this->smallest_y = this->min_y;
01847 }
01848 
01849 void NWidgetViewport::Draw(const Window *w)
01850 {
01851   if (this->disp_flags & ND_NO_TRANSPARENCY) {
01852     TransparencyOptionBits to_backup = _transparency_opt;
01853     _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_LOADING); // Disable all transparency, except textual stuff
01854     w->DrawViewport();
01855     _transparency_opt = to_backup;
01856   } else {
01857     w->DrawViewport();
01858   }
01859 
01860   /* Optionally shade the viewport. */
01861   if (this->disp_flags & (ND_SHADE_GREY | ND_SHADE_DIMMED)) {
01862     GfxFillRect(this->pos_x, this->pos_y, this->pos_x + this->current_x - 1, this->pos_y + this->current_y - 1,
01863         (this->disp_flags & ND_SHADE_DIMMED) ? PALETTE_TO_TRANSPARENT : PALETTE_NEWSPAPER, FILLRECT_RECOLOUR);
01864   }
01865 }
01866 
01873 void NWidgetViewport::InitializeViewport(Window *w, uint32 follow_flags, ZoomLevel zoom)
01874 {
01875   InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, follow_flags, zoom);
01876 }
01877 
01882 void NWidgetViewport::UpdateViewportCoordinates(Window *w)
01883 {
01884   ViewPort *vp = w->viewport;
01885   if (vp != NULL) {
01886     vp->left = w->left + this->pos_x;
01887     vp->top  = w->top + this->pos_y;
01888     vp->width  = this->current_x;
01889     vp->height = this->current_y;
01890 
01891     vp->virtual_width  = ScaleByZoom(vp->width, vp->zoom);
01892     vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
01893   }
01894 }
01895 
01905 int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding, int line_height) const
01906 {
01907   uint pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
01908   if (pos != INT_MAX) pos += this->GetPosition();
01909   return (pos >= this->GetCount()) ? INT_MAX : pos;
01910 }
01911 
01919 void Scrollbar::SetCapacityFromWidget(Window *w, int widget, int padding)
01920 {
01921   NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
01922   if (this->IsVertical()) {
01923     this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
01924   } else {
01925     this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
01926   }
01927 }
01928 
01935 NWidgetScrollbar::NWidgetScrollbar(WidgetType tp, Colours colour, int index) : NWidgetCore(tp, colour, 1, 1, 0x0, STR_NULL), Scrollbar(tp != NWID_HSCROLLBAR)
01936 {
01937   assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR);
01938   this->SetIndex(index);
01939 
01940   switch (this->type) {
01941     case NWID_HSCROLLBAR:
01942       this->SetMinimalSize(0, NWidgetScrollbar::GetHorizontalDimension().height);
01943       this->SetResize(1, 0);
01944       this->SetFill(1, 0);
01945       this->SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
01946       break;
01947 
01948     case NWID_VSCROLLBAR:
01949       this->SetMinimalSize(NWidgetScrollbar::GetVerticalDimension().width, 0);
01950       this->SetResize(0, 1);
01951       this->SetFill(0, 1);
01952       this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
01953       break;
01954 
01955     default: NOT_REACHED();
01956   }
01957 }
01958 
01959 void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array)
01960 {
01961   if (init_array && this->index >= 0) {
01962     assert(w->nested_array_size > (uint)this->index);
01963     w->nested_array[this->index] = this;
01964   }
01965   this->smallest_x = this->min_x;
01966   this->smallest_y = this->min_y;
01967 }
01968 
01969 void NWidgetScrollbar::Draw(const Window *w)
01970 {
01971   if (this->current_x == 0 || this->current_y == 0) return;
01972 
01973   Rect r;
01974   r.left = this->pos_x;
01975   r.right = this->pos_x + this->current_x - 1;
01976   r.top = this->pos_y;
01977   r.bottom = this->pos_y + this->current_y - 1;
01978 
01979   const DrawPixelInfo *dpi = _cur_dpi;
01980   if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
01981 
01982   bool up_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_UP);
01983   bool down_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_DOWN);
01984   bool middle_lowered = !(this->disp_flags & ND_SCROLLBAR_BTN) && w->scrolling_scrollbar == this->index;
01985 
01986   if (this->type == NWID_HSCROLLBAR) {
01987     DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
01988   } else {
01989     DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
01990   }
01991 
01992   if (this->IsDisabled()) {
01993     GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER);
01994   }
01995 }
01996 
01997 /* static */ void NWidgetScrollbar::InvalidateDimensionCache()
01998 {
01999   vertical_dimension.width   = vertical_dimension.height   = 0;
02000   horizontal_dimension.width = horizontal_dimension.height = 0;
02001 }
02002 
02003 /* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
02004 {
02005   static const Dimension extra = {WD_SCROLLBAR_LEFT + WD_SCROLLBAR_RIGHT, WD_SCROLLBAR_TOP + WD_SCROLLBAR_BOTTOM};
02006   if (vertical_dimension.width == 0) {
02007     vertical_dimension = maxdim(GetSpriteSize(SPR_ARROW_UP), GetSpriteSize(SPR_ARROW_DOWN));
02008     vertical_dimension.width += extra.width;
02009     vertical_dimension.height += extra.height;
02010   }
02011   return vertical_dimension;
02012 }
02013 
02014 /* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
02015 {
02016   static const Dimension extra = {WD_SCROLLBAR_LEFT + WD_SCROLLBAR_RIGHT, WD_SCROLLBAR_TOP + WD_SCROLLBAR_BOTTOM};
02017   if (horizontal_dimension.width == 0) {
02018     horizontal_dimension = maxdim(GetSpriteSize(SPR_ARROW_LEFT), GetSpriteSize(SPR_ARROW_RIGHT));
02019     horizontal_dimension.width += extra.width;
02020     horizontal_dimension.height += extra.height;
02021   }
02022   return horizontal_dimension;
02023 }
02024 
02025 Dimension NWidgetScrollbar::vertical_dimension = {0, 0};
02026 Dimension NWidgetScrollbar::horizontal_dimension = {0, 0};
02027 
02029 /* static */ void NWidgetLeaf::InvalidateDimensionCache()
02030 {
02031   shadebox_dimension.width  = shadebox_dimension.height  = 0;
02032   debugbox_dimension.width  = debugbox_dimension.height  = 0;
02033   stickybox_dimension.width = stickybox_dimension.height = 0;
02034   resizebox_dimension.width = resizebox_dimension.height = 0;
02035   closebox_dimension.width  = closebox_dimension.height  = 0;
02036 }
02037 
02038 Dimension NWidgetLeaf::shadebox_dimension  = {0, 0};
02039 Dimension NWidgetLeaf::debugbox_dimension  = {0, 0};
02040 Dimension NWidgetLeaf::stickybox_dimension = {0, 0};
02041 Dimension NWidgetLeaf::resizebox_dimension = {0, 0};
02042 Dimension NWidgetLeaf::closebox_dimension  = {0, 0};
02043 
02052 NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint16 data, StringID tip) : NWidgetCore(tp, colour, 1, 1, data, tip)
02053 {
02054   assert(index >= 0 || tp == WWT_LABEL || tp == WWT_TEXT || tp == WWT_CAPTION || tp == WWT_RESIZEBOX || tp == WWT_SHADEBOX || tp == WWT_DEBUGBOX || tp == WWT_STICKYBOX || tp == WWT_CLOSEBOX);
02055   if (index >= 0) this->SetIndex(index);
02056   this->SetMinimalSize(0, 0);
02057   this->SetResize(0, 0);
02058 
02059   switch (tp) {
02060     case WWT_EMPTY:
02061       break;
02062 
02063     case WWT_PUSHBTN:
02064     case WWT_IMGBTN:
02065     case WWT_PUSHIMGBTN:
02066     case WWT_IMGBTN_2:
02067     case WWT_TEXTBTN:
02068     case WWT_PUSHTXTBTN:
02069     case WWT_TEXTBTN_2:
02070     case WWT_LABEL:
02071     case WWT_TEXT:
02072     case WWT_MATRIX:
02073     case NWID_BUTTON_DROPDOWN:
02074     case NWID_PUSHBUTTON_DROPDOWN:
02075     case WWT_ARROWBTN:
02076     case WWT_PUSHARROWBTN:
02077       this->SetFill(0, 0);
02078       break;
02079 
02080     case WWT_EDITBOX:
02081       this->SetMinimalSize(10, 0);
02082       this->SetFill(0, 0);
02083       break;
02084 
02085     case WWT_CAPTION:
02086       this->SetFill(1, 0);
02087       this->SetResize(1, 0);
02088       this->min_y = WD_CAPTION_HEIGHT;
02089       this->SetDataTip(data, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
02090       break;
02091 
02092     case WWT_STICKYBOX:
02093       this->SetFill(0, 0);
02094       this->SetMinimalSize(WD_STICKYBOX_WIDTH, WD_CAPTION_HEIGHT);
02095       this->SetDataTip(STR_NULL, STR_TOOLTIP_STICKY);
02096       break;
02097 
02098     case WWT_SHADEBOX:
02099       this->SetFill(0, 0);
02100       this->SetMinimalSize(WD_SHADEBOX_TOP, WD_CAPTION_HEIGHT);
02101       this->SetDataTip(STR_NULL, STR_TOOLTIP_SHADE);
02102       break;
02103 
02104     case WWT_DEBUGBOX:
02105       this->SetFill(0, 0);
02106       this->SetMinimalSize(WD_DEBUGBOX_TOP, WD_CAPTION_HEIGHT);
02107       this->SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG);
02108       break;
02109 
02110     case WWT_RESIZEBOX:
02111       this->SetFill(0, 0);
02112       this->SetMinimalSize(WD_RESIZEBOX_WIDTH, 12);
02113       this->SetDataTip(STR_NULL, STR_TOOLTIP_RESIZE);
02114       break;
02115 
02116     case WWT_CLOSEBOX:
02117       this->SetFill(0, 0);
02118       this->SetMinimalSize(WD_CLOSEBOX_WIDTH, WD_CAPTION_HEIGHT);
02119       this->SetDataTip(STR_BLACK_CROSS, STR_TOOLTIP_CLOSE_WINDOW);
02120       break;
02121 
02122     case WWT_DROPDOWN:
02123       this->SetFill(0, 0);
02124       this->min_y = WD_DROPDOWN_HEIGHT;
02125       break;
02126 
02127     default:
02128       NOT_REACHED();
02129   }
02130 }
02131 
02132 void NWidgetLeaf::SetupSmallestSize(Window *w, bool init_array)
02133 {
02134   if (this->index >= 0 && init_array) { // Fill w->nested_array[]
02135     assert(w->nested_array_size > (uint)this->index);
02136     w->nested_array[this->index] = this;
02137   }
02138 
02139   Dimension size = {this->min_x, this->min_y};
02140   Dimension fill = {this->fill_x, this->fill_y};
02141   Dimension resize = {this->resize_x, this->resize_y};
02142   /* Get padding, and update size with the real content size if appropriate. */
02143   const Dimension *padding = NULL;
02144   switch (this->type) {
02145     case WWT_EMPTY: {
02146       static const Dimension extra = {0, 0};
02147       padding = &extra;
02148       break;
02149     }
02150     case WWT_MATRIX: {
02151       static const Dimension extra = {WD_MATRIX_LEFT + WD_MATRIX_RIGHT, WD_MATRIX_TOP + WD_MATRIX_BOTTOM};
02152       padding = &extra;
02153       break;
02154     }
02155     case WWT_SHADEBOX: {
02156       static const Dimension extra = {WD_SHADEBOX_LEFT + WD_SHADEBOX_RIGHT, WD_SHADEBOX_TOP + WD_SHADEBOX_BOTTOM};
02157       padding = &extra;
02158       if (NWidgetLeaf::shadebox_dimension.width == 0) {
02159         NWidgetLeaf::shadebox_dimension = maxdim(GetSpriteSize(SPR_WINDOW_SHADE), GetSpriteSize(SPR_WINDOW_UNSHADE));
02160         NWidgetLeaf::shadebox_dimension.width += extra.width;
02161         NWidgetLeaf::shadebox_dimension.height += extra.height;
02162       }
02163       size = maxdim(size, NWidgetLeaf::shadebox_dimension);
02164       break;
02165     }
02166     case WWT_DEBUGBOX:
02167       if (_settings_client.gui.newgrf_developer_tools && w->IsNewGRFInspectable()) {
02168         static const Dimension extra = {WD_DEBUGBOX_LEFT + WD_DEBUGBOX_RIGHT, WD_DEBUGBOX_TOP + WD_DEBUGBOX_BOTTOM};
02169         padding = &extra;
02170         if (NWidgetLeaf::debugbox_dimension.width == 0) {
02171           NWidgetLeaf::debugbox_dimension = GetSpriteSize(SPR_WINDOW_DEBUG);
02172           NWidgetLeaf::debugbox_dimension.width += extra.width;
02173           NWidgetLeaf::debugbox_dimension.height += extra.height;
02174         }
02175         size = maxdim(size, NWidgetLeaf::debugbox_dimension);
02176       } else {
02177         /* If the setting is disabled we don't want to see it! */
02178         size.width = 0;
02179         fill.width = 0;
02180         resize.width = 0;
02181       }
02182       break;
02183 
02184     case WWT_STICKYBOX: {
02185       static const Dimension extra = {WD_STICKYBOX_LEFT + WD_STICKYBOX_RIGHT, WD_STICKYBOX_TOP + WD_STICKYBOX_BOTTOM};
02186       padding = &extra;
02187       if (NWidgetLeaf::stickybox_dimension.width == 0) {
02188         NWidgetLeaf::stickybox_dimension = maxdim(GetSpriteSize(SPR_PIN_UP), GetSpriteSize(SPR_PIN_DOWN));
02189         NWidgetLeaf::stickybox_dimension.width += extra.width;
02190         NWidgetLeaf::stickybox_dimension.height += extra.height;
02191       }
02192       size = maxdim(size, NWidgetLeaf::stickybox_dimension);
02193       break;
02194     }
02195     case WWT_RESIZEBOX: {
02196       static const Dimension extra = {WD_RESIZEBOX_LEFT + WD_RESIZEBOX_RIGHT, WD_RESIZEBOX_TOP + WD_RESIZEBOX_BOTTOM};
02197       padding = &extra;
02198       if (NWidgetLeaf::resizebox_dimension.width == 0) {
02199         NWidgetLeaf::resizebox_dimension = maxdim(GetSpriteSize(SPR_WINDOW_RESIZE_LEFT), GetSpriteSize(SPR_WINDOW_RESIZE_RIGHT));
02200         NWidgetLeaf::resizebox_dimension.width += extra.width;
02201         NWidgetLeaf::resizebox_dimension.height += extra.height;
02202       }
02203       size = maxdim(size, NWidgetLeaf::resizebox_dimension);
02204       break;
02205     }
02206     case WWT_EDITBOX:
02207       size.height = max(size.height, GetStringBoundingBox("_").height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
02208       /* FALL THROUGH */
02209     case WWT_PUSHBTN: {
02210       static const Dimension extra = {WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM};
02211       padding = &extra;
02212       break;
02213     }
02214     case WWT_IMGBTN:
02215     case WWT_IMGBTN_2:
02216     case WWT_PUSHIMGBTN: {
02217       static const Dimension extra = {WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT,  WD_IMGBTN_TOP + WD_IMGBTN_BOTTOM};
02218       padding = &extra;
02219       Dimension d2 = GetSpriteSize(this->widget_data);
02220       if (this->type == WWT_IMGBTN_2) d2 = maxdim(d2, GetSpriteSize(this->widget_data + 1));
02221       d2.width += extra.width;
02222       d2.height += extra.height;
02223       size = maxdim(size, d2);
02224       break;
02225     }
02226     case WWT_ARROWBTN:
02227     case WWT_PUSHARROWBTN: {
02228       static const Dimension extra = {WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT,  WD_IMGBTN_TOP + WD_IMGBTN_BOTTOM};
02229       padding = &extra;
02230       Dimension d2 = maxdim(GetSpriteSize(SPR_ARROW_LEFT), GetSpriteSize(SPR_ARROW_RIGHT));
02231       d2.width += extra.width;
02232       d2.height += extra.height;
02233       size = maxdim(size, d2);
02234       break;
02235     }
02236 
02237     case WWT_CLOSEBOX: {
02238       static const Dimension extra = {WD_CLOSEBOX_LEFT + WD_CLOSEBOX_RIGHT, WD_CLOSEBOX_TOP + WD_CLOSEBOX_BOTTOM};
02239       padding = &extra;
02240       if (NWidgetLeaf::closebox_dimension.width == 0) {
02241         NWidgetLeaf::closebox_dimension = maxdim(GetStringBoundingBox(STR_BLACK_CROSS), GetStringBoundingBox(STR_SILVER_CROSS));
02242         NWidgetLeaf::closebox_dimension.width += extra.width;
02243         NWidgetLeaf::closebox_dimension.height += extra.height;
02244       }
02245       size = maxdim(size, NWidgetLeaf::closebox_dimension);
02246       break;
02247     }
02248     case WWT_TEXTBTN:
02249     case WWT_PUSHTXTBTN:
02250     case WWT_TEXTBTN_2: {
02251       static const Dimension extra = {WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT,  WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM};
02252       padding = &extra;
02253       if (this->index >= 0) w->SetStringParameters(this->index);
02254       Dimension d2 = GetStringBoundingBox(this->widget_data);
02255       d2.width += extra.width;
02256       d2.height += extra.height;
02257       size = maxdim(size, d2);
02258       break;
02259     }
02260     case WWT_LABEL:
02261     case WWT_TEXT: {
02262       static const Dimension extra = {0, 0};
02263       padding = &extra;
02264       if (this->index >= 0) w->SetStringParameters(this->index);
02265       size = maxdim(size, GetStringBoundingBox(this->widget_data));
02266       break;
02267     }
02268     case WWT_CAPTION: {
02269       static const Dimension extra = {WD_CAPTIONTEXT_LEFT + WD_CAPTIONTEXT_RIGHT, WD_CAPTIONTEXT_TOP + WD_CAPTIONTEXT_BOTTOM};
02270       padding = &extra;
02271       if (this->index >= 0) w->SetStringParameters(this->index);
02272       Dimension d2 = GetStringBoundingBox(this->widget_data);
02273       d2.width += extra.width;
02274       d2.height += extra.height;
02275       size = maxdim(size, d2);
02276       break;
02277     }
02278     case WWT_DROPDOWN:
02279     case NWID_BUTTON_DROPDOWN:
02280     case NWID_PUSHBUTTON_DROPDOWN: {
02281       static const Dimension extra = {WD_DROPDOWNTEXT_LEFT + WD_DROPDOWNTEXT_RIGHT, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM};
02282       padding = &extra;
02283       if (this->index >= 0) w->SetStringParameters(this->index);
02284       Dimension d2 = GetStringBoundingBox(this->widget_data);
02285       d2.width += extra.width;
02286       d2.height += extra.height;
02287       size = maxdim(size, d2);
02288       break;
02289     }
02290     default:
02291       NOT_REACHED();
02292   }
02293 
02294   if (this->index >= 0) w->UpdateWidgetSize(this->index, &size, *padding, &fill, &resize);
02295 
02296   this->smallest_x = size.width;
02297   this->smallest_y = size.height;
02298   this->fill_x = fill.width;
02299   this->fill_y = fill.height;
02300   this->resize_x = resize.width;
02301   this->resize_y = resize.height;
02302 }
02303 
02304 void NWidgetLeaf::Draw(const Window *w)
02305 {
02306   if (this->current_x == 0 || this->current_y == 0) return;
02307 
02308   Rect r;
02309   r.left = this->pos_x;
02310   r.right = this->pos_x + this->current_x - 1;
02311   r.top = this->pos_y;
02312   r.bottom = this->pos_y + this->current_y - 1;
02313 
02314   const DrawPixelInfo *dpi = _cur_dpi;
02315   if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
02316 
02317   bool clicked = this->IsLowered();
02318   switch (this->type) {
02319     case WWT_EMPTY:
02320       break;
02321 
02322     case WWT_PUSHBTN:
02323       assert(this->widget_data == 0);
02324       DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE);
02325       break;
02326 
02327     case WWT_IMGBTN:
02328     case WWT_PUSHIMGBTN:
02329     case WWT_IMGBTN_2:
02330       DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data);
02331       break;
02332 
02333     case WWT_TEXTBTN:
02334     case WWT_PUSHTXTBTN:
02335     case WWT_TEXTBTN_2:
02336       if (this->index >= 0) w->SetStringParameters(this->index);
02337       DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE);
02338       DrawLabel(r, this->type, clicked, this->widget_data);
02339       break;
02340 
02341     case WWT_ARROWBTN:
02342     case WWT_PUSHARROWBTN: {
02343       SpriteID sprite;
02344       switch (this->widget_data) {
02345         case AWV_DECREASE: sprite = _current_text_dir != TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
02346         case AWV_INCREASE: sprite = _current_text_dir == TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
02347         case AWV_LEFT:     sprite = SPR_ARROW_LEFT;  break;
02348         case AWV_RIGHT:    sprite = SPR_ARROW_RIGHT; break;
02349         default: NOT_REACHED();
02350       }
02351       DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite);
02352     }
02353 
02354     case WWT_LABEL:
02355       if (this->index >= 0) w->SetStringParameters(this->index);
02356       DrawLabel(r, this->type, clicked, this->widget_data);
02357       break;
02358 
02359     case WWT_TEXT:
02360       if (this->index >= 0) w->SetStringParameters(this->index);
02361       DrawText(r, (TextColour)this->colour, this->widget_data);
02362       break;
02363 
02364     case WWT_MATRIX:
02365       DrawMatrix(r, this->colour, clicked, this->widget_data);
02366       break;
02367 
02368     case WWT_EDITBOX: {
02369       const QueryString *query = w->GetQueryString(this->index);
02370       if (query != NULL) query->DrawEditBox(w, this->index);
02371       break;
02372     }
02373 
02374     case WWT_CAPTION:
02375       if (this->index >= 0) w->SetStringParameters(this->index);
02376       DrawCaption(r, this->colour, w->owner, this->widget_data);
02377       break;
02378 
02379     case WWT_SHADEBOX:
02380       assert(this->widget_data == 0);
02381       DrawShadeBox(r, this->colour, w->IsShaded());
02382       break;
02383 
02384     case WWT_DEBUGBOX:
02385       DrawDebugBox(r, this->colour, clicked);
02386       break;
02387 
02388     case WWT_STICKYBOX:
02389       assert(this->widget_data == 0);
02390       DrawStickyBox(r, this->colour, !!(w->flags & WF_STICKY));
02391       break;
02392 
02393     case WWT_RESIZEBOX:
02394       assert(this->widget_data == 0);
02395       DrawResizeBox(r, this->colour, this->pos_x < (uint)(w->width / 2), !!(w->flags & WF_SIZING));
02396       break;
02397 
02398     case WWT_CLOSEBOX:
02399       DrawCloseBox(r, this->colour, this->widget_data);
02400       break;
02401 
02402     case WWT_DROPDOWN:
02403       if (this->index >= 0) w->SetStringParameters(this->index);
02404       DrawDropdown(r, this->colour, clicked, this->widget_data);
02405       break;
02406 
02407     case NWID_BUTTON_DROPDOWN:
02408     case NWID_PUSHBUTTON_DROPDOWN:
02409       if (this->index >= 0) w->SetStringParameters(this->index);
02410       DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data);
02411       break;
02412 
02413     default:
02414       NOT_REACHED();
02415   }
02416   if (this->index >= 0) w->DrawWidget(r, this->index);
02417 
02418   if (this->IsDisabled()) {
02419     GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER);
02420   }
02421 }
02422 
02430 bool NWidgetLeaf::ButtonHit(const Point &pt)
02431 {
02432   if (_current_text_dir == TD_LTR) {
02433     int button_width = this->pos_x + this->current_x - 12;
02434     return pt.x < button_width;
02435   } else {
02436     int button_left = this->pos_x + 12;
02437     return pt.x >= button_left;
02438   }
02439 }
02440 
02441 /* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
02442 
02458 static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, bool *fill_dest, int *biggest_index)
02459 {
02460   int num_used = 0;
02461 
02462   *dest = NULL;
02463   *fill_dest = false;
02464 
02465   while (count > num_used) {
02466     switch (parts->type) {
02467       case NWID_SPACER:
02468         if (*dest != NULL) return num_used;
02469         *dest = new NWidgetSpacer(0, 0);
02470         break;
02471 
02472       case NWID_HORIZONTAL:
02473         if (*dest != NULL) return num_used;
02474         *dest = new NWidgetHorizontal(parts->u.cont_flags);
02475         *fill_dest = true;
02476         break;
02477 
02478       case NWID_HORIZONTAL_LTR:
02479         if (*dest != NULL) return num_used;
02480         *dest = new NWidgetHorizontalLTR(parts->u.cont_flags);
02481         *fill_dest = true;
02482         break;
02483 
02484       case WWT_PANEL:
02485       case WWT_INSET:
02486       case WWT_FRAME:
02487         if (*dest != NULL) return num_used;
02488         *dest = new NWidgetBackground(parts->type, parts->u.widget.colour, parts->u.widget.index);
02489         *biggest_index = max(*biggest_index, (int)parts->u.widget.index);
02490         *fill_dest = true;
02491         break;
02492 
02493       case NWID_VERTICAL:
02494         if (*dest != NULL) return num_used;
02495         *dest = new NWidgetVertical(parts->u.cont_flags);
02496         *fill_dest = true;
02497         break;
02498 
02499       case NWID_MATRIX: {
02500         if (*dest != NULL) return num_used;
02501         NWidgetMatrix *nwm = new NWidgetMatrix();
02502         *dest = nwm;
02503         *fill_dest = true;
02504         nwm->SetIndex(parts->u.widget.index);
02505         nwm->SetColour(parts->u.widget.colour);
02506         *biggest_index = max(*biggest_index, (int)parts->u.widget.index);
02507         break;
02508       }
02509 
02510       case WPT_FUNCTION: {
02511         if (*dest != NULL) return num_used;
02512         /* Ensure proper functioning even when the called code simply writes its largest index. */
02513         int biggest = -1;
02514         *dest = parts->u.func_ptr(&biggest);
02515         *biggest_index = max(*biggest_index, biggest);
02516         *fill_dest = false;
02517         break;
02518       }
02519 
02520       case WPT_RESIZE: {
02521         NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
02522         if (nwrb != NULL) {
02523           assert(parts->u.xy.x >= 0 && parts->u.xy.y >= 0);
02524           nwrb->SetResize(parts->u.xy.x, parts->u.xy.y);
02525         }
02526         break;
02527       }
02528 
02529       case WPT_MINSIZE: {
02530         NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
02531         if (nwrb != NULL) {
02532           assert(parts->u.xy.x >= 0 && parts->u.xy.y >= 0);
02533           nwrb->SetMinimalSize(parts->u.xy.x, parts->u.xy.y);
02534         }
02535         break;
02536       }
02537 
02538       case WPT_MINTEXTLINES: {
02539         NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
02540         if (nwrb != NULL) {
02541           assert(parts->u.text_lines.size >= FS_BEGIN && parts->u.text_lines.size < FS_END);
02542           nwrb->SetMinimalTextLines(parts->u.text_lines.lines, parts->u.text_lines.spacing, parts->u.text_lines.size);
02543         }
02544         break;
02545       }
02546 
02547       case WPT_FILL: {
02548         NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
02549         if (nwrb != NULL) nwrb->SetFill(parts->u.xy.x, parts->u.xy.y);
02550         break;
02551       }
02552 
02553       case WPT_DATATIP: {
02554         NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(*dest);
02555         if (nwc != NULL) {
02556           nwc->widget_data = parts->u.data_tip.data;
02557           nwc->tool_tip = parts->u.data_tip.tooltip;
02558         }
02559         break;
02560       }
02561 
02562       case WPT_PADDING:
02563         if (*dest != NULL) (*dest)->SetPadding(parts->u.padding.top, parts->u.padding.right, parts->u.padding.bottom, parts->u.padding.left);
02564         break;
02565 
02566       case WPT_PIPSPACE: {
02567         NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(*dest);
02568         if (nwc != NULL) nwc->SetPIP(parts->u.pip.pre,  parts->u.pip.inter, parts->u.pip.post);
02569 
02570         NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(*dest);
02571         if (nwb != NULL) nwb->SetPIP(parts->u.pip.pre,  parts->u.pip.inter, parts->u.pip.post);
02572         break;
02573       }
02574 
02575       case WPT_SCROLLBAR: {
02576         NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(*dest);
02577         if (nwc != NULL) {
02578           nwc->scrollbar_index = parts->u.widget.index;
02579         }
02580         break;
02581       }
02582 
02583       case WPT_ENDCONTAINER:
02584         return num_used;
02585 
02586       case NWID_VIEWPORT:
02587         if (*dest != NULL) return num_used;
02588         *dest = new NWidgetViewport(parts->u.widget.index);
02589         *biggest_index = max(*biggest_index, (int)parts->u.widget.index);
02590         break;
02591 
02592       case NWID_HSCROLLBAR:
02593       case NWID_VSCROLLBAR:
02594         if (*dest != NULL) return num_used;
02595         *dest = new NWidgetScrollbar(parts->type, parts->u.widget.colour, parts->u.widget.index);
02596         *biggest_index = max(*biggest_index, (int)parts->u.widget.index);
02597         break;
02598 
02599       case NWID_SELECTION: {
02600         if (*dest != NULL) return num_used;
02601         NWidgetStacked *nws = new NWidgetStacked();
02602         *dest = nws;
02603         *fill_dest = true;
02604         nws->SetIndex(parts->u.widget.index);
02605         *biggest_index = max(*biggest_index, (int)parts->u.widget.index);
02606         break;
02607       }
02608 
02609       default:
02610         if (*dest != NULL) return num_used;
02611         assert((parts->type & WWT_MASK) < WWT_LAST || (parts->type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
02612         *dest = new NWidgetLeaf(parts->type, parts->u.widget.colour, parts->u.widget.index, 0x0, STR_NULL);
02613         *biggest_index = max(*biggest_index, (int)parts->u.widget.index);
02614         break;
02615     }
02616     num_used++;
02617     parts++;
02618   }
02619 
02620   return num_used;
02621 }
02622 
02632 static int MakeWidgetTree(const NWidgetPart *parts, int count, NWidgetBase **parent, int *biggest_index)
02633 {
02634   /* If *parent == NULL, only the first widget is read and returned. Otherwise, *parent must point to either
02635    * a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
02636   NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(*parent);
02637   NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(*parent);
02638   assert(*parent == NULL || (nwid_cont != NULL && nwid_parent == NULL) || (nwid_cont == NULL && nwid_parent != NULL));
02639 
02640   int total_used = 0;
02641   for (;;) {
02642     NWidgetBase *sub_widget = NULL;
02643     bool fill_sub = false;
02644     int num_used = MakeNWidget(parts, count - total_used, &sub_widget, &fill_sub, biggest_index);
02645     parts += num_used;
02646     total_used += num_used;
02647 
02648     /* Break out of loop when end reached */
02649     if (sub_widget == NULL) break;
02650 
02651     /* If sub-widget is a container, recursively fill that container. */
02652     WidgetType tp = sub_widget->type;
02653     if (fill_sub && (tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
02654               || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION)) {
02655       NWidgetBase *sub_ptr = sub_widget;
02656       int num_used = MakeWidgetTree(parts, count - total_used, &sub_ptr, biggest_index);
02657       parts += num_used;
02658       total_used += num_used;
02659     }
02660 
02661     /* Add sub_widget to parent container if available, otherwise return the widget to the caller. */
02662     if (nwid_cont != NULL) nwid_cont->Add(sub_widget);
02663     if (nwid_parent != NULL) nwid_parent->Add(sub_widget);
02664     if (nwid_cont == NULL && nwid_parent == NULL) {
02665       *parent = sub_widget;
02666       return total_used;
02667     }
02668   }
02669 
02670   if (count == total_used) return total_used; // Reached the end of the array of parts?
02671 
02672   assert(total_used < count);
02673   assert(parts->type == WPT_ENDCONTAINER);
02674   return total_used + 1; // *parts is also 'used'
02675 }
02676 
02688 NWidgetContainer *MakeNWidgets(const NWidgetPart *parts, int count, int *biggest_index, NWidgetContainer *container)
02689 {
02690   *biggest_index = -1;
02691   if (container == NULL) container = new NWidgetVertical();
02692   NWidgetBase *cont_ptr = container;
02693   MakeWidgetTree(parts, count, &cont_ptr, biggest_index);
02694   return container;
02695 }
02696 
02710 NWidgetContainer *MakeWindowNWidgetTree(const NWidgetPart *parts, int count, int *biggest_index, NWidgetStacked **shade_select)
02711 {
02712   *biggest_index = -1;
02713 
02714   /* Read the first widget recursively from the array. */
02715   NWidgetBase *nwid = NULL;
02716   int num_used = MakeWidgetTree(parts, count, &nwid, biggest_index);
02717   assert(nwid != NULL);
02718   parts += num_used;
02719   count -= num_used;
02720 
02721   NWidgetContainer *root = new NWidgetVertical;
02722   root->Add(nwid);
02723   if (count == 0) { // There is no body at all.
02724     *shade_select = NULL;
02725     return root;
02726   }
02727 
02728   /* If the first widget looks like a titlebar, treat it as such.
02729    * If it has a shading box, silently add a shade selection widget in the tree. */
02730   NWidgetHorizontal *hor_cont = dynamic_cast<NWidgetHorizontal *>(nwid);
02731   NWidgetContainer *body;
02732   if (hor_cont != NULL && hor_cont->GetWidgetOfType(WWT_CAPTION) != NULL && hor_cont->GetWidgetOfType(WWT_SHADEBOX) != NULL) {
02733     *shade_select = new NWidgetStacked;
02734     root->Add(*shade_select);
02735     body = new NWidgetVertical;
02736     (*shade_select)->Add(body);
02737   } else {
02738     *shade_select = NULL;
02739     body = root;
02740   }
02741 
02742   /* Load the remaining parts into 'body'. */
02743   int biggest2 = -1;
02744   MakeNWidgets(parts, count, &biggest2, body);
02745 
02746   *biggest_index = max(*biggest_index, biggest2);
02747   return root;
02748 }
02749 
02760 NWidgetBase *MakeCompanyButtonRows(int *biggest_index, int widget_first, int widget_last, int max_length, StringID button_tooltip)
02761 {
02762   NWidgetVertical *vert = NULL; // Storage for all rows.
02763   NWidgetHorizontal *hor = NULL; // Storage for buttons in one row.
02764   int hor_length = 0;
02765 
02766   Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
02767   sprite_size.width  += WD_MATRIX_LEFT + WD_MATRIX_RIGHT;
02768   sprite_size.height += WD_MATRIX_TOP + WD_MATRIX_BOTTOM + 1; // 1 for the 'offset' of being pressed
02769 
02770   for (int widnum = widget_first; widnum <= widget_last; widnum++) {
02771     /* Ensure there is room in 'hor' for another button. */
02772     if (hor_length == max_length) {
02773       if (vert == NULL) vert = new NWidgetVertical();
02774       vert->Add(hor);
02775       hor = NULL;
02776       hor_length = 0;
02777     }
02778     if (hor == NULL) {
02779       hor = new NWidgetHorizontal();
02780       hor_length = 0;
02781     }
02782 
02783     NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, widnum);
02784     panel->SetMinimalSize(sprite_size.width, sprite_size.height);
02785     panel->SetFill(1, 0);
02786     panel->SetResize(1, 0);
02787     panel->SetDataTip(0x0, button_tooltip);
02788     hor->Add(panel);
02789     hor_length++;
02790   }
02791   *biggest_index = widget_last;
02792   if (vert == NULL) return hor; // All buttons fit in a single row.
02793 
02794   if (hor_length > 0 && hor_length < max_length) {
02795     /* Last row is partial, add a spacer at the end to force all buttons to the left. */
02796     NWidgetSpacer *spc = new NWidgetSpacer(sprite_size.width, sprite_size.height);
02797     spc->SetFill(1, 0);
02798     spc->SetResize(1, 0);
02799     hor->Add(spc);
02800   }
02801   if (hor != NULL) vert->Add(hor);
02802   return vert;
02803 }