From a7c7602e89d58056940885112f8764a31e9991da Mon Sep 17 00:00:00 2001
From: Richard Maudsley <richard.maudsley@isode.com>
Date: Mon, 7 Apr 2014 10:19:50 +0100
Subject: Allow contacts to be dragged from the Chats tab into the search
 window

Change-Id: Ib1ecd2f95fb26269d8aa19094aac6e1f691cdf35

diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp
index 3c7eb67..7844c1b 100644
--- a/Swift/Controllers/Chat/UserSearchController.cpp
+++ b/Swift/Controllers/Chat/UserSearchController.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -45,6 +45,7 @@ UserSearchController::~UserSearchController() {
 		window_->onFormRequested.disconnect(boost::bind(&UserSearchController::handleFormRequested, this, _1));
 		window_->onSearchRequested.disconnect(boost::bind(&UserSearchController::handleSearch, this, _1, _2));
 		window_->onJIDUpdateRequested.disconnect(boost::bind(&UserSearchController::handleJIDUpdateRequested, this, _1));
+		window_->onJIDAddRequested.disconnect(boost::bind(&UserSearchController::handleJIDAddRequested, this, _1));
 		delete window_;
 	}
 	presenceOracle_->onPresenceChange.disconnect(boost::bind(&UserSearchController::handlePresenceChanged, this, _1));
@@ -222,6 +223,14 @@ void UserSearchController::handleJIDUpdateRequested(const std::vector<JID>& jids
 	}
 }
 
