← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/00_notification_framework into lp:widelands

 

SirVer has proposed merging lp:~widelands-dev/widelands/00_notification_framework into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/00_notification_framework/+merge/226975

Suggested commit message:

----
- Adds a new notifications library into src/notifications. This is a weakly coupled general purpose notification library that will help to reduce dependencies between the various Widelands libraries. Sending a note is cheap and defining one only requires a unique ID. I think this should be used for example to play sounds in the UI (Clicking a button will emit a 'Clicked' note, and library will play a sound for that). It also has tests.
- Removes the inheritance based logic/notifications.[h|cc] and ported all users to the new system. This removes a bunch of dicy multiple inheritance cases.
- Turns 'chat.[cc|h]' into a library. Also adds a wl_chat_ui library that can format chat messages. This needed some refactoring around how chat is handled.
----

-- 
https://code.launchpad.net/~widelands-dev/widelands/00_notification_framework/+merge/226975
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/00_notification_framework into lp:widelands.
=== modified file 'cmake/codecheck/rules/missing_padding'
--- cmake/codecheck/rules/missing_padding	2012-12-15 18:40:59 +0000
+++ cmake/codecheck/rules/missing_padding	2014-07-16 08:32:14 +0000
@@ -369,7 +369,6 @@
     "b > a",
     "AreaWatcher(const Player_Area<>);",
     "void func (Player_Area<Area>);",
-    "friend class NoteReceiver<T>;",
 
     'a %= a;',
 

=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt	2014-07-16 05:54:49 +0000
+++ src/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -65,6 +65,7 @@
 
 add_subdirectory(ai)
 add_subdirectory(base)
+add_subdirectory(chat)
 add_subdirectory(economy)
 add_subdirectory(editor)
 add_subdirectory(game_io)
@@ -73,6 +74,7 @@
 add_subdirectory(logic)
 add_subdirectory(map_io)
 add_subdirectory(network)
+add_subdirectory(notifications)
 add_subdirectory(profile)
 add_subdirectory(random)
 add_subdirectory(scripting)
@@ -86,8 +88,6 @@
 # library.
 wl_library(widelands_ball_of_mud
   SRCS
-    chat.cc
-    chat.h
     interval.h
     machdep.h
     wlapplication.cc
@@ -109,7 +109,6 @@
     logic
     logic_game_controller
     logic_game_settings
-    logic_notification
     map_io_map_loader
     network
     profile

=== modified file 'src/ai/CMakeLists.txt'
--- src/ai/CMakeLists.txt	2014-07-03 19:26:30 +0000
+++ src/ai/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -15,5 +15,4 @@
     profile
     base_i18n
     logic
-    logic_notification
 )

=== modified file 'src/ai/computer_player.cc'
--- src/ai/computer_player.cc	2014-07-03 19:08:41 +0000
+++ src/ai/computer_player.cc	2014-07-16 08:32:14 +0000
@@ -27,6 +27,7 @@
 {
 }
 
