# smallmap_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: smallmap_gui.cpp 19552 2010-04-03 19:57:23Z rubidium $ */
    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 #include "table/sprites.h"
    00032
    00034 enum SmallMapWindowWidgets {
    00035   SM_WIDGET_CAPTION,
    00036   SM_WIDGET_MAP_BORDER,
    00037   SM_WIDGET_MAP,
    00038   SM_WIDGET_LEGEND,
    00039   SM_WIDGET_ZOOM_IN,
    00040   SM_WIDGET_ZOOM_OUT,
    00041   SM_WIDGET_CONTOUR,
    00042   SM_WIDGET_VEHICLES,
    00043   SM_WIDGET_INDUSTRIES,
    00044   SM_WIDGET_ROUTES,
    00045   SM_WIDGET_VEGETATION,
    00046   SM_WIDGET_OWNERS,
    00047   SM_WIDGET_CENTERMAP,
    00048   SM_WIDGET_TOGGLETOWNNAME,
    00049   SM_WIDGET_SELECTINDUSTRIES,
    00050   SM_WIDGET_ENABLEINDUSTRIES,
    00051   SM_WIDGET_DISABLEINDUSTRIES,
    00052   SM_WIDGET_SHOW_HEIGHT,
    00053 };
    00054
    00055 static int _smallmap_industry_count;
    00056
    00058 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, false}
    00059
    00060 #define MC(b) MK(0, b)
    00061
    00062 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, true, true, false}
    00063
    00065 #define MS(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, true}
    00066
    00068 struct LegendAndColour {
    00069   uint8 colour;
    00070   StringID legend;
    00071   IndustryType type;
    00072   bool show_on_map;
    00073   bool end;
    00074   bool col_break;
    00075 };
    00076
    00078 static LegendAndColour _legend_land_contours[] = {
    00079   /* The colours for the following values are set at BuildLandLegend() based on each colour scheme. */
    00080   MC(STR_SMALLMAP_LEGENDA_100M),
    00081   MC(STR_SMALLMAP_LEGENDA_200M),
    00082   MC(STR_SMALLMAP_LEGENDA_300M),
    00083   MC(STR_SMALLMAP_LEGENDA_400M),
    00084   MC(STR_SMALLMAP_LEGENDA_500M),
    00085
    00086   MS(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
    00087   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
    00088   MK(0x98, STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
    00089   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00090   MK(0x0F, STR_SMALLMAP_LEGENDA_VEHICLES),
    00091   MKEND()
    00092 };
    00093
    00094 static const LegendAndColour _legend_vehicles[] = {
    00095   MK(0xB8, STR_SMALLMAP_LEGENDA_TRAINS),
    00096   MK(0xBF, STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
    00097   MK(0x98, STR_SMALLMAP_LEGENDA_SHIPS),
    00098   MK(0x0F, STR_SMALLMAP_LEGENDA_AIRCRAFT),
    00099
    00100   MS(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
    00101   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00102   MKEND()
    00103 };
    00104
    00105 static const LegendAndColour _legend_routes[] = {
    00106   MK(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
    00107   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
    00108   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00109
    00110   MS(0x56, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
    00111   MK(0xC2, STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
    00112   MK(0xBF, STR_SMALLMAP_LEGENDA_BUS_STATION),
    00113   MK(0xB8, STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
    00114   MK(0x98, STR_SMALLMAP_LEGENDA_DOCK),
    00115   MKEND()
    00116 };
    00117
    00118 static const LegendAndColour _legend_vegetation[] = {
    00119   MK(0x52, STR_SMALLMAP_LEGENDA_ROUGH_LAND),
    00120   MK(0x54, STR_SMALLMAP_LEGENDA_GRASS_LAND),
    00121   MK(0x37, STR_SMALLMAP_LEGENDA_BARE_LAND),
    00122   MK(0x25, STR_SMALLMAP_LEGENDA_FIELDS),
    00123   MK(0x57, STR_SMALLMAP_LEGENDA_TREES),
    00124   MK(0xD0, STR_SMALLMAP_LEGENDA_FOREST),
    00125
    00126   MS(0x0A, STR_SMALLMAP_LEGENDA_ROCKS),
    00127   MK(0xC2, STR_SMALLMAP_LEGENDA_DESERT),
    00128   MK(0x98, STR_SMALLMAP_LEGENDA_SNOW),
    00129   MK(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
    00130   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00131   MKEND()
    00132 };
    00133
    00134 static const LegendAndColour _legend_land_owners[] = {
    00135   MK(0xCA, STR_SMALLMAP_LEGENDA_WATER),
    00136   MK(0x54, STR_SMALLMAP_LEGENDA_NO_OWNER),
    00137   MK(0xB4, STR_SMALLMAP_LEGENDA_TOWNS),
    00138   MK(0x20, STR_SMALLMAP_LEGENDA_INDUSTRIES),
    00139   MKEND()
    00140 };
    00141 #undef MK
    00142 #undef MC
    00143 #undef MS
    00144 #undef MKEND
    00145
    00148 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
    00149 /* For connecting industry type to position in industries list(small map legend) */
    00150 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
    00152 static bool _smallmap_industry_show_heightmap;
    00153
    00157 void BuildIndustriesLegend()
    00158 {
    00159   uint j = 0;
    00160
    00161   /* Add each name */
    00162   for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
    00163     const IndustrySpec *indsp = GetIndustrySpec(i);
    00164     if (indsp->enabled) {
    00165       _legend_from_industries[j].legend = indsp->name;
    00166       _legend_from_industries[j].colour = indsp->map_colour;
    00167       _legend_from_industries[j].type = i;
    00168       _legend_from_industries[j].show_on_map = true;
    00169       _legend_from_industries[j].col_break = false;
    00170       _legend_from_industries[j].end = false;
    00171
    00172       /* Store widget number for this industry type */
    00173       _industry_to_list_pos[i] = j;
    00174       j++;
    00175     }
    00176   }
    00177   /* Terminate the list */
    00178   _legend_from_industries[j].end = true;
    00179
    00180   /* Store number of enabled industries */
    00181   _smallmap_industry_count = j;
    00182 }
    00183
    00184 static const LegendAndColour * const _legend_table[] = {
    00185   _legend_land_contours,
    00186   _legend_vehicles,
    00187   _legend_from_industries,
    00188   _legend_routes,
    00189   _legend_vegetation,
    00190   _legend_land_owners,
    00191 };
    00192
    00193 #define MKCOLOUR(x) TO_LE32X(x)
    00194
    00196 static const uint32 _green_map_heights[] = {
    00197   MKCOLOUR(0x5A5A5A5A),
    00198   MKCOLOUR(0x5A5B5A5B),
    00199   MKCOLOUR(0x5B5B5B5B),
    00200   MKCOLOUR(0x5B5C5B5C),
    00201   MKCOLOUR(0x5C5C5C5C),
    00202   MKCOLOUR(0x5C5D5C5D),
    00203   MKCOLOUR(0x5D5D5D5D),
    00204   MKCOLOUR(0x5D5E5D5E),
    00205   MKCOLOUR(0x5E5E5E5E),
    00206   MKCOLOUR(0x5E5F5E5F),
    00207   MKCOLOUR(0x5F5F5F5F),
    00208   MKCOLOUR(0x5F1F5F1F),
    00209   MKCOLOUR(0x1F1F1F1F),
    00210   MKCOLOUR(0x1F271F27),
    00211   MKCOLOUR(0x27272727),
    00212   MKCOLOUR(0x27272727),
    00213 };
    00214 assert_compile(lengthof(_green_map_heights) == MAX_TILE_HEIGHT + 1);
    00215
    00217 static const uint32 _dark_green_map_heights[] = {
    00218   MKCOLOUR(0x60606060),
    00219   MKCOLOUR(0x60616061),
    00220   MKCOLOUR(0x61616161),
    00221   MKCOLOUR(0x61626162),
    00222   MKCOLOUR(0x62626262),
    00223   MKCOLOUR(0x62636263),
    00224   MKCOLOUR(0x63636363),
    00225   MKCOLOUR(0x63646364),
    00226   MKCOLOUR(0x64646464),
    00227   MKCOLOUR(0x64656465),
    00228   MKCOLOUR(0x65656565),
    00229   MKCOLOUR(0x65666566),
    00230   MKCOLOUR(0x66666666),
    00231   MKCOLOUR(0x66676667),
    00232   MKCOLOUR(0x67676767),
    00233   MKCOLOUR(0x67676767),
    00234 };
    00235 assert_compile(lengthof(_dark_green_map_heights) == MAX_TILE_HEIGHT + 1);
    00236
    00238 static const uint32 _violet_map_heights[] = {
    00239   MKCOLOUR(0x80808080),
    00240   MKCOLOUR(0x80818081),
    00241   MKCOLOUR(0x81818181),
    00242   MKCOLOUR(0x81828182),
    00243   MKCOLOUR(0x82828282),
    00244   MKCOLOUR(0x82838283),
    00245   MKCOLOUR(0x83838383),
    00246   MKCOLOUR(0x83848384),
    00247   MKCOLOUR(0x84848484),
    00248   MKCOLOUR(0x84858485),
    00249   MKCOLOUR(0x85858585),
    00250   MKCOLOUR(0x85868586),
    00251   MKCOLOUR(0x86868686),
    00252   MKCOLOUR(0x86878687),
    00253   MKCOLOUR(0x87878787),
    00254   MKCOLOUR(0x87878787),
    00255 };
    00256 assert_compile(lengthof(_violet_map_heights) == MAX_TILE_HEIGHT + 1);
    00257
    00259 struct SmallMapColourScheme {
    00260   const uint32 *height_colours;
    00261   uint32 default_colour;
    00262 };
    00263
    00265 static const SmallMapColourScheme _heightmap_schemes[] = {
    00266   {_green_map_heights,      MKCOLOUR(0x54545454)},
    00267   {_dark_green_map_heights, MKCOLOUR(0x62626262)},
    00268   {_violet_map_heights,     MKCOLOUR(0x82828282)},
    00269 };
    00270
    00271 void BuildLandLegend()
    00272 {
    00273   _legend_land_contours[0].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[0];
    00274   _legend_land_contours[1].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[4];
    00275   _legend_land_contours[2].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[8];
    00276   _legend_land_contours[3].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[12];
    00277   _legend_land_contours[4].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[14];
    00278 }
    00279
    00280 struct AndOr {
    00281   uint32 mor;
    00282   uint32 mand;
    00283 };
    00284
    00285 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
    00286 {
    00287   return (colour & mask->mand) | mask->mor;
    00288 }
    00289
    00290
    00292 static const AndOr _smallmap_contours_andor[] = {
    00293   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
    00294   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
    00295   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
    00296   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
    00297   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
    00298   {MKCOLOUR(0x98989898), MKCOLOUR(0x00000000)}, // MP_STATION
    00299   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
    00300   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
    00301   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
    00302   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
    00303   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_UNMOVABLE
    00304   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
    00305 };
    00306
    00308 static const AndOr _smallmap_vehicles_andor[] = {
    00309   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
    00310   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
    00311   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
    00312   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
    00313   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
    00314   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_STATION
    00315   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
    00316   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
    00317   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
    00318   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
    00319   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_UNMOVABLE
    00320   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00321 };
    00322
    00324 static const byte _tiletype_importance[] = {
    00325   2, // MP_CLEAR
    00326   8, // MP_RAILWAY
    00327   7, // MP_ROAD
    00328   5, // MP_HOUSE
    00329   2, // MP_TREES
    00330   9, // MP_STATION
    00331   2, // MP_WATER
    00332   1, // MP_VOID
    00333   6, // MP_INDUSTRY
    00334   8, // MP_TUNNELBRIDGE
    00335   2, // MP_UNMOVABLE
    00336   0,
    00337 };
    00338
    00339
    00340 static inline TileType GetEffectiveTileType(TileIndex tile)
    00341 {
    00342   TileType t = GetTileType(tile);
    00343
    00344   if (t == MP_TUNNELBRIDGE) {
    00345     TransportType tt = GetTunnelBridgeTransportType(tile);
    00346
    00347     switch (tt) {
    00348       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
    00349       case TRANSPORT_ROAD: t = MP_ROAD;    break;
    00350       default:             t = MP_WATER;   break;
    00351     }
    00352   }
    00353   return t;
    00354 }
    00355
    00362 static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t)
    00363 {
    00364   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00365   return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[t]);
    00366 }
    00367
    00375 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
    00376 {
    00377   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00378   return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[t]);
    00379 }
    00380
    00388 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
    00389 {
    00390   if (t == MP_INDUSTRY) {
    00391     /* If industry is allowed to be seen, use its colour on the map */
    00392     if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
    00393       return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
    00394     } else {
    00395       /* Otherwise, return the colour which will make it disappear */
    00396       t = (GetWaterClass(tile) == WATER_CLASS_INVALID) ? MP_CLEAR : MP_WATER;
    00397     }
    00398   }
    00399
    00400   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00401   return ApplyMask(_smallmap_industry_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
    00402 }
    00403
    00411 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t)
    00412 {
    00413   if (t == MP_STATION) {
    00414     switch (GetStationType(tile)) {
    00415       case STATION_RAIL:    return MKCOLOUR(0x56565656);
    00416       case STATION_AIRPORT: return MKCOLOUR(0xB8B8B8B8);
    00417       case STATION_TRUCK:   return MKCOLOUR(0xC2C2C2C2);
    00418       case STATION_BUS:     return MKCOLOUR(0xBFBFBFBF);
    00419       case STATION_DOCK:    return MKCOLOUR(0x98989898);
    00420       default:              return MKCOLOUR(0xFFFFFFFF);
    00421     }
    00422   } else if (t == MP_RAILWAY) {
    00423     AndOr andor = {
    00424       GetRailTypeInfo(GetRailType(tile))->map_colour * MKCOLOUR(0x00010100),
    00425       _smallmap_contours_andor[t].mand
    00426     };
    00427
    00428     const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00429     return ApplyMask(cs->default_colour, &andor);
    00430   }
    00431
    00432   /* Ground colour */
    00433   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
    00434   return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
    00435 }
    00436
    00437
    00438 static const uint32 _vegetation_clear_bits[] = {
    00439   MKCOLOUR(0x54545454),
    00440   MKCOLOUR(0x52525252),
    00441   MKCOLOUR(0x0A0A0A0A),
    00442   MKCOLOUR(0x25252525),
    00443   MKCOLOUR(0x98989898),
    00444   MKCOLOUR(0xC2C2C2C2),
    00445   MKCOLOUR(0x54545454),
    00446   MKCOLOUR(0x54545454),
    00447 };
    00448
    00456 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile, TileType t)
    00457 {
    00458   switch (t) {
    00459     case MP_CLEAR:
    00460       return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR(0x37373737) : _vegetation_clear_bits[GetClearGround(tile)];
    00461
    00462     case MP_INDUSTRY:
    00463       return GetIndustrySpec(Industry::GetByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR(0xD0D0D0D0) : MKCOLOUR(0xB5B5B5B5);
    00464
    00465     case MP_TREES:
    00466       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(tile) == TREE_GROUND_ROUGH_SNOW) {
    00467         return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR(0x98575798) : MKCOLOUR(0xC25757C2);
    00468       }
    00469       return MKCOLOUR(0x54575754);
    00470
    00471     default:
    00472       return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00473   }
    00474 }
    00475
    00476
    00477 static uint32 _owner_colours[OWNER_END + 1];
    00478
    00486 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t)
    00487 {
    00488   Owner o;
    00489
    00490   switch (t) {
    00491     case MP_INDUSTRY: o = OWNER_END;          break;
    00492     case MP_HOUSE:    o = OWNER_TOWN;         break;
    00493     default:          o = GetTileOwner(tile); break;
    00494     /* FIXME: For MP_ROAD there are multiple owners.
    00495      * GetTileOwner returns the rail owner (level crossing) resp. the owner of ROADTYPE_ROAD (normal road),
    00496      * even if there are no ROADTYPE_ROAD bits on the tile.
    00497      */
    00498   }
    00499
    00500   return _owner_colours[o];
    00501 }
    00502
    00504 static const byte _vehicle_type_colours[6] = {
    00505   184, 191, 152, 15, 215, 184
    00506 };
    00507
    00508
    00510 class SmallMapWindow : public Window {
    00512   enum SmallMapType {
    00513     SMT_CONTOUR,
    00514     SMT_VEHICLES,
    00515     SMT_INDUSTRY,
    00516     SMT_ROUTES,
    00517     SMT_VEGETATION,
    00518     SMT_OWNER,
    00519   };
    00520
    00522   enum ZoomLevelChange {
    00523     ZLC_INITIALIZE,
    00524     ZLC_ZOOM_OUT,
    00525     ZLC_ZOOM_IN,
    00526   };
    00527
    00528   static SmallMapType map_type;
    00529   static bool show_towns;
    00530
    00531   static const uint LEGEND_BLOB_WIDTH = 8;
    00532   static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
    00533   uint min_number_of_columns;
    00534   uint min_number_of_fixed_rows;
    00535   uint column_width;
    00536
    00537   int32 scroll_x;
    00538   int32 scroll_y;
    00539   int32 subscroll;
    00540   int zoom;
    00541
    00542   static const uint8 FORCE_REFRESH_PERIOD = 0x1F;
    00543   uint8 refresh;
    00544
    00551   FORCEINLINE Point RemapTile(int tile_x, int tile_y) const
    00552   {
    00553     int x_offset = tile_x - this->scroll_x / TILE_SIZE;
    00554     int y_offset = tile_y - this->scroll_y / TILE_SIZE;
    00555
    00556     if (this->zoom == 1) return RemapCoords(x_offset, y_offset, 0);
    00557
    00558     /* For negative offsets, round towards -inf. */
    00559     if (x_offset < 0) x_offset -= this->zoom - 1;
    00560     if (y_offset < 0) y_offset -= this->zoom - 1;
    00561
    00562     return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
    00563   }
    00564
    00575   FORCEINLINE Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
    00576   {
    00577     if (add_sub) px += this->subscroll;  // Total horizontal offset.
    00578
    00579     /* For each two rows down, add a x and a y tile, and
    00580      * For each four pixels to the right, move a tile to the right. */
    00581     Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
    00582     px &= 3;
    00583
    00584     if (py & 1) { // Odd number of rows, handle the 2 pixel shift.
    00585       if (px < 2) {
    00586         pt.x += this->zoom;
    00587         px += 2;
    00588       } else {
    00589         pt.y += this->zoom;
    00590         px -= 2;
    00591       }
    00592     }
    00593
    00594     *sub = px;
    00595     return pt;
    00596   }
    00597
    00607   Point ComputeScroll(int tx, int ty, int x, int y, int *sub)
    00608   {
    00609     assert(x >= 0 && y >= 0);
    00610
    00611     int new_sub;
    00612     Point tile_xy = PixelToTile(x, y, &new_sub, false);
    00613     tx -= tile_xy.x;
    00614     ty -= tile_xy.y;
    00615
    00616     Point scroll;
    00617     if (new_sub == 0) {
    00618       *sub = 0;
    00619       scroll.x = (tx + this->zoom) * TILE_SIZE;
    00620       scroll.y = (ty - this->zoom) * TILE_SIZE;
    00621     } else {
    00622       *sub = 4 - new_sub;
    00623       scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
    00624       scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
    00625     }
    00626     return scroll;
    00627   }
    00628
    00634   void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
    00635   {
    00636     static const int zoomlevels[] = {1, 2, 4, 6, 8}; // Available zoom levels. Bigger number means more zoom-out (further away).
    00637     static const int MIN_ZOOM_INDEX = 0;
    00638     static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
    00639
    00640     int new_index, cur_index, sub;
    00641     Point tile;
    00642     switch (change) {
    00643       case ZLC_INITIALIZE:
    00644         cur_index = - 1; // Definitely different from new_index.
    00645         new_index = MIN_ZOOM_INDEX;
    00646         break;
    00647
    00648       case ZLC_ZOOM_IN:
    00649       case ZLC_ZOOM_OUT:
    00650         for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
    00651           if (this->zoom == zoomlevels[cur_index]) break;
    00652         }
    00653         assert(cur_index <= MAX_ZOOM_INDEX);
    00654
    00655         tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
    00656         new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
    00657         break;
    00658
    00659       default: NOT_REACHED();
    00660     }
    00661
    00662     if (new_index != cur_index) {
    00663       this->zoom = zoomlevels[new_index];
    00664       if (cur_index >= 0) {
    00665         Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
    00666         this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
    00667             this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
    00668       }
    00669       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN,  this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
    00670       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
    00671       this->SetDirty();
    00672     }
    00673   }
    00674
    00680   inline uint32 GetTileColours(const TileArea &ta) const
    00681   {
    00682     int importance = 0;
    00683     TileIndex tile = INVALID_TILE; // Position of the most important tile.
    00684     TileType et = MP_VOID;         // Effective tile type at that position.
    00685
    00686     TILE_AREA_LOOP(ti, ta) {
    00687       TileType ttype = GetEffectiveTileType(ti);
    00688       if (_tiletype_importance[ttype] > importance) {
    00689         importance = _tiletype_importance[ttype];
    00690         tile = ti;
    00691         et = ttype;
    00692       }
    00693     }
    00694
    00695     switch (this->map_type) {
    00696       case SMT_CONTOUR:
    00697         return GetSmallMapContoursPixels(tile, et);
    00698
    00699       case SMT_VEHICLES:
    00700         return GetSmallMapVehiclesPixels(tile, et);
    00701
    00702       case SMT_INDUSTRY:
    00703         return GetSmallMapIndustriesPixels(tile, et);
    00704
    00705       case SMT_ROUTES:
    00706         return GetSmallMapRoutesPixels(tile, et);
    00707
    00708       case SMT_VEGETATION:
    00709         return GetSmallMapVegetationPixels(tile, et);
    00710
    00711       case SMT_OWNER:
    00712         return GetSmallMapOwnerPixels(tile, et);
    00713
    00714       default: NOT_REACHED();
    00715     }
    00716   }
    00717
    00732   void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
    00733   {
    00734     void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
    00735     uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
    00736
    00737     do {
    00738       /* Check if the tile (xc,yc) is within the map range */
    00739       if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
    00740
    00741       /* Check if the dst pointer points to a pixel inside the screen buffer */
    00742       if (dst < _screen.dst_ptr) continue;
    00743       if (dst >= dst_ptr_abs_end) continue;
    00744
    00745       /* Construct tilearea covered by (xc, yc, xc + this->zoom, yc + this->zoom) such that it is within min_xy limits. */
    00746       TileArea ta;
    00747       if (min_xy == 1 && (xc == 0 || yc == 0)) {
    00748         if (this->zoom == 1) continue; // The tile area is empty, don't draw anything.
    00749
    00750         ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
    00751       } else {
    00752         ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
    00753       }
    00754       ta.ClampToMap(); // Clamp to map boundaries (may contain MP_VOID tiles!).
    00755
    00756       uint32 val = this->GetTileColours(ta);
    00757       uint8 *val8 = (uint8 *)&val;
    00758       int idx = max(0, -start_pos);
    00759       for (int pos = max(0, start_pos); pos < end_pos; pos++) {
    00760         blitter->SetPixel(dst, idx, 0, val8[idx]);
    00761         idx++;
    00762       }
    00763     /* Switch to next tile in the column */
    00764     } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
    00765   }
    00766
    00772   void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
    00773   {
    00774     const Vehicle *v;
    00775     FOR_ALL_VEHICLES(v) {
    00776       if (v->type == VEH_EFFECT) continue;
    00777       if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
    00778
    00779       /* Remap into flat coordinates. */
    00780       Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
    00781
    00782       int y = pt.y - dpi->top;
    00783       if (!IsInsideMM(y, 0, dpi->height)) continue; // y is out of bounds.
    00784
    00785       bool skip = false; // Default is to draw both pixels.
    00786       int x = pt.x - this->subscroll - 3 - dpi->left; // Offset X coordinate.
    00787       if (x < 0) {
    00788         /* if x+1 is 0, that means we're on the very left edge,
    00789          * and should thus only draw a single pixel */
    00790         if (++x != 0) continue;
    00791         skip = true;
    00792       } else if (x >= dpi->width - 1) {
    00793         /* Check if we're at the very right edge, and if so draw only a single pixel */
    00794         if (x != dpi->width - 1) continue;
    00795         skip = true;
    00796       }
    00797
    00798       /* Calculate pointer to pixel and the colour */
    00799       byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
    00800
    00801       /* And draw either one or two pixels depending on clipping */
    00802       blitter->SetPixel(dpi->dst_ptr, x, y, colour);
    00803       if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
    00804     }
    00805   }
    00806
    00811   void DrawTowns(const DrawPixelInfo *dpi) const
    00812   {
    00813     const Town *t;
    00814     FOR_ALL_TOWNS(t) {
    00815       /* Remap the town coordinate */
    00816       Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
    00817       int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
    00818       int y = pt.y;
    00819
    00820       /* Check if the town sign is within bounds */
    00821       if (x + t->sign.width_small > dpi->left &&
    00822           x < dpi->left + dpi->width &&
    00823           y + FONT_HEIGHT_SMALL > dpi->top &&
    00824           y < dpi->top + dpi->height) {
    00825         /* And draw it. */
    00826         SetDParam(0, t->index);
    00827         DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
    00828       }
    00829     }
    00830   }
    00831
    00838   static inline void DrawVertMapIndicator(int x, int y, int y2)
    00839   {
    00840     GfxFillRect(x, y,      x, y + 3, 69);
    00841     GfxFillRect(x, y2 - 3, x, y2,    69);
    00842   }
    00843
    00850   static inline void DrawHorizMapIndicator(int x, int x2, int y)
    00851   {
    00852     GfxFillRect(x,      y, x + 3, y, 69);
    00853     GfxFillRect(x2 - 3, y, x2,    y, 69);
    00854   }
    00855
    00859   void DrawMapIndicators() const
    00860   {
    00861     /* Find main viewport. */
    00862     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    00863
    00864     Point tile = InverseRemapCoords(vp->virtual_left, vp->virtual_top);
    00865     Point tl = this->RemapTile(tile.x >> 4, tile.y >> 4);
    00866     tl.x -= this->subscroll;
    00867
    00868     tile = InverseRemapCoords(vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height);
    00869     Point br = this->RemapTile(tile.x >> 4, tile.y >> 4);
    00870     br.x -= this->subscroll;
    00871
    00872     SmallMapWindow::DrawVertMapIndicator(tl.x, tl.y, br.y);
    00873     SmallMapWindow::DrawVertMapIndicator(br.x, tl.y, br.y);
    00874
    00875     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, tl.y);
    00876     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, br.y);
    00877   }
    00878
    00890   void DrawSmallMap(DrawPixelInfo *dpi) const
    00891   {
    00892     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00893     DrawPixelInfo *old_dpi;
    00894
    00895     old_dpi = _cur_dpi;
    00896     _cur_dpi = dpi;
    00897
    00898     /* Clear it */
    00899     GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
    00900
    00901     /* Setup owner table */
    00902     if (this->map_type == SMT_OWNER) {
    00903       const Company *c;
    00904
    00905       /* Fill with some special colours */
    00906       _owner_colours[OWNER_TOWN]  = MKCOLOUR(0xB4B4B4B4);
    00907       _owner_colours[OWNER_NONE]  = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour;
    00908       _owner_colours[OWNER_WATER] = MKCOLOUR(0xCACACACA);
    00909       _owner_colours[OWNER_END]   = MKCOLOUR(0x20202020); // Industry
    00910
    00911       /* Now fill with the company colours */
    00912       FOR_ALL_COMPANIES(c) {
    00913         _owner_colours[c->index] = _colour_gradient[c->colour][5] * 0x01010101;
    00914       }
    00915     }
    00916
    00917     /* Which tile is displayed at (dpi->left, dpi->top)? */
    00918     int dx;
    00919     Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
    00920     int tile_x = this->scroll_x / TILE_SIZE + tile.x;
    00921     int tile_y = this->scroll_y / TILE_SIZE + tile.y;
    00922
    00923     void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
    00924     int x = - dx - 4;
    00925     int y = 0;
    00926
    00927     for (;;) {
    00928       /* Distance from left edge */
    00929       if (x >= -3) {
    00930         if (x >= dpi->width) break; // Exit the loop.
    00931
    00932         int end_pos = min(dpi->width, x + 4);
    00933         int reps = (dpi->height - y + 1) / 2; // Number of lines.
    00934         if (reps > 0) {
    00935           this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
    00936         }
    00937       }
    00938
    00939       if (y == 0) {
    00940         tile_y += this->zoom;
    00941         y++;
    00942         ptr = blitter->MoveTo(ptr, 0, 1);
    00943       } else {
    00944         tile_x -= this->zoom;
    00945         y--;
    00946         ptr = blitter->MoveTo(ptr, 0, -1);
    00947       }
    00948       ptr = blitter->MoveTo(ptr, 2, 0);
    00949       x += 2;
    00950     }
    00951
    00952     /* Draw vehicles */
    00953     if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
    00954
    00955     /* Draw town names */
    00956     if (this->show_towns) this->DrawTowns(dpi);
    00957
    00958     /* Draw map indicators */
    00959     this->DrawMapIndicators();
    00960
    00961     _cur_dpi = old_dpi;
    00962   }
    00963
    00964 public:
    00965   SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
    00966   {
    00967     this->InitNested(desc, window_number);
    00968     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00969
    00970     _smallmap_industry_show_heightmap = false;
    00971     BuildLandLegend();
    00972     this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
    00973
    00974     this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00975     this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
    00976
    00977     this->SetZoomLevel(ZLC_INITIALIZE, NULL);
    00978     this->SmallMapCenterOnCurrentPos();
    00979   }
    00980
    00984   inline uint GetMaxLegendHeight() const
    00985   {
    00986     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + this->min_number_of_columns - 1) / this->min_number_of_columns);
    00987     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
    00988   }
    00989
    00993   inline uint GetMinLegendWidth() const
    00994   {
    00995     return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
    00996   }
    00997
    01001   inline uint GetNumberColumnsLegend(uint width) const
    01002   {
    01003     return width / this->column_width;
    01004   }
    01005
    01009   uint GetLegendHeight(uint width) const
    01010   {
    01011     uint num_columns = this->GetNumberColumnsLegend(width);
    01012     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + num_columns - 1) / num_columns);
    01013     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
    01014   }
    01015
    01016   virtual void SetStringParameters(int widget) const
    01017   {
    01018     switch (widget) {
    01019       case SM_WIDGET_CAPTION:
    01020         SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
    01021         break;
    01022     }
    01023   }
    01024
    01025   virtual void OnInit()
    01026   {
    01027     uint min_width = 0;
    01028     this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
    01029     this->min_number_of_fixed_rows = 0;
    01030     for (uint i = 0; i < lengthof(_legend_table); i++) {
    01031       uint height = 0;
    01032       uint num_columns = 1;
    01033       for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
    01034         StringID str;
    01035         if (i == SMT_INDUSTRY) {
    01036           SetDParam(0, tbl->legend);
    01037           SetDParam(1, IndustryPool::MAX_SIZE);
    01038           str = STR_SMALLMAP_INDUSTRY;
    01039         } else {
    01040           if (tbl->col_break) {
    01041             this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    01042             height = 0;
    01043             num_columns++;
    01044           }
    01045           height++;
    01046           str = tbl->legend;
    01047         }
    01048         min_width = max(GetStringBoundingBox(str).width, min_width);
    01049       }
    01050       this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    01051       this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
    01052     }
    01053
    01054     /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
    01055     this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    01056   }
    01057
    01058   virtual void DrawWidget(const Rect &r, int widget) const
    01059   {
    01060     switch (widget) {
    01061       case SM_WIDGET_MAP: {
    01062         DrawPixelInfo new_dpi;
    01063         if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
    01064         this->DrawSmallMap(&new_dpi);
    01065       } break;
    01066
    01067       case SM_WIDGET_LEGEND: {
    01068         uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
    01069         uint number_of_rows = max(this->map_type == SMT_INDUSTRY ? (_smallmap_industry_count + columns - 1) / columns : 0, this->min_number_of_fixed_rows);
    01070         bool rtl = _dynlang.text_dir == TD_RTL;
    01071         uint y_org = r.top + WD_FRAMERECT_TOP;
    01072         uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
    01073         uint y = y_org;
    01074         uint i = 0; // Row counter for industry legend.
    01075         uint row_height = FONT_HEIGHT_SMALL;
    01076
    01077         uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
    01078         uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
    01079         uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
    01080         uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
    01081
    01082         for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
    01083           if (tbl->col_break || (this->map_type == SMT_INDUSTRY && i++ >= number_of_rows)) {
    01084             /* Column break needed, continue at top, COLUMN_WIDTH pixels
    01085              * (one "row") to the right. */
    01086             x += rtl ? -(int)this->column_width : this->column_width;
    01087             y = y_org;
    01088             i = 1;
    01089           }
    01090
    01091           if (this->map_type == SMT_INDUSTRY) {
    01092             /* Industry name must be formatted, since it's not in tiny font in the specs.
    01093              * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
    01094             SetDParam(0, tbl->legend);
    01095             assert(tbl->type < NUM_INDUSTRYTYPES);
    01096             SetDParam(1, _industry_counts[tbl->type]);
    01097             if (!tbl->show_on_map) {
    01098               /* Simply draw the string, not the black border of the legend colour.
    01099                * This will enforce the idea of the disabled item */
    01100               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
    01101             } else {
    01102               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
    01103               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
    01104             }
    01105           } else {
    01106             /* Anything that is not an industry is using normal process */
    01107             GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
    01108             DrawString(x + text_left, x + text_right, y, tbl->legend);
    01109           }
    01110           GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
    01111
    01112           y += row_height;
    01113         }
    01114       }
    01115     }
    01116   }
    01117
    01118   virtual void OnPaint()
    01119   {
    01120     this->DrawWidgets();
    01121   }
    01122
    01123   virtual void OnClick(Point pt, int widget, int click_count)
    01124   {
    01125     switch (widget) {
    01126       case SM_WIDGET_MAP: { // Map window
    01127         /*
    01128          * XXX: scrolling with the left mouse button is done by subsequently
    01129          * clicking with the left mouse button; clicking once centers the
    01130          * large map at the selected point. So by unclicking the left mouse
    01131          * button here, it gets reclicked during the next inputloop, which
    01132          * would make it look like the mouse is being dragged, while it is
    01133          * actually being (virtually) clicked every inputloop.
    01134          */
    01135         _left_button_clicked = false;
    01136
    01137         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01138         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    01139         int sub;
    01140         pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
    01141         pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
    01142             this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
    01143
    01144         w->viewport->follow_vehicle = INVALID_VEHICLE;
    01145         w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width  >> 1);
    01146         w->viewport->dest_scrollpos_y = pt.y - (w->viewport->virtual_height >> 1);
    01147
    01148         this->SetDirty();
    01149       } break;
    01150
    01151       case SM_WIDGET_ZOOM_IN:
    01152       case SM_WIDGET_ZOOM_OUT: {
    01153         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01154         Point pt = {wid->current_x / 2, wid->current_y / 2};
    01155         this->SetZoomLevel((widget == SM_WIDGET_ZOOM_IN) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
    01156         SndPlayFx(SND_15_BEEP);
    01157         break;
    01158       }
    01159
    01160       case SM_WIDGET_CONTOUR:    // Show land contours
    01161       case SM_WIDGET_VEHICLES:   // Show vehicles
    01162       case SM_WIDGET_INDUSTRIES: // Show industries
    01163       case SM_WIDGET_ROUTES:     // Show transport routes
    01164       case SM_WIDGET_VEGETATION: // Show vegetation
    01165       case SM_WIDGET_OWNERS:     // Show land owners
    01166         this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
    01167         this->map_type = (SmallMapType)(widget - SM_WIDGET_CONTOUR);
    01168         this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    01169
    01170         /* Hide Enable all/Disable all buttons if is not industry type small map */
    01171         this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
    01172
    01173         this->SetDirty();
    01174         SndPlayFx(SND_15_BEEP);
    01175         break;
    01176
    01177       case SM_WIDGET_CENTERMAP: // Center the smallmap again
    01178         this->SmallMapCenterOnCurrentPos();
    01179         this->HandleButtonClick(SM_WIDGET_CENTERMAP);
    01180         SndPlayFx(SND_15_BEEP);
    01181         break;
    01182
    01183       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
    01184         this->show_towns = !this->show_towns;
    01185         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    01186
    01187         this->SetDirty();
    01188         SndPlayFx(SND_15_BEEP);
    01189         break;
    01190
    01191       case SM_WIDGET_LEGEND: // Legend
    01192         /* If industry type small map*/
    01193         if (this->map_type == SMT_INDUSTRY) {
    01194           /* If click on industries label, find right industry type and enable/disable it */
    01195           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
    01196           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
    01197           uint columns = this->GetNumberColumnsLegend(wi->current_x);
    01198           uint number_of_rows = max((_smallmap_industry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
    01199           if (line >= number_of_rows) break;
    01200
    01201           bool rtl = _dynlang.text_dir == TD_RTL;
    01202           int x = pt.x - wi->pos_x;
    01203           if (rtl) x = wi->current_x - x;
    01204           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
    01205
    01206           /* Check if click is on industry label*/
    01207           int industry_pos = (column * number_of_rows) + line;
    01208           if (industry_pos < _smallmap_industry_count) {
    01209             _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
    01210           }
    01211
    01212           /* Raise the two buttons "all", as we have done a specific choice */
    01213           this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01214           this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01215           this->SetDirty();
    01216         }
    01217         break;
    01218
    01219       case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
    01220         for (int i = 0; i != _smallmap_industry_count; i++) {
    01221           _legend_from_industries[i].show_on_map = true;
    01222         }
    01223         /* Toggle appeareance indicating the choice */
    01224         this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01225         this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01226         this->SetDirty();
    01227         break;
    01228
    01229       case SM_WIDGET_DISABLEINDUSTRIES: // Disable all industries
    01230         for (int i = 0; i != _smallmap_industry_count; i++) {
    01231           _legend_from_industries[i].show_on_map = false;
    01232         }
    01233         /* Toggle appeareance indicating the choice */
    01234         this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01235         this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01236         this->SetDirty();
    01237         break;
    01238
    01239       case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
    01240         _smallmap_industry_show_heightmap = !_smallmap_industry_show_heightmap;
    01241         this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
    01242         this->SetDirty();
    01243         break;
    01244     }
    01245   }
    01246
    01247   virtual void OnRightClick(Point pt, int widget)
    01248   {
    01249     if (widget == SM_WIDGET_MAP) {
    01250       if (_scrolling_viewport) return;
    01251       _scrolling_viewport = true;
    01252     }
    01253   }
    01254
    01255   virtual void OnMouseWheel(int wheel)
    01256   {
    01257     const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01258     int cursor_x = _cursor.pos.x - this->left - wid->pos_x;
    01259     int cursor_y = _cursor.pos.y - this->top  - wid->pos_y;
    01260     if (IsInsideMM(cursor_x, 0, wid->current_x) && IsInsideMM(cursor_y, 0, wid->current_y)) {
    01261       Point pt = {cursor_x, cursor_y};
    01262       this->SetZoomLevel((wheel < 0) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
    01263     }
    01264   }
    01265
    01266   virtual void OnTick()
    01267   {
    01268     /* Update the window every now and then */
    01269     if (--this->refresh != 0) return;
    01270
    01271     this->refresh = FORCE_REFRESH_PERIOD;
    01272     this->SetDirty();
    01273   }
    01274
    01282   void SetNewScroll(int sx, int sy, int sub)
    01283   {
    01284     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01285     Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
    01286     hv.x *= this->zoom;
    01287     hv.y *= this->zoom;
    01288
    01289     if (sx < -hv.x) {
    01290       sx = -hv.x;
    01291       sub = 0;
    01292     }
    01293     if (sx > (int)MapMaxX() * TILE_SIZE - hv.x) {
    01294       sx = MapMaxX() * TILE_SIZE - hv.x;
    01295       sub = 0;
    01296     }
    01297     if (sy < -hv.y) {
    01298       sy = -hv.y;
    01299       sub = 0;
    01300     }
    01301     if (sy > (int)MapMaxY() * TILE_SIZE - hv.y) {
    01302       sy = MapMaxY() * TILE_SIZE - hv.y;
    01303       sub = 0;
    01304     }
    01305
    01306     this->scroll_x = sx;
    01307     this->scroll_y = sy;
    01308     this->subscroll = sub;
    01309   }
    01310
    01311   virtual void OnScroll(Point delta)
    01312   {
    01313     _cursor.fix_at = true;
    01314
    01315     /* While tile is at (delta.x, delta.y)? */
    01316     int sub;
    01317     Point pt = this->PixelToTile(delta.x, delta.y, &sub);
    01318     this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
    01319
    01320     this->SetDirty();
    01321   }
    01322
    01323   void SmallMapCenterOnCurrentPos()
    01324   {
    01325     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    01326     Point pt = InverseRemapCoords(vp->virtual_left + vp->virtual_width  / 2, vp->virtual_top  + vp->virtual_height / 2);
    01327
    01328     int sub;
    01329     const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01330     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);
    01331     this->SetNewScroll(sxy.x, sxy.y, sub);
    01332     this->SetDirty();
    01333   }
    01334 };
    01335
    01336 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
    01337 bool SmallMapWindow::show_towns = true;
    01338
    01347 class NWidgetSmallmapDisplay : public NWidgetContainer {
    01348   const SmallMapWindow *smallmap_window;
    01349 public:
    01350   NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
    01351   {
    01352     this->smallmap_window = NULL;
    01353   }
    01354
    01355   virtual void SetupSmallestSize(Window *w, bool init_array)
    01356   {
    01357     NWidgetBase *display = this->head;
    01358     NWidgetBase *bar = display->next;
    01359
    01360     display->SetupSmallestSize(w, init_array);
    01361     bar->SetupSmallestSize(w, init_array);
    01362
    01363     this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
    01364     this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
    01365     this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetMaxLegendHeight());
    01366     this->fill_x = max(display->fill_x, bar->fill_x);
    01367     this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
    01368     this->resize_x = max(display->resize_x, bar->resize_x);
    01369     this->resize_y = min(display->resize_y, bar->resize_y);
    01370   }
    01371
    01372   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
    01373   {
    01374     this->pos_x = x;
    01375     this->pos_y = y;
    01376     this->current_x = given_width;
    01377     this->current_y = given_height;
    01378
    01379     NWidgetBase *display = this->head;
    01380     NWidgetBase *bar = display->next;
    01381
    01382     if (sizing == ST_SMALLEST) {
    01383       this->smallest_x = given_width;
    01384       this->smallest_y = given_height;
    01385       /* Make display and bar exactly equal to their minimal size. */
    01386       display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
    01387       bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
    01388     }
    01389
    01390     uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(given_width - bar->smallest_x));
    01391     uint display_height = given_height - bar_height;
    01392     display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
    01393     bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
    01394   }
    01395
    01396   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
    01397   {
    01398     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
    01399     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    01400       NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
    01401       if (widget != NULL) return widget;
    01402     }
    01403     return NULL;
    01404   }
    01405
    01406   virtual void Draw(const Window *w)
    01407   {
    01408     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
    01409   }
    01410 };
    01411
    01413 static const NWidgetPart _nested_smallmap_display[] = {
    01414   NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
    01415     NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
    01416   EndContainer(),
    01417 };
    01418
    01420 static const NWidgetPart _nested_smallmap_bar[] = {
    01421   NWidget(WWT_PANEL, COLOUR_BROWN),
    01422     NWidget(NWID_HORIZONTAL),
    01423       NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
    01424       NWidget(NWID_VERTICAL),
    01425         /* Top button row. */
    01426         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01427           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN),
    01428           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
    01429           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
    01430           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
    01431           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
    01432         EndContainer(),
    01433         /* Bottom button row. */
    01434         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01435           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT),
    01436           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
    01437           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
    01438           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
    01439           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
    01440         EndContainer(),
    01441         NWidget(NWID_SPACER), SetResize(0, 1),
    01442       EndContainer(),
    01443     EndContainer(),
    01444   EndContainer(),
    01445 };
    01446
    01447 static NWidgetBase *SmallMapDisplay(int *biggest_index)
    01448 {
    01449   NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
    01450
    01451   MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
    01452   MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
    01453   return map_display;
    01454 }
    01455
    01456
    01457 static const NWidgetPart _nested_smallmap_widgets[] = {
    01458   NWidget(NWID_HORIZONTAL),
    01459     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    01460     NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01461     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    01462     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    01463   EndContainer(),
    01464   NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
    01465   /* Bottom button row and resize box. */
    01466   NWidget(NWID_HORIZONTAL),
    01467     NWidget(WWT_PANEL, COLOUR_BROWN),
    01468       NWidget(NWID_HORIZONTAL),
    01469         NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECTINDUSTRIES),
    01470           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01471             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
    01472             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
    01473             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
    01474           EndContainer(),
    01475           NWidget(NWID_SPACER), SetFill(1, 1),
    01476         EndContainer(),
    01477         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
    01478       EndContainer(),
    01479     EndContainer(),
    01480     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    01481   EndContainer(),
    01482 };
    01483
    01484 static const WindowDesc _smallmap_desc(
    01485   WDP_AUTO, 446, 314,
    01486   WC_SMALLMAP, WC_NONE,
    01487   WDF_UNCLICK_BUTTONS,
    01488   _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
    01489 );
    01490
    01491 void ShowSmallMap()
    01492 {
    01493   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
    01494 }
    01495
    01504 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
    01505 {
    01506   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
    01507
    01508   /* If a user scrolls to a tile (via what way what so ever) and already is on
    01509    * that tile (e.g.: pressed twice), move the smallmap to that location,
    01510    * so you directly see where you are on the smallmap. */
    01511
    01512   if (res) return res;
    01513
    01514   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
    01515   if (w != NULL) w->SmallMapCenterOnCurrentPos();
    01516
    01517   return res;
    01518 }