diff options
71 files changed, 4452 insertions, 168 deletions
| 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.pngBinary files differ new file mode 100644 index 0000000..2908534 --- /dev/null +++ b/Swift/resources/icons/star-checked2.png 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.pngBinary files differ new file mode 100644 index 0000000..ee85d69 --- /dev/null +++ b/Swift/resources/icons/star-unchecked2.png 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()));  	} | 
 Swift
 Swift