# smallmap_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: smallmap_gui.cpp 18719 2010-01-04 18:33:43Z yexo $ */
    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 "gfx_func.h"
    00021 #include "town.h"
    00022 #include "blitter/factory.hpp"
    00023 #include "tunnelbridge_map.h"
    00024 #include "strings_func.h"
    00025 #include "core/endian_func.hpp"
    00026 #include "vehicle_base.h"
    00027 #include "sound_func.h"
    00028 #include "window_func.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         Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
    00957         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    00958         w->viewport->follow_vehicle = INVALID_VEHICLE;
    00959         w->viewport->dest_scrollpos_x = pt.x + ((_cursor.pos.x - this->left + 2) << 4) - (w->viewport->virtual_width >> 1);
    00960         w->viewport->dest_scrollpos_y = pt.y + ((_cursor.pos.y - this->top - 16) << 4) - (w->viewport->virtual_height >> 1);
    00961
    00962         this->SetDirty();
    00963       } break;
    00964
    00965       case SM_WIDGET_CONTOUR:    // Show land contours
    00966       case SM_WIDGET_VEHICLES:   // Show vehicles
    00967       case SM_WIDGET_INDUSTRIES: // Show industries
    00968       case SM_WIDGET_ROUTES:     // Show transport routes
    00969       case SM_WIDGET_VEGETATION: // Show vegetation
    00970       case SM_WIDGET_OWNERS:     // Show land owners
    00971         this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
    00972         this->map_type = (SmallMapType)(widget - SM_WIDGET_CONTOUR);
    00973         this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00974
    00975         /* Hide Enable all/Disable all buttons if is not industry type small map */
    00976         this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
    00977
    00978         this->SetDirty();
    00979         SndPlayFx(SND_15_BEEP);
    00980         break;
    00981
    00982       case SM_WIDGET_CENTERMAP: // Center the smallmap again
    00983         this->SmallMapCenterOnCurrentPos();
    00984         this->HandleButtonClick(SM_WIDGET_CENTERMAP);
    00985         SndPlayFx(SND_15_BEEP);
    00986         break;
    00987
    00988       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
    00989         this->show_towns = !this->show_towns;
    00990         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00991
    00992         this->SetDirty();
    00993         SndPlayFx(SND_15_BEEP);
    00994         break;
    00995
    00996       case SM_WIDGET_LEGEND: // Legend
    00997         /* If industry type small map*/
    00998         if (this->map_type == SMT_INDUSTRY) {
    00999           /* If click on industries label, find right industry type and enable/disable it */
    01000           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
    01001           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
    01002           uint columns = this->GetNumberColumnsLegend(wi->current_x);
    01003           uint number_of_rows = max((_smallmap_industry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
    01004           if (line >= number_of_rows) break;
    01005
    01006           bool rtl = _dynlang.text_dir == TD_RTL;
    01007           int x = pt.x - wi->pos_x;
    01008           if (rtl) x = wi->current_x - x;
    01009           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
    01010
    01011           /* Check if click is on industry label*/
    01012           int industry_pos = (column * number_of_rows) + line;
    01013           if (industry_pos < _smallmap_industry_count) {
    01014             _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
    01015           }
    01016
    01017           /* Raise the two buttons "all", as we have done a specific choice */
    01018           this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01019           this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01020           this->SetDirty();
    01021         }
    01022         break;
    01023
    01024       case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
    01025         for (int i = 0; i != _smallmap_industry_count; i++) {
    01026           _legend_from_industries[i].show_on_map = true;
    01027         }
    01028         /* Toggle appeareance indicating the choice */
    01029         this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01030         this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01031         this->SetDirty();
    01032         break;
    01033
    01034       case SM_WIDGET_DISABLEINDUSTRIES: // Disable all industries
    01035         for (int i = 0; i != _smallmap_industry_count; i++) {
    01036           _legend_from_industries[i].show_on_map = false;
    01037         }
    01038         /* Toggle appeareance indicating the choice */
    01039         this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01040         this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01041         this->SetDirty();
    01042         break;
    01043
    01044       case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
    01045         _smallmap_industry_show_heightmap = !_smallmap_industry_show_heightmap;
    01046         this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
    01047         this->SetDirty();
    01048         break;
    01049     }
    01050   }
    01051
    01052   virtual void OnRightClick(Point pt, int widget)
    01053   {
    01054     if (widget == SM_WIDGET_MAP) {
    01055       if (_scrolling_viewport) return;
    01056       _scrolling_viewport = true;
    01057     }
    01058   }
    01059
    01060   virtual void OnTick()
    01061   {
    01062     /* Update the window every now and then */
    01063     if (--this->refresh != 0) return;
    01064
    01065     this->refresh = FORCE_REFRESH_PERIOD;
    01066     this->SetDirty();
    01067   }
    01068
    01069   virtual void OnScroll(Point delta)
    01070   {
    01071     _cursor.fix_at = true;
    01072
    01073     int x = this->scroll_x;
    01074     int y = this->scroll_y;
    01075
    01076     int sub = this->subscroll + delta.x;
    01077
    01078     x -= (sub >> 2) << 4;
    01079     y += (sub >> 2) << 4;
    01080     sub &= 3;
    01081
    01082     x += (delta.y >> 1) << 4;
    01083     y += (delta.y >> 1) << 4;
    01084
    01085     if (delta.y & 1) {
    01086       x += TILE_SIZE;
    01087       sub += 2;
    01088       if (sub > 3) {
    01089         sub -= 4;
    01090         x -= TILE_SIZE;
    01091         y += TILE_SIZE;
    01092       }
    01093     }
    01094
    01095     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01096     int hx = wi->current_x / 2;
    01097     int hy = wi->current_y / 2;
    01098     int hvx = hx * -4 + hy * 8;
    01099     int hvy = hx *  4 + hy * 8;
    01100     if (x < -hvx) {
    01101       x = -hvx;
    01102       sub = 0;
    01103     }
    01104     if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
    01105       x = MapMaxX() * TILE_SIZE - hvx;
    01106       sub = 0;
    01107     }
    01108     if (y < -hvy) {
    01109       y = -hvy;
    01110       sub = 0;
    01111     }
    01112     if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
    01113       y = MapMaxY() * TILE_SIZE - hvy;
    01114       sub = 0;
    01115     }
    01116
    01117     this->scroll_x = x;
    01118     this->scroll_y = y;
    01119     this->subscroll = sub;
    01120
    01121     this->SetDirty();
    01122   }
    01123
    01124   void SmallMapCenterOnCurrentPos()
    01125   {
    01126     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    01127     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01128
    01129     int x = ((vp->virtual_width  - (int)wi->current_x * TILE_SIZE) / 2 + vp->virtual_left) / 4;
    01130     int y = ((vp->virtual_height - (int)wi->current_y * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
    01131     this->scroll_x = (y - x) & ~0xF;
    01132     this->scroll_y = (x + y) & ~0xF;
    01133     this->SetDirty();
    01134   }
    01135 };
    01136
    01137 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
    01138 bool SmallMapWindow::show_towns = true;
    01139
    01148 class NWidgetSmallmapDisplay : public NWidgetContainer {
    01149   const SmallMapWindow *smallmap_window;
    01150 public:
    01151   NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
    01152   {
    01153     this->smallmap_window = NULL;
    01154   }
    01155
    01156   virtual void SetupSmallestSize(Window *w, bool init_array)
    01157   {
    01158     NWidgetBase *display = this->head;
    01159     NWidgetBase *bar = display->next;
    01160
    01161     display->SetupSmallestSize(w, init_array);
    01162     bar->SetupSmallestSize(w, init_array);
    01163
    01164     this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
    01165     this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
    01166     this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetMaxLegendHeight());
    01167     this->fill_x = max(display->fill_x, bar->fill_x);
    01168     this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
    01169     this->resize_x = max(display->resize_x, bar->resize_x);
    01170     this->resize_y = min(display->resize_y, bar->resize_y);
    01171   }
    01172
    01173   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
    01174   {
    01175     this->pos_x = x;
    01176     this->pos_y = y;
    01177     this->current_x = given_width;
    01178     this->current_y = given_height;
    01179
    01180     NWidgetBase *display = this->head;
    01181     NWidgetBase *bar = display->next;
    01182
    01183     if (sizing == ST_SMALLEST) {
    01184       this->smallest_x = given_width;
    01185       this->smallest_y = given_height;
    01186       /* Make display and bar exactly equal to their minimal size. */
    01187       display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
    01188       bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
    01189     }
    01190
    01191     uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(given_width - bar->smallest_x));
    01192     uint display_height = given_height - bar_height;
    01193     display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
    01194     bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
    01195   }
    01196
    01197   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
    01198   {
    01199     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
    01200     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    01201       NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
    01202       if (widget != NULL) return widget;
    01203     }
    01204     return NULL;
    01205   }
    01206
    01207   virtual void Draw(const Window *w)
    01208   {
    01209     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
    01210   }
    01211 };
    01212
    01214 static const NWidgetPart _nested_smallmap_display[] = {
    01215   NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
    01216     NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
    01217   EndContainer(),
    01218 };
    01219
    01221 static const NWidgetPart _nested_smallmap_bar[] = {
    01222   NWidget(WWT_PANEL, COLOUR_BROWN),
    01223     NWidget(NWID_HORIZONTAL),
    01224       NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
    01225       NWidget(NWID_VERTICAL),
    01226         /* Top button row. */
    01227         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01228           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
    01229           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
    01230           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
    01231           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
    01232         EndContainer(),
    01233         /* Bottom button row. */
    01234         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01235           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
    01236           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
    01237           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
    01238           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
    01239         EndContainer(),
    01240         NWidget(NWID_SPACER), SetResize(0, 1),
    01241       EndContainer(),
    01242     EndContainer(),
    01243   EndContainer(),
    01244 };
    01245
    01246 static NWidgetBase *SmallMapDisplay(int *biggest_index)
    01247 {
    01248   NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
    01249
    01250   MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
    01251   MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
    01252   return map_display;
    01253 }
    01254
    01255
    01256 static const NWidgetPart _nested_smallmap_widgets[] = {
    01257   NWidget(NWID_HORIZONTAL),
    01258     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    01259     NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01260     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    01261     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    01262   EndContainer(),
    01263   NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
    01264   /* Bottom button row and resize box. */
    01265   NWidget(NWID_HORIZONTAL),
    01266     NWidget(WWT_PANEL, COLOUR_BROWN),
    01267       NWidget(NWID_HORIZONTAL),
    01268         NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECTINDUSTRIES),
    01269           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01270             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
    01271             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
    01272             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
    01273           EndContainer(),
    01274           NWidget(NWID_SPACER), SetFill(1, 1),
    01275         EndContainer(),
    01276         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
    01277       EndContainer(),
    01278     EndContainer(),
    01279     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    01280   EndContainer(),
    01281 };
    01282
    01283 static const WindowDesc _smallmap_desc(
    01284   WDP_AUTO, 446, 314,
    01285   WC_SMALLMAP, WC_NONE,
    01286   WDF_UNCLICK_BUTTONS,
    01287   _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
    01288 );
    01289
    01290 void ShowSmallMap()
    01291 {
    01292   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
    01293 }
    01294
    01303 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
    01304 {
    01305   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
    01306
    01307   /* If a user scrolls to a tile (via what way what so ever) and already is on
    01308    * that tile (e.g.: pressed twice), move the smallmap to that location,
    01309    * so you directly see where you are on the smallmap. */
    01310
    01311   if (res) return res;
    01312
    01313   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
    01314   if (w != NULL) w->SmallMapCenterOnCurrentPos();
    01315
    01316   return res;
    01317 }