smallmap_gui.cpp

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

Generated on Wed Dec 23 23:27:54 2009 for OpenTTD by  doxygen 1.5.6