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 }

Generated on Sat Jul 17 18:43:23 2010 for OpenTTD by  doxygen 1.6.1