+Computer_Player::~Computer_Player() {}
 
 struct EmptyAI : Computer_Player {
 	EmptyAI(Widelands::Game & g, const Widelands::Player_Number pid)

=== modified file 'src/ai/computer_player.h'
--- src/ai/computer_player.h	2014-07-14 19:48:07 +0000
+++ src/ai/computer_player.h	2014-07-16 08:32:14 +0000
@@ -20,9 +20,15 @@
 #ifndef WL_AI_COMPUTER_PLAYER_H
 #define WL_AI_COMPUTER_PLAYER_H
 
+#include <string>
+#include <vector>
+
 #include "base/macros.h"
-#include "logic/game.h"
-#include "logic/notification.h"
+#include "logic/widelands.h"
+
+namespace Widelands {
+class Game;
+}  // namespace Widelands
 
 /**
  * The generic interface to AI instances, or "computer players".
@@ -30,17 +36,12 @@
  * Instances of actual AI implementation can be created via the
  * \ref Implementation interface.
  */
-struct Computer_Player :
-	Widelands::NoteReceiver<Widelands::NoteImmovable>,
-	Widelands::NoteReceiver<Widelands::NoteFieldPossession>
-{
+struct Computer_Player {
 	Computer_Player(Widelands::Game &, const Widelands::Player_Number);
+	virtual ~Computer_Player();
 
 	virtual void think () = 0;
 
-	virtual void receive(const Widelands::NoteImmovable &) override {}
-	virtual void receive(const Widelands::NoteFieldPossession     &) override {}
-
 	Widelands::Game & game() const {return m_game;}
 	Widelands::Player_Number player_number() {return m_player_number;}
 

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2014-07-15 10:02:22 +0000
+++ src/ai/defaultai.cc	2014-07-16 08:32:14 +0000
@@ -87,6 +87,24 @@
      military_under_constr_(0),
      military_last_dismantle_(0),
      military_last_build_(0) {
+
+	// Subscribe to NoteFieldPossession.
+	field_possession_subscriber_ =
+		Notifications::subscribe<NoteFieldPossession>([this](const NoteFieldPossession& note) {
+			if (note.ownership == NoteFieldPossession::Ownership::GAINED) {
+				unusable_fields.push_back(note.fc);
+			}
+		});
+
+   // Subscribe to NoteImmovables.
+	immovable_subscriber_ =
+		Notifications::subscribe<NoteImmovable>([this](const NoteImmovable& note) {
+			if (note.ownership == NoteImmovable::Ownership::GAINED) {
+				gain_immovable(*note.pi);
+			} else {
+				lose_immovable(*note.pi);
+			}
+		});
 }
 
 DefaultAI::~DefaultAI() {
@@ -198,21 +216,6 @@
 	}
 }
 
-/// called by Widelands game engine when an immovable changed
-void DefaultAI::receive(const NoteImmovable& note) {
-	if (note.lg == LOSE) {
-
-		lose_immovable(*note.pi);
-	} else
-		gain_immovable(*note.pi);
-}
-
-/// called by Widelands game engine when a field changed
-void DefaultAI::receive(const NoteFieldPossession& note) {
-	if (note.lg == GAIN)
-		unusable_fields.push_back(note.fc);
-}
-
 /**
  * Cares for all variables not initialised during construction
  *
@@ -221,8 +224,6 @@
  */
 void DefaultAI::late_initialization() {
 	player = game().get_player(player_number());
-	NoteReceiver<NoteImmovable>::connect(*player);
-	NoteReceiver<NoteFieldPossession>::connect(*player);
 	tribe = &player->tribe();
 	log("ComputerPlayer(%d): initializing (%u)\n", player_number(), type);
 	Ware_Index const nr_wares = tribe->get_nrwares();

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2014-07-05 16:41:51 +0000
+++ src/ai/defaultai.h	2014-07-16 08:32:14 +0000
@@ -21,10 +21,12 @@
 #define WL_AI_DEFAULTAI_H
 
 #include <map>
+#include <memory>
 
 #include "ai/ai_help_structs.h"
 #include "ai/computer_player.h"
 #include "base/i18n.h"
+#include "logic/immovable.h"
 
 namespace Widelands {
 struct Road;
@@ -70,9 +72,6 @@
 	~DefaultAI();
 	virtual void think() override;
 
-	virtual void receive(const Widelands::NoteImmovable&) override;
-	virtual void receive(const Widelands::NoteFieldPossession&) override;
-
 	enum {
 		AGGRESSIVE = 2,
 		NORMAL = 1,
@@ -209,6 +208,9 @@
 	uint16_t military_last_dismantle_;
 	int32_t military_last_build_;  // sometimes expansions just stops, this is time of last military
 	                               // building build
+
+	std::unique_ptr<Notifications::Subscriber<Widelands::NoteFieldPossession>> field_possession_subscriber_;
+	std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_;
 };
 
 #endif  // end of include guard: WL_AI_DEFAULTAI_H

=== added directory 'src/chat'
=== added file 'src/chat/CMakeLists.txt'
--- src/chat/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ src/chat/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -0,0 +1,7 @@
+wl_library(chat
+  SRCS
+    chat.h
+    chat.cc
+  DEPENDS
+    notifications
+)

=== renamed file 'src/chat.cc' => 'src/chat/chat.cc'
--- src/chat.cc	2014-07-05 14:22:44 +0000
+++ src/chat/chat.cc	2014-07-16 08:32:14 +0000
@@ -17,221 +17,6 @@
  *
  */
 
-#include "chat.h"
-
-#include "logic/constants.h"
-#include "logic/player.h"
-
-using namespace Widelands;
-
-std::string ChatMessage::toOldRichText() const
-{
-	std::string message = "<p font-color=#33ff33 font-size=9>";
-
-	// Escape richtext characters
-	// The goal of this code is two-fold:
-	//  1. Assuming an honest game host, we want to prevent the ability of
-	//     clients to use richtext.
-	//  2. Assuming a malicious host or meta server, we want to reduce the
-	//     likelihood that a bug in the richtext renderer can be exploited,
-	//     by restricting the set of allowed richtext commands.
-	//     Most notably, images are not allowed in richtext at all.
-	//
-	// Note that we do want host and meta server to send some richtext code,
-	// as the ability to send formatted commands is nice for the usability
-	// of meta server and dedicated servers, so we're treading a bit of a
-	// fine line here.
-	std::string sanitized;
-	for (std::string::size_type pos = 0; pos < msg.size(); ++pos) {
-		if (msg[pos] == '<') {
-			if (playern < 0) {
-				static const std::string good1 = "</p><p";
-				static const std::string good2 = "<br>";
-				if (!msg.compare(pos, good1.size(), good1)) {
-					std::string::size_type nextclose = msg.find('>', pos + good1.size());
-					if
-						(nextclose != std::string::npos &&
-						(nextclose == pos + good1.size() || msg[pos + good1.size()] == ' '))
-					{
-						sanitized += good1;
-						pos += good1.size() - 1;
-						continue;
-					}
-				} else if (!msg.compare(pos, good2.size(), good2)) {
-					sanitized += good2;
-					pos += good2.size() - 1;
-					continue;
-				}
-			}
-
-			sanitized += "&lt;";
-		} else {
-			sanitized += msg[pos];
-		}
-	}
-
-	// time calculation
-	char ts[13];
-	strftime(ts, sizeof(ts), "[%H:%M] </p>", localtime(&time));
-	message += ts;
-
-	message += "<p font-size=14 font-face=DejaVuSerif font-color=#";
-	message += color();
-
-	if (recipient.size() && sender.size()) {
-		// Personal message handling
-		if (sanitized.compare(0, 3, "/me")) {
-			message += " font-decoration=underline>";
-			message += sender;
-			message += " @ ";
-			message += recipient;
-			message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
-			message += sanitized;
-		} else {
-			message += ">@";
-			message += recipient;
-			message += " >> </p><p font-size=14";
-			message += " font-face=DejaVuSerif font-color=#";
-			message += color();
-			message += " font-style=italic> ";
-			message += sender;
-			message += sanitized.substr(3);
-		}
-	} else {
-		// Normal messages handling
-		if (not sanitized.compare(0, 3, "/me")) {
-			message += " font-style=italic>-> ";
-			if (sender.size())
-				message += sender;
-			else
-				message += "***";
-			message += sanitized.substr(3);
-		} else if (sender.size()) {
-			message += " font-decoration=underline>";
-			message += sender;
-			message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
-			message += sanitized;
-		} else {
-			message += " font-weight=bold>*** ";
-			message += sanitized;
-		}
-	}
-
-	// return the formated message
-	return message + "<br></p>";
-}
-
-std::string ChatMessage::toPrintable() const
-{
-	std::string message = "<p><font color=33ff33 size=9>";
-
-	// Escape richtext characters
-	// The goal of this code is two-fold:
-	//  1. Assuming an honest game host, we want to prevent the ability of
-	//     clients to use richtext.
-	//  2. Assuming a malicious host or meta server, we want to reduce the
-	//     likelihood that a bug in the richtext renderer can be exploited,
-	//     by restricting the set of allowed richtext commands.
-	//     Most notably, images are not allowed in richtext at all.
-	//
-	// Note that we do want host and meta server to send some richtext code,
-	// as the ability to send formatted commands is nice for the usability
-	// of meta server and dedicated servers, so we're treading a bit of a
-	// fine line here.
-	std::string sanitized;
-	for (std::string::size_type pos = 0; pos < msg.size(); ++pos) {
-		if (msg[pos] == '<') {
-			if (playern < 0) {
-				static const std::string good1 = "</p><p";
-				static const std::string good2 = "<br>";
-				if (!msg.compare(pos, good1.size(), good1)) {
-					std::string::size_type nextclose = msg.find('>', pos + good1.size());
-					if
-						(nextclose != std::string::npos &&
-						(nextclose == pos + good1.size() || msg[pos + good1.size()] == ' '))
-					{
-						sanitized += good1;
-						pos += good1.size() - 1;
-						continue;
-					}
-				} else if (!msg.compare(pos, good2.size(), good2)) {
-					sanitized += good2;
-					pos += good2.size() - 1;
-					continue;
-				}
-			}
-
-			sanitized += "\\<";
-		} else {
-			sanitized += msg[pos];
-		}
-	}
-
-	// time calculation
-	char ts[13];
-	strftime(ts, sizeof(ts), "[%H:%M] ", localtime(&time));
-	message += ts;
-
-	message += "</font><font size=14 face=DejaVuSerif color=";
-	message += color();
-
-	if (recipient.size() && sender.size()) {
-		// Personal message handling
-		if (sanitized.compare(0, 3, "/me")) {
-			message += " bold=1>";
-			message += sender;
-			message += " @ ";
-			message += recipient;
-			message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
-			message += sanitized;
-		} else {
-			message += ">@";
-			message += recipient;
-			message += " \\> </font><font size=14";
-			message += " face=DejaVuSerif color=";
-			message += color();
-			message += " italic=1 shadow=1> ";
-			message += sender;
-			message += sanitized.substr(3);
-		}
-	} else {
-		// Normal messages handling
-		if (not sanitized.compare(0, 3, "/me")) {
-			message += " italic=1>-\\> ";
-			if (sender.size())
-				message += sender;
-			else
-				message += "***";
-			message += sanitized.substr(3);
-		} else if (sender.size()) {
-			message += " bold=1>";
-			message += sender;
-			message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
-			message += sanitized;
-		} else {
-			message += " bold=1>*** ";
-			message += sanitized;
-		}
-	}
-
-	// return the formated message
-	return message + "</font><br></p>";
-}
-
-
-
-std::string ChatMessage::toPlainString() const
-{
-	return sender + ": " + msg;
-}
-
-std::string ChatMessage::color() const
-{
-	if ((playern >= 0) && playern < MAX_PLAYERS) {
-		const RGBColor & clr = Player::Colors[playern];
-		char buf[sizeof("ffffff")];
-		snprintf(buf, sizeof(buf), "%.2x%.2x%.2x", clr.r, clr.g, clr.b);
-		return buf;
-	}
-	return "999999";
-}
+#include "chat/chat.h"
+
+ChatProvider::~ChatProvider() {}

=== renamed file 'src/chat.h' => 'src/chat/chat.h'
--- src/chat.h	2014-07-05 16:41:51 +0000
+++ src/chat/chat.h	2014-07-16 08:32:14 +0000
@@ -17,110 +17,64 @@
  *
  */
 
-#ifndef WL_CHAT_H
-#define WL_CHAT_H
+#ifndef WL_CHAT_CHAT_H
+#define WL_CHAT_CHAT_H
 
 #include <ctime>
 #include <string>
-
-#include "logic/notification.h"
-
-/**
- * Represents one chat message.
- */
+#include <vector>
+
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
+
+// A chat message as received in game.
 struct ChatMessage {
-	/**
-	 * The (real-)time at which the message was received.
-	 */
+	CAN_BE_SEND_AS_NOTE(NoteId::ChatMessage)
+
+	// The (real-)time at which the message was received.
 	time_t time;
 
-	/**
-	 * The playercolor. Used to colorize the senders name;
-	 * negative numbers indicate system messages for which richtext is
-	 * allowed.
-	 */
+	// The playercolor. Used to colorize the senders name; negative numbers
+	// indicate system messages for which richtext is allowed.
 	int16_t playern;
 
-	/**
-	 * A string identifying the sender of the message.
-	 *
-	 * This string is empty for system-generated messages.
-	 *
-	 * \note This is a string instead of an ID because the backlog of
-	 * chat messages might contain chat from a player who has since left
-	 * the game.
-	 */
+	// A string identifying the sender of the message.
+	// This string is empty for system-generated messages.
+	// This is a string instead of an ID because the backlog of
+	// chat messages might contain chat from a player who has since left
+	// the game.
 	std::string sender;
 
-	/**
-	 * A string identifying the recipient of the message.
-	 *
-	 * This string should only be filled for personal messages.
-	 *
-	 * \note This is a string instead of an ID because the backlog of
-	 * chat messages might contain chat from a player who has since left
-	 * the game.
-	 */
+	// A string identifying the recipient of the message. This string should
+	// only be filled for personal messages. This is a string instead of an ID
+	// because the backlog of chat messages might contain chat from a player who
+	// has since left the game.
 	std::string recipient;
 
-	/**
-	 * The actual chat message
-	 */
+	// The actual chat message
 	std::string msg;
-
-
-	/**
-	 * \return a richtext string that can be displayed to the user.
-	 */
-	std::string toPrintable() const;
-	std::string toOldRichText() const;
-
-	/**
-	 * \return a plain string containing the sender + message.
-	 */
-	std::string toPlainString() const;
-
-
-	/**
-	 * \returns the color of the sender
-	 */
-	std::string color() const;
 };
 
-
-/**
- * Provides the chatting interface during a game.
- *
- * Use this interface to send chat messages and to access the list of
- * received chat messages. Note that this class is a
- * Widelands::NoteSender<ChatMessage> and sends a notification every
- * time a new message is received.
- */
-struct ChatProvider : public Widelands::NoteSender<ChatMessage> {
-	virtual ~ChatProvider() {}
-
-	/**
-	 * Send the given chat message.
-	 *
-	 * The message may or may not appear in subsequent calls to \ref getMessages.
-	 */
+// Sends chat messages and owns the list of received chat messages.
+// Base classes must broadcast a ChatMessage as notification when a
+// new message is received.
+struct ChatProvider {
+	virtual ~ChatProvider();
+
+	// Send the given chat message. The message may or may not
+	// appear in subsequent calls to \ref getMessages.
 	virtual void send(const std::string &) = 0;
 
-	/**
-	 * \return a (chronological) list of received chat messages.
-	 * This list need not be stable or monotonic. In other words,
-	 * subsequent calls to this functions may return a smaller or
-	 * greater number of chat messages.
-	 */
-	virtual const std::vector<ChatMessage> & getMessages() const = 0;
+	// \return a (chronological) list of received chat messages.
+	// This list need not be stable or monotonic. In other words,
+	// subsequent calls to this functions may return a smaller or
+	// greater number of chat messages.
+	virtual const std::vector<ChatMessage>& getMessages() const = 0;
 
 	// reimplemented e.g. in internet_gaming to silence the chat if in game.
+	// TODO(sirver): this does not belong here. The receiver of the
+	// notifications should deal with this.
 	virtual bool sound_off() {return false;}
-
-protected:
-	void send(const ChatMessage & c) {
-		Widelands::NoteSender<ChatMessage>::send(c);
-	}
 };
 
-#endif  // end of include guard: WL_CHAT_H
+#endif  // end of include guard:

=== modified file 'src/economy/router.h'
--- src/economy/router.h	2014-07-05 16:41:51 +0000
+++ src/economy/router.h	2014-07-16 08:32:14 +0000
@@ -27,8 +27,8 @@
 #include "logic/wareworker.h"
 
 namespace Widelands {
+class ITransportCostCalculator;
 struct IRoute;
-struct ITransportCostCalculator;
 struct RoutingNode;
 
 /**

=== modified file 'src/editor/map_generator.cc'
--- src/editor/map_generator.cc	2014-07-03 19:08:41 +0000
+++ src/editor/map_generator.cc	2014-07-16 08:32:14 +0000
@@ -414,8 +414,7 @@
 		delete[] values;
 		throw;
 	}
-
-	return nullptr; // Should not be reached
+	// Never here.
 }
 
 

=== modified file 'src/io/CMakeLists.txt'
--- src/io/CMakeLists.txt	2014-07-14 10:45:44 +0000
+++ src/io/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -54,7 +54,7 @@
   DEPENDS
     base_i18n
     base_log
+    chat
     io_fileread
     io_filesystem
-    widelands_ball_of_mud
 )

=== modified file 'src/io/dedicated_log.h'
--- src/io/dedicated_log.h	2014-07-05 16:41:51 +0000
+++ src/io/dedicated_log.h	2014-07-16 08:32:14 +0000
@@ -21,7 +21,7 @@
 #define WL_IO_DEDICATED_LOG_H
 
 #include "base/log.h"
-#include "chat.h"
+#include "chat/chat.h"
 #include "io/filesystem/disk_filesystem.h"
 #include "io/filewrite.h"
 

=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt	2014-07-14 10:45:44 +0000
+++ src/logic/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -1,12 +1,3 @@
-wl_library(logic_notification
-  SRCS
-    notification.h
-    notification.cc
-  DEPENDS
-    logic_widelands_geometry
-)
-
-
 wl_library(logic_widelands_geometry
   SRCS
     widelands_geometry.cc
@@ -229,11 +220,11 @@
     io_stream
     logic_game_controller
     logic_game_settings
-    logic_notification
     logic_widelands_geometry
     map_io
     map_io_map_loader
     network
+    notifications
     profile
     random
     scripting

=== modified file 'src/logic/editor_game_base.cc'
--- src/logic/editor_game_base.cc	2014-07-05 14:22:44 +0000
+++ src/logic/editor_game_base.cc	2014-07-16 08:32:14 +0000
@@ -122,25 +122,6 @@
 	return world_.get();
 }
 
-void Editor_Game_Base::receive(const NoteImmovable & note)
-{
-	note.pi->owner().receive(note);
-}
-
-void Editor_Game_Base::receive(const NoteFieldPossession & note)
-{
-	get_player(note.fc.field->get_owned_by())->receive(note);
-}
-
-void Editor_Game_Base::receive(const NoteFieldTransformed & note)
-{
-	Widelands::Map_Index const i = note.fc.field - &(*map_)[0];
-
-	iterate_players_existing(p, map_->get_nrplayers(), *this, plr)
-		if (plr->vision(i) > 1) // player currently sees field?
-			plr->rediscover_node(*map_, (*map_)[0], note.fc);
-}
-
 Interactive_GameBase* Editor_Game_Base::get_igbase()
 {
 	return dynamic_cast<Interactive_GameBase *>(get_ibase());
@@ -237,8 +218,6 @@
 	delete map_;
 
 	map_ = new_map;
-
-	NoteReceiver<NoteFieldTransformed>::connect(*map_);
 }
 
 
@@ -269,7 +248,7 @@
 
 		if
 			(pid <= MAX_PLAYERS
-			 or
+			 ||
 			 not dynamic_cast<const Game *>(this))
 		{ // if this is editor, load the tribe anyways
 			// the tribe is used, postload it
@@ -533,12 +512,12 @@
 	assert(&first_field <= f.field);
 	assert                (f.field < &first_field + m.max_index());
 	assert
-		(direction == Road_SouthWest or
-		 direction == Road_SouthEast or
+		(direction == Road_SouthWest ||
+		 direction == Road_SouthEast ||
 		 direction == Road_East);
 	assert
-		(roadtype == Road_None or roadtype == Road_Normal or
-		 roadtype == Road_Busy or roadtype == Road_Water);
+		(roadtype == Road_None || roadtype == Road_Normal ||
+		 roadtype == Road_Busy || roadtype == Road_Water);
 
 	if (f.field->get_road(direction) == roadtype)
 		return;
@@ -571,7 +550,7 @@
 		Player::Field & player_field = (&first_player_field)[i];
 		if
 			(1 < player_field                      .vision
-				or
+				||
 				1 < (&first_player_field)[neighbour_i].vision)
 		{
 			player_field.roads &= ~mask;
@@ -641,6 +620,31 @@
 	cleanup_playerimmovables_area(player_area);
 }
 
+void Editor_Game_Base::change_field_owner(const FCoords& fc, Player_Number const new_owner) {
+	const Field & first_field = map()[0];
+
+	Player_Number const old_owner = fc.field->get_owned_by();
+	if (old_owner == new_owner) {
+		return;
+	}
+
+	if (old_owner) {
+		Notifications::publish(
+		   NoteFieldPossession(fc, NoteFieldPossession::Ownership::LOST, get_player(old_owner)));
+	}
+
+	fc.field->set_owned_by(new_owner);
+
+	// TODO: the player should do this when it gets the NoteFieldPossession.
+	// This means also sending a note when new_player = 0, i.e. the field is no
+	// longer owned.
+	inform_players_about_ownership(fc.field - &first_field, new_owner);
+
+	if (new_owner) {
+		Notifications::publish(
+		   NoteFieldPossession(fc, NoteFieldPossession::Ownership::GAINED, get_player(new_owner)));
+	}
+}
 
 void Editor_Game_Base::conquer_area_no_building
 	(Player_Area<Area<FCoords> > player_area)
@@ -656,15 +660,7 @@
 	assert    (player_area.player_number <= map().get_nrplayers());
 	MapRegion<Area<FCoords> > mr(map(), player_area);
 	do {
-		Player_Number const owner = mr.location().field->get_owned_by();
-		if (owner != player_area.player_number) {
-			if (owner)
-				receive(NoteFieldPossession(mr.location(), LOSE));
-			mr.location().field->set_owned_by(player_area.player_number);
-			inform_players_about_ownership
-				(mr.location().field - &first_field, player_area.player_number);
-			receive (NoteFieldPossession(mr.location(), GAIN));
-		}
+		change_field_owner(mr.location(), player_area.player_number);
 	} while (mr.advance(map()));
 
 	//  This must reach one step beyond the conquered area to adjust the borders
@@ -699,7 +695,7 @@
 	assert    (player_area.player_number <= map().get_nrplayers());
 	assert    (preferred_player          <= map().get_nrplayers());
 	assert(preferred_player != player_area.player_number);
-	assert(not conquer or not preferred_player);
+	assert(not conquer || not preferred_player);
 	Player & conquering_player = player(player_area.player_number);
 	MapRegion<Area<FCoords> > mr(map(), player_area);
 	do {
@@ -711,33 +707,22 @@
 		Player_Number const owner = mr.location().field->get_owned_by();
 		if (conquer) {
 			//  adds the influence
-			Military_Influence new_influence_modified =
-				conquering_player.military_influence(index) += influence;
-			if (owner and not conquer_guarded_location_by_superior_influence)
+			Military_Influence new_influence_modified = conquering_player.military_influence(index) +=
+			   influence;
+			if (owner && not conquer_guarded_location_by_superior_influence)
 				new_influence_modified = 1;
-			if
-				(not owner
-				 or
-				 player(owner).military_influence(index) < new_influence_modified)
-			{
-				if (owner)
-					receive(NoteFieldPossession(mr.location(), LOSE));
-				mr.location().field->set_owned_by(player_area.player_number);
-				inform_players_about_ownership(index, player_area.player_number);
-				receive (NoteFieldPossession(mr.location(), GAIN));
+			if (!owner || player(owner).military_influence(index) < new_influence_modified) {
+				change_field_owner(mr.location(), player_area.player_number);
 			}
-		} else if
-			(not (conquering_player.military_influence(index) -= influence)
-			 and
-			 owner == player_area.player_number)
-		{
+		} else if (not(conquering_player.military_influence(index) -= influence) &&
+		           owner == player_area.player_number) {
 			//  The player completely lost influence over the location, which he
 			//  owned. Now we must see if some other player has influence and if
 			//  so, transfer the ownership to that player.
 			Player_Number best_player;
 			if
 				(preferred_player
-				 and
+				 &&
 				 player(preferred_player).military_influence(index))
 				best_player = preferred_player;
 			else {
@@ -761,17 +746,13 @@
 				}
 			}
 			if (best_player != player_area.player_number) {
-				receive (NoteFieldPossession(mr.location(), LOSE));
-				mr.location().field->set_owned_by (best_player);
-				inform_players_about_ownership(index, best_player);
-				if (best_player)
-					receive (NoteFieldPossession(mr.location(), GAIN));
+				change_field_owner(mr.location(), best_player);
 			}
 		}
 	} while (mr.advance(map()));
 
-	//  This must reach one step beyond the conquered area to adjust the borders
-	//  of neighbour players.
+	// This must reach one step beyond the conquered area to adjust the borders
+	// of neighbour players.
 	++player_area.radius;
 	map().recalc_for_field_area(world(), player_area);
 }

=== modified file 'src/logic/editor_game_base.h'
--- src/logic/editor_game_base.h	2014-07-15 05:12:37 +0000
+++ src/logic/editor_game_base.h	2014-07-16 08:32:14 +0000
@@ -29,8 +29,8 @@
 #include "logic/bob.h"
 #include "logic/building.h"
 #include "logic/map.h"
-#include "logic/notification.h"
 #include "logic/player_area.h"
+#include "notifications/notifications.h"
 
 namespace UI {struct ProgressWindow;}
 struct Fullscreen_Menu_LaunchGame;
@@ -53,98 +53,128 @@
 struct Flag;
 struct AttackController;
 
-class Editor_Game_Base :
-	NoteReceiver<NoteImmovable>,
-	NoteReceiver<NoteFieldPossession>,
-	NoteReceiver<NoteFieldTransformed>
-{
+struct NoteFieldPossession {
+	CAN_BE_SEND_AS_NOTE(NoteId::FieldPossession)
+
+	// Has this been lost or gained?
+	enum class Ownership {LOST, GAINED};
+	Ownership ownership;
+
+	// The field that has been lost/gained.
+	FCoords fc;
+
+	// The player that has lost or gained this field.
+	Player * player;
+
+	NoteFieldPossession(const FCoords& init_fc, Ownership const init_ownership, Player* init_player)
+	   : ownership(init_ownership), fc(init_fc), player(init_player) {
+	}
+};
+
+class Editor_Game_Base {
 public:
 	friend class Interactive_Base;
 	friend struct Fullscreen_Menu_LaunchGame;
 	friend struct Game_Game_Class_Data_Packet;
 
-	Editor_Game_Base(LuaInterface * lua);
+	Editor_Game_Base(LuaInterface* lua);
 	virtual ~Editor_Game_Base();
 
-	void set_map(Map *);
-	Map & map() const {return *map_;}
-	Map * get_map() {return map_;}
-	Map & get_map() const {return *map_;}
-	const Object_Manager & objects() const {return objects_;}
-	Object_Manager       & objects()       {return objects_;}
+	void set_map(Map*);
+	// TODO(sirver): this should just be const Map& map() and Map* mutable_map().
+	Map& map() const {
+		return *map_;
+	}
+	Map* get_map() {
+		return map_;
+	}
+	Map& get_map() const {
+		return *map_;
+	}
+	const Object_Manager& objects() const {
+		return objects_;
+	}
+	Object_Manager& objects() {
+		return objects_;
+	}
 
 	// logic handler func
 	virtual void think();
 
 	// Player commands
 	void remove_player(Player_Number);
-	Player * add_player
-		(Player_Number,
-		 uint8_t             initialization_index,
-		 const std::string & tribe,
-		 const std::string & name,
-		 TeamNumber team = 0);
-	Player * get_player(int32_t n) const;
-	Player & player(int32_t n) const;
-	virtual Player * get_safe_player(Player_Number);
+	Player* add_player(Player_Number,
+	                   uint8_t initialization_index,
+	                   const std::string& tribe,
+	                   const std::string& name,
+	                   TeamNumber team = 0);
+	Player* get_player(int32_t n) const;
+	Player& player(int32_t n) const;
+	virtual Player* get_safe_player(Player_Number);
 
 	// loading stuff
 	void allocate_player_maps();
 	virtual void postload();
-	void load_graphics(UI::ProgressWindow & loader_ui);
+	void load_graphics(UI::ProgressWindow& loader_ui);
 	virtual void cleanup_for_load();
 
 	void set_road(FCoords, uint8_t direction, uint8_t roadtype);
 
 	// warping stuff. instantly creating map_objects
-	Building & warp_building
-		(Coords, Player_Number, Building_Index,
-		Building::FormerBuildings former_buildings = Building::FormerBuildings());
-	Building & warp_constructionsite
-		(Coords, Player_Number, Building_Index, bool loading = false,
-		 Building::FormerBuildings former_buildings = Building::FormerBuildings());
-	Building & warp_dismantlesite
-		(Coords, Player_Number, bool loading = false,
-		Building::FormerBuildings former_buildings = Building::FormerBuildings());
-	Bob & create_bob(Coords, const BobDescr &, Player * owner = nullptr);
-	Bob & create_bob
-		(Coords, int, Tribe_Descr const * const = nullptr, Player * owner = nullptr);
-	Bob & create_bob
-		(Coords, const std::string & name, Tribe_Descr const * const = nullptr, Player * owner = nullptr);
-	Immovable & create_immovable(Coords, uint32_t idx, Tribe_Descr const *);
-	Immovable & create_immovable
-		(Coords, const std::string & name, Tribe_Descr const *);
+	Building&
+	warp_building(Coords,
+	              Player_Number,
+	              Building_Index,
+	              Building::FormerBuildings former_buildings = Building::FormerBuildings());
+	Building&
+	warp_constructionsite(Coords,
+	                      Player_Number,
+	                      Building_Index,
+	                      bool loading = false,
+	                      Building::FormerBuildings former_buildings = Building::FormerBuildings());
+	Building&
+	warp_dismantlesite(Coords,
+	                   Player_Number,
+	                   bool loading = false,
+	                   Building::FormerBuildings former_buildings = Building::FormerBuildings());
+	Bob& create_bob(Coords, const BobDescr&, Player* owner = nullptr);
+	Bob& create_bob(Coords, int, Tribe_Descr const* const = nullptr, Player* owner = nullptr);
+	Bob& create_bob(Coords,
+	                const std::string& name,
+	                Tribe_Descr const* const = nullptr,
+	                Player* owner = nullptr);
+	Immovable& create_immovable(Coords, uint32_t idx, Tribe_Descr const*);
+	Immovable& create_immovable(Coords, const std::string& name, Tribe_Descr const*);
 
-	int32_t get_gametime() const {return gametime_;}
-	Interactive_Base * get_ibase() const {return ibase_;}
+	int32_t get_gametime() const {
+		return gametime_;
+	}
+	Interactive_Base* get_ibase() const {
+		return ibase_;
+	}
 
 	// safe system for storing pointers to non-Map_Object C++ objects
 	// unlike objects in the Object_Manager, these pointers need not be
 	// synchronized across the network, and they are not saved in savegames
-	uint32_t add_trackpointer(void *);
-	void * get_trackpointer(uint32_t serial);
+	uint32_t add_trackpointer(void*);
+	void* get_trackpointer(uint32_t serial);
 	void remove_trackpointer(uint32_t serial);
 
 	// Manually load a tribe into memory. Used by the editor
-	const Tribe_Descr & manually_load_tribe(const std::string & tribe);
-	const Tribe_Descr & manually_load_tribe(Player_Number const p) {
+	const Tribe_Descr& manually_load_tribe(const std::string& tribe);
+	const Tribe_Descr& manually_load_tribe(Player_Number const p) {
 		return manually_load_tribe(map().get_scenario_player_tribe(p));
 	}
 	// Get a tribe from the loaded list, when known or nullptr.
-	Tribe_Descr const * get_tribe(const std::string & name) const;
+	Tribe_Descr const* get_tribe(const std::string& name) const;
 
 	void inform_players_about_ownership(Map_Index, Player_Number);
-	void inform_players_about_immovable(Map_Index, Map_Object_Descr const *);
-	void inform_players_about_road     (FCoords,   Map_Object_Descr const *);
-
-	void unconquer_area
-		(Player_Area<Area<FCoords> >, Player_Number destroying_player = 0);
-	void   conquer_area            (Player_Area<Area<FCoords> >);
-	void   conquer_area_no_building(Player_Area<Area<FCoords> > const);
-
-	void receive(const NoteImmovable &) override;
-	void receive(const NoteFieldPossession     &) override;
-	void receive(const NoteFieldTransformed    &) override;
+	void inform_players_about_immovable(Map_Index, Map_Object_Descr const*);
+	void inform_players_about_road(FCoords, Map_Object_Descr const*);
+
+	void unconquer_area(Player_Area<Area<FCoords>>, Player_Number destroying_player = 0);
+	void conquer_area(Player_Area<Area<FCoords>>);
+	void conquer_area_no_building(Player_Area<Area<FCoords>> const);
 
 	void cleanup_objects() {
 		objects().cleanup(*this);
@@ -152,15 +182,23 @@
 
 	// next function is used to update the current gametime,
 	// for queue runs e.g.
-	int32_t & get_game_time_pointer() {return gametime_;}
-	void set_ibase(Interactive_Base * const b) {ibase_ = b;}
+	int32_t& get_game_time_pointer() {
+		return gametime_;
+	}
+	void set_ibase(Interactive_Base* const b) {
+		ibase_ = b;
+	}
 
 	/// Lua frontend, used to run Lua scripts
-	LuaInterface & lua() {return *lua_;}
-
-	Players_Manager* player_manager() {return player_manager_.get();}
-
-	Interactive_GameBase * get_igbase();
+	LuaInterface& lua() {
+		return *lua_;
+	}
+
+	Players_Manager* player_manager() {
+		return player_manager_.get();
+	}
+
+	Interactive_GameBase* get_igbase();
 
 	// Returns the world.
 	const World& world() const;
@@ -168,21 +206,6 @@
 	// Returns the world that can be modified. Prefer world() whenever possible.
 	World* mutable_world();
 
-private:
-	// FIXME -- SDL returns time as uint32. Why do I have int32 ? Please comment or change this to uint32.
-	int32_t gametime_;
-	Object_Manager objects_;
-
-	std::unique_ptr<LuaInterface> lua_;
-	std::unique_ptr<Players_Manager> player_manager_;
-
-	std::unique_ptr<World> world_;
-	Interactive_Base* ibase_;
-	Map* map_;
-
-	uint32_t lasttrackserial_;
-	std::map<uint32_t, void*> trackpointers_;
-
 protected:
 	typedef std::vector<Tribe_Descr*> Tribe_Vector;
 	Tribe_Vector tribes_;
@@ -212,30 +235,47 @@
 	///  attacking) conquer a location even if another player already owns and
 	///  covers the location with a militarysite, if the conquering player's
 	///  influence becomes greater than the owner's influence.
-	virtual void do_conquer_area
-		(Player_Area<Area<FCoords> > player_area,
-		 bool          conquer,
-		 Player_Number preferred_player                               = 0,
-		 bool          neutral_when_no_influence                      = false,
-		 bool          neutral_when_competing_influence               = false,
-		 bool          conquer_guarded_location_by_superior_influence = false);
-	void cleanup_playerimmovables_area(Player_Area<Area<FCoords> >);
-
-	DISALLOW_COPY_AND_ASSIGN(Editor_Game_Base);
-};
-
-#define iterate_players_existing(p, nr_players, egbase, player)               \
-   iterate_player_numbers(p, nr_players)                                      \
-      if (Widelands::Player * const player = (egbase).get_player(p))          \
-
-#define iterate_players_existing_novar(p, nr_players, egbase)                 \
-   iterate_player_numbers(p, nr_players)                                      \
-      if ((egbase).get_player(p))                                             \
-
-#define iterate_players_existing_const(p, nr_players, egbase, player)         \
-   iterate_player_numbers(p, nr_players)                                      \
-      if (Widelands::Player const * const player = (egbase).get_player(p))    \
-
+	virtual void do_conquer_area(Player_Area<Area<FCoords>> player_area,
+	                             bool conquer,
+	                             Player_Number preferred_player = 0,
+	                             bool neutral_when_no_influence = false,
+	                             bool neutral_when_competing_influence = false,
+	                             bool conquer_guarded_location_by_superior_influence = false);
+	void cleanup_playerimmovables_area(Player_Area<Area<FCoords>>);
+
+	// Changes the owner of 'fc' from the current player to the new player and
+	// sends notifications about this.
+	void change_field_owner(const FCoords& fc, Player_Number new_owner);
+
+	// FIXME -- SDL returns time as uint32. Why do I have int32 ? Please comment or change this to
+	// uint32.
+	int32_t gametime_;
+	Object_Manager objects_;
+
+	std::unique_ptr<LuaInterface> lua_;
+	std::unique_ptr<Players_Manager> player_manager_;
+
+	std::unique_ptr<World> world_;
+	Interactive_Base* ibase_;
+	Map* map_;
+
+	uint32_t lasttrackserial_;
+	std::map<uint32_t, void*> trackpointers_;
+
+
+		DISALLOW_COPY_AND_ASSIGN(Editor_Game_Base);
+	};
+
+#define iterate_players_existing(p, nr_players, egbase, player)                                    \
+	iterate_player_numbers(                                                                         \
+	   p, nr_players) if (Widelands::Player* const player = (egbase).get_player(p))
+
+#define iterate_players_existing_novar(p, nr_players, egbase)                                      \
+	iterate_player_numbers(p, nr_players) if ((egbase).get_player(p))
+
+#define iterate_players_existing_const(p, nr_players, egbase, player)                              \
+	iterate_player_numbers(                                                                         \
+	   p, nr_players) if (Widelands::Player const* const player = (egbase).get_player(p))
 }
 
 #endif  // end of include guard: WL_LOGIC_EDITOR_GAME_BASE_H

=== modified file 'src/logic/immovable.cc'
--- src/logic/immovable.cc	2014-07-15 10:02:22 +0000
+++ src/logic/immovable.cc	2014-07-16 08:32:14 +0000
@@ -50,6 +50,7 @@
 #include "logic/worker.h"
 #include "logic/world/world.h"
 #include "map_io/one_world_legacy_lookup_table.h"
+#include "notifications/notifications.h"
 #include "profile/profile.h"
 #include "scripting/lua_table.h"
 #include "sound/sound_handler.h"
@@ -1388,9 +1389,11 @@
  * Set the immovable's owner. Currently, it can only be set once.
 */
 void PlayerImmovable::set_owner(Player * const new_owner) {
+	assert(m_owner == nullptr);
+
 	m_owner = new_owner;
 
-	m_owner->egbase().receive(NoteImmovable(this, GAIN));
+	Notifications::publish(NoteImmovable(this, NoteImmovable::Ownership::GAINED));
 }
 
 /**
@@ -1409,8 +1412,7 @@
 	while (!m_workers.empty())
 		m_workers[0]->set_location(nullptr);
 
-	if (m_owner)
-		m_owner->egbase().receive(NoteImmovable(this, LOSE));
+	Notifications::publish(NoteImmovable(this, NoteImmovable::Ownership::LOST));
 
 	BaseImmovable::cleanup(egbase);
 }

=== modified file 'src/logic/immovable.h'
--- src/logic/immovable.h	2014-07-15 10:02:22 +0000
+++ src/logic/immovable.h	2014-07-16 08:32:14 +0000
@@ -26,6 +26,8 @@
 #include "logic/buildcost.h"
 #include "logic/instances.h"
 #include "logic/widelands_geometry.h"
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
 
 class LuaTable;
 class OneWorldLegacyLookupTable;
@@ -40,8 +42,22 @@
 class Worker;
 class World;
 struct Flag;
+struct PlayerImmovable;
 struct Tribe_Descr;
 
+struct NoteImmovable {
+	CAN_BE_SEND_AS_NOTE(NoteId::Immovable)
+
+	PlayerImmovable* pi;
+
+	enum class Ownership {LOST, GAINED};
+	Ownership ownership;
+
+	NoteImmovable(PlayerImmovable* const init_pi, Ownership const init_ownership)
+	   : pi(init_pi), ownership(init_ownership) {
+	}
+};
+
 /**
  * BaseImmovable is the base for all non-moving objects (immovables such as
  * trees, buildings, flags, roads).

=== modified file 'src/logic/map.cc'
--- src/logic/map.cc	2014-07-03 19:26:30 +0000
+++ src/logic/map.cc	2014-07-16 08:32:14 +0000
@@ -46,6 +46,7 @@
 #include "logic/world/world.h"
 #include "map_io/s2map.h"
 #include "map_io/widelands_map_loader.h"
+#include "notifications/notifications.h"
 #include "wui/overlay_manager.h"
 
 namespace Widelands {
@@ -1846,7 +1847,7 @@
 {
 	c.field->set_terrain(c.t, terrain);
 
-	NoteSender<NoteFieldTransformed>::send(NoteFieldTransformed(c));
+	Notifications::publish(NoteFieldTransformed(c, c.field - &m_fields[0]));
 
 	recalc_for_field_area(world, Area<FCoords>(c, 2));
 

=== modified file 'src/logic/map.h'
--- src/logic/map.h	2014-07-05 16:41:51 +0000
+++ src/logic/map.h	2014-07-16 08:32:14 +0000
@@ -32,10 +32,11 @@
 #include "interval.h"
 #include "logic/field.h"
 #include "logic/map_revision.h"
-#include "logic/notification.h"
 #include "logic/objective.h"
 #include "logic/walkingdir.h"
 #include "logic/widelands_geometry.h"
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
 #include "random/random.h"
 
 class FileSystem;
@@ -69,6 +70,16 @@
 struct Path;
 class Immovable;
 
+struct NoteFieldTransformed {
+	CAN_BE_SEND_AS_NOTE(NoteId::FieldTransformed)
+
+	FCoords fc;
+	Map_Index map_index;
+
+	NoteFieldTransformed(const FCoords& init_fc, const Map_Index init_map_index)
+	   : fc(init_fc), map_index(init_map_index) {
+	}
+};
 
 struct ImmovableFound {
 	BaseImmovable * object;
@@ -117,10 +128,7 @@
  *
  * Warning: width and height must be even
  */
-class Map :
-	public ITransportCostCalculator,
-	public NoteSender<NoteFieldTransformed>
-{
+class Map : public ITransportCostCalculator {
 public:
 	friend class Editor;
 	friend class Editor_Game_Base;

=== removed file 'src/logic/notification.cc'
--- src/logic/notification.cc	2014-07-05 12:17:03 +0000
+++ src/logic/notification.cc	1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-// Dummy file as cmake cannot handle header only libraries :(.

=== removed file 'src/logic/notification.h'
--- src/logic/notification.h	2014-07-05 16:41:51 +0000
+++ src/logic/notification.h	1970-01-01 00:00:00 +0000
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2008-2010 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_LOGIC_NOTIFICATION_H
-#define WL_LOGIC_NOTIFICATION_H
-
-#include <algorithm>
-#include <vector>
-
-#include "logic/widelands_geometry.h"
-
-namespace Widelands {
-
-struct PlayerImmovable;
-
-/*
-
-Sender/Receiver infrastructure
-
-Used by computer players to be notified of events, but could/should be
-extended to a nofication system that sends player information.
-
-*/
-
-template<typename T>
-class NoteReceiver;
-
-template<typename T>
-class NoteSender {
-	friend class NoteReceiver<T>;
-
-	typedef std::vector<NoteReceiver<T>*> Links;
-
-public:
-	~NoteSender() {
-		while (!m_links.empty())
-			(*m_links.rbegin())->disconnect(*this);
-	}
-
-	void send(const T & note) const {
-		for (auto& link : m_links) {
-			link->receive(note);
-		}
-	}
-
-private:
-	Links m_links;
-};
-
-template<typename T>
-class NoteReceiver {
-	friend class NoteSender<T>;
-
-	typedef std::vector<NoteSender<T>*> Links;
-
-public:
-	virtual ~NoteReceiver() {
-		while (!m_links.empty())
-			disconnect(**m_links.rbegin());
-	}
-
-	void connect(NoteSender<T> & sender) {
-		if (std::find(m_links.begin(), m_links.end(), &sender) != m_links.end())
-			return;
-		m_links.push_back(&sender);
-		sender.m_links.push_back(this);
-	}
-
-	void disconnect(NoteSender<T> & sender) {
-		typename NoteSender<T>::Links::iterator oit = std::find
-			(sender.m_links.begin(), sender.m_links.end(), this);
-		if (oit != sender.m_links.end())
-			sender.m_links.erase(oit);
-		typename Links::iterator it = std::find
-			(m_links.begin(), m_links.end(), &sender);
-		if (it != m_links.end())
-			m_links.erase(it);
-	}
-
-	virtual void receive(const T & note) = 0;
-
-private:
-	Links m_links;
-};
-
-
-enum losegain_t {LOSE = 0, GAIN};
-
-struct NoteImmovable {
-	PlayerImmovable * pi;
-	losegain_t lg;
-
-	NoteImmovable(PlayerImmovable * const _pi, losegain_t const _lg)
-		: pi(_pi), lg(_lg) {}
-};
-
-struct NoteFieldPossession {
-	FCoords fc;
-	losegain_t lg;
-
-	NoteFieldPossession(const FCoords & _fc, losegain_t const _lg)
-		: fc(_fc), lg(_lg) {}
-};
-
-struct NoteFieldTransformed {
-	FCoords fc;
-
-	NoteFieldTransformed(const FCoords & _fc)
-		: fc(_fc) {}
-};
-
-}
-
-#endif  // end of include guard: WL_LOGIC_NOTIFICATION_H

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2014-07-15 10:02:22 +0000
+++ src/logic/player.cc	2014-07-16 08:32:14 +0000
@@ -172,6 +172,23 @@
 	m_ware_stocks  (tribe_descr.get_nrwares          ())
 {
 	set_name(name);
+
+	// Subscribe to NoteImmovables.
+	immovable_subscriber_ =
+		Notifications::subscribe<NoteImmovable>([this](const NoteImmovable& note) {
+			if (note.pi->owner().player_number() == player_number()) {
+				if (upcast(Building, building, note.pi))
+					update_building_statistics(*building, note.ownership);
+			}
+		});
+
+	// Subscribe to NoteFieldTransformed.
+	field_transformed_subscriber_ =
+		Notifications::subscribe<NoteFieldTransformed>([this](const NoteFieldTransformed& note) {
+			if (vision(note.map_index) > 1) {
+				rediscover_node(egbase().map(), egbase().map()[0], note.fc);
+			}
+		});
 }
 
 
@@ -1230,7 +1247,7 @@
  * Only to be called by \ref receive
  */
 void Player::update_building_statistics
-	(Building & building, losegain_t const lg)
+	(Building & building, NoteImmovable::Ownership ownership)
 {
 	upcast(ConstructionSite const, constructionsite, &building);
 	const std::string & building_name =
@@ -1246,7 +1263,7 @@
 	std::vector<Building_Stats> & stat =
 		m_building_stats[tribe().building_index(building_name.c_str())];
 
-	if (lg == GAIN) {
+	if (ownership == NoteImmovable::Ownership::GAINED) {
 		Building_Stats new_building;
 		new_building.is_constructionsite = constructionsite;
 		new_building.pos = building.get_position();
@@ -1267,21 +1284,6 @@
 	}
 }
 
-
-void Player::receive(const NoteImmovable & note)
-{
-	if (upcast(Building, building, note.pi))
-		update_building_statistics(*building, note.lg);
-
-	NoteSender<NoteImmovable>::send(note);
-}
-
-
-void Player::receive(const NoteFieldPossession & note)
-{
-	NoteSender<NoteFieldPossession>::send(note);
-}
-
 void Player::setAI(const std::string & ai)
 {
 	m_ai = ai;

=== modified file 'src/logic/player.h'
--- src/logic/player.h	2014-07-14 19:48:07 +0000
+++ src/logic/player.h	2014-07-16 08:32:14 +0000
@@ -20,6 +20,8 @@
 #ifndef WL_LOGIC_PLAYER_H
 #define WL_LOGIC_PLAYER_H
 
+#include <memory>
+
 #include "base/macros.h"
 #include "graphic/color.h"
 #include "logic/building.h"
@@ -27,7 +29,6 @@
 #include "logic/editor_game_base.h"
 #include "logic/mapregion.h"
 #include "logic/message_queue.h"
-#include "logic/notification.h"
 #include "logic/tribe.h"
 #include "logic/warehouse.h"
 #include "logic/widelands.h"
@@ -54,10 +55,7 @@
  * \ref GameController and friends, so that replays and network games function
  * properly.
  */
-class Player :
-	public NoteReceiver<NoteImmovable>, public NoteReceiver<NoteFieldPossession>,
-	public NoteSender  <NoteImmovable>, public NoteSender  <NoteFieldPossession>
-{
+class Player {
 public:
 	// hard-coded playercolors
 	static const RGBColor Colors[MAX_PLAYERS];
@@ -364,13 +362,6 @@
 		// Node visible if > 1
 		return (m_see_all ? 2 : 0) + m_fields[i].vision;
 	}
-	/**
-	 * Called when a node becomes seen or has changed.  Discovers the node and
-	 * those of the 6 surrounding edges/triangles that are not seen from another
-	 * node.
-	 */
-	void rediscover_node(const Map &, const Widelands::Field &, FCoords)
-	;
 
 	bool has_view_changed() {
 		bool t = m_view_changed;
@@ -521,10 +512,6 @@
 	void ware_consumed(Ware_Index, uint8_t);
 	void next_ware_production_period();
 
-	void receive(const NoteImmovable &) override;
-	void receive(const NoteFieldPossession     &) override;
-	void receive(const NoteFieldTransformed     &);
-
 	void setAI(const std::string &);
 	const std::string & getAI() const;
 
@@ -535,13 +522,20 @@
 	}
 
 private:
-	void update_building_statistics(Building &, losegain_t);
+	void update_building_statistics(Building &, NoteImmovable::Ownership ownership);
 	void update_team_players();
 	void play_message_sound(const std::string & sender);
 	void _enhance_or_dismantle
 		(Building *, Building_Index const index_of_new_building);
 
-private:
+	// Called when a node becomes seen or has changed.  Discovers the node and
+	// those of the 6 surrounding edges/triangles that are not seen from another
+	// node.
+	void rediscover_node(const Map&, const Widelands::Field&, FCoords);
+
+	std::unique_ptr<Notifications::Subscriber<NoteImmovable>> immovable_subscriber_;
+	std::unique_ptr<Notifications::Subscriber<NoteFieldTransformed>> field_transformed_subscriber_;
+
 	MessageQueue           m_messages;
 
 	Editor_Game_Base     & m_egbase;

=== modified file 'src/logic/production_program.cc'
--- src/logic/production_program.cc	2014-07-15 18:30:46 +0000
+++ src/logic/production_program.cc	2014-07-16 08:32:14 +0000
@@ -361,8 +361,6 @@
 	} catch (const _wexception & e) {
 		throw game_data_error("economy: %s", e.what());
 	}
-
-	return nullptr; // will never be reached
 }
 
 
@@ -379,8 +377,6 @@
 	} catch (const _wexception & e) {
 		throw game_data_error("site: %s", e.what());
 	}
-
-	return nullptr; // will never be reached
 }
 
 
@@ -397,8 +393,6 @@
 	} catch (const _wexception & e) {
 		throw game_data_error("workers: %s", e.what());
 	}
-
-	return nullptr; // will never be reached
 }
 
 
@@ -422,8 +416,6 @@
 	} catch (const _wexception & e) {
 		throw game_data_error("invalid condition: %s", e.what());
 	}
-
-	return nullptr; // will never be reached
 }
 
 

=== modified file 'src/logic/ship.cc'
--- src/logic/ship.cc	2014-07-15 05:12:37 +0000
+++ src/logic/ship.cc	2014-07-16 08:32:14 +0000
@@ -630,7 +630,6 @@
 				send_message(game, "exp_coast", msg_head, msg_body, "ship_explore_island_cw.png");
 				return;
 			}
-			break;
 		}
 		case EXP_COLONIZING: {
 			assert(m_expedition->seen_port_buildspaces && !m_expedition->seen_port_buildspaces->empty());

=== modified file 'src/logic/worker.cc'
--- src/logic/worker.cc	2014-07-16 05:33:19 +0000
+++ src/logic/worker.cc	2014-07-16 08:32:14 +0000
@@ -3089,8 +3089,6 @@
 	} catch (const std::exception & e) {
 		throw wexception("loading worker: %s", e.what());
 	}
-
-	return nullptr; // Should not be reached
 }
 
 /**

=== modified file 'src/logic/world/map_gen.cc'
--- src/logic/world/map_gen.cc	2014-07-03 20:11:14 +0000
+++ src/logic/world/map_gen.cc	2014-07-16 08:32:14 +0000
@@ -51,7 +51,6 @@
 	default:
 		return nullptr;
 	};
-	return nullptr;
 }
 
 MapGenLandResource::MapGenLandResource(const LuaTable& table, MapGenInfo& mapGenInfo) {

=== modified file 'src/network/CMakeLists.txt'
--- src/network/CMakeLists.txt	2014-07-14 10:45:44 +0000
+++ src/network/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -30,6 +30,7 @@
     base_macros
     base_md5
     build_info
+    chat
     game_io
     helper
     io_dedicated_log

=== modified file 'src/network/internet_gaming.h'
--- src/network/internet_gaming.h	2014-07-05 16:41:51 +0000
+++ src/network/internet_gaming.h	2014-07-16 08:32:14 +0000
@@ -29,7 +29,7 @@
 #endif
 
 #include "build_info.h"
-#include "chat.h"
+#include "chat/chat.h"
 #include "network/internet_gaming_protocol.h"
 #include "network/network.h"
 #include "network/network_lan_promotion.h"
@@ -114,7 +114,10 @@
 	void send(const std::string &) override;
 
 	/// ChatProvider: adds the message to the message list and calls parent.
-	void receive(const ChatMessage & msg) {messages.push_back(msg); ChatProvider::send(msg);}
+	void receive(const ChatMessage & msg) {
+		messages.push_back(msg);
+		Notifications::publish(msg);
+	}
 
 	/// ChatProvider: returns the list of chatmessages.
 	const std::vector<ChatMessage> & getMessages() const override {return messages;}

=== modified file 'src/network/netclient.cc'
--- src/network/netclient.cc	2014-07-14 10:45:44 +0000
+++ src/network/netclient.cc	2014-07-16 08:32:14 +0000
@@ -997,7 +997,7 @@
 		if (packet.Unsigned8())
 			c.recipient = packet.String();
 		d->chatmessages.push_back(c);
-		ChatProvider::send(c); // NoteSender<ChatMessage>
+		Notifications::publish(c);
 		break;
 	}
 	case NETCMD_SYSTEM_MESSAGE_CODE: {
@@ -1011,7 +1011,7 @@
 		c.playern = UserSettings::none(); //  == System message
 		// c.sender remains empty to indicate a system message
 		d->chatmessages.push_back(c);
-		ChatProvider::send(c);
+		Notifications::publish(c);
 		break;
 	}
 	case NETCMD_DEDICATED_ACCESS: {

=== modified file 'src/network/netclient.h'
--- src/network/netclient.h	2014-07-05 16:41:51 +0000
+++ src/network/netclient.h	2014-07-16 08:32:14 +0000
@@ -20,7 +20,7 @@
 #ifndef WL_NETWORK_NETCLIENT_H
 #define WL_NETWORK_NETCLIENT_H
 
-#include "chat.h"
+#include "chat/chat.h"
 #include "logic/game_controller.h"
 #include "logic/game_settings.h"
 #include "network/network.h"

=== modified file 'src/network/nethost.cc'
--- src/network/nethost.cc	2014-07-14 10:45:44 +0000
+++ src/network/nethost.cc	2014-07-16 08:32:14 +0000
@@ -34,7 +34,7 @@
 #include "base/md5.h"
 #include "base/wexception.h"
 #include "build_info.h"
-#include "chat.h"
+#include "chat/chat.h"
 #include "game_io/game_loader.h"
 #include "game_io/game_preload_data_packet.h"
 #include "helper.h"
@@ -514,7 +514,7 @@
 
 	void receive(const ChatMessage & msg) {
 		messages.push_back(msg);
-		ChatProvider::send(msg);
+		Notifications::publish(msg);
 	}
 
 private:
@@ -1022,8 +1022,6 @@
 		broadcast(s);
 
 		d->chat.receive(msg);
-
-		dedicatedlog("[Host]: chat: %s\n", msg.toPlainString().c_str());
 	} else { //  personal messages
 		SendPacket s;
 		s.Unsigned8(NETCMD_CHAT);

=== added directory 'src/notifications'
=== added file 'src/notifications/CMakeLists.txt'
--- src/notifications/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ src/notifications/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -0,0 +1,12 @@
+add_subdirectory(test)
+
+wl_library(notifications
+  SRCS
+    notifications.cc
+    notifications.h
+    notifications_impl.h
+    note_ids.h
+  DEPENDS
+    base_log
+    base_macros
+)

=== added file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h	1970-01-01 00:00:00 +0000
+++ src/notifications/note_ids.h	2014-07-16 08:32:14 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_NOTIFICATIONS_NOTE_IDS_H
+#define WL_NOTIFICATIONS_NOTE_IDS_H
+
+#include <stdint.h>
+
+// List all note ids here. They must be unique in the running
+// system, this is easier to guarantee when they are all listed in
+// one place.
+enum class NoteId : uint32_t {
+	ChatMessage,
+	LogMessage,
+	Immovable,
+	FieldPossession,
+	FieldTransformed,
+};
+
+#endif  // end of include guard: WL_NOTIFICATIONS_NOTE_IDS_H

=== added file 'src/notifications/notifications.cc'
--- src/notifications/notifications.cc	1970-01-01 00:00:00 +0000
+++ src/notifications/notifications.cc	2014-07-16 08:32:14 +0000
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "notifications/notifications.h"
+
+#include "base/log.h"
+
+namespace Notifications {
+
+NotificationsManager* NotificationsManager::get() {
+	static NotificationsManager instance;
+	return &instance;
+}
+
+NotificationsManager::NotificationsManager() : next_subscriber_id_(1), num_subscribers_(0) {
+}
+
+NotificationsManager::~NotificationsManager() {
+	if (num_subscribers_ != 0) {
+		log("ERROR: NotificationsManager is destroyed, but there are still subscribers.");
+	}
+}
+
+}  // namespace Notifications

=== added file 'src/notifications/notifications.h'
--- src/notifications/notifications.h	1970-01-01 00:00:00 +0000
+++ src/notifications/notifications.h	2014-07-16 08:32:14 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_NOTIFICATIONS_NOTIFICATIONS_H
+#define WL_NOTIFICATIONS_NOTIFICATIONS_H
+
+#include <functional>
+#include <memory>
+
+#include "notifications/notifications_impl.h"
+
+namespace Notifications {
+
+// The Notification framework is build around a singleton that dispatches
+// 'Note'. A Note is any class that has a uint32_t note_id() member that must
+// return something unique throughout the whole system. Use the macro
+// CAN_BE_SEND_AS_NOTE to define that method easily.
+//
+// The only public interface for the framework are the two functions below.
+
+#define CAN_BE_SEND_AS_NOTE(id)                                                                    \
+	static uint32_t note_id() {                                                                     \
+		return static_cast<uint32_t>(id);                                                            \
+	}
+
+// Subscribes to a Note of type 'T' with the given callback function. The
+// returned object is opaque, but will unsubscribe on destruction.
+template <typename T>
+std::unique_ptr<Subscriber<T>> subscribe(std::function<void(const T&)> callback) {
+	return NotificationsManager::get()->subscribe<T>(callback);
+}
+
+// Publishes 'message' to all existing subscribers.
+template <typename T> void publish(const T& message) {
+	return NotificationsManager::get()->publish<T>(message);
+}
+
+}  // namespace Notifications
+
+#endif  // end of include guard: WL_NOTIFICATIONS_NOTIFICATIONS_H

=== added file 'src/notifications/notifications_impl.h'
--- src/notifications/notifications_impl.h	1970-01-01 00:00:00 +0000
+++ src/notifications/notifications_impl.h	2014-07-16 08:32:14 +0000
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_NOTIFICATIONS_NOTIFICATIONS_IMPL_H
+#define WL_NOTIFICATIONS_NOTIFICATIONS_IMPL_H
+
+#include "base/macros.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <cassert>
+#include <list>
+#include <memory>
+#include <unordered_map>
+
+namespace Notifications {
+
+// Subscribes to a notification type and unsubscribes on destruction.
+template <typename T> class Subscriber {
+public:
+	Subscriber(uint32_t id, std::function<void(const T&)> callback) : id_(id), callback_(callback) {
+	}
+
+	~Subscriber();
+
+private:
+	friend class NotificationsManager;
+
+	uint32_t id_;
+	std::function<void(const T&)> callback_;
+
+	DISALLOW_COPY_AND_ASSIGN(Subscriber);
+};
+
+// Singleton that dispatches notifications and keeps track of all subscribers.
+// Implementation detail. Instead use the functions from the public header.
+class NotificationsManager {
+public:
+	// Returns the Singleton. Will create it if it does not yet exist.
+	static NotificationsManager* get();
+
+	// Creates a subscriber for 'T' with the given 'callback' and returns it.
+	template <typename T>
+	std::unique_ptr<Subscriber<T>> subscribe(std::function<void(const T&)> callback) {
+		std::list<void*>& subscribers = note_id_to_subscribers_[T::note_id()];
+		auto new_subscriber =
+		   std::unique_ptr<Subscriber<T>>(new Subscriber<T>(next_subscriber_id_, callback));
+		subscribers.push_back(new_subscriber.get());
+		++next_subscriber_id_;
+		++num_subscribers_;
+		return new_subscriber;
+	}
+
+	// Publishes 'message' to all subscribers.
+	template <typename T> void publish(const T& message) {
+		for (void* p_subscriber : note_id_to_subscribers_[T::note_id()]) {
+			Subscriber<T>* subscriber = static_cast<Subscriber<T>*>(p_subscriber);
+			subscriber->callback_(message);
+		}
+	}
+
+	// Unsubscribes 'subscriber'.
+	template <typename T>
+	void unsubscribe(Subscriber<T>* subscriber) {
+		std::list<void*>& subscribers = note_id_to_subscribers_.at(T::note_id());
+		auto subscribers_it =
+		   std::find_if(subscribers.begin(), subscribers.end(), [&subscriber](const void* p_subscriber) {
+			   return static_cast<const Subscriber<T>*>(p_subscriber)->id_ == subscriber->id_;
+			});
+
+		assert(subscribers_it != subscribers.end());
+		subscribers.erase(subscribers_it);
+		--num_subscribers_;
+	}
+
+private:
+	// Private constructor for Singleton.
+	NotificationsManager();
+
+	// Checks that there are no more subscribers.
+	~NotificationsManager();
+
+	uint32_t next_subscriber_id_;
+	uint32_t num_subscribers_;
+
+	// Ideally we would like to keep a list<Subscriber<T>*> instead of void* to
+	// be typesafe. Unfortunately, C++ does not allow for an easy way. I could
+	// introduce a base class and dispatch via a virtual function call, but
+	// since this framework should be as efficient as possible, I opted for
+	// using void* and casting instead.
+	std::unordered_map<uint32_t, std::list<void*>>  note_id_to_subscribers_;
+
+	DISALLOW_COPY_AND_ASSIGN(NotificationsManager);
+};
+
+template <typename T> Subscriber<T>::~Subscriber() {
+	NotificationsManager::get()->unsubscribe<T>(this);
+}
+
+}  // namespace Notifications
+
+#endif  // end of include guard: WL_NOTIFICATIONS_NOTIFICATIONS_IMPL_H

=== added directory 'src/notifications/test'
=== added file 'src/notifications/test/CMakeLists.txt'
--- src/notifications/test/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ src/notifications/test/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -0,0 +1,6 @@
+wl_test(notifications_test
+  SRCS
+    notifications_test.cc
+  DEPENDS
+    notifications
+)

=== added file 'src/notifications/test/notifications_test.cc'
--- src/notifications/test/notifications_test.cc	1970-01-01 00:00:00 +0000
+++ src/notifications/test/notifications_test.cc	2014-07-16 08:32:14 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <memory>
+#include <string>
+
+#define BOOST_TEST_MODULE Notifications
+#include <boost/test/unit_test.hpp>
+
+#include "notifications/notifications.h"
+
+struct SimpleNote {
+	CAN_BE_SEND_AS_NOTE(100)
+
+	SimpleNote(const std::string& init_text) : text(init_text) {}
+
+	std::string text;
+};
+
+BOOST_AUTO_TEST_SUITE(NotificationsTestSuite)
+
+
+BOOST_AUTO_TEST_CASE(SimpleTest) {
+	std::vector<SimpleNote> received1;
+	auto subscriber1 = Notifications::subscribe<SimpleNote>(
+	   [&received1](const SimpleNote& got) {received1.push_back(got);});
+
+	Notifications::publish(SimpleNote("Hello"));
+
+	std::vector<SimpleNote> received2;
+	auto subscriber2 = Notifications::subscribe<SimpleNote>(
+	   [&received2](const SimpleNote& got) {received2.push_back(got);});
+
+	Notifications::publish(SimpleNote("World"));
+
+	BOOST_CHECK_EQUAL(received1.size(), 2);
+	BOOST_CHECK_EQUAL("Hello", received1[0].text);
+	BOOST_CHECK_EQUAL("World", received1[1].text);
+
+	BOOST_CHECK_EQUAL(received2.size(), 1);
+	BOOST_CHECK_EQUAL("World", received2[0].text);
+}
+
+BOOST_AUTO_TEST_SUITE_END()

=== modified file 'src/scripting/lua_globals.cc'
--- src/scripting/lua_globals.cc	2014-06-21 15:17:04 +0000
+++ src/scripting/lua_globals.cc	2014-07-16 08:32:14 +0000
@@ -105,7 +105,6 @@
 				case LUA_TTHREAD:
 				case LUA_TLIGHTUSERDATA:
 					report_error(L, "Cannot format the given type %s at index %i", lua_typename(L, i), i);
-					break;
 			}
 		}
 

=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc	2014-07-16 06:05:23 +0000
+++ src/scripting/lua_map.cc	2014-07-16 08:32:14 +0000
@@ -2121,7 +2121,6 @@
 		case BaseImmovable::BIG: lua_pushstring(L, "big"); break;
 		default:
 		   report_error(L, "Unknown size in L_BaseImmovable::get_size: %i", o->get_size());
-		   break;
 	}
 	return 1;
 }

=== modified file 'src/scripting/lua_map.h'
--- src/scripting/lua_map.h	2014-07-15 10:02:22 +0000
+++ src/scripting/lua_map.h	2014-07-16 08:32:14 +0000
@@ -185,7 +185,7 @@
 	 */
 
 private:
