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 #include "station_base.h"
00030 #include "company_func.h"
00031 #include "cargotype.h"
00032 #include "core/smallmap_type.hpp"
00033 
00034 #include "table/strings.h"
00035 
00037 enum SmallMapWindowWidgets {
00038   SM_WIDGET_CAPTION,           
00039   SM_WIDGET_MAP_BORDER,        
00040   SM_WIDGET_MAP,               
00041   SM_WIDGET_LEGEND,            
00042   SM_WIDGET_ZOOM_IN,           
00043   SM_WIDGET_ZOOM_OUT,          
00044   SM_WIDGET_CONTOUR,           
00045   SM_WIDGET_VEHICLES,          
00046   SM_WIDGET_INDUSTRIES,        
00047   SM_WIDGET_ROUTE_LINKS,       
00048   SM_WIDGET_ROUTES,            
00049   SM_WIDGET_VEGETATION,        
00050   SM_WIDGET_OWNERS,            
00051   SM_WIDGET_CENTERMAP,         
00052   SM_WIDGET_TOGGLETOWNNAME,    
00053   SM_WIDGET_SELECT_BUTTONS,    
00054   SM_WIDGET_ENABLE_ALL,        
00055   SM_WIDGET_DISABLE_ALL,       
00056   SM_WIDGET_SHOW_HEIGHT,       
00057 };
00058 
00059 static int _smallmap_industry_count; 
00060 static int _smallmap_company_count;  
00061 static int _smallmap_cargo_count;    
00062 
00063 static const int NUM_NO_COMPANY_ENTRIES = 4; 
00064 
00065 static const uint8 PC_ROUGH_LAND      = 0x52; 
00066 static const uint8 PC_GRASS_LAND      = 0x54; 
00067 static const uint8 PC_BARE_LAND       = 0x37; 
00068 static const uint8 PC_FIELDS          = 0x25; 
00069 static const uint8 PC_TREES           = 0x57; 
00070 static const uint8 PC_WATER           = 0xCA; 
00071 
00073 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, false}
00074 
00076 #define MC(height)  {0, STR_TINY_BLACK_HEIGHT, INVALID_INDUSTRYTYPE, height, INVALID_COMPANY, true, false, false}
00077 
00079 #define MO(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, false}
00080 
00082 #define MOEND() {0, 0, INVALID_INDUSTRYTYPE, 0, OWNER_NONE, true, true, false}
00083 
00085 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, true, false}
00086 
00091 #define MS(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, true}
00092 
00094 struct LegendAndColour {
00095   uint8 colour;              
00096   StringID legend;           
00097   IndustryType type;         
00098   uint8 height;              
00099   CompanyID company;         
00100   CargoID cid;               
00101   bool show_on_map;          
00102   bool end;                  
00103   bool col_break;            
00104 };
00105 
00107 static LegendAndColour _legend_land_contours[] = {
00108   /* The colours for the following values are set at BuildLandLegend() based on each colour scheme. */
00109   MC(0),
00110   MC(4),
00111   MC(8),
00112   MC(12),
00113   MC(14),
00114 
00115   MS(PC_BLACK,           STR_SMALLMAP_LEGENDA_ROADS),
00116   MK(PC_GREY,            STR_SMALLMAP_LEGENDA_RAILROADS),
00117   MK(PC_LIGHT_BLUE,      STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
00118   MK(PC_DARK_RED,        STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00119   MK(PC_WHITE,           STR_SMALLMAP_LEGENDA_VEHICLES),
00120   MKEND()
00121 };
00122 
00123 static const LegendAndColour _legend_vehicles[] = {
00124   MK(PC_RED,             STR_SMALLMAP_LEGENDA_TRAINS),
00125   MK(PC_YELLOW,          STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
00126   MK(PC_LIGHT_BLUE,      STR_SMALLMAP_LEGENDA_SHIPS),
00127   MK(PC_WHITE,           STR_SMALLMAP_LEGENDA_AIRCRAFT),
00128 
00129   MS(PC_BLACK,           STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00130   MK(PC_DARK_RED,        STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00131   MKEND()
00132 };
00133 
00134 static const LegendAndColour _legend_routes[] = {
00135   MK(PC_BLACK,           STR_SMALLMAP_LEGENDA_ROADS),
00136   MK(PC_GREY,            STR_SMALLMAP_LEGENDA_RAILROADS),
00137   MK(PC_DARK_RED,        STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00138 
00139   MS(PC_VERY_DARK_BROWN, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
00140   MK(PC_ORANGE,          STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
00141   MK(PC_YELLOW,          STR_SMALLMAP_LEGENDA_BUS_STATION),
00142   MK(PC_RED,             STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
00143   MK(PC_LIGHT_BLUE,      STR_SMALLMAP_LEGENDA_DOCK),
00144   MKEND()
00145 };
00146 
00147 static const LegendAndColour _legend_vegetation[] = {
00148   MK(PC_ROUGH_LAND,      STR_SMALLMAP_LEGENDA_ROUGH_LAND),
00149   MK(PC_GRASS_LAND,      STR_SMALLMAP_LEGENDA_GRASS_LAND),
00150   MK(PC_BARE_LAND,       STR_SMALLMAP_LEGENDA_BARE_LAND),
00151   MK(PC_FIELDS,          STR_SMALLMAP_LEGENDA_FIELDS),
00152   MK(PC_TREES,           STR_SMALLMAP_LEGENDA_TREES),
00153   MK(PC_GREEN,           STR_SMALLMAP_LEGENDA_FOREST),
00154 
00155   MS(PC_GREY,            STR_SMALLMAP_LEGENDA_ROCKS),
00156   MK(PC_ORANGE,          STR_SMALLMAP_LEGENDA_DESERT),
00157   MK(PC_LIGHT_BLUE,      STR_SMALLMAP_LEGENDA_SNOW),
00158   MK(PC_BLACK,           STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00159   MK(PC_DARK_RED,        STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00160   MKEND()
00161 };
00162 
00163 static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
00164   MO(PC_WATER,           STR_SMALLMAP_LEGENDA_WATER),
00165   MO(0x00,               STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings.
00166   MO(PC_DARK_RED,        STR_SMALLMAP_LEGENDA_TOWNS),
00167   MO(PC_DARK_GREY,       STR_SMALLMAP_LEGENDA_INDUSTRIES),
00168   /* The legend will be terminated the first time it is used. */
00169   MOEND(),
00170 };
00171 
00172 #undef MK
00173 #undef MC
00174 #undef MS
00175 #undef MO
00176 #undef MOEND
00177 #undef MKEND
00178 
00183 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
00185 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
00187 static LegendAndColour _legend_from_cargoes[NUM_CARGO + 1];
00189 static uint _cargotype_to_list_pos[NUM_CARGO];
00191 static bool _smallmap_show_heightmap = false;
00193 static uint _company_to_list_pos[MAX_COMPANIES];
00194 
00198 void BuildIndustriesLegend()
00199 {
00200   uint j = 0;
00201 
00202   /* Add each name */
00203   for (uint8 i = 0; i < NUM_INDUSTRYTYPES; i++) {
00204     IndustryType ind = _sorted_industry_types[i];
00205     const IndustrySpec *indsp = GetIndustrySpec(ind);
00206     if (indsp->enabled) {
00207       _legend_from_industries[j].legend = indsp->name;
00208       _legend_from_industries[j].colour = indsp->map_colour;
00209       _legend_from_industries[j].type = ind;
00210       _legend_from_industries[j].show_on_map = true;
00211       _legend_from_industries[j].col_break = false;
00212       _legend_from_industries[j].end = false;
00213 
00214       /* Store widget number for this industry type. */
00215       _industry_to_list_pos[ind] = j;
00216       j++;
00217     }
00218   }
00219   /* Terminate the list */
00220   _legend_from_industries[j].end = true;
00221 
00222   /* Store number of enabled industries */
00223   _smallmap_industry_count = j;
00224 }
00225 
00227 void BuildCargoTypesLegend()
00228 {
00229   uint j = 0;
00230 
00231   /* Add all standard cargo types. */
00232   const CargoSpec *cs;
00233   FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
00234     _legend_from_cargoes[j].legend = cs->name;
00235     _legend_from_cargoes[j].colour = cs->legend_colour;
00236     _legend_from_cargoes[j].cid = cs->Index();
00237     _legend_from_cargoes[j].show_on_map = true;
00238     _legend_from_cargoes[j].col_break = false;
00239     _legend_from_cargoes[j].end = false;
00240 
00241     /* Store widget number for this cargo type. */
00242     _cargotype_to_list_pos[cs->Index()] = j;
00243     j++;
00244   }
00245 
00246   /* Terminate list. */
00247   _legend_from_cargoes[j].end = true;
00248 
00249   /* Store number of enabled cargos. */
00250   _smallmap_cargo_count = j;
00251 }
00252 
00253 static const LegendAndColour * const _legend_table[] = {
00254   _legend_land_contours,
00255   _legend_vehicles,
00256   _legend_from_industries,
00257   _legend_from_cargoes,
00258   _legend_routes,
00259   _legend_vegetation,
00260   _legend_land_owners,
00261 };
00262 
00263 #define MKCOLOUR(x)         TO_LE32X(x)
00264 
00265 #define MKCOLOUR_XXXX(x)    (MKCOLOUR(0x01010101) * (uint)(x))
00266 #define MKCOLOUR_X0X0(x)    (MKCOLOUR(0x01000100) * (uint)(x))
00267 #define MKCOLOUR_0X0X(x)    (MKCOLOUR(0x00010001) * (uint)(x))
00268 #define MKCOLOUR_0XX0(x)    (MKCOLOUR(0x00010100) * (uint)(x))
00269 #define MKCOLOUR_X00X(x)    (MKCOLOUR(0x01000001) * (uint)(x))
00270 
00271 #define MKCOLOUR_XYXY(x, y) (MKCOLOUR_X0X0(x) | MKCOLOUR_0X0X(y))
00272 #define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y))
00273 
00274 #define MKCOLOUR_0000       MKCOLOUR_XXXX(0x00)
00275 #define MKCOLOUR_0FF0       MKCOLOUR_0XX0(0xFF)
00276 #define MKCOLOUR_F00F       MKCOLOUR_X00X(0xFF)
00277 #define MKCOLOUR_FFFF       MKCOLOUR_XXXX(0xFF)
00278 
00280 static const uint32 _green_map_heights[] = {
00281   MKCOLOUR_XXXX(0x5A),
00282   MKCOLOUR_XYXY(0x5A, 0x5B),
00283   MKCOLOUR_XXXX(0x5B),
00284   MKCOLOUR_XYXY(0x5B, 0x5C),
00285   MKCOLOUR_XXXX(0x5C),
00286   MKCOLOUR_XYXY(0x5C, 0x5D),
00287   MKCOLOUR_XXXX(0x5D),
00288   MKCOLOUR_XYXY(0x5D, 0x5E),
00289   MKCOLOUR_XXXX(0x5E),
00290   MKCOLOUR_XYXY(0x5E, 0x5F),
00291   MKCOLOUR_XXXX(0x5F),
00292   MKCOLOUR_XYXY(0x5F, 0x1F),
00293   MKCOLOUR_XXXX(0x1F),
00294   MKCOLOUR_XYXY(0x1F, 0x27),
00295   MKCOLOUR_XXXX(0x27),
00296   MKCOLOUR_XXXX(0x27),
00297 };
00298 assert_compile(lengthof(_green_map_heights) == MAX_TILE_HEIGHT + 1);
00299 
00301 static const uint32 _dark_green_map_heights[] = {
00302   MKCOLOUR_XXXX(0x60),
00303   MKCOLOUR_XYXY(0x60, 0x61),
00304   MKCOLOUR_XXXX(0x61),
00305   MKCOLOUR_XYXY(0x61, 0x62),
00306   MKCOLOUR_XXXX(0x62),
00307   MKCOLOUR_XYXY(0x62, 0x63),
00308   MKCOLOUR_XXXX(0x63),
00309   MKCOLOUR_XYXY(0x63, 0x64),
00310   MKCOLOUR_XXXX(0x64),
00311   MKCOLOUR_XYXY(0x64, 0x65),
00312   MKCOLOUR_XXXX(0x65),
00313   MKCOLOUR_XYXY(0x65, 0x66),
00314   MKCOLOUR_XXXX(0x66),
00315   MKCOLOUR_XYXY(0x66, 0x67),
00316   MKCOLOUR_XXXX(0x67),
00317   MKCOLOUR_XXXX(0x67),
00318 };
00319 assert_compile(lengthof(_dark_green_map_heights) == MAX_TILE_HEIGHT + 1);
00320 
00322 static const uint32 _violet_map_heights[] = {
00323   MKCOLOUR_XXXX(0x80),
00324   MKCOLOUR_XYXY(0x80, 0x81),
00325   MKCOLOUR_XXXX(0x81),
00326   MKCOLOUR_XYXY(0x81, 0x82),
00327   MKCOLOUR_XXXX(0x82),
00328   MKCOLOUR_XYXY(0x82, 0x83),
00329   MKCOLOUR_XXXX(0x83),
00330   MKCOLOUR_XYXY(0x83, 0x84),
00331   MKCOLOUR_XXXX(0x84),
00332   MKCOLOUR_XYXY(0x84, 0x85),
00333   MKCOLOUR_XXXX(0x85),
00334   MKCOLOUR_XYXY(0x85, 0x86),
00335   MKCOLOUR_XXXX(0x86),
00336   MKCOLOUR_XYXY(0x86, 0x87),
00337   MKCOLOUR_XXXX(0x87),
00338   MKCOLOUR_XXXX(0x87),
00339 };
00340 assert_compile(lengthof(_violet_map_heights) == MAX_TILE_HEIGHT + 1);
00341 
00343 struct SmallMapColourScheme {
00344   const uint32 *height_colours; 
00345   uint32 default_colour;   
00346 };
00347 
00349 static const SmallMapColourScheme _heightmap_schemes[] = {
00350   {_green_map_heights,      MKCOLOUR_XXXX(0x54)}, 
00351   {_dark_green_map_heights, MKCOLOUR_XXXX(0x62)}, 
00352   {_violet_map_heights,     MKCOLOUR_XXXX(0x82)}, 
00353 };
00354 
00358 void BuildLandLegend()
00359 {
00360   for (LegendAndColour *lc = _legend_land_contours; lc->legend == STR_TINY_BLACK_HEIGHT; lc++) {
00361     lc->colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[lc->height];
00362   }
00363 }
00364 
00368 void BuildOwnerLegend()
00369 {
00370   _legend_land_owners[1].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour;
00371 
00372   int i = NUM_NO_COMPANY_ENTRIES;
00373   const Company *c;
00374   FOR_ALL_COMPANIES(c) {
00375     _legend_land_owners[i].colour = _colour_gradient[c->colour][5];
00376     _legend_land_owners[i].company = c->index;
00377     _legend_land_owners[i].show_on_map = true;
00378     _legend_land_owners[i].col_break = false;
00379     _legend_land_owners[i].end = false;
00380     _company_to_list_pos[c->index] = i;
00381     i++;
00382   }
00383 
00384   /* Terminate the list */
00385   _legend_land_owners[i].end = true;
00386 
00387   /* Store maximum amount of owner legend entries. */
00388   _smallmap_company_count = i;
00389 }
00390 
00391 struct AndOr {
00392   uint32 mor;
00393   uint32 mand;
00394 };
00395 
00396 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
00397 {
00398   return (colour & mask->mand) | mask->mor;
00399 }
00400 
00401 
00403 static const AndOr _smallmap_contours_andor[] = {
00404   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_CLEAR
00405   {MKCOLOUR_0XX0(PC_GREY      ), MKCOLOUR_F00F}, // MP_RAILWAY
00406   {MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F}, // MP_ROAD
00407   {MKCOLOUR_0XX0(PC_DARK_RED  ), MKCOLOUR_F00F}, // MP_HOUSE
00408   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_TREES
00409   {MKCOLOUR_XXXX(PC_LIGHT_BLUE), MKCOLOUR_0000}, // MP_STATION
00410   {MKCOLOUR_XXXX(PC_WATER     ), MKCOLOUR_0000}, // MP_WATER
00411   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_VOID
00412   {MKCOLOUR_XXXX(PC_DARK_RED  ), MKCOLOUR_0000}, // MP_INDUSTRY
00413   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_TUNNELBRIDGE
00414   {MKCOLOUR_0XX0(PC_DARK_RED  ), MKCOLOUR_F00F}, // MP_OBJECT
00415   {MKCOLOUR_0XX0(PC_GREY      ), MKCOLOUR_F00F},
00416 };
00417 
00419 static const AndOr _smallmap_vehicles_andor[] = {
00420   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_CLEAR
00421   {MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F}, // MP_RAILWAY
00422   {MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F}, // MP_ROAD
00423   {MKCOLOUR_0XX0(PC_DARK_RED  ), MKCOLOUR_F00F}, // MP_HOUSE
00424   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_TREES
00425   {MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F}, // MP_STATION
00426   {MKCOLOUR_XXXX(PC_WATER     ), MKCOLOUR_0000}, // MP_WATER
00427   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_VOID
00428   {MKCOLOUR_XXXX(PC_DARK_RED  ), MKCOLOUR_0000}, // MP_INDUSTRY
00429   {MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_TUNNELBRIDGE
00430   {MKCOLOUR_0XX0(PC_DARK_RED  ), MKCOLOUR_F00F}, // MP_OBJECT
00431   {MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F},
00432 };
00433 
00435 static const byte _tiletype_importance[] = {
00436   2, // MP_CLEAR
00437   8, // MP_RAILWAY
00438   7, // MP_ROAD
00439   5, // MP_HOUSE
00440   2, // MP_TREES
00441   9, // MP_STATION
00442   2, // MP_WATER
00443   1, // MP_VOID
00444   6, // MP_INDUSTRY
00445   8, // MP_TUNNELBRIDGE
00446   2, // MP_OBJECT
00447   0,
00448 };
00449 
00450 
00451 static inline TileType GetEffectiveTileType(TileIndex tile)
00452 {
00453   TileType t = GetTileType(tile);
00454 
00455   if (t == MP_TUNNELBRIDGE) {
00456     TransportType tt = GetTunnelBridgeTransportType(tile);
00457 
00458     switch (tt) {
00459       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
00460       case TRANSPORT_ROAD: t = MP_ROAD;    break;
00461       default:             t = MP_WATER;   break;
00462     }
00463   }
00464   return t;
00465 }
00466 
00473 static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t)
00474 {
00475   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00476   return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[t]);
00477 }
00478 
00486 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
00487 {
00488   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00489   return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[t]);
00490 }
00491 
00499 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
00500 {
00501   if (t == MP_INDUSTRY) {
00502     /* If industry is allowed to be seen, use its colour on the map */
00503     if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
00504       return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
00505     } else {
00506       /* Otherwise, return the colour which will make it disappear */
00507       t = (IsTileOnWater(tile) ? MP_WATER : MP_CLEAR);
00508     }
00509   }
00510 
00511   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00512   return ApplyMask(_smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
00513 }
00514 
00523 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t, bool show_height = false)
00524 {
00525   if (t == MP_STATION) {
00526     switch (GetStationType(tile)) {
00527       case STATION_RAIL:    return MKCOLOUR_XXXX(PC_VERY_DARK_BROWN);
00528       case STATION_AIRPORT: return MKCOLOUR_XXXX(PC_RED);
00529       case STATION_TRUCK:   return MKCOLOUR_XXXX(PC_ORANGE);
00530       case STATION_BUS:     return MKCOLOUR_XXXX(PC_YELLOW);
00531       case STATION_DOCK:    return MKCOLOUR_XXXX(PC_LIGHT_BLUE);
00532       default:              return MKCOLOUR_FFFF;
00533     }
00534   } else if (t == MP_RAILWAY) {
00535     AndOr andor = {
00536       MKCOLOUR_0XX0(GetRailTypeInfo(GetRailType(tile))->map_colour),
00537       _smallmap_contours_andor[t].mand
00538     };
00539 
00540     const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00541     return ApplyMask(cs->default_colour, &andor);
00542   }
00543 
00544   /* Ground colour */
00545   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00546   return ApplyMask(show_height ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_contours_andor[t]);
00547 }
00548 
00549 
00550 static const uint32 _vegetation_clear_bits[] = {
00551   MKCOLOUR_XXXX(PC_GRASS_LAND), 
00552   MKCOLOUR_XXXX(PC_ROUGH_LAND), 
00553   MKCOLOUR_XXXX(PC_GREY),       
00554   MKCOLOUR_XXXX(PC_FIELDS),     
00555   MKCOLOUR_XXXX(PC_LIGHT_BLUE), 
00556   MKCOLOUR_XXXX(PC_ORANGE),     
00557   MKCOLOUR_XXXX(PC_GRASS_LAND), 
00558   MKCOLOUR_XXXX(PC_GRASS_LAND), 
00559 };
00560 
00568 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile, TileType t)
00569 {
00570   switch (t) {
00571     case MP_CLEAR:
00572       return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR_XXXX(PC_BARE_LAND) : _vegetation_clear_bits[GetClearGround(tile)];
00573 
00574     case MP_INDUSTRY:
00575       return GetIndustrySpec(Industry::GetByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR_XXXX(PC_GREEN) : MKCOLOUR_XXXX(PC_DARK_RED);
00576 
00577     case MP_TREES:
00578       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(tile) == TREE_GROUND_ROUGH_SNOW) {
00579         return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR_XYYX(PC_LIGHT_BLUE, PC_TREES) : MKCOLOUR_XYYX(PC_ORANGE, PC_TREES);
00580       }
00581       return MKCOLOUR_XYYX(PC_GRASS_LAND, PC_TREES);
00582 
00583     default:
00584       return ApplyMask(MKCOLOUR_XXXX(PC_GRASS_LAND), &_smallmap_vehicles_andor[t]);
00585   }
00586 }
00587 
00595 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t)
00596 {
00597   Owner o;
00598 
00599   switch (t) {
00600     case MP_INDUSTRY: return MKCOLOUR_XXXX(PC_DARK_GREY);
00601     case MP_HOUSE:    return MKCOLOUR_XXXX(PC_DARK_RED);
00602     default:          o = GetTileOwner(tile); break;
00603     /* FIXME: For MP_ROAD there are multiple owners.
00604      * GetTileOwner returns the rail owner (level crossing) resp. the owner of ROADTYPE_ROAD (normal road),
00605      * even if there are no ROADTYPE_ROAD bits on the tile.
00606      */
00607   }
00608 
00609   if ((o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) || o == OWNER_NONE || o == OWNER_WATER) {
00610     if (t == MP_WATER) return MKCOLOUR_XXXX(PC_WATER);
00611     const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00612     return _smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour;
00613   } else if (o == OWNER_TOWN) {
00614     return MKCOLOUR_XXXX(PC_DARK_RED);
00615   }
00616 
00617   return MKCOLOUR_XXXX(_legend_land_owners[_company_to_list_pos[o]].colour);
00618 }
00619 
00621 static const byte _vehicle_type_colours[6] = {
00622   PC_RED, PC_YELLOW, PC_LIGHT_BLUE, PC_WHITE, PC_BLACK, PC_RED
00623 };
00624 
00625 
00627 class SmallMapWindow : public Window {
00629   enum SmallMapType {
00630     SMT_CONTOUR,
00631     SMT_VEHICLES,
00632     SMT_INDUSTRY,
00633     SMT_ROUTE_LINKS,
00634     SMT_ROUTES,
00635     SMT_VEGETATION,
00636     SMT_OWNER,
00637   };
00638 
00640   enum ZoomLevelChange {
00641     ZLC_INITIALIZE, 
00642     ZLC_ZOOM_OUT,   
00643     ZLC_ZOOM_IN,    
00644   };
00645 
00646   static SmallMapType map_type; 
00647   static bool show_towns;       
00648 
00649   static const uint LEGEND_BLOB_WIDTH = 8;              
00650   static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2; 
00651   uint min_number_of_fixed_rows; 
00652   uint column_width;             
00653 
00654   int32 scroll_x;  
00655   int32 scroll_y;  
00656   int32 subscroll; 
00657   int zoom;        
00658 
00659   static const uint8 FORCE_REFRESH_PERIOD = 0x1F; 
00660   uint8 refresh; 
00661 
00668   FORCEINLINE Point RemapTile(int tile_x, int tile_y) const
00669   {
00670     int x_offset = tile_x - this->scroll_x / (int)TILE_SIZE;
00671     int y_offset = tile_y - this->scroll_y / (int)TILE_SIZE;
00672 
00673     if (this->zoom == 1) return RemapCoords(x_offset, y_offset, 0);
00674 
00675     /* For negative offsets, round towards -inf. */
00676     if (x_offset < 0) x_offset -= this->zoom - 1;
00677     if (y_offset < 0) y_offset -= this->zoom - 1;
00678 
00679     return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
00680   }
00681 
00692   FORCEINLINE Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
00693   {
00694     if (add_sub) px += this->subscroll;  // Total horizontal offset.
00695 
00696     /* For each two rows down, add a x and a y tile, and
00697      * For each four pixels to the right, move a tile to the right. */
00698     Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
00699     px &= 3;
00700 
00701     if (py & 1) { // Odd number of rows, handle the 2 pixel shift.
00702       if (px < 2) {
00703         pt.x += this->zoom;
00704         px += 2;
00705       } else {
00706         pt.y += this->zoom;
00707         px -= 2;
00708       }
00709     }
00710 
00711     *sub = px;
00712     return pt;
00713   }
00714 
00724   Point ComputeScroll(int tx, int ty, int x, int y, int *sub)
00725   {
00726     assert(x >= 0 && y >= 0);
00727 
00728     int new_sub;
00729     Point tile_xy = PixelToTile(x, y, &new_sub, false);
00730     tx -= tile_xy.x;
00731     ty -= tile_xy.y;
00732 
00733     Point scroll;
00734     if (new_sub == 0) {
00735       *sub = 0;
00736       scroll.x = (tx + this->zoom) * TILE_SIZE;
00737       scroll.y = (ty - this->zoom) * TILE_SIZE;
00738     } else {
00739       *sub = 4 - new_sub;
00740       scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
00741       scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
00742     }
00743     return scroll;
00744   }
00745 
00752   void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
00753   {
00754     static const int zoomlevels[] = {1, 2, 4, 6, 8}; // Available zoom levels. Bigger number means more zoom-out (further away).
00755     static const int MIN_ZOOM_INDEX = 0;
00756     static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
00757 
00758     int new_index, cur_index, sub;
00759     Point tile;
00760     switch (change) {
00761       case ZLC_INITIALIZE:
00762         cur_index = - 1; // Definitely different from new_index.
00763         new_index = MIN_ZOOM_INDEX;
00764         break;
00765 
00766       case ZLC_ZOOM_IN:
00767       case ZLC_ZOOM_OUT:
00768         for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
00769           if (this->zoom == zoomlevels[cur_index]) break;
00770         }
00771         assert(cur_index <= MAX_ZOOM_INDEX);
00772 
00773         tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00774         new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
00775         break;
00776 
00777       default: NOT_REACHED();
00778     }
00779 
00780     if (new_index != cur_index) {
00781       this->zoom = zoomlevels[new_index];
00782       if (cur_index >= 0) {
00783         Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00784         this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
00785             this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
00786       }
00787       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN,  this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
00788       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
00789       this->SetDirty();
00790     }
00791   }
00792 
00798   inline uint32 GetTileColours(const TileArea &ta) const
00799   {
00800     int importance = 0;
00801     TileIndex tile = INVALID_TILE; // Position of the most important tile.
00802     TileType et = MP_VOID;         // Effective tile type at that position.
00803 
00804     TILE_AREA_LOOP(ti, ta) {
00805       TileType ttype = GetEffectiveTileType(ti);
00806       if (_tiletype_importance[ttype] > importance) {
00807         importance = _tiletype_importance[ttype];
00808         tile = ti;
00809         et = ttype;
00810       }
00811     }
00812 
00813     switch (this->map_type) {
00814       case SMT_CONTOUR:
00815         return GetSmallMapContoursPixels(tile, et);
00816 
00817       case SMT_VEHICLES:
00818         return GetSmallMapVehiclesPixels(tile, et);
00819 
00820       case SMT_INDUSTRY:
00821         return GetSmallMapIndustriesPixels(tile, et);
00822 
00823       case SMT_ROUTE_LINKS:
00824         return GetSmallMapRoutesPixels(tile, et, _smallmap_show_heightmap);
00825 
00826       case SMT_ROUTES:
00827         return GetSmallMapRoutesPixels(tile, et);
00828 
00829       case SMT_VEGETATION:
00830         return GetSmallMapVegetationPixels(tile, et);
00831 
00832       case SMT_OWNER:
00833         return GetSmallMapOwnerPixels(tile, et);
00834 
00835       default: NOT_REACHED();
00836     }
00837   }
00838 
00853   void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
00854   {
00855     void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
00856     uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
00857 
00858     do {
00859       /* Check if the tile (xc,yc) is within the map range */
00860       if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
00861 
00862       /* Check if the dst pointer points to a pixel inside the screen buffer */
00863       if (dst < _screen.dst_ptr) continue;
00864       if (dst >= dst_ptr_abs_end) continue;
00865 
00866       /* Construct tilearea covered by (xc, yc, xc + this->zoom, yc + this->zoom) such that it is within min_xy limits. */
00867       TileArea ta;
00868       if (min_xy == 1 && (xc == 0 || yc == 0)) {
00869         if (this->zoom == 1) continue; // The tile area is empty, don't draw anything.
00870 
00871         ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
00872       } else {
00873         ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
00874       }
00875       ta.ClampToMap(); // Clamp to map boundaries (may contain MP_VOID tiles!).
00876 
00877       uint32 val = this->GetTileColours(ta);
00878       uint8 *val8 = (uint8 *)&val;
00879       int idx = max(0, -start_pos);
00880       for (int pos = max(0, start_pos); pos < end_pos; pos++) {
00881         blitter->SetPixel(dst, idx, 0, val8[idx]);
00882         idx++;
00883       }
00884     /* Switch to next tile in the column */
00885     } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
00886   }
00887 
00893   void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
00894   {
00895     const Vehicle *v;
00896     FOR_ALL_VEHICLES(v) {
00897       if (v->type == VEH_EFFECT) continue;
00898       if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
00899 
00900       /* Remap into flat coordinates. */
00901       Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
00902 
00903       int y = pt.y - dpi->top;
00904       if (!IsInsideMM(y, 0, dpi->height)) continue; // y is out of bounds.
00905 
00906       bool skip = false; // Default is to draw both pixels.
00907       int x = pt.x - this->subscroll - 3 - dpi->left; // Offset X coordinate.
00908       if (x < 0) {
00909         /* if x+1 is 0, that means we're on the very left edge,
00910          * and should thus only draw a single pixel */
00911         if (++x != 0) continue;
00912         skip = true;
00913       } else if (x >= dpi->width - 1) {
00914         /* Check if we're at the very right edge, and if so draw only a single pixel */
00915         if (x != dpi->width - 1) continue;
00916         skip = true;
00917       }
00918 
00919       /* Calculate pointer to pixel and the colour */
00920       byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : PC_WHITE;
00921 
00922       /* And draw either one or two pixels depending on clipping */
00923       blitter->SetPixel(dpi->dst_ptr, x, y, colour);
00924       if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
00925     }
00926   }
00927 
00932   void DrawTowns(const DrawPixelInfo *dpi) const
00933   {
00934     const Town *t;
00935     FOR_ALL_TOWNS(t) {
00936       /* Remap the town coordinate */
00937       Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
00938       int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
00939       int y = pt.y;
00940 
00941       /* Check if the town sign is within bounds */
00942       if (x + t->sign.width_small > dpi->left &&
00943           x < dpi->left + dpi->width &&
00944           y + FONT_HEIGHT_SMALL > dpi->top &&
00945           y < dpi->top + dpi->height) {
00946         /* And draw it. */
00947         SetDParam(0, t->index);
00948         DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
00949       }
00950     }
00951   }
00952 
00956   void DrawRouteLinks() const
00957   {
00958     /* Iterate all shown cargo types. */
00959     for (int i = 0; i < _smallmap_cargo_count; i++) {
00960       if (_legend_from_cargoes[i].show_on_map) {
00961         CargoID cid = _legend_from_cargoes[i].cid;
00962 
00963         /* Iterate all stations. */
00964         const Station *st;
00965         FOR_ALL_STATIONS(st) {
00966           Point src_pt = this->RemapTile(TileX(st->xy), TileY(st->xy));
00967           src_pt.x -= this->subscroll;
00968 
00969           /* Collect waiting cargo per destination station. */
00970           std::map<StationID, uint> links;
00971           for (RouteLinkList::const_iterator l = st->goods[cid].routes.begin(); l != st->goods[cid].routes.end(); ++l) {
00972             if (IsInteractiveCompany((*l)->GetOwner())) links[(*l)->GetDestination()] += st->goods[cid].cargo.CountForNextHop((*l)->GetOriginOrderId());
00973           }
00974 
00975           /* Add cargo count on back-links. */
00976           for (std::map<StationID, uint>::iterator itr = links.begin(); itr != links.end(); ++itr) {
00977             /* Get destination location. */
00978             const Station *dest = Station::Get(itr->first);
00979             Point dest_pt = this->RemapTile(TileX(dest->xy), TileY(dest->xy));
00980             dest_pt.x -= this->subscroll;
00981 
00982             /* Get total count including back-links. */
00983             uint count = itr->second;
00984             for (RouteLinkList::const_iterator j = dest->goods[cid].routes.begin(); j != dest->goods[cid].routes.end(); ++j) {
00985               if ((*j)->GetDestination() == st->index && IsInteractiveCompany((*j)->GetOwner())) count += dest->goods[cid].cargo.CountForNextHop((*j)->GetOriginOrderId());
00986             }
00987 
00988             /* Calculate line size from waiting cargo. */
00989             int size = 1;
00990             if (count >= 400) size++;
00991             if (count >= 800) size++;
00992             if (count >= 1600) size++;
00993             if (count >= 3200) size++;
00994 
00995             /* Draw black border and cargo coloured line. */
00996             GfxDrawLine(src_pt.x, src_pt.y, dest_pt.x, dest_pt.y, PC_BLACK, size + 2);
00997             GfxDrawLine(src_pt.x, src_pt.y, dest_pt.x, dest_pt.y, _legend_from_cargoes[i].colour, size);
00998           }
00999         }
01000       }
01001     }
01002 
01003     /* Draw station rect. */
01004     const Station *st;
01005     FOR_ALL_STATIONS(st) {
01006       /* Count total cargo and check for links for all shown cargo types. */
01007       uint total = 0;
01008       bool show = false;
01009       for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01010         if (_legend_from_cargoes[_cargotype_to_list_pos[cid]].show_on_map) {
01011           total += st->goods[cid].cargo.Count();
01012           show |= !st->goods[cid].routes.empty();
01013         }
01014       }
01015 
01016       if (!show) continue;
01017 
01018       /* Get rect size from total cargo count. */
01019       int d = 1;
01020       if (total >= 200) d++;
01021       if (total >= 400) d++;
01022       if (total >= 800) d++;
01023       if (total >= 1600) d++;
01024       if (total >= 3200) d++;
01025       if (total >= 6400) d++;
01026 
01027       /* Get top-left corner of the rect. */
01028       Point dest_pt = this->RemapTile(TileX(st->xy), TileY(st->xy));
01029       dest_pt.x -= this->subscroll + d/2;
01030       dest_pt.y -= d/2;
01031 
01032       /* Draw black border and company-colour inset. */
01033       byte colour = _colour_gradient[Company::IsValidID(st->owner) ? Company::Get(st->owner)->colour : COLOUR_GREY][6];
01034       GfxFillRect(dest_pt.x - 1, dest_pt.y - 1, dest_pt.x + d + 1, dest_pt.y + d + 1, PC_BLACK); // Draw black frame
01035       GfxFillRect(dest_pt.x, dest_pt.y, dest_pt.x + d, dest_pt.y + d, colour); // Draw colour insert
01036     }
01037   }
01038 
01045   static inline void DrawVertMapIndicator(int x, int y, int y2)
01046   {
01047     GfxFillRect(x, y,      x, y + 3, PC_VERY_LIGHT_YELLOW);
01048     GfxFillRect(x, y2 - 3, x, y2,    PC_VERY_LIGHT_YELLOW);
01049   }
01050 
01057   static inline void DrawHorizMapIndicator(int x, int x2, int y)
01058   {
01059     GfxFillRect(x,      y, x + 3, y, PC_VERY_LIGHT_YELLOW);
01060     GfxFillRect(x2 - 3, y, x2,    y, PC_VERY_LIGHT_YELLOW);
01061   }
01062 
01066   void DrawMapIndicators() const
01067   {
01068     /* Find main viewport. */
01069     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
01070 
01071     Point tile = InverseRemapCoords(vp->virtual_left, vp->virtual_top);
01072     Point tl = this->RemapTile(tile.x >> 4, tile.y >> 4);
01073     tl.x -= this->subscroll;
01074 
01075     tile = InverseRemapCoords(vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height);
01076     Point br = this->RemapTile(tile.x >> 4, tile.y >> 4);
01077     br.x -= this->subscroll;
01078 
01079     SmallMapWindow::DrawVertMapIndicator(tl.x, tl.y, br.y);
01080     SmallMapWindow::DrawVertMapIndicator(br.x, tl.y, br.y);
01081 
01082     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, tl.y);
01083     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, br.y);
01084   }
01085 
01097   void DrawSmallMap(DrawPixelInfo *dpi) const
01098   {
01099     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01100     DrawPixelInfo *old_dpi;
01101 
01102     old_dpi = _cur_dpi;
01103     _cur_dpi = dpi;
01104 
01105     /* Clear it */
01106     GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, PC_BLACK);
01107 
01108     /* Which tile is displayed at (dpi->left, dpi->top)? */
01109     int dx;
01110     Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
01111     int tile_x = this->scroll_x / (int)TILE_SIZE + tile.x;
01112     int tile_y = this->scroll_y / (int)TILE_SIZE + tile.y;
01113 
01114     void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
01115     int x = - dx - 4;
01116     int y = 0;
01117 
01118     for (;;) {
01119       /* Distance from left edge */
01120       if (x >= -3) {
01121         if (x >= dpi->width) break; // Exit the loop.
01122 
01123         int end_pos = min(dpi->width, x + 4);
01124         int reps = (dpi->height - y + 1) / 2; // Number of lines.
01125         if (reps > 0) {
01126           this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
01127         }
01128       }
01129 
01130       if (y == 0) {
01131         tile_y += this->zoom;
01132         y++;
01133         ptr = blitter->MoveTo(ptr, 0, 1);
01134       } else {
01135         tile_x -= this->zoom;
01136         y--;
01137         ptr = blitter->MoveTo(ptr, 0, -1);
01138       }
01139       ptr = blitter->MoveTo(ptr, 2, 0);
01140       x += 2;
01141     }
01142 
01143     /* Draw vehicles */
01144     if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
01145 
01146     /* Draw route links. */
01147     if (this->map_type == SMT_ROUTE_LINKS) this->DrawRouteLinks();
01148 
01149     /* Draw town names */
01150     if (this->show_towns) this->DrawTowns(dpi);
01151 
01152     /* Draw map indicators */
01153     this->DrawMapIndicators();
01154 
01155     _cur_dpi = old_dpi;
01156   }
01157 
01161   void SetupWidgetData()
01162   {
01163     StringID legend_tooltip;
01164     StringID enable_all_tooltip;
01165     StringID disable_all_tooltip;
01166     int plane;
01167     switch (this->map_type) {
01168       case SMT_INDUSTRY:
01169         legend_tooltip = STR_SMALLMAP_TOOLTIP_INDUSTRY_SELECTION;
01170         enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_INDUSTRIES;
01171         disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_INDUSTRIES;
01172         plane = 0;
01173         break;
01174 
01175       case SMT_OWNER:
01176         legend_tooltip = STR_SMALLMAP_TOOLTIP_COMPANY_SELECTION;
01177         enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_COMPANIES;
01178         disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_COMPANIES;
01179         plane = 0;
01180         break;
01181 
01182       case SMT_ROUTE_LINKS:
01183         legend_tooltip = STR_SMALLMAP_TOOLTIP_ROUTELINK_SELECTION;
01184         enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_ROUTELINKS;
01185         disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_ROUTELINKS;
01186         plane = 0;
01187         break;
01188 
01189       default:
01190         legend_tooltip = STR_NULL;
01191         enable_all_tooltip = STR_NULL;
01192         disable_all_tooltip = STR_NULL;
01193         plane = 1;
01194         break;
01195     }
01196 
01197     this->GetWidget<NWidgetCore>(SM_WIDGET_LEGEND)->SetDataTip(STR_NULL, legend_tooltip);
01198     this->GetWidget<NWidgetCore>(SM_WIDGET_ENABLE_ALL)->SetDataTip(STR_SMALLMAP_ENABLE_ALL, enable_all_tooltip);
01199     this->GetWidget<NWidgetCore>(SM_WIDGET_DISABLE_ALL)->SetDataTip(STR_SMALLMAP_DISABLE_ALL, disable_all_tooltip);
01200     this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECT_BUTTONS)->SetDisplayedPlane(plane);
01201   }
01202 
01203 public:
01204   uint min_number_of_columns;    
01205 
01206   SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
01207   {
01208     this->InitNested(desc, window_number);
01209     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
01210 
01211     BuildLandLegend();
01212     this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
01213 
01214     this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
01215 
01216     this->SetupWidgetData();
01217 
01218     this->SetZoomLevel(ZLC_INITIALIZE, NULL);
01219     this->SmallMapCenterOnCurrentPos();
01220   }
01221 
01226   inline uint GetMinLegendWidth() const
01227   {
01228     return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
01229   }
01230 
01235   inline uint GetNumberColumnsLegend(uint width) const
01236   {
01237     return width / this->column_width;
01238   }
01239 
01245   uint GetLegendHeight(uint num_columns) const
01246   {
01247     uint num_rows = max(this->min_number_of_fixed_rows, CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), num_columns));
01248     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
01249   }
01250 
01251   virtual void SetStringParameters(int widget) const
01252   {
01253     switch (widget) {
01254       case SM_WIDGET_CAPTION:
01255         SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
01256         break;
01257     }
01258   }
01259 
01260   virtual void OnInit()
01261   {
01262     uint min_width = 0;
01263     this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
01264     this->min_number_of_fixed_rows = 0;
01265     for (uint i = 0; i < lengthof(_legend_table); i++) {
01266       uint height = 0;
01267       uint num_columns = 1;
01268       for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
01269         StringID str;
01270         if (i == SMT_INDUSTRY) {
01271           SetDParam(0, tbl->legend);
01272           SetDParam(1, IndustryPool::MAX_SIZE);
01273           str = STR_SMALLMAP_INDUSTRY;
01274         } else if (i == SMT_OWNER) {
01275           if (tbl->company != INVALID_COMPANY) {
01276             if (!Company::IsValidID(tbl->company)) {
01277               /* Rebuild the owner legend. */
01278               BuildOwnerLegend();
01279               this->OnInit();
01280               return;
01281             }
01282             /* Non-fixed legend entries for the owner view. */
01283             SetDParam(0, tbl->company);
01284             str = STR_SMALLMAP_COMPANY;
01285           } else {
01286             str = tbl->legend;
01287           }
01288         } else if (i == SMT_ROUTE_LINKS) {
01289           SetDParam(0, tbl->legend);
01290           str = STR_SMALLMAP_CARGO;
01291         } else {
01292           if (tbl->col_break) {
01293             this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01294             height = 0;
01295             num_columns++;
01296           }
01297           height++;
01298           str = tbl->legend;
01299         }
01300         min_width = max(GetStringBoundingBox(str).width, min_width);
01301       }
01302       this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01303       this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
01304     }
01305 
01306     /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
01307     this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01308   }
01309 
01310   virtual void OnPaint()
01311   {
01312     if (this->map_type == SMT_OWNER) {
01313       for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01314         if (tbl->company != INVALID_COMPANY && !Company::IsValidID(tbl->company)) {
01315           /* Rebuild the owner legend. */
01316           BuildOwnerLegend();
01317           this->InvalidateData(1);
01318           break;
01319         }
01320       }
01321     }
01322 
01323     this->DrawWidgets();
01324   }
01325 
01326   virtual void DrawWidget(const Rect &r, int widget) const
01327   {
01328     switch (widget) {
01329       case SM_WIDGET_MAP: {
01330         DrawPixelInfo new_dpi;
01331         if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
01332         this->DrawSmallMap(&new_dpi);
01333         break;
01334       }
01335 
01336       case SM_WIDGET_LEGEND: {
01337         uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
01338         uint number_of_rows = max((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER || this->map_type == SMT_ROUTE_LINKS) ? CeilDiv(max(_smallmap_company_count, max(_smallmap_industry_count, _smallmap_cargo_count)), columns) : 0, this->min_number_of_fixed_rows);
01339         bool rtl = _current_text_dir == TD_RTL;
01340         uint y_org = r.top + WD_FRAMERECT_TOP;
01341         uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
01342         uint y = y_org;
01343         uint i = 0; // Row counter for industry legend.
01344         uint row_height = FONT_HEIGHT_SMALL;
01345 
01346         uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
01347         uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
01348         uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
01349         uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
01350 
01351         for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01352           if (tbl->col_break || ((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER || this->map_type == SMT_ROUTE_LINKS) && i++ >= number_of_rows)) {
01353             /* Column break needed, continue at top, COLUMN_WIDTH pixels
01354              * (one "row") to the right. */
01355             x += rtl ? -(int)this->column_width : this->column_width;
01356             y = y_org;
01357             i = 1;
01358           }
01359 
01360           if (this->map_type == SMT_INDUSTRY) {
01361             /* Industry name must be formatted, since it's not in tiny font in the specs.
01362              * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
01363             SetDParam(0, tbl->legend);
01364             SetDParam(1, Industry::GetIndustryTypeCount(tbl->type));
01365             if (!tbl->show_on_map) {
01366               /* Simply draw the string, not the black border of the legend colour.
01367                * This will enforce the idea of the disabled item */
01368               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
01369             } else {
01370               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
01371               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK); // Outer border of the legend colour
01372             }
01373           } else if (this->map_type == SMT_ROUTE_LINKS) {
01374             /* Cargo name needs formatting for tiny font. */
01375             SetDParam(0, tbl->legend);
01376             if (!tbl->show_on_map) {
01377               /* Draw only the string and not the border of the legend colour. */
01378               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_CARGO, TC_GREY);
01379             } else {
01380               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_CARGO, TC_BLACK);
01381               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK); // Outer border of the legend colour
01382             }
01383           } else if (this->map_type == SMT_OWNER && tbl->company != INVALID_COMPANY) {
01384             SetDParam(0, tbl->company);
01385             if (!tbl->show_on_map) {
01386               /* Simply draw the string, not the black border of the legend colour.
01387                * This will enforce the idea of the disabled item */
01388               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_GREY);
01389             } else {
01390               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_BLACK);
01391               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK); // Outer border of the legend colour
01392             }
01393           } else {
01394             if (this->map_type == SMT_CONTOUR) SetDParam(0, tbl->height * TILE_HEIGHT_STEP);
01395 
01396             /* Anything that is not an industry or a company is using normal process */
01397             GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK);
01398             DrawString(x + text_left, x + text_right, y, tbl->legend);
01399           }
01400           GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
01401 
01402           y += row_height;
01403         }
01404       }
01405     }
01406   }
01407 
01412   void SwitchMapType(SmallMapType map_type)
01413   {
01414     this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
01415     this->map_type = map_type;
01416     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
01417 
01418     this->SetupWidgetData();
01419 
01420     this->SetDirty();
01421   }
01422 
01423   virtual void OnClick(Point pt, int widget, int click_count)
01424   {
01425     /* User clicked something, notify the industry chain window to stop sending newly selected industries. */
01426     InvalidateWindowClassesData(WC_INDUSTRY_CARGOES, NUM_INDUSTRYTYPES);
01427 
01428     switch (widget) {
01429       case SM_WIDGET_MAP: { // Map window
01430         /*
01431          * XXX: scrolling with the left mouse button is done by subsequently
01432          * clicking with the left mouse button; clicking once centers the
01433          * large map at the selected point. So by unclicking the left mouse
01434          * button here, it gets reclicked during the next inputloop, which
01435          * would make it look like the mouse is being dragged, while it is
01436          * actually being (virtually) clicked every inputloop.
01437          */
01438         _left_button_clicked = false;
01439 
01440         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01441         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01442         int sub;
01443         pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
01444         pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
01445             this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
01446 
01447         w->viewport->follow_vehicle = INVALID_VEHICLE;
01448         w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width  >> 1);
01449         w->viewport->dest_scrollpos_y = pt.y - (w->viewport->virtual_height >> 1);
01450 
01451         this->SetDirty();
01452         break;
01453       }
01454 
01455       case SM_WIDGET_ZOOM_IN:
01456       case SM_WIDGET_ZOOM_OUT: {
01457         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01458         Point pt = {wid->current_x / 2, wid->current_y / 2};
01459         this->SetZoomLevel((widget == SM_WIDGET_ZOOM_IN) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01460         SndPlayFx(SND_15_BEEP);
01461         break;
01462       }
01463 
01464       case SM_WIDGET_CONTOUR:    // Show land contours
01465       case SM_WIDGET_VEHICLES:   // Show vehicles
01466       case SM_WIDGET_INDUSTRIES: // Show industries
01467       case SM_WIDGET_ROUTE_LINKS:// Show route links
01468       case SM_WIDGET_ROUTES:     // Show transport routes
01469       case SM_WIDGET_VEGETATION: // Show vegetation
01470       case SM_WIDGET_OWNERS:     // Show land owners
01471         this->SwitchMapType((SmallMapType)(widget - SM_WIDGET_CONTOUR));
01472         SndPlayFx(SND_15_BEEP);
01473         break;
01474 
01475       case SM_WIDGET_CENTERMAP: // Center the smallmap again
01476         this->SmallMapCenterOnCurrentPos();
01477         this->HandleButtonClick(SM_WIDGET_CENTERMAP);
01478         SndPlayFx(SND_15_BEEP);
01479         break;
01480 
01481       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
01482         this->show_towns = !this->show_towns;
01483         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
01484 
01485         this->SetDirty();
01486         SndPlayFx(SND_15_BEEP);
01487         break;
01488 
01489       case SM_WIDGET_LEGEND: // Legend
01490         if (this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER || this->map_type == SMT_ROUTE_LINKS) {
01491           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
01492           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
01493           uint columns = this->GetNumberColumnsLegend(wi->current_x);
01494           uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, max(_smallmap_industry_count, _smallmap_cargo_count)), columns), this->min_number_of_fixed_rows);
01495           if (line >= number_of_rows) break;
01496 
01497           bool rtl = _current_text_dir == TD_RTL;
01498           int x = pt.x - wi->pos_x;
01499           if (rtl) x = wi->current_x - x;
01500           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
01501 
01502           /* If industry type small map*/
01503           if (this->map_type == SMT_INDUSTRY) {
01504             /* If click on industries label, find right industry type and enable/disable it. */
01505             int industry_pos = (column * number_of_rows) + line;
01506             if (industry_pos < _smallmap_industry_count) {
01507               if (_ctrl_pressed) {
01508                 /* Disable all, except the clicked one. */
01509                 bool changes = false;
01510                 for (int i = 0; i != _smallmap_industry_count; i++) {
01511                   bool new_state = i == industry_pos;
01512                   if (_legend_from_industries[i].show_on_map != new_state) {
01513                     changes = true;
01514                     _legend_from_industries[i].show_on_map = new_state;
01515                   }
01516                 }
01517                 if (!changes) {
01518                   /* Nothing changed? Then show all (again). */
01519                   for (int i = 0; i != _smallmap_industry_count; i++) {
01520                     _legend_from_industries[i].show_on_map = true;
01521                   }
01522                 }
01523               } else {
01524                 _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
01525               }
01526             }
01527           } else if (this->map_type == SMT_OWNER) {
01528             /* If click on companies label, find right company and enable/disable it. */
01529             int company_pos = (column * number_of_rows) + line;
01530             if (company_pos < NUM_NO_COMPANY_ENTRIES) break;
01531             if (company_pos < _smallmap_company_count) {
01532               if (_ctrl_pressed) {
01533                 /* Disable all, except the clicked one */
01534                 bool changes = false;
01535                 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01536                   bool new_state = i == company_pos;
01537                   if (_legend_land_owners[i].show_on_map != new_state) {
01538                     changes = true;
01539                     _legend_land_owners[i].show_on_map = new_state;
01540                   }
01541                 }
01542                 if (!changes) {
01543                   /* Nothing changed? Then show all (again). */
01544                   for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01545                     _legend_land_owners[i].show_on_map = true;
01546                   }
01547                 }
01548               } else {
01549                 _legend_land_owners[company_pos].show_on_map = !_legend_land_owners[company_pos].show_on_map;
01550               }
01551             }
01552           } else if (this->map_type == SMT_ROUTE_LINKS) {
01553             /* If click on cargo label, find right cargo type and enable/disable it. */
01554             int cargo_pos = (column * number_of_rows) + line;
01555             if (cargo_pos < _smallmap_cargo_count) {
01556               if (_ctrl_pressed) {
01557                 /* Disable all, except the clicked one */
01558                 bool changes = false;
01559                 for (int i = 0; i != _smallmap_cargo_count; i++) {
01560                   bool new_state = i == cargo_pos;
01561                   if (_legend_from_cargoes[i].show_on_map != new_state) {
01562                     changes = true;
01563                     _legend_from_cargoes[i].show_on_map = new_state;
01564                   }
01565                 }
01566                 if (!changes) {
01567                   /* Nothing changed? Then show all (again). */
01568                   for (int i = 0; i != _smallmap_cargo_count; i++) {
01569                     _legend_from_cargoes[i].show_on_map = true;
01570                   }
01571                 }
01572               } else {
01573                 _legend_from_cargoes[cargo_pos].show_on_map = !_legend_from_cargoes[cargo_pos].show_on_map;
01574               }
01575             }
01576           }
01577           this->SetDirty();
01578         }
01579         break;
01580 
01581       case SM_WIDGET_ENABLE_ALL:
01582         if (this->map_type == SMT_INDUSTRY) {
01583           for (int i = 0; i != _smallmap_industry_count; i++) {
01584             _legend_from_industries[i].show_on_map = true;
01585           }
01586         } else if (this->map_type == SMT_OWNER) {
01587           for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01588             _legend_land_owners[i].show_on_map = true;
01589           }
01590         } else if (this->map_type == SMT_ROUTE_LINKS) {
01591           for (int i = 0; i != _smallmap_cargo_count; i++) {
01592             _legend_from_cargoes[i].show_on_map = true;
01593           }
01594         }
01595         this->SetDirty();
01596         break;
01597 
01598       case SM_WIDGET_DISABLE_ALL:
01599         if (this->map_type == SMT_INDUSTRY) {
01600           for (int i = 0; i != _smallmap_industry_count; i++) {
01601             _legend_from_industries[i].show_on_map = false;
01602           }
01603         } else if (this->map_type == SMT_OWNER) {
01604           for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01605             _legend_land_owners[i].show_on_map = false;
01606           }
01607         } else if (this->map_type == SMT_ROUTE_LINKS) {
01608           for (int i = 0; i != _smallmap_cargo_count; i++) {
01609             _legend_from_cargoes[i].show_on_map = false;
01610           }
01611         }
01612         this->SetDirty();
01613         break;
01614 
01615       case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
01616         _smallmap_show_heightmap = !_smallmap_show_heightmap;
01617         this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
01618         this->SetDirty();
01619         break;
01620     }
01621   }
01622 
01630   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01631   {
01632     if (!gui_scope) return;
01633     switch (data) {
01634       case 1:
01635         /* The owner legend has already been rebuilt. */
01636         this->ReInit();
01637         break;
01638 
01639       case 0: {
01640         extern uint64 _displayed_industries;
01641         if (this->map_type != SMT_INDUSTRY) this->SwitchMapType(SMT_INDUSTRY);
01642 
01643         for (int i = 0; i != _smallmap_industry_count; i++) {
01644           _legend_from_industries[i].show_on_map = HasBit(_displayed_industries, _legend_from_industries[i].type);
01645         }
01646         break;
01647       }
01648 
01649       default: NOT_REACHED();
01650     }
01651     this->SetDirty();
01652   }
01653 
01654   virtual bool OnRightClick(Point pt, int widget)
01655   {
01656     if (widget != SM_WIDGET_MAP || _scrolling_viewport) return false;
01657 
01658     _scrolling_viewport = true;
01659     return true;
01660   }
01661 
01662   virtual void OnMouseWheel(int wheel)
01663   {
01664     if (_settings_client.gui.scrollwheel_scrolling == 0) {
01665       const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01666       int cursor_x = _cursor.pos.x - this->left - wid->pos_x;
01667       int cursor_y = _cursor.pos.y - this->top  - wid->pos_y;
01668       if (IsInsideMM(cursor_x, 0, wid->current_x) && IsInsideMM(cursor_y, 0, wid->current_y)) {
01669         Point pt = {cursor_x, cursor_y};
01670         this->SetZoomLevel((wheel < 0) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01671       }
01672     }
01673   }
01674 
01675   virtual void OnTick()
01676   {
01677     /* Update the window every now and then */
01678     if (--this->refresh != 0) return;
01679 
01680     this->refresh = FORCE_REFRESH_PERIOD;
01681     this->SetDirty();
01682   }
01683 
01691   void SetNewScroll(int sx, int sy, int sub)
01692   {
01693     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01694     Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
01695     hv.x *= this->zoom;
01696     hv.y *= this->zoom;
01697 
01698     if (sx < -hv.x) {
01699       sx = -hv.x;
01700       sub = 0;
01701     }
01702     if (sx > (int)(MapMaxX() * TILE_SIZE) - hv.x) {
01703       sx = MapMaxX() * TILE_SIZE - hv.x;
01704       sub = 0;
01705     }
01706     if (sy < -hv.y) {
01707       sy = -hv.y;
01708       sub = 0;
01709     }
01710     if (sy > (int)(MapMaxY() * TILE_SIZE) - hv.y) {
01711       sy = MapMaxY() * TILE_SIZE - hv.y;
01712       sub = 0;
01713     }
01714 
01715     this->scroll_x = sx;
01716     this->scroll_y = sy;
01717     this->subscroll = sub;
01718   }
01719 
01720   virtual void OnScroll(Point delta)
01721   {
01722     _cursor.fix_at = true;
01723 
01724     /* While tile is at (delta.x, delta.y)? */
01725     int sub;
01726     Point pt = this->PixelToTile(delta.x, delta.y, &sub);
01727     this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
01728 
01729     this->SetDirty();
01730   }
01731 
01732   void SmallMapCenterOnCurrentPos()
01733   {
01734     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
01735     Point pt = InverseRemapCoords(vp->virtual_left + vp->virtual_width  / 2, vp->virtual_top  + vp->virtual_height / 2);
01736 
01737     int sub;
01738     const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01739     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);
01740     this->SetNewScroll(sxy.x, sxy.y, sub);
01741     this->SetDirty();
01742   }
01743 };
01744 
01745 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
01746 bool SmallMapWindow::show_towns = true;
01747 
01756 class NWidgetSmallmapDisplay : public NWidgetContainer {
01757   const SmallMapWindow *smallmap_window; 
01758 public:
01759   NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
01760   {
01761     this->smallmap_window = NULL;
01762   }
01763 
01764   virtual void SetupSmallestSize(Window *w, bool init_array)
01765   {
01766     NWidgetBase *display = this->head;
01767     NWidgetBase *bar = display->next;
01768 
01769     display->SetupSmallestSize(w, init_array);
01770     bar->SetupSmallestSize(w, init_array);
01771 
01772     this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
01773     this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
01774     this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetLegendHeight(smallmap_window->min_number_of_columns));
01775     this->fill_x = max(display->fill_x, bar->fill_x);
01776     this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
01777     this->resize_x = max(display->resize_x, bar->resize_x);
01778     this->resize_y = min(display->resize_y, bar->resize_y);
01779   }
01780 
01781   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01782   {
01783     this->pos_x = x;
01784     this->pos_y = y;
01785     this->current_x = given_width;
01786     this->current_y = given_height;
01787 
01788     NWidgetBase *display = this->head;
01789     NWidgetBase *bar = display->next;
01790 
01791     if (sizing == ST_SMALLEST) {
01792       this->smallest_x = given_width;
01793       this->smallest_y = given_height;
01794       /* Make display and bar exactly equal to their minimal size. */
01795       display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
01796       bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
01797     }
01798 
01799     uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(this->smallmap_window->GetNumberColumnsLegend(given_width - bar->smallest_x)));
01800     uint display_height = given_height - bar_height;
01801     display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
01802     bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
01803   }
01804 
01805   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
01806   {
01807     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01808     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01809       NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
01810       if (widget != NULL) return widget;
01811     }
01812     return NULL;
01813   }
01814 
01815   virtual void Draw(const Window *w)
01816   {
01817     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
01818   }
01819 };
01820 
01822 static const NWidgetPart _nested_smallmap_display[] = {
01823   NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
01824     NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
01825   EndContainer(),
01826 };
01827 
01829 static const NWidgetPart _nested_smallmap_bar[] = {
01830   NWidget(WWT_PANEL, COLOUR_BROWN),
01831     NWidget(NWID_HORIZONTAL),
01832       NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
01833       NWidget(NWID_VERTICAL),
01834         /* Top button row. */
01835         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01836           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN),
01837               SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), SetFill(1, 1),
01838           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP),
01839               SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER), SetFill(1, 1),
01840           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR),
01841               SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP), SetFill(1, 1),
01842           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES),
01843               SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP), SetFill(1, 1),
01844           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES),
01845               SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP), SetFill(1, 1),
01846           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTE_LINKS),
01847               SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_ROUTE_LINKS_ON_MAP), SetFill(1, 1),
01848         EndContainer(),
01849         /* Bottom button row. */
01850         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01851           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT),
01852               SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), SetFill(1, 1),
01853           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME),
01854               SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF), SetFill(1, 1),
01855           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES),
01856               SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON), SetFill(1, 1),
01857           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION),
01858               SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP), SetFill(1, 1),
01859           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS),
01860               SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP), SetFill(1, 1),
01861           NWidget(NWID_SPACER), SetFill(1, 1),
01862         EndContainer(),
01863         NWidget(NWID_SPACER), SetResize(0, 1),
01864       EndContainer(),
01865     EndContainer(),
01866   EndContainer(),
01867 };
01868 
01869 static NWidgetBase *SmallMapDisplay(int *biggest_index)
01870 {
01871   NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
01872 
01873   MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
01874   MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
01875   return map_display;
01876 }
01877 
01878 
01879 static const NWidgetPart _nested_smallmap_widgets[] = {
01880   NWidget(NWID_HORIZONTAL),
01881     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
01882     NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01883     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
01884     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
01885   EndContainer(),
01886   NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
01887   /* Bottom button row and resize box. */
01888   NWidget(NWID_HORIZONTAL),
01889     NWidget(WWT_PANEL, COLOUR_BROWN),
01890       NWidget(NWID_HORIZONTAL),
01891         NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECT_BUTTONS),
01892           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01893             NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_NULL),
01894             NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_NULL),
01895             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
01896           EndContainer(),
01897           NWidget(NWID_SPACER), SetFill(1, 1),
01898         EndContainer(),
01899         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
01900       EndContainer(),
01901     EndContainer(),
01902     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
01903   EndContainer(),
01904 };
01905 
01906 static const WindowDesc _smallmap_desc(
01907   WDP_AUTO, 446, 314,
01908   WC_SMALLMAP, WC_NONE,
01909   WDF_UNCLICK_BUTTONS,
01910   _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
01911 );
01912 
01916 void ShowSmallMap()
01917 {
01918   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
01919 }
01920 
01929 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
01930 {
01931   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
01932 
01933   /* If a user scrolls to a tile (via what way what so ever) and already is on
01934    * that tile (e.g.: pressed twice), move the smallmap to that location,
01935    * so you directly see where you are on the smallmap. */
01936 
01937   if (res) return res;
01938 
01939   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
01940   if (w != NULL) w->SmallMapCenterOnCurrentPos();
01941 
01942   return res;
01943 }

Generated on Sun May 8 07:30:19 2011 for OpenTTD by  doxygen 1.6.1