From 7368b120827e4e9da659da62398bb37a68c19ab5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 2 Aug 2009 10:27:38 +0200
Subject: Slimber: Abstract controllers out of Cocoa UI.


diff --git a/Slimber/Cocoa/CocoaController.h b/Slimber/Cocoa/CocoaController.h
new file mode 100644
index 0000000..f4be87d
--- /dev/null
+++ b/Slimber/Cocoa/CocoaController.h
@@ -0,0 +1,11 @@
+#include <Cocoa/Cocoa.h>
+
+class MainController;
+class CocoaMenulet;
+
+@interface CocoaController : NSObject {
+	CocoaMenulet* menulet;
+	MainController* main;
+}
+
+@end
diff --git a/Slimber/Cocoa/CocoaController.mm b/Slimber/Cocoa/CocoaController.mm
new file mode 100644
index 0000000..437d85a
--- /dev/null
+++ b/Slimber/Cocoa/CocoaController.mm
@@ -0,0 +1,19 @@
+#include "Slimber/Cocoa/CocoaController.h"
+
+#include "Slimber/MainController.h"
+#include "Slimber/Cocoa/CocoaMenulet.h"
+
+@implementation CocoaController
+
+- (void) dealloc {
+	delete main;
+	delete menulet;
+	[super dealloc];
+}
+
+- (void) awakeFromNib {
+	menulet = new CocoaMenulet();
+	main = new MainController(menulet);
+}
+
+@end
diff --git a/Slimber/Cocoa/CocoaMenulet.h b/Slimber/Cocoa/CocoaMenulet.h
new file mode 100644
index 0000000..86f0eaa
--- /dev/null
+++ b/Slimber/Cocoa/CocoaMenulet.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <Cocoa/Cocoa.h>
+
+#include "Slimber/Menulet.h"
+
+class CocoaMenulet : public Menulet {
+	public:
+		CocoaMenulet();
+		~CocoaMenulet();		
+	
+	private:
+		virtual void clear();
+		virtual void addItem(const Swift::String& name, const Swift::String& icon);
+		virtual void addSeparator();
+		void setIcon(const Swift::String& icon);
+		virtual void addAboutItem();
+		virtual void addExitItem();
+	
+	private:
+		NSStatusItem* statusItem;
+		NSMenu* menu;
+};
diff --git a/Slimber/Cocoa/CocoaMenulet.mm b/Slimber/Cocoa/CocoaMenulet.mm
new file mode 100644
index 0000000..c159aa8
--- /dev/null
+++ b/Slimber/Cocoa/CocoaMenulet.mm
@@ -0,0 +1,66 @@
+#include "Slimber/Cocoa/CocoaMenulet.h"
+
+using namespace Swift;
+
+CocoaMenulet::CocoaMenulet() {
+	menu = [[NSMenu alloc] init];
+
+	statusItem = [[[NSStatusBar systemStatusBar] 
+			statusItemWithLength: NSVariableStatusItemLength] retain];
+	[statusItem setHighlightMode: YES];
+	[statusItem setEnabled: YES];
+	[statusItem setToolTip: @"Slimber"];	
+	[statusItem setMenu: menu];
+}
+
+CocoaMenulet::~CocoaMenulet() {
+	[statusItem release];
+	[menu release];
+}
+
+void CocoaMenulet::setIcon(const String& icon) {
+	NSString* path = [[NSBundle mainBundle] pathForResource: 
+			[NSString stringWithUTF8String: icon.getUTF8Data()] ofType:@"png"];
+	NSImage* image = [[NSImage alloc] initWithContentsOfFile: path];
+	[statusItem setImage: image];
+	[image release];
+}
+
+void CocoaMenulet::clear() {
+	while ([menu numberOfItems] > 0) {
+		[menu removeItemAtIndex: 0];
+	}
+}
+
+void CocoaMenulet::addItem(const Swift::String& name, const String& icon) {
+	NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: 
+		[NSString stringWithUTF8String: name.getUTF8Data()]
+		action: NULL keyEquivalent: @""];
+	if (!icon.isEmpty()) {
+		NSString* path = [[NSBundle mainBundle] pathForResource: 
+				[NSString stringWithUTF8String: icon.getUTF8Data()] ofType:@"png"];
+		NSImage* image = [[NSImage alloc] initWithContentsOfFile: path];
+		[item setImage: [[NSImage alloc] initWithContentsOfFile: path]];
+		[image release];
+	}
+	[menu addItem: item];
+	[item release];
+}
+
+void CocoaMenulet::addAboutItem() {
+	NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"About Slimber" action: @selector(orderFrontStandardAboutPanel:) keyEquivalent: @""];
+	[item setTarget: [NSApplication sharedApplication]];
+	[menu addItem: item];
+	[item release];
+}
+
+void CocoaMenulet::addExitItem() {
+	NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"Exit" action: @selector(terminate:) keyEquivalent: @""];
+	[item setTarget: [NSApplication sharedApplication]];
+	[menu addItem: item];
+	[item release];
+}
+
+void CocoaMenulet::addSeparator() {
+	[menu addItem: [NSMenuItem separatorItem]];
+}
diff --git a/Slimber/Cocoa/MainController.h b/Slimber/Cocoa/MainController.h
deleted file mode 100644
index c6e20c5..0000000
--- a/Slimber/Cocoa/MainController.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <Cocoa/Cocoa.h>
-
-class Slimber;
-
-@interface MainController : NSObject {
-	Slimber* slimber;
-}
-
-@end
diff --git a/Slimber/Cocoa/MainController.mm b/Slimber/Cocoa/MainController.mm
deleted file mode 100644
index 30b7a16..0000000
--- a/Slimber/Cocoa/MainController.mm
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "MainController.h"
-#include "Slimber.h"
-
-@implementation MainController
-
-- (void) dealloc {
-	delete slimber;
-	[super dealloc];
-}
-
-- (void) awakeFromNib {
-	slimber = new Slimber();
-}
-
-@end
diff --git a/Slimber/Cocoa/MainMenu.xib b/Slimber/Cocoa/MainMenu.xib
index 50e4ca3..bed7223 100644
--- a/Slimber/Cocoa/MainMenu.xib
+++ b/Slimber/Cocoa/MainMenu.xib
@@ -180,7 +180,7 @@
 				<string key="NSName">_NSMainMenu</string>
 			</object>
 			<object class="NSCustomObject" id="16040424">
