← Back to team overview

widelands-dev team mailing list archive

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

 

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

Commit message:
Building windows are now handled by a new notification 'NoteBuilding', so that Map Objects no longer depend directly on wui. This also brings some improvements regarding the closing/minimizing of windows:
- Productionsites no longer close their window when the stop/start button is pressed.
- Minimized construction windows no longer get maximized when the construction is finished.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1290045 in widelands: "minimized construction window gets maximized when construction ends"
  https://bugs.launchpad.net/widelands/+bug/1290045
  Bug #1662080 in widelands: "Let buildings window stay open when stop/start is clicked"
  https://bugs.launchpad.net/widelands/+bug/1662080

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/notifications_buildingwindows/+merge/317264
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/notifications_buildingwindows into lp:widelands.
=== modified file 'src/economy/CMakeLists.txt'
--- src/economy/CMakeLists.txt	2017-02-09 16:38:35 +0000
+++ src/economy/CMakeLists.txt	2017-02-14 20:57:42 +0000
@@ -55,6 +55,5 @@
     logic_widelands_geometry
     map_io
     notifications
-    wui
 )
 add_subdirectory(test)

=== modified file 'src/economy/expedition_bootstrap.cc'
--- src/economy/expedition_bootstrap.cc	2017-02-09 19:23:44 +0000
+++ src/economy/expedition_bootstrap.cc	2017-02-14 20:57:42 +0000
@@ -26,12 +26,12 @@
 #include "economy/portdock.h"
 #include "economy/wares_queue.h"
 #include "economy/workers_queue.h"
+#include "io/fileread.h"
 #include "io/filewrite.h"
 #include "logic/map_objects/tribes/warehouse.h"
 #include "logic/player.h"
 #include "map_io/map_object_loader.h"
 #include "map_io/map_object_saver.h"
-#include "wui/interactive_gamebase.h"
 
 namespace Widelands {
 
@@ -87,8 +87,8 @@
 	queues_[buildcost_size]->set_callback(input_callback, this);
 
 	// Update the user interface
-	if (upcast(InteractiveGameBase, igb, warehouse->owner().egbase().get_ibase()))
-		warehouse->refresh_options(*igb);
+	Notifications::publish(
+	   NoteBuilding(warehouse->serial(), NoteBuilding::Action::kRefresh));
 }
 
 void ExpeditionBootstrap::cancel(Game& game) {
@@ -109,9 +109,8 @@
 	queues_.clear();
 
 	// Update the user interface
-	if (upcast(InteractiveGameBase, igb, warehouse->owner().egbase().get_ibase())) {
-		warehouse->refresh_options(*igb);
-	}
+	Notifications::publish(
+	   NoteBuilding(warehouse->serial(), NoteBuilding::Action::kRefresh));
 	Notifications::publish(NoteExpeditionCanceled(this));
 }
 

=== modified file 'src/economy/portdock.cc'
--- src/economy/portdock.cc	2017-02-02 20:03:59 +0000
+++ src/economy/portdock.cc	2017-02-14 20:57:42 +0000
@@ -38,7 +38,6 @@
 #include "logic/widelands_geometry_io.h"
 #include "map_io/map_object_loader.h"
 #include "map_io/map_object_saver.h"
-#include "wui/interactive_gamebase.h"
 
 namespace Widelands {
 

=== modified file 'src/logic/editor_game_base.h'
--- src/logic/editor_game_base.h	2017-01-25 18:55:59 +0000
+++ src/logic/editor_game_base.h	2017-02-14 20:57:42 +0000
@@ -38,6 +38,7 @@
 }
 struct FullscreenMenuLaunchGame;
 class InteractiveBase;
+class InteractiveGameBase;  // TODO(GunChleoc): Get rid
 
 namespace Widelands {
 

=== modified file 'src/logic/map_objects/immovable.cc'
--- src/logic/map_objects/immovable.cc	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/immovable.cc	2017-02-14 20:57:42 +0000
@@ -32,6 +32,7 @@
 #include "config.h"
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
+#include "graphic/text_constants.h"
 #include "helper.h"
 #include "io/fileread.h"
 #include "io/filewrite.h"
@@ -53,7 +54,6 @@
 #include "notifications/notifications.h"
 #include "scripting/lua_table.h"
 #include "sound/sound_handler.h"
-#include "wui/interactive_base.h"
 
 namespace Widelands {
 

=== modified file 'src/logic/map_objects/tribes/building.cc'
--- src/logic/map_objects/tribes/building.cc	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/building.cc	2017-02-14 20:57:42 +0000
@@ -47,7 +47,6 @@
 #include "logic/map_objects/tribes/worker.h"
 #include "logic/player.h"
 #include "sound/sound_handler.h"
-#include "wui/interactive_player.h"
 
 namespace Widelands {
 
@@ -230,7 +229,6 @@
 
 Building::Building(const BuildingDescr& building_descr)
    : PlayerImmovable(building_descr),
-     optionswindow_(nullptr),
      flag_(nullptr),
      anim_(0),
      animstart_(0),
@@ -239,12 +237,6 @@
      seeing_(false) {
 }
 
-Building::~Building() {
-	if (optionswindow_) {
-		hide_options();
-	}
-}
-
 void Building::load_finish(EditorGameBase& egbase) {
 	auto should_be_deleted = [&egbase, this](const OPtr<Worker>& optr) {
 		Worker& worker = *optr.get(egbase);
@@ -395,9 +387,6 @@
 	}
 
 	PlayerImmovable::cleanup(egbase);
-
-	for (boost::signals2::connection& c : options_window_connections)
-		c.disconnect();
 }
 
 /*
@@ -442,6 +431,7 @@
 ===============
 */
 void Building::destroy(EditorGameBase& egbase) {
+	Notifications::publish(NoteBuilding(serial(), NoteBuilding::Action::kClose));
 	const bool fire = burn_on_destroy();
 	const Coords pos = position_;
 	Player* building_owner = get_owner();
@@ -690,14 +680,16 @@
 		}
 	}
 	PlayerImmovable::add_worker(worker);
-	workers_changed();
+	Notifications::publish(
+	   NoteBuilding(serial(), NoteBuilding::Action::kWorkersChanged));
 }
 
 void Building::remove_worker(Worker& worker) {
 	PlayerImmovable::remove_worker(worker);
 	if (!get_workers().size())
 		set_seeing(false);
-	workers_changed();
+	Notifications::publish(
+	   NoteBuilding(serial(), NoteBuilding::Action::kWorkersChanged));
 }
 
 /**

=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/building.h	2017-02-14 20:57:42 +0000
@@ -35,13 +35,10 @@
 #include "logic/map_objects/tribes/workarea_info.h"
 #include "logic/message.h"
 #include "logic/widelands.h"
+#include "notifications/notifications.h"
 #include "scripting/lua_table.h"
 
-namespace UI {
-class Window;
-}
 struct BuildingHints;
-class InteractiveGameBase;
 class Image;
 
 namespace Widelands {
@@ -191,6 +188,19 @@
 	DISALLOW_COPY_AND_ASSIGN(BuildingDescr);
 };
 
+struct NoteBuilding {
+	CAN_BE_SENT_AS_NOTE(NoteId::Building)
+
+	Serial serial;
+
+	enum class Action { kRefresh, kClose, kStartWarp, kFinishWarp, kWorkersChanged };
+	const Action action;
+
+	NoteBuilding(Serial init_serial, const Action& init_action)
+	   : serial(init_serial), action(init_action) {
+	}
+};
+
 class Building : public PlayerImmovable {
 	friend class BuildingDescr;
 	friend class MapBuildingdataPacket;
@@ -211,7 +221,6 @@
 	enum class InfoStringFormat { kCensus, kStatistics, kTooltip };
 
 	Building(const BuildingDescr&);
-	virtual ~Building();
 
 	void load_finish(EditorGameBase&) override;
 
@@ -241,12 +250,6 @@
 	virtual bool burn_on_destroy();
 	void destroy(EditorGameBase&) override;
 
-	void show_options(InteractiveGameBase&,
-	                  bool avoid_fastclick = false,
-	                  Vector2i pos = Vector2i(-1, -1));
-	void hide_options();
-	void refresh_options(InteractiveGameBase&);
-
 	virtual bool fetch_from_flag(Game&);
 	virtual bool get_building_work(Game&, Worker&, bool success);
 
@@ -289,7 +292,6 @@
 
 	void add_worker(Worker&) override;
 	void remove_worker(Worker&) override;
-	mutable boost::signals2::signal<void()> workers_changed;
 
 	void send_message(Game& game,
 	                  const Message::Type msgtype,
@@ -321,11 +323,8 @@
 	void
 	draw_info(TextToDraw draw_text, const Vector2f& point_on_dst, float scale, RenderTarget* dst);
 
-	virtual void create_options_window(InteractiveGameBase&, UI::Window*& registry) = 0;
-
 	void set_seeing(bool see);
 
-	UI::Window* optionswindow_;
 	Coords position_;
 	Flag* flag_;
 
@@ -346,9 +345,6 @@
 	/// Whether we see our vision_range area based on workers in the building
 	bool seeing_;
 
-	// Signals connected for the option window
-	std::vector<boost::signals2::connection> options_window_connections;
-
 	// The former buildings names, with the current one in last position.
 	FormerBuildings old_buildings_;
 

=== modified file 'src/logic/map_objects/tribes/constructionsite.cc'
--- src/logic/map_objects/tribes/constructionsite.cc	2017-01-28 14:53:28 +0000
+++ src/logic/map_objects/tribes/constructionsite.cc	2017-02-14 20:57:42 +0000
@@ -37,7 +37,6 @@
 #include "logic/map_objects/tribes/worker.h"
 #include "sound/sound_handler.h"
 #include "ui_basic/window.h"
-#include "wui/interactive_gamebase.h"
 
 namespace Widelands {
 
@@ -150,6 +149,8 @@
 ===============
 */
 void ConstructionSite::cleanup(EditorGameBase& egbase) {
+	// Register whether the window was open
+	Notifications::publish(NoteBuilding(serial(), NoteBuilding::Action::kStartWarp));
 	PartiallyFinishedBuilding::cleanup(egbase);
 
 	if (work_steps_ <= work_completed_) {
@@ -162,12 +163,8 @@
 			builder->set_location(&b);
 		}
 		// Open the new building window if needed
-		if (optionswindow_) {
-			Vector2i window_position = optionswindow_->get_pos();
-			hide_options();
-			InteractiveGameBase& igbase = dynamic_cast<InteractiveGameBase&>(*egbase.get_ibase());
-			b.show_options(igbase, false, window_position);
-		}
+		Notifications::publish(
+		   NoteBuilding(b.serial(), NoteBuilding::Action::kFinishWarp));
 	}
 }
 

