From 50a3962e4d0b16fd0316be54121cfb293c3117bd Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Thu, 1 Oct 2015 11:09:04 +0100
Subject: Ignore DND drops of JIDs for contacts already in the conversation

Swift allows dropping contacts from group chats or the roster
on a chat window to invite them or start an impromptu chat.
With this commit Swift will ignore drops which only contain
JIDs that are already part of the conversation.

Test-Information:

Tested the described behavior with roster and room contact
drags in anonymous and non-anonymous rooms.

Change-Id: I2f06082ea4bc1140210f9f1a165bdf276a130273

diff --git a/Swift/Controllers/Roster/Roster.cpp b/Swift/Controllers/Roster/Roster.cpp
index 84561e5..77d6b78 100644
--- a/Swift/Controllers/Roster/Roster.cpp
+++ b/Swift/Controllers/Roster/Roster.cpp
@@ -51,6 +51,28 @@ GroupRosterItem* Roster::getRoot() const {
 	return root_;
 }
 
+std::set<JID> Roster::getJIDs() const {
+	std::set<JID> jids;
+
+	std::deque<RosterItem*> queue;
+	queue.push_back(root_);
+	while (!queue.empty()) {
+		RosterItem* item = *queue.begin();
+		queue.pop_front();
+		GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item);
+		ContactRosterItem *contact = dynamic_cast<ContactRosterItem*>(item);
+		if (contact) {
+			jids.insert(contact->getJID());
+			jids.insert(contact->getDisplayJID());
+		}
+		else if (group) {
+			queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end());
+		}
+	}
+
+	return jids;
+}
+
 GroupRosterItem* Roster::getGroup(const std::string& groupName) {
 	foreach (RosterItem *item, root_->getChildren()) {
 		GroupRosterItem *group = dynamic_cast<GroupRosterItem*>(item);
diff --git a/Swift/Controllers/Roster/Roster.h b/Swift/Controllers/Roster/Roster.h
index c25feaa..269ec4d 100644
--- a/Swift/Controllers/Roster/Roster.h
+++ b/Swift/Controllers/Roster/Roster.h
@@ -42,6 +42,8 @@ class Roster {
 		void addFilter(RosterFilter* filter);
 		void removeFilter(RosterFilter* filter);
 		GroupRosterItem* getRoot() const;
+		std::set<JID> getJIDs() const;
+
 		std::vector<RosterFilter*> getFilters() {return filters_;}
 		boost::signal<void (GroupRosterItem*)> onChildrenChanged;
 		boost::signal<void (GroupRosterItem*)> onGroupAdded;
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index d58393b..abbfae5 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -597,7 +597,29 @@ void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) {
 		}
 		else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) {
 			if (isMUC_ || supportsImpromptuChat_) {
-				event->acceptProposedAction();
+				// Prevent invitations or impromptu initializations for contacts that you are already chatting to.
+				std::vector<JID> droppedJIDs =jidListFromQByteArray(event->mimeData()->data("application/vnd.swift.contact-jid-list"));
+				std::set<JID> conversationJIDs;
+				if (isMUC_) {
+					conversationJIDs = treeWidget_->getRoster()->getJIDs();
+				}
+
+				for (std::vector<JID>::iterator i = droppedJIDs.begin(); i != droppedJIDs.end(); ) {
+					const JID& droppedJID = *i;
+					if (conversationJIDs.find(droppedJID) != conversationJIDs.end()) {
+						i = droppedJIDs.erase(i);
+					}
+					else {
+						++i;
+					}
+				}
+
+				if (droppedJIDs.empty()) {
+					event->ignore();
+				}
+				else {
+					event->acceptProposedAction();
+				}
 			}
 		}
 	}
@@ -616,18 +638,22 @@ void QtChatWindow::dropEvent(QDropEvent *event) {
 		}
 	}
 	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);
-		std::vector<JID> invites;
-		while (!dataStream.atEnd()) {
-			QString jidString;
-			dataStream >> jidString;
-			invites.push_back(Q2PSTRING(jidString));
-		}
+		std::vector<JID> invites = jidListFromQByteArray(event->mimeData()->data("application/vnd.swift.contact-jid-list"));
 		onInviteToChat(invites);
 	}
 }
 
+std::vector<JID> QtChatWindow::jidListFromQByteArray(const QByteArray& dataBytes) {
+	QDataStream dataStream(dataBytes);
+	std::vector<JID> invites;
+	while (!dataStream.atEnd()) {
+		QString jidString;
+		dataStream >> jidString;
+		invites.push_back(Q2PSTRING(jidString));
+	}
+	return invites;
+}
+
 void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
 	treeWidget_->setAvailableOccupantActions(actions);
 }
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 8d7db59..bbcdee7 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -185,6 +185,8 @@ namespace Swift {
 			void handleOccupantSelectionChanged(RosterItem* item);
 			void handleAppendedToLog();
 
+			static std::vector<JID> jidListFromQByteArray(const QByteArray& dataBytes);
+
 		private:
 			int unreadCount_;
 			bool contactIsTyping_;
-- 
cgit v0.10.2-6-g49f6