diff --git a/bin/data/innerhighlight.grf b/bin/data/innerhighlight.grf
new file mode 100644
index 0000000000..4118f515d1
Binary files /dev/null and b/bin/data/innerhighlight.grf differ
diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj
index c958101718..770a868c00 100644
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -663,6 +663,7 @@
+
@@ -748,6 +749,7 @@
+
@@ -831,6 +833,7 @@
+
diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters
index d83ca2a376..3400671e56 100644
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -1218,6 +1218,9 @@
Header Files
+
+ Header Files
+
Core Source Code
@@ -1473,6 +1476,9 @@
GUI Source Code
+
+ GUI Source Code
+
Widgets
@@ -1722,6 +1728,9 @@
Command handlers
+
+ Command handlers
+
Save/Load handlers
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index a84a122b32..60a4518eaa 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -1926,6 +1926,10 @@
RelativePath=".\..\src\zoom_type.h"
>
+
+
+
+
+
+
+
+
+
+
+
+
Append() = new DropDownListStringItem(STR_SETTINGS_MENU_SCRIPT_SETTINGS, OME_SCRIPT_SETTINGS, false);
*list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_NEWGRF_SETTINGS, OME_NEWGRFSETTINGS, false);
+ *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_ZONING, OME_ZONING, false);
*list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS, OME_TRANSPARENCIES, false);
*list->Append() = new DropDownListItem(-1, false);
*list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED, OME_SHOW_TOWNNAMES, false, HasBit(_display_opt, DO_SHOW_TOWN_NAMES));
@@ -347,6 +350,7 @@ static CallBackFunction MenuClickSettings(int index)
case OME_SETTINGS: ShowGameSettings(); return CBF_NONE;
case OME_SCRIPT_SETTINGS: ShowAIConfigWindow(); return CBF_NONE;
case OME_NEWGRFSETTINGS: ShowNewGRFSettings(!_networking && _settings_client.gui.UserIsAllowedToChangeNewGRFs(), true, true, &_grfconfig); return CBF_NONE;
+ case OME_ZONING: ShowZoningToolbar(); break;
case OME_TRANSPARENCIES: ShowTransparencyToolbar(); break;
case OME_SHOW_TOWNNAMES: ToggleBit(_display_opt, DO_SHOW_TOWN_NAMES); break;
diff --git a/src/viewport.cpp b/src/viewport.cpp
index a1bb2c81d1..142738be41 100644
--- a/src/viewport.cpp
+++ b/src/viewport.cpp
@@ -80,6 +80,7 @@
#include "waypoint_func.h"
#include "window_func.h"
#include "tilehighlight_func.h"
+#include "zoning.h"
#include "window_gui.h"
#include "linkgraph/linkgraph_gui.h"
#include "viewport_sprite_sorter.h"
@@ -1200,7 +1201,10 @@ static void ViewportAddLandscape()
_vd.last_foundation_child[1] = NULL;
_tile_type_procs[tile_type]->draw_tile_proc(&tile_info);
- if (tile_info.tile != INVALID_TILE) DrawTileSelection(&tile_info);
+ if (tile_info.tile != INVALID_TILE) {
+ DrawTileSelection(&tile_info);
+ DrawTileZoning(&tile_info);
+ }
}
}
}
diff --git a/src/window_type.h b/src/window_type.h
index 2b6b13595d..ba46330a27 100644
--- a/src/window_type.h
+++ b/src/window_type.h
@@ -668,6 +668,7 @@ enum WindowClass {
* - 0 = #SpriteAlignerWidgets
*/
WC_SPRITE_ALIGNER,
+ WC_ZONING_TOOLBAR,
/**
* Linkgraph legend; %Window numbers:
diff --git a/src/zoning.h b/src/zoning.h
new file mode 100644
index 0000000000..bfea293342
--- /dev/null
+++ b/src/zoning.h
@@ -0,0 +1,48 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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 .
+ */
+
+/** @file zoning.h */
+
+#ifndef ZONING_H
+#define ZONING_H
+
+#include "tile_cmd.h"
+#include "company_type.h"
+
+/**
+ * Zoning evaluation modes
+ */
+enum ZoningEvaluationMode {
+ ZEM_NOTHING = 0, ///< No zoning action selected
+ ZEM_AUTHORITY, ///< Check the local authority's opinion.
+ ZEM_CAN_BUILD, ///< Check wither or not the player can build.
+ ZEM_STA_CATCH, ///< Check catchment area for stations
+ ZEM_BUL_UNSER, ///< Check for unserved buildings
+ ZEM_IND_UNSER, ///< Check for unserved industries
+};
+
+/**
+ * Global Zoning state structure
+ */
+struct Zoning {
+ ZoningEvaluationMode inner;
+ ZoningEvaluationMode outer;
+};
+
+extern Zoning _zoning;
+
+SpriteID TileZoningSpriteEvaluation(TileIndex tile, Owner owner, ZoningEvaluationMode ev_mode);
+
+int TileZoningEvaluation(TileIndex tile, Owner owner, ZoningEvaluationMode ev_mode);
+
+void DrawTileZoning(const TileInfo *ti);
+
+void ShowZoningToolbar();
+
+#endif /* ZONING_H */
diff --git a/src/zoning_cmd.cpp b/src/zoning_cmd.cpp
new file mode 100644
index 0000000000..5f9240d76c
--- /dev/null
+++ b/src/zoning_cmd.cpp
@@ -0,0 +1,332 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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 .
+ */
+
+/** @file zoning_cmd.cpp */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "station_type.h"
+#include "station_base.h"
+#include "industry.h"
+#include "gfx_func.h"
+#include "viewport_func.h"
+#include "map_func.h"
+#include "company_func.h"
+#include "town_map.h"
+#include "table/sprites.h"
+#include "station_func.h"
+#include "station_map.h"
+#include "town.h"
+#include "zoning.h"
+
+Zoning _zoning;
+static const SpriteID ZONING_INVALID_SPRITE_ID = UINT_MAX;
+
+/**
+ * Draw the zoning sprites.
+ *
+ * @param SpriteID image
+ * the image
+ * @param SpriteID colour
+ * the colour of the zoning
+ * @param TileInfo ti
+ * the tile
+ */
+void DrawZoningSprites(SpriteID image, SpriteID colour, const TileInfo *ti)
+{
+ if (colour != ZONING_INVALID_SPRITE_ID) {
+ AddSortableSpriteToDraw(image + ti->tileh, colour, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7);
+ }
+}
+
+/**
+ * Detect whether this area is within the acceptance of any station.
+ *
+ * @param TileArea area
+ * the area to search by
+ * @param Owner owner
+ * the owner of the stations which we need to match again
+ * @param StationFacility facility_mask
+ * one or more facilities in the mask must be present for a station to be used
+ * @return true if a station is found
+ */
+bool IsAreaWithinAcceptanceZoneOfStation(TileArea area, Owner owner, StationFacility facility_mask)
+{
+ int catchment = _settings_game.station.station_spread + (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
+
+ StationFinder morestations(TileArea(TileXY(TileX(area.tile) - (catchment / 2), TileY(area.tile) - (catchment / 2)),
+ TileX(area.tile) + area.w + catchment, TileY(area.tile) + area.h + catchment));
+
+ for (Station * const *st_iter = morestations.GetStations()->Begin(); st_iter != morestations.GetStations()->End(); ++st_iter) {
+ const Station *st = *st_iter;
+ if (st->owner != owner || !(st->facilities & facility_mask)) continue;
+ Rect rect = st->GetCatchmentRect();
+ return TileArea(TileXY(rect.left, rect.top), TileXY(rect.right, rect.bottom)).Intersects(area);
+ }
+
+ return false;
+}
+
+/**
+ * Detect whether this tile is within the acceptance of any station.
+ *
+ * @param TileIndex tile
+ * the tile to search by
+ * @param Owner owner
+ * the owner of the stations
+ * @param StationFacility facility_mask
+ * one or more facilities in the mask must be present for a station to be used
+ * @return true if a station is found
+ */
+bool IsTileWithinAcceptanceZoneOfStation(TileIndex tile, Owner owner, StationFacility facility_mask)
+{
+ int catchment = _settings_game.station.station_spread + (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
+
+ StationFinder morestations(TileArea(TileXY(TileX(tile) - (catchment / 2), TileY(tile) - (catchment / 2)),
+ catchment, catchment));
+
+ for (Station * const *st_iter = morestations.GetStations()->Begin(); st_iter != morestations.GetStations()->End(); ++st_iter) {
+ const Station *st = *st_iter;
+ if (st->owner != owner || !(st->facilities & facility_mask)) continue;
+ Rect rect = st->GetCatchmentRect();
+ if ((uint)rect.left <= TileX(tile) && TileX(tile) <= (uint)rect.right
+ && (uint)rect.top <= TileY(tile) && TileY(tile) <= (uint)rect.bottom) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Check whether the player can build in tile.
+ *
+ * @param TileIndex tile
+ * @param Owner owner
+ * @return red if they cannot
+ */
+SpriteID TileZoneCheckBuildEvaluation(TileIndex tile, Owner owner)
+{
+ /* Let's first check for the obvious things you cannot build on */
+ switch (GetTileType(tile)) {
+ case MP_INDUSTRY:
+ case MP_OBJECT:
+ case MP_STATION:
+ case MP_HOUSE:
+ case MP_TUNNELBRIDGE:
+ return SPR_ZONING_INNER_HIGHLIGHT_RED;
+
+ /* There are only two things you can own (or some else
+ * can own) that you can still build on. i.e. roads and
+ * railways.
+ * @todo
+ * Add something more intelligent, check what tool the
+ * user is currently using (and if none, assume some
+ * standards), then check it against if owned by some-
+ * one else (e.g. railway on someone else's road).
+ * While that being said, it should also check if it
+ * is not possible to build railway/road on someone
+ * else's/your own road/railway (e.g. the railway track
+ * is curved or a cross).
+ */
+ case MP_ROAD:
+ case MP_RAILWAY:
+ if (GetTileOwner(tile) != owner) {
+ return SPR_ZONING_INNER_HIGHLIGHT_RED;
+ } else {
+ return ZONING_INVALID_SPRITE_ID;
+ }
+
+ default:
+ return ZONING_INVALID_SPRITE_ID;
+ }
+}
+
+/**
+ * Check the opinion of the local authority in the tile.
+ *
+ * @param TileIndex tile
+ * @param Owner owner
+ * @return black if no opinion, orange if bad,
+ * light blue if good or invalid if no town
+ */
+SpriteID TileZoneCheckOpinionEvaluation(TileIndex tile, Owner owner)
+{
+ int opinion = 0; // 0: no town, 1: no opinion, 2: bad, 3: good
+ Town *town = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority);
+
+ if (town != NULL) {
+ if (HasBit(town->have_ratings, owner)) {
+ opinion = (town->ratings[owner] > 0) ? 3 : 2;
+ } else {
+ opinion = 1;
+ }
+ }
+
+ switch (opinion) {
+ case 1: return SPR_ZONING_INNER_HIGHLIGHT_BLACK; // no opinion
+ case 2: return SPR_ZONING_INNER_HIGHLIGHT_ORANGE; // bad
+ case 3: return SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE; // good
+ default: return ZONING_INVALID_SPRITE_ID; // no town
+ }
+}
+
+/**
+ * Detect whether the tile is within the catchment zone of a station.
+ *
+ * @param TileIndex tile
+ * @param Owner owner
+ * @return black if within, light blue if only in acceptance zone
+ * and nothing if no nearby station.
+ */
+SpriteID TileZoneCheckStationCatchmentEvaluation(TileIndex tile, Owner owner)
+{
+ // Never on a station.
+ if (IsTileType(tile, MP_STATION)) {
+ return ZONING_INVALID_SPRITE_ID;
+ }
+
+ // For provided goods
+ StationFinder stations(TileArea(tile, 1, 1));
+
+ for (Station * const *st_iter = stations.GetStations()->Begin(); st_iter != stations.GetStations()->End(); ++st_iter) {
+ const Station *st = *st_iter;
+ if (st->owner == owner) {
+ return SPR_ZONING_INNER_HIGHLIGHT_BLACK;
+ }
+ }
+
+ // For accepted goods
+ if (IsTileWithinAcceptanceZoneOfStation(tile, owner, ~FACIL_NONE)) {
+ return SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE;
+ }
+
+ return ZONING_INVALID_SPRITE_ID;
+}
+
+/**
+ * Detect whether a building is unserved by a station of owner.
+ *
+ * @param TileIndex tile
+ * @param Owner owner
+ * @return red if unserved, orange if only accepting, nothing if served or not
+ * a building
+ */
+SpriteID TileZoneCheckUnservedBuildingsEvaluation(TileIndex tile, Owner owner)
+{
+ if (!IsTileType(tile, MP_HOUSE)) {
+ return ZONING_INVALID_SPRITE_ID;
+ }
+
+ CargoArray dat;
+
+ memset(&dat, 0, sizeof(dat));
+ AddAcceptedCargo(tile, dat, NULL);
+ if (dat[CT_MAIL] + dat[CT_PASSENGERS] == 0) {
+ // nothing is accepted, so now test if cargo is produced
+ AddProducedCargo(tile, dat);
+ if (dat[CT_MAIL] + dat[CT_PASSENGERS] == 0) {
+ // total is still 0, so give up
+ return ZONING_INVALID_SPRITE_ID;
+ }
+ }
+
+ StationFinder stations(TileArea(tile, 1, 1));
+
+ for (Station * const *st_iter = stations.GetStations()->Begin(); st_iter != stations.GetStations()->End(); ++st_iter) {
+ const Station *st = *st_iter;
+ if (st->owner == owner) {
+ return ZONING_INVALID_SPRITE_ID;
+ }
+ }
+
+ // For accepted goods
+ if (IsTileWithinAcceptanceZoneOfStation(tile, owner, ~FACIL_NONE)) {
+ return SPR_ZONING_INNER_HIGHLIGHT_ORANGE;
+ }
+
+ return SPR_ZONING_INNER_HIGHLIGHT_RED;
+}
+
+/**
+ * Detect whether an industry is unserved by a station of owner.
+ *
+ * @param TileIndex tile
+ * @param Owner owner
+ * @return red if unserved, orange if only accepting, nothing if served or not
+ * a building
+ */
+SpriteID TileZoneCheckUnservedIndustriesEvaluation(TileIndex tile, Owner owner)
+{
+ if (IsTileType(tile, MP_INDUSTRY)) {
+ Industry *ind = Industry::GetByTile(tile);
+ StationFinder stations(ind->location);
+
+ for (Station * const *st_iter = stations.GetStations()->Begin(); st_iter != stations.GetStations()->End(); ++st_iter) {
+ const Station *st = *st_iter;
+ if (st->owner == owner && st->facilities & (~FACIL_BUS_STOP)) {
+ return ZONING_INVALID_SPRITE_ID;
+ }
+ }
+
+ // For accepted goods
+ if (IsAreaWithinAcceptanceZoneOfStation(ind->location, owner, ~FACIL_BUS_STOP)) {
+ return SPR_ZONING_INNER_HIGHLIGHT_ORANGE;
+ }
+
+ return SPR_ZONING_INNER_HIGHLIGHT_RED;
+ }
+
+ return ZONING_INVALID_SPRITE_ID;
+}
+
+/**
+ * General evaluation function; calls all the other functions depending on
+ * evaluation mode.
+ *
+ * @param TileIndex tile
+ * Tile to be evaluated.
+ * @param Owner owner
+ * The current player
+ * @param ZoningEvaluationMode ev_mode
+ * The current evaluation mode.
+ * @return The colour returned by the evaluation functions (none if no ev_mode).
+ */
+SpriteID TileZoningSpriteEvaluation(TileIndex tile, Owner owner, ZoningEvaluationMode ev_mode)
+{
+ switch (ev_mode) {
+ case ZEM_CAN_BUILD: return TileZoneCheckBuildEvaluation(tile, owner);
+ case ZEM_AUTHORITY: return TileZoneCheckOpinionEvaluation(tile, owner);
+ case ZEM_STA_CATCH: return TileZoneCheckStationCatchmentEvaluation(tile, owner);
+ case ZEM_BUL_UNSER: return TileZoneCheckUnservedBuildingsEvaluation(tile, owner);
+ case ZEM_IND_UNSER: return TileZoneCheckUnservedIndustriesEvaluation(tile, owner);
+ default: return ZONING_INVALID_SPRITE_ID;
+ }
+}
+
+/**
+ * Draw the the zoning on the tile.
+ *
+ * @param TileInfo ti
+ * the tile to draw on.
+ */
+void DrawTileZoning(const TileInfo *ti)
+{
+ if (IsTileType(ti->tile, MP_VOID) || _game_mode != GM_NORMAL) {
+ return;
+ }
+
+ if (_zoning.outer != ZEM_NOTHING) {
+ DrawZoningSprites(SPR_SELECT_TILE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.outer), ti);
+ }
+
+ if (_zoning.inner != ZEM_NOTHING) {
+ DrawZoningSprites(SPR_ZONING_INNER_HIGHLIGHT_BASE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.inner), ti);
+ }
+}
diff --git a/src/zoning_gui.cpp b/src/zoning_gui.cpp
new file mode 100644
index 0000000000..74be21b8f6
--- /dev/null
+++ b/src/zoning_gui.cpp
@@ -0,0 +1,181 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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 .
+ */
+
+/** @file zoning_gui.cpp */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "widgets/dropdown_func.h"
+#include "widget_type.h"
+#include "window_func.h"
+#include "gui.h"
+#include "viewport_func.h"
+#include "sound_func.h"
+#include "table/sprites.h"
+#include "table/strings.h"
+#include "strings_func.h"
+#include "gfx_func.h"
+#include "core/geometry_func.hpp"
+#include "core/random_func.hpp"
+#include "zoning.h"
+
+enum ZoningToolbarWidgets {
+ ZTW_OUTER = 4,
+ ZTW_OUTER_DROPDOWN,
+ ZTW_INNER,
+ ZTW_INNER_DROPDOWN,
+ ZTW_CAPTION
+};
+
+static const StringID _zone_type_strings[] = {
+ STR_ZONING_NO_ZONING,
+ STR_ZONING_AUTHORITY,
+ STR_ZONING_CAN_BUILD,
+ STR_ZONING_STA_CATCH,
+ STR_ZONING_BUL_UNSER,
+ STR_ZONING_IND_UNSER,
+ INVALID_STRING_ID
+};
+
+static const ZoningEvaluationMode _zone_type_modes[] = {
+ ZEM_NOTHING,
+ ZEM_AUTHORITY,
+ ZEM_CAN_BUILD,
+ ZEM_STA_CATCH,
+ ZEM_BUL_UNSER,
+ ZEM_IND_UNSER,
+};
+
+static ZoningEvaluationMode DropDownIndexToZoningEvaluationMode(int index)
+{
+ if (index < 0 || index >= (int) lengthof(_zone_type_modes)) {
+ return ZEM_NOTHING;
+ }
+ return _zone_type_modes[index];
+}
+
+static int ZoningEvaluationModeToDropDownIndex(ZoningEvaluationMode ev_mode)
+{
+ for (int i = 0; i < (int) lengthof(_zone_type_modes); i++) {
+ if (_zone_type_modes[i] == ev_mode) return i;
+ }
+ NOT_REACHED();
+}
+
+struct ZoningWindow : public Window {
+
+ ZoningWindow(WindowDesc *desc, int window_number)
+ : Window(desc)
+ {
+ this->InitNested(window_number);
+ this->InvalidateData();
+ }
+
+ virtual void OnPaint()
+ {
+ this->DrawWidgets();
+ }
+
+ virtual void OnClick(Point pt, int widget, int click_count)
+ {
+ switch (widget) {
+ case ZTW_OUTER_DROPDOWN:
+ ShowDropDownMenu(this, _zone_type_strings, ZoningEvaluationModeToDropDownIndex(_zoning.outer), ZTW_OUTER_DROPDOWN, 0, 0);
+ break;
+
+ case ZTW_INNER_DROPDOWN:
+ ShowDropDownMenu(this, _zone_type_strings, ZoningEvaluationModeToDropDownIndex(_zoning.inner), ZTW_INNER_DROPDOWN, 0, 0);
+ break;
+ }
+ }
+
+ virtual void OnDropdownSelect(int widget, int index)
+ {
+ switch(widget) {
+ case ZTW_OUTER_DROPDOWN:
+ _zoning.outer = DropDownIndexToZoningEvaluationMode(index);
+ break;
+
+ case ZTW_INNER_DROPDOWN:
+ _zoning.inner = DropDownIndexToZoningEvaluationMode(index);
+ break;
+ }
+ this->InvalidateData();
+ MarkWholeScreenDirty();
+ }
+
+ virtual void SetStringParameters(int widget) const
+ {
+ switch (widget) {
+ case ZTW_OUTER_DROPDOWN:
+ SetDParam(0, _zone_type_strings[ZoningEvaluationModeToDropDownIndex(_zoning.outer)]);
+ break;
+
+ case ZTW_INNER_DROPDOWN:
+ SetDParam(0, _zone_type_strings[ZoningEvaluationModeToDropDownIndex(_zoning.inner)]);
+ break;
+ }
+ }
+
+ virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
+ {
+ const StringID *strs = NULL;
+ switch (widget) {
+ case ZTW_OUTER_DROPDOWN:
+ case ZTW_INNER_DROPDOWN:
+ strs = _zone_type_strings;
+ break;
+ }
+ if (strs != NULL) {
+ while (*strs != INVALID_STRING_ID) {
+ *size = maxdim(*size, GetStringBoundingBox(*strs++));
+ }
+ }
+ size->width += padding.width;
+ size->height = FONT_HEIGHT_NORMAL + WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM;
+ }
+};
+
+static const NWidgetPart _nested_zoning_widgets[] = {
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_CLOSEBOX, COLOUR_GREY),
+ NWidget(WWT_CAPTION, COLOUR_GREY, ZTW_CAPTION), SetDataTip(STR_ZONING_TOOLBAR, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_SHADEBOX, COLOUR_GREY),
+ NWidget(WWT_STICKYBOX, COLOUR_GREY),
+ EndContainer(),
+
+ NWidget(WWT_PANEL, COLOUR_GREY),
+ NWidget(NWID_HORIZONTAL, COLOUR_GREY), SetPIP(10, 3, 10),
+ NWidget(NWID_VERTICAL, COLOUR_GREY), SetPadding(5, 0, 5, 0),
+ NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_ZONING_OUTER, STR_NULL), SetResize(1, 0), SetPadding(1, 6, 1, 6),
+ NWidget(WWT_TEXT, COLOUR_GREY, ZTW_OUTER),
+ NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_ZONING_INNER, STR_NULL), SetResize(1, 0), SetPadding(1, 6, 1, 6),
+ NWidget(WWT_TEXT, COLOUR_GREY, ZTW_INNER),
+ EndContainer(),
+ NWidget(NWID_VERTICAL, COLOUR_GREY), SetPadding(5, 0, 5, 0),
+ NWidget(WWT_DROPDOWN, COLOUR_GREY, ZTW_OUTER_DROPDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
+ NWidget(WWT_TEXT, COLOUR_GREY),
+ NWidget(WWT_DROPDOWN, COLOUR_GREY, ZTW_INNER_DROPDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
+ NWidget(WWT_TEXT, COLOUR_GREY),
+ EndContainer(),
+ EndContainer(),
+ EndContainer()
+};
+
+static WindowDesc _zoning_desc (
+ WDP_CENTER, "zoning_gui", 0, 0,
+ WC_ZONING_TOOLBAR, WC_NONE,
+ 0,
+ _nested_zoning_widgets, lengthof(_nested_zoning_widgets)
+);
+
+void ShowZoningToolbar()
+{
+ AllocateWindowDescFront(&_zoning_desc, 0);
+}