-	CASTED_GET_DESCRIPTION(Building_Descr);
+	CASTED_GET_DESCRIPTION(Building_Descr)
 };
 
 
@@ -218,7 +218,7 @@
 	 */
 
 private:
-	CASTED_GET_DESCRIPTION(ProductionSite_Descr);
+	CASTED_GET_DESCRIPTION(ProductionSite_Descr)
 };
 
 
@@ -250,7 +250,7 @@
 	 */
 
 private:
-	CASTED_GET_DESCRIPTION(MilitarySite_Descr);
+	CASTED_GET_DESCRIPTION(MilitarySite_Descr)
 };
 
 
@@ -289,7 +289,7 @@
 	 */
 
 private:
-	CASTED_GET_DESCRIPTION(TrainingSite_Descr);
+	CASTED_GET_DESCRIPTION(TrainingSite_Descr)
 };
 
 
@@ -321,7 +321,7 @@
 	 */
 
 private:
-	CASTED_GET_DESCRIPTION(Warehouse_Descr);
+	CASTED_GET_DESCRIPTION(Warehouse_Descr)
 };
 
 
@@ -356,7 +356,7 @@
 	 */
 
 private:
-	CASTED_GET_DESCRIPTION(WareDescr);
+	CASTED_GET_DESCRIPTION(WareDescr)
 };
 
 
