# smallmap_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: smallmap_gui.cpp 18046 2009-11-11 21:45:50Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "clear_map.h"
    00007 #include "industry_map.h"
    00008 #include "station_map.h"
    00009 #include "landscape.h"
    00010 #include "window_gui.h"
    00011 #include "tree_map.h"
    00012 #include "viewport_func.h"
    00013 #include "gfx_func.h"
    00014 #include "town.h"
    00015 #include "blitter/factory.hpp"
    00016 #include "tunnelbridge_map.h"
    00017 #include "strings_func.h"
    00018 #include "zoom_func.h"
    00019 #include "core/endian_func.hpp"
    00020 #include "vehicle_base.h"
    00021 #include "sound_func.h"
    00022 #include "window_func.h"
    00023
    00024 #include "table/strings.h"
    00025 #include "table/sprites.h"
    00026
    00027 static const Widget _smallmap_widgets[] = {
    00028 {  WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_BROWN,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
    00029 {   WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_BROWN,    11,   337,     0,    13, STR_00B0_MAP,             STR_018C_WINDOW_TITLE_DRAG_THIS},
    00030 { WWT_STICKYBOX,     RESIZE_LR,  COLOUR_BROWN,   338,   349,     0,    13, 0x0,                      STR_STICKY_BUTTON},
    00031 {     WWT_PANEL,     RESIZE_RB,  COLOUR_BROWN,     0,   349,    14,   157, 0x0,                      STR_NULL},
    00032 {     WWT_INSET,     RESIZE_RB,  COLOUR_BROWN,     2,   347,    16,   155, 0x0,                      STR_NULL},
    00033 {     WWT_PANEL,    RESIZE_RTB,  COLOUR_BROWN,     0,   261,   158,   201, 0x0,                      STR_NULL},
    00034 {     WWT_PANEL,   RESIZE_LRTB,  COLOUR_BROWN,   262,   349,   158,   158, 0x0,                      STR_NULL},
    00035 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   284,   305,   158,   179, SPR_IMG_SHOW_COUNTOURS,   STR_0191_SHOW_LAND_CONTOURS_ON_MAP},
    00036 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   306,   327,   158,   179, SPR_IMG_SHOW_VEHICLES,    STR_0192_SHOW_VEHICLES_ON_MAP},
    00037 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   328,   349,   158,   179, SPR_IMG_INDUSTRY,         STR_0193_SHOW_INDUSTRIES_ON_MAP},
    00038 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   284,   305,   180,   201, SPR_IMG_SHOW_ROUTES,      STR_0194_SHOW_TRANSPORT_ROUTES_ON},
    00039 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   306,   327,   180,   201, SPR_IMG_PLANTTREES,       STR_0195_SHOW_VEGETATION_ON_MAP},
    00040 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   328,   349,   180,   201, SPR_IMG_COMPANY_GENERAL,  STR_0196_SHOW_LAND_OWNERS_ON_MAP},
    00041 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   262,   283,   158,   179, SPR_IMG_SMALLMAP,         STR_SMALLMAP_CENTER},
    00042 {    WWT_IMGBTN,   RESIZE_LRTB,  COLOUR_BROWN,   262,   283,   180,   201, SPR_IMG_TOWN,             STR_0197_TOGGLE_TOWN_NAMES_ON_OFF},
    00043 {     WWT_PANEL,    RESIZE_RTB,  COLOUR_BROWN,     0,   337,   202,   213, 0x0,                      STR_NULL},
    00044 {   WWT_TEXTBTN,     RESIZE_TB,  COLOUR_BROWN,     0,    99,   202,   213, STR_MESSAGES_ENABLE_ALL,  STR_NULL},
    00045 {   WWT_TEXTBTN,     RESIZE_TB,  COLOUR_BROWN,   100,   201,   202,   213, STR_MESSAGES_DISABLE_ALL, STR_NULL},
    00046 { WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_BROWN,   338,   349,   202,   213, 0x0,                      STR_RESIZE_BUTTON},
    00047 {  WIDGETS_END},
    00048 };
    00049
    00050 /* number of used industries */
    00051 static int _smallmap_industry_count;
    00052
    00054 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, false}
    00055
    00056 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, true, true, false}
    00057
    00059 #define MS(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, true}
    00060
    00062 struct LegendAndColour {
    00063   uint16 colour;
    00064   StringID legend;
    00065   IndustryType type;
    00066   bool show_on_map;
    00067   bool end;
    00068   bool col_break;
    00069 };
    00070
    00072 static const LegendAndColour _legend_land_contours[] = {
    00073   MK(0x5A, STR_00F0_100M),
    00074   MK(0x5C, STR_00F1_200M),
    00075   MK(0x5E, STR_00F2_300M),
    00076   MK(0x1F, STR_00F3_400M),
    00077   MK(0x27, STR_00F4_500M),
    00078
    00079   MS(0xD7, STR_00EB_ROADS),
    00080   MK(0x0A, STR_00EC_RAILROADS),
    00081   MK(0x98, STR_00ED_STATIONS_AIRPORTS_DOCKS),
    00082   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00083   MK(0x0F, STR_00EF_VEHICLES),
    00084   MKEND()
    00085 };
    00086
    00087 static const LegendAndColour _legend_vehicles[] = {
    00088   MK(0xB8, STR_00F5_TRAINS),
    00089   MK(0xBF, STR_00F6_ROAD_VEHICLES),
    00090   MK(0x98, STR_00F7_SHIPS),
    00091   MK(0x0F, STR_00F8_AIRCRAFT),
    00092   MS(0xD7, STR_00F9_TRANSPORT_ROUTES),
    00093   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00094   MKEND()
    00095 };
    00096
    00097 static const LegendAndColour _legend_routes[] = {
    00098   MK(0xD7, STR_00EB_ROADS),
    00099   MK(0x0A, STR_00EC_RAILROADS),
    00100   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00101   MS(0x56, STR_011B_RAILROAD_STATION),
    00102
    00103   MK(0xC2, STR_011C_TRUCK_LOADING_BAY),
    00104   MK(0xBF, STR_011D_BUS_STATION),
    00105   MK(0xB8, STR_011E_AIRPORT_HELIPORT),
    00106   MK(0x98, STR_011F_DOCK),
    00107   MKEND()
    00108 };
    00109
    00110 static const LegendAndColour _legend_vegetation[] = {
    00111   MK(0x52, STR_0120_ROUGH_LAND),
    00112   MK(0x54, STR_0121_GRASS_LAND),
    00113   MK(0x37, STR_0122_BARE_LAND),
    00114   MK(0x25, STR_0123_FIELDS),
    00115   MK(0x57, STR_0124_TREES),
    00116   MK(0xD0, STR_00FC_FOREST),
    00117   MS(0x0A, STR_0125_ROCKS),
    00118
    00119   MK(0xC2, STR_012A_DESERT),
    00120   MK(0x98, STR_012B_SNOW),
    00121   MK(0xD7, STR_00F9_TRANSPORT_ROUTES),
    00122   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00123   MKEND()
    00124 };
    00125
    00126 static const LegendAndColour _legend_land_owners[] = {
    00127   MK(0xCA, STR_0126_WATER),
    00128   MK(0x54, STR_0127_NO_OWNER),
    00129   MK(0xB4, STR_0128_TOWNS),
    00130   MK(0x20, STR_0129_INDUSTRIES),
    00131   MKEND()
    00132 };
    00133 #undef MK
    00134 #undef MS
    00135 #undef MKEND
    00136
    00139 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
    00140 /* For connecting industry type to position in industries list(small map legend) */
    00141 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
    00142
    00146 void BuildIndustriesLegend()
    00147 {
    00148   const IndustrySpec *indsp;
    00149   uint j = 0;
    00150
    00151   /* Add each name */
    00152   for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
    00153     indsp = GetIndustrySpec(i);
    00154     if (indsp->enabled) {
    00155       _legend_from_industries[j].legend = indsp->name;
    00156       _legend_from_industries[j].colour = indsp->map_colour;
    00157       _legend_from_industries[j].type = i;
    00158       _legend_from_industries[j].show_on_map = true;
    00159       _legend_from_industries[j].col_break = false;
    00160       _legend_from_industries[j].end = false;
    00161
    00162       /* Store widget number for this industry type */
    00163       _industry_to_list_pos[i] = j;
    00164       j++;
    00165     }
    00166   }
    00167   /* Terminate the list */
    00168   _legend_from_industries[j].end = true;
    00169
    00170   /* Store number of enabled industries */
    00171   _smallmap_industry_count = j;
    00172 }
    00173
    00174 static const LegendAndColour * const _legend_table[] = {
    00175   _legend_land_contours,
    00176   _legend_vehicles,
    00177   _legend_from_industries,
    00178   _legend_routes,
    00179   _legend_vegetation,
    00180   _legend_land_owners,
    00181 };
    00182
    00183 #define MKCOLOUR(x) TO_LE32X(x)
    00184
    00188 static const uint32 _map_height_bits[] = {
    00189   MKCOLOUR(0x5A5A5A5A),
    00190   MKCOLOUR(0x5A5B5A5B),
    00191   MKCOLOUR(0x5B5B5B5B),
    00192   MKCOLOUR(0x5B5C5B5C),
    00193   MKCOLOUR(0x5C5C5C5C),
    00194   MKCOLOUR(0x5C5D5C5D),
    00195   MKCOLOUR(0x5D5D5D5D),
    00196   MKCOLOUR(0x5D5E5D5E),
    00197   MKCOLOUR(0x5E5E5E5E),
    00198   MKCOLOUR(0x5E5F5E5F),
    00199   MKCOLOUR(0x5F5F5F5F),
    00200   MKCOLOUR(0x5F1F5F1F),
    00201   MKCOLOUR(0x1F1F1F1F),
    00202   MKCOLOUR(0x1F271F27),
    00203   MKCOLOUR(0x27272727),
    00204   MKCOLOUR(0x27272727),
    00205 };
    00206 assert_compile(lengthof(_map_height_bits) == MAX_TILE_HEIGHT + 1);
    00207
    00208 struct AndOr {
    00209   uint32 mor;
    00210   uint32 mand;
    00211 };
    00212
    00213 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
    00214 {
    00215   return (colour & mask->mand) | mask->mor;
    00216 }
    00217
    00218
    00219 static const AndOr _smallmap_contours_andor[] = {
    00220   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00221   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
    00222   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00223   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00224   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00225   {MKCOLOUR(0x98989898), MKCOLOUR(0x00000000)},
    00226   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)},
    00227   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00228   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)},
    00229   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00230   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00231   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
    00232 };
    00233
    00234 static const AndOr _smallmap_vehicles_andor[] = {
    00235   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00236   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00237   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00238   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00239   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00240   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00241   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)},
    00242   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00243   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)},
    00244   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00245   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00246   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00247 };
    00248
    00249 static const AndOr _smallmap_vegetation_andor[] = {
    00250   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00251   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00252   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00253   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00254   {MKCOLOUR(0x00575700), MKCOLOUR(0xFF0000FF)},
    00255   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00256   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)},
    00257   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00258   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)},
    00259   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
    00260   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
    00261   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
    00262 };
    00263
    00264 typedef uint32 GetSmallMapPixels(TileIndex tile); // typedef callthrough function
    00265
    00279 static void DrawSmallMapStuff(void *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, GetSmallMapPixels *proc)
    00280 {
    00281   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00282   void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
    00283   void *dst_ptr_end = blitter->MoveTo(dst_ptr_abs_end, -4, 0);
    00284
    00285   do {
    00286     /* check if the tile (xc,yc) is within the map range */
    00287     uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
    00288     if (IsInsideMM(xc, min_xy, MapMaxX()) && IsInsideMM(yc, min_xy, MapMaxY())) {
    00289       /* check if the dst pointer points to a pixel inside the screen buffer */
    00290       if (dst < _screen.dst_ptr) continue;
    00291       if (dst >= dst_ptr_abs_end) continue;
    00292
    00293       uint32 val = proc(TileXY(xc, yc)) & mask;
    00294       uint8 *val8 = (uint8 *)&val;
    00295
    00296       if (dst <= dst_ptr_end) {
    00297         blitter->SetPixelIfEmpty(dst, 0, 0, val8[0]);
    00298         blitter->SetPixelIfEmpty(dst, 1, 0, val8[1]);
    00299         blitter->SetPixelIfEmpty(dst, 2, 0, val8[2]);
    00300         blitter->SetPixelIfEmpty(dst, 3, 0, val8[3]);
    00301       } else {
    00302         /* It happens that there are only 1, 2 or 3 pixels left to fill, so in that special case, write till the end of the video-buffer */
    00303         int i = 0;
    00304         do {
    00305           blitter->SetPixelIfEmpty(dst, 0, 0, val8[i]);
    00306         } while (i++, dst = blitter->MoveTo(dst, 1, 0), dst < dst_ptr_abs_end);
    00307       }
    00308     }
    00309   /* switch to next tile in the column */
    00310   } while (xc++, yc++, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
    00311 }
    00312
    00313
    00314 static inline TileType GetEffectiveTileType(TileIndex tile)
    00315 {
    00316   TileType t = GetTileType(tile);
    00317
    00318   if (t == MP_TUNNELBRIDGE) {
    00319     TransportType tt = GetTunnelBridgeTransportType(tile);
    00320
    00321     switch (tt) {
    00322       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
    00323       case TRANSPORT_ROAD: t = MP_ROAD;    break;
    00324       default:             t = MP_WATER;   break;
    00325     }
    00326   }
    00327   return t;
    00328 }
    00329
    00335 static inline uint32 GetSmallMapContoursPixels(TileIndex tile)
    00336 {
    00337   TileType t = GetEffectiveTileType(tile);
    00338
    00339   return
    00340     ApplyMask(_map_height_bits[TileHeight(tile)], &_smallmap_contours_andor[t]);
    00341 }
    00342
    00349 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile)
    00350 {
    00351   TileType t = GetEffectiveTileType(tile);
    00352
    00353   return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00354 }
    00355
    00362 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
    00363 {
    00364   TileType t = GetEffectiveTileType(tile);
    00365
    00366   if (t == MP_INDUSTRY) {
    00367     /* If industry is allowed to be seen, use its colour on the map */
    00368     if (_legend_from_industries[_industry_to_list_pos[GetIndustryByTile(tile)->type]].show_on_map) {
    00369       return GetIndustrySpec(GetIndustryByTile(tile)->type)->map_colour * 0x01010101;
    00370     } else {
    00371       /* otherwise, return the colour of the clear tiles, which will make it disappear */
    00372       return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[MP_CLEAR]);
    00373     }
    00374   }
    00375
    00376   return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00377 }
    00378
    00385 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
    00386 {
    00387   TileType t = GetEffectiveTileType(tile);
    00388   uint32 bits;
    00389
    00390   if (t == MP_STATION) {
    00391     switch (GetStationType(tile)) {
    00392       case STATION_RAIL:    bits = MKCOLOUR(0x56565656); break;
    00393       case STATION_AIRPORT: bits = MKCOLOUR(0xB8B8B8B8); break;
    00394       case STATION_TRUCK:   bits = MKCOLOUR(0xC2C2C2C2); break;
    00395       case STATION_BUS:     bits = MKCOLOUR(0xBFBFBFBF); break;
    00396       case STATION_DOCK:    bits = MKCOLOUR(0x98989898); break;
    00397       default:              bits = MKCOLOUR(0xFFFFFFFF); break;
    00398     }
    00399   } else {
    00400     /* ground colour */
    00401     bits = ApplyMask(MKCOLOUR(0x54545454), &_smallmap_contours_andor[t]);
    00402   }
    00403   return bits;
    00404 }
    00405
    00406
    00407 static const uint32 _vegetation_clear_bits[] = {
    00408   MKCOLOUR(0x54545454),
    00409   MKCOLOUR(0x52525252),
    00410   MKCOLOUR(0x0A0A0A0A),
    00411   MKCOLOUR(0x25252525),
    00412   MKCOLOUR(0x98989898),
    00413   MKCOLOUR(0xC2C2C2C2),
    00414   MKCOLOUR(0x54545454),
    00415   MKCOLOUR(0x54545454),
    00416 };
    00417
    00418 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile)
    00419 {
    00420   TileType t = GetEffectiveTileType(tile);
    00421   uint32 bits;
    00422
    00423   switch (t) {
    00424     case MP_CLEAR:
    00425       if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) {
    00426         bits = MKCOLOUR(0x37373737);
    00427       } else {
    00428         bits = _vegetation_clear_bits[GetClearGround(tile)];
    00429       }
    00430       break;
    00431
    00432     case MP_INDUSTRY:
    00433       bits = GetIndustrySpec(GetIndustryByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR(0xD0D0D0D0) : MKCOLOUR(0xB5B5B5B5);
    00434       break;
    00435
    00436     case MP_TREES:
    00437       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT) {
    00438         bits = (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR(0x98575798) : MKCOLOUR(0xC25757C2);
    00439       } else {
    00440         bits = MKCOLOUR(0x54575754);
    00441       }
    00442       break;
    00443
    00444     default:
    00445       bits = ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
    00446       break;
    00447   }
    00448
    00449   return bits;
    00450 }
    00451
    00452
    00453 static uint32 _owner_colours[OWNER_END + 1];
    00454
    00461 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
    00462 {
    00463   Owner o;
    00464
    00465   switch (GetTileType(tile)) {
    00466     case MP_INDUSTRY: o = OWNER_END;          break;
    00467     case MP_HOUSE:    o = OWNER_TOWN;         break;
    00468     default:          o = GetTileOwner(tile); break;
    00469     /* FIXME: For MP_ROAD there are multiple owners.
    00470      * GetTileOwner returns the rail owner (level crossing) resp. the owner of ROADTYPE_ROAD (normal road),
    00471      * even if there are no ROADTYPE_ROAD bits on the tile.
    00472      */
    00473   }
    00474
    00475   return _owner_colours[o];
    00476 }
    00477
    00478
    00479 static const uint32 _smallmap_mask_left[3] = {
    00480   MKCOLOUR(0xFF000000),
    00481   MKCOLOUR(0xFFFF0000),
    00482   MKCOLOUR(0xFFFFFF00),
    00483 };
    00484
    00485 static const uint32 _smallmap_mask_right[] = {
    00486   MKCOLOUR(0x000000FF),
    00487   MKCOLOUR(0x0000FFFF),
    00488   MKCOLOUR(0x00FFFFFF),
    00489 };
    00490
    00491 /* each tile has 4 x pixels and 1 y pixel */
    00492
    00493 static GetSmallMapPixels *_smallmap_draw_procs[] = {
    00494   GetSmallMapContoursPixels,
    00495   GetSmallMapVehiclesPixels,
    00496   GetSmallMapIndustriesPixels,
    00497   GetSmallMapRoutesPixels,
    00498   GetSmallMapVegetationPixels,
    00499   GetSmallMapOwnerPixels,
    00500 };
    00501
    00502 static const byte _vehicle_type_colours[6] = {
    00503   184, 191, 152, 15, 215, 184
    00504 };
    00505
    00506
    00507 static void DrawVertMapIndicator(int x, int y, int x2, int y2)
    00508 {
    00509   GfxFillRect(x, y,      x2, y + 3, 69);
    00510   GfxFillRect(x, y2 - 3, x2, y2,    69);
    00511 }
    00512
    00513 static void DrawHorizMapIndicator(int x, int y, int x2, int y2)
    00514 {
    00515   GfxFillRect(x,      y, x + 3, y2, 69);
    00516   GfxFillRect(x2 - 3, y, x2,    y2, 69);
    00517 }
    00518
    00519 enum SmallMapWindowWidgets {
    00520   SM_WIDGET_MAP_BORDER = 3,
    00521   SM_WIDGET_MAP,
    00522   SM_WIDGET_LEGEND,
    00523   SM_WIDGET_BUTTONSPANEL,
    00524   SM_WIDGET_CONTOUR,
    00525   SM_WIDGET_VEHICLES,
    00526   SM_WIDGET_INDUSTRIES,
    00527   SM_WIDGET_ROUTES,
    00528   SM_WIDGET_VEGETATION,
    00529   SM_WIDGET_OWNERS,
    00530   SM_WIDGET_CENTERMAP,
    00531   SM_WIDGET_TOGGLETOWNNAME,
    00532   SM_WIDGET_BOTTOMPANEL,
    00533   SM_WIDGET_ENABLEINDUSTRIES,
    00534   SM_WIDGET_DISABLEINDUSTRIES,
    00535   SM_WIDGET_RESIZEBOX,
    00536 };
    00537
    00538 class SmallMapWindow : public Window
    00539 {
    00540   enum SmallMapType {
    00541     SMT_CONTOUR,
    00542     SMT_VEHICLES,
    00543     SMT_INDUSTRY,
    00544     SMT_ROUTES,
    00545     SMT_VEGETATION,
    00546     SMT_OWNER,
    00547   };
    00548
    00549   static SmallMapType map_type;
    00550   static bool show_towns;
    00551
    00552   int32 scroll_x;
    00553   int32 scroll_y;
    00554   int32 subscroll;
    00555   uint8 refresh;
    00556
    00557   static const int COLUMN_WIDTH = 119;
    00558   static const int MIN_LEGEND_HEIGHT = 6 * 7;
    00559
    00560 public:
    00575   void DrawSmallMap(DrawPixelInfo *dpi)
    00576   {
    00577     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00578     DrawPixelInfo *old_dpi;
    00579     int dx, dy, x, y, x2, y2;
    00580     void *ptr;
    00581     int tile_x;
    00582     int tile_y;
    00583     ViewPort *vp;
    00584
    00585     old_dpi = _cur_dpi;
    00586     _cur_dpi = dpi;
    00587
    00588     /* clear it */
    00589     GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
    00590
    00591     /* setup owner table */
    00592     if (this->map_type == SMT_OWNER) {
    00593       const Company *c;
    00594
    00595       /* fill with some special colours */
    00596       _owner_colours[OWNER_TOWN] = MKCOLOUR(0xB4B4B4B4);
    00597       _owner_colours[OWNER_NONE] = MKCOLOUR(0x54545454);
    00598       _owner_colours[OWNER_WATER] = MKCOLOUR(0xCACACACA);
    00599       _owner_colours[OWNER_END]   = MKCOLOUR(0x20202020); // industry
    00600
    00601       /* now fill with the company colours */
    00602       FOR_ALL_COMPANIES(c) {
    00603         _owner_colours[c->index] =
    00604           _colour_gradient[c->colour][5] * 0x01010101;
    00605       }
    00606     }
    00607
    00608     tile_x = this->scroll_x / TILE_SIZE;
    00609     tile_y = this->scroll_y / TILE_SIZE;
    00610
    00611     dx = dpi->left + this->subscroll;
    00612     tile_x -= dx / 4;
    00613     tile_y += dx / 4;
    00614     dx &= 3;
    00615
    00616     dy = dpi->top;
    00617     tile_x += dy / 2;
    00618     tile_y += dy / 2;
    00619
    00620     if (dy & 1) {
    00621       tile_x++;
    00622       dx += 2;
    00623       if (dx > 3) {
    00624         dx -= 4;
    00625         tile_x--;
    00626         tile_y++;
    00627       }
    00628     }
    00629
    00630     ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
    00631     x = - dx - 4;
    00632     y = 0;
    00633
    00634     for (;;) {
    00635       uint32 mask = 0xFFFFFFFF;
    00636       int reps;
    00637       int t;
    00638
    00639       /* distance from left edge */
    00640       if (x < 0) {
    00641         if (x < -3) goto skip_column;
    00642         /* mask to use at the left edge */
    00643         mask = _smallmap_mask_left[x + 3];
    00644       }
    00645
    00646       /* distance from right edge */
    00647       t = dpi->width - x;
    00648       if (t < 4) {
    00649         if (t <= 0) break; // exit loop
    00650         /* mask to use at the right edge */
    00651         mask &= _smallmap_mask_right[t - 1];
    00652       }
    00653
    00654       /* number of lines */
    00655       reps = (dpi->height - y + 1) / 2;
    00656       if (reps > 0) {
    00657         DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[this->map_type]);
    00658       }
    00659
    00660   skip_column:
    00661       if (y == 0) {
    00662         tile_y++;
    00663         y++;
    00664         ptr = blitter->MoveTo(ptr, 0, 1);
    00665       } else {
    00666         tile_x--;
    00667         y--;
    00668         ptr = blitter->MoveTo(ptr, 0, -1);
    00669       }
    00670       ptr = blitter->MoveTo(ptr, 2, 0);
    00671       x += 2;
    00672     }
    00673
    00674     /* draw vehicles? */
    00675     if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) {
    00676       Vehicle *v;
    00677       bool skip;
    00678       byte colour;
    00679
    00680       FOR_ALL_VEHICLES(v) {
    00681         if (v->type != VEH_EFFECT &&
    00682             (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) {
    00683           /* Remap into flat coordinates. */
    00684           Point pt = RemapCoords(
    00685             v->x_pos / TILE_SIZE - this->scroll_x / TILE_SIZE, // divide each one separately because (a-b)/c != a/c-b/c in integer world
    00686             v->y_pos / TILE_SIZE - this->scroll_y / TILE_SIZE, //    dtto
    00687             0);
    00688           x = pt.x;
    00689           y = pt.y;
    00690
    00691           /* Check if y is out of bounds? */
    00692           y -= dpi->top;
    00693           if (!IsInsideMM(y, 0, dpi->height)) continue;
    00694
    00695           /* Default is to draw both pixels. */
    00696           skip = false;
    00697
    00698           /* Offset X coordinate */
    00699           x -= this->subscroll + 3 + dpi->left;
    00700
    00701           if (x < 0) {
    00702             /* if x+1 is 0, that means we're on the very left edge,
    00703              *  and should thus only draw a single pixel */
    00704             if (++x != 0) continue;
    00705             skip = true;
    00706           } else if (x >= dpi->width - 1) {
    00707             /* Check if we're at the very right edge, and if so draw only a single pixel */
    00708             if (x != dpi->width - 1) continue;
    00709             skip = true;
    00710           }
    00711
    00712           /* Calculate pointer to pixel and the colour */
    00713           colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
    00714
    00715           /* And draw either one or two pixels depending on clipping */
    00716           blitter->SetPixel(dpi->dst_ptr, x, y, colour);
    00717           if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
    00718         }
    00719       }
    00720     }
    00721
    00722     if (this->show_towns) {
    00723       const Town *t;
    00724
    00725       FOR_ALL_TOWNS(t) {
    00726         /* Remap the town coordinate */
    00727         Point pt = RemapCoords(
    00728           (int)(TileX(t->xy) * TILE_SIZE - this->scroll_x) / TILE_SIZE,
    00729           (int)(TileY(t->xy) * TILE_SIZE - this->scroll_y) / TILE_SIZE,
    00730           0);
    00731         x = pt.x - this->subscroll + 3 - (t->sign.width_2 >> 1);
    00732         y = pt.y;
    00733
    00734         /* Check if the town sign is within bounds */
    00735         if (x + t->sign.width_2 > dpi->left &&
    00736             x < dpi->left + dpi->width &&
    00737             y + 6 > dpi->top &&
    00738             y < dpi->top + dpi->height) {
    00739           /* And draw it. */
    00740           SetDParam(0, t->index);
    00741           DrawString(x, y, STR_2056, TC_WHITE);
    00742         }
    00743       }
    00744     }
    00745
    00746     /* Draw map indicators */
    00747     {
    00748       Point pt;
    00749
    00750       /* Find main viewport. */
    00751       vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    00752
    00753       pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
    00754
    00755       x = vp->virtual_left - pt.x;
    00756       y = vp->virtual_top - pt.y;
    00757       x2 = (x + vp->virtual_width) / TILE_SIZE;
    00758       y2 = (y + vp->virtual_height) / TILE_SIZE;
    00759       x /= TILE_SIZE;
    00760       y /= TILE_SIZE;
    00761
    00762       x -= this->subscroll;
    00763       x2 -= this->subscroll;
    00764
    00765       DrawVertMapIndicator(x, y, x, y2);
    00766       DrawVertMapIndicator(x2, y, x2, y2);
    00767
    00768       DrawHorizMapIndicator(x, y, x2, y);
    00769       DrawHorizMapIndicator(x, y2, x2, y2);
    00770     }
    00771     _cur_dpi = old_dpi;
    00772   }
    00773
    00774   void SmallMapCenterOnCurrentPos()
    00775   {
    00776     int x, y;
    00777     ViewPort *vp;
    00778     vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    00779
    00780     x  = ((vp->virtual_width  - (this->widget[SM_WIDGET_MAP].right  - this->widget[SM_WIDGET_MAP].left) * TILE_SIZE) / 2 + vp->virtual_left) / 4;
    00781     y  = ((vp->virtual_height - (this->widget[SM_WIDGET_MAP].bottom - this->widget[SM_WIDGET_MAP].top ) * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
    00782     this->scroll_x = (y - x) & ~0xF;
    00783     this->scroll_y = (x + y) & ~0xF;
    00784     this->SetDirty();
    00785   }
    00786
    00787   void ResizeLegend()
    00788   {
    00789     Widget *legend = &this->widget[SM_WIDGET_LEGEND];
    00790     int rows = (legend->bottom - legend->top) - 1;
    00791     int columns = (legend->right - legend->left) / COLUMN_WIDTH;
    00792     int new_rows = (this->map_type == SMT_INDUSTRY) ? ((_smallmap_industry_count + columns - 1) / columns) * 6 : MIN_LEGEND_HEIGHT;
    00793
    00794     new_rows = max(new_rows, MIN_LEGEND_HEIGHT);
    00795
    00796     if (new_rows != rows) {
    00797       this->SetDirty();
    00798
    00799       /* The legend widget needs manual adjustment as by default
    00800        * it lays outside the filler widget's bounds. */
    00801       legend->top--;
    00802       /* Resize the filler widget, and move widgets below it. */
    00803       ResizeWindowForWidget(this, SM_WIDGET_BUTTONSPANEL, 0, new_rows - rows);
    00804       legend->top++;
    00805
    00806       /* Resize map border widget so the window stays the same size */
    00807       ResizeWindowForWidget(this, SM_WIDGET_MAP_BORDER, 0, rows - new_rows);
    00808       /* Manually adjust the map widget as it lies completely within
    00809        * the map border widget */
    00810       this->widget[SM_WIDGET_MAP].bottom += rows - new_rows;
    00811
    00812       this->SetDirty();
    00813     }
    00814   }
    00815
    00816   SmallMapWindow(const WindowDesc *desc, int window_number) : Window(desc, window_number)
    00817   {
    00818     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00819     this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00820
    00821     this->SmallMapCenterOnCurrentPos();
    00822     this->FindWindowPlacementAndResize(desc);
    00823   }
    00824
    00825   virtual void OnPaint()
    00826   {
    00827     DrawPixelInfo new_dpi;
    00828
    00829     /* Hide Enable all/Disable all buttons if is not industry type small map*/
    00830     this->SetWidgetHiddenState(SM_WIDGET_ENABLEINDUSTRIES, this->map_type != SMT_INDUSTRY);
    00831     this->SetWidgetHiddenState(SM_WIDGET_DISABLEINDUSTRIES, this->map_type != SMT_INDUSTRY);
    00832
    00833     /* draw the window */
    00834     SetDParam(0, STR_00E5_CONTOURS + this->map_type);
    00835     this->DrawWidgets();
    00836
    00837     const Widget *legend = &this->widget[SM_WIDGET_LEGEND];
    00838
    00839     int y_org = legend->top + 1;
    00840     int x = 4;
    00841     int y = y_org;
    00842
    00843     for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
    00844       if (tbl->col_break || y >= legend->bottom) {
    00845         /* Column break needed, continue at top, COLUMN_WIDTH pixels
    00846          * (one "row") to the right. */
    00847         x += COLUMN_WIDTH;
    00848         y = y_org;
    00849       }
    00850
    00851       if (this->map_type == SMT_INDUSTRY) {
    00852         /* Industry name must be formated, since it's not in tiny font in the specs.
    00853          * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font.*/
    00854         SetDParam(0, tbl->legend);
    00855         assert(tbl->type < NUM_INDUSTRYTYPES);
    00856         SetDParam(1, _industry_counts[tbl->type]);
    00857         if (!tbl->show_on_map) {
    00858           /* Simply draw the string, not the black border of the legend colour.
    00859            * This will enforce the idea of the disabled item */
    00860           DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
    00861         } else {
    00862           DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
    00863           GfxFillRect(x, y + 1, x + 8, y + 5, 0); // outer border of the legend colour
    00864         }
    00865       } else {
    00866         /* Anything that is not an industry is using normal process */
    00867         GfxFillRect(x, y + 1, x + 8, y + 5, 0);
    00868         DrawString(x + 11, y, tbl->legend, TC_FROMSTRING);
    00869       }
    00870       GfxFillRect(x + 1, y + 2, x + 7, y + 4, tbl->colour); // legend colour
    00871
    00872       y += 6;
    00873     }
    00874
    00875     const Widget *wi = &this->widget[SM_WIDGET_MAP];
    00876     if (!FillDrawPixelInfo(&new_dpi, wi->left + 1, wi->top + 1, wi->right - wi->left - 1, wi->bottom - wi->top - 1)) return;
    00877
    00878     this->DrawSmallMap(&new_dpi);
    00879   }
    00880
    00881   virtual void OnClick(Point pt, int widget)
    00882   {
    00883     switch (widget) {
    00884       case SM_WIDGET_MAP: { // Map window
    00885         /*
    00886          * XXX: scrolling with the left mouse button is done by subsequently
    00887          * clicking with the left mouse button; clicking once centers the
    00888          * large map at the selected point. So by unclicking the left mouse
    00889          * button here, it gets reclicked during the next inputloop, which
    00890          * would make it look like the mouse is being dragged, while it is
    00891          * actually being (virtually) clicked every inputloop.
    00892          */
    00893         _left_button_clicked = false;
    00894
    00895         Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
    00896         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    00897         w->viewport->follow_vehicle = INVALID_VEHICLE;
    00898         w->viewport->dest_scrollpos_x = pt.x + ((_cursor.pos.x - this->left + 2) << 4) - (w->viewport->virtual_width >> 1);
    00899         w->viewport->dest_scrollpos_y = pt.y + ((_cursor.pos.y - this->top - 16) << 4) - (w->viewport->virtual_height >> 1);
    00900
    00901         this->SetDirty();
    00902       } break;
    00903
    00904       case SM_WIDGET_CONTOUR:    // Show land contours
    00905       case SM_WIDGET_VEHICLES:   // Show vehicles
    00906       case SM_WIDGET_INDUSTRIES: // Show industries
    00907       case SM_WIDGET_ROUTES:     // Show transport routes
    00908       case SM_WIDGET_VEGETATION: // Show vegetation
    00909       case SM_WIDGET_OWNERS:     // Show land owners
    00910         this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
    00911         this->map_type = (SmallMapType)(widget - SM_WIDGET_CONTOUR);
    00912         this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
    00913
    00914         this->ResizeLegend();
    00915
    00916         this->SetDirty();
    00917         SndPlayFx(SND_15_BEEP);
    00918         break;
    00919
    00920       case SM_WIDGET_CENTERMAP: // Center the smallmap again
    00921         this->SmallMapCenterOnCurrentPos();
    00922
    00923         this->SetDirty();
    00924         SndPlayFx(SND_15_BEEP);
    00925         break;
    00926
    00927       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
    00928         this->show_towns = !this->show_towns;
    00929         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
    00930
    00931         this->SetDirty();
    00932         SndPlayFx(SND_15_BEEP);
    00933         break;
    00934
    00935       case SM_WIDGET_LEGEND: // Legend
    00936         /* if industry type small map*/
    00937         if (this->map_type == SMT_INDUSTRY) {
    00938           /* if click on industries label, find right industry type and enable/disable it */
    00939           Widget *wi = &this->widget[SM_WIDGET_LEGEND]; // label panel
    00940           uint column = (pt.x - 4) / COLUMN_WIDTH;
    00941           uint line = (pt.y - wi->top - 2) / 6;
    00942           int rows_per_column = (wi->bottom - wi->top) / 6;
    00943
    00944           /* check if click is on industry label*/
    00945           int industry_pos = (column * rows_per_column) + line;
    00946           if (industry_pos < _smallmap_industry_count) {
    00947             _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
    00948           }
    00949
    00950           /* Raise the two buttons "all", as we have done a specific choice */
    00951           this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    00952           this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    00953           this->SetDirty();
    00954         }
    00955         break;
    00956
    00957       case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
    00958         for (int i = 0; i != _smallmap_industry_count; i++) {
    00959           _legend_from_industries[i].show_on_map = true;
    00960         }
    00961         /* toggle appeareance indicating the choice */
    00962         this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
    00963         this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    00964         this->SetDirty();
    00965         break;
    00966
    00967       case SM_WIDGET_DISABLEINDUSTRIES: // disable all industries
    00968         for (int i = 0; i != _smallmap_industry_count; i++) {
    00969           _legend_from_industries[i].show_on_map = false;
    00970         }
    00971         /* toggle appeareance indicating the choice */
    00972         this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    00973         this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
    00974         this->SetDirty();
    00975         break;
    00976     }
    00977   }
    00978
    00979   virtual void OnRightClick(Point pt, int widget)
    00980   {
    00981     if (widget == SM_WIDGET_MAP) {
    00982       if (_scrolling_viewport) return;
    00983       _scrolling_viewport = true;
    00984     }
    00985   }
    00986
    00987   virtual void OnTick()
    00988   {
    00989     /* update the window every now and then */
    00990     if ((++this->refresh & 0x1F) == 0) this->SetDirty();
    00991   }
    00992
    00993   virtual void OnScroll(Point delta)
    00994   {
    00995     _cursor.fix_at = true;
    00996
    00997     int x = this->scroll_x;
    00998     int y = this->scroll_y;
    00999
    01000     int sub = this->subscroll + delta.x;
    01001
    01002     x -= (sub >> 2) << 4;
    01003     y += (sub >> 2) << 4;
    01004     sub &= 3;
    01005
    01006     x += (delta.y >> 1) << 4;
    01007     y += (delta.y >> 1) << 4;
    01008
    01009     if (delta.y & 1) {
    01010       x += TILE_SIZE;
    01011       sub += 2;
    01012       if (sub > 3) {
    01013         sub -= 4;
    01014         x -= TILE_SIZE;
    01015         y += TILE_SIZE;
    01016       }
    01017     }
    01018
    01019     int hx = (this->widget[SM_WIDGET_MAP].right  - this->widget[SM_WIDGET_MAP].left) / 2;
    01020     int hy = (this->widget[SM_WIDGET_MAP].bottom - this->widget[SM_WIDGET_MAP].top ) / 2;
    01021     int hvx = hx * -4 + hy * 8;
    01022     int hvy = hx *  4 + hy * 8;
    01023     if (x < -hvx) {
    01024       x = -hvx;
    01025       sub = 0;
    01026     }
    01027     if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
    01028       x = MapMaxX() * TILE_SIZE - hvx;
    01029       sub = 0;
    01030     }
    01031     if (y < -hvy) {
    01032       y = -hvy;
    01033       sub = 0;
    01034     }
    01035     if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
    01036       y = MapMaxY() * TILE_SIZE - hvy;
    01037       sub = 0;
    01038     }
    01039
    01040     this->scroll_x = x;
    01041     this->scroll_y = y;
    01042     this->subscroll = sub;
    01043
    01044     this->SetDirty();
    01045   }
    01046
    01047   virtual void OnResize(Point new_size, Point delta)
    01048   {
    01049     if (delta.x != 0 && this->map_type == SMT_INDUSTRY) this->ResizeLegend();
    01050   }
    01051 };
    01052
    01053 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
    01054 bool SmallMapWindow::show_towns = true;
    01055
    01056 static const WindowDesc _smallmap_desc(
    01057   WDP_AUTO, WDP_AUTO, 350, 214, 446, 314,
    01058   WC_SMALLMAP, WC_NONE,
    01059   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01060   _smallmap_widgets
    01061 );
    01062
    01063 void ShowSmallMap()
    01064 {
    01065   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
    01066 }
    01067
    01068 /* Extra ViewPort Window Stuff */
    01069 static const Widget _extra_view_port_widgets[] = {
    01070 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,    0,   13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
    01071 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   287,    0,   13, STR_EXTRA_VIEW_PORT_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},
    01072 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   288,   299,    0,   13, 0x0,                              STR_STICKY_BUTTON},
    01073 {      WWT_PANEL,     RESIZE_RB,  COLOUR_GREY,     0,   299,   14,   33, 0x0,                              STR_NULL},
    01074 {      WWT_INSET,     RESIZE_RB,  COLOUR_GREY,     2,   297,   16,   31, 0x0,                              STR_NULL},
    01075 { WWT_PUSHIMGBTN,     RESIZE_TB,  COLOUR_GREY,     0,    21,   34,   55, SPR_IMG_ZOOMIN,                   STR_017F_ZOOM_THE_VIEW_IN},
    01076 { WWT_PUSHIMGBTN,     RESIZE_TB,  COLOUR_GREY,    22,    43,   34,   55, SPR_IMG_ZOOMOUT,                  STR_0180_ZOOM_THE_VIEW_OUT},
    01077 { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,    44,   171,   34,   55, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT},
    01078 { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,   172,   298,   34,   55, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT},
    01079 {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,   299,   299,   34,   55, 0x0,                              STR_NULL},
    01080 {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   287,   56,   67, 0x0,                              STR_NULL},
    01081 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   288,   299,   56,   67, 0x0,                              STR_RESIZE_BUTTON},
    01082 {   WIDGETS_END},
    01083 };
    01084
    01085 class ExtraViewportWindow : public Window
    01086 {
    01087   enum ExtraViewportWindowWidgets {
    01088     EVW_CLOSE,
    01089     EVW_CAPTION,
    01090     EVW_STICKY,
    01091     EVW_BACKGROUND,
    01092     EVW_VIEWPORT,
    01093     EVW_ZOOMIN,
    01094     EVW_ZOOMOUT,
    01095     EVW_MAIN_TO_VIEW,
    01096     EVW_VIEW_TO_MAIN,
    01097     EVW_SPACER1,
    01098     EVW_SPACER2,
    01099     EVW_RESIZE,
    01100   };
    01101
    01102 public:
    01103   ExtraViewportWindow(const WindowDesc *desc, int window_number, TileIndex tile) : Window(desc, window_number)
    01104   {
    01105     /* New viewport start at (zero,zero) */
    01106     InitializeWindowViewport(this, 3, 17, this->widget[EVW_VIEWPORT].right - this->widget[EVW_VIEWPORT].left - 1, this->widget[EVW_VIEWPORT].bottom - this->widget[EVW_VIEWPORT].top - 1, 0, ZOOM_LVL_VIEWPORT);
    01107
    01108     this->DisableWidget(EVW_ZOOMIN);
    01109     this->FindWindowPlacementAndResize(desc);
    01110
    01111     Point pt;
    01112     if (tile == INVALID_TILE) {
    01113       /* the main window with the main view */
    01114       const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    01115
    01116       /* center on same place as main window (zoom is maximum, no adjustment needed) */
    01117       pt.x = w->viewport->scrollpos_x + w->viewport->virtual_height / 2;
    01118       pt.y = w->viewport->scrollpos_y + w->viewport->virtual_height / 2;
    01119     } else {
    01120       pt = RemapCoords(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, TileHeight(tile));
    01121     }
    01122
    01123     this->viewport->scrollpos_x = pt.x - ((this->widget[EVW_VIEWPORT].right - this->widget[EVW_VIEWPORT].left) - 1) / 2;
    01124     this->viewport->scrollpos_y = pt.y - ((this->widget[EVW_VIEWPORT].bottom - this->widget[EVW_VIEWPORT].top) - 1) / 2;
    01125     this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x;
    01126     this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y;
    01127
    01128   }
    01129
    01130   virtual void OnPaint()
    01131   {
    01132     /* set the number in the title bar */
    01133     SetDParam(0, this->window_number + 1);
    01134
    01135     this->DrawWidgets();
    01136     this->DrawViewport();
    01137   }
    01138
    01139   virtual void OnClick(Point pt, int widget)
    01140   {
    01141     switch (widget) {
    01142       case EVW_ZOOMIN: DoZoomInOutWindow(ZOOM_IN,  this); break;
    01143       case EVW_ZOOMOUT: DoZoomInOutWindow(ZOOM_OUT, this); break;
    01144
    01145       case EVW_MAIN_TO_VIEW: { // location button (move main view to same spot as this view) 'Paste Location'
    01146         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    01147         int x = this->viewport->scrollpos_x; // Where is the main looking at
    01148         int y = this->viewport->scrollpos_y;
    01149
    01150         /* set this view to same location. Based on the center, adjusting for zoom */
    01151         w->viewport->dest_scrollpos_x =  x - (w->viewport->virtual_width -  this->viewport->virtual_width) / 2;
    01152         w->viewport->dest_scrollpos_y =  y - (w->viewport->virtual_height - this->viewport->virtual_height) / 2;
    01153         w->viewport->follow_vehicle   = INVALID_VEHICLE;
    01154       } break;
    01155
    01156       case EVW_VIEW_TO_MAIN: { // inverse location button (move this view to same spot as main view) 'Copy Location'
    01157         const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
    01158         int x = w->viewport->scrollpos_x;
    01159         int y = w->viewport->scrollpos_y;
    01160
    01161         this->viewport->dest_scrollpos_x =  x + (w->viewport->virtual_width -  this->viewport->virtual_width) / 2;
    01162         this->viewport->dest_scrollpos_y =  y + (w->viewport->virtual_height - this->viewport->virtual_height) / 2;
    01163       } break;
    01164     }
    01165   }
    01166
    01167   virtual void OnResize(Point new_size, Point delta)
    01168   {
    01169     this->viewport->width          += delta.x;
    01170     this->viewport->height         += delta.y;
    01171     this->viewport->virtual_width  += delta.x;
    01172     this->viewport->virtual_height += delta.y;
    01173   }
    01174
    01175   virtual void OnScroll(Point delta)
    01176   {
    01177     const ViewPort *vp = IsPtInWindowViewport(this, _cursor.pos.x, _cursor.pos.y);
    01178     if (vp == NULL) return;
    01179
    01180     this->viewport->scrollpos_x += ScaleByZoom(delta.x, vp->zoom);
    01181     this->viewport->scrollpos_y += ScaleByZoom(delta.y, vp->zoom);
    01182     this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x;
    01183     this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y;
    01184   }
    01185
    01186   virtual void OnMouseWheel(int wheel)
    01187   {
    01188     ZoomInOrOutToCursorWindow(wheel < 0, this);
    01189   }
    01190
    01191   virtual void OnInvalidateData(int data = 0)
    01192   {
    01193     /* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */
    01194     HandleZoomMessage(this, this->viewport, EVW_ZOOMIN, EVW_ZOOMOUT);
    01195   }
    01196 };
    01197
    01198 static const WindowDesc _extra_view_port_desc(
    01199   WDP_AUTO, WDP_AUTO, 300, 68, 300, 268,
    01200   WC_EXTRA_VIEW_PORT, WC_NONE,
    01201   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01202   _extra_view_port_widgets
    01203 );
    01204
    01205 void ShowExtraViewPortWindow(TileIndex tile)
    01206 {
    01207   int i = 0;
    01208
    01209   /* find next free window number for extra viewport */
    01210   while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++;
    01211
    01212   new ExtraViewportWindow(&_extra_view_port_desc, i, tile);
    01213 }
    01214
    01223 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
    01224 {
    01225   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
    01226
    01227   /* If a user scrolls to a tile (via what way what so ever) and already is on
    01228    *  that tile (e.g.: pressed twice), move the smallmap to that location,
    01229    *  so you directly see where you are on the smallmap. */
    01230
    01231   if (res) return res;
    01232
    01233   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
    01234   if (w != NULL) w->SmallMapCenterOnCurrentPos();
    01235
    01236   return res;
    01237 }