# smallmap_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: smallmap_gui.cpp 18827 2010-01-16 15:24:00Z 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 #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_CONTOUR,
    00040   SM_WIDGET_VEHICLES,
    00041   SM_WIDGET_INDUSTRIES,
    00042   SM_WIDGET_ROUTES,
    00043   SM_WIDGET_VEGETATION,
    00044   SM_WIDGET_OWNERS,
    00045   SM_WIDGET_CENTERMAP,
    00046   SM_WIDGET_TOGGLETOWNNAME,
    00047   SM_WIDGET_SELECTINDUSTRIES,
    00048   SM_WIDGET_ENABLEINDUSTRIES,
    00049   SM_WIDGET_DISABLEINDUSTRIES,
    00050   SM_WIDGET_SHOW_HEIGHT,
    00051 };
    00052
    00053 static int _smallmap_industry_count;
    00054
    00056 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, false}
    00057
    00058 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, true, true, false}
    00059
    00061 #define MS(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, true}
    00062
    00064 struct LegendAndColour {
    00065   uint8 colour;
    00066   StringID legend;
    00067   IndustryType type;
    00068   bool show_on_map;
    00069   bool end;
    00070   bool col_break;
    00071 };
    00072
    00074 static const LegendAndColour _legend_land_contours[] = {
    00075   MK(0x5A, STR_SMALLMAP_LEGENDA_100M),
    00076   MK(0x5C, STR_SMALLMAP_LEGENDA_200M),
    00077   MK(0x5E, STR_SMALLMAP_LEGENDA_300M),
    00078   MK(0x1F, STR_SMALLMAP_LEGENDA_400M),
    00079   MK(0x27, STR_SMALLMAP_LEGENDA_500M),
    00080
    00081   MS(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
    00082   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
    00083   MK(0x98, STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
    00084   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00085   MK(0x0F, STR_SMALLMAP_LEGENDA_VEHICLES),
    00086   MKEND()
    00087 };
    00088
    00089 static const LegendAndColour _legend_vehicles[] = {
    00090   MK(0xB8, STR_SMALLMAP_LEGENDA_TRAINS),
    00091   MK(0xBF, STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
    00092   MK(0x98, STR_SMALLMAP_LEGENDA_SHIPS),
    00093   MK(0x0F, STR_SMALLMAP_LEGENDA_AIRCRAFT),
    00094
    00095   MS(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
    00096   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00097   MKEND()
    00098 };
    00099
    00100 static const LegendAndColour _legend_routes[] = {
    00101   MK(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
    00102   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
    00103   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00104
    00105   MS(0x56, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
    00106   MK(0xC2, STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
    00107   MK(0xBF, STR_SMALLMAP_LEGENDA_BUS_STATION),
    00108   MK(0xB8, STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
    00109   MK(0x98, STR_SMALLMAP_LEGENDA_DOCK),
    00110   MKEND()
    00111 };
    00112
    00113 static const LegendAndColour _legend_vegetation[] = {
    00114   MK(0x52, STR_SMALLMAP_LEGENDA_ROUGH_LAND),
    00115   MK(0x54, STR_SMALLMAP_LEGENDA_GRASS_LAND),
    00116   MK(0x37, STR_SMALLMAP_LEGENDA_BARE_LAND),
    00117   MK(0x25, STR_SMALLMAP_LEGENDA_FIELDS),
    00118   MK(0x57, STR_SMALLMAP_LEGENDA_TREES),
    00119   MK(0xD0, STR_SMALLMAP_LEGENDA_FOREST),
    00120
    00121   MS(0x0A, STR_SMALLMAP_LEGENDA_ROCKS),
    00122   MK(0xC2, STR_SMALLMAP_LEGENDA_DESERT),
    00123   MK(0x98, STR_SMALLMAP_LEGENDA_SNOW),
    00124   MK(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
    00125   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
    00126   MKEND()
    00127 };
    00128
    00129 static const LegendAndColour _legend_land_owners[] = {
    00130   MK(0xCA, STR_SMALLMAP_LEGENDA_WATER),
    00131   MK(0x54, STR_SMALLMAP_LEGENDA_NO_OWNER),
    00132   MK(0xB4, STR_SMALLMAP_LEGENDA_TOWNS),
    00133   MK(0x20, STR_SMALLMAP_LEGENDA_INDUSTRIES),
    00134   MKEND()
    00135 };
    00136 #undef MK
    00137 #undef MS
    00138 #undef MKEND
    00139
    00142 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
    00143 /* For connecting industry type to position in industries list(small map legend) */
    00144 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
    00146 static bool _smallmap_industry_show_heightmap;
    00147
    00151 void BuildIndustriesLegend()
    00152 {
    00153   uint j = 0;
    00154
    00155   /* Add each name */
    00156   for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
    00157     const IndustrySpec *indsp = GetIndustrySpec(i);
    00158     if (indsp->enabled) {
    00159       _legend_from_industries[j].legend = indsp->name;
    00160       _legend_from_industries[j].colour = indsp->map_colour;
    00161       _legend_from_industries[j].type = i;
    00162       _legend_from_industries[j].show_on_map = true;
    00163       _legend_from_industries[j].col_break = false;
    00164       _legend_from_industries[j].end = false;
    00165
    00166       /* Store widget number for this industry type */
    00167       _industry_to_list_pos[i] = j;
    00168       j++;
    00169     }
    00170   }
    00171   /* Terminate the list */
    00172   _legend_from_industries[j].end = true;
    00173
    00174   /* Store number of enabled industries */
    00175   _smallmap_industry_count = j;
    00176 }
    00177
    00178 static const LegendAndColour * const _legend_table[] = {
    00179   _legend_land_contours,
    00180   _legend_vehicles,
    00181   _legend_from_industries,
    00182   _legend_routes,
    00183   _legend_vegetation,
    00184   _legend_land_owners,
    00185 };
    00186
    00187 #define MKCOLOUR(x) TO_LE32X(x)
    00188
    00192 static const uint32 _map_height_bits[] = {
    00193   MKCOLOUR(0x5A5A5A5A),
    00194   MKCOLOUR(0x5A5B5A5B),
    00195   MKCOLOUR(0x5B5B5B5B),
    00196   MKCOLOUR(0x5B5C5B5C),
    00197   MKCOLOUR(0x5C5C5C5C),
    00198   MKCOLOUR(0x5C5D5C5D),
    00199   MKCOLOUR(0x5D5D5D5D),
    00200   MKCOLOUR(0x5D5E5D5E),
    00201   MKCOLOUR(0x5E5E5E5E),
    00202   MKCOLOUR(0x5E5F5E5F),
    00203   MKCOLOUR(0x5F5F5F5F),
    00204   MKCOLOUR(0x5F1F5F1F),
    00205   MKCOLOUR(0x1F1F1F1F),
    00206   MKCOLOUR(0x1F271F27),
    00207   MKCOLOUR(0x27272727),
    00208   MKCOLOUR(0x27272727),
    00209 };
    00210 assert_compile(lengthof(_map_height_bits) == MAX_TILE_HEIGHT + 1);
    00211
    00212 struct AndOr {
    00213   uint32 mor;
    00214   uint32 mand;
    00215 };
    00216
    00217 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
    00218 {
    00219   return (colour & mask->mand) | mask->mor;
    00220 }
    00221
    00222
    00224 static const AndOr _smallmap_contours_andor[] = {
    00225   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
    00226   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
    00227   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
    00228   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
    00229   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
    00230   {MKCOLOUR(0x98989898), MKCOLOUR(0x00000000)}, // MP_STATION
    00231   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
    00232   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
    00233   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
    00234   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
    00235   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_UNMOVABLE
    00236   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
    00237 };
    00238
    00240 static const AndOr _smallmap_vehicles_andor[] = {
    00241   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
    00242   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
    00243   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
    00244   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
    00245   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
    00246   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_STATION
    00247   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
    00248   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
    00249   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
    00250   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
    00251   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_UNMOVABLE
    00252   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00253 };
    00254
    00255 typedef uint32 GetSmallMapPixels(TileIndex tile);
    00256
    00257
    00258 static inline TileType GetEffectiveTileType(TileIndex tile)
    00259 {
    00260   TileType t = GetTileType(tile);
    00261
    00262   if (t == MP_TUNNELBRIDGE) {
    00263     TransportType tt = GetTunnelBridgeTransportType(tile);
    00264
    00265     switch (tt) {
    00266       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
    00267       case TRANSPORT_ROAD: t = MP_ROAD;    break;
    00268       default:             t = MP_WATER;   break;
    00269     }
    00270   }
    00271   return t;
    00272 }
    00273
    00279 static inline uint32 GetSmallMapContoursPixels(TileIndex tile)
    00280 {
    00281   TileType t = GetEffectiveTileType(tile);
    00282
    00283   return ApplyMask(_map_height_bits[TileHeight(tile)], &_smallmap_contours_andor[t]);
    00284 }
    00285
    00292 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile)
    00293 {
    00294   TileType t = GetEffectiveTileType(tile);
    00295
    00296   return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00297 }
    00298
    00305 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
    00306 {
    00307   TileType t = GetEffectiveTileType(tile);
    00308
    00309   if (t == MP_INDUSTRY) {
    00310     /* If industry is allowed to be seen, use its colour on the map */
    00311     if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
    00312       return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
    00313     } else {
    00314       /* Otherwise, return the colour of the clear tiles, which will make it disappear */
    00315       t = MP_CLEAR;
    00316     }
    00317   }
    00318
    00319   return ApplyMask(_smallmap_industry_show_heightmap ? _map_height_bits[TileHeight(tile)] : MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00320 }
    00321
    00328 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
    00329 {
    00330   TileType t = GetEffectiveTileType(tile);
    00331
    00332   if (t == MP_STATION) {
    00333     switch (GetStationType(tile)) {
    00334       case STATION_RAIL:    return MKCOLOUR(0x56565656);
    00335       case STATION_AIRPORT: return MKCOLOUR(0xB8B8B8B8);
    00336       case STATION_TRUCK:   return MKCOLOUR(0xC2C2C2C2);
    00337       case STATION_BUS:     return MKCOLOUR(0xBFBFBFBF);
    00338       case STATION_DOCK:    return MKCOLOUR(0x98989898);
    00339       default:              return MKCOLOUR(0xFFFFFFFF);
    00340     }
    00341   }
    00342
    00343   /* Ground colour */
    00344   return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_contours_andor[t]);
    00345 }
    00346
    00347
    00348 static const uint32 _vegetation_clear_bits[] = {
    00349   MKCOLOUR(0x54545454),
    00350   MKCOLOUR(0x52525252),
    00351   MKCOLOUR(0x0A0A0A0A),
    00352   MKCOLOUR(0x25252525),
    00353   MKCOLOUR(0x98989898),
    00354   MKCOLOUR(0xC2C2C2C2),
    00355   MKCOLOUR(0x54545454),
    00356   MKCOLOUR(0x54545454),
    00357 };
    00358
    00365 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile)
    00366 {
    00367   TileType t = GetEffectiveTileType(tile);
    00368
    00369   switch (t) {
    00370     case MP_CLEAR:
    00371       return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR(0x37373737) : _vegetation_clear_bits[GetClearGround(tile)];
    00372
    00373     case MP_INDUSTRY:
    00374       return GetIndustrySpec(Industry::GetByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR(0xD0D0D0D0) : MKCOLOUR(0xB5B5B5B5);
    00375
    00376     case MP_TREES:
    00377       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(tile) == TREE_GROUND_ROUGH_SNOW) {
    00378         return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR(0x98575798) : MKCOLOUR(0xC25757C2);
    00379       }
    00380       return MKCOLOUR(0x54575754);
    00381
    00382     default:
    00383       return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00384   }
    00385 }
    00386
    00387
    00388 static uint32 _owner_colours[OWNER_END + 1];
    00389
    00396 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
    00397 {
    00398   Owner o;
    00399
    00400   switch (GetTileType(tile)) {
    00401     case MP_INDUSTRY: o = OWNER_END;          break;
    00402     case MP_HOUSE:    o = OWNER_TOWN;         break;
    00403     default:          o = GetTileOwner(tile); break;
    00404     /* FIXME: For MP_ROAD there are multiple owners.
    00405      * GetTileOwner returns the rail owner (level crossing) resp. the owner of ROADTYPE_ROAD (normal road),
    00406      * even if there are no ROADTYPE_ROAD bits on the tile.
    00407      */
    00408   }
    00409
    00410   return _owner_colours[o];
    00411 }
    00412
    00413
    00414 static const uint32 _smallmap_mask_left[3] = {
    00415   MKCOLOUR(0xFF000000),
    00416   MKCOLOUR(0xFFFF0000),
    00417   MKCOLOUR(0xFFFFFF00),
    00418 };
    00419
    00420 static const uint32 _smallmap_mask_right[] = {
    00421   MKCOLOUR(0x000000FF),
    00422   MKCOLOUR(0x0000FFFF),
    00423   MKCOLOUR(0x00FFFFFF),
    00424 };
    00425
    00426 /* Each tile has 4 x pixels and 1 y pixel */
    00427
    00429 static GetSmallMapPixels * const _smallmap_draw_procs[] = {
    00430   GetSmallMapContoursPixels,
    00431   GetSmallMapVehiclesPixels,
    00432   GetSmallMapIndustriesPixels,
    00433   GetSmallMapRoutesPixels,
    00434   GetSmallMapVegetationPixels,
    00435   GetSmallMapOwnerPixels,
    00436 };
    00437
    00439 static const byte _vehicle_type_colours[6] = {
    00440   184, 191, 152, 15, 215, 184
    00441 };
    00442
    00443
    00445 class SmallMapWindow : public Window {
    00447   enum SmallMapType {
    00448     SMT_CONTOUR,
    00449     SMT_VEHICLES,
    00450     SMT_INDUSTRY,
    00451     SMT_ROUTES,
    00452     SMT_VEGETATION,
    00453     SMT_OWNER,
    00454   };
    00455
    00456   static SmallMapType map_type;
    00457   static bool show_towns;
    00458
    00459   static const uint LEGEND_BLOB_WIDTH = 8;
    00460   static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
    00461   uint min_number_of_columns;
    00462   uint min_number_of_fixed_rows;
    00463   uint column_width;
    00464
    00465   int32 scroll_x;
    00466   int32 scroll_y;
    00467   int32 subscroll;
    00468
    00469   static const uint8 FORCE_REFRESH_PERIOD = 0x1F;
    00470   uint8 refresh;
    00471
    00478   inline int RemapX(int tile_x) const
    00479   {
    00480     return tile_x - this->scroll_x / TILE_SIZE;
    00481   }
    00482
    00489   inline int RemapY(int tile_y) const
    00490   {
    00491     return tile_y - this->scroll_y / TILE_SIZE;
    00492   }
    00493
    00508   void DrawSmallMapStuff(void *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, Blitter *blitter, GetSmallMapPixels *proc) const
    00509   {
    00510     void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
    00511     void *dst_ptr_end = blitter->MoveTo(dst_ptr_abs_end, -4, 0);
    00512
    00513     do {
    00514       /* Check if the tile (xc,yc) is within the map range */
    00515       uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
    00516       if (IsInsideMM(xc, min_xy, MapMaxX()) && IsInsideMM(yc, min_xy, MapMaxY())) {
    00517         /* Check if the dst pointer points to a pixel inside the screen buffer */
    00518         if (dst < _screen.dst_ptr) continue;
    00519         if (dst >= dst_ptr_abs_end) continue;
    00520
    00521         uint32 val = proc(TileXY(xc, yc)) & mask;
    00522         uint8 *val8 = (uint8 *)&val;
    00523
    00524         if (dst <= dst_ptr_end) {
    00525           blitter->SetPixelIfEmpty(dst, 0, 0, val8[0]);
    00526           blitter->SetPixelIfEmpty(dst, 1, 0, val8[1]);
    00527           blitter->SetPixelIfEmpty(dst, 2, 0, val8[2]);
    00528           blitter->SetPixelIfEmpty(dst, 3, 0, val8[3]);
    00529         } else {
    00530           /* It happens that there are only 1, 2 or 3 pixels left to fill, so
    00531            * in that special case, write till the end of the video-buffer */
    00532           int i = 0;
    00533           do {
    00534             blitter->SetPixelIfEmpty(dst, 0, 0, val8[i]);
    00535           } while (i++, dst = blitter->MoveTo(dst, 1, 0), dst < dst_ptr_abs_end);
    00536         }
    00537       }
    00538     /* Switch to next tile in the column */
    00539     } while (xc++, yc++, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
    00540   }
    00541
    00547   void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
    00548   {
    00549     const Vehicle *v;
    00550     FOR_ALL_VEHICLES(v) {
    00551       if (v->type == VEH_EFFECT) continue;
    00552       if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
    00553
    00554       /* Remap into flat coordinates. */
    00555       Point pt = RemapCoords(
    00556           this->RemapX(v->x_pos / TILE_SIZE),
    00557           this->RemapY(v->y_pos / TILE_SIZE),
    00558           0);
    00559       int x = pt.x;
    00560       int y = pt.y;
    00561
    00562       /* Check if y is out of bounds? */
    00563       y -= dpi->top;
    00564       if (!IsInsideMM(y, 0, dpi->height)) continue;
    00565
    00566       /* Default is to draw both pixels. */
    00567       bool skip = false;
    00568
    00569       /* Offset X coordinate */
    00570       x -= this->subscroll + 3 + dpi->left;
    00571
    00572       if (x < 0) {
    00573         /* if x+1 is 0, that means we're on the very left edge,
    00574          * and should thus only draw a single pixel */
    00575         if (++x != 0) continue;
    00576         skip = true;
    00577       } else if (x >= dpi->width - 1) {
    00578         /* Check if we're at the very right edge, and if so draw only a single pixel */
    00579         if (x != dpi->width - 1) continue;
    00580         skip = true;
    00581       }
    00582
    00583       /* Calculate pointer to pixel and the colour */
    00584       byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
    00585
    00586       /* And draw either one or two pixels depending on clipping */
    00587       blitter->SetPixel(dpi->dst_ptr, x, y, colour);
    00588       if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
    00589     }
    00590   }
    00591
    00596   void DrawTowns(const DrawPixelInfo *dpi) const
    00597   {
    00598     const Town *t;
    00599     FOR_ALL_TOWNS(t) {
    00600       /* Remap the town coordinate */
    00601       Point pt = RemapCoords(
    00602           this->RemapX(TileX(t->xy)),
    00603           this->RemapY(TileY(t->xy)),
    00604           0);
    00605       int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
    00606       int y = pt.y;
    00607
    00608       /* Check if the town sign is within bounds */
    00609       if (x + t->sign.width_small > dpi->left &&
    00610           x < dpi->left + dpi->width &&
    00611           y + FONT_HEIGHT_SMALL > dpi->top &&
    00612           y < dpi->top + dpi->height) {
    00613         /* And draw it. */
    00614         SetDParam(0, t->index);
    00615         DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
    00616       }
    00617     }
    00618   }
    00619
    00626   static inline void DrawVertMapIndicator(int x, int y, int y2)
    00627   {
    00628     GfxFillRect(x, y,      x, y + 3, 69);
    00629     GfxFillRect(x, y2 - 3, x, y2,    69);
    00630   }
    00631
    00638   static inline void DrawHorizMapIndicator(int x, int x2, int y)
    00639   {
    00640     GfxFillRect(x,      y, x + 3, y, 69);
    00641     GfxFillRect(x2 - 3, y, x2,    y, 69);
    00642   }
    00643
    00647   void DrawMapIndicators() const
    00648   {
    00649     /* Find main viewport. */
    00650     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    00651
    00652     Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
    00653
    00654     int x = vp->virtual_left - pt.x;
    00655     int y = vp->virtual_top - pt.y;
    00656     int x2 = (x + vp->virtual_width) / TILE_SIZE;
    00657     int y2 = (y + vp->virtual_height) / TILE_SIZE;
    00658     x /= TILE_SIZE;
    00659     y /= TILE_SIZE;
    00660
    00661     x -= this->subscroll;
    00662     x2 -= this->subscroll;
    00663
    00664     SmallMapWindow::DrawVertMapIndicator(x, y, y2);
    00665     SmallMapWindow::DrawVertMapIndicator(x2, y, y2);
    00666
    00667     SmallMapWindow::DrawHorizMapIndicator(x, x2, y);
    00668     SmallMapWindow::DrawHorizMapIndicator(x, x2, y2);
    00669   }
    00670
    00682   void DrawSmallMap(DrawPixelInfo *dpi) const
    00683   {
    00684     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00685     DrawPixelInfo *old_dpi;
    00686
    00687     old_dpi = _cur_dpi;
    00688     _cur_dpi = dpi;
    00689
    00690     /* Clear it */
    00691     GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
    00692
    00693     /* Setup owner table */
    00694     if (this->map_type == SMT_OWNER) {
    00695       const Company *c;
    00696
    00697       /* Fill with some special colours */
    00698       _owner_colours[OWNER_TOWN]  = MKCOLOUR(0xB4B4B4B4);
    00699       _owner_colours[OWNER_NONE]  = MKCOLOUR(0x54545454);
    00700       _owner_colours[OWNER_WATER] = MKCOLOUR(0xCACACACA);
    00701       _owner_colours[OWNER_END]   = MKCOLOUR(0x20202020); // Industry
    00702
    00703       /* Now fill with the company colours */
    00704       FOR_ALL_COMPANIES(c) {
    00705         _owner_colours[c->index] = _colour_gradient[c->colour][5] * 0x01010101;
    00706       }
    00707     }
    00708
    00709     int tile_x = this->scroll_x / TILE_SIZE;
    00710     int tile_y = this->scroll_y / TILE_SIZE;
    00711
    00712     int dx = dpi->left + this->subscroll;
    00713     tile_x -= dx / 4;
    00714     tile_y += dx / 4;
    00715     dx &= 3;
    00716
    00717     int dy = dpi->top;
    00718     tile_x += dy / 2;
    00719     tile_y += dy / 2;
    00720
    00721     if (dy & 1) {
    00722       tile_x++;
    00723       dx += 2;
    00724       if (dx > 3) {
    00725         dx -= 4;
    00726         tile_x--;
    00727         tile_y++;
    00728       }
    00729     }
    00730
    00731     void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
    00732     int x = - dx - 4;
    00733     int y = 0;
    00734
    00735     for (;;) {
    00736       uint32 mask = 0xFFFFFFFF;
    00737
    00738       /* Distance from left edge */
    00739       if (x >= -3) {
    00740         if (x < 0) {
    00741           /* Mask to use at the left edge */
    00742           mask = _smallmap_mask_left[x + 3];
    00743         }
    00744
    00745         /* Distance from right edge */
    00746         int t = dpi->width - x;
    00747         if (t < 4) {
    00748           if (t <= 0) break; // Exit loop
    00749           /* Mask to use at the right edge */
    00750           mask &= _smallmap_mask_right[t - 1];
    00751         }
    00752
    00753         /* Number of lines */
    00754         int reps = (dpi->height - y + 1) / 2;
    00755         if (reps > 0) {
    00756           this->DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, blitter, _smallmap_draw_procs[this->map_type]);
    00757         }
    00758       }
    00759
    00760       if (y == 0) {
    00761         tile_y++;
    00762         y++;
    00763         ptr = blitter->MoveTo(ptr, 0, 1);
    00764       } else {
    00765         tile_x--;
    00766         y--;
    00767         ptr = blitter->MoveTo(ptr, 0, -1);
    00768       }
    00769       ptr = blitter->MoveTo(ptr, 2, 0);
    00770       x += 2;
    00771     }
    00772
    00773     /* Draw vehicles */
    00774     if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
    00775
    00776     /* Draw town names */
    00777     if (this->show_towns) this->DrawTowns(dpi);
    00778
    00779     /* Draw map indicators */
    00780     this->DrawMapIndicators();
    00781
    00782     _cur_dpi = old_dpi;
    00783   }
    00784
    00785 public:
    00786   SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
    00787   {
    00788     this->InitNested(desc, window_number);
    00789     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00790
    00791     _smallmap_industry_show_heightmap = false;
    00792     this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
    00793
    00794     this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00795     this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
    00796
    00797     this->SmallMapCenterOnCurrentPos();
    00798   }
    00799
    00803   inline uint GetMaxLegendHeight() const
    00804   {
    00805     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + this->min_number_of_columns - 1) / this->min_number_of_columns);
    00806     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
    00807   }
    00808
    00812   inline uint GetMinLegendWidth() const
    00813   {
    00814     return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
    00815   }
    00816
    00820   inline uint GetNumberColumnsLegend(uint width) const
    00821   {
    00822     return width / this->column_width;
    00823   }
    00824
    00828   uint GetLegendHeight(uint width) const
    00829   {
    00830     uint num_columns = this->GetNumberColumnsLegend(width);
    00831     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + num_columns - 1) / num_columns);
    00832     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
    00833   }
    00834
    00835   virtual void SetStringParameters(int widget) const
    00836   {
    00837     switch (widget) {
    00838       case SM_WIDGET_CAPTION:
    00839         SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
    00840         break;
    00841     }
    00842   }
    00843
    00844   virtual void OnInit()
    00845   {
    00846     uint min_width = 0;
    00847     this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
    00848     this->min_number_of_fixed_rows = 0;
    00849     for (uint i = 0; i < lengthof(_legend_table); i++) {
    00850       uint height = 0;
    00851       uint num_columns = 1;
    00852       for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
    00853         StringID str;
    00854         if (i == SMT_INDUSTRY) {
    00855           SetDParam(0, tbl->legend);
    00856           SetDParam(1, IndustryPool::MAX_SIZE);
    00857           str = STR_SMALLMAP_INDUSTRY;
    00858         } else {
    00859           if (tbl->col_break) {
    00860             this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    00861             height = 0;
    00862             num_columns++;
    00863           }
    00864           height++;
    00865           str = tbl->legend;
    00866         }
    00867         min_width = max(GetStringBoundingBox(str).width, min_width);
    00868       }
    00869       this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    00870       this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
    00871     }
    00872
    00873     /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
    00874     this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00875   }
    00876
    00877   virtual void DrawWidget(const Rect &r, int widget) const
    00878   {
    00879     switch (widget) {
    00880       case SM_WIDGET_MAP: {
    00881         DrawPixelInfo new_dpi;
    00882         if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
    00883         this->DrawSmallMap(&new_dpi);
    00884       } break;
    00885
    00886       case SM_WIDGET_LEGEND: {
    00887         uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
    00888         uint number_of_rows = max(this->map_type == SMT_INDUSTRY ? (_smallmap_industry_count + columns - 1) / columns : 0, this->min_number_of_fixed_rows);
    00889         bool rtl = _dynlang.text_dir == TD_RTL;
    00890         uint y_org = r.top + WD_FRAMERECT_TOP;
    00891         uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
    00892         uint y = y_org;
    00893         uint i = 0; // Row counter for industry legend.
    00894         uint row_height = FONT_HEIGHT_SMALL;
    00895
    00896         uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
    00897         uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
    00898         uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
    00899         uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
    00900
    00901         for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
    00902           if (tbl->col_break || (this->map_type == SMT_INDUSTRY && i++ >= number_of_rows)) {
    00903             /* Column break needed, continue at top, COLUMN_WIDTH pixels
    00904              * (one "row") to the right. */
    00905             x += rtl ? -(int)this->column_width : this->column_width;
    00906             y = y_org;
    00907             i = 1;
    00908           }
    00909
    00910           if (this->map_type == SMT_INDUSTRY) {
    00911             /* Industry name must be formatted, since it's not in tiny font in the specs.
    00912              * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
    00913             SetDParam(0, tbl->legend);
    00914             assert(tbl->type < NUM_INDUSTRYTYPES);
    00915             SetDParam(1, _industry_counts[tbl->type]);
    00916             if (!tbl->show_on_map) {
    00917               /* Simply draw the string, not the black border of the legend colour.
    00918                * This will enforce the idea of the disabled item */
    00919               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
    00920             } else {
    00921               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
    00922               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
    00923             }
    00924           } else {
    00925             /* Anything that is not an industry is using normal process */
    00926             GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
    00927             DrawString(x + text_left, x + text_right, y, tbl->legend);
    00928           }
    00929           GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
    00930
    00931           y += row_height;
    00932         }
    00933       }
    00934     }
    00935   }
    00936
    00937   virtual void OnPaint()
    00938   {
    00939     this->DrawWidgets();
    00940   }
    00941
    00942   virtual void OnClick(Point pt, int widget)
    00943   {
    00944     switch (widget) {
    00945       case SM_WIDGET_MAP: { // Map window
    00946         /*
    00947          * XXX: scrolling with the left mouse button is done by subsequently
    00948          * clicking with the left mouse button; clicking once centers the
    00949          * large map at the selected point. So by unclicking the left mouse
    00950          * button here, it gets reclicked during the next inputloop, which
    00951          * would make it look like the mouse is being dragged, while it is
    00952          * actually being (virtually) clicked every inputloop.
    00953          */
    00954         _left_button_clicked = false;
    00955
    00956         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    00957         Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
    00958         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    00959         w->viewport->follow_vehicle = INVALID_VEHICLE;
    00960         w->viewport->dest_scrollpos_x = pt.x + ((_cursor.pos.x - this->left + wid->pos_x) << 4) - (w->viewport->virtual_width  >> 1);
    00961         w->viewport->dest_scrollpos_y = pt.y + ((_cursor.pos.y - this->top  - wid->pos_y) << 4) - (w->viewport->virtual_height >> 1);
    00962
    00963         this->SetDirty();
    00964       } break;
    00965
    00966       case SM_WIDGET_CONTOUR:    // Show land contours
    00967       case SM_WIDGET_VEHICLES:   // Show vehicles
    00968       case SM_WIDGET_INDUSTRIES: // Show industries
    00969       case SM_WIDGET_ROUTES:     // Show transport routes
    00970       case SM_WIDGET_VEGETATION: // Show vegetation
    00971       case SM_WIDGET_OWNERS:     // Show land owners
    00972         this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
    00973         this->map_type = (SmallMapType)(widget - SM_WIDGET_CONTOUR);
    00974         this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00975
    00976         /* Hide Enable all/Disable all buttons if is not industry type small map */
    00977         this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
    00978
    00979         this->SetDirty();
    00980         SndPlayFx(SND_15_BEEP);
    00981         break;
    00982
    00983       case SM_WIDGET_CENTERMAP: // Center the smallmap again
    00984         this->SmallMapCenterOnCurrentPos();
    00985         this->HandleButtonClick(SM_WIDGET_CENTERMAP);
    00986         SndPlayFx(SND_15_BEEP);
    00987         break;
    00988
    00989       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
    00990         this->show_towns = !this->show_towns;
    00991         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00992
    00993         this->SetDirty();
    00994         SndPlayFx(SND_15_BEEP);
    00995         break;
    00996
    00997       case SM_WIDGET_LEGEND: // Legend
    00998         /* If industry type small map*/
    00999         if (this->map_type == SMT_INDUSTRY) {
    01000           /* If click on industries label, find right industry type and enable/disable it */
    01001           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
    01002           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
    01003           uint columns = this->GetNumberColumnsLegend(wi->current_x);
    01004           uint number_of_rows = max((_smallmap_industry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
    01005           if (line >= number_of_rows) break;
    01006
    01007           bool rtl = _dynlang.text_dir == TD_RTL;
    01008           int x = pt.x - wi->pos_x;
    01009           if (rtl) x = wi->current_x - x;
    01010           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
    01011
    01012           /* Check if click is on industry label*/
    01013           int industry_pos = (column * number_of_rows) + line;
    01014           if (industry_pos < _smallmap_industry_count) {
    01015             _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
    01016           }
    01017
    01018           /* Raise the two buttons "all", as we have done a specific choice */
    01019           this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01020           this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01021           this->SetDirty();
    01022         }
    01023         break;
    01024
    01025       case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
    01026         for (int i = 0; i != _smallmap_industry_count; i++) {
    01027           _legend_from_industries[i].show_on_map = true;
    01028         }
    01029         /* Toggle appeareance indicating the choice */
    01030         this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01031         this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01032         this->SetDirty();
    01033         break;
    01034
    01035       case SM_WIDGET_DISABLEINDUSTRIES: // Disable all industries
    01036         for (int i = 0; i != _smallmap_industry_count; i++) {
    01037           _legend_from_industries[i].show_on_map = false;
    01038         }
    01039         /* Toggle appeareance indicating the choice */
    01040         this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01041         this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01042         this->SetDirty();
    01043         break;
    01044
    01045       case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
    01046         _smallmap_industry_show_heightmap = !_smallmap_industry_show_heightmap;
    01047         this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
    01048         this->SetDirty();
    01049         break;
    01050     }
    01051   }
    01052
    01053   virtual void OnRightClick(Point pt, int widget)
    01054   {
    01055     if (widget == SM_WIDGET_MAP) {
    01056       if (_scrolling_viewport) return;
    01057       _scrolling_viewport = true;
    01058     }
    01059   }
    01060
    01061   virtual void OnTick()
    01062   {
    01063     /* Update the window every now and then */
    01064     if (--this->refresh != 0) return;
    01065
    01066     this->refresh = FORCE_REFRESH_PERIOD;
    01067     this->SetDirty();
    01068   }
    01069
    01070   virtual void OnScroll(Point delta)
    01071   {
    01072     _cursor.fix_at = true;
    01073
    01074     int x = this->scroll_x;
    01075     int y = this->scroll_y;
    01076
    01077     int sub = this->subscroll + delta.x;
    01078
    01079     x -= (sub >> 2) << 4;
    01080     y += (sub >> 2) << 4;
    01081     sub &= 3;
    01082
    01083     x += (delta.y >> 1) << 4;
    01084     y += (delta.y >> 1) << 4;
    01085
    01086     if (delta.y & 1) {
    01087       x += TILE_SIZE;
    01088       sub += 2;
    01089       if (sub > 3) {
    01090         sub -= 4;
    01091         x -= TILE_SIZE;
    01092         y += TILE_SIZE;
    01093       }
    01094     }
    01095
    01096     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01097     int hx = wi->current_x / 2;
    01098     int hy = wi->current_y / 2;
    01099     int hvx = hx * -4 + hy * 8;
    01100     int hvy = hx *  4 + hy * 8;
    01101     if (x < -hvx) {
    01102       x = -hvx;
    01103       sub = 0;
    01104     }
    01105     if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
    01106       x = MapMaxX() * TILE_SIZE - hvx;
    01107       sub = 0;
    01108     }
    01109     if (y < -hvy) {
    01110       y = -hvy;
    01111       sub = 0;
    01112     }
    01113     if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
    01114       y = MapMaxY() * TILE_SIZE - hvy;
    01115       sub = 0;
    01116     }
    01117
    01118     this->scroll_x = x;
    01119     this->scroll_y = y;
    01120     this->subscroll = sub;
    01121
    01122     this->SetDirty();
    01123   }
    01124
    01125   void SmallMapCenterOnCurrentPos()
    01126   {
    01127     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    01128     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01129
    01130     int x = ((vp->virtual_width  - (int)wi->current_x * TILE_SIZE) / 2 + vp->virtual_left) / 4;
    01131     int y = ((vp->virtual_height - (int)wi->current_y * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
    01132     this->scroll_x = (y - x) & ~0xF;
    01133     this->scroll_y = (x + y) & ~0xF;
    01134     this->SetDirty();
    01135   }
    01136 };
    01137
    01138 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
    01139 bool SmallMapWindow::show_towns = true;
    01140
    01149 class NWidgetSmallmapDisplay : public NWidgetContainer {
    01150   const SmallMapWindow *smallmap_window;
    01151 public:
    01152   NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
    01153   {
    01154     this->smallmap_window = NULL;
    01155   }
    01156
    01157   virtual void SetupSmallestSize(Window *w, bool init_array)
    01158   {
    01159     NWidgetBase *display = this->head;
    01160     NWidgetBase *bar = display->next;
    01161
    01162     display->SetupSmallestSize(w, init_array);
    01163     bar->SetupSmallestSize(w, init_array);
    01164
    01165     this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
    01166     this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
    01167     this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetMaxLegendHeight());
    01168     this->fill_x = max(display->fill_x, bar->fill_x);
    01169     this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
    01170     this->resize_x = max(display->resize_x, bar->resize_x);
    01171     this->resize_y = min(display->resize_y, bar->resize_y);
    01172   }
    01173
    01174   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
    01175   {
    01176     this->pos_x = x;
    01177     this->pos_y = y;
    01178     this->current_x = given_width;
    01179     this->current_y = given_height;
    01180
    01181     NWidgetBase *display = this->head;
    01182     NWidgetBase *bar = display->next;
    01183
    01184     if (sizing == ST_SMALLEST) {
    01185       this->smallest_x = given_width;
    01186       this->smallest_y = given_height;
    01187       /* Make display and bar exactly equal to their minimal size. */
    01188       display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
    01189       bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
    01190     }
    01191
    01192     uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(given_width - bar->smallest_x));
    01193     uint display_height = given_height - bar_height;
    01194     display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
    01195     bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
    01196   }
    01197
    01198   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
    01199   {
    01200     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
    01201     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    01202       NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
    01203       if (widget != NULL) return widget;
    01204     }
    01205     return NULL;
    01206   }
    01207
    01208   virtual void Draw(const Window *w)
    01209   {
    01210     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
    01211   }
    01212 };
    01213
    01215 static const NWidgetPart _nested_smallmap_display[] = {
    01216   NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
    01217     NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
    01218   EndContainer(),
    01219 };
    01220
    01222 static const NWidgetPart _nested_smallmap_bar[] = {
    01223   NWidget(WWT_PANEL, COLOUR_BROWN),
    01224     NWidget(NWID_HORIZONTAL),
    01225       NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
    01226       NWidget(NWID_VERTICAL),
    01227         /* Top button row. */
    01228         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01229           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
    01230           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
    01231           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
    01232           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
    01233         EndContainer(),
    01234         /* Bottom button row. */
    01235         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01236           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
    01237           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
    01238           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
    01239           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
    01240         EndContainer(),
    01241         NWidget(NWID_SPACER), SetResize(0, 1),
    01242       EndContainer(),
    01243     EndContainer(),
    01244   EndContainer(),
    01245 };
    01246
    01247 static NWidgetBase *SmallMapDisplay(int *biggest_index)
    01248 {
    01249   NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
    01250
    01251   MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
    01252   MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
    01253   return map_display;
    01254 }
    01255
    01256
    01257 static const NWidgetPart _nested_smallmap_widgets[] = {
    01258   NWidget(NWID_HORIZONTAL),
    01259     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    01260     NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01261     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    01262     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    01263   EndContainer(),
    01264   NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
    01265   /* Bottom button row and resize box. */
    01266   NWidget(NWID_HORIZONTAL),
    01267     NWidget(WWT_PANEL, COLOUR_BROWN),
    01268       NWidget(NWID_HORIZONTAL),
    01269         NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECTINDUSTRIES),
    01270           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01271             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
    01272             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
    01273             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
    01274           EndContainer(),
    01275           NWidget(NWID_SPACER), SetFill(1, 1),
    01276         EndContainer(),
    01277         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
    01278       EndContainer(),
    01279     EndContainer(),
    01280     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    01281   EndContainer(),
    01282 };
    01283
    01284 static const WindowDesc _smallmap_desc(
    01285   WDP_AUTO, 446, 314,
    01286   WC_SMALLMAP, WC_NONE,
    01287   WDF_UNCLICK_BUTTONS,
    01288   _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
    01289 );
    01290
    01291 void ShowSmallMap()
    01292 {
    01293   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
    01294 }
    01295
    01304 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
    01305 {
    01306   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
    01307
    01308   /* If a user scrolls to a tile (via what way what so ever) and already is on
    01309    * that tile (e.g.: pressed twice), move the smallmap to that location,
    01310    * so you directly see where you are on the smallmap. */
    01311
    01312   if (res) return res;
    01313
    01314   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
    01315   if (w != NULL) w->SmallMapCenterOnCurrentPos();
    01316
    01317   return res;
    01318 }