=== modified file 'src/logic/map_objects/tribes/constructionsite.h'
--- src/logic/map_objects/tribes/constructionsite.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/constructionsite.h	2017-02-14 20:57:42 +0000
@@ -110,7 +110,6 @@
 	uint32_t build_step_time() const override {
 		return CONSTRUCTIONSITE_STEP_TIME;
 	}
-	void create_options_window(InteractiveGameBase&, UI::Window*& registry) override;
 
 	static void wares_queue_callback(Game&, InputQueue*, DescriptionIndex, Worker*, void* data);
 

=== modified file 'src/logic/map_objects/tribes/dismantlesite.h'
--- src/logic/map_objects/tribes/dismantlesite.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/dismantlesite.h	2017-02-14 20:57:42 +0000
@@ -87,8 +87,6 @@
 		return DISMANTLESITE_STEP_TIME;
 	}
 
-	void create_options_window(InteractiveGameBase&, UI::Window*& registry) override;
-
 	void draw(uint32_t gametime,
 	          TextToDraw draw_text,
 	          const Vector2f& point_on_dst,

=== modified file 'src/logic/map_objects/tribes/militarysite.h'
--- src/logic/map_objects/tribes/militarysite.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/militarysite.h	2017-02-14 20:57:42 +0000
@@ -134,8 +134,6 @@
 protected:
 	void conquer_area(EditorGameBase&);
 
-	void create_options_window(InteractiveGameBase&, UI::Window*& registry) override;
-
 private:
 	void update_statistics_string(std::string*) override;
 

=== modified file 'src/logic/map_objects/tribes/productionsite.cc'
--- src/logic/map_objects/tribes/productionsite.cc	2017-02-11 12:38:17 +0000
+++ src/logic/map_objects/tribes/productionsite.cc	2017-02-14 20:57:42 +0000
@@ -636,7 +636,8 @@
 	// the last one we need to start working.
 	w->start_task_idle(game, 0, -1);
 	psite.try_start_working(game);
-	psite.workers_changed();
+	Notifications::publish(
+	   NoteBuilding(psite.serial(), NoteBuilding::Action::kWorkersChanged));
 }
 
 /**
@@ -716,6 +717,7 @@
 void ProductionSite::set_stopped(bool const stopped) {
 	is_stopped_ = stopped;
 	get_economy()->rebalance_supply();
+	Notifications::publish(NoteBuilding(serial(), NoteBuilding::Action::kRefresh));
 }
 
 /**
@@ -918,7 +920,8 @@
 void ProductionSite::train_workers(Game& game) {
 	for (uint32_t i = descr().nr_working_positions(); i;)
 		working_positions_[--i].worker->gain_experience(game);
-	Building::workers_changed();
+	Notifications::publish(
+	   NoteBuilding(serial(), NoteBuilding::Action::kWorkersChanged));
 }
 
 void ProductionSite::notify_player(Game& game, uint8_t minutes, FailNotificationType type) {

=== modified file 'src/logic/map_objects/tribes/productionsite.h'
--- src/logic/map_objects/tribes/productionsite.h	2017-02-14 20:22:29 +0000
+++ src/logic/map_objects/tribes/productionsite.h	2017-02-14 20:57:42 +0000
@@ -237,8 +237,6 @@
 protected:
 	void update_statistics_string(std::string* statistics) override;
 
-	void create_options_window(InteractiveGameBase&, UI::Window*& registry) override;
-
 	void load_finish(EditorGameBase& egbase) override;
 
 protected:

=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc	2017-01-30 14:40:12 +0000
+++ src/logic/map_objects/tribes/ship.cc	2017-02-14 20:57:42 +0000
@@ -32,6 +32,7 @@
 #include "economy/wares_queue.h"
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
+#include "graphic/text_constants.h"
 #include "io/fileread.h"
 #include "io/filewrite.h"
 #include "logic/findbob.h"
@@ -48,7 +49,6 @@
 #include "logic/widelands_geometry_io.h"
 #include "map_io/map_object_loader.h"
 #include "map_io/map_object_saver.h"
-#include "wui/interactive_gamebase.h"
 
 namespace Widelands {
 

=== modified file 'src/logic/map_objects/tribes/trainingsite.h'
--- src/logic/map_objects/tribes/trainingsite.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/trainingsite.h	2017-02-14 20:57:42 +0000
@@ -209,7 +209,6 @@
 	void training_done();
 
 protected:
-	void create_options_window(InteractiveGameBase&, UI::Window*& registry) override;
 	void program_end(Game&, ProgramResult) override;
 
 private:

=== modified file 'src/logic/map_objects/tribes/warehouse.h'
--- src/logic/map_objects/tribes/warehouse.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/warehouse.h	2017-02-14 20:57:42 +0000
@@ -253,8 +253,6 @@
 protected:
 	/// Initializes the container sizes for the owner's tribe.
 	void init_containers(Player& owner);
-	/// Create the warehouse information window.
-	void create_options_window(InteractiveGameBase&, UI::Window*& registry) override;
 
 private:
 	void init_portdock(EditorGameBase& egbase);

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2017-02-14 20:31:26 +0000
+++ src/logic/player.cc	2017-02-14 20:57:42 +0000
@@ -693,6 +693,8 @@
 			workers = building->get_workers();
 		}
 
+		Notifications::publish(
+		   NoteBuilding(building->serial(), NoteBuilding::Action::kClose));
 		building->remove(egbase());  //  no fire or stuff
 		//  Hereafter the old building does not exist and building is a dangling
 		//  pointer.

=== modified file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h	2017-02-09 17:56:23 +0000
+++ src/notifications/note_ids.h	2017-02-14 20:57:42 +0000
@@ -36,6 +36,7 @@
 	TrainingSiteSoldierTrained,
 	ShipMessage,
 	ShipWindow,
+	Building,
 	Economy,
 	GraphicResolutionChanged,
 	NoteExpeditionCanceled

=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt	2017-02-09 19:14:32 +0000
+++ src/wui/CMakeLists.txt	2017-02-14 20:57:42 +0000
@@ -145,13 +145,14 @@
     attack_box.h
     building_statistics_menu.cc
     building_statistics_menu.h
-    building_ui.cc
     buildingwindow.cc
     buildingwindow.h
     constructionsitewindow.cc
+    constructionsitewindow.h
     debugconsole.cc
     debugconsole.h
     dismantlesitewindow.cc
+    dismantlesitewindow.h
     encyclopedia_window.cc
     encyclopedia_window.h
     fieldaction.cc
@@ -194,6 +195,7 @@
     login_box.h
     logmessage.h
     militarysitewindow.cc
+    militarysitewindow.h
     minimap.cc
     minimap.h
     multiplayersetupgroup.cc
@@ -217,6 +219,7 @@
     story_message_box.cc
     story_message_box.h
     trainingsitewindow.cc
+    trainingsitewindow.h
     transport_draw.cc
     tribal_encyclopedia.cc
     tribal_encyclopedia.h
@@ -225,6 +228,7 @@
     ware_statistics_menu.cc
     ware_statistics_menu.h
     warehousewindow.cc
+    warehousewindow.h
     watchwindow.cc
     watchwindow.h
   USES_SDL2

=== removed file 'src/wui/building_ui.cc'
--- src/wui/building_ui.cc	2017-01-25 18:55:59 +0000
+++ src/wui/building_ui.cc	1970-01-01 00:00:00 +0000
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2002-2017 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.
- *
- */
-
-#include "wui/buildingwindow.h"
-
-#include <boost/lexical_cast.hpp>
-
-#include "base/macros.h"
-#include "logic/map_objects/tribes/building.h"
-#include "ui_basic/window.h"
-
-using Widelands::Building;
-
-/**
- * Create the building's options window if necessary and bring it to
- * the top to be seen by the player.
- */
-void Building::show_options(InteractiveGameBase& igbase, bool avoid_fastclick, Vector2i pos) {
-	if (optionswindow_) {
-		if (optionswindow_->is_minimal())
-			optionswindow_->restore();
-		optionswindow_->move_to_top();
-	} else {
-		create_options_window(igbase, optionswindow_);
-		if (upcast(BuildingWindow, bw, optionswindow_)) {
-			bw->set_avoid_fastclick(avoid_fastclick);
-		}
-		// Run a first think here so that certain things like caps buttons
-		// get properly initialized
-		optionswindow_->think();
-	}
-	if (pos.x >= 0 && pos.y >= 0) {
-		optionswindow_->set_pos(pos);
-	}
-}
-
-/**
- * Remove the building's options window.
- */
-void Building::hide_options() {
-	for (boost::signals2::connection& c : options_window_connections)
-		c.disconnect();
-	delete optionswindow_;
-	optionswindow_ = nullptr;
-}
-
-/**
- * refreshs the option window of a building - useful if some ui elements have to be removed or added
- */
-void Building::refresh_options(InteractiveGameBase& igb) {
-	// Only do something if there is actually a window
-	if (optionswindow_) {
-		Vector2i window_position = optionswindow_->get_pos();
-		hide_options();
-		show_options(igb, true);
-		optionswindow_->set_pos(window_position);
-	}
-}