@@ -393,7 +393,7 @@
 	 */
 
 private:
-	CASTED_GET_DESCRIPTION(Worker_Descr);
+	CASTED_GET_DESCRIPTION(Worker_Descr)
 };
 
 #undef CASTED_GET_DESCRIPTION

=== modified file 'src/ui_fsmenu/CMakeLists.txt'
--- src/ui_fsmenu/CMakeLists.txt	2014-07-14 10:45:44 +0000
+++ src/ui_fsmenu/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -61,5 +61,6 @@
     ui_basic
     widelands_ball_of_mud
     wui
+    wui_chat_ui
     wui_text_layout
 )

=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt	2014-07-14 10:45:44 +0000
+++ src/wui/CMakeLists.txt	2014-07-16 08:32:14 +0000
@@ -7,6 +7,28 @@
     graphic_color
 )
 
+wl_library(wui_chat_ui
+  SRCS
+    chatoverlay.cc
+    chatoverlay.h
+    game_chat_menu.cc
+    game_chat_menu.h
+    gamechatpanel.cc
+    gamechatpanel.h
+    chat_msg_layout.h
+    chat_msg_layout.cc
+  DEPENDS
+    base_i18n
+    chat
+    graphic
+    graphic_color
+    graphic_text
+    logic
+    profile
+    ui_basic
+    wui
+)
+
 wl_library(wui
   SRCS
     actionconfirm.cc
@@ -18,8 +40,6 @@
     building_ui.cc
     buildingwindow.cc
     buildingwindow.h
-    chatoverlay.cc
-    chatoverlay.h
     constructionsitewindow.cc
     debugconsole.cc
     debugconsole.h
@@ -28,8 +48,6 @@
     encyclopedia_window.h
     fieldaction.cc
     fieldaction.h
-    game_chat_menu.cc
-    game_chat_menu.h
     game_debug_ui.cc
     game_debug_ui.h
     game_main_menu.cc
@@ -48,8 +66,6 @@
     game_summary.h
     game_tips.cc
     game_tips.h
-    gamechatpanel.cc
-    gamechatpanel.h
     general_statistics_menu.cc
     general_statistics_menu.h
     interactive_base.cc
@@ -120,26 +136,27 @@
     base_log
     base_macros
     base_time_string
+    chat
     economy
     game_io
     graphic
     graphic_color
     graphic_image
     graphic_surface
-    graphic_text
     io_fileread
     io_filesystem
     logic
     logic_game_controller
     logic_game_settings
-    logic_notification
     logic_widelands_geometry
     network
+    notifications
     profile
     scripting
     sound
     ui_basic
     ui_fsmenu
     widelands_ball_of_mud
+    wui_chat_ui
     wui_text_layout
 )