-				<string key="NSClassName">MainController</string>
+				<string key="NSClassName">CocoaController</string>
 			</object>
 		</object>
 		<object class="IBObjectContainer" key="IBDocument.Objects">
@@ -482,11 +482,11 @@
 			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.1+">
 				<bool key="EncodedWithXMLCoder">YES</bool>
 				<object class="IBPartialClassDescription">
-					<string key="className">MainController</string>
+					<string key="className">CocoaController</string>
 					<string key="superclassName">NSObject</string>
 					<object class="IBClassDescriptionSource" key="sourceIdentifier">
 						<string key="majorKey">IBDocumentRelativeSource</string>
-						<string key="minorKey">MainController.h</string>
+						<string key="minorKey">CocoaController.h</string>
 					</object>
 				</object>
 			</object>
diff --git a/Slimber/Cocoa/Makefile.inc b/Slimber/Cocoa/Makefile.inc
index 8cd72cd..6d225a7 100644
--- a/Slimber/Cocoa/Makefile.inc
+++ b/Slimber/Cocoa/Makefile.inc
@@ -2,10 +2,9 @@ SLIMBER_COCOA_TARGET = Slimber/Cocoa/Slimber.app
 SLIMBER_COCOA_BINARY = \
 	Slimber/Cocoa/Slimber
 SLIMBER_COCOA_SOURCES = \
-	Slimber/Cocoa/MainController.mm \
-	Slimber/Cocoa/Slimber.mm \
 	Slimber/Cocoa/main.mm \