+void UserSearchController::handleJIDAddRequested(const std::vector<JID>& jids) {
+	std::vector<Contact> contacts;
+	foreach(const JID& jid, jids) {
+		contacts.push_back(convertJIDtoContact(jid));
+	}
+	window_->addContacts(contacts);
+}
+
 Contact UserSearchController::convertJIDtoContact(const JID& jid) {
 	Contact contact;
 	contact.jid = jid;
@@ -278,6 +287,7 @@ void UserSearchController::initializeUserWindow() {
 		window_->onSearchRequested.connect(boost::bind(&UserSearchController::handleSearch, this, _1, _2));
 		window_->onContactSuggestionsRequested.connect(boost::bind(&UserSearchController::handleContactSuggestionsRequested, this, _1));
 		window_->onJIDUpdateRequested.connect(boost::bind(&UserSearchController::handleJIDUpdateRequested, this, _1));
+		window_->onJIDAddRequested.connect(boost::bind(&UserSearchController::handleJIDAddRequested, this, _1));
 		window_->setSelectedService(JID(jid_.getDomain()));
 		window_->clear();
 	}
diff --git a/Swift/Controllers/Chat/UserSearchController.h b/Swift/Controllers/Chat/UserSearchController.h
index 21cad5e..fc4c8e9 100644
--- a/Swift/Controllers/Chat/UserSearchController.h
+++ b/Swift/Controllers/Chat/UserSearchController.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -67,6 +67,7 @@ namespace Swift {
 			void handleAvatarChanged(const JID& jid);
 			void handlePresenceChanged(Presence::ref presence);
 			void handleJIDUpdateRequested(const std::vector<JID>& jids);
+			void handleJIDAddRequested(const std::vector<JID>& jids);
 			Contact convertJIDtoContact(const JID& jid);
 			void endDiscoWalker();
 			void initializeUserWindow();
diff --git a/Swift/Controllers/UIInterfaces/UserSearchWindow.h b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
index 9dd1811..0245f34 100644
--- a/Swift/Controllers/UIInterfaces/UserSearchWindow.h
+++ b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -39,6 +39,7 @@ namespace Swift {
 			virtual std::vector<JID> getJIDs() const = 0;
 			virtual void setCanStartImpromptuChats(bool supportsImpromptu) = 0;
 			virtual void updateContacts(const std::vector<Contact>& contacts) = 0;
+			virtual void addContacts(const std::vector<Contact>& contacts) = 0;
 
 			virtual void show() = 0;
 
@@ -47,5 +48,6 @@ namespace Swift {
 			boost::signal<void (const JID&)> onNameSuggestionRequested;
 			boost::signal<void (const std::string&)> onContactSuggestionsRequested;
 			boost::signal<void (const std::vector<JID>&)> onJIDUpdateRequested;
+			boost::signal<void (const std::vector<JID>&)> onJIDAddRequested;
 	};
 }
diff --git a/Swift/QtUI/ChatList/ChatListModel.cpp b/Swift/QtUI/ChatList/ChatListModel.cpp
index 7913c61..d09b0dd 100644
--- a/Swift/QtUI/ChatList/ChatListModel.cpp
+++ b/Swift/QtUI/ChatList/ChatListModel.cpp
@@ -1,14 +1,18 @@
 /*
- * Copyright (c) 2010-2011 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
 #include <Swift/QtUI/ChatList/ChatListModel.h>
 
+#include <QMimeData>
+#include <QUrl>
+
 #include <Swift/QtUI/ChatList/ChatListMUCItem.h>
 #include <Swift/QtUI/ChatList/ChatListRecentItem.h>
 #include <Swift/QtUI/ChatList/ChatListWhiteboardItem.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
 
 namespace Swift {
 
@@ -25,6 +29,14 @@ ChatListModel::ChatListModel() : whiteboards_(NULL) {
 	root_->addItem(mucBookmarks_);
 }
 
+Qt::ItemFlags ChatListModel::flags(const QModelIndex& index) const {
+	Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+	if (dynamic_cast<ChatListRecentItem*>(getItemForIndex(index))) {
+		flags |= Qt::ItemIsDragEnabled;
+	}
+	return flags;
+}
+
 void ChatListModel::clearBookmarks() {
 	emit layoutAboutToBeChanged();
 	mucBookmarks_->clear();
@@ -80,6 +92,34 @@ void ChatListModel::setRecents(const std::list<ChatListWindow::Chat>& recents) {
 	emit layoutChanged();
 }
 
+QMimeData* ChatListModel::mimeData(const QModelIndexList& indexes) const {
+	QMimeData* data = QAbstractItemModel::mimeData(indexes);
+	ChatListRecentItem *item = dynamic_cast<ChatListRecentItem*>(getItemForIndex(indexes.first()));
+	if (item == NULL) {
+		return data;
+	}
+
+	QByteArray itemData;
+	QDataStream dataStream(&itemData, QIODevice::WriteOnly);
+	const ChatListWindow::Chat& chat = item->getChat();
+
+	QString mimeType = "application/vnd.swift.contact-jid-list";
+	if (!chat.impromptuJIDs.size()) {
+		if (chat.isMUC) {
+			mimeType = "application/vnd.swift.contact-jid-muc";
+		}
+		dataStream << P2QSTRING(chat.jid.toString());
+	} else {
+		typedef std::map<std::string, JID> JIDMap;
+		foreach (const JIDMap::value_type& jid, chat.impromptuJIDs) {
+			dataStream << P2QSTRING(jid.second.toString());
+		}
+	}
+
+	data->setData(mimeType, itemData);
+	return data;
+}
+
 int ChatListModel::columnCount(const QModelIndex& /*parent*/) const {
 	return 1;
 }
diff --git a/Swift/QtUI/ChatList/ChatListModel.h b/Swift/QtUI/ChatList/ChatListModel.h
index 04e369a..a15cbcd 100644
--- a/Swift/QtUI/ChatList/ChatListModel.h
+++ b/Swift/QtUI/ChatList/ChatListModel.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -19,6 +19,7 @@ namespace Swift {
 		Q_OBJECT
 		public:
 			ChatListModel();
+			Qt::ItemFlags flags(const QModelIndex& index) const;
 			void addMUCBookmark(const MUCBookmark& bookmark);
 			void removeMUCBookmark(const MUCBookmark& bookmark);
 			void addWhiteboardSession(const ChatListWindow::Chat& chat);
@@ -31,6 +32,7 @@ namespace Swift {
 			ChatListItem* getItemForIndex(const QModelIndex& index) const;
 			void clearBookmarks();
 			void setRecents(const std::list<ChatListWindow::Chat>& recents);
+			QMimeData* mimeData(const QModelIndexList& indexes) const;
 		private:
 			ChatListGroupItem* mucBookmarks_;
 			ChatListGroupItem* recents_;
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp
index 4d1f19b..7455fb5 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.cpp
+++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -8,23 +8,24 @@
 
 #include <boost/bind.hpp>
 
-#include <QMenu>
 #include <QContextMenuEvent>
+#include <QMenu>
+#include <QMimeData>
+#include <QUrl>
 
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h>
+#include <Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h>
+#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
+#include <Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h>
 #include <Swift/QtUI/ChatList/ChatListMUCItem.h>
 #include <Swift/QtUI/ChatList/ChatListRecentItem.h>
 #include <Swift/QtUI/ChatList/ChatListWhiteboardItem.h>
 #include <Swift/QtUI/QtAddBookmarkWindow.h>
 #include <Swift/QtUI/QtEditBookmarkWindow.h>
 #include <Swift/QtUI/QtUISettingConstants.h>
-#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
-#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
-#include <Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h>
-#include <Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h>
-#include <Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h>
-#include <Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h>
-#include <Swift/Controllers/Settings/SettingsProvider.h>
-
 
 namespace Swift {
 
@@ -43,6 +44,7 @@ QtChatListWindow::QtChatListWindow(UIEventStream *uiEventStream, SettingsProvide
 	expandAll();
 	setAnimated(true);
 	setIndentation(0);
+	setDragEnabled(true);
 	setRootIsDecorated(true);
 	setupContextMenus();
 	connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&)));
@@ -152,6 +154,11 @@ void QtChatListWindow::handleEditBookmark() {
 	window->show();
 }
 
+void QtChatListWindow::dragEnterEvent(QDragEnterEvent *event) {
+	if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {
+		event->acceptProposedAction();
+	}
+}
 
 void QtChatListWindow::contextMenuEvent(QContextMenuEvent* event) {
 	QModelIndex index = indexAt(event->pos());
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h
index ef4ce0f..e218266 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.h
+++ b/Swift/QtUI/ChatList/QtChatListWindow.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -40,6 +40,7 @@ namespace Swift {
 			void handleSettingChanged(const std::string& setting);
 
 		protected:
+			void dragEnterEvent(QDragEnterEvent* event);
 			void contextMenuEvent(QContextMenuEvent* event);
 
 		private:
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index bd7c817..d3cce6d 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -31,6 +31,7 @@
 #include <QTime>
 #include <QToolButton>
 #include <QUrl>
+#include <QMimeData>
 
 #include <Swiften/Base/Log.h>
 
@@ -540,7 +541,7 @@ void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) {
 		if (!isMUC_) {
 			event->acceptProposedAction();
 		}
-	} else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid")) {
+	} else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) {
 		if (isMUC_ || supportsImpromptuChat_) {
 			event->acceptProposedAction();
 		}
@@ -557,16 +558,19 @@ void QtChatWindow::dropEvent(QDropEvent *event) {
 			message.append(boost::make_shared<ChatTextMessagePart>(messageText));
 			addSystemMessage(message, DefaultDirection);
 		}
-	} else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid")) {
-		QByteArray dataBytes = event->mimeData()->data("application/vnd.swift.contact-jid");
+	} else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) {
+		QByteArray dataBytes = event->mimeData()->data("application/vnd.swift.contact-jid-list");
 		QDataStream dataStream(&dataBytes, QIODevice::ReadOnly);
-		QString jidString;
-		dataStream >> jidString;
-		onInviteToChat(std::vector<JID>(1, JID(Q2PSTRING(jidString))));
+		std::vector<JID> invites;
+		while (!dataStream.atEnd()) {
+			QString jidString;
+			dataStream >> jidString;
+			invites.push_back(Q2PSTRING(jidString));
+		}
+		onInviteToChat(invites);
 	}
 }
 