=== added file 'src/wui/chat_msg_layout.cc'
--- src/wui/chat_msg_layout.cc	1970-01-01 00:00:00 +0000
+++ src/wui/chat_msg_layout.cc	2014-07-16 08:32:14 +0000
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "wui/chat_msg_layout.h"
+
+#include "chat/chat.h"
+#include "graphic/color.h"
+#include "logic/constants.h"
+#include "logic/player.h"
+
+namespace {
+
+// Returns the hexcolor for the 'player'.
+std::string color(const int16_t playern)
+{
+	if ((playern >= 0) && playern < MAX_PLAYERS) {
+		const RGBColor & clr = Widelands::Player::Colors[playern];
+		char buf[sizeof("ffffff")];
+		snprintf(buf, sizeof(buf), "%.2x%.2x%.2x", clr.r, clr.g, clr.b);
+		return buf;
+	}
+	return "999999";
+}
+
+}  // namespace
+
+// TODO(sirver): remove as soon as old text renderer is gone.
+std::string format_as_old_richtext(const ChatMessage& chat_message) {
+	std::string message = "<p font-color=#33ff33 font-size=9>";
+
+	// Escape richtext characters
+	// The goal of this code is two-fold:
+	//  1. Assuming an honest game host, we want to prevent the ability of
+	//     clients to use richtext.
+	//  2. Assuming a malicious host or meta server, we want to reduce the
+	//     likelihood that a bug in the richtext renderer can be exploited,
+	//     by restricting the set of allowed richtext commands.
+	//     Most notably, images are not allowed in richtext at all.
+	//
+	// Note that we do want host and meta server to send some richtext code,
+	// as the ability to send formatted commands is nice for the usability
+	// of meta server and dedicated servers, so we're treading a bit of a
+	// fine line here.
+	std::string sanitized;
+	for (std::string::size_type pos = 0; pos < chat_message.msg.size(); ++pos) {
+		if (chat_message.msg[pos] == '<') {
+			if (chat_message.playern < 0) {
+				static const std::string good1 = "</p><p";
+				static const std::string good2 = "<br>";
+				if (!chat_message.msg.compare(pos, good1.size(), good1)) {
+					std::string::size_type nextclose = chat_message.msg.find('>', pos + good1.size());
+					if (nextclose != std::string::npos &&
+					    (nextclose == pos + good1.size() || chat_message.msg[pos + good1.size()] == ' ')) {
+						sanitized += good1;
+						pos += good1.size() - 1;
+						continue;
+					}
+				} else if (!chat_message.msg.compare(pos, good2.size(), good2)) {
+					sanitized += good2;
+					pos += good2.size() - 1;
+					continue;
+				}
+			}
+
+			sanitized += "&lt;";
+		} else {
+			sanitized += chat_message.msg[pos];
+		}
+	}
+
+	// time calculation
+	char ts[13];
+	strftime(ts, sizeof(ts), "[%H:%M] </p>", localtime(&chat_message.time));
+	message += ts;
+
+	message += "<p font-size=14 font-face=DejaVuSerif font-color=#";
+	message += color(chat_message.playern);
+
+	if (chat_message.recipient.size() && chat_message.sender.size()) {
+		// Personal message handling
+		if (sanitized.compare(0, 3, "/me")) {
+			message += " font-decoration=underline>";
+			message += chat_message.sender;
+			message += " @ ";
+			message += chat_message.recipient;
+			message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
+			message += sanitized;
+		} else {
+			message += ">@";
+			message += chat_message.recipient;
+			message += " >> </p><p font-size=14";
+			message += " font-face=DejaVuSerif font-color=#";
+			message += color(chat_message.playern);
+			message += " font-style=italic> ";
+			message += chat_message.sender;
+			message += sanitized.substr(3);
+		}
+	} else {
+		// Normal messages handling
+		if (not sanitized.compare(0, 3, "/me")) {
+			message += " font-style=italic>-> ";
+			if (chat_message.sender.size())
+				message += chat_message.sender;
+			else
+				message += "***";
+			message += sanitized.substr(3);
+		} else if (chat_message.sender.size()) {
+			message += " font-decoration=underline>";
+			message += chat_message.sender;
+			message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
+			message += sanitized;
+		} else {
+			message += " font-weight=bold>*** ";
+			message += sanitized;
+		}
+	}
+
+	// return the formated message
+	return message + "<br></p>";
+}
+
+// Returns a richtext string that can be displayed to the user.
+std::string format_as_richtext(const ChatMessage& chat_message) {
+	std::string message = "<p><font color=33ff33 size=9>";
+
+	// Escape richtext characters
+	// The goal of this code is two-fold:
+	//  1. Assuming an honest game host, we want to prevent the ability of
+	//     clients to use richtext.
+	//  2. Assuming a malicious host or meta server, we want to reduce the
+	//     likelihood that a bug in the richtext renderer can be exploited,
+	//     by restricting the set of allowed richtext commands.
+	//     Most notably, images are not allowed in richtext at all.
+	//
+	// Note that we do want host and meta server to send some richtext code,
+	// as the ability to send formatted commands is nice for the usability
+	// of meta server and dedicated servers, so we're treading a bit of a
+	// fine line here.
+	std::string sanitized;
+	for (std::string::size_type pos = 0; pos < chat_message.msg.size(); ++pos) {
+		if (chat_message.msg[pos] == '<') {
+			if (chat_message.playern < 0) {
+				static const std::string good1 = "</p><p";
+				static const std::string good2 = "<br>";
+				if (!chat_message.msg.compare(pos, good1.size(), good1)) {
+					std::string::size_type nextclose = chat_message.msg.find('>', pos + good1.size());
+					if
+						(nextclose != std::string::npos &&
+						(nextclose == pos + good1.size() || chat_message.msg[pos + good1.size()] == ' '))
+					{
+						sanitized += good1;
+						pos += good1.size() - 1;
+						continue;
+					}
+				} else if (!chat_message.msg.compare(pos, good2.size(), good2)) {
+					sanitized += good2;
+					pos += good2.size() - 1;
+					continue;
+				}
+			}
+
+			sanitized += "\\<";
+		} else {
+			sanitized += chat_message.msg[pos];
+		}
+	}
+
+	// time calculation
+	char ts[13];
+	strftime(ts, sizeof(ts), "[%H:%M] ", localtime(&chat_message.time));
+	message += ts;
+
+	message += "</font><font size=14 face=DejaVuSerif color=";
+	message += color(chat_message.playern);
+
+	if (chat_message.recipient.size() && chat_message.sender.size()) {
+		// Personal message handling
+		if (sanitized.compare(0, 3, "/me")) {
+			message += " bold=1>";
+			message += chat_message.sender;
+			message += " @ ";
+			message += chat_message.recipient;
+			message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
+			message += sanitized;
+		} else {
+			message += ">@";
+			message += chat_message.recipient;
+			message += " \\> </font><font size=14";
+			message += " face=DejaVuSerif color=";
+			message += color(chat_message.playern);
+			message += " italic=1 shadow=1> ";
+			message += chat_message.sender;
+			message += sanitized.substr(3);
+		}
+	} else {
+		// Normal messages handling
+		if (not sanitized.compare(0, 3, "/me")) {
+			message += " italic=1>-\\> ";
+			if (chat_message.sender.size())
+				message += chat_message.sender;
+			else
+				message += "***";
+			message += sanitized.substr(3);
+		} else if (chat_message.sender.size()) {
+			message += " bold=1>";
+			message += chat_message.sender;
+			message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
+			message += sanitized;
+		} else {
+			message += " bold=1>*** ";
+			message += sanitized;
+		}
+	}
+
+	// return the formated message
+	return message + "</font><br></p>";
+}