-	Slimber/Cocoa/Menulet.m
+	Slimber/Cocoa/CocoaController.mm \
+	Slimber/Cocoa/CocoaMenulet.mm
 SLIMBER_COCOA_XIBS = \
 	Slimber/Cocoa/MainMenu.xib
 SLIMBER_COCOA_RESOURCES = \
@@ -26,8 +25,8 @@ CLEANFILES += \
 	$(SLIMBER_COCOA_NIBS) \
 	$(SLIMBER_COCOA_TARGET) \
 	$(SLIMBER_COCOA_BINARY)
-//DEPS += \
-//	$(SLIMBER_COCOA_SOURCES:.m=.dep) \
+DEPS += \
+	$(SLIMBER_COCOA_SOURCES:.mm=.dep)
 
 .PHONY: slimber-cocoa
 slimber-cocoa: $(SLIMBER_COCOA_TARGET)
@@ -42,5 +41,5 @@ $(SLIMBER_COCOA_TARGET): $(SLIMBER_COCOA_BINARY) $(SLIMBER_COCOA_NIBS) Slimber/C
 	cp $(SLIMBER_COCOA_NIBS) $(SLIMBER_COCOA_TARGET)/Contents/Resources
 	cp $(SLIMBER_COCOA_RESOURCES) $(SLIMBER_COCOA_TARGET)/Contents/Resources
 
-$(SLIMBER_COCOA_BINARY): $(SLIMBER_COCOA_OBJECTS) $(SWIFTEN_TARGET) $(SLIMBER_TRGET)
+$(SLIMBER_COCOA_BINARY): $(SLIMBER_COCOA_OBJECTS) $(SWIFTEN_TARGET) $(SLIMBER_TARGET)
 	$(QUIET_LINK)$(CXX) -o $@ $(SLIMBER_COCOA_OBJECTS) $(LDFLAGS) $(SLIMBER_TARGET) $(SWIFTEN_TARGET) $(LIBS) -framework Cocoa
