# smallmap_gui.cpp

Go to the documentation of this file.

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