# smallmap_gui.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "clear_map.h"
    00014 #include "industry.h"
    00015 #include "station_map.h"
    00016 #include "landscape.h"
    00017 #include "window_gui.h"
    00018 #include "tree_map.h"
    00019 #include "viewport_func.h"
    00020 #include "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
    00223 static const AndOr _smallmap_contours_andor[] = {
    00224   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00225   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
    00226   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00227   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00228   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00229   {MKCOLOUR(0x98989898), MKCOLOUR(0x00000000)},
    00230   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)},
    00231   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00232   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)},
    00233   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00234   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00235   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
    00236 };
    00237
    00238 static const AndOr _smallmap_vehicles_andor[] = {
    00239   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00240   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00241   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00242   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00243   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00244   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00245   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)},
    00246   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00247   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)},
    00248   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00249   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00250   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00251 };
    00252
    00253 typedef uint32 GetSmallMapPixels(TileIndex tile);
    00254
    00255
    00256 static inline TileType GetEffectiveTileType(TileIndex tile)
    00257 {
    00258   TileType t = GetTileType(tile);
    00259
    00260   if (t == MP_TUNNELBRIDGE) {
    00261     TransportType tt = GetTunnelBridgeTransportType(tile);
    00262
    00263     switch (tt) {
    00264       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
    00265       case TRANSPORT_ROAD: t = MP_ROAD;    break;
    00266       default:             t = MP_WATER;   break;
    00267     }
    00268   }
    00269   return t;
    00270 }
    00271
    00277 static inline uint32 GetSmallMapContoursPixels(TileIndex tile)
    00278 {
    00279   TileType t = GetEffectiveTileType(tile);
    00280
    00281   return ApplyMask(_map_height_bits[TileHeight(tile)], &_smallmap_contours_andor[t]);
    00282 }
    00283
    00290 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile)
    00291 {
    00292   TileType t = GetEffectiveTileType(tile);
    00293
    00294   return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00295 }
    00296
    00303 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
    00304 {
    00305   TileType t = GetEffectiveTileType(tile);
    00306
    00307   if (t == MP_INDUSTRY) {
    00308     /* If industry is allowed to be seen, use its colour on the map */
    00309     if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
    00310       return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
    00311     } else {
    00312       /* Otherwise, return the colour of the clear tiles, which will make it disappear */
    00313       t = MP_CLEAR;
    00314     }
    00315   }
    00316
    00317   return ApplyMask(_smallmap_industry_show_heightmap ? _map_height_bits[TileHeight(tile)] : MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00318 }
    00319
    00326 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
    00327 {
    00328   TileType t = GetEffectiveTileType(tile);
    00329
    00330   if (t == MP_STATION) {
    00331     switch (GetStationType(tile)) {
    00332       case STATION_RAIL:    return MKCOLOUR(0x56565656);
    00333       case STATION_AIRPORT: return MKCOLOUR(0xB8B8B8B8);
    00334       case STATION_TRUCK:   return MKCOLOUR(0xC2C2C2C2);
    00335       case STATION_BUS:     return MKCOLOUR(0xBFBFBFBF);
    00336       case STATION_DOCK:    return MKCOLOUR(0x98989898);
    00337       default:              return MKCOLOUR(0xFFFFFFFF);
    00338     }
    00339   }
    00340
    00341   /* Ground colour */
    00342   return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_contours_andor[t]);
    00343 }
    00344
    00345
    00346 static const uint32 _vegetation_clear_bits[] = {
    00347   MKCOLOUR(0x54545454),
    00348   MKCOLOUR(0x52525252),
    00349   MKCOLOUR(0x0A0A0A0A),
    00350   MKCOLOUR(0x25252525),
    00351   MKCOLOUR(0x98989898),
    00352   MKCOLOUR(0xC2C2C2C2),
    00353   MKCOLOUR(0x54545454),
    00354   MKCOLOUR(0x54545454),
    00355 };
    00356
    00357 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile)
    00358 {
    00359   TileType t = GetEffectiveTileType(tile);
    00360
    00361   switch (t) {
    00362     case MP_CLEAR:
    00363       return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR(0x37373737) : _vegetation_clear_bits[GetClearGround(tile)];
    00364
    00365     case MP_INDUSTRY:
    00366       return GetIndustrySpec(Industry::GetByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR(0xD0D0D0D0) : MKCOLOUR(0xB5B5B5B5);
    00367
    00368     case MP_TREES:
    00369       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT) {
    00370         return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR(0x98575798) : MKCOLOUR(0xC25757C2);
    00371       }
    00372       return MKCOLOUR(0x54575754);
    00373
    00374     default:
    00375       return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00376   }
    00377 }
    00378
    00379
    00380 static uint32 _owner_colours[OWNER_END + 1];
    00381
    00388 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
    00389 {
    00390   Owner o;
    00391
    00392   switch (GetTileType(tile)) {
    00393     case MP_INDUSTRY: o = OWNER_END;          break;
    00394     case MP_HOUSE:    o = OWNER_TOWN;         break;
    00395     default:          o = GetTileOwner(tile); break;
    00396     /* FIXME: For MP_ROAD there are multiple owners.
    00397      * GetTileOwner returns the rail owner (level crossing) resp. the owner of ROADTYPE_ROAD (normal road),
    00398      * even if there are no ROADTYPE_ROAD bits on the tile.
    00399      */
    00400   }
    00401
    00402   return _owner_colours[o];
    00403 }
    00404
    00405
    00406 static const uint32 _smallmap_mask_left[3] = {
    00407   MKCOLOUR(0xFF000000),
    00408   MKCOLOUR(0xFFFF0000),
    00409   MKCOLOUR(0xFFFFFF00),
    00410 };
    00411
    00412 static const uint32 _smallmap_mask_right[] = {
    00413   MKCOLOUR(0x000000FF),
    00414   MKCOLOUR(0x0000FFFF),
    00415   MKCOLOUR(0x00FFFFFF),
    00416 };
    00417
    00418 /* Each tile has 4 x pixels and 1 y pixel */
    00419
    00420 static GetSmallMapPixels * const _smallmap_draw_procs[] = {
    00421   GetSmallMapContoursPixels,
    00422   GetSmallMapVehiclesPixels,
    00423   GetSmallMapIndustriesPixels,
    00424   GetSmallMapRoutesPixels,
    00425   GetSmallMapVegetationPixels,
    00426   GetSmallMapOwnerPixels,
    00427 };
    00428
    00429 static const byte _vehicle_type_colours[6] = {
    00430   184, 191, 152, 15, 215, 184
    00431 };
    00432
    00433
    00435 class SmallMapWindow : public Window {
    00437   enum SmallMapType {
    00438     SMT_CONTOUR,
    00439     SMT_VEHICLES,
    00440     SMT_INDUSTRY,
    00441     SMT_ROUTES,
    00442     SMT_VEGETATION,
    00443     SMT_OWNER,
    00444   };
    00445
    00446   static SmallMapType map_type;
    00447   static bool show_towns;
    00448
    00449   static const uint LEGEND_BLOB_WIDTH = 8;
    00450   static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
    00451   uint min_number_of_columns;
    00452   uint min_number_of_fixed_rows;
    00453   uint column_width;
    00454
    00455   int32 scroll_x;
    00456   int32 scroll_y;
    00457   int32 subscroll;
    00458
    00459   static const uint8 FORCE_REFRESH_PERIOD = 0x1F;
    00460   uint8 refresh;
    00461
    00468   inline int RemapX(int tile_x) const
    00469   {
    00470     return tile_x - this->scroll_x / TILE_SIZE;
    00471   }
    00472
    00479   inline int RemapY(int tile_y) const
    00480   {
    00481     return tile_y - this->scroll_y / TILE_SIZE;
    00482   }
    00483
    00498   void DrawSmallMapStuff(void *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, Blitter *blitter, GetSmallMapPixels *proc) const
    00499   {
    00500     void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
    00501     void *dst_ptr_end = blitter->MoveTo(dst_ptr_abs_end, -4, 0);
    00502
    00503     do {
    00504       /* Check if the tile (xc,yc) is within the map range */
    00505       uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
    00506       if (IsInsideMM(xc, min_xy, MapMaxX()) && IsInsideMM(yc, min_xy, MapMaxY())) {
    00507         /* Check if the dst pointer points to a pixel inside the screen buffer */
    00508         if (dst < _screen.dst_ptr) continue;
    00509         if (dst >= dst_ptr_abs_end) continue;
    00510
    00511         uint32 val = proc(TileXY(xc, yc)) & mask;
    00512         uint8 *val8 = (uint8 *)&val;
    00513
    00514         if (dst <= dst_ptr_end) {
    00515           blitter->SetPixelIfEmpty(dst, 0, 0, val8[0]);
    00516           blitter->SetPixelIfEmpty(dst, 1, 0, val8[1]);
    00517           blitter->SetPixelIfEmpty(dst, 2, 0, val8[2]);
    00518           blitter->SetPixelIfEmpty(dst, 3, 0, val8[3]);
    00519         } else {
    00520           /* It happens that there are only 1, 2 or 3 pixels left to fill, so
    00521            * in that special case, write till the end of the video-buffer */
    00522           int i = 0;
    00523           do {
    00524             blitter->SetPixelIfEmpty(dst, 0, 0, val8[i]);
    00525           } while (i++, dst = blitter->MoveTo(dst, 1, 0), dst < dst_ptr_abs_end);
    00526         }
    00527       }
    00528     /* Switch to next tile in the column */
    00529     } while (xc++, yc++, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
    00530   }
    00531
    00537   void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
    00538   {
    00539     const Vehicle *v;
    00540     FOR_ALL_VEHICLES(v) {
    00541       if (v->type == VEH_EFFECT) continue;
    00542       if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
    00543
    00544       /* Remap into flat coordinates. */
    00545       Point pt = RemapCoords(
    00546           this->RemapX(v->x_pos / TILE_SIZE),
    00547           this->RemapY(v->y_pos / TILE_SIZE),
    00548           0);
    00549       int x = pt.x;
    00550       int y = pt.y;
    00551
    00552       /* Check if y is out of bounds? */
    00553       y -= dpi->top;
    00554       if (!IsInsideMM(y, 0, dpi->height)) continue;
    00555
    00556       /* Default is to draw both pixels. */
    00557       bool skip = false;
    00558
    00559       /* Offset X coordinate */
    00560       x -= this->subscroll + 3 + dpi->left;
    00561
    00562       if (x < 0) {
    00563         /* if x+1 is 0, that means we're on the very left edge,
    00564          * and should thus only draw a single pixel */
    00565         if (++x != 0) continue;
    00566         skip = true;
    00567       } else if (x >= dpi->width - 1) {
    00568         /* Check if we're at the very right edge, and if so draw only a single pixel */
    00569         if (x != dpi->width - 1) continue;
    00570         skip = true;
    00571       }
    00572
    00573       /* Calculate pointer to pixel and the colour */
    00574       byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
    00575
    00576       /* And draw either one or two pixels depending on clipping */
    00577       blitter->SetPixel(dpi->dst_ptr, x, y, colour);
    00578       if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
    00579     }
    00580   }
    00581
    00586   void DrawTowns(const DrawPixelInfo *dpi) const
    00587   {
    00588     const Town *t;
    00589     FOR_ALL_TOWNS(t) {
    00590       /* Remap the town coordinate */
    00591       Point pt = RemapCoords(
    00592           this->RemapX(TileX(t->xy)),
    00593           this->RemapY(TileY(t->xy)),
    00594           0);
    00595       int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
    00596       int y = pt.y;
    00597
    00598       /* Check if the town sign is within bounds */
    00599       if (x + t->sign.width_small > dpi->left &&
    00600           x < dpi->left + dpi->width &&
    00601           y + FONT_HEIGHT_SMALL > dpi->top &&
    00602           y < dpi->top + dpi->height) {
    00603         /* And draw it. */
    00604         SetDParam(0, t->index);
    00605         DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
    00606       }
    00607     }
    00608   }
    00609
    00616   static inline void DrawVertMapIndicator(int x, int y, int y2)
    00617   {
    00618     GfxFillRect(x, y,      x, y + 3, 69);
    00619     GfxFillRect(x, y2 - 3, x, y2,    69);
    00620   }
    00621
    00628   static inline void DrawHorizMapIndicator(int x, int x2, int y)
    00629   {
    00630     GfxFillRect(x,      y, x + 3, y, 69);
    00631     GfxFillRect(x2 - 3, y, x2,    y, 69);
    00632   }
    00633
    00637   void DrawMapIndicators() const
    00638   {
    00639     /* Find main viewport. */
    00640     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    00641
    00642     Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
    00643
    00644     int x = vp->virtual_left - pt.x;
    00645     int y = vp->virtual_top - pt.y;
    00646     int x2 = (x + vp->virtual_width) / TILE_SIZE;
    00647     int y2 = (y + vp->virtual_height) / TILE_SIZE;
    00648     x /= TILE_SIZE;
    00649     y /= TILE_SIZE;
    00650
    00651     x -= this->subscroll;
    00652     x2 -= this->subscroll;
    00653
    00654     SmallMapWindow::DrawVertMapIndicator(x, y, y2);
    00655     SmallMapWindow::DrawVertMapIndicator(x2, y, y2);
    00656
    00657     SmallMapWindow::DrawHorizMapIndicator(x, x2, y);
    00658     SmallMapWindow::DrawHorizMapIndicator(x, x2, y2);
    00659   }
    00660
    00672   void DrawSmallMap(DrawPixelInfo *dpi) const
    00673   {
    00674     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00675     DrawPixelInfo *old_dpi;
    00676
    00677     old_dpi = _cur_dpi;
    00678     _cur_dpi = dpi;
    00679
    00680     /* Clear it */
    00681     GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
    00682
    00683     /* Setup owner table */
    00684     if (this->map_type == SMT_OWNER) {
    00685       const Company *c;
    00686
    00687       /* Fill with some special colours */
    00688       _owner_colours[OWNER_TOWN]  = MKCOLOUR(0xB4B4B4B4);
    00689       _owner_colours[OWNER_NONE]  = MKCOLOUR(0x54545454);
    00690       _owner_colours[OWNER_WATER] = MKCOLOUR(0xCACACACA);
    00691       _owner_colours[OWNER_END]   = MKCOLOUR(0x20202020); // Industry
    00692
    00693       /* Now fill with the company colours */
    00694       FOR_ALL_COMPANIES(c) {
    00695         _owner_colours[c->index] = _colour_gradient[c->colour][5] * 0x01010101;
    00696       }
    00697     }
    00698
    00699     int tile_x = this->scroll_x / TILE_SIZE;
    00700     int tile_y = this->scroll_y / TILE_SIZE;
    00701
    00702     int dx = dpi->left + this->subscroll;
    00703     tile_x -= dx / 4;
    00704     tile_y += dx / 4;
    00705     dx &= 3;
    00706
    00707     int dy = dpi->top;
    00708     tile_x += dy / 2;
    00709     tile_y += dy / 2;
    00710
    00711     if (dy & 1) {
    00712       tile_x++;
    00713       dx += 2;
    00714       if (dx > 3) {
    00715         dx -= 4;
    00716         tile_x--;
    00717         tile_y++;
    00718       }
    00719     }
    00720
    00721     void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
    00722     int x = - dx - 4;
    00723     int y = 0;
    00724
    00725     for (;;) {
    00726       uint32 mask = 0xFFFFFFFF;
    00727
    00728       /* Distance from left edge */
    00729       if (x >= -3) {
    00730         if (x < 0) {
    00731           /* Mask to use at the left edge */
    00732           mask = _smallmap_mask_left[x + 3];
    00733         }
    00734
    00735         /* Distance from right edge */
    00736         int t = dpi->width - x;
    00737         if (t < 4) {
    00738           if (t <= 0) break; // Exit loop
    00739           /* Mask to use at the right edge */
    00740           mask &= _smallmap_mask_right[t - 1];
    00741         }
    00742
    00743         /* Number of lines */
    00744         int reps = (dpi->height - y + 1) / 2;
    00745         if (reps > 0) {
    00746           this->DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, blitter, _smallmap_draw_procs[this->map_type]);
    00747         }
    00748       }
    00749
    00750       if (y == 0) {
    00751         tile_y++;
    00752         y++;
    00753         ptr = blitter->MoveTo(ptr, 0, 1);
    00754       } else {
    00755         tile_x--;
    00756         y--;
    00757         ptr = blitter->MoveTo(ptr, 0, -1);
    00758       }
    00759       ptr = blitter->MoveTo(ptr, 2, 0);
    00760       x += 2;
    00761     }
    00762
    00763     /* Draw vehicles */
    00764     if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
    00765
    00766     /* Draw town names */
    00767     if (this->show_towns) this->DrawTowns(dpi);
    00768
    00769     /* Draw map indicators */
    00770     this->DrawMapIndicators();
    00771
    00772     _cur_dpi = old_dpi;
    00773   }
    00774
    00775 public:
    00776   SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
    00777   {
    00778     this->InitNested(desc, window_number);
    00779     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00780
    00781     _smallmap_industry_show_heightmap = false;
    00782     this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
    00783
    00784     this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00785     this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
    00786
    00787     this->SmallMapCenterOnCurrentPos();
    00788   }
    00789
    00793   inline uint GetMaxLegendHeight() const
    00794   {
    00795     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + this->min_number_of_columns - 1) / this->min_number_of_columns);
    00796     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
    00797   }
    00798
    00802   inline uint GetMinLegendWidth() const
    00803   {
    00804     return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
    00805   }
    00806
    00810   inline uint GetNumberColumnsLegend(uint width) const
    00811   {
    00812     return width / this->column_width;
    00813   }
    00814
    00818   uint GetLegendHeight(uint width) const
    00819   {
    00820     uint num_columns = this->GetNumberColumnsLegend(width);
    00821     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + num_columns - 1) / num_columns);
    00822     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
    00823   }
    00824
    00825   virtual void SetStringParameters(int widget) const
    00826   {
    00827     switch (widget) {
    00828       case SM_WIDGET_CAPTION:
    00829         SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
    00830         break;
    00831     }
    00832   }
    00833
    00834   virtual void OnInit()
    00835   {
    00836     uint min_width = 0;
    00837     this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
    00838     this->min_number_of_fixed_rows = 0;
    00839     for (uint i = 0; i < lengthof(_legend_table); i++) {
    00840       uint height = 0;
    00841       uint num_columns = 1;
    00842       for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
    00843         StringID str;
    00844         if (i == SMT_INDUSTRY) {
    00845           SetDParam(0, tbl->legend);
    00846           SetDParam(1, IndustryPool::MAX_SIZE);
    00847           str = STR_SMALLMAP_INDUSTRY;
    00848         } else {
    00849           if (tbl->col_break) {
    00850             this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    00851             height = 0;
    00852             num_columns++;
    00853           }
    00854           height++;
    00855           str = tbl->legend;
    00856         }
    00857         min_width = max(GetStringBoundingBox(str).width, min_width);
    00858       }
    00859       this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
    00860       this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
    00861     }
    00862
    00863     /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
    00864     this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00865   }
    00866
    00867   virtual void DrawWidget(const Rect &r, int widget) const
    00868   {
    00869     switch (widget) {
    00870       case SM_WIDGET_MAP: {
    00871         DrawPixelInfo new_dpi;
    00872         if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
    00873         this->DrawSmallMap(&new_dpi);
    00874       } break;
    00875
    00876       case SM_WIDGET_LEGEND: {
    00877         uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
    00878         uint number_of_rows = max(this->map_type == SMT_INDUSTRY ? (_smallmap_industry_count + columns - 1) / columns : 0, this->min_number_of_fixed_rows);
    00879         bool rtl = _dynlang.text_dir == TD_RTL;
    00880         uint y_org = r.top + WD_FRAMERECT_TOP;
    00881         uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
    00882         uint y = y_org;
    00883         uint i = 0; // Row counter for industry legend.
    00884         uint row_height = FONT_HEIGHT_SMALL;
    00885
    00886         uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
    00887         uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
    00888         uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
    00889         uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
    00890
    00891         for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
    00892           if (tbl->col_break || (this->map_type == SMT_INDUSTRY && i++ >= number_of_rows)) {
    00893             /* Column break needed, continue at top, COLUMN_WIDTH pixels
    00894              * (one "row") to the right. */
    00895             x += rtl ? -(int)this->column_width : this->column_width;
    00896             y = y_org;
    00897             i = 1;
    00898           }
    00899
    00900           if (this->map_type == SMT_INDUSTRY) {
    00901             /* Industry name must be formatted, since it's not in tiny font in the specs.
    00902              * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
    00903             SetDParam(0, tbl->legend);
    00904             assert(tbl->type < NUM_INDUSTRYTYPES);
    00905             SetDParam(1, _industry_counts[tbl->type]);
    00906             if (!tbl->show_on_map) {
    00907               /* Simply draw the string, not the black border of the legend colour.
    00908                * This will enforce the idea of the disabled item */
    00909               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
    00910             } else {
    00911               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
    00912               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
    00913             }
    00914           } else {
    00915             /* Anything that is not an industry is using normal process */
    00916             GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
    00917             DrawString(x + text_left, x + text_right, y, tbl->legend);
    00918           }
    00919           GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
    00920
    00921           y += row_height;
    00922         }
    00923       }
    00924     }
    00925   }
    00926
    00927   virtual void OnPaint()
    00928   {
    00929     this->DrawWidgets();
    00930   }
    00931
    00932   virtual void OnClick(Point pt, int widget)
    00933   {
    00934     switch (widget) {
    00935       case SM_WIDGET_MAP: { // Map window
    00936         /*
    00937          * XXX: scrolling with the left mouse button is done by subsequently
    00938          * clicking with the left mouse button; clicking once centers the
    00939          * large map at the selected point. So by unclicking the left mouse
    00940          * button here, it gets reclicked during the next inputloop, which
    00941          * would make it look like the mouse is being dragged, while it is
    00942          * actually being (virtually) clicked every inputloop.
    00943          */
    00944         _left_button_clicked = false;
    00945
    00946         Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
    00947         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    00948         w->viewport->follow_vehicle = INVALID_VEHICLE;
    00949         w->viewport->dest_scrollpos_x = pt.x + ((_cursor.pos.x - this->left + 2) << 4) - (w->viewport->virtual_width >> 1);
    00950         w->viewport->dest_scrollpos_y = pt.y + ((_cursor.pos.y - this->top - 16) << 4) - (w->viewport->virtual_height >> 1);
    00951
    00952         this->SetDirty();
    00953       } break;
    00954
    00955       case SM_WIDGET_CONTOUR:    // Show land contours
    00956       case SM_WIDGET_VEHICLES:   // Show vehicles
    00957       case SM_WIDGET_INDUSTRIES: // Show industries
    00958       case SM_WIDGET_ROUTES:     // Show transport routes
    00959       case SM_WIDGET_VEGETATION: // Show vegetation
    00960       case SM_WIDGET_OWNERS:     // Show land owners
    00961         this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
    00962         this->map_type = (SmallMapType)(widget - SM_WIDGET_CONTOUR);
    00963         this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00964
    00965         /* Hide Enable all/Disable all buttons if is not industry type small map */
    00966         this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
    00967
    00968         this->SetDirty();
    00969         SndPlayFx(SND_15_BEEP);
    00970         break;
    00971
    00972       case SM_WIDGET_CENTERMAP: // Center the smallmap again
    00973         this->SmallMapCenterOnCurrentPos();
    00974         this->HandleButtonClick(SM_WIDGET_CENTERMAP);
    00975         SndPlayFx(SND_15_BEEP);
    00976         break;
    00977
    00978       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
    00979         this->show_towns = !this->show_towns;
    00980         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00981
    00982         this->SetDirty();
    00983         SndPlayFx(SND_15_BEEP);
    00984         break;
    00985
    00986       case SM_WIDGET_LEGEND: // Legend
    00987         /* If industry type small map*/
    00988         if (this->map_type == SMT_INDUSTRY) {
    00989           /* If click on industries label, find right industry type and enable/disable it */
    00990           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
    00991           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
    00992           uint columns = this->GetNumberColumnsLegend(wi->current_x);
    00993           uint number_of_rows = max((_smallmap_industry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
    00994           if (line >= number_of_rows) break;
    00995
    00996           bool rtl = _dynlang.text_dir == TD_RTL;
    00997           int x = pt.x - wi->pos_x;
    00998           if (rtl) x = wi->current_x - x;
    00999           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
    01000
    01001           /* Check if click is on industry label*/
    01002           int industry_pos = (column * number_of_rows) + line;
    01003           if (industry_pos < _smallmap_industry_count) {
    01004             _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
    01005           }
    01006
    01007           /* Raise the two buttons "all", as we have done a specific choice */
    01008           this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01009           this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01010           this->SetDirty();
    01011         }
    01012         break;
    01013
    01014       case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
    01015         for (int i = 0; i != _smallmap_industry_count; i++) {
    01016           _legend_from_industries[i].show_on_map = true;
    01017         }
    01018         /* Toggle appeareance indicating the choice */
    01019         this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01020         this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01021         this->SetDirty();
    01022         break;
    01023
    01024       case SM_WIDGET_DISABLEINDUSTRIES: // Disable all industries
    01025         for (int i = 0; i != _smallmap_industry_count; i++) {
    01026           _legend_from_industries[i].show_on_map = false;
    01027         }
    01028         /* Toggle appeareance indicating the choice */
    01029         this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    01030         this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
    01031         this->SetDirty();
    01032         break;
    01033
    01034       case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
    01035         _smallmap_industry_show_heightmap = !_smallmap_industry_show_heightmap;
    01036         this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
    01037         this->SetDirty();
    01038         break;
    01039     }
    01040   }
    01041
    01042   virtual void OnRightClick(Point pt, int widget)
    01043   {
    01044     if (widget == SM_WIDGET_MAP) {
    01045       if (_scrolling_viewport) return;
    01046       _scrolling_viewport = true;
    01047     }
    01048   }
    01049
    01050   virtual void OnTick()
    01051   {
    01052     /* Update the window every now and then */
    01053     if (--this->refresh != 0) return;
    01054
    01055     this->refresh = FORCE_REFRESH_PERIOD;
    01056     this->SetDirty();
    01057   }
    01058
    01059   virtual void OnScroll(Point delta)
    01060   {
    01061     _cursor.fix_at = true;
    01062
    01063     int x = this->scroll_x;
    01064     int y = this->scroll_y;
    01065
    01066     int sub = this->subscroll + delta.x;
    01067
    01068     x -= (sub >> 2) << 4;
    01069     y += (sub >> 2) << 4;
    01070     sub &= 3;
    01071
    01072     x += (delta.y >> 1) << 4;
    01073     y += (delta.y >> 1) << 4;
    01074
    01075     if (delta.y & 1) {
    01076       x += TILE_SIZE;
    01077       sub += 2;
    01078       if (sub > 3) {
    01079         sub -= 4;
    01080         x -= TILE_SIZE;
    01081         y += TILE_SIZE;
    01082       }
    01083     }
    01084
    01085     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01086     int hx = wi->current_x / 2;
    01087     int hy = wi->current_y / 2;
    01088     int hvx = hx * -4 + hy * 8;
    01089     int hvy = hx *  4 + hy * 8;
    01090     if (x < -hvx) {
    01091       x = -hvx;
    01092       sub = 0;
    01093     }
    01094     if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
    01095       x = MapMaxX() * TILE_SIZE - hvx;
    01096       sub = 0;
    01097     }
    01098     if (y < -hvy) {
    01099       y = -hvy;
    01100       sub = 0;
    01101     }
    01102     if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
    01103       y = MapMaxY() * TILE_SIZE - hvy;
    01104       sub = 0;
    01105     }
    01106
    01107     this->scroll_x = x;
    01108     this->scroll_y = y;
    01109     this->subscroll = sub;
    01110
    01111     this->SetDirty();
    01112   }
    01113
    01114   void SmallMapCenterOnCurrentPos()
    01115   {
    01116     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    01117     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
    01118
    01119     int x = ((vp->virtual_width  - (int)wi->current_x * TILE_SIZE) / 2 + vp->virtual_left) / 4;
    01120     int y = ((vp->virtual_height - (int)wi->current_y * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
    01121     this->scroll_x = (y - x) & ~0xF;
    01122     this->scroll_y = (x + y) & ~0xF;
    01123     this->SetDirty();
    01124   }
    01125 };
    01126
    01127 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
    01128 bool SmallMapWindow::show_towns = true;
    01129
    01138 class NWidgetSmallmapDisplay : public NWidgetContainer {
    01139   const SmallMapWindow *smallmap_window;
    01140 public:
    01141   NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
    01142   {
    01143     this->smallmap_window = NULL;
    01144   }
    01145
    01146   virtual void SetupSmallestSize(Window *w, bool init_array)
    01147   {
    01148     NWidgetBase *display = this->head;
    01149     NWidgetBase *bar = display->next;
    01150
    01151     display->SetupSmallestSize(w, init_array);
    01152     bar->SetupSmallestSize(w, init_array);
    01153
    01154     this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
    01155     this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
    01156     this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetMaxLegendHeight());
    01157     this->fill_x = max(display->fill_x, bar->fill_x);
    01158     this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
    01159     this->resize_x = max(display->resize_x, bar->resize_x);
    01160     this->resize_y = min(display->resize_y, bar->resize_y);
    01161   }
    01162
    01163   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
    01164   {
    01165     this->pos_x = x;
    01166     this->pos_y = y;
    01167     this->current_x = given_width;
    01168     this->current_y = given_height;
    01169
    01170     NWidgetBase *display = this->head;
    01171     NWidgetBase *bar = display->next;
    01172
    01173     if (sizing == ST_SMALLEST) {
    01174       this->smallest_x = given_width;
    01175       this->smallest_y = given_height;
    01176       /* Make display and bar exactly equal to their minimal size. */
    01177       display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
    01178       bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
    01179     }
    01180
    01181     uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(given_width - bar->smallest_x));
    01182     uint display_height = given_height - bar_height;
    01183     display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
    01184     bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
    01185   }
    01186
    01187   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
    01188   {
    01189     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
    01190     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    01191       NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
    01192       if (widget != NULL) return widget;
    01193     }
    01194     return NULL;
    01195   }
    01196
    01197   virtual void Draw(const Window *w)
    01198   {
    01199     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
    01200   }
    01201 };
    01202
    01204 static const NWidgetPart _nested_smallmap_display[] = {
    01205   NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
    01206     NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
    01207   EndContainer(),
    01208 };
    01209
    01211 static const NWidgetPart _nested_smallmap_bar[] = {
    01212   NWidget(WWT_PANEL, COLOUR_BROWN),
    01213     NWidget(NWID_HORIZONTAL),
    01214       NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
    01215       NWidget(NWID_VERTICAL),
    01216         /* Top button row. */
    01217         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01218           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
    01219           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
    01220           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
    01221           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
    01222         EndContainer(),
    01223         /* Bottom button row. */
    01224         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01225           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
    01226           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
    01227           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
    01228           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
    01229         EndContainer(),
    01230         NWidget(NWID_SPACER), SetResize(0, 1),
    01231       EndContainer(),
    01232     EndContainer(),
    01233   EndContainer(),
    01234 };
    01235
    01236 static NWidgetBase *SmallMapDisplay(int *biggest_index)
    01237 {
    01238   NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
    01239
    01240   MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
    01241   MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
    01242   return map_display;
    01243 }
    01244
    01245
    01246 static const NWidgetPart _nested_smallmap_widgets[] = {
    01247   NWidget(NWID_HORIZONTAL),
    01248     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    01249     NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01250     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    01251     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    01252   EndContainer(),
    01253   NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
    01254   /* Bottom button row and resize box. */
    01255   NWidget(NWID_HORIZONTAL),
    01256     NWidget(WWT_PANEL, COLOUR_BROWN),
    01257       NWidget(NWID_HORIZONTAL),
    01258         NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECTINDUSTRIES),
    01259           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    01260             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
    01261             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
    01262             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
    01263           EndContainer(),
    01264           NWidget(NWID_SPACER), SetFill(1, 1),
    01265         EndContainer(),
    01266         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
    01267       EndContainer(),
    01268     EndContainer(),
    01269     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    01270   EndContainer(),
    01271 };
    01272
    01273 static const WindowDesc _smallmap_desc(
    01274   WDP_AUTO, 446, 314,
    01275   WC_SMALLMAP, WC_NONE,
    01276   WDF_UNCLICK_BUTTONS,
    01277   _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
    01278 );
    01279
    01280 void ShowSmallMap()
    01281 {
    01282   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
    01283 }
    01284
    01293 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
    01294 {
    01295   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
    01296
    01297   /* If a user scrolls to a tile (via what way what so ever) and already is on
    01298    * that tile (e.g.: pressed twice), move the smallmap to that location,
    01299    * so you directly see where you are on the smallmap. */
    01300
    01301   if (res) return res;
    01302
    01303   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
    01304   if (w != NULL) w->SmallMapCenterOnCurrentPos();
    01305
    01306   return res;
    01307 }