# smallmap_gui.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 "clear_map.h"
    00014 #include "industry.h"
    00015 #include "station_map.h"
    00016 #include "landscape.h"
    00017 #include "window_gui.h"
    00018 #include "tree_map.h"
    00019 #include "viewport_func.h"
    00020 #include "town.h"
    00021 #include "blitter/factory.hpp"
    00022 #include "tunnelbridge_map.h"
    00023 #include "strings_func.h"
    00024 #include "core/endian_func.hpp"
    00025 #include "vehicle_base.h"
    00026 #include "sound_func.h"
    00027 #include "window_func.h"
    00028 #include "company_base.h"
    00029
    00030 #include "table/strings.h"
    00031
    00033 enum SmallMapWindowWidgets {
    00034   SM_WIDGET_CAPTION,
    00035   SM_WIDGET_MAP_BORDER,
    00036   SM_WIDGET_MAP,
    00037   SM_WIDGET_LEGEND,
    00038   SM_WIDGET_ZOOM_IN,
    00039   SM_WIDGET_ZOOM_OUT,
    00040   SM_WIDGET_CONTOUR,
    00041   SM_WIDGET_VEHICLES,
    00042   SM_WIDGET_INDUSTRIES,
    00043   SM_WIDGET_ROUTES,
    00044   SM_WIDGET_VEGETATION,
    00045   SM_WIDGET_OWNERS,
    00046   SM_WIDGET_CENTERMAP,
    00047   SM_WIDGET_TOGGLETOWNNAME,
    00048   SM_WIDGET_SELECT_BUTTONS,
    00049   SM_WIDGET_ENABLE_ALL,
    00050   SM_WIDGET_DISABLE_ALL,
    00051   SM_WIDGET_SHOW_HEIGHT,
    00052 };
    00053
    00054 static int _smallmap_industry_count;
    00055 static int _smallmap_company_count;
    00056
    00057 static const int NUM_NO_COMPANY_ENTRIES = 4;
    00058
    00060 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, false}
    00061
    00063 #define MC(height)  {0, STR_TINY_BLACK_HEIGHT, INVALID_INDUSTRYTYPE, height, INVALID_COMPANY, true, false, false}
    00064
    00066 #define MO(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, false}
    00067
    00069 #define MOEND() {0, 0, INVALID_INDUSTRYTYPE, 0, OWNER_NONE, true, true, false}
    00070
    00072 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, true, false}
    00073
    00078 #define MS(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, true}
    00079
    00081 struct LegendAndColour {
    00082   uint8 colour;
    00083   StringID legend;
    00084   IndustryType type;
    00085   uint8 height;
    00086   CompanyID company;
    00087   bool show_on_map;
    00088   bool end;
    00089   bool col_break;
    00090 };
    00091
    00093 static LegendAndColour _legend_land_contours[] = {
    00094   /* The colours for the following values are set at BuildLandLegend() based on each colour scheme. */
    00095   MC(0),
    00096   MC(4),
    00097   MC(8),
    00098   MC(12),
    00099   MC(14),
    00100
    00101   MS(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
    00102   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
    00103   MK(0x98, STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
    00104   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00105   MK(0x0F, STR_SMALLMAP_LEGENDA_VEHICLES),
    00106   MKEND()
    00107 };
    00108
    00109 static const LegendAndColour _legend_vehicles[] = {
    00110   MK(0xB8, STR_SMALLMAP_LEGENDA_TRAINS),
    00111   MK(0xBF, STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
    00112   MK(0x98, STR_SMALLMAP_LEGENDA_SHIPS),
    00113   MK(0x0F, STR_SMALLMAP_LEGENDA_AIRCRAFT),
    00114
    00115   MS(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
    00116   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00117   MKEND()
    00118 };
    00119
    00120 static const LegendAndColour _legend_routes[] = {
    00121   MK(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
    00122   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
    00123   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00124
    00125   MS(0x56, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
    00126   MK(0xC2, STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
    00127   MK(0xBF, STR_SMALLMAP_LEGENDA_BUS_STATION),
    00128   MK(0xB8, STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
    00129   MK(0x98, STR_SMALLMAP_LEGENDA_DOCK),
    00130   MKEND()
    00131 };
    00132
    00133 static const LegendAndColour _legend_vegetation[] = {
    00134   MK(0x52, STR_SMALLMAP_LEGENDA_ROUGH_LAND),
    00135   MK(0x54, STR_SMALLMAP_LEGENDA_GRASS_LAND),
    00136   MK(0x37, STR_SMALLMAP_LEGENDA_BARE_LAND),
    00137   MK(0x25, STR_SMALLMAP_LEGENDA_FIELDS),
    00138   MK(0x57, STR_SMALLMAP_LEGENDA_TREES),
    00139   MK(0xD0, STR_SMALLMAP_LEGENDA_FOREST),
    00140
    00141   MS(0x0A, STR_SMALLMAP_LEGENDA_ROCKS),
    00142   MK(0xC2, STR_SMALLMAP_LEGENDA_DESERT),
    00143   MK(0x98, STR_SMALLMAP_LEGENDA_SNOW),
    00144   MK(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
    00145   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00146   MKEND()
    00147 };
    00148
    00149 static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
    00150   MO(0xCA, STR_SMALLMAP_LEGENDA_WATER),
    00151   MO(0x00, STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings.
    00152   MO(0xB4, STR_SMALLMAP_LEGENDA_TOWNS),
    00153   MO(0x20, STR_SMALLMAP_LEGENDA_INDUSTRIES),
    00154   /* The legend will be terminated the first time it is used. */
    00155   MOEND(),
    00156 };
    00157
    00158 #undef MK
    00159 #undef MC
    00160 #undef MS
    00161 #undef MO
    00162 #undef MOEND
    00163 #undef MKEND
    00164
    00169 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
    00171 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
    00173 static bool _smallmap_show_heightmap = false;
    00175 static uint _company_to_list_pos[MAX_COMPANIES];
    00176
    00180 void BuildIndustriesLegend()
    00181 {
    00182   uint j = 0;
    00183
    00184   /* Add each name */
    00185   for (uint8 i = 0; i < NUM_INDUSTRYTYPES; i++) {
    00186     IndustryType ind = _sorted_industry_types[i];
    00187     const IndustrySpec *indsp = GetIndustrySpec(ind);
    00188     if (indsp->enabled) {
    00189       _legend_from_industries[j].legend = indsp->name;
    00190       _legend_from_industries[j].colour = indsp->map_colour;
    00191       _legend_from_industries[j].type = ind;
    00192       _legend_from_industries[j].show_on_map = true;
    00193       _legend_from_industries[j].col_break = false;
    00194       _legend_from_industries[j].end = false;
    00195
    00196       /* Store widget number for this industry type. */
    00197       _industry_to_list_pos[ind] = j;
    00198       j++;
    00199     }
    00200   }
    00201   /* Terminate the list */
    00202   _legend_from_industries[j].end = true;
    00203
    00204   /* Store number of enabled industries */
    00205   _smallmap_industry_count = j;
    00206 }
    00207
    00208 static const LegendAndColour * const _legend_table[] = {
    00209   _legend_land_contours,
    00210   _legend_vehicles,
    00211   _legend_from_industries,
    00212   _legend_routes,
    00213   _legend_vegetation,
    00214   _legend_land_owners,
    00215 };
    00216
    00217 #define MKCOLOUR(x) TO_LE32X(x)
    00218
    00220 static const uint32 _green_map_heights[] = {
    00221   MKCOLOUR(0x5A5A5A5A),
    00222   MKCOLOUR(0x5A5B5A5B),
    00223   MKCOLOUR(0x5B5B5B5B),
    00224   MKCOLOUR(0x5B5C5B5C),
    00225   MKCOLOUR(0x5C5C5C5C),
    00226   MKCOLOUR(0x5C5D5C5D),
    00227   MKCOLOUR(0x5D5D5D5D),
    00228   MKCOLOUR(0x5D5E5D5E),
    00229   MKCOLOUR(0x5E5E5E5E),
    00230   MKCOLOUR(0x5E5F5E5F),
    00231   MKCOLOUR(0x5F5F5F5F),
    00232   MKCOLOUR(0x5F1F5F1F),
    00233   MKCOLOUR(0x1F1F1F1F),
    00234   MKCOLOUR(0x1F271F27),
    00235   MKCOLOUR(0x27272727),
    00236   MKCOLOUR(0x27272727),
    00237 };
    00238 assert_compile(lengthof(_green_map_heights) == MAX_TILE_HEIGHT + 1);
    00239
    00241 static const uint32 _dark_green_map_heights[] = {
    00242   MKCOLOUR(0x60606060),
    00243   MKCOLOUR(0x60616061),
    00244   MKCOLOUR(0x61616161),
    00245   MKCOLOUR(0x61626162),
    00246   MKCOLOUR(0x62626262),
    00247   MKCOLOUR(0x62636263),
    00248   MKCOLOUR(0x63636363),
    00249   MKCOLOUR(0x63646364),
    00250   MKCOLOUR(0x64646464),
    00251   MKCOLOUR(0x64656465),
    00252   MKCOLOUR(0x65656565),
    00253   MKCOLOUR(0x65666566),
    00254   MKCOLOUR(0x66666666),
    00255   MKCOLOUR(0x66676667),
    00256   MKCOLOUR(0x67676767),
    00257   MKCOLOUR(0x67676767),
    00258 };
    00259 assert_compile(lengthof(_dark_green_map_heights) == MAX_TILE_HEIGHT + 1);
    00260
    00262 static const uint32 _violet_map_heights[] = {
    00263   MKCOLOUR(0x80808080),
    00264   MKCOLOUR(0x80818081),
    00265   MKCOLOUR(0x81818181),
    00266   MKCOLOUR(0x81828182),
    00267   MKCOLOUR(0x82828282),
    00268   MKCOLOUR(0x82838283),
    00269   MKCOLOUR(0x83838383),
    00270   MKCOLOUR(0x83848384),
    00271   MKCOLOUR(0x84848484),
    00272   MKCOLOUR(0x84858485),
    00273   MKCOLOUR(0x85858585),
    00274   MKCOLOUR(0x85868586),
    00275   MKCOLOUR(0x86868686),
    00276   MKCOLOUR(0x86878687),
    00277   MKCOLOUR(0x87878787),
    00278   MKCOLOUR(0x87878787),
    00279 };
    00280 assert_compile(lengthof(_violet_map_heights) == MAX_TILE_HEIGHT + 1);
    00281
    00283 struct SmallMapColourScheme {
    00284   const uint32 *height_colours;
    00285   uint32 default_colour;
    00286 };
    00287
    00289 static const SmallMapColourScheme _heightmap_schemes[] = {
    00290   {_green_map_heights,      MKCOLOUR(0x54545454)},
    00291   {_dark_green_map_heights, MKCOLOUR(0x62626262)},
    00292   {_violet_map_heights,     MKCOLOUR(0x82828282)},
    00293 };
    00294
    00295 void BuildLandLegend()
    00296 {
    00297   for (LegendAndColour *lc = _legend_land_contours; lc->legend == STR_TINY_BLACK_HEIGHT; lc++) {
    00298     lc->colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[lc->height];
    00299   }
    00300 }
    00301
    00305 void BuildOwnerLegend()
    00306 {
    00307   _legend_land_owners[1].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour;
    00308
    00309   int i = NUM_NO_COMPANY_ENTRIES;
    00310   const Company *c;
    00311   FOR_ALL_COMPANIES(c) {
    00312     _legend_land_owners[i].colour = _colour_gradient[c->colour][5];
    00313     _legend_land_owners[i].company = c->index;
    00314     _legend_land_owners[i].show_on_map = true;
    00315     _legend_land_owners[i].col_break = false;
    00316     _legend_land_owners[i].end = false;
    00317     _company_to_list_pos[c->index] = i;
    00318     i++;
    00319   }
    00320
    00321   /* Terminate the list */
    00322   _legend_land_owners[i].end = true;
    00323
    00324   /* Store maximum amount of owner legend entries. */
    00325   _smallmap_company_count = i;
    00326 }
    00327
    00328 struct AndOr {
    00329   uint32 mor;
    00330   uint32 mand;
    00331 };
    00332
    00333 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
    00334 {
    00335   return (colour & mask->mand) | mask->mor;
    00336 }
    00337
    00338
    00340 static const AndOr _smallmap_contours_andor[] = {
    00341   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
    00342   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
    00343   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
    00344   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
    00345   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
    00346   {MKCOLOUR(0x98989898), MKCOLOUR(0x00000000)}, // MP_STATION
    00347   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
    00348   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
    00349   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
    00350   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
    00351   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_OBJECT
    00352   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
    00353 };
    00354
    00356 static const AndOr _smallmap_vehicles_andor[] = {
    00357   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
    00358   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
    00359   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
    00360   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
    00361   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
    00362   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_STATION
    00363   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
    00364   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
    00365   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
    00366   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
    00367   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_OBJECT
    00368   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00369 };
    00370
    00372 static const byte _tiletype_importance[] = {
    00373   2, // MP_CLEAR
    00374   8, // MP_RAILWAY
    00375   7, // MP_ROAD
    00376   5, // MP_HOUSE
    00377   2, // MP_TREES
    00378   9, // MP_STATION
    00379   2, // MP_WATER
    00380   1, // MP_VOID
    00381   6, // MP_INDUSTRY
    00382   8, // MP_TUNNELBRIDGE
    00383   2, // MP_OBJECT
    00384   0,
    00385 };
    00386
    00387
    00388 static inline TileType GetEffectiveTileType(TileIndex tile)
    00389 {
    00390   TileType t = GetTileType(tile);
    00391
    00392   if (t == MP_TUNNELBRIDGE) {
    00393     TransportType tt = GetTunnelBridgeTransportType(tile);
    00394
    00395     switch (tt) {
    00396       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
    00397       case TRANSPORT_ROAD: t = MP_ROAD;    break;
    00398       default:             t = MP_WATER;   break;
    00399     }
    00400   }
    00401   return t;
    00402 }
    00403
    00410 static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t)
    00411 {
    00412   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00413   return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[t]);
    00414 }
    00415
    00423 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
    00424 {
    00425   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00426   return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[t]);
    00427 }
    00428
    00436 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
    00437 {
    00438   if (t == MP_INDUSTRY) {
    00439     /* If industry is allowed to be seen, use its colour on the map */
    00440     if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
    00441       return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
    00442     } else {
    00443       /* Otherwise, return the colour which will make it disappear */
    00444       t = (IsTileOnWater(tile) ? MP_WATER : MP_CLEAR);
    00445     }
    00446   }
    00447
    00448   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00449   return ApplyMask(_smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
    00450 }
    00451
    00459 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t)
    00460 {
    00461   if (t == MP_STATION) {
    00462     switch (GetStationType(tile)) {
    00463       case STATION_RAIL:    return MKCOLOUR(0x56565656);
    00464       case STATION_AIRPORT: return MKCOLOUR(0xB8B8B8B8);
    00465       case STATION_TRUCK:   return MKCOLOUR(0xC2C2C2C2);
    00466       case STATION_BUS:     return MKCOLOUR(0xBFBFBFBF);
    00467       case STATION_DOCK:    return MKCOLOUR(0x98989898);
    00468       default:              return MKCOLOUR(0xFFFFFFFF);
    00469     }
    00470   } else if (t == MP_RAILWAY) {
    00471     AndOr andor = {
    00472       GetRailTypeInfo(GetRailType(tile))->map_colour * MKCOLOUR(0x00010100),
    00473       _smallmap_contours_andor[t].mand
    00474     };
    00475
    00476     const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00477     return ApplyMask(cs->default_colour, &andor);
    00478   }
    00479
    00480   /* Ground colour */
    00481   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00482   return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
    00483 }
    00484
    00485
    00486 static const uint32 _vegetation_clear_bits[] = {
    00487   MKCOLOUR(0x54545454),
    00488   MKCOLOUR(0x52525252),
    00489   MKCOLOUR(0x0A0A0A0A),
    00490   MKCOLOUR(0x25252525),
    00491   MKCOLOUR(0x98989898),
    00492   MKCOLOUR(0xC2C2C2C2),
    00493   MKCOLOUR(0x54545454),
    00494   MKCOLOUR(0x54545454),
    00495 };
    00496
    00504 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile, TileType t)
    00505 {
    00506   switch (t) {
    00507     case MP_CLEAR:
    00508       return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR(0x37373737) : _vegetation_clear_bits[GetClearGround(tile)];
    00509
    00510     case MP_INDUSTRY:
    00511       return GetIndustrySpec(Industry::GetByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR(0xD0D0D0D0) : MKCOLOUR(0xB5B5B5B5);
    00512
    00513     case MP_TREES:
    00514       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(tile) == TREE_GROUND_ROUGH_SNOW) {
    00515         return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR(0x98575798) : MKCOLOUR(0xC25757C2);
    00516       }
    00517       return MKCOLOUR(0x54575754);
    00518
    00519     default:
    00520       return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00521   }
    00522 }
    00523
    00531 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t)
    00532 {
    00533   Owner o;
    00534
    00535   switch (t) {
    00536     case MP_INDUSTRY: return MKCOLOUR(0x20202020);
    00537     case MP_HOUSE:    return MKCOLOUR(0xB4B4B4B4);
    00538     default:          o = GetTileOwner(tile); break;
    00539     /* FIXME: For MP_ROAD there are multiple owners.
    00540      * GetTileOwner returns the rail owner (level crossing) resp. the owner of ROADTYPE_ROAD (normal road),
    00541      * even if there are no ROADTYPE_ROAD bits on the tile.
    00542      */
    00543   }
    00544
    00545   if ((o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) || o == OWNER_NONE || o == OWNER_WATER) {
    00546     if (t == MP_WATER) return MKCOLOUR(0xCACACACA);
    00547     const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00548     return _smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour;
    00549   } else if (o == OWNER_TOWN) {
    00550     return MKCOLOUR(0xB4B4B4B4);
    00551   }
    00552
    00553   return _legend_land_owners[_company_to_list_pos[o]].colour * 0x01010101;
    00554 }
    00555
    00557 static const byte _vehicle_type_colours[6] = {
    00558   184, 191, 152, 15, 215, 184
    00559 };
    00560
    00561
    00563 class SmallMapWindow : public Window {
    00565   enum SmallMapType {
    00566     SMT_CONTOUR,
    00567     SMT_VEHICLES,
    00568     SMT_INDUSTRY,
    00569     SMT_ROUTES,
    00570     SMT_VEGETATION,
    00571     SMT_OWNER,
    00572   };
    00573
    00575   enum ZoomLevelChange {
    00576     ZLC_INITIALIZE,
    00577     ZLC_ZOOM_OUT,
    00578     ZLC_ZOOM_IN,
    00579   };
    00580
    00581   static SmallMapType map_type;
    00582   static bool show_towns;
    00583
    00584   static const uint LEGEND_BLOB_WIDTH = 8;
    00585   static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
    00586   uint min_number_of_fixed_rows;
    00587   uint column_width;
    00588
    00589   int32 scroll_x;
    00590   int32 scroll_y;
    00591   int32 subscroll;
    00592   int zoom;
    00593
    00594   static const uint8 FORCE_REFRESH_PERIOD = 0x1F;
    00595   uint8 refresh;
    00596
    00603   FORCEINLINE Point RemapTile(int tile_x, int tile_y) const
    00604   {
    00605     int x_offset = tile_x - this->scroll_x / (int)TILE_SIZE;
    00606     int y_offset = tile_y - this->scroll_y / (int)TILE_SIZE;
    00607
    00608     if (this->zoom == 1) return RemapCoords(x_offset, y_offset, 0);
    00609
    00610     /* For negative offsets, round towards -inf. */
    00611     if (x_offset < 0) x_offset -= this->zoom - 1;
    00612     if (y_offset < 0) y_offset -= this->zoom - 1;
    00613
    00614     return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
    00615   }
    00616
    00627   FORCEINLINE Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
    00628   {
    00629     if (add_sub) px += this->subscroll;  // Total horizontal offset.
    00630
    00631     /* For each two rows down, add a x and a y tile, and
    00632      * For each four pixels to the right, move a tile to the right. */
    00633     Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
    00634     px &= 3;
    00635
    00636     if (py & 1) { // Odd number of rows, handle the 2 pixel shift.
    00637       if (px < 2) {
    00638         pt.x += this->zoom;
    00639         px += 2;
    00640       } else {
    00641         pt.y += this->zoom;
    00642         px -= 2;
    00643       }
    00644     }
    00645
    00646     *sub = px;
    00647     return pt;
    00648   }
    00649
    00659   Point ComputeScroll(int tx, int ty, int x, int y, int *sub)
    00660   {
    00661     assert(x >= 0 && y >= 0);
    00662
    00663     int new_sub;
    00664     Point tile_xy = PixelToTile(x, y, &new_sub, false);
    00665     tx -= tile_xy.x;
    00666     ty -= tile_xy.y;
    00667
    00668     Point scroll;
    00669     if (new_sub == 0) {
    00670       *sub = 0;
    00671       scroll.x = (tx + this->zoom) * TILE_SIZE;
    00672       scroll.y = (ty - this->zoom) * TILE_SIZE;
    00673     } else {
    00674       *sub = 4 - new_sub;
    00675       scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
    00676       scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
    00677     }
    00678     return scroll;
    00679   }
    00680
    00687   void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
    00688   {
    00689     static const int zoomlevels[] = {1, 2, 4, 6, 8}; // Available zoom levels. Bigger number means more zoom-out (further away).
    00690     static const int MIN_ZOOM_INDEX = 0;
    00691     static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
    00692
    00693     int new_index, cur_index, sub;
    00694     Point tile;
    00695     switch (change) {
    00696       case ZLC_INITIALIZE:
    00697         cur_index = - 1; // Definitely different from new_index.
    00698         new_index = MIN_ZOOM_INDEX;
    00699         break;
    00700
    00701       case ZLC_ZOOM_IN:
    00702       case ZLC_ZOOM_OUT:
    00703         for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
    00704           if (this->zoom == zoomlevels[cur_index]) break;
    00705         }
    00706         assert(cur_index <= MAX_ZOOM_INDEX);
    00707
    00708         tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
    00709         new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
    00710         break;
    00711
    00712       default: NOT_REACHED();
    00713     }
    00714
    00715     if (new_index != cur_index) {
    00716       this->zoom = zoomlevels[new_index];
    00717       if (cur_index >= 0) {
    00718         Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
    00719         this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
    00720             this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
    00721       }
    00722       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN,  this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
    00723       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
    00724       this->SetDirty();
    00725     }
    00726   }
    00727
    00733   inline uint32 GetTileColours(const TileArea &ta) const
    00734   {
    00735     int importance = 0;
    00736     TileIndex tile = INVALID_TILE; // Position of the most important tile.
    00737     TileType et = MP_VOID;         // Effective tile type at that position.
    00738
    00739     TILE_AREA_LOOP(ti, ta) {
    00740       TileType ttype = GetEffectiveTileType(ti);
    00741       if (_tiletype_importance[ttype] > importance) {
    00742         importance = _tiletype_importance[ttype];
    00743         tile = ti;
    00744         et = ttype;
    00745       }
    00746     }
    00747
    00748     switch (this->map_type) {
    00749       case SMT_CONTOUR:
    00750         return GetSmallMapContoursPixels(tile, et);
    00751
    00752       case SMT_VEHICLES:
    00753         return GetSmallMapVehiclesPixels(tile, et);
    00754
    00755       case SMT_INDUSTRY:
    00756         return GetSmallMapIndustriesPixels(tile, et);
    00757
    00758       case SMT_ROUTES:
    00759         return GetSmallMapRoutesPixels(tile, et);
    00760
    00761       case SMT_VEGETATION:
    00762         return GetSmallMapVegetationPixels(tile, et);
    00763
    00764       case SMT_OWNER:
    00765         return GetSmallMapOwnerPixels(tile, et);
    00766
    00767       default: NOT_REACHED();
    00768     }
    00769   }
    00770
    00785   void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
    00786   {
    00787     void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
    00788     uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
    00789
    00790     do {
    00791       /* Check if the tile (xc,yc) is within the map range */
    00792       if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
    00793
    00794       /* Check if the dst pointer points to a pixel inside the screen buffer */
    00795       if (dst < _screen.dst_ptr) continue;
    00796       if (dst >= dst_ptr_abs_end) continue;
    00797
    00798       /* Construct tilearea covered by (xc, yc, xc + this->zoom, yc + this->zoom) such that it is within min_xy limits. */
    00799       TileArea ta;
    00800       if (min_xy == 1 && (xc == 0 || yc == 0)) {
    00801         if (this->zoom == 1) continue; // The tile area is empty, don't draw anything.
    00802
    00803         ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
    00804       } else {
    00805         ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
    00806       }
    00807       ta.ClampToMap(); // Clamp to map boundaries (may contain MP_VOID tiles!).
    00808
    00809       uint32 val = this->GetTileColours(ta);
    00810       uint8 *val8 = (uint8 *)&val;
    00811       int idx = max(0, -start_pos);
    00812       for (int pos = max(0, start_pos); pos < end_pos; pos++) {
    00813         blitter->SetPixel(dst, idx, 0, val8[idx]);
    00814         idx++;
    00815       }
    00816     /* Switch to next tile in the column */
    00817     } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
    00818   }
    00819
    00825   void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
    00826   {
    00827     const Vehicle *v;
    00828     FOR_ALL_VEHICLES(v) {
    00829       if (v->type == VEH_EFFECT) continue;
    00830       if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
    00831
    00832       /* Remap into flat coordinates. */
    00833       Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
    00834
    00835       int y = pt.y - dpi->top;
    00836       if (!IsInsideMM(y, 0, dpi->height)) continue; // y is out of bounds.
    00837
    00838       bool skip = false; // Default is to draw both pixels.
    00839       int x = pt.x - this->subscroll - 3 - dpi->left; // Offset X coordinate.
    00840       if (x < 0) {
    00841         /* if x+1 is 0, that means we're on the very left edge,
    00842          * and should thus only draw a single pixel */
    00843         if (++x != 0) continue;
    00844         skip = true;
    00845       } else if (x >= dpi->width - 1) {
    00846         /* Check if we're at the very right edge, and if so draw only a single pixel */
    00847         if (x != dpi->width - 1) continue;
    00848         skip = true;
    00849       }
    00850
    00851       /* Calculate pointer to pixel and the colour */
    00852       byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
    00853
    00854       /* And draw either one or two pixels depending on clipping */
    00855       blitter->SetPixel(dpi->dst_ptr, x, y, colour);
    00856       if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
    00857     }
    00858   }
    00859
    00864   void DrawTowns(const DrawPixelInfo *dpi) const
    00865   {
    00866     const Town *t;
    00867     FOR_ALL_TOWNS(t) {
    00868       /* Remap the town coordinate */
    00869       Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
    00870       int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
    00871       int y = pt.y;
    00872
    00873       /* Check if the town sign is within bounds */
    00874       if (x + t->sign.width_small > dpi->left &&
    00875           x < dpi->left + dpi->width &&
    00876           y + FONT_HEIGHT_SMALL > dpi->top &&
    00877           y < dpi->top + dpi->height) {
    00878         /* And draw it. */
    00879         SetDParam(0, t->index);
    00880         DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
    00881       }
    00882     }
    00883   }
    00884
    00891   static inline void DrawVertMapIndicator(int x, int y, int y2)
    00892   {
    00893     GfxFillRect(x, y,      x, y + 3, 69);
    00894     GfxFillRect(x, y2 - 3, x, y2,    69);
    00895   }
    00896
    00903   static inline void DrawHorizMapIndicator(int x, int x2, int y)
    00904   {
    00905     GfxFillRect(x,      y, x + 3, y, 69);
    00906     GfxFillRect(x2 - 3, y, x2,    y, 69);
    00907   }
    00908
    00912   void DrawMapIndicators() const
    00913   {
    00914     /* Find main viewport. */
    00915     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    00916
    00917     Point tile = InverseRemapCoords(vp->virtual_left, vp->virtual_top);
    00918     Point tl = this->RemapTile(tile.x >> 4, tile.y >> 4);
    00919     tl.x -= this->subscroll;
    00920
    00921     tile = InverseRemapCoords(vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height);
    00922     Point br = this->RemapTile(tile.x >> 4, tile.y >> 4);
    00923     br.x -= this->subscroll;
    00924
    00925     SmallMapWindow::DrawVertMapIndicator(tl.x, tl.y, br.y);
    00926     SmallMapWindow::DrawVertMapIndicator(br.x, tl.y, br.y);
    00927
    00928     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, tl.y);
    00929     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, br.y);
    00930   }
    00931
    00943   void DrawSmallMap(DrawPixelInfo *dpi) const
    00944   {
    00945     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00946     DrawPixelInfo *old_dpi;
    00947
    00948     old_dpi = _cur_dpi;
    00949     _cur_dpi = dpi;
    00950
    00951     /* Clear it */
    00952     GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
    00953
    00954     /* Which tile is displayed at (dpi->left, dpi->top)? */
    00955     int dx;
    00956     Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
    00957     int tile_x = this->scroll_x / (int)TILE_SIZE + tile.x;
    00958     int tile_y = this->scroll_y / (int)TILE_SIZE + tile.y;
    00959
    00960     void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
    00961     int x = - dx - 4;
    00962     int y = 0;
    00963
    00964     for (;;) {
    00965       /* Distance from left edge */
    00966       if (x >= -3) {
    00967         if (x >= dpi->width) break; // Exit the loop.
    00968
    00969         int end_pos = min(dpi->width, x + 4);
    00970         int reps = (dpi->height - y + 1) / 2; // Number of lines.
    00971         if (reps > 0) {
    00972           this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
    00973         }
    00974       }
    00975
    00976       if (y == 0) {
    00977         tile_y += this->zoom;
    00978         y++;
    00979         ptr = blitter->MoveTo(ptr, 0, 1);
    00980       } else {
    00981         tile_x -= this->zoom;
    00982         y--;
    00983         ptr = blitter->MoveTo(ptr, 0, -1);
    00984       }
    00985       ptr = blitter->MoveTo(ptr, 2, 0);
    00986       x += 2;
    00987     }
    00988
    00989     /* Draw vehicles */
    00990     if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
    00991
    00992     /* Draw town names */
    00993     if (this->show_towns) this->DrawTowns(dpi);
    00994
    00995     /* Draw map indicators */
    00996     this->DrawMapIndicators();
    00997
    00998     _cur_dpi = old_dpi;
    00999   }
    01000
    01004   void SetupWidgetData()
    01005   {
    01006     StringID legend_tooltip;
    01007     StringID enable_all_tooltip;
    01008     StringID disable_all_tooltip;
    01009     int plane;
    01010     switch (this->map_type) {
    01011       case SMT_INDUSTRY:
    01012         legend_tooltip = STR_SMALLMAP_TOOLTIP_INDUSTRY_SELECTION;
    01013         enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_INDUSTRIES;
    01014         disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_INDUSTRIES;
    01015         plane = 0;
    01016         break;
    01017
    01018       case SMT_OWNER:
    01019         legend_tooltip = STR_SMALLMAP_TOOLTIP_COMPANY_SELECTION;
    01020         enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_COMPANIES;
    01021         disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_COMPANIES;
    01022         plane = 0;
    01023         break;
    01024
    01025       default:
    01026         legend_tooltip = STR_NULL;
    01027         enable_all_tooltip = STR_NULL;
    01028         disable_all_tooltip = STR_NULL;
    01029         plane = 1;
    01030         break;
    01031     }
    01032
    01033     this->GetWidget<NWidgetCore>(SM_WIDGET_LEGEND)->SetDataTip(STR_NULL, legend_tooltip);
    01034     this->GetWidget<NWidgetCore>(SM_WIDGET_ENABLE_ALL)->SetDataTip(STR_SMALLMAP_ENABLE_ALL, enable_all_tooltip);
    01035     this->GetWidget<NWidgetCore>(SM_WIDGET_DISABLE_ALL)->SetDataTip(STR_SMALLMAP_DISABLE_ALL, disable_all_tooltip);
    01036     this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECT_BUTTONS)->SetDisplayedPlane(plane);
    01037   }
    01038
    01039 public:
    01040   uint min_number_of_columns;
    01041
    01042   SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
    01043   {
    01044     this->InitNested(desc, window_number);
    01045     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    01046
    01047     BuildLandLegend();
    01048     this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
    01049
    01050     this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    01051
    01052     this->SetupWidgetData();
    01053
    01054     this->SetZoomLevel(ZLC_INITIALIZE, NULL);
    01055     this->SmallMapCenterOnCurrentPos();
    01056   }
    01057
    01062   inline uint GetMinLegendWidth() const
    01063   {
    01064     return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
    01065   }
    01066
    01071   inline uint GetNumberColumnsLegend(uint width) const
    01072   {
    01073     return width / this->column_width;
    01074   }
    01075
    01081   uint GetLegendHeight(uint num_columns) const
    01082   {
    01083     uint num_rows = max(this->min_number_of_fixed_rows, CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), num_columns));
    01084     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
    01085   }
    01086
    01087   virtual void SetStringParameters(int widget) const
    01088   {
    01089     switch (widget) {
    01090       case SM_WIDGET_CAPTION:
    01091         SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
    01092         break;
    01093     }
    01094   }
    01095
    01096   virtual void OnInit()
    01097   {
    01098     uint min_width = 0;
    01099     this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
    01100     this->min_number_of_fixed_rows = 0;
    01101     for (uint i = 0; i < lengthof(_legend_table); i++) {
    01102       uint height = 0;
    01103       uint num_columns = 1;
    01104       for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
    01105         StringID str;
    01106         if (i == SMT_INDUSTRY) {
    01107           SetDParam(0, tbl->legend);
    01108           SetDParam(1, IndustryPool::MAX_SIZE);
    01109           str = STR_SMALLMAP_INDUSTRY;
    01110         } else if (i == SMT_OWNER) {
    01111           if (tbl->company != INVALID_COMPANY) {
    01112             if (!Company::IsValidID(tbl->company)) {
    01113               /* Rebuild the owner legend. */
    01114               BuildOwnerLegend();
    01115               this->OnInit();
    01116               return;
    01117             }
    01118             /* Non-fixed legend entries for the owner view. */
    01119             SetDParam(0, tbl->company);
    01120             str = STR_SMALLMAP_COMPANY;
    01121           } else {
    01122             str = tbl->legend;
    01123           }
    01124         } else {
    01125           if (tbl->col_break) {
    01126             this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    01127             height = 0;
    01128             num_columns++;
    01129           }
    01130           height++;
    01131           str = tbl->legend;
    01132         }
    01133         min_width = max(GetStringBoundingBox(str).width, min_width);
    01134       }
    01135       this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    01136       this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
    01137     }
    01138
    01139     /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
    01140     this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    01141   }
    01142
    01143   virtual void OnPaint()
    01144   {
    01145     if (this->map_type == SMT_OWNER) {
    01146       for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
    01147         if (tbl->company != INVALID_COMPANY && !Company::IsValidID(tbl->company)) {
    01148           /* Rebuild the owner legend. */
    01149           BuildOwnerLegend();
    01150           this->InvalidateData(1);
    01151           break;
    01152         }
    01153       }
    01154     }
    01155
    01156     this->DrawWidgets();
    01157   }
    01158
    01159   virtual void DrawWidget(const Rect &r, int widget) const
    01160   {
    01161     switch (widget) {
    01162       case SM_WIDGET_MAP: {
    01163         DrawPixelInfo new_dpi;
    01164         if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
    01165         this->DrawSmallMap(&new_dpi);
    01166         break;
    01167       }
    01168
    01169       case SM_WIDGET_LEGEND: {
    01170         uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
    01171         uint number_of_rows = max((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER) ? CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns) : 0, this->min_number_of_fixed_rows);
    01172         bool rtl = _current_text_dir == TD_RTL;
    01173         uint y_org = r.top + WD_FRAMERECT_TOP;
    01174         uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
    01175         uint y = y_org;
    01176         uint i = 0; // Row counter for industry legend.
    01177         uint row_height = FONT_HEIGHT_SMALL;
    01178
    01179         uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
    01180         uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
    01181         uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
    01182         uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
    01183
    01184         for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
    01185           if (tbl->col_break || ((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER) && i++ >= number_of_rows)) {
    01186             /* Column break needed, continue at top, COLUMN_WIDTH pixels
    01187              * (one "row") to the right. */
    01188             x += rtl ? -(int)this->column_width : this->column_width;
    01189             y = y_org;
    01190             i = 1;
    01191           }
    01192
    01193           if (this->map_type == SMT_INDUSTRY) {
    01194             /* Industry name must be formatted, since it's not in tiny font in the specs.
    01195              * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
    01196             SetDParam(0, tbl->legend);
    01197             SetDParam(1, Industry::GetIndustryTypeCount(tbl->type));
    01198             if (!tbl->show_on_map) {
    01199               /* Simply draw the string, not the black border of the legend colour.
    01200                * This will enforce the idea of the disabled item */
    01201               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
    01202             } else {
    01203               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
    01204               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
    01205             }
    01206           } else if (this->map_type == SMT_OWNER && tbl->company != INVALID_COMPANY) {
    01207             SetDParam(0, tbl->company);
    01208             if (!tbl->show_on_map) {
    01209               /* Simply draw the string, not the black border of the legend colour.
    01210                * This will enforce the idea of the disabled item */
    01211               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_GREY);
    01212             } else {
    01213               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_BLACK);
    01214               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
    01215             }
    01216           } else {
    01217             if (this->map_type == SMT_CONTOUR) SetDParam(0, tbl->height * TILE_HEIGHT_STEP);
    01218
    01219             /* Anything that is not an industry or a company is using normal process */
    01220             GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
    01221             DrawString(x + text_left, x + text_right, y, tbl->legend);
    01222           }
    01223           GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
    01224
    01225           y += row_height;
    01226         }
    01227       }
    01228     }
    01229   }
    01230
    01235   void SwitchMapType(SmallMapType map_type)
    01236   {
    01237     this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
    01238     this->map_type = map_type;
    01239     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    01240
    01241     this->SetupWidgetData();
    01242
    01243     this->SetDirty();
    01244   }
    01245
    01246   virtual void OnClick(Point pt, int widget, int click_count)
    01247   {
    01248     /* User clicked something, notify the industry chain window to stop sending newly selected industries. */
    01249     InvalidateWindowClassesData(WC_INDUSTRY_CARGOES, NUM_INDUSTRYTYPES);
    01250
    01251     switch (widget) {
    01252       case SM_WIDGET_MAP: { // Map window
    01253         /*
    01254          * XXX: scrolling with the left mouse button is done by subsequently
    01255          * clicking with the left mouse button; clicking once centers the
    01256          * large map at the selected point. So by unclicking the left mouse
    01257          * button here, it gets reclicked during the next inputloop, which
    01258          * would make it look like the mouse is being dragged, while it is
    01259          * actually being (virtually) clicked every inputloop.
    01260          */
    01261         _left_button_clicked = false;
    01262
    01263         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01264         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    01265         int sub;
    01266         pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
    01267         pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
    01268             this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
    01269
    01270         w->viewport->follow_vehicle = INVALID_VEHICLE;
    01271         w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width  >> 1);
    01272         w->viewport->dest_scrollpos_y = pt.y - (w->viewport->virtual_height >> 1);
    01273
    01274         this->SetDirty();
    01275         break;
    01276       }
    01277
    01278       case SM_WIDGET_ZOOM_IN:
    01279       case SM_WIDGET_ZOOM_OUT: {
    01280         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01281         Point pt = {wid->current_x / 2, wid->current_y / 2};
    01282         this->SetZoomLevel((widget == SM_WIDGET_ZOOM_IN) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
    01283         SndPlayFx(SND_15_BEEP);
    01284         break;
    01285       }
    01286
    01287       case SM_WIDGET_CONTOUR:    // Show land contours
    01288       case SM_WIDGET_VEHICLES:   // Show vehicles
    01289       case SM_WIDGET_INDUSTRIES: // Show industries
    01290       case SM_WIDGET_ROUTES:     // Show transport routes
    01291       case SM_WIDGET_VEGETATION: // Show vegetation
    01292       case SM_WIDGET_OWNERS:     // Show land owners
    01293         this->SwitchMapType((SmallMapType)(widget - SM_WIDGET_CONTOUR));
    01294         SndPlayFx(SND_15_BEEP);
    01295         break;
    01296
    01297       case SM_WIDGET_CENTERMAP: // Center the smallmap again
    01298         this->SmallMapCenterOnCurrentPos();
    01299         this->HandleButtonClick(SM_WIDGET_CENTERMAP);
    01300         SndPlayFx(SND_15_BEEP);
    01301         break;
    01302
    01303       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
    01304         this->show_towns = !this->show_towns;
    01305         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    01306
    01307         this->SetDirty();
    01308         SndPlayFx(SND_15_BEEP);
    01309         break;
    01310
    01311       case SM_WIDGET_LEGEND: // Legend
    01312         /* If industry type small map*/
    01313         if (this->map_type == SMT_INDUSTRY) {
    01314           /* If click on industries label, find right industry type and enable/disable it */
    01315           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
    01316           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
    01317           uint columns = this->GetNumberColumnsLegend(wi->current_x);
    01318           uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns), this->min_number_of_fixed_rows);
    01319           if (line >= number_of_rows) break;
    01320
    01321           bool rtl = _current_text_dir == TD_RTL;
    01322           int x = pt.x - wi->pos_x;
    01323           if (rtl) x = wi->current_x - x;
    01324           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
    01325
    01326           /* Check if click is on industry label*/
    01327           int industry_pos = (column * number_of_rows) + line;
    01328           if (industry_pos < _smallmap_industry_count) {
    01329             if (_ctrl_pressed) {
    01330               /* Disable all, except the clicked one */
    01331               bool changes = false;
    01332               for (int i = 0; i != _smallmap_industry_count; i++) {
    01333                 bool new_state = i == industry_pos;
    01334                 if (_legend_from_industries[i].show_on_map != new_state) {
    01335                   changes = true;
    01336                   _legend_from_industries[i].show_on_map = new_state;
    01337                 }
    01338               }
    01339               if (!changes) {
    01340                 /* Nothing changed? Then show all (again). */
    01341                 for (int i = 0; i != _smallmap_industry_count; i++) {
    01342                   _legend_from_industries[i].show_on_map = true;
    01343                 }
    01344               }
    01345             } else {
    01346               _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
    01347             }
    01348           }
    01349           this->SetDirty();
    01350         } else if (this->map_type == SMT_OWNER) {
    01351           /* If click on companies label, find right company and enable/disable it. */
    01352           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND);
    01353           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
    01354           uint columns = this->GetNumberColumnsLegend(wi->current_x);
    01355           uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns), this->min_number_of_fixed_rows);
    01356           if (line >= number_of_rows) break;
    01357
    01358           bool rtl = _current_text_dir == TD_RTL;
    01359           int x = pt.x - wi->pos_x;
    01360           if (rtl) x = wi->current_x - x;
    01361           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
    01362
    01363           /* Check if click is on company label. */
    01364           int company_pos = (column * number_of_rows) + line;
    01365           if (company_pos < NUM_NO_COMPANY_ENTRIES) break;
    01366           if (company_pos < _smallmap_company_count) {
    01367             if (_ctrl_pressed) {
    01368               /* Disable all, except the clicked one */
    01369               bool changes = false;
    01370               for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
    01371                 bool new_state = i == company_pos;
    01372                 if (_legend_land_owners[i].show_on_map != new_state) {
    01373                   changes = true;
    01374                   _legend_land_owners[i].show_on_map = new_state;
    01375                 }
    01376               }
    01377               if (!changes) {
    01378                 /* Nothing changed? Then show all (again). */
    01379                 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
    01380                   _legend_land_owners[i].show_on_map = true;
    01381                 }
    01382               }
    01383             } else {
    01384               _legend_land_owners[company_pos].show_on_map = !_legend_land_owners[company_pos].show_on_map;
    01385             }
    01386           }
    01387           this->SetDirty();
    01388         }
    01389         break;
    01390
    01391       case SM_WIDGET_ENABLE_ALL:
    01392         if (this->map_type == SMT_INDUSTRY) {
    01393           for (int i = 0; i != _smallmap_industry_count; i++) {
    01394             _legend_from_industries[i].show_on_map = true;
    01395           }
    01396         } else if (this->map_type == SMT_OWNER) {
    01397           for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
    01398             _legend_land_owners[i].show_on_map = true;
    01399           }
    01400         }
    01401         this->SetDirty();
    01402         break;
    01403
    01404       case SM_WIDGET_DISABLE_ALL:
    01405         if (this->map_type == SMT_INDUSTRY) {
    01406           for (int i = 0; i != _smallmap_industry_count; i++) {
    01407             _legend_from_industries[i].show_on_map = false;
    01408           }
    01409         } else {
    01410           for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
    01411             _legend_land_owners[i].show_on_map = false;
    01412           }
    01413         }
    01414         this->SetDirty();
    01415         break;
    01416
    01417       case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
    01418         _smallmap_show_heightmap = !_smallmap_show_heightmap;
    01419         this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
    01420         this->SetDirty();
    01421         break;
    01422     }
    01423   }
    01424
    01432   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    01433   {
    01434     if (!gui_scope) return;
    01435     switch (data) {
    01436       case 1:
    01437         /* The owner legend has already been rebuilt. */
    01438         this->ReInit();
    01439         break;
    01440
    01441       case 0: {
    01442         extern uint64 _displayed_industries;
    01443         if (this->map_type != SMT_INDUSTRY) this->SwitchMapType(SMT_INDUSTRY);
    01444
    01445         for (int i = 0; i != _smallmap_industry_count; i++) {
    01446           _legend_from_industries[i].show_on_map = HasBit(_displayed_industries, _legend_from_industries[i].type);
    01447         }
    01448         break;
    01449       }
    01450
    01451       default: NOT_REACHED();
    01452     }
    01453     this->SetDirty();
    01454   }
    01455
    01456   virtual bool OnRightClick(Point pt, int widget)
    01457   {
    01458     if (widget != SM_WIDGET_MAP || _scrolling_viewport) return false;
    01459
    01460     _scrolling_viewport = true;
    01461     return true;
    01462   }
    01463
    01464   virtual void OnMouseWheel(int wheel)
    01465   {
    01466     if (_settings_client.gui.scrollwheel_scrolling == 0) {
    01467       const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01468       int cursor_x = _cursor.pos.x - this->left - wid->pos_x;
    01469       int cursor_y = _cursor.pos.y - this->top  - wid->pos_y;
    01470       if (IsInsideMM(cursor_x, 0, wid->current_x) && IsInsideMM(cursor_y, 0, wid->current_y)) {
    01471         Point pt = {cursor_x, cursor_y};
    01472         this->SetZoomLevel((wheel < 0) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
    01473       }
    01474     }
    01475   }
    01476
    01477   virtual void OnTick()
    01478   {
    01479     /* Update the window every now and then */
    01480     if (--this->refresh != 0) return;
    01481
    01482     this->refresh = FORCE_REFRESH_PERIOD;
    01483     this->SetDirty();
    01484   }
    01485
    01493   void SetNewScroll(int sx, int sy, int sub)
    01494   {
    01495     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01496     Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
    01497     hv.x *= this->zoom;
    01498     hv.y *= this->zoom;
    01499
    01500     if (sx < -hv.x) {
    01501       sx = -hv.x;
    01502       sub = 0;
    01503     }
    01504     if (sx > (int)(MapMaxX() * TILE_SIZE) - hv.x) {
    01505       sx = MapMaxX() * TILE_SIZE - hv.x;
    01506       sub = 0;
    01507     }
    01508     if (sy < -hv.y) {
    01509       sy = -hv.y;
    01510       sub = 0;
    01511     }
    01512     if (sy > (int)(MapMaxY() * TILE_SIZE) - hv.y) {
    01513       sy = MapMaxY() * TILE_SIZE - hv.y;
    01514       sub = 0;
    01515     }
    01516
    01517     this->scroll_x = sx;
    01518     this->scroll_y = sy;
    01519     this->subscroll = sub;
    01520   }
    01521
    01522   virtual void OnScroll(Point delta)
    01523   {
    01524     _cursor.fix_at = true;
    01525
    01526     /* While tile is at (delta.x, delta.y)? */
    01527     int sub;
    01528     Point pt = this->PixelToTile(delta.x, delta.y, &sub);
    01529     this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
    01530
    01531     this->SetDirty();
    01532   }
    01533
    01534   void SmallMapCenterOnCurrentPos()
    01535   {
    01536     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    01537     Point pt = InverseRemapCoords(vp->virtual_left + vp->virtual_width  / 2, vp->virtual_top  + vp->virtual_height / 2);
    01538
    01539     int sub;
    01540     const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01541     Point sxy = this->ComputeScroll(pt.x / TILE_SIZE, pt.y / TILE_SIZE, max(0, (int)wid->current_x / 2 - 2), wid->current_y / 2, &sub);
    01542     this->SetNewScroll(sxy.x, sxy.y, sub);
    01543     this->SetDirty();
    01544   }
    01545 };
    01546
    01547 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
    01548 bool SmallMapWindow::show_towns = true;
    01549
    01558 class NWidgetSmallmapDisplay : public NWidgetContainer {
    01559   const SmallMapWindow *smallmap_window;
    01560 public:
    01561   NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
    01562   {
    01563     this->smallmap_window = NULL;
    01564   }
    01565
    01566   virtual void SetupSmallestSize(Window *w, bool init_array)
    01567   {
    01568     NWidgetBase *display = this->head;
    01569     NWidgetBase *bar = display->next;
    01570
    01571     display->SetupSmallestSize(w, init_array);
    01572     bar->SetupSmallestSize(w, init_array);
    01573
    01574     this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
    01575     this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
    01576     this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetLegendHeight(smallmap_window->min_number_of_columns));
    01577     this->fill_x = max(display->fill_x, bar->fill_x);
    01578     this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
    01579     this->resize_x = max(display->resize_x, bar->resize_x);
    01580     this->resize_y = min(display->resize_y, bar->resize_y);
    01581   }
    01582
    01583   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
    01584   {
    01585     this->pos_x = x;
    01586     this->pos_y = y;
    01587     this->current_x = given_width;
    01588     this->current_y = given_height;
    01589
    01590     NWidgetBase *display = this->head;
    01591     NWidgetBase *bar = display->next;
    01592
    01593     if (sizing == ST_SMALLEST) {
    01594       this->smallest_x = given_width;
    01595       this->smallest_y = given_height;
    01596       /* Make display and bar exactly equal to their minimal size. */
    01597       display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
    01598       bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
    01599     }
    01600
    01601     uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(this->smallmap_window->GetNumberColumnsLegend(given_width - bar->smallest_x)));
    01602     uint display_height = given_height - bar_height;
    01603     display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
    01604     bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
    01605   }
    01606
    01607   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
    01608   {
    01609     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
    01610     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    01611       NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
    01612       if (widget != NULL) return widget;
    01613     }
    01614     return NULL;
    01615   }
    01616
    01617   virtual void Draw(const Window *w)
    01618   {
    01619     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
    01620   }
    01621 };
    01622
    01624 static const NWidgetPart _nested_smallmap_display[] = {
    01625   NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
    01626     NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
    01627   EndContainer(),
    01628 };
    01629
    01631 static const NWidgetPart _nested_smallmap_bar[] = {
    01632   NWidget(WWT_PANEL, COLOUR_BROWN),
    01633     NWidget(NWID_HORIZONTAL),
    01634       NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
    01635       NWidget(NWID_VERTICAL),
    01636         /* Top button row. */
    01637         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01638           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN),
    01639               SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), SetFill(1, 1),
    01640           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP),
    01641               SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER), SetFill(1, 1),
    01642           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR),
    01643               SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP), SetFill(1, 1),
    01644           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES),
    01645               SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP), SetFill(1, 1),
    01646           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES),
    01647               SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP), SetFill(1, 1),
    01648         EndContainer(),
    01649         /* Bottom button row. */
    01650         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01651           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT),
    01652               SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), SetFill(1, 1),
    01653           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME),
    01654               SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF), SetFill(1, 1),
    01655           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES),
    01656               SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON), SetFill(1, 1),
    01657           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION),
    01658               SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP), SetFill(1, 1),
    01659           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS),
    01660               SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP), SetFill(1, 1),
    01661         EndContainer(),
    01662         NWidget(NWID_SPACER), SetResize(0, 1),
    01663       EndContainer(),
    01664     EndContainer(),
    01665   EndContainer(),
    01666 };
    01667
    01668 static NWidgetBase *SmallMapDisplay(int *biggest_index)
    01669 {
    01670   NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
    01671
    01672   MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
    01673   MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
    01674   return map_display;
    01675 }
    01676
    01677
    01678 static const NWidgetPart _nested_smallmap_widgets[] = {
    01679   NWidget(NWID_HORIZONTAL),
    01680     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    01681     NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01682     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    01683     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    01684   EndContainer(),
    01685   NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
    01686   /* Bottom button row and resize box. */
    01687   NWidget(NWID_HORIZONTAL),
    01688     NWidget(WWT_PANEL, COLOUR_BROWN),
    01689       NWidget(NWID_HORIZONTAL),
    01690         NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECT_BUTTONS),
    01691           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01692             NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_NULL),
    01693             NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_NULL),
    01694             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
    01695           EndContainer(),
    01696           NWidget(NWID_SPACER), SetFill(1, 1),
    01697         EndContainer(),
    01698         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
    01699       EndContainer(),
    01700     EndContainer(),
    01701     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    01702   EndContainer(),
    01703 };
    01704
    01705 static const WindowDesc _smallmap_desc(
    01706   WDP_AUTO, 446, 314,
    01707   WC_SMALLMAP, WC_NONE,
    01708   WDF_UNCLICK_BUTTONS,
    01709   _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
    01710 );
    01711
    01712 void ShowSmallMap()
    01713 {
    01714   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
    01715 }
    01716
    01725 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
    01726 {
    01727   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
    01728
    01729   /* If a user scrolls to a tile (via what way what so ever) and already is on
    01730    * that tile (e.g.: pressed twice), move the smallmap to that location,
    01731    * so you directly see where you are on the smallmap. */
    01732
    01733   if (res) return res;
    01734
    01735   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
    01736   if (w != NULL) w->SmallMapCenterOnCurrentPos();
    01737
    01738   return res;
    01739 }