=== modified file 'src/wui/buildingwindow.cc'
--- src/wui/buildingwindow.cc	2017-01-28 14:53:28 +0000
+++ src/wui/buildingwindow.cc	2017-02-14 20:57:42 +0000
@@ -46,58 +46,77 @@
 static const char* pic_debug = "images/wui/fieldaction/menu_debug.png";
 
 BuildingWindow::BuildingWindow(InteractiveGameBase& parent,
+                               UI::UniqueWindow::Registry& reg,
                                Widelands::Building& b,
-                               UI::Window*& registry)
-   : UI::Window(&parent, "building_window", 0, 0, Width, 0, b.descr().descname()),
-     registry_(registry),
+                               bool avoid_fastclick)
+   : UI::UniqueWindow(&parent, "building_window", &reg, Width, 0, b.descr().descname()),
+     is_dying_(false),
      building_(b),
      workarea_overlay_id_(0),
-     avoid_fastclick_(false),
+     avoid_fastclick_(avoid_fastclick),
      expeditionbtn_(nullptr) {
-	delete registry_;
-	registry_ = this;
-
+	buildingnotes_subscriber_ = Notifications::subscribe<Widelands::NoteBuilding>(
+	   [this](const Widelands::NoteBuilding& note) { on_building_note(note); });
+}
+
+BuildingWindow::~BuildingWindow() {
+	if (workarea_overlay_id_) {
+		igbase().mutable_field_overlay_manager()->remove_overlay(workarea_overlay_id_);
+	}
+}
+
+void BuildingWindow::on_building_note(const Widelands::NoteBuilding& note) {
+	if (note.serial == building_.serial() && !is_dying_) {
+		switch (note.action) {
+		// The building's state has changed
+		case Widelands::NoteBuilding::Action::kRefresh:
+			init(true);
+			break;
+		// The building is no more
+		case Widelands::NoteBuilding::Action::kStartWarp:
+			igbase().add_wanted_building_window(building().get_position(), get_pos(), is_minimal());
+		// Fallthrough intended
+		case Widelands::NoteBuilding::Action::kClose:
+			// Stop everybody from thinking to avoid segfaults
+			is_dying_ = true;
+			set_thinks(false);
+			vbox_.reset(nullptr);
+			die();
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+namespace Widelands {
+class BuildingDescr;
+}
+using Widelands::Building;
+
+void BuildingWindow::init(bool avoid_fastclick) {
 	capscache_player_number_ = 0;
 	capsbuttons_ = nullptr;
 	capscache_ = 0;
 	caps_setup_ = false;
 	toggle_workarea_ = nullptr;
-
-	UI::Box* vbox = new UI::Box(this, 0, 0, UI::Box::Vertical);
-
-	tabs_ = new UI::TabPanel(vbox, 0, 0, nullptr);
-	vbox->add(tabs_, UI::Align::kLeft, true);
-
-	capsbuttons_ = new UI::Box(vbox, 0, 0, UI::Box::Horizontal);
-	vbox->add(capsbuttons_, UI::Align::kLeft, true);
+	avoid_fastclick_ = avoid_fastclick,
+
+	vbox_.reset(new UI::Box(this, 0, 0, UI::Box::Vertical));
+
+	tabs_ = new UI::TabPanel(vbox_.get(), 0, 0, nullptr);
+	vbox_->add(tabs_, UI::Align::kLeft, true);
+
+	capsbuttons_ = new UI::Box(vbox_.get(), 0, 0, UI::Box::Horizontal);
+	vbox_->add(capsbuttons_, UI::Align::kLeft, true);
 	// actually create buttons on the first call to think(),
 	// so that overriding create_capsbuttons() works
 
-	set_center_panel(vbox);
+	set_center_panel(vbox_.get());
 	set_thinks(true);
 	set_fastclick_panel(this);
-
 	show_workarea();
-
-	// Title for construction site
-	if (upcast(Widelands::ConstructionSite, csite, &building_)) {
-		// Show name in parenthesis as it may take all width already
-		const std::string title = (boost::format("(%s)") % csite->building().descname()).str();
-		set_title(title);
-	}
-}
-
-BuildingWindow::~BuildingWindow() {
-	if (workarea_overlay_id_) {
-		igbase().mutable_field_overlay_manager()->remove_overlay(workarea_overlay_id_);
-	}
-	registry_ = nullptr;
-}
-
-namespace Widelands {
-class BuildingDescr;
-}
-using Widelands::Building;
+}
 
 /*
 ===============
@@ -129,9 +148,10 @@
 	    building().get_playercaps() != capscache_) {
 		capsbuttons_->free_children();
 		create_capsbuttons(capsbuttons_);
-		move_out_of_the_way();
-		if (!avoid_fastclick_)
+		if (!avoid_fastclick_) {
+			move_out_of_the_way();
 			warp_mouse_to_fastclick_panel();
+		}
 		caps_setup_ = true;
 	}
 
@@ -345,10 +365,9 @@
 ===============
 */
 void BuildingWindow::act_start_stop() {
-	if (dynamic_cast<const Widelands::ProductionSite*>(&building_))
+	if (dynamic_cast<const Widelands::ProductionSite*>(&building_)) {
 		igbase().game().send_player_start_stop_building(building_);
-
-	die();
+	}
 }
 
 /**

=== modified file 'src/wui/buildingwindow.h'
--- src/wui/buildingwindow.h	2017-01-25 18:55:59 +0000
+++ src/wui/buildingwindow.h	2017-02-14 20:57:42 +0000
@@ -24,8 +24,9 @@
 #include <memory>
 
 #include "economy/expedition_bootstrap.h"
+#include "notifications/notifications.h"
 #include "ui_basic/button.h"
-#include "ui_basic/window.h"
+#include "ui_basic/unique_window.h"
 #include "wui/field_overlay_manager.h"
 #include "wui/interactive_gamebase.h"
 #include "wui/waresdisplay.h"
@@ -35,14 +36,17 @@
  *
  * This class is sub-classed for all building types to provide something useful.
  */
-struct BuildingWindow : public UI::Window {
+struct BuildingWindow : public UI::UniqueWindow {
 	friend struct TrainingSiteWindow;
 	friend struct MilitarySiteWindow;
 	enum {
 		Width = 4 * 34  //  4 normally sized buttons
 	};
 
-	BuildingWindow(InteractiveGameBase& parent, Widelands::Building&, UI::Window*& registry);
+	BuildingWindow(InteractiveGameBase& parent,
+	               UI::UniqueWindow::Registry& reg,
+	               Widelands::Building&,
+	               bool avoid_fastclick);
 
 	virtual ~BuildingWindow();
 
@@ -56,11 +60,10 @@
 
 	void draw(RenderTarget&) override;
 	void think() override;
-	void set_avoid_fastclick(bool afc) {
-		avoid_fastclick_ = afc;
-	}
 
 protected:
+	virtual void init(bool avoid_fastclick);
+
 	UI::TabPanel* get_tabs() {
 		return tabs_;
 	}
@@ -82,11 +85,15 @@
 
 	virtual void create_capsbuttons(UI::Box* buttons);
 
-	UI::Window*& registry_;
+	bool is_dying_;
 
 private:
+	/// Actions performed when a NoteBuilding is received.
+	void on_building_note(const Widelands::NoteBuilding& note);
 	Widelands::Building& building_;
 
+	std::unique_ptr<UI::Box> vbox_;
+
 	UI::TabPanel* tabs_;
 
 	UI::Box* capsbuttons_;  ///< \ref UI::Box that contains capabilities buttons
@@ -106,6 +113,8 @@
 	UI::Button* expeditionbtn_;
 	std::unique_ptr<Notifications::Subscriber<Widelands::NoteExpeditionCanceled>>
 	   expedition_canceled_subscriber_;
+	std::unique_ptr<Notifications::Subscriber<Widelands::NoteBuilding>> buildingnotes_subscriber_;
+	DISALLOW_COPY_AND_ASSIGN(BuildingWindow);
 };
 
 #endif  // end of include guard: WL_WUI_BUILDINGWINDOW_H

=== modified file 'src/wui/constructionsitewindow.cc'
--- src/wui/constructionsitewindow.cc	2017-01-25 18:55:59 +0000
+++ src/wui/constructionsitewindow.cc	2017-02-14 20:57:42 +0000
@@ -17,35 +17,29 @@
  *
  */
 
-#include "wui/buildingwindow.h"
+#include "wui/constructionsitewindow.h"
 
-#include "wui/inputqueuedisplay.h"
+#include <boost/format.hpp>
 
 #include "graphic/graphic.h"
-#include "logic/map_objects/tribes/constructionsite.h"
-#include "ui_basic/progressbar.h"
 #include "ui_basic/tabpanel.h"
+#include "wui/inputqueuedisplay.h"
 
 static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";
 
-/**
- * Status window for construction sites.
- */
-struct ConstructionSiteWindow : public BuildingWindow {
-	ConstructionSiteWindow(InteractiveGameBase& parent,
-	                       Widelands::ConstructionSite&,
-	                       UI::Window*& registry);
-
-	void think() override;
-
-private:
-	UI::ProgressBar* progress_;
-};
+using namespace Widelands;
 
 ConstructionSiteWindow::ConstructionSiteWindow(InteractiveGameBase& parent,
+                                               UI::UniqueWindow::Registry& reg,
                                                Widelands::ConstructionSite& cs,
-                                               UI::Window*& registry)
-   : BuildingWindow(parent, cs, registry) {
+                                               bool avoid_fastclick)
+   : BuildingWindow(parent, reg, cs, avoid_fastclick) {
+	init(avoid_fastclick);
+}
+
+void ConstructionSiteWindow::init(bool avoid_fastclick) {
+	BuildingWindow::init(avoid_fastclick);
+	Widelands::ConstructionSite& cs = dynamic_cast<Widelands::ConstructionSite&>(building());
 	UI::Box& box = *new UI::Box(get_tabs(), 0, 0, UI::Box::Vertical);
 
 	// Add the progress bar
@@ -62,6 +56,9 @@
 		   new InputQueueDisplay(&box, 0, 0, igbase(), cs, cs.get_waresqueue(i)), UI::Align::kLeft);
 
 	get_tabs()->add("wares", g_gr->images().get(pic_tab_wares), &box, _("Building materials"));
+
+	set_title((boost::format("(%s)") % cs.building().descname()).str());
+	think();
 }
 
 /*
@@ -76,13 +73,3 @@
 
 	progress_->set_state(cs.get_built_per64k());
 }
-
-/*
-===============
-Create the status window describing the construction site.
-===============
-*/
-void Widelands::ConstructionSite::create_options_window(InteractiveGameBase& parent,
-                                                        UI::Window*& registry) {
-	new ConstructionSiteWindow(parent, *this, registry);
-}

=== added file 'src/wui/constructionsitewindow.h'
--- src/wui/constructionsitewindow.h	1970-01-01 00:00:00 +0000
+++ src/wui/constructionsitewindow.h	2017-02-14 20:57:42 +0000
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2002-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_CONSTRUCTIONSITEWINDOW_H
+#define WL_WUI_CONSTRUCTIONSITEWINDOW_H
+
+#include "logic/map_objects/tribes/constructionsite.h"
+#include "ui_basic/progressbar.h"
+#include "wui/buildingwindow.h"
+
+/**
+ * Status window for construction sites.
+ */
+struct ConstructionSiteWindow : public BuildingWindow {
+	ConstructionSiteWindow(InteractiveGameBase& parent,
+	                       UI::UniqueWindow::Registry& reg,
+	                       Widelands::ConstructionSite&,
+	                       bool avoid_fastclick);
+
+	void think() override;
+
+protected:
+	void init(bool avoid_fastclick) override;
+
+private:
+	UI::ProgressBar* progress_;
+	DISALLOW_COPY_AND_ASSIGN(ConstructionSiteWindow);
+};
+
+#endif  // end of include guard: WL_WUI_CONSTRUCTIONSITEWINDOW_H

=== modified file 'src/wui/dismantlesitewindow.cc'
--- src/wui/dismantlesitewindow.cc	2017-01-25 18:55:59 +0000
+++ src/wui/dismantlesitewindow.cc	2017-02-14 20:57:42 +0000
@@ -17,33 +17,26 @@
  *
  */
 
-#include "logic/map_objects/tribes/dismantlesite.h"
+#include "wui/dismantlesitewindow.h"
 
 #include "graphic/graphic.h"
-#include "ui_basic/progressbar.h"
 #include "ui_basic/tabpanel.h"
-#include "wui/buildingwindow.h"
 
 static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";
 
-/**
- * Status window for dismantle sites.
- */
-struct DismantleSiteWindow : public BuildingWindow {
-	DismantleSiteWindow(InteractiveGameBase& parent,
-	                    Widelands::DismantleSite&,
-	                    UI::Window*& registry);
-
-	void think() override;
-
-private:
-	UI::ProgressBar* progress_;
-};
+using namespace Widelands;
 
 DismantleSiteWindow::DismantleSiteWindow(InteractiveGameBase& parent,
-                                         Widelands::DismantleSite& cs,
-                                         UI::Window*& registry)
-   : BuildingWindow(parent, cs, registry) {
+                                         UI::UniqueWindow::Registry& reg,
+                                         Widelands::DismantleSite& ds,
+                                         bool avoid_fastclick)
+   : BuildingWindow(parent, reg, ds, avoid_fastclick) {
+	init(avoid_fastclick);
+}
+
+void DismantleSiteWindow::init(bool avoid_fastclick) {
+	BuildingWindow::init(avoid_fastclick);
+	Widelands::DismantleSite& ds = dynamic_cast<Widelands::DismantleSite&>(building());
 	UI::Box& box = *new UI::Box(get_tabs(), 0, 0, UI::Box::Vertical);
 
 	// Add the progress bar
@@ -55,10 +48,11 @@
 	box.add_space(8);
 
 	// Add the wares queue
-	for (uint32_t i = 0; i < cs.get_nrwaresqueues(); ++i)
-		BuildingWindow::create_input_queue_panel(&box, cs, cs.get_waresqueue(i), true);
+	for (uint32_t i = 0; i < ds.get_nrwaresqueues(); ++i)
+		BuildingWindow::create_input_queue_panel(&box, ds, ds.get_waresqueue(i), true);
 
 	get_tabs()->add("wares", g_gr->images().get(pic_tab_wares), &box, _("Building materials"));
+	think();
 }
 
 /*
@@ -73,13 +67,3 @@
 
 	progress_->set_state(ds.get_built_per64k());
 }
-
-/*
-===============
-Create the status window describing the site.
-===============
-*/
-void Widelands::DismantleSite::create_options_window(InteractiveGameBase& parent,
-                                                     UI::Window*& registry) {
-	new DismantleSiteWindow(parent, *this, registry);
-}

=== added file 'src/wui/dismantlesitewindow.h'
--- src/wui/dismantlesitewindow.h	1970-01-01 00:00:00 +0000
+++ src/wui/dismantlesitewindow.h	2017-02-14 20:57:42 +0000
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2002-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_DISMANTLESITEWINDOW_H
+#define WL_WUI_DISMANTLESITEWINDOW_H
+
+#include "logic/map_objects/tribes/dismantlesite.h"
+#include "ui_basic/progressbar.h"
+#include "wui/buildingwindow.h"
+
+/**
+ * Status window for dismantle sites.
+ */
+struct DismantleSiteWindow : public BuildingWindow {
+	DismantleSiteWindow(InteractiveGameBase& parent,
+	                    UI::UniqueWindow::Registry& reg,
+	                    Widelands::DismantleSite&,
+	                    bool avoid_fastclick);
+
+	void think() override;
+
+protected:
+	void init(bool avoid_fastclick) override;
+
+private:
+	UI::ProgressBar* progress_;
+	DISALLOW_COPY_AND_ASSIGN(DismantleSiteWindow);
+};
+
+#endif  // end of include guard: WL_WUI_DISMANTLESITEWINDOW_H

=== modified file 'src/wui/interactive_gamebase.cc'
--- src/wui/interactive_gamebase.cc	2017-02-10 15:20:46 +0000
+++ src/wui/interactive_gamebase.cc	2017-02-14 20:57:42 +0000
@@ -33,8 +33,15 @@
 #include "logic/map_objects/tribes/ship.h"
 #include "logic/player.h"
 #include "profile/profile.h"
+#include "wui/constructionsitewindow.h"
+#include "wui/dismantlesitewindow.h"
 #include "wui/game_summary.h"
+#include "wui/militarysitewindow.h"
+#include "wui/productionsitewindow.h"
 #include "wui/shipwindow.h"
+#include "wui/trainingsitewindow.h"
+#include "wui/unique_window_handler.h"
+#include "wui/warehousewindow.h"
 
 namespace {
 
@@ -55,6 +62,28 @@
      chat_provider_(nullptr),
      multiplayer_(multiplayer),
      playertype_(pt) {
+	buildingnotes_subscriber_ = Notifications::subscribe<Widelands::NoteBuilding>(
+	   [this](const Widelands::NoteBuilding& note) {
+		   switch (note.action) {
+		   case Widelands::NoteBuilding::Action::kFinishWarp: {
+			   if (upcast(
+			          Widelands::Building const, building, game().objects().get_object(note.serial))) {
+				   const Widelands::Coords coords = building->get_position();
+				   // Check whether the window is wanted
+				   if (wanted_building_windows_.count(coords.hash()) == 1) {
+					   UI::UniqueWindow* building_window = show_building_window(coords, true);
+					   building_window->set_pos(std::get<1>(wanted_building_windows_.at(coords.hash())));
+					   if (std::get<2>(wanted_building_windows_.at(coords.hash()))) {
+						   building_window->minimize();
+					   }
+					   wanted_building_windows_.erase(coords.hash());
+				   }
+			   }
+		   } break;
+		   default:
+			   break;
+		   }
+		});
 }
 
 /// \return a pointer to the running \ref Game instance.
@@ -128,6 +157,68 @@
 	toggle_buildhelp_->set_perm_pressed(value);
 }
 
+void InteractiveGameBase::add_wanted_building_window(const Widelands::Coords& coords,
+                                                     const Vector2i point,
+                                                     bool was_minimal) {
+	wanted_building_windows_.insert(
+	   std::make_pair(coords.hash(), std::make_tuple(coords, point, was_minimal)));
+}
+
+UI::UniqueWindow* InteractiveGameBase::show_building_window(const Widelands::Coords& coord,
+                                                            bool avoid_fastclick) {
+	Widelands::BaseImmovable* immovable = game().map().get_immovable(coord);
+	upcast(Widelands::Building, building, immovable);
+	assert(building);
+	UI::UniqueWindow::Registry& registry =
+	   unique_windows().get_registry((boost::format("building_%d") % building->serial()).str());
+
+	switch (building->descr().type()) {
+	case Widelands::MapObjectType::CONSTRUCTIONSITE:
+		registry.open_window = [this, &registry, building, avoid_fastclick] {
+			new ConstructionSiteWindow(*this, registry,
+			                           *dynamic_cast<Widelands::ConstructionSite*>(building),
+			                           avoid_fastclick);
+		};
+		break;
+	case Widelands::MapObjectType::DISMANTLESITE:
+		registry.open_window = [this, &registry, building, avoid_fastclick] {
+			new DismantleSiteWindow(
+			   *this, registry, *dynamic_cast<Widelands::DismantleSite*>(building), avoid_fastclick);
+		};
+		break;
+	case Widelands::MapObjectType::MILITARYSITE:
+		registry.open_window = [this, &registry, building, avoid_fastclick] {
+			new MilitarySiteWindow(
+			   *this, registry, *dynamic_cast<Widelands::MilitarySite*>(building), avoid_fastclick);
+		};
+		break;
+	case Widelands::MapObjectType::PRODUCTIONSITE:
+		registry.open_window = [this, &registry, building, avoid_fastclick] {
+			new ProductionSiteWindow(
+			   *this, registry, *dynamic_cast<Widelands::ProductionSite*>(building), avoid_fastclick);
+		};
+		break;
+	case Widelands::MapObjectType::TRAININGSITE:
+		registry.open_window = [this, &registry, building, avoid_fastclick] {
+			new TrainingSiteWindow(
+			   *this, registry, *dynamic_cast<Widelands::TrainingSite*>(building), avoid_fastclick);
+		};
+		break;
+	case Widelands::MapObjectType::WAREHOUSE:
+		registry.open_window = [this, &registry, building, avoid_fastclick] {
+			new WarehouseWindow(
+			   *this, registry, *dynamic_cast<Widelands::Warehouse*>(building), avoid_fastclick);
+		};
+		break;
+	default:
+		log("Unable to show window for building '%s', type '%s'.\n", building->descr().name().c_str(),
+		    to_string(building->descr().type()).c_str());
+		NEVER_HERE();
+	}
+	registry.create();
+	return registry.window;
+}
+
 /**
  * See if we can reasonably open a ship window at the current selection position.
  * If so, do it and return true; otherwise, return false.

=== modified file 'src/wui/interactive_gamebase.h'
--- src/wui/interactive_gamebase.h	2017-01-25 18:55:59 +0000
+++ src/wui/interactive_gamebase.h	2017-02-14 20:57:42 +0000
@@ -20,11 +20,16 @@
 #ifndef WL_WUI_INTERACTIVE_GAMEBASE_H
 #define WL_WUI_INTERACTIVE_GAMEBASE_H
 
+#include <map>
+#include <memory>
+#include <tuple>
+
 #include "logic/game.h"
 #include "profile/profile.h"
 #include "wui/general_statistics_menu.h"
 #include "wui/interactive_base.h"
 
+struct BuildingWindow;
 struct ChatProvider;
 
 enum PlayerType { NONE, OBSERVER, PLAYING, VICTORIOUS, DEFEATED };
@@ -69,6 +74,10 @@
 		playertype_ = pt;
 	}
 
+	void add_wanted_building_window(const Widelands::Coords& coords,
+	                                const Vector2i point,
+	                                bool was_minimal);
+	UI::UniqueWindow* show_building_window(const Widelands::Coords& coords, bool avoid_fastclick);
 	bool try_show_ship_window();
 	bool is_multiplayer() {
 		return multiplayer_;
@@ -94,6 +103,10 @@
 
 private:
 	void on_buildhelp_changed(const bool value) override;
+	// Building coordinates, window position, whether the window was minimized
+	std::map<uint32_t, std::tuple<const Widelands::Coords, const Vector2i, bool>>
+	   wanted_building_windows_;
+	std::unique_ptr<Notifications::Subscriber<Widelands::NoteBuilding>> buildingnotes_subscriber_;
 };
 
 #endif  // end of include guard: WL_WUI_INTERACTIVE_GAMEBASE_H

=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc	2017-02-09 16:38:35 +0000
+++ src/wui/interactive_player.cc	2017-02-14 20:57:42 +0000
@@ -195,8 +195,10 @@
 	if (1 < player().vision(Map::get_index(get_sel_pos().node, map.get_width()))) {
 		// Special case for buildings
 		if (upcast(Building, building, map.get_immovable(get_sel_pos().node)))
-			if (can_see(building->owner().player_number()))
-				return building->show_options(*this);
+			if (can_see(building->owner().player_number())) {
+				show_building_window(get_sel_pos().node, false);
+				return;
+			}
 
 		if (!is_building_road()) {
 			if (try_show_ship_window())

=== modified file 'src/wui/interactive_spectator.cc'
--- src/wui/interactive_spectator.cc	2017-01-25 18:55:59 +0000
+++ src/wui/interactive_spectator.cc	2017-02-14 20:57:42 +0000
@@ -142,12 +142,15 @@
  * Observer has clicked on the given node; bring up the context menu.
  */
 void InteractiveSpectator::node_action() {
-	if  //  special case for buildings
-	   (upcast(Widelands::Building, building, egbase().map().get_immovable(get_sel_pos().node)))
-		return building->show_options(*this);
+	// Special case for buildings
+	if (is_a(Widelands::Building, egbase().map().get_immovable(get_sel_pos().node))) {
+		show_building_window(get_sel_pos().node, false);
+		return;
+	}
 
-	if (try_show_ship_window())
+	if (try_show_ship_window()) {
 		return;
+	}
 
 	//  everything else can bring up the temporary dialog
 	show_field_action(this, nullptr, &fieldaction_);

=== modified file 'src/wui/militarysitewindow.cc'
--- src/wui/militarysitewindow.cc	2017-01-25 18:55:59 +0000
+++ src/wui/militarysitewindow.cc	2017-02-14 20:57:42 +0000
@@ -17,47 +17,32 @@
  *
  */
 
-#include "logic/map_objects/tribes/militarysite.h"
+#include "wui/militarysitewindow.h"
 
 #include "graphic/graphic.h"
 #include "ui_basic/tabpanel.h"
-#include "wui/buildingwindow.h"
 #include "wui/soldiercapacitycontrol.h"
 #include "wui/soldierlist.h"
 
-using Widelands::MilitarySite;
+using namespace Widelands;
 
 static char const* pic_tab_military = "images/wui/buildings/menu_tab_military.png";
 
-/**
- * Status window for \ref MilitarySite
- */
-struct MilitarySiteWindow : public BuildingWindow {
-	MilitarySiteWindow(InteractiveGameBase& parent, MilitarySite&, UI::Window*& registry);
-
-	MilitarySite& militarysite() {
-		return dynamic_cast<MilitarySite&>(building());
-	}
-
-protected:
-	void create_capsbuttons(UI::Box* buttons) override;
-};
-
 MilitarySiteWindow::MilitarySiteWindow(InteractiveGameBase& parent,
+                                       UI::UniqueWindow::Registry& reg,
                                        MilitarySite& ms,
-                                       UI::Window*& registry)
-   : BuildingWindow(parent, ms, registry) {
-	get_tabs()->add("soldiers", g_gr->images().get(pic_tab_military),
-	                create_soldier_list(*get_tabs(), parent, militarysite()), _("Soldiers"));
+                                       bool avoid_fastclick)
+   : BuildingWindow(parent, reg, ms, avoid_fastclick) {
+	init(avoid_fastclick);
 }
 
 void MilitarySiteWindow::create_capsbuttons(UI::Box* buttons) {
 	BuildingWindow::create_capsbuttons(buttons);
 }
 
-/**
- * Create the  military site information window.
- */
-void MilitarySite::create_options_window(InteractiveGameBase& plr, UI::Window*& registry) {
-	new MilitarySiteWindow(plr, *this, registry);
+void MilitarySiteWindow::init(bool avoid_fastclick) {
+	BuildingWindow::init(avoid_fastclick);
+	get_tabs()->add("soldiers", g_gr->images().get(pic_tab_military),
+	                create_soldier_list(*get_tabs(), igbase(), militarysite()), _("Soldiers"));
+	think();
 }

=== added file 'src/wui/militarysitewindow.h'
--- src/wui/militarysitewindow.h	1970-01-01 00:00:00 +0000
+++ src/wui/militarysitewindow.h	2017-02-14 20:57:42 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2002-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_MILITARYSITEWINDOW_H
+#define WL_WUI_MILITARYSITEWINDOW_H
+
+#include "logic/map_objects/tribes/militarysite.h"
+#include "wui/buildingwindow.h"
+
+/**
+ * Status window for \ref MilitarySite
+ */
+struct MilitarySiteWindow : public BuildingWindow {
+	MilitarySiteWindow(InteractiveGameBase& parent,
+	                   UI::UniqueWindow::Registry& reg,
+	                   Widelands::MilitarySite&,
+	                   bool avoid_fastclick);
+
+	Widelands::MilitarySite& militarysite() {
+		return dynamic_cast<Widelands::MilitarySite&>(building());
+	}
+
+protected:
+	void init(bool avoid_fastclick) override;
+	void create_capsbuttons(UI::Box* buttons) override;
+
+private:
+	DISALLOW_COPY_AND_ASSIGN(MilitarySiteWindow);
+};
+
+#endif  // end of include guard: WL_WUI_MILITARYSITEWINDOW_H

=== modified file 'src/wui/productionsitewindow.cc'
--- src/wui/productionsitewindow.cc	2017-01-25 18:55:59 +0000
+++ src/wui/productionsitewindow.cc	2017-02-14 20:57:42 +0000
@@ -44,10 +44,28 @@
 ===============
 */
 ProductionSiteWindow::ProductionSiteWindow(InteractiveGameBase& parent,
+                                           UI::UniqueWindow::Registry& reg,
                                            ProductionSite& ps,
-                                           UI::Window*& registry)
-   : BuildingWindow(parent, ps, registry) {
-	const std::vector<Widelands::InputQueue*>& inputqueues = ps.inputqueues();
+                                           bool avoid_fastclick)
+   : BuildingWindow(parent, reg, ps, avoid_fastclick) {
+	productionsitenotes_subscriber_ = Notifications::subscribe<Widelands::NoteBuilding>(
+	   [this](const Widelands::NoteBuilding& note) {
+		   if (note.serial == building().serial() && !is_dying_) {
+			   switch (note.action) {
+			   case Widelands::NoteBuilding::Action::kWorkersChanged:
+				   update_worker_table();
+				   break;
+			   default:
+				   break;
+			   }
+		   }
+		});
+	init(avoid_fastclick);
+}
+
+void ProductionSiteWindow::init(bool avoid_fastclick) {
+	BuildingWindow::init(avoid_fastclick);
+	const std::vector<Widelands::InputQueue*>& inputqueues = productionsite().inputqueues();
 
 	if (inputqueues.size()) {
 		// Add the wares tab
@@ -56,7 +74,8 @@
 
 		for (uint32_t i = 0; i < inputqueues.size(); ++i) {
 			prod_box->add(
-			   new InputQueueDisplay(prod_box, 0, 0, igbase(), ps, inputqueues[i]), UI::Align::kLeft);
+			   new InputQueueDisplay(prod_box, 0, 0, igbase(), productionsite(), inputqueues[i]),
+			   UI::Align::kLeft);
 		}
 
 		get_tabs()->add("wares", g_gr->images().get(pic_tab_wares), prod_box, _("Wares"));
@@ -99,6 +118,7 @@
 		   (ngettext("Worker", "Workers", productionsite().descr().nr_working_positions())));
 		update_worker_table();
 	}
+	think();
 }
 
 void ProductionSiteWindow::think() {
@@ -114,17 +134,6 @@
 	}
 }
 
-/*
-===============
-Create the production site information window.
-===============
-*/
-void ProductionSite::create_options_window(InteractiveGameBase& parent, UI::Window*& registry) {
-	ProductionSiteWindow* win = new ProductionSiteWindow(parent, *this, registry);
-	Building::options_window_connections.push_back(Building::workers_changed.connect(
-	   boost::bind(&ProductionSiteWindow::update_worker_table, boost::ref(*win))));
-}
-
 void ProductionSiteWindow::update_worker_table() {
 	if (worker_table_ == nullptr) {
 		return;

=== modified file 'src/wui/productionsitewindow.h'
--- src/wui/productionsitewindow.h	2017-01-25 18:55:59 +0000
+++ src/wui/productionsitewindow.h	2017-02-14 20:57:42 +0000
@@ -20,14 +20,17 @@
 #ifndef WL_WUI_PRODUCTIONSITEWINDOW_H
 #define WL_WUI_PRODUCTIONSITEWINDOW_H
 
+#include <memory>
+
 #include "logic/map_objects/tribes/productionsite.h"
 #include "ui_basic/table.h"
 #include "wui/buildingwindow.h"
 
 struct ProductionSiteWindow : public BuildingWindow {
 	ProductionSiteWindow(InteractiveGameBase& parent,
+	                     UI::UniqueWindow::Registry& reg,
 	                     Widelands::ProductionSite&,
-	                     UI::Window*& registry);
+	                     bool avoid_fastclick);
 
 	Widelands::ProductionSite& productionsite() {
 		return dynamic_cast<Widelands::ProductionSite&>(building());
@@ -35,12 +38,16 @@
 	void update_worker_table();
 
 protected:
+	void init(bool avoid_fastclick) override;
 	void think() override;
 	void evict_worker();
 
 private:
 	UI::Table<uintptr_t>* worker_table_;
 	UI::Box* worker_caps_;
+	std::unique_ptr<Notifications::Subscriber<Widelands::NoteBuilding>>
+	   productionsitenotes_subscriber_;
+	DISALLOW_COPY_AND_ASSIGN(ProductionSiteWindow);
 };
 
 #endif  // end of include guard: WL_WUI_PRODUCTIONSITEWINDOW_H

=== modified file 'src/wui/trainingsitewindow.cc'
--- src/wui/trainingsitewindow.cc	2017-01-25 18:55:59 +0000
+++ src/wui/trainingsitewindow.cc	2017-02-14 20:57:42 +0000
@@ -17,56 +17,36 @@
  *
  */
 
-#include "logic/map_objects/tribes/trainingsite.h"
+#include "wui/trainingsitewindow.h"
 
 #include "graphic/graphic.h"
 #include "ui_basic/tabpanel.h"
-#include "wui/productionsitewindow.h"
 #include "wui/soldiercapacitycontrol.h"
 #include "wui/soldierlist.h"
 
-using Widelands::TrainingSite;
-
 static char const* pic_tab_military = "images/wui/buildings/menu_tab_military.png";
 
-/**
- * Status window for \ref TrainingSite
- */
-struct TrainingSiteWindow : public ProductionSiteWindow {
-	TrainingSiteWindow(InteractiveGameBase& parent, TrainingSite&, UI::Window*& registry);
-
-	TrainingSite& trainingsite() {
-		return dynamic_cast<TrainingSite&>(building());
-	}
-
-protected:
-	void create_capsbuttons(UI::Box* buttons) override;
-};
+using namespace Widelands;
 
 /**
  * Create the \ref TrainingSite specific soldier list tab.
  */
 TrainingSiteWindow::TrainingSiteWindow(InteractiveGameBase& parent,
+                                       UI::UniqueWindow::Registry& reg,
                                        TrainingSite& ts,
-                                       UI::Window*& registry)
-   : ProductionSiteWindow(parent, ts, registry) {
+                                       bool avoid_fastclick)
+   : ProductionSiteWindow(parent, reg, ts, avoid_fastclick) {
+	init(avoid_fastclick);
+}
+
+void TrainingSiteWindow::init(bool avoid_fastclick) {
+	ProductionSiteWindow::init(avoid_fastclick);
 	get_tabs()->add("soldiers", g_gr->images().get(pic_tab_military),
-	                create_soldier_list(*get_tabs(), parent, trainingsite()),
+	                create_soldier_list(*get_tabs(), igbase(), trainingsite()),
 	                _("Soldiers in training"));
+	think();
 }
 
 void TrainingSiteWindow::create_capsbuttons(UI::Box* buttons) {
 	ProductionSiteWindow::create_capsbuttons(buttons);
 }
-
-/*
-===============
-Create the training site information window.
-===============
-*/
-void TrainingSite::create_options_window(InteractiveGameBase& plr, UI::Window*& registry) {
-	ProductionSiteWindow* win =
-	   dynamic_cast<ProductionSiteWindow*>(new TrainingSiteWindow(plr, *this, registry));
-	Building::options_window_connections.push_back(Building::workers_changed.connect(
-	   boost::bind(&ProductionSiteWindow::update_worker_table, boost::ref(*win))));
-}

=== added file 'src/wui/trainingsitewindow.h'
--- src/wui/trainingsitewindow.h	1970-01-01 00:00:00 +0000
+++ src/wui/trainingsitewindow.h	2017-02-14 20:57:42 +0000
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2002-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_TRAININGSITEWINDOW_H
+#define WL_WUI_TRAININGSITEWINDOW_H
+
+#include "logic/map_objects/tribes/trainingsite.h"
+#include "ui_basic/button.h"
+#include "wui/productionsitewindow.h"
+
+/**
+ * Status window for \ref TrainingSite
+ */
+struct TrainingSiteWindow : public ProductionSiteWindow {
+	TrainingSiteWindow(InteractiveGameBase& parent,
+	                   UI::UniqueWindow::Registry& reg,
+	                   Widelands::TrainingSite&,
+	                   bool avoid_fastclick);
+
+	Widelands::TrainingSite& trainingsite() {
+		return dynamic_cast<Widelands::TrainingSite&>(building());
+	}
+
+protected:
+	void init(bool avoid_fastclick) override;
+	void create_capsbuttons(UI::Box* buttons) override;
+
+private:
+	DISALLOW_COPY_AND_ASSIGN(TrainingSiteWindow);
+};
+
+#endif  // end of include guard: WL_WUI_TRAININGSITEWINDOW_H

=== modified file 'src/wui/warehousewindow.cc'
--- src/wui/warehousewindow.cc	2017-01-25 18:55:59 +0000
+++ src/wui/warehousewindow.cc	2017-02-14 20:57:42 +0000
@@ -17,10 +17,11 @@
  *
  */
 
-#include "logic/map_objects/tribes/warehouse.h"
+#include "wui/warehousewindow.h"
 
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
+#include "logic/map_objects/tribes/warehouse.h"
 #include "logic/player.h"
 #include "logic/playercommand.h"
 #include "ui_basic/tabpanel.h"
@@ -28,8 +29,6 @@
 #include "wui/portdockwaresdisplay.h"
 #include "wui/waresdisplay.h"
 
-using Widelands::Warehouse;
-
 static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";
 static const char pic_tab_workers[] = "images/wui/buildings/menu_tab_workers.png";
 static const char pic_tab_dock_wares[] = "images/wui/buildings/menu_tab_wares_dock.png";
@@ -40,6 +39,8 @@
 static const char pic_policy_dontstock[] = "images/wui/buildings/stock_policy_dontstock.png";
 static const char pic_policy_remove[] = "images/wui/buildings/stock_policy_remove.png";
 
+using namespace Widelands;
+
 /**
  * Extends the wares display to show and modify stock policy of items.
  */
@@ -169,21 +170,18 @@
 }
 
 /**
- * Status window for warehouses
- */
-struct WarehouseWindow : public BuildingWindow {
-	WarehouseWindow(InteractiveGameBase& parent, Warehouse&, UI::Window*& registry);
-
-	Warehouse& warehouse() {
-		return dynamic_cast<Warehouse&>(building());
-	}
-};
-
-/**
  * Create the tabs of a warehouse window.
  */
-WarehouseWindow::WarehouseWindow(InteractiveGameBase& parent, Warehouse& wh, UI::Window*& registry)
-   : BuildingWindow(parent, wh, registry) {
+WarehouseWindow::WarehouseWindow(InteractiveGameBase& parent,
+                                 UI::UniqueWindow::Registry& reg,
+                                 Warehouse& wh,
+                                 bool avoid_fastclick)
+   : BuildingWindow(parent, reg, wh, avoid_fastclick) {
+	init(avoid_fastclick);
+}
+
+void WarehouseWindow::init(bool avoid_fastclick) {
+	BuildingWindow::init(avoid_fastclick);
 	get_tabs()->add(
 	   "wares", g_gr->images().get(pic_tab_wares),
 	   new WarehouseWaresPanel(get_tabs(), Width, igbase(), warehouse(), Widelands::wwWARE),
@@ -193,7 +191,7 @@
 	   new WarehouseWaresPanel(get_tabs(), Width, igbase(), warehouse(), Widelands::wwWORKER),
 	   _("Workers"));
 
-	if (Widelands::PortDock* pd = wh.get_portdock()) {
+	if (Widelands::PortDock* pd = warehouse().get_portdock()) {
 		get_tabs()->add("dock_wares", g_gr->images().get(pic_tab_dock_wares),
 		                create_portdock_wares_display(get_tabs(), Width, *pd, Widelands::wwWARE),
 		                _("Wares waiting to be shipped"));
@@ -206,12 +204,5 @@
 			                _("Expedition"));
 		}
 	}
-}
-
-/**
- * Create the status window describing the warehouse.
- */
-void Widelands::Warehouse::create_options_window(InteractiveGameBase& parent,
-                                                 UI::Window*& registry) {
-	new WarehouseWindow(parent, *this, registry);
+	think();
 }

=== added file 'src/wui/warehousewindow.h'
--- src/wui/warehousewindow.h	1970-01-01 00:00:00 +0000
+++ src/wui/warehousewindow.h	2017-02-14 20:57:42 +0000
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2002-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_WAREHOUSEWINDOW_H
+#define WL_WUI_WAREHOUSEWINDOW_H
+
+#include "logic/map_objects/tribes/warehouse.h"
+#include "wui/buildingwindow.h"
+
+/**
+ * Status window for warehouses
+ */
+struct WarehouseWindow : public BuildingWindow {
+	WarehouseWindow(InteractiveGameBase& parent,
+	                UI::UniqueWindow::Registry& reg,
+	                Widelands::Warehouse&,
+	                bool avoid_fastclick);
+
+	Widelands::Warehouse& warehouse() {
+		return dynamic_cast<Widelands::Warehouse&>(building());
+	}
+
+protected:
+	void init(bool avoid_fastclick) override;
+
+private:
+	DISALLOW_COPY_AND_ASSIGN(WarehouseWindow);
+};
+
+#endif  // end of include guard: WL_WUI_WAREHOUSEWINDOW_H


Follow ups