← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/notifications_shipwindow into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/notifications_shipwindow into lp:widelands.

Commit message:
Ships no longer know about their windows.
- Ships send a NoteShipWindow when window contents need to be changed / the window destroyed.
- Ship windows are now controlled by InteractiveGameBase.
- Made a bunch of functions in Ship const.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/notifications_shipwindow/+merge/307048

Another one for Build 20.

I want to remove the dependency from logic to wui, and I think the Notification system is a good way of doing that. I'd like some feedback on the design before I do more of these.

I also made a bunch of functions in Ship const while I was at it.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/notifications_shipwindow into lp:widelands.
=== modified file 'ChangeLog'
--- ChangeLog	2016-09-24 09:15:11 +0000
+++ ChangeLog	2016-09-28 15:09:32 +0000
@@ -1,4 +1,4 @@
-### Build 19 until r8110
+### Build 19 until r8119
 
 
 * Animations, Icons and Overlays
@@ -57,7 +57,7 @@
   - New maps: Archipelago Sea, Dolomites, Wide World
   - Removed maps: Dry Riverbed, Long long way, War of the Valleys
   - Overhauled maps: Comet Islands, Full Moon, Glacier Lake, Kings and Queens,
-    Last Bastion, The Nile, Twin Lagoons, Volcanic Winter
+    Last Bastion, River Explorers, The Nile, Twin Lagoons, Volcanic Winter
   - Added "hint" to the json file created by wl_map_info for use on the website.
 
 
@@ -125,7 +125,6 @@
   - Other tribes’ buildings that have been conquered can now be dismantled
   - Balancing: Added gold to building cost for Atlantean training sites.
     Made Barbarian attack stronger.
-  - Trainers now need the same tools as a soldier.
   - When an expedition ship places a port, a bit of land is cleared of trees to
     make room for a few buildings.
   - Ports can be affected by terrain changes up to 3 tiles away from their original

=== modified file 'src/economy/portdock.cc'
--- src/economy/portdock.cc	2016-08-04 15:49:05 +0000
+++ src/economy/portdock.cc	2016-09-28 15:09:32 +0000
@@ -337,8 +337,7 @@
 			// The expedition goods are now on the ship, so from now on it is independent from the port
 			// and thus we switch the port to normal, so we could even start a new expedition,
 			cancel_expedition(game);
-			if (upcast(InteractiveGameBase, igb, game.get_ibase()))
-				ship.refresh_window(*igb);
+			Notifications::publish(NoteShipWindow(ship, NoteShipWindow::Action::kRefresh));
 			return fleet_->update(game);
 		}
 	}

=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc	2016-09-07 09:30:49 +0000
+++ src/logic/map_objects/tribes/ship.cc	2016-09-28 15:09:32 +0000
@@ -126,14 +126,13 @@
 
 Ship::Ship(const ShipDescr& gdescr)
    : Bob(gdescr),
-     window_(nullptr),
      fleet_(nullptr),
      economy_(nullptr),
      ship_state_(ShipStates::kTransport) {
 }
 
 Ship::~Ship() {
-	close_window();
+	Notifications::publish(NoteShipWindow(*this, NoteShipWindow::Action::kRemove));
 }
 
 PortDock* Ship::get_destination(EditorGameBase& egbase) const {
@@ -680,8 +679,7 @@
 
 			expedition_.reset(nullptr);
 
-			if (upcast(InteractiveGameBase, igb, game.get_ibase()))
-				refresh_window(*igb);
+			Notifications::publish(NoteShipWindow(*this, NoteShipWindow::Action::kRefresh));
 			return start_task_idle(game, descr().main_animation(), 1500);
 		}
 	}
@@ -743,7 +741,7 @@
 /**
  * Find a path to the dock @p pd, returns its length, and the path optionally.
  */