-
 void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
 	treeWidget_->setAvailableOccupantActions(actions);
 }
diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp
index 2bd0d09..d8108ba 100644
--- a/Swift/QtUI/Roster/RosterModel.cpp
+++ b/Swift/QtUI/Roster/RosterModel.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -263,16 +263,11 @@ QMimeData* RosterModel::mimeData(const QModelIndexList& indexes) const {
 		return data;
 	}
 
+	/* only a single JID in this list */
 	QByteArray itemData;
 	QDataStream dataStream(&itemData, QIODevice::WriteOnly);
-
-	// jid, chatName, activity, statusType, avatarPath
 	dataStream << P2QSTRING(item->getJID().toString());
-	dataStream << P2QSTRING(item->getDisplayName());
-	dataStream << P2QSTRING(item->getStatusText());
-	dataStream << item->getSimplifiedStatusShow();
-	dataStream << P2QSTRING(item->getAvatarPath().string());
-	data->setData("application/vnd.swift.contact-jid", itemData);
+	data->setData("application/vnd.swift.contact-jid-list", itemData);
 	return data;
 }
 
diff --git a/Swift/QtUI/UserSearch/ContactListModel.cpp b/Swift/QtUI/UserSearch/ContactListModel.cpp
index 4c4a3ea..907142f 100644
--- a/Swift/QtUI/UserSearch/ContactListModel.cpp
+++ b/Swift/QtUI/UserSearch/ContactListModel.cpp
@@ -4,6 +4,12 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
+/*
+ * Copyright (c) 2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
 #include <Swift/QtUI/UserSearch/ContactListModel.h>
 
 #include <QMimeData>
@@ -82,43 +88,6 @@ QVariant ContactListModel::data(const QModelIndex& index, int role) const {
 	}
 }
 
-bool ContactListModel::dropMimeData(const QMimeData* data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex& /*parent*/) {
-	if (!data->hasFormat("application/vnd.swift.contact-jid")) {
-		return false;
-	}
-
-	QByteArray dataBytes = data->data("application/vnd.swift.contact-jid");
-	QDataStream dataStream(&dataBytes, QIODevice::ReadOnly);
-	QString jidString;
-	QString displayName;
-	QString statusText;
-	StatusShow::Type statusType;
-	QString avatarPath;
-
-	dataStream >> jidString;
-	dataStream >> displayName;
-	dataStream >> statusText;
-	dataStream >> statusType;
-	dataStream >> avatarPath;
-
-	JID jid = JID(Q2PSTRING(jidString));
-
-	foreach(const Contact& contact, contacts_) {
-		if (contact.jid == jid) {
-			return false;
-		}
-	}
-
-	emit layoutAboutToBeChanged();
-	contacts_.push_back(Contact(Q2PSTRING(displayName), jid, statusType, Q2PSTRING(avatarPath)));
-	emit layoutChanged();
-
-	onJIDsDropped(std::vector<JID>(1, jid));
-	onListChanged(getList());
-
-	return true;
-}
-
 QModelIndex ContactListModel::index(int row, int column, const QModelIndex& parent) const {
 	if (!hasIndex(row, column, parent)) {
 		return QModelIndex();
diff --git a/Swift/QtUI/UserSearch/ContactListModel.h b/Swift/QtUI/UserSearch/ContactListModel.h
index e7f4a0b..6ca505e 100644
--- a/Swift/QtUI/UserSearch/ContactListModel.h
+++ b/Swift/QtUI/UserSearch/ContactListModel.h
@@ -4,6 +4,12 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
+/*
+ * Copyright (c) 2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
 #pragma once
 
 #include <vector>
@@ -36,7 +42,6 @@ namespace Swift {
 			Qt::ItemFlags flags(const QModelIndex& index) const;
 			int columnCount(const QModelIndex& parent = QModelIndex()) const;
 			QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
-			bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
 			QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
 			QModelIndex parent(const QModelIndex& index) const;
 			int rowCount(const QModelIndex& parent = QModelIndex()) const;
diff --git a/Swift/QtUI/UserSearch/QtContactListWidget.cpp b/Swift/QtUI/UserSearch/QtContactListWidget.cpp
index 90adc11..6ad1169 100644
--- a/Swift/QtUI/UserSearch/QtContactListWidget.cpp
+++ b/Swift/QtUI/UserSearch/QtContactListWidget.cpp
@@ -4,6 +4,12 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
+/*
+ * Copyright (c) 2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
 #include <Swift/QtUI/UserSearch/QtContactListWidget.h>
 
 #include <Swift/QtUI/UserSearch/ContactListModel.h>
@@ -20,15 +26,11 @@ QtContactListWidget::QtContactListWidget(QWidget* parent, SettingsProvider* sett
 	contactListModel_ = new ContactListModel(true);
 	setModel(contactListModel_);
 
-	connect(contactListModel_, SIGNAL(onListChanged(std::vector<Contact>)), this, SLOT(handleListChanged(std::vector<Contact>)));
 	connect(contactListModel_, SIGNAL(onListChanged(std::vector<Contact>)), this, SIGNAL(onListChanged(std::vector<Contact>)));
 	connect(contactListModel_, SIGNAL(onJIDsDropped(std::vector<JID>)), this, SIGNAL(onJIDsAdded(std::vector<JID>)));
 
 	setSelectionMode(QAbstractItemView::SingleSelection);
 	setSelectionBehavior(QAbstractItemView::SelectRows);
-	setDragEnabled(true);
-	setAcceptDrops(true);
-	setDropIndicatorShown(true);
 	setUniformRowHeights(true);
 
 	setAlternatingRowColors(true);
@@ -69,12 +71,6 @@ std::vector<Contact> QtContactListWidget::getList() const {
 
 void QtContactListWidget::setMaximumNoOfContactsToOne(bool limited) {
 	limited_ = limited;
-	if (limited) {
-		handleListChanged(getList());
-	} else {
-		setAcceptDrops(true);
-		setDropIndicatorShown(true);
-	}
 }
 
 void QtContactListWidget::updateContacts(const std::vector<Contact>& contactUpdates) {
@@ -90,13 +86,6 @@ void QtContactListWidget::updateContacts(const std::vector<Contact>& contactUpda
 	contactListModel_->setList(contacts);
 }
 
-void QtContactListWidget::handleListChanged(std::vector<Contact> list) {
-	if (limited_) {
-		setAcceptDrops(list.size() <= 1);
-		setDropIndicatorShown(list.size() <= 1);
-	}
-}
-
 void QtContactListWidget::handleSettingsChanged(const std::string&) {
 	contactListDelegate_->setCompact(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER));
 }
diff --git a/Swift/QtUI/UserSearch/QtContactListWidget.h b/Swift/QtUI/UserSearch/QtContactListWidget.h
index f360a91..a83b47a 100644
--- a/Swift/QtUI/UserSearch/QtContactListWidget.h
+++ b/Swift/QtUI/UserSearch/QtContactListWidget.h
@@ -4,6 +4,12 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
+/*
+ * Copyright (c) 2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
 #pragma once
 
 #include <vector>
@@ -41,9 +47,6 @@ signals:
 	void onListChanged(std::vector<Contact> list);
 	void onJIDsAdded(const std::vector<JID>& jids);
 
-private slots:
-	void handleListChanged(std::vector<Contact> list);
-
 private:
 	void handleSettingsChanged(const std::string&);
 
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp
index b1e9a12..360ce0a 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp
@@ -4,11 +4,21 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
+/*
+ * Copyright (c) 2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
 #include "Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h"
 
+#include <QMessageBox>
+#include <QMimeData>
+#include <QUrl>
+
 #include "Swift/QtUI/QtSwiftUtil.h"
-#include <Swift/QtUI/UserSearch/QtContactListWidget.h>
 #include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/QtUI/UserSearch/QtContactListWidget.h>
 #include <Swift/QtUI/UserSearch/QtSuggestingJIDInput.h>
 
 namespace Swift {
@@ -39,6 +49,8 @@ QtUserSearchFirstMultiJIDPage::QtUserSearchFirstMultiJIDPage(UserSearchWindow::T
 
 	connect(contactList_, SIGNAL(onListChanged(std::vector<Contact>)), this, SLOT(emitCompletenessCheck()));
 	connect(jid_, SIGNAL(editingDone()), this, SLOT(handleEditingDone()));
+
+	setAcceptDrops(true);
 }
 
 bool QtUserSearchFirstMultiJIDPage::isComplete() const {
@@ -53,4 +65,30 @@ void QtUserSearchFirstMultiJIDPage::handleEditingDone() {
 	addContactButton_->setFocus();
 }
 
+void QtUserSearchFirstMultiJIDPage::dragEnterEvent(QDragEnterEvent *event) {
+	if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")
+		|| event->mimeData()->hasFormat("application/vnd.swift.contact-jid-muc")) {
+			event->acceptProposedAction();
+	}
+}
+
+void QtUserSearchFirstMultiJIDPage::dropEvent(QDropEvent *event) {
+	if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) {
+		QByteArray dataBytes = event->mimeData()->data("application/vnd.swift.contact-jid-list");
+		QDataStream dataStream(&dataBytes, QIODevice::ReadOnly);
+		std::vector<JID> jids;
+		while (!dataStream.atEnd()) {
+			QString jidString;
+			dataStream >> jidString;
+			jids.push_back(Q2PSTRING(jidString));
+		}
+		onJIDsDropped(jids);
+	} else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-muc")) {
+		QMessageBox* messageBox = new QMessageBox(this);
+		messageBox->setText(tr("You can't invite a room to chat."));
+		messageBox->setWindowTitle(tr("Error inviting room to chat"));
+		messageBox->show();
+	}
+}
+
 }
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h
index 427995e..9905f21 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h
@@ -4,6 +4,12 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
+/*
+ * Copyright (c) 2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
 #pragma once
 
 #include <QWizardPage>
@@ -29,9 +35,14 @@ namespace Swift {
 			QtUserSearchFirstMultiJIDPage(UserSearchWindow::Type type, const QString& title, SettingsProvider* settings);
 			virtual bool isComplete() const;
 
+		signals:
+			void onJIDsDropped(std::vector<JID> jid);
+
 		public slots:
 			void emitCompletenessCheck();
 			void handleEditingDone();
+			virtual void dragEnterEvent(QDragEnterEvent *event);
+			virtual void dropEvent(QDropEvent *event);
 
 		public:
 			QtContactListWidget* contactList_;
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index c0c7972..ec5dd39 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -377,6 +377,26 @@ void QtUserSearchWindow::updateContacts(const std::vector<Contact>& contacts) {
 	}
 }
 
+void QtUserSearchWindow::addContacts(const std::vector<Contact>& contacts) {
+	if (type_ != AddContact) {
+		/* prevent duplicate JIDs from appearing in the contact list */
+		foreach (const Contact& newContact, contacts) {
+			bool found = false;
+			foreach (const Contact& oldContact, contactVector_) {
+				if (newContact.jid == oldContact.jid) {
+					found = true;
+					break;
+				}
+			}
+			if (!found) {
+				contactVector_.push_back(newContact);
+			}
+		}
+		firstMultiJIDPage_->contactList_->setList(contactVector_);
+		firstMultiJIDPage_->emitCompletenessCheck();
+	}
+}
+
 void QtUserSearchWindow::handleAddViaSearch() {
 	searchNext_ = true;
 	next();
@@ -390,7 +410,7 @@ void QtUserSearchWindow::handleListChanged(std::vector<Contact> list) {
 }
 
 void QtUserSearchWindow::handleJIDsAdded(std::vector<JID> jids) {
-	onJIDUpdateRequested(jids);
+	onJIDAddRequested(jids);
 }
 
 void QtUserSearchWindow::setResults(const std::vector<UserSearchResult>& results) {
@@ -449,6 +469,7 @@ void QtUserSearchWindow::setFirstPage(QString title) {
 		connect(firstMultiJIDPage_->addViaSearchButton_, SIGNAL(clicked()), this, SLOT(handleAddViaSearch()));
 		connect(firstMultiJIDPage_->contactList_, SIGNAL(onListChanged(std::vector<Contact>)), this, SLOT(handleListChanged(std::vector<Contact>)));
 		connect(firstMultiJIDPage_->contactList_, SIGNAL(onJIDsAdded(std::vector<JID>)), this, SLOT(handleJIDsAdded(std::vector<JID>)));
+		connect(firstMultiJIDPage_, SIGNAL(onJIDsDropped(std::vector<JID>)), this, SLOT(handleJIDsAdded(std::vector<JID>)));
 		setPage(1, firstMultiJIDPage_);
 	}
 }
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
index e5a9f80..941e455 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -50,6 +50,7 @@ namespace Swift {
 			virtual std::vector<JID> getJIDs() const;
 			virtual void setCanStartImpromptuChats(bool supportsImpromptu);
 			virtual void updateContacts(const std::vector<Contact> &contacts);
+			virtual void addContacts(const std::vector<Contact>& contacts);
 
 		protected:
 			virtual int nextId() const;
-- 
cgit v0.10.2-6-g49f6