=== added file 'src/wui/chat_msg_layout.h'
--- src/wui/chat_msg_layout.h	1970-01-01 00:00:00 +0000
+++ src/wui/chat_msg_layout.h	2014-07-16 08:32:14 +0000
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006-2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_WUI_CHAT_MSG_LAYOUT_H
+#define WL_WUI_CHAT_MSG_LAYOUT_H
+
+#include <string>
+
+struct ChatMessage;
+
+// Formats 'chat_message' as old richtext.
+std::string format_as_old_richtext(const ChatMessage& chat_message);
+
+// Formats 'chat_message' as richtext.
+std::string format_as_richtext(const ChatMessage& chat_message);
+
+#endif  // end of include guard: WL_WUI_CHAT_MSG_LAYOUT_H

=== modified file 'src/wui/chatoverlay.cc'
--- src/wui/chatoverlay.cc	2014-07-14 10:45:44 +0000
+++ src/wui/chatoverlay.cc	2014-07-16 08:32:14 +0000
@@ -19,11 +19,14 @@
 
 #include "wui/chatoverlay.h"
 
-#include "chat.h"
+#include <memory>
+
+#include "chat/chat.h"
 #include "graphic/font_handler1.h"
 #include "graphic/rendertarget.h"
 #include "graphic/text/rt_errors.h"
 #include "profile/profile.h"