diff --git a/Slimber/Cocoa/Menulet.h b/Slimber/Cocoa/Menulet.h
deleted file mode 100644
index 823213f..0000000
--- a/Slimber/Cocoa/Menulet.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <Cocoa/Cocoa.h>
-
-@interface Menulet : NSObject {
-	NSStatusItem* statusItem;
-	NSMenu* statusMenu;
-	NSImage* menuIcon;
-	NSArray* userNames;
-	BOOL xmppOnline;
-	NSString* xmppStatus;
-}
-
-- (id) init;
-- (void) updateMenu;
-- (void) setUserNames: (NSArray*) names;
-- (void) setXMPPStatus: (NSString*) status online: (BOOL) online;
-
-@end
diff --git a/Slimber/Cocoa/Menulet.m b/Slimber/Cocoa/Menulet.m
deleted file mode 100644
index 2a62992..0000000
--- a/Slimber/Cocoa/Menulet.m
+++ /dev/null
@@ -1,105 +0,0 @@
-#import "Menulet.h"
-
-@implementation Menulet
-
-- (id) init {
-	if ([super init]) {
-		statusMenu = [[NSMenu alloc] init];
-
-		statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSVariableStatusItemLength] retain];
-		[statusItem setHighlightMode: YES];
-		[statusItem setEnabled: YES];
-		[statusItem setToolTip: @"Slimber"];	
-		[statusItem setMenu: statusMenu];
-
-		xmppStatus = @"";
-		xmppOnline = NO;
-		userNames = [[NSArray alloc] init];
-
-		[self updateMenu];
-	}
-	return self;
-}
-
-- (void) dealloc {
-	[statusItem release];
-	[menuIcon release];
-	[super dealloc];
-}
-
-- (void) updateIcon: (BOOL) online {
-	NSBundle* bundle = [NSBundle bundleForClass: [self class]];
-	NSString* path;
-	if (online) {
-		path = [bundle pathForResource: @"UsersOnline" ofType:@"png"];
-	}
-	else {
-		path = [bundle pathForResource: @"UsersOffline" ofType:@"png"];
-	}
-	[statusItem setImage: [[NSImage alloc] initWithContentsOfFile: path]];
-}
-
-- (void) updateMenu {
-	// Clear the menu
-	while ([statusMenu numberOfItems] > 0) {
-		[statusMenu removeItemAtIndex: 0];
-	}
-
-	// User items
-	if ([userNames count] > 0) {
-		[statusMenu addItem: [[NSMenuItem alloc] initWithTitle: @"Online users:" action: NULL keyEquivalent: @""]];
-		int i;
-		for (i = 0; i < [userNames count]; ++i) {
-			NSString* text = [NSString stringWithFormat: @"  %@", [userNames objectAtIndex: i]]; 
-			NSMenuItem* userItem = [[NSMenuItem alloc] initWithTitle: text action: NULL keyEquivalent: @""];
-			[statusMenu addItem: userItem];
-			[userItem release];
-		}
-	}
-	else {
-		[statusMenu addItem: [[NSMenuItem alloc] initWithTitle: @"No online users" action: NULL keyEquivalent: @""]];
-	}
-	[self updateIcon: [userNames count] > 0];
-	[statusMenu addItem: [NSMenuItem separatorItem]];
-
-	// Self item
-	NSMenuItem* loggedInItem;
-	NSBundle* bundle = [NSBundle bundleForClass: [self class]];
-	NSString* path;
-	loggedInItem = [[NSMenuItem alloc] initWithTitle: xmppStatus action: NULL keyEquivalent: @""];
-	if (xmppOnline) {
-		path = [bundle pathForResource: @"Online" ofType:@"png"];
-	}
-	else {
-		path = [bundle pathForResource: @"Offline" ofType:@"png"];
-	}
-	[loggedInItem setImage: [[NSImage alloc] initWithContentsOfFile: path]];
-	[statusMenu addItem: loggedInItem];
-	[statusMenu addItem: [NSMenuItem separatorItem]];
-
-	// About menu
-	NSMenuItem* aboutMenuItem = [[NSMenuItem alloc] initWithTitle: @"About Slimber" action: @selector(orderFrontStandardAboutPanel:) keyEquivalent: @""];
-	[aboutMenuItem setTarget: [NSApplication sharedApplication]];
-	[statusMenu addItem: aboutMenuItem];
-	[statusMenu addItem: [NSMenuItem separatorItem]];
-
-	// Exit item
-	NSMenuItem* exitMenuItem = [[NSMenuItem alloc] initWithTitle: @"Exit" action: @selector(terminate:) keyEquivalent: @""];
-	[exitMenuItem setTarget: [NSApplication sharedApplication]];
-	[statusMenu addItem: exitMenuItem];
-}
-
-- (void) setXMPPStatus: (NSString*) status online: (BOOL) online {
-	xmppStatus = status; // TODO: Should I retain status?
-	xmppOnline = online;
-	[self updateMenu];
-}
-
-- (void) setUserNames: (NSArray*) names {
-	[names retain];
-	[userNames release];
-	userNames = names;
-	[self updateMenu];
-}
-
-@end
diff --git a/Slimber/Cocoa/Slimber.h b/Slimber/Cocoa/Slimber.h
deleted file mode 100644
index fdce501..0000000
--- a/Slimber/Cocoa/Slimber.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
-
-#include "Slimber/ServerError.h"
-
-@class Menulet;
-namespace Swift {
-	class Server;
-	class VCardCollection;
-	class LinkLocalServiceBrowser;
-	class BonjourQuerier;
-}
-
-class Slimber {
-	public:
-		Slimber();
-		~Slimber();
-
-	private:
-		void handleSelfConnected(bool b);
-		void handleServicesChanged();
-		void handleServerStopped(boost::optional<Swift::ServerError> error);
-
-	private:
-		boost::shared_ptr<Swift::BonjourQuerier> dnsSDQuerier;
-		Swift::LinkLocalServiceBrowser* linkLocalServiceBrowser;
-		Swift::VCardCollection* vCardCollection;
-		Swift::Server* server;
-		Menulet* menulet;
-};
diff --git a/Slimber/Cocoa/Slimber.mm b/Slimber/Cocoa/Slimber.mm
deleted file mode 100644
index 9e15614..0000000
--- a/Slimber/Cocoa/Slimber.mm
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "Slimber/Cocoa/Slimber.h"
-
-#include <boost/bind.hpp>
-
-#include "Swiften/Base/foreach.h"
-#include "Swiften/Application/Platform/PlatformApplication.h"
-#include "Swiften/LinkLocal/LinkLocalService.h"
-#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
-#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h"
-#include "Slimber/Cocoa/Menulet.h"
-#include "Slimber/Server.h"
-#include "Slimber/FileVCardCollection.h"
-
-using namespace Swift;
-
-Slimber::Slimber() {
-	dnsSDQuerier = boost::shared_ptr<BonjourQuerier>(new BonjourQuerier());
-	dnsSDQuerier->start();
-
-	linkLocalServiceBrowser = new LinkLocalServiceBrowser(dnsSDQuerier);
-	linkLocalServiceBrowser->onServiceAdded.connect(
-			boost::bind(&Slimber::handleServicesChanged, this));
-	linkLocalServiceBrowser->onServiceRemoved.connect(
-			boost::bind(&Slimber::handleServicesChanged, this));
-	linkLocalServiceBrowser->onServiceChanged.connect(
-			boost::bind(&Slimber::handleServicesChanged, this));
-	linkLocalServiceBrowser->start();
-
-	vCardCollection = new FileVCardCollection(
-			PlatformApplication("Slimber").getSettingsDir());
-
-	server = new Server(5222, 5562, linkLocalServiceBrowser, vCardCollection);
-	server->onStopped.connect(
-			boost::bind(&Slimber::handleServerStopped, this, _1));
-	server->onSelfConnected.connect(
-			boost::bind(&Slimber::handleSelfConnected, this, _1));
-
-	menulet = [[Menulet alloc] init];
-	handleSelfConnected(false);
-	handleServicesChanged();
-
-	server->start();
-}
-
-Slimber::~Slimber() {
-	[menulet release];
-	delete server;
-	delete vCardCollection;
-	linkLocalServiceBrowser->stop();
-	delete linkLocalServiceBrowser;
-	dnsSDQuerier->stop();
-}
-
-void Slimber::handleSelfConnected(bool b) {
-	if (b) {
-		[menulet setXMPPStatus: @"You are logged in" online: true];
-	}
-	else {
-		[menulet setXMPPStatus: @"You are not logged in" online: false];
-	}
-}
-
-void Slimber::handleServicesChanged() {
-	NSMutableArray* names = [[NSMutableArray alloc] init];
-	foreach(const LinkLocalService& service, linkLocalServiceBrowser->getServices()) {
-		[names addObject: [NSString stringWithUTF8String: service.getDescription().getUTF8Data()]];
-	}
-
-	[menulet setUserNames: names];
-	[names release];
-}
-
-void Slimber::handleServerStopped(boost::optional<ServerError> error) {
-	if (error) {
-		[menulet setXMPPStatus: @"XMPP Server Error." online: false];
-	}
-	else {
-		[menulet setXMPPStatus: @"XMPP Server Not Runnning." online: false];
-	}
-}
diff --git a/Slimber/MainController.cpp b/Slimber/MainController.cpp
new file mode 100644
index 0000000..2d196a5
--- /dev/null
+++ b/Slimber/MainController.cpp
@@ -0,0 +1,80 @@
+#include "Slimber/MainController.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Application/Platform/PlatformApplication.h"
+#include "Swiften/LinkLocal/LinkLocalService.h"
+#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h"
+#include "Slimber/Server.h"
+#include "Slimber/FileVCardCollection.h"
+#include "Slimber/MenuletController.h"
+#include "Slimber/Menulet.h"
+
+using namespace Swift;
+
+MainController::MainController(Menulet* menulet) : menulet(menulet) {
+	dnsSDQuerier = boost::shared_ptr<BonjourQuerier>(new BonjourQuerier());
+	dnsSDQuerier->start();
+
+	linkLocalServiceBrowser = new LinkLocalServiceBrowser(dnsSDQuerier);
+	linkLocalServiceBrowser->onServiceAdded.connect(
+			boost::bind(&MainController::handleServicesChanged, this));
+	linkLocalServiceBrowser->onServiceRemoved.connect(
+			boost::bind(&MainController::handleServicesChanged, this));
+	linkLocalServiceBrowser->onServiceChanged.connect(
+			boost::bind(&MainController::handleServicesChanged, this));
+	linkLocalServiceBrowser->start();
+
+	vCardCollection = new FileVCardCollection(
+			PlatformApplication("Slimber").getSettingsDir());
+
+	server = new Server(5222, 5562, linkLocalServiceBrowser, vCardCollection);
+	server->onStopped.connect(
+			boost::bind(&MainController::handleServerStopped, this, _1));
+	server->onSelfConnected.connect(
+			boost::bind(&MainController::handleSelfConnected, this, _1));
+
+	menuletController = new MenuletController(menulet);
+
+	handleSelfConnected(false);
+	handleServicesChanged();
+
+	server->start();
+}
+
+MainController::~MainController() {
+	delete menuletController;
+	delete server;
+	delete vCardCollection;
+	linkLocalServiceBrowser->stop();
+	delete linkLocalServiceBrowser;
+	dnsSDQuerier->stop();
+}
+
+void MainController::handleSelfConnected(bool b) {
+	if (b) {
+		menuletController->setXMPPStatus("You are logged in", MenuletController::Online);
+	}
+	else {
+		menuletController->setXMPPStatus("You are not logged in", MenuletController::Offline);
+	}
+}
+
+void MainController::handleServicesChanged() {
+	std::vector<String> names;
+	foreach(const LinkLocalService& service, linkLocalServiceBrowser->getServices()) {
+		names.push_back(service.getDescription());
+	}
+	menuletController->setUserNames(names);
+}
+
+void MainController::handleServerStopped(boost::optional<ServerError> error) {
+	if (error) {
+		menuletController->setXMPPStatus("XMPP Server Error", MenuletController::Offline);
+	}
+	else {
+		menuletController->setXMPPStatus("XMPP Server Not Running", MenuletController::Offline);
+	}
+}
diff --git a/Slimber/MainController.h b/Slimber/MainController.h
new file mode 100644
index 0000000..8d355fe
--- /dev/null
+++ b/Slimber/MainController.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include "Slimber/ServerError.h"
+
+namespace Swift {
+	class DNSSDQuerier;
+	class LinkLocalServiceBrowser;
+	class VCardCollection;
+	class Server;
+}
+
+class MenuletController;
+class Menulet;
+
+class MainController {
+	public:
+		MainController(Menulet* menulet);
+		virtual ~MainController();
+
+	private:
+		void handleSelfConnected(bool b);
+		void handleServicesChanged();
+		void handleServerStopped(boost::optional<Swift::ServerError> error);
+
+	private:
+		Menulet* menulet;
+		boost::shared_ptr<Swift::DNSSDQuerier> dnsSDQuerier;
+		Swift::LinkLocalServiceBrowser* linkLocalServiceBrowser;
+		Swift::VCardCollection* vCardCollection;
+		Swift::Server* server;
+		MenuletController* menuletController;
+};
diff --git a/Slimber/Makefile.inc b/Slimber/Makefile.inc
index 16fcb8f..2821d18 100644
--- a/Slimber/Makefile.inc
+++ b/Slimber/Makefile.inc
@@ -3,7 +3,10 @@ SLIMBER_SOURCES = \
 	Slimber/LinkLocalPresenceManager.cpp \
 	Slimber/FileVCardCollection.cpp \
 	Slimber/VCardCollection.cpp \
