# smallmap_gui.cpp

Go to the documentation of this file.

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