From 25b10f8d2b77bc5b3a40aac8d2edd5d42e1b6585 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Sun, 26 Feb 2012 15:32:39 +0100
Subject: Adding basic vCard edit/show support.

Change-Id: I3104efcb9d56cfcaafda45eac2a51d2702f5245b
License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.

diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 937116f..a61b5f0 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -24,6 +24,7 @@
 #include <Swift/Controllers/UIEvents/UIEventStream.h>
 #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
 #include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
+#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h>
 #include <Swift/Controllers/Roster/GroupRosterItem.h>
 #include <Swift/Controllers/Roster/ContactRosterItem.h>
 #include <Swiften/Avatars/AvatarManager.h>
@@ -150,6 +151,7 @@ void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item
 		if (muc_->getOccupant(item->getJID().getResource()).getRealJID()) {
 			actions.push_back(ChatWindow::AddContact);
 		}
+		actions.push_back(ChatWindow::ShowProfile);
 	}
 	chatWindow_->setAvailableOccupantActions(actions);
 }
@@ -168,6 +170,7 @@ void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction a
 		case ChatWindow::MakeParticipant: muc_->changeOccupantRole(mucJID, MUCOccupant::Participant);break;
 		case ChatWindow::MakeVisitor: muc_->changeOccupantRole(mucJID, MUCOccupant::Visitor);break;
 		case ChatWindow::AddContact: if (occupant.getRealJID()) events_->send(boost::make_shared<RequestAddUserDialogUIEvent>(realJID, occupant.getNick()));break;
+		case ChatWindow::ShowProfile: events_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(mucJID));break;
 	}
 }
 
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index bb74ed7..87ec94d 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -75,6 +75,7 @@
 #include "Swift/Controllers/Storages/CertificateStorageTrustChecker.h"
 #include "Swiften/Network/NetworkFactories.h"
 #include <Swift/Controllers/ProfileController.h>
+#include <Swift/Controllers/ShowProfileController.h>
 #include <Swift/Controllers/ContactEditController.h>
 #include <Swift/Controllers/XMPPURIController.h>
 #include "Swift/Controllers/AdHocManager.h"