-	Slimber/Server.cpp
+	Slimber/Server.cpp \
+	Slimber/MainController.cpp \
+	Slimber/MenuletController.cpp \
+	Slimber/Menulet.cpp
 SLIMBER_OBJECTS = \
 	$(SLIMBER_SOURCES:.cpp=.o)
 
diff --git a/Slimber/Menulet.cpp b/Slimber/Menulet.cpp
new file mode 100644
index 0000000..bdadb98
--- /dev/null
+++ b/Slimber/Menulet.cpp
@@ -0,0 +1,4 @@
+#include "Slimber/Menulet.h"
+
+Menulet::~Menulet() {
+}
diff --git a/Slimber/Menulet.h b/Slimber/Menulet.h
new file mode 100644
index 0000000..631fbbd
--- /dev/null
+++ b/Slimber/Menulet.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+
+class Menulet {
+	public:
+		virtual ~Menulet();
+
+		virtual void clear() = 0;
+		virtual void addItem(const Swift::String& name, const Swift::String& icon = Swift::String()) = 0;
+		virtual void addAboutItem() = 0;
+		virtual void addExitItem() = 0;
+		virtual void addSeparator() = 0;
+		virtual void setIcon(const Swift::String&) = 0;
+};
diff --git a/Slimber/MenuletController.cpp b/Slimber/MenuletController.cpp
new file mode 100644
index 0000000..09face5
--- /dev/null
+++ b/Slimber/MenuletController.cpp
@@ -0,0 +1,49 @@
+#include "Slimber/MenuletController.h"
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Base/String.h"
+#include "Slimber/Menulet.h"
+
+#include <iostream>
+
+using namespace Swift;
+
+MenuletController::MenuletController(Menulet* menulet) : 
+		menulet(menulet), xmppStatus(Offline) {
+	update();
+}
+
+MenuletController::~MenuletController() {
+}
+
+void MenuletController::setXMPPStatus(const String& message, Status status) {
+	xmppStatus = status;
+	xmppStatusMessage = message;
+	update();
+}
+
+void MenuletController::setUserNames(const std::vector<String>& users) {
+	linkLocalUsers = users;
+	update();
+}
+
+void MenuletController::update() {
+	menulet->clear();
+	if (linkLocalUsers.empty()) {
+		menulet->setIcon("UsersOffline");
+		menulet->addItem("No online users");
+	}
+	else {
+		menulet->setIcon("UsersOnline");
+		menulet->addItem("Online users:");
+		foreach(const String& user, linkLocalUsers) {
+			menulet->addItem(user);
+		}
+	}
+	menulet->addSeparator();
+	menulet->addItem(xmppStatusMessage, (xmppStatus == Online ? "Online" : "Offline"));
+	menulet->addSeparator();
+	menulet->addAboutItem();
+	menulet->addSeparator();
+	menulet->addExitItem();
+}
diff --git a/Slimber/MenuletController.h b/Slimber/MenuletController.h
new file mode 100644
index 0000000..4073900
--- /dev/null
+++ b/Slimber/MenuletController.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <vector>
+
+#include "Swiften/Base/String.h"
+
+class Menulet;
+
+class MenuletController {
+	public:
+		enum Status {
+			Online,
+			Offline
+		};
+
+		MenuletController(Menulet*);
+		virtual ~MenuletController();
+
+		void setXMPPStatus(const Swift::String& message, Status status);
+		void setUserNames(const std::vector<Swift::String>&);
+	
+	private:
+		void update();
+	
+	private:
+		Menulet* menulet;
+		Status xmppStatus;
+		Swift::String xmppStatusMessage;
+		std::vector<Swift::String> linkLocalUsers;
+};
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h
index 799bc0c..efcc140 100644
--- a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h
+++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h
@@ -15,6 +15,9 @@ namespace Swift {
 		public:
 			virtual ~DNSSDQuerier();
 
+			virtual void start() = 0;
+			virtual void stop() = 0;
+
 			virtual boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery() = 0;
 			virtual boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery(
 					const String& name, int port, const ByteArray& info) = 0;
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h
index 5af49dc..c67ba7b 100644
--- a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h
@@ -24,6 +24,9 @@ namespace Swift {
 		public:
 			FakeDNSSDQuerier(const String& domain);
 
+			void start() {}
+			void stop() {}
+
 			boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery();
 			boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery(
 					const String& name, int port, const ByteArray& info);
-- 
cgit v0.10.2-6-g49f6