smallmap_gui.cpp

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

Generated on Fri Dec 31 17:15:38 2010 for OpenTTD by  doxygen 1.6.1