+#include "wui/chat_msg_layout.h"
 #include "wui/logmessage.h"
 
 /**
@@ -32,8 +35,7 @@
 static const int32_t CHAT_DISPLAY_TIME = 10;
 static const uint32_t MARGIN = 2;
 
-struct ChatOverlay::Impl : Widelands::NoteReceiver<ChatMessage>,
-	Widelands::NoteReceiver<LogMessage> {
+struct ChatOverlay::Impl {
 	bool transparent_;
 	ChatProvider * chat_;
 	bool havemessages_;
@@ -47,11 +49,24 @@
 	/// Log messages
 	std::vector<LogMessage> log_messages_;
 
-	Impl() : transparent_(false), chat_(nullptr), havemessages_(false), oldest_(0) {}
+	std::unique_ptr<Notifications::Subscriber<ChatMessage>> chat_message_subscriber_;
+	std::unique_ptr<Notifications::Subscriber<LogMessage>> log_message_subscriber_;
+
+	Impl()
+	   : transparent_(false),
+	     chat_(nullptr),
+	     havemessages_(false),
+	     oldest_(0),
+	     chat_message_subscriber_(
+	        Notifications::subscribe<ChatMessage>([this](const ChatMessage&) {recompute();})),
+	     log_message_subscriber_(
+	        Notifications::subscribe<LogMessage>([this](const LogMessage& note) {
+		        log_messages_.push_back(note);
+		        recompute();
+		     })) {
+	}
 
 	void recompute();
-	virtual void receive(const ChatMessage & note) override;
-	virtual void receive(const LogMessage & note) override;
 };
 
 ChatOverlay::ChatOverlay
@@ -72,20 +87,9 @@
 void ChatOverlay::setChatProvider(ChatProvider & chat)
 {
 	m->chat_ = &chat;
-	Widelands::NoteReceiver<ChatMessage>* cmr
-		= dynamic_cast<Widelands::NoteReceiver<ChatMessage>*>(m.get());
-	cmr->connect(chat);
 	m->recompute();
 }
 
-void ChatOverlay::setLogProvider(Widelands::NoteSender<LogMessage>& log_sender)
-{
-	Widelands::NoteReceiver<LogMessage>* lmr
-		= dynamic_cast<Widelands::NoteReceiver<LogMessage>*>(m.get());
-	lmr->connect(log_sender);
-}
-
-
 /**
  * Check for message expiry.
  */
