smallmap_gui.cpp

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

Generated on Mon Sep 22 20:34:18 2008 for openttd by  doxygen 1.5.6