-uint32_t Ship::calculate_sea_route(Game& game, PortDock& pd, Path* finalpath) {
+uint32_t Ship::calculate_sea_route(Game& game, PortDock& pd, Path* finalpath) const {
 	Map& map = game.map();
 	StepEvalAStar se(pd.get_warehouse()->get_position());
 	se.swim_ = true;
@@ -847,7 +845,7 @@
 	expedition_->island_exploration = false;
 }
 
-WalkingDir Ship::get_scouting_direction() {
+WalkingDir Ship::get_scouting_direction() const {
 	if (expedition_ && ship_state_ == ShipStates::kExpeditionScouting &&
 	    !expedition_->island_exploration) {
 		return expedition_->scouting_direction;
@@ -882,7 +880,7 @@
 	expedition_->island_exploration = true;
 }
 
-IslandExploreDirection Ship::get_island_explore_direction() {
+IslandExploreDirection Ship::get_island_explore_direction() const {
 	if (expedition_ && ship_state_ == ShipStates::kExpeditionScouting &&
 	    expedition_->island_exploration) {
 		return expedition_->island_explore_direction;
@@ -926,8 +924,7 @@
 	expedition_.reset(nullptr);
 
 	// And finally update our ship window
-	if (upcast(InteractiveGameBase, igb, game.get_ibase()))
-		refresh_window(*igb);
+	Notifications::publish(NoteShipWindow(*this, NoteShipWindow::Action::kRefresh));
 }
 
 /// Sinks the ship
@@ -936,10 +933,10 @@
 	// Running colonization has the highest priority + a sink request is only valid once
 	if (!state_is_sinkable())
 		return;
+	Notifications::publish(NoteShipWindow(*this, NoteShipWindow::Action::kRemove));
 	ship_state_ = ShipStates::kSinkRequest;
 	// Make sure the ship is active and close possible open windows
 	ship_wakeup(game);
-	close_window();
 }
 
 void Ship::draw(const EditorGameBase& game, RenderTarget& dst, const Point& pos) const {

=== modified file 'src/logic/map_objects/tribes/ship.h'
--- src/logic/map_objects/tribes/ship.h	2016-09-07 09:30:49 +0000
+++ src/logic/map_objects/tribes/ship.h	2016-09-28 15:09:32 +0000
@@ -28,11 +28,6 @@
 #include "graphic/diranimations.h"
 #include "logic/map_objects/bob.h"
 
-namespace UI {
-class Window;
-}
-class InteractiveGameBase;
-
 namespace Widelands {
 
 class Economy;
@@ -59,6 +54,19 @@
 	}
 };
 
+struct NoteShipWindow {
+	CAN_BE_SENT_AS_NOTE(NoteId::ShipWindow)
+
+	Ship& ship;
+
+	enum class Action { kRefresh, kRemove, kClosed };
+	const Action action;
+
+	NoteShipWindow(Ship& init_ship, const Action& init_action)
+		: ship(init_ship), action(init_action) {
+	}
+};
+
 class ShipDescr : public BobDescr {
 public:
 	ShipDescr(const std::string& init_descname, const LuaTable& t);
@@ -119,7 +127,7 @@
 	void start_task_movetodock(Game&, PortDock&);
 	void start_task_expedition(Game&);
 
-	uint32_t calculate_sea_route(Game& game, PortDock& pd, Path* finalpath = nullptr);
+	uint32_t calculate_sea_route(Game& game, PortDock& pd, Path* finalpath = nullptr) const;
 
 	void log_general_info(const EditorGameBase&) override;
 
@@ -133,10 +141,6 @@
 	void withdraw_items(Game& game, PortDock& pd, std::vector<ShippingItem>& items);
 	void add_item(Game&, const ShippingItem& item);
 
-	void show_window(InteractiveGameBase&, bool avoid_fastclick = false);
-	void close_window();
-	void refresh_window(InteractiveGameBase&);
-
 	// A ship with task expedition can be in four states: kExpeditionWaiting, kExpeditionScouting,
 	// kExpeditionPortspaceFound or kExpeditionColonizing in the first states, the owning player of
 	// this ship
@@ -169,42 +173,42 @@
 	};
 
 	/// \returns the current state the ship is in
-	ShipStates get_ship_state() {
+	ShipStates get_ship_state() const {
 		return ship_state_;
 	}
 
 	/// \returns the current name of ship
-	const std::string& get_shipname() {
+	const std::string& get_shipname() const {
 		return shipname_;
 	}
 
 	/// \returns whether the ship is currently on an expedition
-	bool state_is_expedition() {
+	bool state_is_expedition() const {
 		return (ship_state_ == ShipStates::kExpeditionScouting ||
 		        ship_state_ == ShipStates::kExpeditionWaiting ||
 		        ship_state_ == ShipStates::kExpeditionPortspaceFound ||
 		        ship_state_ == ShipStates::kExpeditionColonizing);
 	}
 	/// \returns whether the ship is in transport mode
-	bool state_is_transport() {
+	bool state_is_transport() const {
 		return (ship_state_ == ShipStates::kTransport);
 	}
 	/// \returns whether a sink request for the ship is currently valid
-	bool state_is_sinkable() {
+	bool state_is_sinkable() const {
 		return (ship_state_ != ShipStates::kSinkRequest &&
 		        ship_state_ != ShipStates::kSinkAnimation &&
 		        ship_state_ != ShipStates::kExpeditionColonizing);
 	}
 
 	/// \returns (in expedition mode only!) whether the next field in direction \arg dir is swimmable
-	bool exp_dir_swimmable(Direction dir) {
+	bool exp_dir_swimmable(Direction dir) const {
 		if (!expedition_)
 			return false;
 		return expedition_->swimmable[dir - 1];
 	}
 
 	/// \returns whether the expedition ship is close to the coast
-	bool exp_close_to_coast() {
+	bool exp_close_to_coast() const {
 		if (!expedition_)
 			return false;
 		for (uint8_t dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir)
@@ -214,7 +218,7 @@
 	}
 
 	/// \returns (in expedition mode only!) the list of currently seen port build spaces
-	const std::vector<Coords>& exp_port_spaces() {
+	const std::vector<Coords>& exp_port_spaces() const {
 		return expedition_->seen_port_buildspaces;
 	}
 
@@ -224,12 +228,12 @@
 
 	// Returns integer of direction, or WalkingDir::IDLE if query invalid
 	// Intended for LUA scripting
-	WalkingDir get_scouting_direction();
+	WalkingDir get_scouting_direction() const;
 
 	// Returns integer of direction, or IslandExploreDirection::kNotSet
 	// if query invalid
 	// Intended for LUA scripting
-	IslandExploreDirection get_island_explore_direction();
+	IslandExploreDirection get_island_explore_direction() const;
 
 	void exp_cancel(Game&);
 	void sink_ship(Game&);
@@ -239,7 +243,6 @@
 
 private:
 	friend struct Fleet;
-	friend struct ShipWindow;
 
 	void wakeup_neighbours(Game&);
 
@@ -261,8 +264,6 @@
 	                  const std::string& description,
 	                  const std::string& picture);
 
-	UI::Window* window_;
-
 	Fleet* fleet_;
 	Economy* economy_;
 	OPtr<PortDock> lastdock_;

=== modified file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h	2016-03-03 21:46:11 +0000
+++ src/notifications/note_ids.h	2016-09-28 15:09:32 +0000
@@ -35,6 +35,7 @@
 	ProductionSiteOutOfResources,
 	TrainingSiteSoldierTrained,
 	ShipMessage,
+	ShipWindow,
 	GraphicResolutionChanged,
 	NoteExpeditionCanceled
 };

=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt	2016-04-15 17:23:00 +0000
+++ src/wui/CMakeLists.txt	2016-09-28 15:09:32 +0000
@@ -153,6 +153,7 @@
     quicknavigation.cc
     quicknavigation.h
     shipwindow.cc
+    shipwindow.h
     soldiercapacitycontrol.cc
     soldiercapacitycontrol.h
     soldierlist.cc

=== modified file 'src/wui/interactive_gamebase.cc'
--- src/wui/interactive_gamebase.cc	2016-08-04 15:49:05 +0000
+++ src/wui/interactive_gamebase.cc	2016-09-28 15:09:32 +0000
@@ -34,6 +34,7 @@
 #include "logic/player.h"
 #include "profile/profile.h"
 #include "wui/game_summary.h"
+#include "wui/shipwindow.h"
 
 namespace {
 
@@ -63,6 +64,53 @@
      toggle_buildhelp_(INIT_BTN(
         "wui/menus/menu_toggle_buildhelp", "buildhelp", _("Show Building Spaces (on/off)"))) {
 	toggle_buildhelp_.sigclicked.connect(boost::bind(&InteractiveGameBase::toggle_buildhelp, this));
+
+	shipnotes_subscriber_ = Notifications::subscribe<Widelands::NoteShipWindow>(
+	   [this](const Widelands::NoteShipWindow& note) {
+		   const Widelands::Serial serial = note.ship.serial();
+		   switch (note.action) {
+		   // The ship state has changed or the user requested a new window
+		   case Widelands::NoteShipWindow::Action::kRefresh: {
+			   bool is_refreshing = false;
+			   Point pos(0, 0);
+			   ShipWindow* shipwindow;
+			   if (shipwindows_.count(serial) == 1) {
+				   shipwindow = shipwindows_.at(serial);
+				   if (shipwindow) {
+					   is_refreshing = true;
+					   pos = shipwindow->get_pos();
+					   delete shipwindow;
+					   shipwindow = nullptr;
+				   }
+			   }
+			   shipwindow = new ShipWindow(*this, note.ship, is_refreshing);
+			   shipwindows_.insert(std::pair<Widelands::Serial, ShipWindow*>(serial, shipwindow));
+			   if (is_refreshing) {
+				   shipwindow->set_pos(pos);
+			   }
+
+		   } break;
+		   // The ship has been removed
+		   case Widelands::NoteShipWindow::Action::kRemove: {
+			   if (shipwindows_.count(serial) == 1) {
+				   ShipWindow* shipwindow = shipwindows_.at(serial);
+				   if (shipwindow) {
+					   delete shipwindow;
+					   shipwindow = nullptr;
+				   }
+				   shipwindows_.erase(serial);
+			   }
+
+		   } break;
+		   // The user closed the window
+		   case Widelands::NoteShipWindow::Action::kClosed: {
+			   if (shipwindows_.count(serial) == 1) {
+				   shipwindows_.erase(serial);
+			   }
+
+		   } break;
+		   }
+		});
 }
 
 /// \return a pointer to the running \ref Game instance.
@@ -158,7 +206,8 @@
 	for (Widelands::Bob* temp_ship : ships) {
 		if (upcast(Widelands::Ship, ship, temp_ship)) {
 			if (can_see(ship->get_owner()->player_number())) {
-				ship->show_window(*this);
+				Notifications::publish(
+				   Widelands::NoteShipWindow(*ship, Widelands::NoteShipWindow::Action::kRefresh));
 				return true;
 			}
 		}

=== modified file 'src/wui/interactive_gamebase.h'
--- src/wui/interactive_gamebase.h	2016-08-04 15:49:05 +0000
+++ src/wui/interactive_gamebase.h	2016-09-28 15:09:32 +0000
@@ -20,19 +20,28 @@
 #ifndef WL_WUI_INTERACTIVE_GAMEBASE_H
 #define WL_WUI_INTERACTIVE_GAMEBASE_H
 
+#include <map>
+#include <memory>
+
 #include "logic/game.h"
+#include "logic/widelands.h"
+#include "notifications/notifications.h"
 #include "profile/profile.h"
 #include "wui/general_statistics_menu.h"
 #include "wui/interactive_base.h"
 
 struct ChatProvider;
+class ShipWindow;
+
+namespace Widelands {
+struct NoteShipWindow;
+}
 
 enum PlayerType { NONE, OBSERVER, PLAYING, VICTORIOUS, DEFEATED };
 
 class InteractiveGameBase : public InteractiveBase {
 public:
-	class GameMainMenuWindows {
-	public:
+	struct GameMainMenuWindows {
 		UI::UniqueWindow::Registry loadgame;
 		UI::UniqueWindow::Registry savegame;
 		UI::UniqueWindow::Registry readme;
@@ -97,6 +106,11 @@
 
 private:
 	void on_buildhelp_changed(const bool value) override;
+
+	// Registers all ship windows to avoid duplication
+	std::map<Widelands::Serial, ShipWindow*> shipwindows_;
+	// Handles opening, refreshing and closing of ship windows
+	std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipWindow>> shipnotes_subscriber_;
 };
 
 #endif  // end of include guard: WL_WUI_INTERACTIVE_GAMEBASE_H

=== modified file 'src/wui/shipwindow.cc'
--- src/wui/shipwindow.cc	2016-09-07 09:30:49 +0000
+++ src/wui/shipwindow.cc	2016-09-28 15:09:32 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2013 by the Widelands Development Team
+ * Copyright (C) 2011 - 2016 by the Widelands Development Team
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
  *
  */
 
-#include "logic/map_objects/tribes/ship.h"
+#include "wui/shipwindow.h"
 
 #include "base/macros.h"
 #include "economy/portdock.h"
@@ -29,10 +29,9 @@
 #include "ui_basic/box.h"
 #include "wui/actionconfirm.h"
 #include "wui/game_debug_ui.h"
-#include "wui/interactive_gamebase.h"
 #include "wui/interactive_player.h"
-#include "wui/itemwaresdisplay.h"
 
+namespace {
 static const char pic_goto[] = "images/wui/ship/menu_ship_goto.png";
 static const char pic_destination[] = "images/wui/ship/menu_ship_destination.png";
 static const char pic_sink[] = "images/wui/ship/menu_ship_sink.png";
@@ -47,55 +46,13 @@
 static const char pic_scout_sw[] = "images/wui/ship/ship_scout_sw.png";
 static const char pic_scout_se[] = "images/wui/ship/ship_scout_se.png";
 static const char pic_construct_port[] = "images/wui/editor/fsel_editor_set_port_space.png";
-
-namespace Widelands {
-
-/**
- * Display information about a ship.
- */
-struct ShipWindow : UI::Window {
-	ShipWindow(InteractiveGameBase& igb, Ship& ship, const std::string& title);
-	virtual ~ShipWindow();
-
-	void think() override;
-
-	UI::Button* make_button(UI::Panel* parent,
-	                        const std::string& name,
-	                        const std::string& title,
-	                        const std::string& picname,
-	                        boost::function<void()> callback);
-
-	void act_goto();
-	void act_destination();
-	void act_sink();
-	void act_debug();
-	void act_cancel_expedition();
-	void act_scout_towards(WalkingDir);
-	void act_construct_port();
-	void act_explore_island(IslandExploreDirection);
-
-private:
-	InteractiveGameBase& igbase_;
-	Ship& ship_;
-
-	UI::Button* btn_goto_;
-	UI::Button* btn_destination_;
-	UI::Button* btn_sink_;
-	UI::Button* btn_debug_;
-	UI::Button* btn_cancel_expedition_;
-	UI::Button* btn_explore_island_cw_;
-	UI::Button* btn_explore_island_ccw_;
-	UI::Button*
-	   btn_scout_[LAST_DIRECTION];  // format: DIRECTION - 1, as 0 is normally the current location.
-	UI::Button* btn_construct_port_;
-	ItemWaresDisplay* display_;
-};
-
-ShipWindow::ShipWindow(InteractiveGameBase& igb, Ship& ship, const std::string& title)
-   : Window(&igb, "shipwindow", 0, 0, 0, 0, title), igbase_(igb), ship_(ship) {
-	assert(!ship_.window_);
+}  // namespace
+
+using namespace Widelands;
+
+ShipWindow::ShipWindow(InteractiveGameBase& igb, Ship& ship, bool avoid_fastclick)
+   : Window(&igb, "shipwindow", 0, 0, 0, 0, ship.get_shipname()), igbase_(igb), ship_(ship) {
 	assert(ship_.get_owner());
-	ship_.window_ = this;
 
 	UI::Box* vbox = new UI::Box(this, 0, 0, UI::Box::Vertical);
 
@@ -190,15 +147,15 @@
 	}
 	set_center_panel(vbox);
 	set_thinks(true);
-
-	center_to_parent();
-	move_out_of_the_way();
 	set_fastclick_panel(btn_goto_);
+	if (!avoid_fastclick) {
+		move_out_of_the_way();
+		warp_mouse_to_fastclick_panel();
+	}
 }
 
 ShipWindow::~ShipWindow() {
-	assert(ship_.window_ == this);
-	ship_.window_ = nullptr;
+	Notifications::publish(NoteShipWindow(ship_, NoteShipWindow::Action::kClosed));
 }
 
 void ShipWindow::think() {
@@ -335,52 +292,3 @@
 		return;
 	igbase_.game().send_player_ship_explore_island(ship_, direction);
 }
-
-/**
- * Show the window for this ship as long as it is not sinking:
- * either bring it to the front, or create it.
- */
-void Ship::show_window(InteractiveGameBase& igb, bool avoid_fastclick) {
-	// No window, if ship is sinking
-	if (ship_state_ == ShipStates::kSinkRequest || ship_state_ == ShipStates::kSinkAnimation)
-		return;
-
-	if (window_) {
-		if (window_->is_minimal())
-			window_->restore();
-		window_->move_to_top();
-	} else {
-		const std::string& title = get_shipname();
-		new ShipWindow(igb, *this, title);
-		if (!avoid_fastclick)
-			window_->warp_mouse_to_fastclick_panel();
-	}
-}
-
-/**
- * Close the window for this ship.
- */
-void Ship::close_window() {
-	if (window_) {
-		delete window_;
-		window_ = nullptr;
-	}
-}
-
-/**
- * refreshes the window of this ship - useful if some ui elements have to be removed or added
- */
-void Ship::refresh_window(InteractiveGameBase& igb) {
-	// Only do something if there is actually a window
-	if (window_) {
-		Point window_position = window_->get_pos();
-		close_window();
-		show_window(igb, true);
-		// show window could theoretically fail if refresh_window was called at the very same moment
-		// as the ship begins to sink
-		if (window_)
-			window_->set_pos(window_position);
-	}
-}
-
-}  // namespace Widelands

=== added file 'src/wui/shipwindow.h'
--- src/wui/shipwindow.h	1970-01-01 00:00:00 +0000
+++ src/wui/shipwindow.h	2016-09-28 15:09:32 +0000
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 - 2016 by the Widelands Development Team
+ *
+ * This program 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef WL_WUI_SHIPWINDOW_H
+#define WL_WUI_SHIPWINDOW_H
+
+#include "logic/game.h"
+#include "logic/map_objects/tribes/ship.h"
+#include "logic/map_objects/walkingdir.h"
+#include "notifications/notifications.h"
+#include "ui_basic/button.h"
+#include "ui_basic/window.h"
+#include "wui/interactive_gamebase.h"
+#include "wui/itemwaresdisplay.h"
+
+/**
+ * Display information about a ship.
+ */
+class ShipWindow : protected UI::Window {
+protected:
+	ShipWindow(InteractiveGameBase& igb, Widelands::Ship& ship, bool avoid_fastclick = false);
+	virtual ~ShipWindow();
+
+	void think() override;
+
+	UI::Button* make_button(UI::Panel* parent,
+	                        const std::string& name,
+	                        const std::string& title,
+	                        const std::string& picname,
+	                        boost::function<void()> callback);
+
+	void act_goto();
+	void act_destination();
+	void act_sink();
+	void act_debug();
+	void act_cancel_expedition();
+	void act_scout_towards(Widelands::WalkingDir);
+	void act_construct_port();
+	void act_explore_island(Widelands::IslandExploreDirection);
+
+private:
+	friend class InteractiveGameBase;
+	InteractiveGameBase& igbase_;
+	Widelands::Ship& ship_;
+
+	UI::Button* btn_goto_;
+	UI::Button* btn_destination_;
+	UI::Button* btn_sink_;
+	UI::Button* btn_debug_;
+	UI::Button* btn_cancel_expedition_;
+	UI::Button* btn_explore_island_cw_;
+	UI::Button* btn_explore_island_ccw_;
+	// format: DIRECTION - 1, as 0 is normally the current location.
+	UI::Button* btn_scout_[Widelands::LAST_DIRECTION];
+	UI::Button* btn_construct_port_;
+	ItemWaresDisplay* display_;
+};
+
+#endif  // end of include guard: WL_WUI_SHIPWINDOW_H


Follow ups