@@ -98,21 +102,6 @@
 }
 
 /**
- * Callback that is run when a new chat message comes in.
- */
-void ChatOverlay::Impl::receive(const ChatMessage & /* note */)
-{
-	recompute();
-}
-
-void ChatOverlay::Impl::receive(const LogMessage& note)
-{
-	log_messages_.push_back(note);
-	recompute();
-}
-
-
-/**
  * Recompute the chat message display.
  */
 void ChatOverlay::Impl::recompute()
@@ -147,7 +136,7 @@
 			// Chat message is more recent
 			oldest_ = chat_->getMessages()[chat_idx].time;
 			if (now - oldest_ < CHAT_DISPLAY_TIME) {
-				richtext = chat_->getMessages()[chat_idx].toPrintable()
+				richtext = format_as_richtext(chat_->getMessages()[chat_idx])
 					+ richtext;
 			}
 			chat_idx--;

=== modified file 'src/wui/chatoverlay.h'
--- src/wui/chatoverlay.h	2014-07-05 16:41:51 +0000
+++ src/wui/chatoverlay.h	2014-07-16 08:32:14 +0000
@@ -22,11 +22,9 @@
 
 #include <memory>
 
-#include "logic/notification.h"
 #include "ui_basic/panel.h"
 
 struct ChatProvider;
-struct LogMessage;
 
 /**
  * The overlay that displays all new chat messages for some timeout on the main window.
@@ -38,7 +36,6 @@
 	~ChatOverlay();
 
 	void setChatProvider(ChatProvider &);
-	void setLogProvider(Widelands::NoteSender<LogMessage> &);
 	virtual void draw(RenderTarget &) override;
 	virtual void think() override;
 

=== modified file 'src/wui/debugconsole.cc'
--- src/wui/debugconsole.cc	2014-07-05 12:48:58 +0000
+++ src/wui/debugconsole.cc	2014-07-16 08:32:14 +0000
@@ -24,7 +24,7 @@
 #include <boost/bind.hpp>
 
 #include "base/log.h"
-#include "chat.h"
+#include "chat/chat.h"
 
 namespace DebugConsole {
 
@@ -105,7 +105,7 @@
 		if (messages.size() > 1000)
 			messages.erase(messages.begin(), messages.begin() + 100);
 
-		ChatProvider::send(cm); // Notify listeners, i.e. the UI
+		Notifications::publish(cm); // Notify listeners, i.e. the UI
 	}
 };
 

=== modified file 'src/wui/gamechatpanel.cc'
--- src/wui/gamechatpanel.cc	2014-04-16 10:49:34 +0000
+++ src/wui/gamechatpanel.cc	2014-07-16 08:32:14 +0000
@@ -20,6 +20,9 @@
 #include "wui/gamechatpanel.h"
 
 #include <limits>
+#include <string>
+
+#include "wui/chat_msg_layout.h"
 
 /**
  * Create a game chat panel
@@ -44,7 +47,8 @@
 	set_handle_mouse(true);
 	set_can_focus(true);
 
-	connect(m_chat);
+	chat_message_subscriber_ =
+	   Notifications::subscribe<ChatMessage>([this](const ChatMessage&) {recalculate();});
 	recalculate();
 }
 
@@ -57,8 +61,7 @@
 
 	std::string str = "<rt>";
 	for (uint32_t i = 0; i < msgs.size(); ++i) {
-		// FIXME use toPrintable() when old renderer is kicked out
-		str += msgs[i].toOldRichText();
+		str += format_as_old_richtext(msgs[i]);
 		str += '\n';
 	}
 	str += "</rt>";
@@ -96,11 +99,6 @@
 	editbox.focus();
 }
 
-void GameChatPanel::receive(const ChatMessage &)
-{
-	recalculate();
-}
-
 void GameChatPanel::keyEnter()
 {
 	const std::string & str = editbox.text();

=== modified file 'src/wui/gamechatpanel.h'
--- src/wui/gamechatpanel.h	2014-07-05 16:41:51 +0000
+++ src/wui/gamechatpanel.h	2014-07-16 08:32:14 +0000
@@ -20,7 +20,9 @@
 #ifndef WL_WUI_GAMECHATPANEL_H
 #define WL_WUI_GAMECHATPANEL_H
 
-#include "chat.h"
+#include <memory>
+
+#include "chat/chat.h"
 #include "ui_basic/editbox.h"
 #include "ui_basic/multilinetextarea.h"
 
@@ -30,22 +32,16 @@
  * Provides a panel that contains chat message scrollbar and a chat message
  * entry field.
  */
-struct GameChatPanel :
-	public UI::Panel, public Widelands::NoteReceiver<ChatMessage>
-{
+struct GameChatPanel : public UI::Panel {
 	GameChatPanel
 		(UI::Panel    *,
 		 int32_t x, int32_t y, uint32_t w, uint32_t h,
 		 ChatProvider &);
 
-	/**
-	 * Signal is called when a message has been sent by the user.
-	 */
+	// Signal is called when a message has been sent by the user.
 	boost::signals2::signal<void ()> sent;
 
-	/**
-	 * Signal is called when the user has aborted entering a message.
-	 */
+	// Signal is called when the user has aborted entering a message.
 	boost::signals2::signal<void ()> aborted;
 
 	const std::string & get_edit_text() const {return editbox.text();}
@@ -53,8 +49,6 @@
 
 	void focusEdit();
 
-	void receive(const ChatMessage &) override;
-
 private:
 	void recalculate();
 	void keyEnter();
@@ -64,6 +58,7 @@
 	UI::Multiline_Textarea chatbox;
 	UI::EditBox editbox;
 	uint32_t chat_message_counter;
+	std::unique_ptr<Notifications::Subscriber<ChatMessage>> chat_message_subscriber_;
 };
 
 #endif  // end of include guard: WL_WUI_GAMECHATPANEL_H

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2014-07-14 10:45:44 +0000
+++ src/wui/interactive_base.cc	2014-07-16 08:32:14 +0000
@@ -45,6 +45,7 @@
 #include "wui/game_chat_menu.h"
 #include "wui/game_debug_ui.h"
 #include "wui/interactive_player.h"
+#include "wui/logmessage.h"
 #include "wui/mapviewpixelconstants.h"
 #include "wui/mapviewpixelfunctions.h"
 #include "wui/minimap.h"
@@ -103,13 +104,12 @@
      m_road_build_player(0),
      m_label_speed_shadow(this, get_w() - 1, 0, std::string(), UI::Align_TopRight),
      m_label_speed(this, get_w(), 1, std::string(), UI::Align_TopRight),
-     unique_window_handler_(new UniqueWindowHandler()),
-     // Load workarea images.
      // Start at idx 0 for 2 enhancements, idx 3 for 1, idx 5 if none
 		m_workarea_pics
 		{g_gr->images().get("pics/workarea123.png"), g_gr->images().get("pics/workarea23.png"),
 			g_gr->images().get("pics/workarea3.png"), g_gr->images().get("pics/workarea12.png"),
-			g_gr->images().get("pics/workarea2.png"), g_gr->images().get("pics/workarea1.png")}
+			g_gr->images().get("pics/workarea2.png"), g_gr->images().get("pics/workarea1.png")},
+     unique_window_handler_(new UniqueWindowHandler())
 {
 	m_toolbar.set_layout_toplevel(true);
 	m->quicknavigation->set_setview
@@ -127,8 +127,6 @@
 	set_dock_windows_to_edges
 		(global_s.get_bool("dock_windows_to_edges", false));
 
-	m_chatOverlay->setLogProvider(m_log_sender);
-
 	//  Switch to the new graphics system now, if necessary.
 	WLApplication::get()->refresh_graphics();
 
@@ -770,7 +768,7 @@
 	LogMessage lm;
 	lm.msg = message;
 	lm.time = time(nullptr);
-	m_log_sender.send(lm);
+	Notifications::publish(lm);
 }
 
 

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2014-07-14 10:45:44 +0000
+++ src/wui/interactive_base.h	2014-07-16 08:32:14 +0000
@@ -26,10 +26,8 @@
 
 #include "logic/editor_game_base.h"
 #include "logic/map.h"
-#include "logic/notification.h"
 #include "wui/chatoverlay.h"
 #include "wui/debugconsole.h"
-#include "wui/logmessage.h"
 #include "wui/mapview.h"
 #include "wui/overlay_manager.h"
 #include "ui_basic/box.h"
@@ -196,7 +194,6 @@
 	UI::Textarea m_label_speed;
 
 	UI::UniqueWindow::Registry m_debugconsole;
-	Widelands::NoteSender<LogMessage> m_log_sender;
 	std::unique_ptr<UniqueWindowHandler> unique_window_handler_;
 	std::vector<const Image*> m_workarea_pics;
 };

=== modified file 'src/wui/interactive_spectator.cc'
--- src/wui/interactive_spectator.cc	2014-07-05 12:48:58 +0000
+++ src/wui/interactive_spectator.cc	2014-07-16 08:32:14 +0000
@@ -21,7 +21,7 @@
 
 #include "base/i18n.h"
 #include "base/macros.h"
-#include "chat.h"
+#include "chat/chat.h"
 #include "graphic/graphic.h"
 #include "logic/game_controller.h"
 #include "ui_basic/editbox.h"

=== modified file 'src/wui/logmessage.h'
--- src/wui/logmessage.h	2014-07-05 16:41:51 +0000
+++ src/wui/logmessage.h	2014-07-16 08:32:14 +0000
@@ -22,11 +22,15 @@
 
 #include <string>
 
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
 
 /**
  * Represents one log message.
  */
 struct LogMessage {
+	CAN_BE_SEND_AS_NOTE(NoteId::LogMessage)
+
 	/**
 	 * The (real-)time at which the message was received.
 	 */


Follow ups