@@ -129,6 +130,7 @@ MainController::MainController(
 	historyViewController_ = NULL;
 	eventWindowController_ = NULL;
 	profileController_ = NULL;
+	showProfileController_ = NULL;
 	contactEditController_ = NULL;
 	userSearchControllerChat_ = NULL;
 	userSearchControllerAdd_ = NULL;
@@ -239,6 +241,8 @@ void MainController::resetClient() {
 	contactEditController_ = NULL;
 	delete profileController_;
 	profileController_ = NULL;
+	delete showProfileController_;
+	showProfileController_ = NULL;
 	delete eventWindowController_;
 	eventWindowController_ = NULL;
 	delete chatsManager_;
@@ -311,6 +315,7 @@ void MainController::handleConnected() {
 	myStatusLooksOnline_ = true;
 	if (freshLogin) {
 		profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
+		showProfileController_ = new ShowProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
 		srand(static_cast<unsigned int>(time(NULL)));
 		int randomPort = 10000 + rand() % 10000;
 		client_->getFileTransferManager()->startListeningOnPort(randomPort);
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index fc8d518..4f37e12 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -43,6 +43,7 @@ namespace Swift {
 	class MUCController;
 	class Notifier;
 	class ProfileController;
+	class ShowProfileController;
 	class ContactEditController;
 	class TogglableNotifier;
 	class PresenceNotifier;
@@ -155,6 +156,7 @@ namespace Swift {
 			FileTransferListController* fileTransferListController_;
 			ChatsManager* chatsManager_;
 			ProfileController* profileController_;
+			ShowProfileController* showProfileController_;
 			ContactEditController* contactEditController_;
 			JID jid_;
 			JID boundJID_;
diff --git a/Swift/Controllers/ProfileController.cpp b/Swift/Controllers/ProfileController.cpp
index 101e283..e641988 100644
--- a/Swift/Controllers/ProfileController.cpp
+++ b/Swift/Controllers/ProfileController.cpp
@@ -37,6 +37,7 @@ void ProfileController::handleUIEvent(UIEvent::ref event) {
 
 	if (!profileWindow) {
 		profileWindow = profileWindowFactory->createProfileWindow();
+		profileWindow->setEditable(true);
 		profileWindow->onVCardChangeRequest.connect(boost::bind(&ProfileController::handleVCardChangeRequest, this, _1));
 		vcardManager->onOwnVCardChanged.connect(boost::bind(&ProfileController::handleOwnVCardChanged, this, _1));
 	}
diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript
index cd88dd9..26b9334 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -30,6 +30,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Chat/UserSearchController.cpp",
 			"MainController.cpp",
 			"ProfileController.cpp",
+			"ShowProfileController.cpp",
 			"ContactEditController.cpp",
 			"FileTransfer/FileTransferController.cpp",
 			"FileTransfer/FileTransferOverview.cpp",
diff --git a/Swift/Controllers/ShowProfileController.cpp b/Swift/Controllers/ShowProfileController.cpp
new file mode 100644
index 0000000..ee0854b
--- /dev/null
+++ b/Swift/Controllers/ShowProfileController.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "ShowProfileController.h"
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/VCards/VCardManager.h>
+
+#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h>
+
+namespace Swift {
+
+ShowProfileController::ShowProfileController(VCardManager* vcardManager, ProfileWindowFactory* profileWindowFactory, UIEventStream* uiEventStream) : vcardManager(vcardManager), profileWindowFactory(profileWindowFactory), uiEventStream(uiEventStream) {
+	uiEventStream->onUIEvent.connect(boost::bind(&ShowProfileController::handleUIEvent, this, _1));
+	vcardManager->onVCardChanged.connect(boost::bind(&ShowProfileController::handleVCardChanged, this, _1, _2));
+}
+
+ShowProfileController::~ShowProfileController() {
+	typedef std::pair<JID, ProfileWindow*> JIDProfileWindowPair;
+	foreach(const JIDProfileWindowPair& jidWndPair, openedProfileWindows) {
+		jidWndPair.second->onWindowClosed.disconnect(boost::bind(&ShowProfileController::handleProfileWindowClosed, this, _1));
+		delete jidWndPair.second;
+	}
+
+	vcardManager->onVCardChanged.disconnect(boost::bind(&ShowProfileController::handleVCardChanged, this, _1, _2));
+	uiEventStream->onUIEvent.disconnect(boost::bind(&ShowProfileController::handleUIEvent, this, _1));
+}
+
+void ShowProfileController::handleUIEvent(UIEvent::ref event) {
+	ShowProfileForRosterItemUIEvent::ref showProfileEvent = boost::dynamic_pointer_cast<ShowProfileForRosterItemUIEvent>(event);
+	if (!showProfileEvent) {
+		return;
+	}
+
+	if (openedProfileWindows.find(showProfileEvent->getJID()) == openedProfileWindows.end()) {
+		ProfileWindow* newProfileWindow = profileWindowFactory->createProfileWindow();
+		newProfileWindow->setJID(showProfileEvent->getJID());
+		newProfileWindow->onWindowClosed.connect(boost::bind(&ShowProfileController::handleProfileWindowClosed, this, _1));
+		openedProfileWindows[showProfileEvent->getJID()] = newProfileWindow;
+		VCard::ref vcard = vcardManager->getVCardAndRequestWhenNeeded(showProfileEvent->getJID());
+		if (vcard) {
+			newProfileWindow->setVCard(vcard);
+		} else {
+			newProfileWindow->setProcessing(true);
+		}
+		newProfileWindow->show();
+	} else {
+		openedProfileWindows[showProfileEvent->getJID()]->show();
+	}
+}
+
+void ShowProfileController::handleVCardChanged(const JID& jid, VCard::ref vcard) {
+	if (openedProfileWindows.find(jid) == openedProfileWindows.end()) {
+		return;
+	}
+
+	ProfileWindow* profileWindow = openedProfileWindows[jid];
+	profileWindow->setVCard(vcard);
+	profileWindow->setProcessing(false);
+	profileWindow->show();
+}
+
+void ShowProfileController::handleProfileWindowClosed(const JID& profileJid) {
+	ProfileWindow* profileWindow = openedProfileWindows[profileJid];
+	openedProfileWindows.erase(profileJid);
+	delete profileWindow;
+}
+
+}
diff --git a/Swift/Controllers/ShowProfileController.h b/Swift/Controllers/ShowProfileController.h
new file mode 100644
index 0000000..5646f5e
--- /dev/null
+++ b/Swift/Controllers/ShowProfileController.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/VCard.h>
+
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+	class VCardManager;
+	class ProfileWindow;
+	class ProfileWindowFactory;
+	class UIEventStream;
+
+	class ShowProfileController {
+		public:
+			ShowProfileController(VCardManager* vcardManager, ProfileWindowFactory* profileWindowFactory, UIEventStream* uiEventStream);
+			~ShowProfileController();
+
+		private:
+			void handleUIEvent(UIEvent::ref event);
+			void handleVCardChanged(const JID&, VCard::ref);
+			void handleProfileWindowClosed(const JID& profileJid);
+
+		private:
+			VCardManager* vcardManager;
+			ProfileWindowFactory* profileWindowFactory;
+			UIEventStream* uiEventStream;
+			std::map<JID, ProfileWindow*> openedProfileWindows;
+	};
+}
diff --git a/Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h b/Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h
new file mode 100644
index 0000000..4a603ea
--- /dev/null
+++ b/Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/JID/JID.h>
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+
+class ShowProfileForRosterItemUIEvent : public UIEvent {
+	public:
+		typedef boost::shared_ptr<ShowProfileForRosterItemUIEvent> ref;
+	public:
+		ShowProfileForRosterItemUIEvent(const JID& jid) : jid_(jid) {}
+		virtual ~ShowProfileForRosterItemUIEvent() {}
+		JID getJID() const {return jid_;}
+	private:
+		JID jid_;
+};
+
+}
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 252e43d..d6b3656 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -35,7 +35,7 @@ namespace Swift {
 			enum AckState {Pending, Received, Failed};
 			enum ReceiptState {ReceiptRequested, ReceiptReceived};
 			enum Tristate {Yes, No, Maybe};
-			enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact};
+			enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact, ShowProfile};
 			enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite};
 			enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};
 			enum WhiteboardSessionState {WhiteboardAccepted, WhiteboardTerminated, WhiteboardRejected};
diff --git a/Swift/Controllers/UIInterfaces/ProfileWindow.h b/Swift/Controllers/UIInterfaces/ProfileWindow.h
index 2ed1bc1..0ce2ccf 100644
--- a/Swift/Controllers/UIInterfaces/ProfileWindow.h
+++ b/Swift/Controllers/UIInterfaces/ProfileWindow.h
@@ -12,19 +12,24 @@
 #include <Swiften/Elements/VCard.h>
 
 namespace Swift {
+	class JID;
+
 	class ProfileWindow {
 		public:
 			virtual ~ProfileWindow() {}
 
+			virtual void setJID(const JID& jid) = 0;
 			virtual void setVCard(VCard::ref vcard) = 0;
 
 			virtual void setEnabled(bool b) = 0;
 			virtual void setProcessing(bool b) = 0;
 			virtual void setError(const std::string&) = 0;
+			virtual void setEditable(bool b) = 0;
 
 			virtual void show() = 0;
 			virtual void hide() = 0;
 
 			boost::signal<void (VCard::ref)> onVCardChangeRequest;
+			boost::signal<void (const JID&)> onWindowClosed;
 	};
 }
diff --git a/Swift/QtUI/QtAvatarWidget.cpp b/Swift/QtUI/QtAvatarWidget.cpp
index f0bdf3c..ae9559a 100644
--- a/Swift/QtUI/QtAvatarWidget.cpp
+++ b/Swift/QtUI/QtAvatarWidget.cpp
@@ -68,6 +68,9 @@ void QtAvatarWidget::setAvatar(const ByteArray& data, const std::string& type) {
 }
 
 void QtAvatarWidget::mousePressEvent(QMouseEvent* event) {
+	if (!editable) {
+		return;
+	}
 	QMenu menu;
 
 	QAction* selectPicture = new QAction(tr("Select picture ..."), this);
diff --git a/Swift/QtUI/QtAvatarWidget.h b/Swift/QtUI/QtAvatarWidget.h
index 8830d82..f4ac4cf 100644
--- a/Swift/QtUI/QtAvatarWidget.h
+++ b/Swift/QtUI/QtAvatarWidget.h
@@ -15,6 +15,7 @@ class QLabel;
 namespace Swift {
 	class QtAvatarWidget : public QWidget {
 			Q_OBJECT
+			Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
 		public:
 			QtAvatarWidget(QWidget* parent);
 
@@ -28,9 +29,18 @@ namespace Swift {
 				return type;
 			}
 
+			void setEditable(bool b) {
+				editable = b;
+			}
+
+			bool isEditable() const {
+				return editable;
+			}
+
 			void mousePressEvent(QMouseEvent* event);
 
 		private:
+			bool editable;
 			ByteArray data;
 			std::string type;
 			QLabel* label;
diff --git a/Swift/QtUI/QtProfileWindow.cpp b/Swift/QtUI/QtProfileWindow.cpp
index 0faa78f..ccc6ae9 100644
--- a/Swift/QtUI/QtProfileWindow.cpp
+++ b/Swift/QtUI/QtProfileWindow.cpp
@@ -4,97 +4,80 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
 #include "QtProfileWindow.h"
+#include "ui_QtProfileWindow.h"
 
-#include <QImage>
-#include <QPixmap>
-#include <QSizePolicy>
-#include <QGridLayout>
-#include <QLabel>
-#include <QLineEdit>
-#include <QPushButton>
+#include <QCloseEvent>
 #include <QMovie>
+#include <QShortcut>
+#include <QTextDocument>
 
-#include "QtSwiftUtil.h"
-#include "QtAvatarWidget.h"
+#include <Swift/QtUI/QtSwiftUtil.h>
 
 namespace Swift {
 
-QtProfileWindow::QtProfileWindow() {
-	setWindowTitle(tr("Edit Profile"));
-
-	QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
-	sizePolicy.setHorizontalStretch(0);
-	sizePolicy.setVerticalStretch(0);
-	sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth());
-	setSizePolicy(sizePolicy);
-
-	QVBoxLayout* layout = new QVBoxLayout(this);
-	layout->setContentsMargins(10, 10, 10, 10);
-
-	QHBoxLayout* topLayout = new QHBoxLayout();
-
-	avatar = new QtAvatarWidget(this);
-	topLayout->addWidget(avatar);
-
-	QVBoxLayout* fieldsLayout = new QVBoxLayout();
-
-	QHBoxLayout* horizontalLayout_2 = new QHBoxLayout();
-	nicknameLabel = new QLabel(tr("Nickname:"), this);
-	horizontalLayout_2->addWidget(nicknameLabel);
-	nickname = new QLineEdit(this);
-	horizontalLayout_2->addWidget(nickname);
-
-	fieldsLayout->addLayout(horizontalLayout_2);
-
-	errorLabel = new QLabel(this);
-	errorLabel->setAlignment(Qt::AlignHCenter);
-	fieldsLayout->addWidget(errorLabel);
-
-	fieldsLayout->addItem(new QSpacerItem(198, 17, QSizePolicy::Minimum, QSizePolicy::Expanding));
-	topLayout->addLayout(fieldsLayout);
-
-	layout->addLayout(topLayout);
-
-	QHBoxLayout* horizontalLayout = new QHBoxLayout();
-	horizontalLayout->setContentsMargins(0, 0, 0, 0);
-	horizontalLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
-
-	throbberLabel = new QLabel(this);
-	throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this));
-	horizontalLayout->addWidget(throbberLabel);
-
-	saveButton = new QPushButton(tr("Save"), this);
-	saveButton->setDefault( true );
-	connect(saveButton, SIGNAL(clicked()), SLOT(handleSave()));
-	horizontalLayout->addWidget(saveButton);
+QtProfileWindow::QtProfileWindow(QWidget* parent) :
+	QWidget(parent),
+	ui(new Ui::QtProfileWindow) {
+	ui->setupUi(this);
+	new QShortcut(QKeySequence::Close, this, SLOT(close()));
+	ui->throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this));
+	connect(ui->savePushButton, SIGNAL(clicked()), SLOT(handleSave()));
+	setEditable(false);
+}
 
-	fieldsLayout->addLayout(horizontalLayout);
+QtProfileWindow::~QtProfileWindow() {
+	delete ui;
+}
 
-	resize(360, 120);
+void QtProfileWindow::setJID(const JID& jid) {
+	this->jid = jid;
+	updateTitle();
 }
 
-void QtProfileWindow::setVCard(Swift::VCard::ref vcard) {
-	this->vcard = vcard;
-	nickname->setText(P2QSTRING(vcard->getNickname()));
-	avatar->setAvatar(vcard->getPhoto(), vcard->getPhotoType());
+void QtProfileWindow::setVCard(VCard::ref vcard) {
+	ui->vcard->setVCard(vcard);
 }
 
 void QtProfileWindow::setEnabled(bool b) {
-	nickname->setEnabled(b);
-	nicknameLabel->setEnabled(b);
-	avatar->setEnabled(b);
-	saveButton->setEnabled(b);
+	ui->vcard->setEnabled(b);
+	ui->savePushButton->setEnabled(b);
+}
+
+void QtProfileWindow::setEditable(bool b) {
+	if (b) {
+		ui->savePushButton->show();
+		ui->vcard->setEditable(true);
+	} else {
+		ui->savePushButton->hide();
+		ui->vcard->setEditable(false);
+	}
+	updateTitle();
 }
 
 void QtProfileWindow::setProcessing(bool processing) {
 	if (processing) {
-		throbberLabel->movie()->start();
-		throbberLabel->show();
+		ui->throbberLabel->movie()->start();
+		ui->throbberLabel->show();
+	}
+	else {
+		ui->throbberLabel->hide();
+		ui->throbberLabel->movie()->stop();
+	}
+}
+
+void QtProfileWindow::setError(const std::string& error) {
+	if (!error.empty()) {
+		ui->errorLabel->setText("<font color='red'>" + Qt::escape(P2QSTRING(error)) + "</font>");
 	}
 	else {
-		throbberLabel->hide();
-		throbberLabel->movie()->stop();
+		ui->errorLabel->setText("");
 	}
 }
 
@@ -103,31 +86,30 @@ void QtProfileWindow::show() {
 	QWidget::activateWindow();
 }
 
-void QtProfileWindow::hideEvent(QHideEvent* event) {
-	QWidget::hideEvent(event);
-}
-
 void QtProfileWindow::hide() {
 	QWidget::hide();
 }
 
-void QtProfileWindow::handleSave() {
-	assert(vcard);
-	vcard->setNickname(Q2PSTRING(nickname->text()));
-	vcard->setPhoto(avatar->getAvatarData());
-	vcard->setPhotoType(avatar->getAvatarType());
-	onVCardChangeRequest(vcard);
-}
-
-void QtProfileWindow::setError(const std::string& error) {
-	if (!error.empty()) {
-		errorLabel->setText("<font color='red'>" + P2QSTRING(error) + "</font>");
+void QtProfileWindow::updateTitle() {
+	QString jidString;
+	if (jid.isValid()) {
+		jidString = QString(" ( %1 )").arg(P2QSTRING(jid.toString()));
 	}
-	else {
-		errorLabel->setText("");
+
+	if (ui->vcard->isEditable()) {
+		setWindowTitle(tr("Edit Profile") + jidString);
+	} else {
+		setWindowTitle(tr("Show Profile") + jidString);
 	}
 }
 
+void QtProfileWindow::closeEvent(QCloseEvent* event) {
+	onWindowClosed(jid);
+	event->accept();
+}
 
+void QtProfileWindow::handleSave() {
+	onVCardChangeRequest(ui->vcard->getVCard());
+}
 
 }
diff --git a/Swift/QtUI/QtProfileWindow.h b/Swift/QtUI/QtProfileWindow.h
index edb9cce..1dbc0fb 100644
--- a/Swift/QtUI/QtProfileWindow.h
+++ b/Swift/QtUI/QtProfileWindow.h
@@ -4,45 +4,54 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
 #pragma once
 
-#include <QWidget>
+#include <Swiften/JID/JID.h>
 
 #include <Swift/Controllers/UIInterfaces/ProfileWindow.h>
 
-class QLabel;
-class QLineEdit;
-class QHBoxLayout;
-class QPushButton;
+#include <QWidget>
+
+namespace Ui {
+	class QtProfileWindow;
+}
 
 namespace Swift {
-	class QtAvatarWidget;
-
-	class QtProfileWindow : public QWidget, public ProfileWindow {
-			Q_OBJECT
-		public:
-			QtProfileWindow();
-
-			void setVCard(Swift::VCard::ref);
-			void setEnabled(bool);
-			void setProcessing(bool);
-			virtual void setError(const std::string&);
-			void show();
-			void hide();
-
-			void hideEvent (QHideEvent* event);
-
-		private slots:
-			void handleSave();
-
-		private:
-			VCard::ref vcard;
-	    QtAvatarWidget* avatar;
-	    QLabel* nicknameLabel;
-	    QLineEdit* nickname;
-	    QLabel* throbberLabel;
-	    QLabel* errorLabel;
-	    QHBoxLayout* horizontalLayout;
-	    QPushButton* saveButton;
-	};
+
+class QtProfileWindow : public QWidget, public ProfileWindow {
+	Q_OBJECT
+
+	public:
+		explicit QtProfileWindow(QWidget* parent = 0);
+		virtual ~QtProfileWindow();
+
+		virtual void setJID(const JID& jid);
+		virtual void setVCard(VCard::ref vcard);
+
+		virtual void setEnabled(bool b);
+		virtual void setProcessing(bool processing);
+		virtual void setError(const std::string& error);
+		virtual void setEditable(bool b);
+
+		virtual void show();
+		virtual void hide();
+
+	private:
+		void updateTitle();
+		virtual void closeEvent(QCloseEvent* event);
+
+	private slots:
+		void handleSave();
+
+	private:
+		Ui::QtProfileWindow* ui;
+		JID jid;
+};
+
 }
diff --git a/Swift/QtUI/QtProfileWindow.ui b/Swift/QtUI/QtProfileWindow.ui
new file mode 100644
index 0000000..68a36da
--- /dev/null
+++ b/Swift/QtUI/QtProfileWindow.ui
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtProfileWindow</class>
+ <widget class="QWidget" name="QtProfileWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>334</width>
+    <height>197</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Edit Profile</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
+   <property name="margin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="Swift::QtVCardWidget" name="vcard" native="true"/>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="sizeConstraint">
+      <enum>QLayout::SetDefaultConstraint</enum>
+     </property>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="errorLabel">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="throbberLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::NoTextInteraction</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="savePushButton">
+       <property name="text">
+        <string>Save</string>
+       </property>
+       <property name="default">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Swift::QtVCardWidget</class>
+   <extends>QWidget</extends>
+   <header>Swift/QtUI/QtVCardWidget/QtVCardWidget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/QtSwiftUtil.h b/Swift/QtUI/QtSwiftUtil.h
index 2d0f970..c903af1 100644
--- a/Swift/QtUI/QtSwiftUtil.h
+++ b/Swift/QtUI/QtSwiftUtil.h
@@ -9,4 +9,6 @@
 #define P2QSTRING(a) QString::fromUtf8(a.c_str())
 #define Q2PSTRING(a) std::string(a.toUtf8())
 
+#include <boost/date_time/posix_time/posix_time.hpp>
+
 #define B2QDATE(a) QDateTime::fromTime_t((a - boost::posix_time::from_time_t(0)).total_seconds())
diff --git a/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp b/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp
new file mode 100644
index 0000000..a6afe81
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtCloseButton.h"
+
+#include <QMouseEvent>
+#include <QPainter>
+#include <QStyle>
+#include <QStyleOption>
+
+namespace Swift {
+
+QtCloseButton::QtCloseButton(QWidget *parent) : QAbstractButton(parent) {
+	lightPixmap = QPixmap(12,12);
+	lightPixmap.fill(QColor(0,0,0,0));
+	QStyleOption opt;
+	opt.init(this);
+	opt.state = QStyle::State(0);
+	opt.state |= QStyle::State_MouseOver;
+	QPainter lightPixmapPainter(&lightPixmap);
+	style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &lightPixmapPainter);
+
+	darkPixmap = QPixmap(12,12);
+	darkPixmap.fill(QColor(0,0,0,0));
+	opt.init(this);
+	opt.state = QStyle::State(0);
+	QPainter darkPixmapPainter(&darkPixmap);
+	style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &darkPixmapPainter);
+}
+
+QSize QtCloseButton::sizeHint() const {
+	return QSize(width(), height());
+}
+
+bool QtCloseButton::event(QEvent *e) {
+	if (e->type() == QEvent::Enter || e->type() == QEvent::Leave) {
+		update();
+	}
+	return QAbstractButton::event(e);
+}
+
+void QtCloseButton::paintEvent(QPaintEvent *) {
+	QPainter painter(this);
+	painter.setRenderHint(QPainter::HighQualityAntialiasing);
+	if (underMouse()) {
+		painter.drawPixmap(0, 0, height(), height(), darkPixmap);
+	} else {
+		painter.drawPixmap(0, 0, height(), height(), lightPixmap);
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtCloseButton.h b/Swift/QtUI/QtVCardWidget/QtCloseButton.h
new file mode 100644
index 0000000..6ce8d30
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtCloseButton.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QAbstractButton>
+
+namespace Swift {
+
+	class QtCloseButton : public QAbstractButton {
+			Q_OBJECT
+		public:
+			explicit QtCloseButton(QWidget *parent = 0);
+			virtual QSize sizeHint() const;
+
+		protected:
+			virtual bool event(QEvent *e);
+			virtual void paintEvent(QPaintEvent* );
+
+		private:
+			QPixmap lightPixmap;
+			QPixmap darkPixmap;
+	};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp
new file mode 100644
index 0000000..b586444
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtRemovableItemDelegate.h"
+
+#include <QEvent>
+#include <QPainter>
+
+namespace Swift {
+
+QtRemovableItemDelegate::QtRemovableItemDelegate(const QStyle* style) : style(style) {
+
+}
+
+void QtRemovableItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex&) const {
+	QStyleOption opt;
+	opt.state = QStyle::State(0);
+	opt.state |= QStyle::State_MouseOver;
+	painter->save();
+	painter->fillRect(option.rect, option.state & QStyle::State_Selected ? option.palette.highlight() : option.palette.base());
+	painter->translate(option.rect.x(), option.rect.y()+(option.rect.height() - 12)/2);
+	style->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, painter);
+	painter->restore();
+}
+
+QWidget* QtRemovableItemDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const {
+	return NULL;
+}
+
+bool QtRemovableItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) {
+	if (event->type() == QEvent::MouseButtonRelease) {
+		model->removeRow(index.row());
+		return true;
+	} else {
+		return QItemDelegate::editorEvent(event, model, option, index);
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h
new file mode 100644
index 0000000..3d99ad8
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QItemDelegate>
+
+namespace Swift {
+
+class QtRemovableItemDelegate : public QItemDelegate {
+	public:
+		QtRemovableItemDelegate(const QStyle* style);
+
+		virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex&) const;
+		virtual QWidget* createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const;
+
+	protected:
+		virtual bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index);
+
+	private:
+		const QStyle* style;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp
new file mode 100644
index 0000000..efe04dc
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtResizableLineEdit.h"
+
+namespace Swift {
+
+QtResizableLineEdit::QtResizableLineEdit(QWidget* parent) :
+	QLineEdit(parent) {
+	connect(this, SIGNAL(textChanged(QString)), SLOT(textChanged(QString)));
+	setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+	int marginHeight = 6;
+	setMaximumHeight(fontMetrics().height() + marginHeight);
+}
+
+QtResizableLineEdit::~QtResizableLineEdit() {
+}
+
+bool QtResizableLineEdit::isEditable() const {
+	return editable;
+}
+void QtResizableLineEdit::setEditable(const bool editable) {
+	this->editable = editable;
+	if (editable) {
+		setReadOnly(false);
+	} else {
+		setReadOnly(true);
+	}
+}
+
+
+QSize QtResizableLineEdit::sizeHint() const {
+	int horizontalMargin = 10;
+#if QT_VERSION >= 0x040700
+	int w = fontMetrics().boundingRect(text().isEmpty() ? placeholderText() : text()).width() + horizontalMargin;
+#else
+	int w = fontMetrics().boundingRect(text().isEmpty() ? QString("   ") : text()).width() + horizontalMargin;
+#endif
+	return QSize(w, height());
+}
+
+void QtResizableLineEdit::textChanged(QString) {
+	updateGeometry();
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h
new file mode 100644
index 0000000..9022d38
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QLineEdit>
+
+namespace Swift {
+
+	class QtResizableLineEdit : public QLineEdit {
+		Q_OBJECT
+		Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
+
+		public:
+			explicit QtResizableLineEdit(QWidget* parent = 0);
+			~QtResizableLineEdit();
+
+			bool isEditable() const;
+			void setEditable(const bool);
+
+			virtual QSize sizeHint() const;
+
+		private slots:
+			void textChanged(QString);
+
+		private:
+			bool editable;
+	};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp b/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp
new file mode 100644
index 0000000..bade009
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtTagComboBox.h"
+
+#include <QAbstractItemView>
+#include <QtGui>
+
+namespace Swift {
+
+QtTagComboBox::QtTagComboBox(QWidget* parent) : QComboBox(parent) {
+	setEditable(false);
+	displayModel = new QStandardItemModel();
+	displayItem = new QStandardItem();
+	displayItem->setText("");
+	displayModel->insertRow(0, displayItem);
+	editMenu = new QMenu();
+	this->setModel(displayModel);
+	editable = true;
+}
+
+QtTagComboBox::~QtTagComboBox() {
+
+}
+
+bool QtTagComboBox::isEditable() const {
+	return editable;
+}
+
+void QtTagComboBox::setEditable(const bool editable) {
+	this->editable = editable;
+}
+
+void QtTagComboBox::addTag(const QString &id, const QString &label) {
+	QAction* tagAction = new QAction(editMenu);
+	tagAction->setText(label);
+	tagAction->setCheckable(true);
+	tagAction->setData(QString(id));
+	editMenu->addAction(tagAction);
+}
+
+void QtTagComboBox::setTag(const QString &id, bool value) {
+	QList<QAction*> tagActions = editMenu->actions();
+	foreach(QAction* action, tagActions) {
+		if (action->data() == id) {
+			action->setChecked(value);
+			updateDisplayItem();
+			return;
+		}
+	}
+}
+
+bool QtTagComboBox::isTagSet(const QString &id) const {
+	QList<QAction*> tagActions = editMenu->actions();
+	foreach(QAction* action, tagActions) {
+		if (action->data() == id) {
+			return action->isChecked();
+		}
+	}
+	return false;
+}
+
+void QtTagComboBox::showPopup() {
+
+}
+
+void QtTagComboBox::hidePopup() {
+
+}
+
+bool QtTagComboBox::event(QEvent* event) {
+	if (event->type() == QEvent::MouseButtonPress ||
+		event->type() == QEvent::KeyRelease) {
+		if (!editable) return true;
+
+		QPoint p = mapToGlobal(QPoint(0,0));
+		p += QPoint(0, height());
+		editMenu->exec(p);
+		updateDisplayItem();
+		return true;
+	}
+	return QComboBox::event(event);
+}
+
+void QtTagComboBox::updateDisplayItem() {
+	QList<QAction*> tagActions = editMenu->actions();
+	QString text = "";
+	foreach(QAction* action, tagActions) {
+		if (action->isChecked()) {
+			if (text != "") {
+				text += ", ";
+			}
+			text += action->text();
+		}
+	}
+	setItemText(0, text);
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtTagComboBox.h b/Swift/QtUI/QtVCardWidget/QtTagComboBox.h
new file mode 100644
index 0000000..37a60af
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtTagComboBox.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QComboBox>
+#include <QMenu>
+#include <QStandardItem>
+#include <QStandardItemModel>
+
+namespace Swift {
+
+class QtTagComboBox : public QComboBox {
+	Q_OBJECT
+	Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
+
+	public:
+		explicit QtTagComboBox(QWidget* parent = 0);
+		~QtTagComboBox();
+
+		bool isEditable() const;
+		void setEditable(const bool);
+
+		void addTag(const QString& id, const QString& label);
+		void setTag(const QString& id, bool value);
+		bool isTagSet(const QString& id) const;
+
+		virtual void showPopup();
+		virtual void hidePopup();
+
+		virtual bool event(QEvent* event);
+
+	private:
+		void updateDisplayItem();
+
+	private:
+		bool editable;
+		QStandardItemModel* displayModel;
+		QStandardItem* displayItem;
+		QMenu* editMenu;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp
new file mode 100644
index 0000000..4d34e53
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardAddressField.h"
+
+#include <QGridLayout>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardAddressField::QtVCardAddressField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Address")) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardAddressField::~QtVCardAddressField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardAddressField::setupContentWidgets() {
+	textFieldGridLayout = new QGridLayout();
+
+	streetLineEdit = new QtResizableLineEdit(this);
+	textFieldGridLayout->addWidget(streetLineEdit, 0, 0, Qt::AlignVCenter);
+
+	poboxLineEdit = new QtResizableLineEdit(this);
+	textFieldGridLayout->addWidget(poboxLineEdit, 0, 1, Qt::AlignVCenter);
+
+	addressextLineEdit = new QtResizableLineEdit(this);
+	textFieldGridLayout->addWidget(addressextLineEdit, 1, 0, Qt::AlignVCenter);
+
+	cityLineEdit = new QtResizableLineEdit(this);
+	textFieldGridLayout->addWidget(cityLineEdit, 2, 0, Qt::AlignVCenter);
+
+	pocodeLineEdit = new QtResizableLineEdit(this);
+	textFieldGridLayout->addWidget(pocodeLineEdit, 2, 1, Qt::AlignVCenter);
+
+	regionLineEdit = new QtResizableLineEdit(this);
+	textFieldGridLayout->addWidget(regionLineEdit, 3, 0, Qt::AlignVCenter);
+
+	countryLineEdit = new QtResizableLineEdit(this);
+	textFieldGridLayout->addWidget(countryLineEdit, 4, 0, Qt::AlignVCenter);
+	textFieldGridLayout->setVerticalSpacing(2);
+	getGridLayout()->addLayout(textFieldGridLayout, getGridLayout()->rowCount()-1, 2, 5, 2, Qt::AlignVCenter);
+	textFieldGridLayoutItem = getGridLayout()->itemAtPosition(getGridLayout()->rowCount()-1, 2);
+
+#if QT_VERSION >= 0x040700
+	streetLineEdit->setPlaceholderText(tr("Street"));
+	poboxLineEdit->setPlaceholderText(tr("PO Box"));
+	addressextLineEdit->setPlaceholderText(tr("Address Extension"));
+	cityLineEdit->setPlaceholderText(tr("City"));
+	pocodeLineEdit->setPlaceholderText(tr("Postal Code"));
+	regionLineEdit->setPlaceholderText(tr("Region"));
+	countryLineEdit->setPlaceholderText(tr("Country"));
+#endif
+
+	deliveryTypeLabel = new QLabel(this);
+	deliveryTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+	getGridLayout()->addWidget(deliveryTypeLabel, getGridLayout()->rowCount()-3, 4, Qt::AlignVCenter);
+
+	domesticRadioButton = new QRadioButton(tr("Domestic Delivery"), this);
+	getGridLayout()->addWidget(domesticRadioButton, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter);
+
+	internationalRadioButton = new QRadioButton(tr("International Delivery"), this);
+	getGridLayout()->addWidget(internationalRadioButton, getGridLayout()->rowCount()-1, 4, Qt::AlignVCenter);
+
+	buttonGroup = new QButtonGroup(this);
+	buttonGroup->addButton(domesticRadioButton);
+	buttonGroup->addButton(internationalRadioButton);
+
+	setTabOrder(internationalRadioButton, getTagComboBox());
+	getTagComboBox()->addTag("postal", tr("Postal"));
+	getTagComboBox()->addTag("parcel", tr("Parcel"));
+
+	QtVCardHomeWork::setTagComboBox(getTagComboBox());
+
+	textFields << streetLineEdit << poboxLineEdit << addressextLineEdit << cityLineEdit << pocodeLineEdit << regionLineEdit << countryLineEdit;
+	childWidgets << deliveryTypeLabel << domesticRadioButton << internationalRadioButton;
+}
+
+void QtVCardAddressField::customCleanup() {
+	foreach(QWidget* widget, textFields) {
+		widget->hide();
+		textFieldGridLayout->removeWidget(widget);
+	}
+	getGridLayout()->removeItem(textFieldGridLayoutItem);
+}
+
+
+
+bool QtVCardAddressField::isEmpty() const {
+	return streetLineEdit->text().isEmpty() &&
+			poboxLineEdit->text().isEmpty() &&
+			addressextLineEdit->text().isEmpty() &&
+			cityLineEdit->text().isEmpty() &&
+			pocodeLineEdit->text().isEmpty() &&
+			regionLineEdit->text().isEmpty() &&
+			countryLineEdit->text().isEmpty();
+}
+
+void QtVCardAddressField::setAddress(const VCard::Address& address) {
+	setPreferred(address.isPreferred);
+	setHome(address.isHome);
+	setWork(address.isWork);
+	getTagComboBox()->setTag("postal", address.isPostal);
+	getTagComboBox()->setTag("parcel", address.isParcel);
+	domesticRadioButton->setChecked(address.deliveryType == VCard::DomesticDelivery);
+	internationalRadioButton->setChecked(address.deliveryType == VCard::InternationalDelivery);
+	streetLineEdit->setText(P2QSTRING(address.street));
+	poboxLineEdit->setText(P2QSTRING(address.poBox));
+	addressextLineEdit->setText(P2QSTRING(address.addressExtension));
+	cityLineEdit->setText(P2QSTRING(address.locality));
+	pocodeLineEdit->setText(P2QSTRING(address.postalCode));
+	regionLineEdit->setText(P2QSTRING(address.region));
+	countryLineEdit->setText(P2QSTRING(address.country));
+}
+
+VCard::Address QtVCardAddressField::getAddress() const {
+	VCard::Address address;
+	address.isPreferred = getPreferred();
+	address.isHome = getHome();
+	address.isWork = getWork();
+	address.deliveryType = domesticRadioButton->isChecked() ? VCard::DomesticDelivery : (internationalRadioButton->isChecked() ? VCard::InternationalDelivery : VCard::None);
+	address.isPostal = getTagComboBox()->isTagSet("postal");
+	address.isParcel = getTagComboBox()->isTagSet("parcel");
+	address.street = Q2PSTRING(streetLineEdit->text());
+	address.poBox = Q2PSTRING(poboxLineEdit->text());
+	address.addressExtension = Q2PSTRING(addressextLineEdit->text());
+	address.locality = Q2PSTRING(cityLineEdit->text());
+	address.postalCode = Q2PSTRING(pocodeLineEdit->text());
+	address.region = Q2PSTRING(regionLineEdit->text());
+	address.country = Q2PSTRING(countryLineEdit->text());
+	return address;
+}
+
+void QtVCardAddressField::handleEditibleChanged(bool isEditable) {
+	if (streetLineEdit) streetLineEdit->setEditable(isEditable);
+	if (poboxLineEdit) poboxLineEdit->setEditable(isEditable);
+	if (addressextLineEdit) addressextLineEdit->setEditable(isEditable);
+	if (cityLineEdit) cityLineEdit->setEditable(isEditable);
+	if (pocodeLineEdit) pocodeLineEdit->setEditable(isEditable);
+	if (regionLineEdit) regionLineEdit->setEditable(isEditable);
+	if (countryLineEdit) countryLineEdit->setEditable(isEditable);
+
+	if (deliveryTypeLabel) {
+		deliveryTypeLabel->setText(buttonGroup->checkedButton() == 0 ? "" : buttonGroup->checkedButton()->text());
+		deliveryTypeLabel->setVisible(!isEditable);
+	}
+	if (domesticRadioButton) domesticRadioButton->setVisible(isEditable);
+	if (internationalRadioButton) internationalRadioButton->setVisible(isEditable);
+
+	foreach (QWidget* widget, textFields) {
+		QtResizableLineEdit* lineEdit;
+		if ((lineEdit = dynamic_cast<QtResizableLineEdit*>(widget))) {
+			lineEdit->setShown(isEditable ? true : !lineEdit->text().isEmpty());
+			lineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }");
+		}
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h
new file mode 100644
index 0000000..5a1256a
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include <QButtonGroup>
+#include <QRadioButton>
+
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+#include "QtVCardHomeWork.h"
+
+namespace Swift {
+
+class QtVCardAddressField : public QtVCardGeneralField, public QtVCardHomeWork {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("Address", UNLIMITED_INSTANCES, QtVCardAddressField)
+
+		QtVCardAddressField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardAddressField();
+
+		virtual bool isEmpty() const;
+
+		void setAddress(const VCard::Address& address);
+		VCard::Address getAddress() const;
+
+	protected:
+		virtual void setupContentWidgets();
+		virtual void customCleanup();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QList<QWidget*> textFields;
+		QtResizableLineEdit* streetLineEdit;
+		QtResizableLineEdit* poboxLineEdit;
+		QtResizableLineEdit* addressextLineEdit;
+		QtResizableLineEdit* cityLineEdit;
+		QtResizableLineEdit* pocodeLineEdit;
+		QtResizableLineEdit* regionLineEdit;
+		QtResizableLineEdit* countryLineEdit;
+		QGridLayout* textFieldGridLayout;
+		QLayoutItem* textFieldGridLayoutItem;
+
+		QLabel* deliveryTypeLabel;
+		QRadioButton* domesticRadioButton;
+		QRadioButton* internationalRadioButton;
+		QButtonGroup* buttonGroup;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp
new file mode 100644
index 0000000..20f48b9
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardAddressLabelField.h"
+
+#include <QGridLayout>
+#include <boost/algorithm/string.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardAddressLabelField::QtVCardAddressLabelField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Address Label")) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardAddressLabelField::~QtVCardAddressLabelField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardAddressLabelField::setupContentWidgets() {
+	addressLabelPlainTextEdit = new QPlainTextEdit(this);
+	addressLabelPlainTextEdit->setTabChangesFocus(true);
+	getGridLayout()->addWidget(addressLabelPlainTextEdit, getGridLayout()->rowCount()-1, 2, 3, 2, Qt::AlignVCenter);
+
+	deliveryTypeLabel = new QLabel(this);
+	deliveryTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+	getGridLayout()->addWidget(deliveryTypeLabel, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter);
+
+	domesticRadioButton = new QRadioButton(tr("Domestic Delivery"), this);
+	getGridLayout()->addWidget(domesticRadioButton, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter);
+
+	internationalRadioButton = new QRadioButton(tr("International Delivery"), this);
+	getGridLayout()->addWidget(internationalRadioButton, getGridLayout()->rowCount()-1, 4, Qt::AlignVCenter);
+
+	buttonGroup = new QButtonGroup(this);
+	buttonGroup->addButton(domesticRadioButton);
+	buttonGroup->addButton(internationalRadioButton);
+
+	setTabOrder(internationalRadioButton, getTagComboBox());
+	getTagComboBox()->addTag("postal", tr("Postal"));
+	getTagComboBox()->addTag("parcel", tr("Parcel"));
+
+	QtVCardHomeWork::setTagComboBox(getTagComboBox());
+	deliveryTypeLabel->hide();
+	childWidgets << addressLabelPlainTextEdit << deliveryTypeLabel << domesticRadioButton << internationalRadioButton;
+}
+
+bool QtVCardAddressLabelField::isEmpty() const {
+	return addressLabelPlainTextEdit->toPlainText().isEmpty();
+}
+
+void QtVCardAddressLabelField::setAddressLabel(const VCard::AddressLabel& addressLabel) {
+	setPreferred(addressLabel.isPreferred);
+	setHome(addressLabel.isHome);
+	setWork(addressLabel.isWork);
+	getTagComboBox()->setTag("postal", addressLabel.isPostal);
+	getTagComboBox()->setTag("parcel", addressLabel.isParcel);
+	domesticRadioButton->setChecked(addressLabel.deliveryType == VCard::DomesticDelivery);
+	internationalRadioButton->setChecked(addressLabel.deliveryType == VCard::InternationalDelivery);
+	std::string joinedLines = boost::algorithm::join(addressLabel.lines, "\n");
+	addressLabelPlainTextEdit->setPlainText(P2QSTRING(joinedLines));
+}
+
+VCard::AddressLabel QtVCardAddressLabelField::getAddressLabel() const {
+	VCard::AddressLabel addressLabel;
+	addressLabel.isPreferred = getPreferred();
+	addressLabel.isHome = getHome();
+	addressLabel.isWork = getWork();
+	addressLabel.deliveryType = domesticRadioButton->isChecked() ? VCard::DomesticDelivery : (internationalRadioButton->isChecked() ? VCard::InternationalDelivery : VCard::None);
+	addressLabel.isPostal = getTagComboBox()->isTagSet("postal");
+	addressLabel.isParcel = getTagComboBox()->isTagSet("parcel");
+
+	std::string lines = Q2PSTRING(addressLabelPlainTextEdit->toPlainText());
+	boost::split(addressLabel.lines, lines, boost::is_any_of("\n"));
+	return addressLabel;
+}
+
+void QtVCardAddressLabelField::handleEditibleChanged(bool isEditable) {
+	if (addressLabelPlainTextEdit) {
+		addressLabelPlainTextEdit->setReadOnly(!isEditable);
+		addressLabelPlainTextEdit->setStyleSheet(isEditable ? "" : "QPlainTextEdit { background: transparent; }");
+	}
+
+	if (deliveryTypeLabel) {
+		deliveryTypeLabel->setText(buttonGroup->checkedButton() == 0 ? "" : buttonGroup->checkedButton()->text());
+		deliveryTypeLabel->setVisible(!isEditable);
+	}
+	if (domesticRadioButton) domesticRadioButton->setVisible(isEditable);
+	if (internationalRadioButton) internationalRadioButton->setVisible(isEditable);
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h
new file mode 100644
index 0000000..0e097d9
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QButtonGroup>
+#include <QPlainTextEdit>
+#include <QRadioButton>
+#include <Swiften/Elements/VCard.h>
+
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+#include "QtVCardHomeWork.h"
+
+namespace Swift {
+
+class QtVCardAddressLabelField : public QtVCardGeneralField, public QtVCardHomeWork {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("AddressLabel", UNLIMITED_INSTANCES, QtVCardAddressLabelField)
+
+		QtVCardAddressLabelField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardAddressLabelField();
+
+		virtual bool isEmpty() const;
+
+		void setAddressLabel(const VCard::AddressLabel& addressLabel);
+		VCard::AddressLabel getAddressLabel() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QPlainTextEdit* addressLabelPlainTextEdit;
+
+		QLabel* deliveryTypeLabel;
+		QRadioButton* domesticRadioButton;
+		QRadioButton* internationalRadioButton;
+		QButtonGroup* buttonGroup;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp
new file mode 100644
index 0000000..2afc2f6
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardBirthdayField.h"
+
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <boost/algorithm/string.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardBirthdayField::QtVCardBirthdayField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Birthday"), false, false) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardBirthdayField::~QtVCardBirthdayField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardBirthdayField::setupContentWidgets() {
+	birthdayLabel = new QLabel(this);
+	birthdayLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+	birthdayDateEdit = new QDateEdit(this);
+	birthdayDateEdit->setCalendarPopup(true);
+
+	QHBoxLayout* birthdayLayout = new QHBoxLayout();
+	birthdayLayout->addWidget(birthdayLabel);
+	birthdayLayout->addWidget(birthdayDateEdit);
+
+	getGridLayout()->addLayout(birthdayLayout, getGridLayout()->rowCount()-1, 2, Qt::AlignVCenter);
+
+	getTagComboBox()->hide();
+	birthdayLabel->hide();
+	childWidgets << birthdayLabel << birthdayDateEdit;
+}
+
+bool QtVCardBirthdayField::isEmpty() const {
+	return false;
+}
+
+void QtVCardBirthdayField::setBirthday(const boost::posix_time::ptime& birthday) {
+	birthdayDateEdit->setDate(B2QDATE(birthday).date());
+}
+
+boost::posix_time::ptime QtVCardBirthdayField::getBirthday() const {
+	return boost::posix_time::from_time_t(QDateTime(birthdayDateEdit->date()).toTime_t());
+}
+
+void QtVCardBirthdayField::handleEditibleChanged(bool isEditable) {
+	birthdayLabel->setText(birthdayDateEdit->date().toString(Qt::DefaultLocaleLongDate));
+	birthdayDateEdit->setVisible(isEditable);
+	birthdayLabel->setVisible(!isEditable);
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h
new file mode 100644
index 0000000..4be6e27
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QDateEdit>
+#include <Swiften/Elements/VCard.h>
+
+#include "QtCloseButton.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+
+namespace Swift {
+
+class QtVCardBirthdayField : public QtVCardGeneralField {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("Birthday", 1, QtVCardBirthdayField)
+
+		QtVCardBirthdayField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardBirthdayField();
+
+		virtual bool isEmpty() const;
+
+		void setBirthday(const boost::posix_time::ptime& addressLabel);
+		boost::posix_time::ptime getBirthday() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QLabel* birthdayLabel;
+		QDateEdit* birthdayDateEdit;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp
new file mode 100644
index 0000000..f907d78
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardDescriptionField.h"
+
+#include <boost/algorithm/string.hpp>
+#include <QFontMetrics>
+#include <QGridLayout>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardDescriptionField::QtVCardDescriptionField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Description"), false, false) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardDescriptionField::~QtVCardDescriptionField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardDescriptionField::setupContentWidgets() {
+	descriptionPlainTextEdit = new QPlainTextEdit(this);
+	descriptionPlainTextEdit->setMinimumHeight(70);
+	getGridLayout()->addWidget(descriptionPlainTextEdit, getGridLayout()->rowCount()-1, 2, 2, 2, Qt::AlignVCenter);
+	getTagComboBox()->hide();
+	childWidgets << descriptionPlainTextEdit;
+}
+
+bool QtVCardDescriptionField::isEmpty() const {
+	return descriptionPlainTextEdit->toPlainText().isEmpty();
+}
+
+void QtVCardDescriptionField::setDescription(const std::string& description) {
+	descriptionPlainTextEdit->setPlainText(P2QSTRING(description));
+}
+
+std::string QtVCardDescriptionField::getDescription() const {
+	return Q2PSTRING(descriptionPlainTextEdit->toPlainText());
+}
+
+void QtVCardDescriptionField::handleEditibleChanged(bool isEditable) {
+	if (descriptionPlainTextEdit) {
+		if (isEditable) {
+			descriptionPlainTextEdit->setMinimumHeight(70);
+		} else {
+			QFontMetrics inputMetrics(descriptionPlainTextEdit->document()->defaultFont());
+			QRect horizontalBounds = contentsRect().adjusted(0,0,0,9999);
+			QRect boundingRect = inputMetrics.boundingRect(horizontalBounds, Qt::TextWordWrap, descriptionPlainTextEdit->toPlainText() + "A");
+			int left, top, right, bottom;
+			getContentsMargins(&left, &top, &right, &bottom);
+			int height = boundingRect.height() + top + bottom + inputMetrics.height();
+			descriptionPlainTextEdit->setMinimumHeight(height > 70 ? 70 : height);
+		}
+		descriptionPlainTextEdit->setReadOnly(!isEditable);
+		descriptionPlainTextEdit->setStyleSheet(isEditable ? "" : "QPlainTextEdit { background: transparent; }");
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h
new file mode 100644
index 0000000..3b1b3d9
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include <QPlainTextEdit>
+
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+
+namespace Swift {
+
+class QtVCardDescriptionField : public QtVCardGeneralField {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("Description", 1, QtVCardDescriptionField)
+
+		QtVCardDescriptionField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardDescriptionField();
+
+		virtual bool isEmpty() const;
+
+		void setDescription(const std::string& description);
+		std::string getDescription() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QPlainTextEdit* descriptionPlainTextEdit;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h
new file mode 100644
index 0000000..168c01b
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QGridLayout>
+#include <QObject>
+#include <QString>
+#include <typeinfo>
+
+#define GENERIC_QT_VCARD_FIELD_INFO(MENU_NAME, ALLOWED_INSTANCES, FIELD_CLASS) \
+	class FieldInfo : public QtVCardFieldInfo { \
+		public: \
+			virtual ~FieldInfo() { \
+			} \
+	\
+			virtual QString getMenuName() const { \
+				return QObject::tr(MENU_NAME); \
+			} \
+	\
+			virtual int getAllowedInstances() const { \
+				return ALLOWED_INSTANCES; \
+			} \
+	\
+			virtual QWidget* createFieldInstance(QWidget* parent, QGridLayout* layout, bool editable) const { \
+				return new FIELD_CLASS(parent, layout, editable); \
+			} \
+	\
+			virtual bool testInstance(QWidget* widget) const { \
+				return dynamic_cast<FIELD_CLASS*>(widget) != 0; \
+			} \
+	};
+
+class QWidget;
+
+namespace Swift {
+
+	class QtVCardFieldInfo {
+		public:
+			static const int UNLIMITED_INSTANCES = -1;
+
+			virtual ~QtVCardFieldInfo() {
+			}
+			virtual QString getMenuName() const = 0;
+			virtual int getAllowedInstances() const = 0;
+			virtual QWidget* createFieldInstance(QWidget* parent, QGridLayout* layout, bool editable) const = 0;
+			virtual bool testInstance(QWidget*) const = 0;
+	};
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp
new file mode 100644
index 0000000..5b3ef87
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardGeneralField.h"
+
+#include <QHBoxLayout>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+QtVCardGeneralField::QtVCardGeneralField(QWidget* parent, QGridLayout* layout, bool editable, int row, QString label, bool preferrable, bool taggable) :
+	QWidget(parent), preferrable(preferrable), taggable(taggable), layout(layout), row(row), preferredCheckBox(0), label(0), labelText(label),
+	tagComboBox(0), closeButton(0) {
+	setEditable(editable);
+}
+
+QtVCardGeneralField::~QtVCardGeneralField() {
+
+}
+
+void QtVCardGeneralField::initialize() {
+	if (preferrable) {
+		preferredCheckBox = new QCheckBox(this);
+		preferredCheckBox->setStyleSheet(
+					"QCheckBox::indicator { width: 18px; height: 18px; }"
+					"QCheckBox::indicator:checked { image: url(:/icons/star-checked.png); }"
+					"QCheckBox::indicator:unchecked { image: url(:/icons/star-unchecked); }"
+			);
+		layout->addWidget(preferredCheckBox, row, 0, Qt::AlignVCenter);
+		childWidgets << preferredCheckBox;
+	}
+	label = new QLabel(this);
+	label->setText(labelText);
+	layout->addWidget(label, row, 1, Qt::AlignVCenter | Qt::AlignRight);
+
+	tagLabel = new QLabel(this);
+	tagLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+
+	tagComboBox = new QtTagComboBox(this);
+	closeButton = new QtCloseButton(this);
+	connect(closeButton, SIGNAL(clicked()), SLOT(handleCloseButtonClicked()));
+
+	QHBoxLayout* tagLayout = new QHBoxLayout();
+	tagLayout->addWidget(tagLabel);
+	tagLayout->addWidget(tagComboBox);
+
+	setupContentWidgets();
+	layout->addLayout(tagLayout, row, 4, Qt::AlignTop);
+	layout->addWidget(closeButton, row, 5, Qt::AlignCenter);
+	closeButton->resize(12, 12);
+	tagLabel->hide();
+
+	childWidgets << label << tagComboBox << tagLabel << closeButton;
+}
+
+bool QtVCardGeneralField::isEditable() const {
+	return editable;
+}
+
+void QtVCardGeneralField::setEditable(bool editable) {
+	this->editable = editable;
+	if (tagComboBox) {
+		if (taggable) {
+			tagLabel->setText(tagComboBox->itemText(0));
+			tagComboBox->setVisible(editable);
+			tagLabel->setVisible(!editable);
+		} else {
+			tagLabel->hide();
+			tagComboBox->hide();
+		}
+	}
+	if (closeButton) closeButton->setVisible(editable);
+	if (preferredCheckBox) {
+		if (editable) {
+			preferredCheckBox->show();
+		} else if (!preferredCheckBox->isChecked()) {
+			preferredCheckBox->hide();
+		}
+		preferredCheckBox->setEnabled(editable);
+	}
+	editableChanged(this->editable);
+}
+
+void QtVCardGeneralField::setPreferred(const bool preferred) {
+	if (preferredCheckBox) preferredCheckBox->setChecked(preferred);
+}
+
+bool QtVCardGeneralField::getPreferred() const {
+	return preferredCheckBox ? preferredCheckBox->isChecked() : false;
+}
+
+void QtVCardGeneralField::customCleanup() {
+}
+
+QtTagComboBox* QtVCardGeneralField::getTagComboBox() const {
+	return tagComboBox;
+}
+
+QGridLayout* QtVCardGeneralField::getGridLayout() const {
+	return layout;
+}
+
+void QtVCardGeneralField::handleCloseButtonClicked() {
+	customCleanup();
+	foreach(QWidget* widget, childWidgets) {
+		widget->hide();
+		layout->removeWidget(widget);
+	}
+	deleteField(this);
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h
new file mode 100644
index 0000000..4afe692
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QWidget>
+
+#include "QtCloseButton.h"
+#include "QtTagComboBox.h"
+
+namespace Swift {
+
+/*
+ *	covers features like:
+ *		- preffered (star ceckbox)
+ *		- combo check boxh
+ *		- label
+ *		- remove button
+ */
+class QtVCardGeneralField : public QWidget {
+		Q_OBJECT
+		Q_PROPERTY(bool editable READ isEditable WRITE setEditable NOTIFY editableChanged)
+		Q_PROPERTY(bool empty READ isEmpty)
+
+	public:
+		explicit QtVCardGeneralField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false, int row = 0, QString label = QString(),
+										bool preferrable = true, bool taggable = true);
+		virtual ~QtVCardGeneralField();
+
+		void initialize();
+
+		virtual bool isEditable() const;
+		virtual void setEditable(bool);
+
+		virtual bool isEmpty() const = 0;
+
+		void setPreferred(const bool preferred);
+		bool getPreferred() const;
+
+	protected:
+		virtual void setupContentWidgets() = 0;
+		virtual void customCleanup();
+
+		QtTagComboBox* getTagComboBox() const;
+		QGridLayout* getGridLayout() const;
+
+	signals:
+		void editableChanged(bool);
+		void deleteField(QtVCardGeneralField*);
+		
+	public slots:
+		void handleCloseButtonClicked();
+
+	protected:
+		QList<QWidget*> childWidgets;
+
+	private:
+		bool editable;
+		bool preferrable;
+		bool taggable;
+		QGridLayout* layout;
+		int row;
+		QCheckBox* preferredCheckBox;
+		QLabel* label;
+		QString labelText;
+		QtTagComboBox* tagComboBox;
+		QLabel* tagLabel;
+		QtCloseButton* closeButton;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp
new file mode 100644
index 0000000..3119a80
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardHomeWork.h"
+
+namespace Swift {
+
+QtVCardHomeWork::QtVCardHomeWork() : tagComboBox(0) {
+}
+
+QtVCardHomeWork::~QtVCardHomeWork() {
+}
+
+void QtVCardHomeWork::setTagComboBox(QtTagComboBox* tagBox) {
+	tagComboBox = tagBox;
+	tagComboBox->addTag("home", QObject::tr("Home"));
+	tagComboBox->addTag("work", QObject::tr("Work"));
+}
+
+void QtVCardHomeWork::setHome(const bool home) {
+	tagComboBox->setTag("home", home);
+}
+
+bool QtVCardHomeWork::getHome() const {
+	return tagComboBox->isTagSet("home");
+}
+
+void QtVCardHomeWork::setWork(const bool work) {
+	tagComboBox->setTag("work", work);
+}
+
+bool QtVCardHomeWork::getWork() const {
+	return tagComboBox->isTagSet("work");
+}
+
+}
+
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h
new file mode 100644
index 0000000..768d984
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QObject>
+
+#include "QtTagComboBox.h"
+
+namespace Swift {
+
+class QtVCardHomeWork {
+	public:
+		QtVCardHomeWork();
+		virtual ~QtVCardHomeWork();
+
+		void setTagComboBox(QtTagComboBox* tagBox);
+
+		void setHome(const bool home);
+		bool getHome() const;
+		void setWork(const bool work);
+		bool getWork() const;
+
+	private:
+		QtTagComboBox* tagComboBox;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp
new file mode 100644
index 0000000..46f253f
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardInternetEMailField.h"
+
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QTextDocument>
+#include <Swiften/Base/Log.h>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardInternetEMailField::QtVCardInternetEMailField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("E-Mail")) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardInternetEMailField::~QtVCardInternetEMailField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardInternetEMailField::setupContentWidgets() {
+	emailLabel = new QLabel(this);
+	emailLabel->setOpenExternalLinks(true);
+	emailLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
+	emailLineEdit = new QtResizableLineEdit(this);
+#if QT_VERSION >= 0x040700
+	emailLineEdit->setPlaceholderText(tr("alice@wonderland.lit"));
+#endif
+	QHBoxLayout* emailLayout = new QHBoxLayout();
+	emailLayout->addWidget(emailLabel);
+	emailLayout->addWidget(emailLineEdit);
+	getGridLayout()->addLayout(emailLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter);
+	setTabOrder(emailLineEdit, getTagComboBox());
+	QtVCardHomeWork::setTagComboBox(getTagComboBox());
+	emailLabel->hide();
+	childWidgets << emailLabel << emailLineEdit;
+}
+
+bool QtVCardInternetEMailField::isEmpty() const {
+	return emailLineEdit->text().isEmpty();
+}
+
+void QtVCardInternetEMailField::setInternetEMailAddress(const VCard::EMailAddress& address) {
+	assert(address.isInternet);
+	setPreferred(address.isPreferred);
+	setHome(address.isHome);
+	setWork(address.isWork);
+	emailLineEdit->setText(P2QSTRING(address.address));
+}
+
+VCard::EMailAddress QtVCardInternetEMailField::getInternetEMailAddress() const {
+	VCard::EMailAddress address;
+	address.isInternet = true;
+	address.isPreferred = getPreferred();
+	address.isHome = getHome();
+	address.isWork = getWork();
+	address.address = Q2PSTRING(emailLineEdit->text());
+	return address;
+}
+
+void QtVCardInternetEMailField::handleEditibleChanged(bool isEditable) {
+	if (isEditable) {
+		if (emailLineEdit) emailLineEdit->show();
+		if (emailLabel) emailLabel->hide();
+	} else {
+		if (emailLineEdit) emailLineEdit->hide();
+		if (emailLabel) {
+			emailLabel->setText(QString("<a href=\"mailto:%1\">%1</a>").arg(Qt::escape(emailLineEdit->text())));
+			emailLabel->show();
+		}
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h
new file mode 100644
index 0000000..3f8a27f
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+#include "QtVCardHomeWork.h"
+
+namespace Swift {
+
+class QtVCardInternetEMailField : public QtVCardGeneralField, public QtVCardHomeWork {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("E-Mail", UNLIMITED_INSTANCES, QtVCardInternetEMailField)
+
+		QtVCardInternetEMailField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardInternetEMailField();
+
+		virtual bool isEmpty() const;
+
+		void setInternetEMailAddress(const VCard::EMailAddress& address);
+		VCard::EMailAddress getInternetEMailAddress() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QtResizableLineEdit* emailLineEdit;
+		QLabel* emailLabel;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp
new file mode 100644
index 0000000..dbb4b7c
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardJIDField.h"
+
+#include <QGridLayout>
+#include <QTextDocument>
+#include <boost/algorithm/string.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardJIDField::QtVCardJIDField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("JID"), false, false) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardJIDField::~QtVCardJIDField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardJIDField::setupContentWidgets() {
+	jidLabel = new QLabel(this);
+	jidLabel->setOpenExternalLinks(true);
+	jidLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
+	jidLineEdit = new QtResizableLineEdit(this);
+#if QT_VERSION >= 0x040700
+	jidLineEdit->setPlaceholderText(tr("alice@wonderland.lit"));
+#endif
+	QHBoxLayout* jidLayout = new QHBoxLayout();
+	jidLayout->addWidget(jidLabel);
+	jidLayout->addWidget(jidLineEdit);
+	getGridLayout()->addLayout(jidLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter);
+
+	jidLabel->hide();
+	getTagComboBox()->hide();
+
+	childWidgets << jidLabel << jidLineEdit;
+}
+
+bool QtVCardJIDField::isEmpty() const {
+	return jidLineEdit->text().isEmpty();
+}
+
+void QtVCardJIDField::setJID(const JID& jid) {
+	std::string jidStr = jid.toBare().toString();
+	jidLineEdit->setText(P2QSTRING(jidStr));
+}
+
+JID QtVCardJIDField::getJID() const {
+	return JID(Q2PSTRING(jidLineEdit->text()));
+}
+
+void QtVCardJIDField::handleEditibleChanged(bool isEditable) {
+	if (isEditable) {
+		if (jidLineEdit) jidLineEdit->show();
+		if (jidLabel) jidLabel->hide();
+	} else {
+		if (jidLineEdit) jidLineEdit->hide();
+		if (jidLabel) {
+			jidLabel->setText(QString("<a href=\"xmpp:%1\">%1</a>").arg(Qt::escape(jidLineEdit->text())));
+			jidLabel->show();
+		}
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h
new file mode 100644
index 0000000..016bcf8
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+
+namespace Swift {
+
+class QtVCardJIDField : public QtVCardGeneralField {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("JID", UNLIMITED_INSTANCES, QtVCardJIDField)
+
+		QtVCardJIDField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardJIDField();
+
+		virtual bool isEmpty() const;
+
+		void setJID(const JID& jid);
+		JID getJID() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QLabel* jidLabel;
+		QtResizableLineEdit* jidLineEdit;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp
new file mode 100644
index 0000000..5f231dc
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardOrganizationField.h"
+
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <boost/algorithm/string.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardOrganizationField::QtVCardOrganizationField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Organisation"), false, false) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardOrganizationField::~QtVCardOrganizationField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardOrganizationField::setupContentWidgets() {
+	organizationLabel = new QLabel(this);
+	organizationLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+	organizationLineEdit = new QtResizableLineEdit(this);
+	QHBoxLayout* organizationLayout = new QHBoxLayout();
+	organizationLayout->addWidget(organizationLabel);
+	organizationLayout->addWidget(organizationLineEdit);
+
+	getGridLayout()->addLayout(organizationLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter);
+
+	itemDelegate = new QtRemovableItemDelegate(style());
+
+	unitsTreeWidget = new QTreeWidget(this);
+	unitsTreeWidget->setColumnCount(2);
+	unitsTreeWidget->header()->setStretchLastSection(false);
+	int closeIconWidth = unitsTreeWidget->fontMetrics().height();
+	unitsTreeWidget->header()->resizeSection(1, closeIconWidth);
+	unitsTreeWidget->header()->setResizeMode(0, QHeaderView::Stretch);
+	unitsTreeWidget->setHeaderHidden(true);
+	unitsTreeWidget->setRootIsDecorated(false);
+	unitsTreeWidget->setEditTriggers(QAbstractItemView::DoubleClicked);
+	unitsTreeWidget->setItemDelegateForColumn(1, itemDelegate);
+	connect(unitsTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(handleItemChanged(QTreeWidgetItem*,int)));
+	getGridLayout()->addWidget(unitsTreeWidget, getGridLayout()->rowCount()-1, 4, 2, 1);
+
+	QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << "");
+	item->setFlags(item->flags() | Qt::ItemIsEditable);
+	unitsTreeWidget->addTopLevelItem(item);
+
+	getTagComboBox()->hide();
+	organizationLabel->hide();
+	childWidgets << organizationLabel << organizationLineEdit << unitsTreeWidget;
+}
+
+bool QtVCardOrganizationField::isEmpty() const {
+	return organizationLineEdit->text().isEmpty() && unitsTreeWidget->model()->rowCount() != 0;
+}
+
+void QtVCardOrganizationField::setOrganization(const VCard::Organization& organization) {
+	organizationLineEdit->setText(P2QSTRING(organization.name));
+	unitsTreeWidget->clear();
+	foreach(std::string unit, organization.units) {
+		QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(P2QSTRING(unit)) << "");
+		item->setFlags(item->flags() | Qt::ItemIsEditable);
+		unitsTreeWidget->addTopLevelItem(item);
+	}
+}
+
+VCard::Organization QtVCardOrganizationField::getOrganization() const {
+	VCard::Organization organization;
+	organization.name = Q2PSTRING(organizationLineEdit->text());
+	for(int i=0; i < unitsTreeWidget->topLevelItemCount(); ++i) {
+		QTreeWidgetItem* row = unitsTreeWidget->topLevelItem(i);
+		if (!row->text(0).isEmpty()) {
+			organization.units.push_back(Q2PSTRING(row->text(0)));
+		}
+	}
+
+	QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << "");
+	item->setFlags(item->flags() | Qt::ItemIsEditable);
+	unitsTreeWidget->addTopLevelItem(item);
+
+	return organization;
+}
+
+void QtVCardOrganizationField::handleEditibleChanged(bool isEditable) {
+	if (organizationLineEdit) {
+		organizationLineEdit->setVisible(isEditable);
+		organizationLabel->setVisible(!isEditable);
+
+		if (!isEditable) {
+			QString label;
+			for(int i=0; i < unitsTreeWidget->topLevelItemCount(); ++i) {
+				QTreeWidgetItem* row = unitsTreeWidget->topLevelItem(i);
+				if (!row->text(0).isEmpty()) {
+					label += row->text(0) + ", ";
+				}
+			}
+			label += organizationLineEdit->text();
+			organizationLabel->setText(label);
+		}
+	}
+	if (unitsTreeWidget) unitsTreeWidget->setVisible(isEditable);
+}
+
+void QtVCardOrganizationField::handleItemChanged(QTreeWidgetItem *, int) {
+	bool hasEmptyRow = false;
+	QList<QTreeWidgetItem*> rows = unitsTreeWidget->findItems("", Qt::MatchFixedString);
+	foreach(QTreeWidgetItem* row, rows) {
+		if (row->text(0).isEmpty()) {
+			hasEmptyRow = true;
+		}
+	}
+
+	if (!hasEmptyRow) {
+		QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << "");
+		item->setFlags(item->flags() | Qt::ItemIsEditable);
+		unitsTreeWidget->addTopLevelItem(item);
+	}
+	getTagComboBox()->hide();
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h
new file mode 100644
index 0000000..917e22a
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include <QTreeWidget>
+
+#include "QtRemovableItemDelegate.h"
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+
+namespace Swift {
+
+class QtVCardOrganizationField : public QtVCardGeneralField {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("Organization", UNLIMITED_INSTANCES, QtVCardOrganizationField)
+
+		QtVCardOrganizationField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardOrganizationField();
+
+		virtual bool isEmpty() const;
+
+		void setOrganization(const VCard::Organization& organization);
+		VCard::Organization getOrganization() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private slots:
+		void handleItemChanged(QTreeWidgetItem*, int);
+
+	private:
+		QLabel* organizationLabel;
+		QtResizableLineEdit* organizationLineEdit;
+		QTreeWidget* unitsTreeWidget;
+		QtRemovableItemDelegate* itemDelegate;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp
new file mode 100644
index 0000000..3ddc86b
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardPhotoAndNameFields.h"
+#include "ui_QtVCardPhotoAndNameFields.h"
+
+#include <QMenu>
+
+namespace Swift {
+
+QtVCardPhotoAndNameFields::QtVCardPhotoAndNameFields(QWidget* parent) :
+	QWidget(parent),
+	ui(new Ui::QtVCardPhotoAndNameFields) {
+	ui->setupUi(this);
+	ui->lineEditPREFIX->hide();
+	ui->lineEditMIDDLE->hide();
+	ui->lineEditSUFFIX->hide();
+	ui->lineEditFN->hide();
+	ui->lineEditNICKNAME->hide();
+	ui->labelFULLNAME->hide();
+
+#if QT_VERSION >= 0x040700
+	ui->lineEditFN->setPlaceholderText(tr("Formatted Name"));
+	ui->lineEditNICKNAME->setPlaceholderText(tr("Nickname"));
+	ui->lineEditPREFIX->setPlaceholderText(tr("Prefix"));
+	ui->lineEditGIVEN->setPlaceholderText(tr("Given Name"));
+	ui->lineEditMIDDLE->setPlaceholderText(tr("Middle Name"));
+	ui->lineEditFAMILY->setPlaceholderText(tr("Last Name"));
+	ui->lineEditSUFFIX->setPlaceholderText(tr("Suffix"));
+#endif
+
+	addFieldMenu = new QMenu("Name", this);
+
+	actionSignalMapper = new QSignalMapper(this);
+
+	connect(actionSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(showField(const QString &)));
+	prepareAddFieldMenu();
+}
+
+QtVCardPhotoAndNameFields::~QtVCardPhotoAndNameFields() {
+	delete ui;
+	delete actionSignalMapper;
+}
+
+bool QtVCardPhotoAndNameFields::isEditable() const {
+	return editable;
+}
+
+void QtVCardPhotoAndNameFields::setEditable(bool editable) {
+	this->editable = editable;
+
+	ui->avatarWidget->setEditable(editable);
+	ui->lineEditFN->setVisible(editable ? true : !ui->lineEditFN->text().isEmpty());
+	ui->lineEditFN->setEditable(editable);
+	ui->lineEditFN->setStyleSheet(editable ? "" : "QLineEdit {border: none; background-color: transparent;}");
+
+	ui->lineEditNICKNAME->setVisible(editable ? true : !ui->lineEditNICKNAME->text().isEmpty());
+	ui->lineEditNICKNAME->setEditable(editable);
+	ui->lineEditNICKNAME->setStyleSheet(editable ? "" : "QLineEdit {border: none; background-color: transparent;}");
+
+	// prefix given middle last suffix
+	ui->lineEditPREFIX->setVisible(editable);
+	ui->lineEditGIVEN->setVisible(editable);
+	ui->lineEditMIDDLE->setVisible(editable);
+	ui->lineEditFAMILY->setVisible(editable);
+	ui->lineEditSUFFIX->setVisible(editable);
+	ui->labelFULLNAME->setVisible(!editable);
+	ui->labelFULLNAME->setText(	ui->lineEditPREFIX->text() + " " + ui->lineEditGIVEN->text() + " " +
+								ui->lineEditMIDDLE->text() + " " + ui->lineEditFAMILY->text() + " " + ui->lineEditSUFFIX->text());
+
+	prepareAddFieldMenu();
+}
+
+QMenu* QtVCardPhotoAndNameFields::getAddFieldMenu() const {
+	return addFieldMenu;
+}
+
+void QtVCardPhotoAndNameFields::setAvatar(const ByteArray &data, const std::string &type) {
+	ui->avatarWidget->setAvatar(data, type);
+}
+
+ByteArray QtVCardPhotoAndNameFields::getAvatarData() const {
+	return ui->avatarWidget->getAvatarData();
+}
+
+std::string QtVCardPhotoAndNameFields::getAvatarType() const {
+	return ui->avatarWidget->getAvatarType();
+}
+
+void QtVCardPhotoAndNameFields::setFormattedName(const QString formattedName) {
+	ui->lineEditFN->setText(formattedName);
+}
+
+QString QtVCardPhotoAndNameFields::getFormattedName() const {
+	return ui->lineEditFN->text();
+}
+
+void QtVCardPhotoAndNameFields::setNickname(const QString nickname) {
+	ui->lineEditNICKNAME->setText(nickname);
+}
+
+QString QtVCardPhotoAndNameFields::getNickname() const {
+	return ui->lineEditNICKNAME->text();
+}
+
+void QtVCardPhotoAndNameFields::setPrefix(const QString prefix) {
+	ui->lineEditPREFIX->setText(prefix);
+}
+
+QString QtVCardPhotoAndNameFields::getPrefix() const {
+	return ui->lineEditPREFIX->text();
+}
+
+void QtVCardPhotoAndNameFields::setGivenName(const QString givenName) {
+	ui->lineEditGIVEN->setText(givenName);
+}
+
+QString QtVCardPhotoAndNameFields::getGivenName() const {
+	return ui->lineEditGIVEN->text();
+}
+
+void QtVCardPhotoAndNameFields::setMiddleName(const QString middleName) {
+	ui->lineEditMIDDLE->setText(middleName);
+}
+
+QString QtVCardPhotoAndNameFields::getMiddleName() const {
+	return ui->lineEditMIDDLE->text();
+}
+
+void QtVCardPhotoAndNameFields::setFamilyName(const QString familyName) {
+	ui->lineEditFAMILY->setText(familyName);
+}
+
+QString QtVCardPhotoAndNameFields::getFamilyName() const {
+	return ui->lineEditFAMILY->text();
+}
+
+void QtVCardPhotoAndNameFields::setSuffix(const QString suffix) {
+	ui->lineEditSUFFIX->setText(suffix);
+}
+
+QString QtVCardPhotoAndNameFields::getSuffix() const {
+	return ui->lineEditSUFFIX->text();
+}
+
+void QtVCardPhotoAndNameFields::prepareAddFieldMenu() {
+	foreach(QAction* action, addFieldMenu->actions()) {
+		actionSignalMapper->removeMappings(action);
+	}
+
+	addFieldMenu->clear();
+	foreach(QObject* obj, children()) {
+		QLineEdit* lineEdit = 0;
+		if (!(lineEdit = dynamic_cast<QLineEdit*>(obj))) continue;
+		if (lineEdit->isHidden()) {
+#if QT_VERSION >= 0x040700
+			QAction* action = addFieldMenu->addAction(QString("Add ") + lineEdit->placeholderText(), actionSignalMapper, SLOT(map()));
+#else
+			QAction* action = addFieldMenu->addAction(QString("Add ") + lineEdit->toolTip(), actionSignalMapper, SLOT(map()));
+#endif
+			actionSignalMapper->setMapping(action, lineEdit->objectName());
+		}
+	}
+}
+
+void QtVCardPhotoAndNameFields::showField(const QString& widgetName) {
+	QLineEdit* lineEditToShow = findChild<QLineEdit*>(widgetName);
+	if (lineEditToShow) lineEditToShow->show();
+
+	prepareAddFieldMenu();
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h
new file mode 100644
index 0000000..f279701
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QMenu>
+#include <QSignalMapper>
+#include <QWidget>
+#include <Swiften/Base/ByteArray.h>
+
+namespace Ui {
+	class QtVCardPhotoAndNameFields;
+}
+
+
+namespace Swift {
+
+	class QtVCardPhotoAndNameFields : public QWidget {
+		Q_OBJECT
+		Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
+
+		public:
+			explicit QtVCardPhotoAndNameFields(QWidget* parent = 0);
+			~QtVCardPhotoAndNameFields();
+
+			bool isEditable() const;
+			void setEditable(bool);
+
+			QMenu* getAddFieldMenu() const;
+
+			void setAvatar(const ByteArray& data, const std::string& type);
+			ByteArray getAvatarData() const;
+			std::string getAvatarType() const;
+
+			void setFormattedName(const QString formattedName);
+			QString getFormattedName() const;
+
+			void setNickname(const QString nickname);
+			QString getNickname() const;
+
+			void setPrefix(const QString prefix);
+			QString getPrefix() const;
+
+			void setGivenName(const QString givenName);
+			QString getGivenName() const;
+
+			void setMiddleName(const QString middleName);
+			QString getMiddleName() const;
+
+			void setFamilyName(const QString familyName);
+			QString getFamilyName() const;
+
+			void setSuffix(const QString suffix);
+			QString getSuffix() const;
+
+		public slots:
+			void showField(const QString& widgetName);
+
+		private:
+			void prepareAddFieldMenu();
+
+		private:
+			Ui::QtVCardPhotoAndNameFields* ui;
+			bool editable;
+
+			QMenu* addFieldMenu;
+			QSignalMapper* actionSignalMapper;
+	};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui
new file mode 100644
index 0000000..04da2bc
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui
@@ -0,0 +1,251 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtVCardPhotoAndNameFields</class>
+ <widget class="QWidget" name="QtVCardPhotoAndNameFields">
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>522</width>
+    <height>81</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0" rowminimumheight="0,0,0,0,0">
+   <property name="sizeConstraint">
+    <enum>QLayout::SetMinimumSize</enum>
+   </property>
+   <property name="horizontalSpacing">
+    <number>5</number>
+   </property>
+   <property name="verticalSpacing">
+    <number>1</number>
+   </property>
+   <property name="margin">
+    <number>0</number>
+   </property>
+   <item row="0" column="0" rowspan="5">
+    <widget class="Swift::QtAvatarWidget" name="avatarWidget" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text" stdset="0">
+      <string/>
+     </property>
+     <property name="flat" stdset="0">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="Swift::QtResizableLineEdit" name="lineEditFN">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>18</pointsize>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="toolTip">
+        <string>Formatted Name</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="1">
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="Swift::QtResizableLineEdit" name="lineEditNICKNAME">
+       <property name="toolTip">
+        <string>Nickname</string>
+       </property>
+       <property name="frame">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_3">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item row="2" column="1">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <property name="sizeConstraint">
+      <enum>QLayout::SetMinimumSize</enum>
+     </property>
+     <item>
+      <widget class="QLabel" name="labelFULLNAME">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Swift::QtResizableLineEdit" name="lineEditPREFIX">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Prefix</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Swift::QtResizableLineEdit" name="lineEditGIVEN">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Given Name</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="readOnly">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Swift::QtResizableLineEdit" name="lineEditMIDDLE">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Middle Name</string>
+       </property>
+       <property name="styleSheet">
+        <string notr="true"/>
+       </property>
+       <property name="frame">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Swift::QtResizableLineEdit" name="lineEditFAMILY">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Last Name</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Swift::QtResizableLineEdit" name="lineEditSUFFIX">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Suffix</string>
+       </property>
+       <property name="readOnly">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Swift::QtResizableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>QtResizableLineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>Swift::QtAvatarWidget</class>
+   <extends>QWidget</extends>
+   <header>Swift/QtUI/QtAvatarWidget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp
new file mode 100644
index 0000000..8af4e64
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardRoleField.h"
+
+#include <QGridLayout>
+#include <boost/algorithm/string.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardRoleField::QtVCardRoleField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Role"), false, false) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardRoleField::~QtVCardRoleField() {
+}
+
+void QtVCardRoleField::setupContentWidgets() {
+	roleLineEdit = new QtResizableLineEdit(this);
+	getGridLayout()->addWidget(roleLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter);
+	getTagComboBox()->hide();
+	childWidgets << roleLineEdit;
+}
+
+bool QtVCardRoleField::isEmpty() const {
+	return roleLineEdit->text().isEmpty();
+}
+
+void QtVCardRoleField::setRole(const std::string& role) {
+	roleLineEdit->setText(P2QSTRING(role));
+}
+
+std::string QtVCardRoleField::getRole() const {
+	return Q2PSTRING(roleLineEdit->text());
+}
+
+void QtVCardRoleField::handleEditibleChanged(bool isEditable) {
+	if (roleLineEdit) {
+		roleLineEdit->setEditable(isEditable);
+		roleLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }");
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h
new file mode 100644
index 0000000..3c819ed
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+
+namespace Swift {
+
+class QtVCardRoleField : public QtVCardGeneralField {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("Role", UNLIMITED_INSTANCES, QtVCardRoleField)
+
+		QtVCardRoleField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardRoleField();
+
+		virtual bool isEmpty() const;
+
+		void setRole(const std::string& role);
+		std::string getRole() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QtResizableLineEdit* roleLineEdit;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp
new file mode 100644
index 0000000..ee93c01
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardTelephoneField.h"
+
+#include <QGridLayout>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardTelephoneField::QtVCardTelephoneField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Telephone")) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardTelephoneField::~QtVCardTelephoneField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardTelephoneField::setupContentWidgets() {
+	telephoneLineEdit = new QtResizableLineEdit(this);
+#if QT_VERSION >= 0x040700
+	telephoneLineEdit->setPlaceholderText(tr("0118 999 881 999 119 7253"));
+#endif
+	getGridLayout()->addWidget(telephoneLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter);
+	setTabOrder(telephoneLineEdit, getTagComboBox());
+	QtVCardHomeWork::setTagComboBox(getTagComboBox());
+
+	getTagComboBox()->addTag("voice", QObject::tr("Voice"));
+	getTagComboBox()->addTag("fax",	QObject::tr("Fax"));
+	getTagComboBox()->addTag("pager", QObject::tr("Pager"));
+	getTagComboBox()->addTag("msg",	QObject::tr("Voice Messaging"));
+	getTagComboBox()->addTag("cell", QObject::tr("Cell"));
+	getTagComboBox()->addTag("video", QObject::tr("Video"));
+	getTagComboBox()->addTag("bbs",	QObject::tr("Bulletin Board System"));
+	getTagComboBox()->addTag("modem", QObject::tr("Modem"));
+	getTagComboBox()->addTag("isdn", QObject::tr("ISDN"));
+	getTagComboBox()->addTag("pcs",	QObject::tr("Personal Communication Services"));
+
+	childWidgets << telephoneLineEdit;
+}
+
+bool QtVCardTelephoneField::isEmpty() const {
+	return telephoneLineEdit->text().isEmpty();
+}
+
+void QtVCardTelephoneField::setTelephone(const VCard::Telephone& telephone) {
+	setPreferred(telephone.isPreferred);
+	setHome(telephone.isHome);
+	setWork(telephone.isWork);
+
+	telephoneLineEdit->setText(P2QSTRING(telephone.number));
+
+	getTagComboBox()->setTag("voice", telephone.isVoice);
+	getTagComboBox()->setTag("fax", telephone.isFax);
+	getTagComboBox()->setTag("pager", telephone.isPager);
+	getTagComboBox()->setTag("msg", telephone.isMSG);
+	getTagComboBox()->setTag("cell", telephone.isCell);
+	getTagComboBox()->setTag("video", telephone.isVideo);
+	getTagComboBox()->setTag("bbs", telephone.isBBS);
+	getTagComboBox()->setTag("modem", telephone.isModem);
+	getTagComboBox()->setTag("isdn", telephone.isISDN);
+	getTagComboBox()->setTag("pcs", telephone.isPCS);
+}
+
+VCard::Telephone QtVCardTelephoneField::getTelephone() const {
+	VCard::Telephone telephone;
+
+	telephone.number = Q2PSTRING(telephoneLineEdit->text());
+
+	telephone.isPreferred = getPreferred();
+	telephone.isHome = getHome();
+	telephone.isWork = getWork();
+
+	telephone.isVoice = getTagComboBox()->isTagSet("voice");
+	telephone.isFax = getTagComboBox()->isTagSet("fax");
+	telephone.isPager = getTagComboBox()->isTagSet("pager");
+	telephone.isMSG = getTagComboBox()->isTagSet("msg");
+	telephone.isCell = getTagComboBox()->isTagSet("cell");
+	telephone.isVideo = getTagComboBox()->isTagSet("video");
+	telephone.isBBS = getTagComboBox()->isTagSet("bbs");
+	telephone.isModem = getTagComboBox()->isTagSet("modem");
+	telephone.isISDN = getTagComboBox()->isTagSet("isdn");
+	telephone.isPCS = getTagComboBox()->isTagSet("pcs");
+
+	return telephone;
+}
+
+void QtVCardTelephoneField::handleEditibleChanged(bool isEditable) {
+	if (telephoneLineEdit) {
+		telephoneLineEdit->setEditable(isEditable);
+		telephoneLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }");
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h
new file mode 100644
index 0000000..b433e3c
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+#include "QtVCardHomeWork.h"
+
+namespace Swift {
+
+class QtVCardTelephoneField : public QtVCardGeneralField, public QtVCardHomeWork {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("Telephone", UNLIMITED_INSTANCES, QtVCardTelephoneField)
+
+		QtVCardTelephoneField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardTelephoneField();
+
+		virtual bool isEmpty() const;
+
+		void setTelephone(const VCard::Telephone& telephone);
+		VCard::Telephone getTelephone() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QtResizableLineEdit* telephoneLineEdit;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp
new file mode 100644
index 0000000..aac4e31
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardTitleField.h"
+
+#include <QGridLayout>
+#include <boost/algorithm/string.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardTitleField::QtVCardTitleField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Title"), false, false) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardTitleField::~QtVCardTitleField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardTitleField::setupContentWidgets() {
+	titleLineEdit = new QtResizableLineEdit(this);
+	getGridLayout()->addWidget(titleLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter);
+	getTagComboBox()->hide();
+	childWidgets << titleLineEdit;
+}
+
+bool QtVCardTitleField::isEmpty() const {
+	return titleLineEdit->text().isEmpty();
+}
+
+void QtVCardTitleField::setTitle(const std::string& title) {
+	titleLineEdit->setText(P2QSTRING(title));
+}
+
+std::string QtVCardTitleField::getTitle() const {
+	return Q2PSTRING(titleLineEdit->text());
+}
+
+void QtVCardTitleField::handleEditibleChanged(bool isEditable) {
+	if (titleLineEdit) {
+		titleLineEdit->setEditable(isEditable);
+		titleLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }");
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h
new file mode 100644
index 0000000..28dc603
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+
+namespace Swift {
+
+class QtVCardTitleField : public QtVCardGeneralField {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("Title", UNLIMITED_INSTANCES, QtVCardTitleField)
+
+		QtVCardTitleField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardTitleField();
+
+		virtual bool isEmpty() const;
+
+		void setTitle(const std::string& title);
+		std::string getTitle() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QtResizableLineEdit* titleLineEdit;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp
new file mode 100644
index 0000000..0b6f0c1
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardURLField.h"
+
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QTextDocument>
+#include <boost/algorithm/string.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+QtVCardURLField::QtVCardURLField(QWidget* parent, QGridLayout *layout, bool editable) :
+	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("URL"), false, false) {
+	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool)));
+}
+
+QtVCardURLField::~QtVCardURLField() {
+	disconnect(this, SLOT(handleEditibleChanged(bool)));
+}
+
+void QtVCardURLField::setupContentWidgets() {
+	urlLabel = new QLabel(this);
+	urlLabel->setOpenExternalLinks(true);
+	urlLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
+	urlLineEdit = new QtResizableLineEdit(this);
+
+	QHBoxLayout* urlLayout = new QHBoxLayout();
+	urlLayout->addWidget(urlLabel);
+	urlLayout->addWidget(urlLineEdit);
+
+	getGridLayout()->addLayout(urlLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter);
+	getTagComboBox()->hide();
+	urlLabel->hide();
+	childWidgets << urlLabel << urlLineEdit;
+}
+
+bool QtVCardURLField::isEmpty() const {
+	return urlLineEdit->text().isEmpty();
+}
+
+void QtVCardURLField::setURL(const std::string& url) {
+	urlLineEdit->setText(P2QSTRING(url));
+}
+
+std::string QtVCardURLField::getURL() const {
+	return Q2PSTRING(urlLineEdit->text());
+}
+
+void QtVCardURLField::handleEditibleChanged(bool isEditable) {
+	if (isEditable) {
+		if (urlLineEdit) urlLineEdit->show();
+		if (urlLabel) urlLabel->hide();
+	} else {
+		if (urlLineEdit) urlLineEdit->hide();
+		if (urlLabel) {
+			urlLabel->setText(QString("<a href=\"%1\">%1</a>").arg(Qt::escape(urlLineEdit->text())));
+			urlLabel->show();
+		}
+	}
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardURLField.h b/Swift/QtUI/QtVCardWidget/QtVCardURLField.h
new file mode 100644
index 0000000..2c011d8
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardURLField.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/VCard.h>
+
+#include "QtResizableLineEdit.h"
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+
+namespace Swift {
+
+class QtVCardURLField : public QtVCardGeneralField {
+	Q_OBJECT
+
+	public:
+		GENERIC_QT_VCARD_FIELD_INFO("URL", UNLIMITED_INSTANCES, QtVCardURLField)
+
+		QtVCardURLField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false);
+		virtual ~QtVCardURLField();
+
+		virtual bool isEmpty() const;
+
+		void setURL(const std::string& url);
+		std::string getURL() const;
+
+	protected:
+		virtual void setupContentWidgets();
+
+	public slots:
+		void handleEditibleChanged(bool isEditable);
+
+	private:
+		QLabel* urlLabel;
+		QtResizableLineEdit* urlLineEdit;
+};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp
new file mode 100644
index 0000000..1c80fa4
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtVCardWidget.h"
+#include "ui_QtVCardWidget.h"
+
+#include <QDebug>
+#include <QLineEdit>
+#include <QMenu>
+
+#include "QtVCardAddressField.h"
+#include "QtVCardAddressLabelField.h"
+#include "QtVCardBirthdayField.h"
+#include "QtVCardDescriptionField.h"
+#include "QtVCardGeneralField.h"
+#include "QtVCardInternetEMailField.h"
+#include "QtVCardJIDField.h"
+#include "QtVCardOrganizationField.h"
+#include "QtVCardRoleField.h"
+#include "QtVCardTelephoneField.h"
+#include "QtVCardTitleField.h"
+#include "QtVCardURLField.h"
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+QtVCardWidget::QtVCardWidget(QWidget* parent) :
+	QWidget(parent),
+	ui(new ::Ui::QtVCardWidget) {
+	ui->setupUi(this);
+
+	ui->cardFields->setColumnStretch(0,0);
+	ui->cardFields->setColumnStretch(1,0);
+	ui->cardFields->setColumnStretch(2,2);
+	ui->cardFields->setColumnStretch(3,1);
+	ui->cardFields->setColumnStretch(4,2);
+	menu = new QMenu(this);
+
+	menu->addMenu(ui->photoAndName->getAddFieldMenu());
+	ui->toolButton->setMenu(menu);
+
+	addFieldType(menu, boost::make_shared<QtVCardInternetEMailField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardTelephoneField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardAddressField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardAddressLabelField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardBirthdayField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardJIDField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardDescriptionField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardRoleField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardTitleField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardOrganizationField::FieldInfo>());
+	addFieldType(menu, boost::make_shared<QtVCardURLField::FieldInfo>());
+
+	setEditable(false);
+}
+
+QtVCardWidget::~QtVCardWidget() {
+	delete ui;
+}
+
+bool QtVCardWidget::isEditable() const {
+	return editable;
+}
+
+void QtVCardWidget::setEditable(bool editable) {
+	this->editable = editable;
+
+	ui->photoAndName->setProperty("editable", QVariant(editable));
+
+	foreach(QtVCardGeneralField* field, fields) {
+		field->setEditable(editable);
+	}
+
+	if (editable) {
+		ui->toolButton->show();
+		//if ((findChild<QtVCardBirthdayField*>() == 0)) {
+		//}
+	} else {
+		ui->toolButton->hide();
+	}
+
+	editableChanged(editable);
+}
+
+void QtVCardWidget::setVCard(VCard::ref vcard) {
+	SWIFT_LOG(debug) << std::endl;
+	clearFields();
+	this->vcard = vcard;
+	ui->photoAndName->setFormattedName(P2QSTRING(vcard->getFullName()));
+	ui->photoAndName->setNickname(P2QSTRING(vcard->getNickname()));
+	ui->photoAndName->setPrefix(P2QSTRING(vcard->getPrefix()));
+	ui->photoAndName->setGivenName(P2QSTRING(vcard->getGivenName()));
+	ui->photoAndName->setMiddleName(P2QSTRING(vcard->getMiddleName()));
+	ui->photoAndName->setFamilyName(P2QSTRING(vcard->getFamilyName()));
+	ui->photoAndName->setSuffix(P2QSTRING(vcard->getSuffix()));
+	ui->photoAndName->setAvatar(vcard->getPhoto(), vcard->getPhotoType());
+
+	foreach (const VCard::EMailAddress& address, vcard->getEMailAddresses()) {
+		if (address.isInternet) {
+			QtVCardInternetEMailField* internetEmailField = new QtVCardInternetEMailField(this, ui->cardFields);
+			internetEmailField->initialize();
+			internetEmailField->setInternetEMailAddress(address);
+			appendField(internetEmailField);
+		}
+	}
+
+	foreach (const VCard::Telephone& telephone, vcard->getTelephones()) {
+		QtVCardTelephoneField* telField = new QtVCardTelephoneField(this, ui->cardFields);
+		telField->initialize();
+		telField->setTelephone(telephone);
+		appendField(telField);
+	}
+
+	foreach (const VCard::Address& address, vcard->getAddresses()) {
+		QtVCardAddressField* addressField = new QtVCardAddressField(this, ui->cardFields);
+		addressField->initialize();
+		addressField->setAddress(address);
+		appendField(addressField);
+	}
+
+	foreach (const VCard::AddressLabel& label, vcard->getAddressLabels()) {
+		QtVCardAddressLabelField* addressLabelField = new QtVCardAddressLabelField(this, ui->cardFields);
+		addressLabelField->initialize();
+		addressLabelField->setAddressLabel(label);
+		appendField(addressLabelField);
+	}
+
+	if (!vcard->getBirthday().is_not_a_date_time()) {
+		QtVCardBirthdayField* bdayField = new QtVCardBirthdayField(this, ui->cardFields);
+		bdayField->initialize();
+		bdayField->setBirthday(vcard->getBirthday());
+		appendField(bdayField);
+	}
+
+	foreach (const JID& jid, vcard->getJIDs()) {
+		QtVCardJIDField* jidField = new QtVCardJIDField(this, ui->cardFields);
+		jidField->initialize();
+		jidField->setJID(jid);
+		appendField(jidField);
+	}
+
+	if (!vcard->getDescription().empty()) {
+		QtVCardDescriptionField* descField = new QtVCardDescriptionField(this, ui->cardFields);
+		descField->initialize();
+		descField->setDescription(vcard->getDescription());
+		appendField(descField);
+	}
+
+	foreach (const VCard::Organization& org, vcard->getOrganizations()) {
+		QtVCardOrganizationField* orgField = new QtVCardOrganizationField(this, ui->cardFields);
+		orgField->initialize();
+		orgField->setOrganization(org);
+		appendField(orgField);
+	}
+
+	foreach (const std::string& role, vcard->getRoles()) {
+		QtVCardRoleField* roleField = new QtVCardRoleField(this, ui->cardFields);
+		roleField->initialize();
+		roleField->setRole(role);
+		appendField(roleField);
+	}
+
+	foreach (const std::string& title, vcard->getTitles()) {
+		QtVCardTitleField* titleField = new QtVCardTitleField(this, ui->cardFields);
+		titleField->initialize();
+		titleField->setTitle(title);
+		appendField(titleField);
+	}
+
+	foreach (const std::string& url, vcard->getURLs()) {
+		QtVCardURLField* urlField = new QtVCardURLField(this, ui->cardFields);
+		urlField->initialize();
+		urlField->setURL(url);
+		appendField(urlField);
+	}
+
+	setEditable(editable);
+}
+
+VCard::ref QtVCardWidget::getVCard() {
+	SWIFT_LOG(debug) << std::endl;
+	clearEmptyFields();
+	vcard->setFullName(Q2PSTRING(ui->photoAndName->getFormattedName()));
+	vcard->setNickname(Q2PSTRING(ui->photoAndName->getNickname()));
+	vcard->setPrefix(Q2PSTRING(ui->photoAndName->getPrefix()));
+	vcard->setGivenName(Q2PSTRING(ui->photoAndName->getGivenName()));
+	vcard->setMiddleName(Q2PSTRING(ui->photoAndName->getMiddleName()));
+	vcard->setFamilyName(Q2PSTRING(ui->photoAndName->getFamilyName()));
+	vcard->setSuffix(Q2PSTRING(ui->photoAndName->getSuffix()));
+	vcard->setPhoto(ui->photoAndName->getAvatarData());
+	vcard->setPhotoType(ui->photoAndName->getAvatarType());
+
+	vcard->clearEMailAddresses();
+	vcard->clearJIDs();
+	vcard->clearURLs();
+	vcard->clearTelephones();
+	vcard->clearRoles();
+	vcard->clearTitles();
+	vcard->clearOrganizations();
+	vcard->clearAddresses();
+	vcard->clearAddressLabels();
+
+
+	QtVCardBirthdayField* bdayField = NULL;
+	QtVCardDescriptionField* descriptionField = NULL;
+
+	foreach(QtVCardGeneralField* field, fields) {
+		QtVCardInternetEMailField* emailField;
+		if ((emailField = dynamic_cast<QtVCardInternetEMailField*>(field))) {
+			vcard->addEMailAddress(emailField->getInternetEMailAddress());
+			continue;
+		}
+
+		QtVCardTelephoneField* telephoneField;
+		if ((telephoneField = dynamic_cast<QtVCardTelephoneField*>(field))) {
+			vcard->addTelephone(telephoneField->getTelephone());
+			continue;
+		}
+
+		QtVCardAddressField* addressField;
+		if ((addressField = dynamic_cast<QtVCardAddressField*>(field))) {
+			vcard->addAddress(addressField->getAddress());
+			continue;
+		}
+
+		QtVCardAddressLabelField* addressLabelField;
+		if ((addressLabelField = dynamic_cast<QtVCardAddressLabelField*>(field))) {
+			vcard->addAddressLabel(addressLabelField->getAddressLabel());
+			continue;
+		}
+
+		if ((bdayField = dynamic_cast<QtVCardBirthdayField*>(field))) {
+			continue;
+		}
+
+		QtVCardJIDField* jidField;
+		if ((jidField = dynamic_cast<QtVCardJIDField*>(field))) {
+			vcard->addJID(jidField->getJID());
+			continue;
+		}
+
+		if ((descriptionField = dynamic_cast<QtVCardDescriptionField*>(field))) {
+			continue;
+		}
+
+		QtVCardOrganizationField* orgField;
+		if ((orgField = dynamic_cast<QtVCardOrganizationField*>(field))) {
+			vcard->addOrganization(orgField->getOrganization());
+			continue;
+		}
+
+		QtVCardRoleField* roleField;
+		if ((roleField = dynamic_cast<QtVCardRoleField*>(field))) {
+			vcard->addRole(roleField->getRole());
+			continue;
+		}
+
+		QtVCardTitleField* titleField;
+		if ((titleField = dynamic_cast<QtVCardTitleField*>(field))) {
+			vcard->addTitle(titleField->getTitle());
+			continue;
+		}
+
+		QtVCardURLField* urlField;
+		if ((urlField = dynamic_cast<QtVCardURLField*>(field))) {
+			vcard->addURL(urlField->getURL());
+			continue;
+		}
+	}
+
+	if (bdayField) {
+		vcard->setBirthday(bdayField->getBirthday());
+	} else {
+		vcard->setBirthday(boost::posix_time::ptime());
+	}
+
+	if (descriptionField) {
+		vcard->setDescription(descriptionField->getDescription());
+	} else {
+		vcard->setDescription("");
+	}
+
+	return vcard;
+}
+
+void QtVCardWidget::addField() {
+	QAction* action = NULL;
+	if ((action = dynamic_cast<QAction*>(sender()))) {
+		boost::shared_ptr<QtVCardFieldInfo> fieldInfo = actionFieldInfo[action];
+		QWidget* newField = fieldInfo->createFieldInstance(this, ui->cardFields, true);
+		QtVCardGeneralField* newGeneralField = dynamic_cast<QtVCardGeneralField*>(newField);
+		if (newGeneralField) {
+			newGeneralField->initialize();
+		}
+		appendField(newGeneralField);
+	}
+}
+
+void QtVCardWidget::removeField(QtVCardGeneralField *field) {
+	fields.remove(field);
+	delete field;
+}
+
+void QtVCardWidget::addFieldType(QMenu* menu, boost::shared_ptr<QtVCardFieldInfo> fieldType) {
+	QAction* action = new QAction(tr("Add ") + fieldType->getMenuName(), this);
+	actionFieldInfo[action] = fieldType;
+	connect(action, SIGNAL(triggered()), this, SLOT(addField()));
+	menu->addAction(action);
+}
+
+int QtVCardWidget::fieldTypeInstances(boost::shared_ptr<QtVCardFieldInfo> fieldType) {
+	int instances = 0;
+	for (int n = 0; n < ui->cardFields->count(); n++) {
+		if (fieldType->testInstance(ui->cardFields->itemAt(n)->widget())) instances++;
+	}
+	return instances;
+}
+
+void layoutDeleteChildren(QLayout *layout) {
+	while(layout->count() > 0) {
+		QLayoutItem* child;
+		if ((child = layout->takeAt(0)) != 0) {
+			if (child->layout()) {
+				layoutDeleteChildren(child->layout());
+			}
+			delete child->widget();
+			delete child;
+		}
+	}
+}
+
+void QtVCardWidget::clearFields() {
+	foreach(QtVCardGeneralField* field, fields) {
+		delete field;
+	}
+	fields.clear();
+
+	assert(ui->cardFields->count() >= 0);
+	layoutDeleteChildren(ui->cardFields);
+}
+
+void QtVCardWidget::clearEmptyFields() {
+	std::vector<QtVCardGeneralField*> items_to_remove;
+	foreach (QtVCardGeneralField* field, fields) {
+		if (field->property("empty").isValid() && field->property("empty").toBool()) {
+			ui->cardFields->removeWidget(field);
+			items_to_remove.push_back(field);
+			delete field;
+		}
+	}
+
+	foreach(QtVCardGeneralField* field, items_to_remove) {
+		fields.remove(field);
+	}
+}
+
+void QtVCardWidget::appendField(QtVCardGeneralField *field) {
+	connect(field, SIGNAL(deleteField(QtVCardGeneralField*)), SLOT(removeField(QtVCardGeneralField*)));
+	fields.push_back(field);
+}
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.h b/Swift/QtUI/QtVCardWidget/QtVCardWidget.h
new file mode 100644
index 0000000..07f528d
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QWidget>
+#include <Swiften/Elements/VCard.h>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include "QtVCardFieldInfo.h"
+#include "QtVCardGeneralField.h"
+#include "QtVCardPhotoAndNameFields.h"
+
+namespace Ui {
+	class QtVCardWidget;
+}
+
+namespace Swift {
+
+	class QtVCardWidget : public QWidget {
+		Q_OBJECT
+		Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
+
+		public :
+			explicit QtVCardWidget(QWidget* parent = 0);
+			~QtVCardWidget();
+
+			bool isEditable() const;
+			void setEditable(bool);
+
+			void setVCard(VCard::ref vcard);
+			VCard::ref getVCard();
+
+		signals:
+			void editableChanged(bool editable);
+
+		private slots:
+			void addField();
+			void removeField(QtVCardGeneralField* field);
+
+		private:
+			void addFieldType(QMenu*, boost::shared_ptr<QtVCardFieldInfo>);
+			int fieldTypeInstances(boost::shared_ptr<QtVCardFieldInfo>);
+			void clearFields();
+			void clearEmptyFields();
+			void appendField(QtVCardGeneralField* field);
+
+		private:
+			VCard::ref vcard;
+			Ui::QtVCardWidget* ui;
+			bool editable;
+			QMenu* menu;
+			std::list<QtVCardGeneralField*> fields;
+			std::map<QAction*, boost::shared_ptr<QtVCardFieldInfo> > actionFieldInfo;
+	};
+
+}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui b/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui
new file mode 100644
index 0000000..eae1006
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtVCardWidget</class>
+ <widget class="QWidget" name="QtVCardWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>535</width>
+    <height>126</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout" rowstretch="1,0" columnstretch="1,0">
+   <property name="margin">
+    <number>5</number>
+   </property>
+   <item row="0" column="0" colspan="2">
+    <layout class="QVBoxLayout" name="card" stretch="0,0,1">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <property name="sizeConstraint">
+      <enum>QLayout::SetDefaultConstraint</enum>
+     </property>
+     <item>
+      <widget class="Swift::QtVCardPhotoAndNameFields" name="photoAndName" native="true"/>
+     </item>
+     <item>
+      <widget class="Line" name="line">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QScrollArea" name="scrollArea">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="frameShape">
+        <enum>QFrame::NoFrame</enum>
+       </property>
+       <property name="frameShadow">
+        <enum>QFrame::Plain</enum>
+       </property>
+       <property name="horizontalScrollBarPolicy">
+        <enum>Qt::ScrollBarAlwaysOff</enum>
+       </property>
+       <property name="widgetResizable">
+        <bool>true</bool>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+       <widget class="QWidget" name="scrollAreaWidgetContents">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>0</y>
+          <width>523</width>
+          <height>76</height>
+         </rect>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <layout class="QVBoxLayout" name="verticalLayout">
+         <property name="sizeConstraint">
+          <enum>QLayout::SetMinAndMaxSize</enum>
+         </property>
+         <property name="margin">
+          <number>0</number>
+         </property>
+         <item>
+          <layout class="QGridLayout" name="cardFields">
+          </layout>
+         </item>
+         <item>
+          <spacer name="verticalSpacer">
+           <property name="orientation">
+            <enum>Qt::Vertical</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>1000</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </widget>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="1">
+    <widget class="QToolButton" name="toolButton">
+     <property name="text">
+      <string>Add Field</string>
+     </property>
+     <property name="popupMode">
+      <enum>QToolButton::InstantPopup</enum>
+     </property>
+     <property name="autoRaise">
+      <bool>false</bool>
+     </property>
+     <property name="arrowType">
+      <enum>Qt::NoArrow</enum>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Swift::QtVCardPhotoAndNameFields</class>
+   <extends>QWidget</extends>
+   <header>QtVCardPhotoAndNameFields.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.cpp b/Swift/QtUI/Roster/QtOccupantListWidget.cpp
index 5d26c46..12dc1e4 100644
--- a/Swift/QtUI/Roster/QtOccupantListWidget.cpp
+++ b/Swift/QtUI/Roster/QtOccupantListWidget.cpp
@@ -58,6 +58,7 @@ void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) {
 					case ChatWindow::MakeParticipant: text = tr("Make participant"); break;
 					case ChatWindow::MakeVisitor: text = tr("Remove voice"); break;
 					case ChatWindow::AddContact: text = tr("Add to contacts"); break;
+					case ChatWindow::ShowProfile: text = tr("Show profile"); break;
 				}
 				QAction* action = contextMenu.addAction(text);
 				actions[action] = availableAction;
diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp
index 1cf073b..b783ff6 100644
--- a/Swift/QtUI/Roster/QtRosterWidget.cpp
+++ b/Swift/QtUI/Roster/QtRosterWidget.cpp
@@ -16,6 +16,7 @@
 #include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h"
 #include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
 #include "Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h"
+#include "Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h"
 #include "QtContactEditWindow.h"
 #include "Swift/Controllers/Roster/ContactRosterItem.h"
 #include "Swift/Controllers/Roster/GroupRosterItem.h"
@@ -57,6 +58,7 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {
 	if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {
 		QAction* editContact = contextMenu.addAction(tr("Edit…"));
 		QAction* removeContact = contextMenu.addAction(tr("Remove"));
+		QAction* showProfileForContact = contextMenu.addAction(tr("Show Profile"));
 #ifdef SWIFT_EXPERIMENTAL_FT
 		QAction* sendFile = NULL;
 		if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
@@ -78,6 +80,9 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {
 				eventStream_->send(boost::make_shared<RemoveRosterItemUIEvent>(contact->getJID()));
 			}
 		}
+		else if (result == showProfileForContact) {
+			eventStream_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(contact->getJID()));
+		}
 #ifdef SWIFT_EXPERIMENTAL_FT
 		else if (sendFile && result == sendFile) {
 			QString fileName = QFileDialog::getOpenFileName(this, tr("Send File"), "", tr("All Files (*);;"));
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index cd0ed57..4ab6792 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -24,7 +24,7 @@ myenv = env.Clone()
 # Disable warnings that affect Qt
 myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"])
 if "clang" in env["CC"] :
-	myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-missing-prototypes", "-Wno-unreachable-code", "-Wno-disabled-macro-expansion", "-Wno-unused-private-field", "-Wno-extra-semi", "-Wno-duplicate-enum", "-Wno-missing-variable-declarations"])
+	myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-missing-prototypes", "-Wno-unreachable-code", "-Wno-disabled-macro-expansion", "-Wno-unused-private-field", "-Wno-extra-semi", "-Wno-duplicate-enum", "-Wno-missing-variable-declarations", "-Wno-conversion"])
 
 myenv.UseFlags(env["SWIFT_CONTROLLERS_FLAGS"])
 myenv.UseFlags(env["SWIFTOOLS_FLAGS"])
@@ -175,6 +175,34 @@ sources = [
     "QtURLValidator.cpp"
   ]
 
+# QtVCardWidget
+sources.extend([
+	"QtVCardWidget/QtCloseButton.cpp",
+	"QtVCardWidget/QtRemovableItemDelegate.cpp",
+	"QtVCardWidget/QtResizableLineEdit.cpp",
+	"QtVCardWidget/QtTagComboBox.cpp",
+	"QtVCardWidget/QtVCardHomeWork.cpp",
+	"QtVCardWidget/QtVCardAddressField.cpp",
+	"QtVCardWidget/QtVCardAddressLabelField.cpp",
+	"QtVCardWidget/QtVCardBirthdayField.cpp",
+	"QtVCardWidget/QtVCardDescriptionField.cpp",
+	"QtVCardWidget/QtVCardInternetEMailField.cpp",
+	"QtVCardWidget/QtVCardJIDField.cpp",
+	"QtVCardWidget/QtVCardOrganizationField.cpp",
+	"QtVCardWidget/QtVCardPhotoAndNameFields.cpp",
+	"QtVCardWidget/QtVCardRoleField.cpp",
+	"QtVCardWidget/QtVCardTelephoneField.cpp",
+	"QtVCardWidget/QtVCardTitleField.cpp",
+	"QtVCardWidget/QtVCardURLField.cpp",
+	"QtVCardWidget/QtVCardGeneralField.cpp",
+	"QtVCardWidget/QtVCardWidget.cpp"
+])
+
+myenv.Uic4("QtVCardWidget/QtVCardPhotoAndNameFields.ui")
+myenv.Uic4("QtVCardWidget/QtVCardWidget.ui")
+myenv.Uic4("QtProfileWindow.ui")
+
+
 # Determine the version
 myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
 if env["PLATFORM"] == "win32" :
diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc
index 39201da..934bd80 100644
--- a/Swift/QtUI/Swift.qrc
+++ b/Swift/QtUI/Swift.qrc
@@ -38,5 +38,7 @@
 		<file alias="emoticons/tongue.png">../resources/emoticons/tongue.png</file>
 		<file alias="emoticons/unhappy.png">../resources/emoticons/unhappy.png</file>
 		<file alias="emoticons/wink.png">../resources/emoticons/wink.png</file>
+		<file alias="icons/star-checked.png">../resources/icons/star-checked2.png</file>
+		<file alias="icons/star-unchecked.png">../resources/icons/star-unchecked2.png</file>
 	</qresource>
 </RCC>
diff --git a/Swift/resources/icons/star-checked2.png b/Swift/resources/icons/star-checked2.png
new file mode 100644
index 0000000..2908534
Binary files /dev/null and b/Swift/resources/icons/star-checked2.png differ
diff --git a/Swift/resources/icons/star-checked2.svg b/Swift/resources/icons/star-checked2.svg
new file mode 100644
index 0000000..ea4d1da
--- /dev/null
+++ b/Swift/resources/icons/star-checked2.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="89.89167"
+   height="85.751091"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="star-checked2.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="7.9195959"
+     inkscape:cx="27.108144"
+     inkscape:cy="28.243526"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     showguides="false"
+     inkscape:snap-global="false"
+     fit-margin-top="0.6"
+     fit-margin-left="0.6"
+     fit-margin-right="0.6"
+     fit-margin-bottom="0.6"
+     inkscape:window-width="1440"
+     inkscape:window-height="852"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2987"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-114.63435,-344.09596)">
+    <path
+       sodipodi:type="star"
+       style="fill:#ffd700;fill-opacity:1;stroke:#444444;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="path2989"
+       sodipodi:sides="5"
+       sodipodi:cx="150"
+       sodipodi:cy="393.07648"
+       sodipodi:r1="44.498337"
+       sodipodi:r2="24.474085"
+       sodipodi:arg1="-0.32682665"
+       sodipodi:arg2="0.30149188"
+       inkscape:flatsided="false"
+       inkscape:rounded="0.05"
+       inkscape:randomized="0"
+       d="m 192.14286,378.79076 c 0.4588,1.35347 -18.34832,20.18852 -18.77269,21.55318 -0.42437,1.36466 4.38466,27.54365 3.23921,28.39825 -1.14545,0.85459 -24.87036,-11.21169 -26.29937,-11.19359 -1.429,0.0181 -24.84064,12.68151 -26.00737,11.85621 -1.16672,-0.8253 2.97759,-27.11772 2.51879,-28.47119 -0.4588,-1.35347 -19.73702,-19.70605 -19.31265,-21.07071 0.42437,-1.36466 26.71061,-5.54798 27.85606,-6.40257 1.14545,-0.8546 12.64249,-24.86053 14.0715,-24.87863 1.429,-0.0181 13.53048,23.68888 14.69721,24.51418 1.16672,0.82531 27.5505,4.3414 28.00931,5.69487 z"
+       inkscape:transform-center-x="0.015620988"
+       inkscape:transform-center-y="-4.2343566"
+       transform="matrix(0.99993351,0.01153134,-0.01153134,0.99993351,14.139118,-3.5976011)" />
+  </g>
+</svg>
diff --git a/Swift/resources/icons/star-unchecked2.png b/Swift/resources/icons/star-unchecked2.png
new file mode 100644
index 0000000..ee85d69
Binary files /dev/null and b/Swift/resources/icons/star-unchecked2.png differ
diff --git a/Swift/resources/icons/star-unchecked2.svg b/Swift/resources/icons/star-unchecked2.svg
new file mode 100644
index 0000000..4186d0e
--- /dev/null
+++ b/Swift/resources/icons/star-unchecked2.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="89.89167"
+   height="85.751091"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="star-unchecked2.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="7.9195959"
+     inkscape:cx="27.108141"
+     inkscape:cy="28.243523"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     showguides="false"
+     inkscape:snap-global="false"
+     fit-margin-top="0.6"
+     fit-margin-left="0.6"
+     fit-margin-right="0.6"
+     fit-margin-bottom="0.6"
+     inkscape:window-width="1440"
+     inkscape:window-height="852"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2987"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-114.63435,-344.09596)">
+    <path
+       sodipodi:type="star"
+       style="fill:#ffffff;fill-opacity:1;stroke:#444444;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="path2989"
+       sodipodi:sides="5"
+       sodipodi:cx="150"
+       sodipodi:cy="393.07648"
+       sodipodi:r1="44.498337"
+       sodipodi:r2="24.474085"
+       sodipodi:arg1="-0.32682665"
+       sodipodi:arg2="0.30149188"
+       inkscape:flatsided="false"
+       inkscape:rounded="0.05"
+       inkscape:randomized="0"
+       d="m 192.14286,378.79076 c 0.4588,1.35347 -18.34832,20.18852 -18.77269,21.55318 -0.42437,1.36466 4.38466,27.54365 3.23921,28.39825 -1.14545,0.85459 -24.87036,-11.21169 -26.29937,-11.19359 -1.429,0.0181 -24.84064,12.68151 -26.00737,11.85621 -1.16672,-0.8253 2.97759,-27.11772 2.51879,-28.47119 -0.4588,-1.35347 -19.73702,-19.70605 -19.31265,-21.07071 0.42437,-1.36466 26.71061,-5.54798 27.85606,-6.40257 1.14545,-0.8546 12.64249,-24.86053 14.0715,-24.87863 1.429,-0.0181 13.53048,23.68888 14.69721,24.51418 1.16672,0.82531 27.5505,4.3414 28.00931,5.69487 z"
+       inkscape:transform-center-x="0.015620988"
+       inkscape:transform-center-y="-4.2343566"
+       transform="matrix(0.99993351,0.01153134,-0.01153134,0.99993351,14.139118,-3.5976011)" />
+  </g>
+</svg>
diff --git a/Swiften/Elements/VCard.h b/Swiften/Elements/VCard.h
index f9822c9..84b6cfe 100644
--- a/Swiften/Elements/VCard.h
+++ b/Swiften/Elements/VCard.h
@@ -7,8 +7,10 @@
 #pragma once
 
 #include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
 
 #include <string>
+#include <Swiften/JID/JID.h>
 #include <Swiften/Base/ByteArray.h>
 #include <Swiften/Elements/Payload.h>
 
@@ -29,6 +31,71 @@ namespace Swift {
 				std::string address;
 			};
 
+			struct Telephone {
+				Telephone() : isHome(false), isWork(false), isVoice(false), isFax(false), isPager(false), isMSG(false), isCell(false),
+					isVideo(false), isBBS(false), isModem(false), isISDN(false), isPCS(false), isPreferred(false) {
+				}
+
+				bool isHome;
+				bool isWork;
+				bool isVoice;
+				bool isFax;
+				bool isPager;
+				bool isMSG;
+				bool isCell;
+				bool isVideo;
+				bool isBBS;
+				bool isModem;
+				bool isISDN;
+				bool isPCS;
+				bool isPreferred;
+				std::string number;
+			};
+
+			enum DeliveryType {
+				DomesticDelivery,
+				InternationalDelivery,
+				None
+			};
+
+			struct Address {
+				Address() : isHome(false), isWork(false), isPostal(false), isParcel(false), deliveryType(None), isPreferred(false) {
+				}
+
+				bool isHome;
+				bool isWork;
+				bool isPostal;
+				bool isParcel;
+				DeliveryType deliveryType;
+				bool isPreferred;
+
+				std::string poBox;
+				std::string addressExtension;
+				std::string street;
+				std::string locality;
+				std::string region;
+				std::string postalCode;
+				std::string country;
+			};
+
+			struct AddressLabel {
+				AddressLabel() : isHome(false), isWork(false), isPostal(false), isParcel(false), deliveryType(None), isPreferred(false) {
+				}
+
+				bool isHome;
+				bool isWork;
+				bool isPostal;
+				bool isParcel;
+				DeliveryType deliveryType;
+				bool isPreferred;
+				std::vector<std::string> lines;
+			};
+
+			struct Organization {
+				std::string name;
+				std::vector<std::string> units;
+			};
+
 			VCard() {}
 
 			void setVersion(const std::string& version) { version_ = version; }
@@ -78,8 +145,124 @@ namespace Swift {
 				emailAddresses_.push_back(email);
 			}
 
+			void clearEMailAddresses() {
+				emailAddresses_.clear();
+			}
+
 			EMailAddress getPreferredEMailAddress() const;
 
+			void setBirthday(const boost::posix_time::ptime& birthday) {
+				birthday_ = birthday;
+			}
+
+			const boost::posix_time::ptime& getBirthday() const {
+				return birthday_;
+			}
+
+			const std::vector<Telephone>& getTelephones() const {
+				return telephones_;
+			}
+
+			void addTelephone(const Telephone& phone) {
+				telephones_.push_back(phone);
+			}
+
+			void clearTelephones() {
+				telephones_.clear();
+			}
+
+			const std::vector<Address>& getAddresses() const {
+				return addresses_;
+			}
+
+			void addAddress(const Address& address) {
+				addresses_.push_back(address);
+			}
+
+			void clearAddresses() {
+				addresses_.clear();
+			}
+
+			const std::vector<AddressLabel>& getAddressLabels() const {
+				return addressLabels_;
+			}
+
+			void addAddressLabel(const AddressLabel& addressLabel) {
+				addressLabels_.push_back(addressLabel);
+			}
+
+			void clearAddressLabels() {
+				addressLabels_.clear();
+			}
+
+			const std::vector<JID>& getJIDs() const {
+				return jids_;
+			}
+
+			void addJID(const JID& jid) {
+				jids_.push_back(jid);
+			}
+
+			void clearJIDs() {
+				jids_.clear();
+			}
+
+			const std::string& getDescription() const {
+				return description_;
+			}
+
+			void setDescription(const std::string& description) {
+				this->description_ = description;
+			}
+
+			const std::vector<Organization>& getOrganizations() const {
+				return organizations_;
+			}
+
+			void addOrganization(const Organization& organization) {
+				organizations_.push_back(organization);
+			}
+
+			void clearOrganizations() {
+				organizations_.clear();
+			}
+
+			const std::vector<std::string>& getTitles() const {
+				return titles_;
+			}
+
+			void addTitle(const std::string& title) {
+				titles_.push_back(title);
+			}
+
+			void clearTitles() {
+				titles_.clear();
+			}
+
+			const std::vector<std::string>& getRoles() const {
+				return roles_;
+			}
+
+			void addRole(const std::string& role) {
+				roles_.push_back(role);
+			}
+
+			void clearRoles() {
+				roles_.clear();
+			}
+
+			const std::vector<std::string>& getURLs() const {
+				return urls_;
+			}
+
+			void addURL(const std::string& url) {
+				urls_.push_back(url);
+			}
+
+			void clearURLs() {
+				urls_.clear();
+			}
+
 		private:
 			std::string version_;
 			std::string fullName_;
@@ -92,7 +275,17 @@ namespace Swift {
 			ByteArray photo_;
 			std::string photoType_;
 			std::string nick_;
+			boost::posix_time::ptime birthday_;
 			std::string unknownContent_;
 			std::vector<EMailAddress> emailAddresses_;
+			std::vector<Telephone> telephones_;
+			std::vector<Address> addresses_;
+			std::vector<AddressLabel> addressLabels_;
+			std::vector<JID> jids_;
+			std::string description_;
+			std::vector<Organization> organizations_;
+			std::vector<std::string> titles_;
+			std::vector<std::string> roles_;
+			std::vector<std::string> urls_;
 	};
 }
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp
index f1e6635..eda2547 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp
@@ -7,6 +7,8 @@
 #include <Swiften/Base/ByteArray.h>
 #include <QA/Checker/IO.h>
 
+#include <boost/date_time/posix_time/posix_time.hpp>
+
 #include <cppunit/extensions/HelperMacros.h>
 #include <cppunit/extensions/TestFactoryRegistry.h>
 
@@ -48,8 +50,36 @@ class VCardParserTest : public CppUnit::TestFixture {
 						"<WORK/>"
 						"<X400/>"
 					"</EMAIL>"
+					"<TEL>"
+						"<NUMBER>555-6273</NUMBER>"
+						"<HOME/>"
+						"<VOICE/>"
+					"</TEL>"
+					"<ADR>"
+						"<LOCALITY>Any Town</LOCALITY>"
+						"<STREET>Fake Street 123</STREET>"
+						"<PCODE>12345</PCODE>"
+						"<CTRY>USA</CTRY>"
+						"<HOME/>"
+					"</ADR>"
+					"<LABEL>"
+						"<LINE>Fake Street 123</LINE>"
+						"<LINE>12345 Any Town</LINE>"
+						"<LINE>USA</LINE>"
+						"<HOME/>"
+					"</LABEL>"
 					"<NICKNAME>DreamGirl</NICKNAME>"
-					"<BDAY>1234</BDAY>"
+					"<BDAY>1865-05-04</BDAY>"
+					"<JID>alice@teaparty.lit</JID>"
+					"<JID>alice@wonderland.lit</JID>"
+					"<DESC>I once fell down a rabbit hole.</DESC>"
+					"<ORG>"
+						"<ORGNAME>Alice In Wonderland Inc.</ORGNAME>"
+					"</ORG>"
+					"<TITLE>Some Title</TITLE>"
+					"<ROLE>Main Character</ROLE>"
+					"<URL>http://wonderland.lit/~alice</URL>"
+					"<URL>http://teaparty.lit/~alice2</URL>"
 					"<MAILER>mutt</MAILER>"
 				"</vCard>"));
 
@@ -62,7 +92,7 @@ class VCardParserTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(std::string("Mrs"), payload->getPrefix());
 			CPPUNIT_ASSERT_EQUAL(std::string("PhD"), payload->getSuffix());
 			CPPUNIT_ASSERT_EQUAL(std::string("DreamGirl"), payload->getNickname());
-			CPPUNIT_ASSERT_EQUAL(std::string("<BDAY xmlns=\"vcard-temp\">1234</BDAY><MAILER xmlns=\"vcard-temp\">mutt</MAILER>"), payload->getUnknownContent());
+			CPPUNIT_ASSERT_EQUAL(boost::posix_time::ptime(boost::gregorian::date(1865, 5, 4)), payload->getBirthday());
 			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(payload->getEMailAddresses().size()));
 			CPPUNIT_ASSERT_EQUAL(std::string("alice@wonderland.lit"), payload->getEMailAddresses()[0].address);
 			CPPUNIT_ASSERT(payload->getEMailAddresses()[0].isHome);
@@ -76,6 +106,45 @@ class VCardParserTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT(!payload->getEMailAddresses()[1].isPreferred);
 			CPPUNIT_ASSERT(payload->getEMailAddresses()[1].isWork);
 			CPPUNIT_ASSERT(payload->getEMailAddresses()[1].isX400);
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(payload->getTelephones().size()));
+			CPPUNIT_ASSERT_EQUAL(std::string("555-6273"), payload->getTelephones()[0].number);
+			CPPUNIT_ASSERT(payload->getTelephones()[0].isHome);
+			CPPUNIT_ASSERT(payload->getTelephones()[0].isVoice);
+			CPPUNIT_ASSERT(!payload->getTelephones()[0].isPreferred);
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(payload->getAddresses().size()));
+			CPPUNIT_ASSERT_EQUAL(std::string("Any Town"), payload->getAddresses()[0].locality);
+			CPPUNIT_ASSERT_EQUAL(std::string("Fake Street 123"), payload->getAddresses()[0].street);
+			CPPUNIT_ASSERT_EQUAL(std::string("12345"), payload->getAddresses()[0].postalCode);
+			CPPUNIT_ASSERT_EQUAL(std::string("USA"), payload->getAddresses()[0].country);
+			CPPUNIT_ASSERT(payload->getAddresses()[0].isHome);
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(payload->getAddressLabels().size()));
+			CPPUNIT_ASSERT_EQUAL(std::string("Fake Street 123"), payload->getAddressLabels()[0].lines[0]);
+			CPPUNIT_ASSERT_EQUAL(std::string("12345 Any Town"), payload->getAddressLabels()[0].lines[1]);
+			CPPUNIT_ASSERT_EQUAL(std::string("USA"), payload->getAddressLabels()[0].lines[2]);
+			CPPUNIT_ASSERT(payload->getAddressLabels()[0].isHome);
+
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(payload->getJIDs().size()));
+			CPPUNIT_ASSERT_EQUAL(JID("alice@teaparty.lit"), payload->getJIDs()[0]);
+			CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit"), payload->getJIDs()[1]);
+
+			CPPUNIT_ASSERT_EQUAL(std::string("I once fell down a rabbit hole."), payload->getDescription());
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(payload->getOrganizations().size()));
+			CPPUNIT_ASSERT_EQUAL(std::string("Alice In Wonderland Inc."), payload->getOrganizations()[0].name);
+			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(payload->getOrganizations()[0].units.size()));
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(payload->getTitles().size()));
+			CPPUNIT_ASSERT_EQUAL(std::string("Some Title"), payload->getTitles()[0]);
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(payload->getRoles().size()));
+			CPPUNIT_ASSERT_EQUAL(std::string("Main Character"), payload->getRoles()[0]);
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(payload->getURLs().size()));
+			CPPUNIT_ASSERT_EQUAL(std::string("http://wonderland.lit/~alice"), payload->getURLs()[0]);
+			CPPUNIT_ASSERT_EQUAL(std::string("http://teaparty.lit/~alice2"), payload->getURLs()[1]);
+
+			CPPUNIT_ASSERT_EQUAL(std::string("<MAILER xmlns=\"vcard-temp\">mutt</MAILER>"), payload->getUnknownContent());
 		}
 
 		void testParse_Photo() {
diff --git a/Swiften/Parser/PayloadParsers/VCardParser.cpp b/Swiften/Parser/PayloadParsers/VCardParser.cpp
index 553d26a..620a307 100644
--- a/Swiften/Parser/PayloadParsers/VCardParser.cpp
+++ b/Swiften/Parser/PayloadParsers/VCardParser.cpp
@@ -6,6 +6,7 @@
 
 #include <Swiften/Parser/PayloadParsers/VCardParser.h>
 #include <Swiften/Base/foreach.h>
+#include <Swiften/Base/DateTime.h>
 #include <Swiften/StringCodecs/Base64.h>
 #include <Swiften/Parser/SerializingParser.h>
 
@@ -20,6 +21,18 @@ void VCardParser::handleStartElement(const std::string& element, const std::stri
 	if (elementHierarchy == "/vCard/EMAIL") {
 		currentEMailAddress_ = VCard::EMailAddress();
 	}
+	if (elementHierarchy == "/vCard/TEL") {
+		currentTelephone_ = VCard::Telephone();
+	}
+	if (elementHierarchy == "/vCard/ADR") {
+		currentAddress_ = VCard::Address();
+	}
+	if (elementHierarchy == "/vCard/LABEL") {
+		currentAddressLabel_ = VCard::AddressLabel();
+	}
+	if (elementHierarchy == "/vCard/ORG") {
+		currentOrganization_ = VCard::Organization();
+	}
 	if (elementStack_.size() == 2) {
 		assert(!unknownContentParser_);
 		unknownContentParser_ = new SerializingParser();
@@ -90,9 +103,160 @@ void VCardParser::handleEndElement(const std::string& element, const std::string
 	else if (elementHierarchy == "/vCard/EMAIL/PREF") {
 		currentEMailAddress_.isPreferred = true;
 	}
-	else if (elementHierarchy == "/vCard/EMAIL") {
+	else if (elementHierarchy == "/vCard/EMAIL"  && !currentEMailAddress_.address.empty()) {
 		getPayloadInternal()->addEMailAddress(currentEMailAddress_);
 	}
+	else if (elementHierarchy == "/vCard/BDAY" && !currentText_.empty()) {
+		getPayloadInternal()->setBirthday(stringToDateTime(currentText_));
+	}
+	else if (elementHierarchy == "/vCard/TEL/NUMBER") {
+		currentTelephone_.number = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/TEL/HOME") {
+		currentTelephone_.isHome = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/WORK") {
+		currentTelephone_.isWork = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/VOICE") {
+		currentTelephone_.isVoice = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/FAX") {
+		currentTelephone_.isFax = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/PAGER") {
+		currentTelephone_.isPager = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/MSG") {
+		currentTelephone_.isMSG = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/CELL") {
+		currentTelephone_.isCell = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/VIDEO") {
+		currentTelephone_.isVideo = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/BBS") {
+		currentTelephone_.isBBS = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/MODEM") {
+		currentTelephone_.isModem = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/ISDN") {
+		currentTelephone_.isISDN = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/PCS") {
+		currentTelephone_.isPCS = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL/PREF") {
+		currentTelephone_.isPreferred = true;
+	}
+	else if (elementHierarchy == "/vCard/TEL" && !currentTelephone_.number.empty()) {
+		getPayloadInternal()->addTelephone(currentTelephone_);
+	}
+	else if (elementHierarchy == "/vCard/ADR/HOME") {
+		currentAddress_.isHome = true;
+	}
+	else if (elementHierarchy == "/vCard/ADR/WORK") {
+		currentAddress_.isWork = true;
+	}
+	else if (elementHierarchy == "/vCard/ADR/POSTAL") {
+		currentAddress_.isPostal = true;
+	}
+	else if (elementHierarchy == "/vCard/ADR/PARCEL") {
+		currentAddress_.isParcel = true;
+	}
+	else if (elementHierarchy == "/vCard/ADR/DOM") {
+		currentAddress_.deliveryType = VCard::DomesticDelivery;
+	}
+	else if (elementHierarchy == "/vCard/ADR/INTL") {
+		currentAddress_.deliveryType = VCard::InternationalDelivery;
+	}
+	else if (elementHierarchy == "/vCard/ADR/PREF") {
+		currentAddress_.isPreferred = true;
+	}
+	else if (elementHierarchy == "/vCard/ADR/POBOX") {
+		currentAddress_.poBox = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ADR/EXTADD") {
+		currentAddress_.addressExtension = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ADR/STREET") {
+		currentAddress_.street = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ADR/LOCALITY") {
+		currentAddress_.locality = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ADR/REGION") {
+		currentAddress_.region = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ADR/PCODE") {
+		currentAddress_.postalCode = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ADR/CTRY") {
+		currentAddress_.country = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ADR") {
+		if (!currentAddress_.poBox.empty() || !currentAddress_.addressExtension.empty() ||
+			!currentAddress_.street.empty() || !currentAddress_.locality.empty() ||
+			!currentAddress_.region.empty() || !currentAddress_.region.empty() ||
+			!currentAddress_.postalCode.empty() || !currentAddress_.country.empty()) {
+			getPayloadInternal()->addAddress(currentAddress_);
+		}
+	}
+	else if (elementHierarchy == "/vCard/LABEL/HOME") {
+		currentAddressLabel_.isHome = true;
+	}
+	else if (elementHierarchy == "/vCard/LABEL/WORK") {
+		currentAddressLabel_.isWork = true;
+	}
+	else if (elementHierarchy == "/vCard/LABEL/POSTAL") {
+		currentAddressLabel_.isPostal = true;
+	}
+	else if (elementHierarchy == "/vCard/LABEL/PARCEL") {
+		currentAddressLabel_.isParcel = true;
+	}
+	else if (elementHierarchy == "/vCard/LABEL/DOM") {
+		currentAddressLabel_.deliveryType = VCard::DomesticDelivery;
+	}
+	else if (elementHierarchy == "/vCard/LABEL/INTL") {
+		currentAddressLabel_.deliveryType = VCard::InternationalDelivery;
+	}
+	else if (elementHierarchy == "/vCard/LABEL/PREF") {
+		currentAddressLabel_.isPreferred = true;
+	}
+	else if (elementHierarchy == "/vCard/LABEL/LINE") {
+		currentAddressLabel_.lines.push_back(currentText_);
+	}
+	else if (elementHierarchy == "/vCard/LABEL") {
+		getPayloadInternal()->addAddressLabel(currentAddressLabel_);
+	}
+	else if (elementHierarchy == "/vCard/JID" && !currentText_.empty()) {
+		getPayloadInternal()->addJID(JID(currentText_));
+	}
+	else if (elementHierarchy == "/vCard/DESC") {
+		getPayloadInternal()->setDescription(currentText_);
+	}
+	else if (elementHierarchy == "/vCard/ORG/ORGNAME") {
+		currentOrganization_.name = currentText_;
+	}
+	else if (elementHierarchy == "/vCard/ORG/ORGUNIT" && !currentText_.empty()) {
+		currentOrganization_.units.push_back(currentText_);
+	}
+	else if (elementHierarchy == "/vCard/ORG") {
+		if (!currentOrganization_.name.empty() || !currentOrganization_.units.empty()) {
+			getPayloadInternal()->addOrganization(currentOrganization_);
+		}
+	}
+	else if (elementHierarchy == "/vCard/TITLE" && !currentText_.empty()) {
+		getPayloadInternal()->addTitle(currentText_);
+	}
+	else if (elementHierarchy == "/vCard/ROLE" && !currentText_.empty()) {
+		getPayloadInternal()->addRole(currentText_);
+	}
+	else if (elementHierarchy == "/vCard/URL" && !currentText_.empty()) {
+		getPayloadInternal()->addURL(currentText_);
+	}
 	else if (elementStack_.size() == 2 && unknownContentParser_) {
 		getPayloadInternal()->addUnknownContent(unknownContentParser_->getResult());
 	}
diff --git a/Swiften/Parser/PayloadParsers/VCardParser.h b/Swiften/Parser/PayloadParsers/VCardParser.h
index b1c47a3..f10d639 100644
--- a/Swiften/Parser/PayloadParsers/VCardParser.h
+++ b/Swiften/Parser/PayloadParsers/VCardParser.h
@@ -28,6 +28,10 @@ namespace Swift {
 		private:
 			std::vector<std::string> elementStack_;
 			VCard::EMailAddress currentEMailAddress_;
+			VCard::Telephone currentTelephone_;
+			VCard::Address currentAddress_;
+			VCard::AddressLabel currentAddressLabel_;
+			VCard::Organization currentOrganization_;
 			SerializingParser* unknownContentParser_;
 			std::string currentText_;
 	};
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp
index 3ac1d77..01c8e77 100644
--- a/Swiften/Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp
@@ -31,14 +31,15 @@ class VCardSerializerTest : public CppUnit::TestFixture
 			vcard->setNickname("DreamGirl");
 			vcard->setPhoto(createByteArray("abcdef"));
 			vcard->setPhotoType("image/png");
-			vcard->addUnknownContent("<BDAY>1234</BDAY><MAILER>mutt</MAILER>");
+			vcard->setBirthday(boost::posix_time::ptime(boost::gregorian::date(1865, 5, 4)));
+			vcard->addUnknownContent("<MAILER>mutt</MAILER>");
 
-			VCard::EMailAddress address1;
-			address1.address = "alice@wonderland.lit";
-			address1.isHome = true;
-			address1.isPreferred = true;
-			address1.isInternet = true;
-			vcard->addEMailAddress(address1);
+			VCard::EMailAddress emailAddress1;
+			emailAddress1.address = "alice@wonderland.lit";
+			emailAddress1.isHome = true;
+			emailAddress1.isPreferred = true;
+			emailAddress1.isInternet = true;
+			vcard->addEMailAddress(emailAddress1);
 
 			VCard::EMailAddress address2;
 			address2.address = "alice@teaparty.lit";
@@ -46,6 +47,41 @@ class VCardSerializerTest : public CppUnit::TestFixture
 			address2.isX400 = true;
 			vcard->addEMailAddress(address2);
 
+			VCard::Telephone telephone1;
+			telephone1.number = "555-6273";
+			telephone1.isHome = true;
+			telephone1.isVoice = true;
+			vcard->addTelephone(telephone1);
+
+			VCard::Address address1;
+			address1.locality = "Any Town";
+			address1.street = "Fake Street 123";
+			address1.postalCode = "12345";
+			address1.country = "USA";
+			address1.isHome = true;
+			vcard->addAddress(address1);
+
+			VCard::AddressLabel label1;
+			label1.lines.push_back("Fake Street 123");
+			label1.lines.push_back("12345 Any Town");
+			label1.lines.push_back("USA");
+			label1.isHome = true;
+			vcard->addAddressLabel(label1);
+
+			vcard->addJID(JID("alice@teaparty.lit"));
+			vcard->addJID(JID("alice@wonderland.lit"));
+
+			vcard->setDescription("I once fell down a rabbit hole.");
+
+			VCard::Organization org1;
+			org1.name = "Alice In Wonderland Inc.";
+			vcard->addOrganization(org1);
+
+			vcard->addTitle("Some Title");
+			vcard->addRole("Main Character");
+			vcard->addURL("http://wonderland.lit/~alice");
+			vcard->addURL("http://teaparty.lit/~alice2");
+
 			std::string expectedResult = 
 				"<vCard xmlns=\"vcard-temp\">"
 					"<VERSION>2.0</VERSION>"
@@ -73,7 +109,35 @@ class VCardSerializerTest : public CppUnit::TestFixture
 						"<TYPE>image/png</TYPE>"
 						"<BINVAL>YWJjZGVm</BINVAL>"
 					"</PHOTO>"
-					"<BDAY>1234</BDAY>"
+					"<BDAY>1865-05-04T00:00:00Z</BDAY>"
+					"<TEL>"
+						"<NUMBER>555-6273</NUMBER>"
+						"<HOME/>"
+						"<VOICE/>"
+					"</TEL>"
+					"<ADR>"
+						"<STREET>Fake Street 123</STREET>"
+						"<LOCALITY>Any Town</LOCALITY>"
+						"<PCODE>12345</PCODE>"
+						"<CTRY>USA</CTRY>"
+						"<HOME/>"
+					"</ADR>"
+					"<LABEL>"
+						"<LINE>Fake Street 123</LINE>"
+						"<LINE>12345 Any Town</LINE>"
+						"<LINE>USA</LINE>"
+						"<HOME/>"
+					"</LABEL>"
+					"<JID>alice@teaparty.lit</JID>"
+					"<JID>alice@wonderland.lit</JID>"
+					"<DESC>I once fell down a rabbit hole.</DESC>"
+					"<ORG>"
+						"<ORGNAME>Alice In Wonderland Inc.</ORGNAME>"
+					"</ORG>"
+					"<TITLE>Some Title</TITLE>"
+					"<ROLE>Main Character</ROLE>"
+					"<URL>http://wonderland.lit/~alice</URL>"
+					"<URL>http://teaparty.lit/~alice2</URL>"
 					"<MAILER>mutt</MAILER>"
 				"</vCard>";
 
diff --git a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
index 1512c6c..22d59b4 100644
--- a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
@@ -13,6 +13,7 @@
 #include <Swiften/Serializer/XML/XMLTextNode.h>
 #include <Swiften/Serializer/XML/XMLRawTextNode.h>
 #include <Swiften/StringCodecs/Base64.h>
+#include <Swiften/Base/DateTime.h>
 #include <Swiften/Base/foreach.h>
 
 namespace Swift {
@@ -23,49 +24,33 @@ VCardSerializer::VCardSerializer() : GenericPayloadSerializer<VCard>() {
 std::string VCardSerializer::serializePayload(boost::shared_ptr<VCard> vcard)  const {
 	XMLElement queryElement("vCard", "vcard-temp");
 	if (!vcard->getVersion().empty()) {
-		boost::shared_ptr<XMLElement> versionElement(new XMLElement("VERSION"));
-		versionElement->addNode(boost::make_shared<XMLTextNode>(vcard->getVersion()));
-		queryElement.addNode(versionElement);
+		queryElement.addNode(boost::make_shared<XMLElement>("VERSION", "", vcard->getVersion()));
 	}
 	if (!vcard->getFullName().empty()) {
-		boost::shared_ptr<XMLElement> fullNameElement(new XMLElement("FN"));
-		fullNameElement->addNode(boost::make_shared<XMLTextNode>(vcard->getFullName()));
-		queryElement.addNode(fullNameElement);
+		queryElement.addNode(boost::make_shared<XMLElement>("FN", "", vcard->getFullName()));
 	}
 	if (!vcard->getGivenName().empty() || !vcard->getFamilyName().empty() || !vcard->getMiddleName().empty() || !vcard->getPrefix().empty() || !vcard->getSuffix().empty()) {
 		boost::shared_ptr<XMLElement> nameElement(new XMLElement("N"));
 		if (!vcard->getFamilyName().empty()) {
-			boost::shared_ptr<XMLElement> familyNameElement(new XMLElement("FAMILY"));
-			familyNameElement->addNode(boost::make_shared<XMLTextNode>(vcard->getFamilyName()));
-			nameElement->addNode(familyNameElement);
+			nameElement->addNode(boost::make_shared<XMLElement>("FAMILY", "", vcard->getFamilyName()));
 		}
 		if (!vcard->getGivenName().empty()) {
-			boost::shared_ptr<XMLElement> givenNameElement(new XMLElement("GIVEN"));
-			givenNameElement->addNode(boost::make_shared<XMLTextNode>(vcard->getGivenName()));
-			nameElement->addNode(givenNameElement);
+			nameElement->addNode(boost::make_shared<XMLElement>("GIVEN", "", vcard->getGivenName()));
 		}
 		if (!vcard->getMiddleName().empty()) {
-			boost::shared_ptr<XMLElement> middleNameElement(new XMLElement("MIDDLE"));
-			middleNameElement->addNode(boost::make_shared<XMLTextNode>(vcard->getMiddleName()));
-			nameElement->addNode(middleNameElement);
+			nameElement->addNode(boost::make_shared<XMLElement>("MIDDLE", "", vcard->getMiddleName()));
 		}
 		if (!vcard->getPrefix().empty()) {
-			boost::shared_ptr<XMLElement> prefixElement(new XMLElement("PREFIX"));
-			prefixElement->addNode(boost::make_shared<XMLTextNode>(vcard->getPrefix()));
-			nameElement->addNode(prefixElement);
+			nameElement->addNode(boost::make_shared<XMLElement>("PREFIX", "", vcard->getPrefix()));
 		}
 		if (!vcard->getSuffix().empty()) {
-			boost::shared_ptr<XMLElement> suffixElement(new XMLElement("SUFFIX"));
-			suffixElement->addNode(boost::make_shared<XMLTextNode>(vcard->getSuffix()));
-			nameElement->addNode(suffixElement);
+			nameElement->addNode(boost::make_shared<XMLElement>("SUFFIX", "", vcard->getSuffix()));
 		}
 		queryElement.addNode(nameElement);
 	}
 	foreach(const VCard::EMailAddress& emailAddress, vcard->getEMailAddresses()) {
 		boost::shared_ptr<XMLElement> emailElement(new XMLElement("EMAIL"));
-		boost::shared_ptr<XMLElement> userIDElement(new XMLElement("USERID"));
-		userIDElement->addNode(boost::make_shared<XMLTextNode>(emailAddress.address));
-		emailElement->addNode(userIDElement);
+		emailElement->addNode(boost::make_shared<XMLElement>("USERID", "", emailAddress.address));
 		if (emailAddress.isHome) {
 			emailElement->addNode(boost::make_shared<XMLElement>("HOME"));
 		}
@@ -84,24 +69,179 @@ std::string VCardSerializer::serializePayload(boost::shared_ptr<VCard> vcard)  c
 		queryElement.addNode(emailElement);
 	}
 	if (!vcard->getNickname().empty()) {
-		boost::shared_ptr<XMLElement> nickElement(new XMLElement("NICKNAME"));
-		nickElement->addNode(boost::make_shared<XMLTextNode>(vcard->getNickname()));
-		queryElement.addNode(nickElement);
+		queryElement.addNode(boost::make_shared<XMLElement>("NICKNAME", "", vcard->getNickname()));
 	}
 	if (!vcard->getPhoto().empty() || !vcard->getPhotoType().empty()) {
 		XMLElement::ref photoElement(new XMLElement("PHOTO"));
 		if (!vcard->getPhotoType().empty()) {
-			XMLElement::ref typeElement(new XMLElement("TYPE"));
-			typeElement->addNode(XMLTextNode::ref(new XMLTextNode(vcard->getPhotoType())));
-			photoElement->addNode(typeElement);
+			photoElement->addNode(boost::make_shared<XMLElement>("TYPE", "", vcard->getPhotoType()));
 		}
 		if (!vcard->getPhoto().empty()) {
-			XMLElement::ref binvalElement(new XMLElement("BINVAL"));
-			binvalElement->addNode(XMLTextNode::ref(new XMLTextNode(Base64::encode(vcard->getPhoto()))));
-			photoElement->addNode(binvalElement);
+			photoElement->addNode(boost::make_shared<XMLElement>("BINVAL", "", Base64::encode(vcard->getPhoto())));
 		}
 		queryElement.addNode(photoElement);
 	}
+	if (!vcard->getBirthday().is_not_a_date_time()) {
+		queryElement.addNode(boost::make_shared<XMLElement>("BDAY", "", dateTimeToString(vcard->getBirthday())));
+	}
+
+	foreach(const VCard::Telephone& telephone, vcard->getTelephones()) {
+		boost::shared_ptr<XMLElement> telElement(new XMLElement("TEL"));
+		telElement->addNode(boost::make_shared<XMLElement>("NUMBER", "", telephone.number));
+		if (telephone.isHome) {
+			telElement->addNode(boost::make_shared<XMLElement>("HOME"));
+		}
+		if (telephone.isWork) {
+			telElement->addNode(boost::make_shared<XMLElement>("WORK"));
+		}
+		if (telephone.isVoice) {
+			telElement->addNode(boost::make_shared<XMLElement>("VOICE"));
+		}
+		if (telephone.isFax) {
+			telElement->addNode(boost::make_shared<XMLElement>("FAX"));
+		}
+		if (telephone.isPager) {
+			telElement->addNode(boost::make_shared<XMLElement>("PAGER"));
+		}
+		if (telephone.isMSG) {
+			telElement->addNode(boost::make_shared<XMLElement>("MSG"));
+		}
+		if (telephone.isCell) {
+			telElement->addNode(boost::make_shared<XMLElement>("CELL"));
+		}
+		if (telephone.isVideo) {
+			telElement->addNode(boost::make_shared<XMLElement>("VIDEO"));
+		}
+		if (telephone.isBBS) {
+			telElement->addNode(boost::make_shared<XMLElement>("BBS"));
+		}
+		if (telephone.isModem) {
+			telElement->addNode(boost::make_shared<XMLElement>("MODEM"));
+		}
+		if (telephone.isISDN) {
+			telElement->addNode(boost::make_shared<XMLElement>("ISDN"));
+		}
+		if (telephone.isPCS) {
+			telElement->addNode(boost::make_shared<XMLElement>("PCS"));
+		}
+		if (telephone.isPreferred) {
+			telElement->addNode(boost::make_shared<XMLElement>("PREF"));
+		}
+		queryElement.addNode(telElement);
+	}
+
+	foreach(const VCard::Address& address, vcard->getAddresses()) {
+		boost::shared_ptr<XMLElement> adrElement = boost::make_shared<XMLElement>("ADR");
+		if (!address.poBox.empty()) {
+			adrElement->addNode(boost::make_shared<XMLElement>("POBOX", "", address.poBox));
+		}
+		if (!address.addressExtension.empty()) {
+			adrElement->addNode(boost::make_shared<XMLElement>("EXTADD", "", address.addressExtension));
+		}
+		if (!address.street.empty()) {
+			adrElement->addNode(boost::make_shared<XMLElement>("STREET", "", address.street));
+		}
+		if (!address.locality.empty()) {
+			adrElement->addNode(boost::make_shared<XMLElement>("LOCALITY", "", address.locality));
+		}
+		if (!address.region.empty()) {
+			adrElement->addNode(boost::make_shared<XMLElement>("REGION", "", address.region));
+		}
+		if (!address.postalCode.empty()) {
+			adrElement->addNode(boost::make_shared<XMLElement>("PCODE", "", address.postalCode));
+		}
+		if (!address.country.empty()) {
+			adrElement->addNode(boost::make_shared<XMLElement>("CTRY", "", address.country));
+		}
+
+		if (address.isHome) {
+			adrElement->addNode(boost::make_shared<XMLElement>("HOME"));
+		}
+		if (address.isWork) {
+			adrElement->addNode(boost::make_shared<XMLElement>("WORK"));
+		}
+		if (address.isPostal) {
+			adrElement->addNode(boost::make_shared<XMLElement>("POSTAL"));
+		}
+		if (address.isParcel) {
+			adrElement->addNode(boost::make_shared<XMLElement>("PARCEL"));
+		}
+		if (address.deliveryType == VCard::DomesticDelivery) {
+			adrElement->addNode(boost::make_shared<XMLElement>("DOM"));
+		}
+		if (address.deliveryType == VCard::InternationalDelivery) {
+			adrElement->addNode(boost::make_shared<XMLElement>("INTL"));
+		}
+		if (address.isPreferred) {
+			adrElement->addNode(boost::make_shared<XMLElement>("PREF"));
+		}
+		queryElement.addNode(adrElement);
+	}
+
+	foreach(const VCard::AddressLabel& addressLabel, vcard->getAddressLabels()) {
+		boost::shared_ptr<XMLElement> labelElement = boost::make_shared<XMLElement>("LABEL");
+
+		foreach(const std::string& line, addressLabel.lines) {
+			labelElement->addNode(boost::make_shared<XMLElement>("LINE", "", line));
+		}
+
+		if (addressLabel.isHome) {
+			labelElement->addNode(boost::make_shared<XMLElement>("HOME"));
+		}
+		if (addressLabel.isWork) {
+			labelElement->addNode(boost::make_shared<XMLElement>("WORK"));
+		}
+		if (addressLabel.isPostal) {
+			labelElement->addNode(boost::make_shared<XMLElement>("POSTAL"));
+		}
+		if (addressLabel.isParcel) {
+			labelElement->addNode(boost::make_shared<XMLElement>("PARCEL"));
+		}
+		if (addressLabel.deliveryType == VCard::DomesticDelivery) {
+			labelElement->addNode(boost::make_shared<XMLElement>("DOM"));
+		}
+		if (addressLabel.deliveryType == VCard::InternationalDelivery) {
+			labelElement->addNode(boost::make_shared<XMLElement>("INTL"));
+		}
+		if (addressLabel.isPreferred) {
+			labelElement->addNode(boost::make_shared<XMLElement>("PREF"));
+		}
+		queryElement.addNode(labelElement);
+	}
+
+	foreach(const JID& jid, vcard->getJIDs()) {
+		queryElement.addNode(boost::make_shared<XMLElement>("JID", "", jid.toString()));
+	}
+
+	if (!vcard->getDescription().empty()) {
+		queryElement.addNode(boost::make_shared<XMLElement>("DESC", "", vcard->getDescription()));
+	}
+
+	foreach(const VCard::Organization& org, vcard->getOrganizations()) {
+		boost::shared_ptr<XMLElement> orgElement = boost::make_shared<XMLElement>("ORG");
+		if (!org.name.empty()) {
+			orgElement->addNode(boost::make_shared<XMLElement>("ORGNAME", "", org.name));
+		}
+		if (!org.units.empty()) {
+			foreach(const std::string& unit, org.units) {
+				orgElement->addNode(boost::make_shared<XMLElement>("ORGUNIT", "", unit));
+			}
+		}
+		queryElement.addNode(orgElement);
+	}
+
+	foreach(const std::string& title, vcard->getTitles()) {
+		queryElement.addNode(boost::make_shared<XMLElement>("TITLE", "", title));
+	}
+
+	foreach(const std::string& role, vcard->getRoles()) {
+		queryElement.addNode(boost::make_shared<XMLElement>("ROLE", "", role));
+	}
+
+	foreach(const std::string& url, vcard->getURLs()) {
+		queryElement.addNode(boost::make_shared<XMLElement>("URL", "", url));
+	}
+
 	if (!vcard->getUnknownContent().empty()) {
 		queryElement.addNode(boost::make_shared<XMLRawTextNode>(vcard->getUnknownContent()));
 	}
-- 
cgit v0.10.2-6-g49f6