smallmap_gui.cpp

Go to the documentation of this file.
00001 /* $Id: smallmap_gui.cpp 15725 2009-03-15 15:25:18Z smatz $ */
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       _cursor.delta.x = 0;
00985       _cursor.delta.y = 0;
00986     }
00987   }
00988 
00989   virtual void OnTick()
00990   {
00991     /* update the window every now and then */
00992     if ((++this->refresh & 0x1F) == 0) this->SetDirty();
00993   }
00994 
00995   virtual void OnScroll(Point delta)
00996   {
00997     _cursor.fix_at = true;
00998 
00999     int x = this->scroll_x;
01000     int y = this->scroll_y;
01001 
01002     int sub = this->subscroll + delta.x;
01003 
01004     x -= (sub >> 2) << 4;
01005     y += (sub >> 2) << 4;
01006     sub &= 3;
01007 
01008     x += (delta.y >> 1) << 4;
01009     y += (delta.y >> 1) << 4;
01010 
01011     if (delta.y & 1) {
01012       x += TILE_SIZE;
01013       sub += 2;
01014       if (sub > 3) {
01015         sub -= 4;
01016         x -= TILE_SIZE;
01017         y += TILE_SIZE;
01018       }
01019     }
01020 
01021     int hx = (this->widget[SM_WIDGET_MAP].right  - this->widget[SM_WIDGET_MAP].left) / 2;
01022     int hy = (this->widget[SM_WIDGET_MAP].bottom - this->widget[SM_WIDGET_MAP].top ) / 2;
01023     int hvx = hx * -4 + hy * 8;
01024     int hvy = hx *  4 + hy * 8;
01025     if (x < -hvx) {
01026       x = -hvx;
01027       sub = 0;
01028     }
01029     if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
01030       x = MapMaxX() * TILE_SIZE - hvx;
01031       sub = 0;
01032     }
01033     if (y < -hvy) {
01034       y = -hvy;
01035       sub = 0;
01036     }
01037     if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
01038       y = MapMaxY() * TILE_SIZE - hvy;
01039       sub = 0;
01040     }
01041 
01042     this->scroll_x = x;
01043     this->scroll_y = y;
01044     this->subscroll = sub;
01045 
01046     this->SetDirty();
01047   }
01048 
01049   virtual void OnResize(Point new_size, Point delta)
01050   {
01051     if (delta.x != 0 && this->map_type == SMT_INDUSTRY) this->ResizeLegend();
01052   }
01053 };
01054 
01055 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
01056 bool SmallMapWindow::show_towns = true;
01057 
01058 static const WindowDesc _smallmap_desc(
01059   WDP_AUTO, WDP_AUTO, 350, 214, 446, 314,
01060   WC_SMALLMAP, WC_NONE,
01061   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
01062   _smallmap_widgets
01063 );
01064 
01065 void ShowSmallMap()
01066 {
01067   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
01068 }
01069 
01070 /* Extra ViewPort Window Stuff */
01071 static const Widget _extra_view_port_widgets[] = {
01072 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,    0,   13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
01073 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   287,    0,   13, STR_EXTRA_VIEW_PORT_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},
01074 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   288,   299,    0,   13, 0x0,                              STR_STICKY_BUTTON},
01075 {      WWT_PANEL,     RESIZE_RB,  COLOUR_GREY,     0,   299,   14,   33, 0x0,                              STR_NULL},
01076 {      WWT_INSET,     RESIZE_RB,  COLOUR_GREY,     2,   297,   16,   31, 0x0,                              STR_NULL},
01077 { WWT_PUSHIMGBTN,     RESIZE_TB,  COLOUR_GREY,     0,    21,   34,   55, SPR_IMG_ZOOMIN,                   STR_017F_ZOOM_THE_VIEW_IN},
01078 { WWT_PUSHIMGBTN,     RESIZE_TB,  COLOUR_GREY,    22,    43,   34,   55, SPR_IMG_ZOOMOUT,                  STR_0180_ZOOM_THE_VIEW_OUT},
01079 { 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},
01080 { 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},
01081 {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,   299,   299,   34,   55, 0x0,                              STR_NULL},
01082 {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   287,   56,   67, 0x0,                              STR_NULL},
01083 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   288,   299,   56,   67, 0x0,                              STR_RESIZE_BUTTON},
01084 {   WIDGETS_END},
01085 };
01086 
01087 class ExtraViewportWindow : public Window
01088 {
01089   enum ExtraViewportWindowWidgets {
01090     EVW_CLOSE,
01091     EVW_CAPTION,
01092     EVW_STICKY,
01093     EVW_BACKGROUND,
01094     EVW_VIEWPORT,
01095     EVW_ZOOMIN,
01096     EVW_ZOOMOUT,
01097     EVW_MAIN_TO_VIEW,
01098     EVW_VIEW_TO_MAIN,
01099     EVW_SPACER1,
01100     EVW_SPACER2,
01101     EVW_RESIZE,
01102   };
01103 
01104 public:
01105   ExtraViewportWindow(const WindowDesc *desc, int window_number, TileIndex tile) : Window(desc, window_number)
01106   {
01107     /* New viewport start at (zero,zero) */
01108     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);
01109 
01110     this->DisableWidget(EVW_ZOOMIN);
01111     this->FindWindowPlacementAndResize(desc);
01112 
01113     Point pt;
01114     if (tile == INVALID_TILE) {
01115       /* the main window with the main view */
01116       const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01117 
01118       /* center on same place as main window (zoom is maximum, no adjustment needed) */
01119       pt.x = w->viewport->scrollpos_x + w->viewport->virtual_height / 2;
01120       pt.y = w->viewport->scrollpos_y + w->viewport->virtual_height / 2;
01121     } else {
01122       pt = RemapCoords(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, TileHeight(tile));
01123     }
01124 
01125     this->viewport->scrollpos_x = pt.x - ((this->widget[EVW_VIEWPORT].right - this->widget[EVW_VIEWPORT].left) - 1) / 2;
01126     this->viewport->scrollpos_y = pt.y - ((this->widget[EVW_VIEWPORT].bottom - this->widget[EVW_VIEWPORT].top) - 1) / 2;
01127     this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x;
01128     this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y;
01129 
01130   }
01131 
01132   virtual void OnPaint()
01133   {
01134     /* set the number in the title bar */
01135     SetDParam(0, this->window_number + 1);
01136 
01137     this->DrawWidgets();
01138     this->DrawViewport();
01139   }
01140 
01141   virtual void OnClick(Point pt, int widget)
01142   {
01143     switch (widget) {
01144       case EVW_ZOOMIN: DoZoomInOutWindow(ZOOM_IN,  this); break;
01145       case EVW_ZOOMOUT: DoZoomInOutWindow(ZOOM_OUT, this); break;
01146 
01147       case EVW_MAIN_TO_VIEW: { // location button (move main view to same spot as this view) 'Paste Location'
01148         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01149         int x = this->viewport->scrollpos_x; // Where is the main looking at
01150         int y = this->viewport->scrollpos_y;
01151 
01152         /* set this view to same location. Based on the center, adjusting for zoom */
01153         w->viewport->dest_scrollpos_x =  x - (w->viewport->virtual_width -  this->viewport->virtual_width) / 2;
01154         w->viewport->dest_scrollpos_y =  y - (w->viewport->virtual_height - this->viewport->virtual_height) / 2;
01155         w->viewport->follow_vehicle   = INVALID_VEHICLE;
01156       } break;
01157 
01158       case EVW_VIEW_TO_MAIN: { // inverse location button (move this view to same spot as main view) 'Copy Location'
01159         const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01160         int x = w->viewport->scrollpos_x;
01161         int y = w->viewport->scrollpos_y;
01162 
01163         this->viewport->dest_scrollpos_x =  x + (w->viewport->virtual_width -  this->viewport->virtual_width) / 2;
01164         this->viewport->dest_scrollpos_y =  y + (w->viewport->virtual_height - this->viewport->virtual_height) / 2;
01165       } break;
01166     }
01167   }
01168 
01169   virtual void OnResize(Point new_size, Point delta)
01170   {
01171     this->viewport->width          += delta.x;
01172     this->viewport->height         += delta.y;
01173     this->viewport->virtual_width  += delta.x;
01174     this->viewport->virtual_height += delta.y;
01175   }
01176 
01177   virtual void OnScroll(Point delta)
01178   {
01179     const ViewPort *vp = IsPtInWindowViewport(this, _cursor.pos.x, _cursor.pos.y);
01180     if (vp == NULL) return;
01181 
01182     this->viewport->scrollpos_x += ScaleByZoom(delta.x, vp->zoom);
01183     this->viewport->scrollpos_y += ScaleByZoom(delta.y, vp->zoom);
01184     this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x;
01185     this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y;
01186   }
01187 
01188   virtual void OnMouseWheel(int wheel)
01189   {
01190     ZoomInOrOutToCursorWindow(wheel < 0, this);
01191   }
01192 
01193   virtual void OnInvalidateData(int data = 0)
01194   {
01195     /* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */
01196     HandleZoomMessage(this, this->viewport, EVW_ZOOMIN, EVW_ZOOMOUT);
01197   }
01198 };
01199 
01200 static const WindowDesc _extra_view_port_desc(
01201   WDP_AUTO, WDP_AUTO, 300, 68, 300, 268,
01202   WC_EXTRA_VIEW_PORT, WC_NONE,
01203   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
01204   _extra_view_port_widgets
01205 );
01206 
01207 void ShowExtraViewPortWindow(TileIndex tile)
01208 {
01209   int i = 0;
01210 
01211   /* find next free window number for extra viewport */
01212   while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++;
01213 
01214   new ExtraViewportWindow(&_extra_view_port_desc, i, tile);
01215 }
01216 
01225 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
01226 {
01227   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
01228 
01229   /* If a user scrolls to a tile (via what way what so ever) and already is on
01230    *  that tile (e.g.: pressed twice), move the smallmap to that location,
01231    *  so you directly see where you are on the smallmap. */
01232 
01233   if (res) return res;
01234 
01235   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
01236   if (w != NULL) w->SmallMapCenterOnCurrentPos();
01237 
01238   return res;
01239 }

Generated on Wed Jul 15 20:36:02 2009 for OpenTTD by  doxygen 1.5.6