summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2013-04-30 20:10:12 (GMT)
committerSwift Review <review@swift.im>2013-10-01 14:48:44 (GMT)
commitfd47dd7d5cec5155b9985959d2f0e0f3b386cd98 (patch)
treed4da57ce3f1c90f56701cab8757d75e089473c9e /Swift/QtUI
parenta3781c5b770c03ff32c5cf8f1280b21c2a8e2626 (diff)
downloadswift-fd47dd7d5cec5155b9985959d2f0e0f3b386cd98.zip
swift-fd47dd7d5cec5155b9985959d2f0e0f3b386cd98.tar.bz2
Adding support for impromptu MUCs.
Change-Id: I363e9d740bbec311454827645f4ea6df8bb60bed License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.
Diffstat (limited to 'Swift/QtUI')
-rw-r--r--Swift/QtUI/ChatList/ChatListModel.h2
-rw-r--r--Swift/QtUI/ChatList/ChatListRecentItem.cpp2
-rw-r--r--Swift/QtUI/ChatList/QtChatListWindow.cpp2
-rw-r--r--Swift/QtUI/QtChatTabs.cpp5
-rw-r--r--Swift/QtUI/QtChatWindow.cpp93
-rw-r--r--Swift/QtUI/QtChatWindow.h14
-rw-r--r--Swift/QtUI/QtChatWindowJSBridge.h2
-rw-r--r--Swift/QtUI/QtInviteToChatWindow.cpp129
-rw-r--r--Swift/QtUI/QtInviteToChatWindow.h44
-rw-r--r--Swift/QtUI/QtMainWindow.cpp2
-rw-r--r--Swift/QtUI/QtUIFactory.cpp2
-rw-r--r--Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp4
-rw-r--r--Swift/QtUI/Roster/QtTreeWidget.cpp3
-rw-r--r--Swift/QtUI/Roster/RosterModel.cpp32
-rw-r--r--Swift/QtUI/Roster/RosterModel.h3
-rw-r--r--Swift/QtUI/SConscript7
-rw-r--r--Swift/QtUI/UserSearch/ContactListDelegate.cpp46
-rw-r--r--Swift/QtUI/UserSearch/ContactListDelegate.h30
-rw-r--r--Swift/QtUI/UserSearch/ContactListModel.cpp173
-rw-r--r--Swift/QtUI/UserSearch/ContactListModel.h58
-rw-r--r--Swift/QtUI/UserSearch/QtContactListWidget.cpp105
-rw-r--r--Swift/QtUI/UserSearch/QtContactListWidget.h58
-rw-r--r--Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp182
-rw-r--r--Swift/QtUI/UserSearch/QtSuggestingJIDInput.h57
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp56
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h40
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.ui222
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFirstPage.cpp9
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFirstPage.h6
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui12
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchWindow.cpp354
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchWindow.h30
32 files changed, 1485 insertions, 299 deletions
diff --git a/Swift/QtUI/ChatList/ChatListModel.h b/Swift/QtUI/ChatList/ChatListModel.h
index e384a04..04e369a 100644
--- a/Swift/QtUI/ChatList/ChatListModel.h
+++ b/Swift/QtUI/ChatList/ChatListModel.h
@@ -6,8 +6,6 @@
#pragma once
-#include <boost/shared_ptr.hpp>
-
#include <QAbstractItemModel>
#include <QList>
diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.cpp b/Swift/QtUI/ChatList/ChatListRecentItem.cpp
index 5a4d1e1..e9ecec8 100644
--- a/Swift/QtUI/ChatList/ChatListRecentItem.cpp
+++ b/Swift/QtUI/ChatList/ChatListRecentItem.cpp
@@ -20,7 +20,7 @@ const ChatListWindow::Chat& ChatListRecentItem::getChat() const {
QVariant ChatListRecentItem::data(int role) const {
switch (role) {
- case Qt::DisplayRole: return P2QSTRING(chat_.chatName);
+ case Qt::DisplayRole: return chat_.impromptuJIDs.empty() ? P2QSTRING(chat_.chatName) : P2QSTRING(chat_.getImpromptuTitle());
case DetailTextRole: return P2QSTRING(chat_.activity);
/*case Qt::TextColorRole: return textColor_;
case Qt::BackgroundColorRole: return backgroundColor_;
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp
index 9692c9c..4d1f19b 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.cpp
+++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp
@@ -30,7 +30,7 @@ namespace Swift {
QtChatListWindow::QtChatListWindow(UIEventStream *uiEventStream, SettingsProvider* settings, QWidget* parent) : QTreeView(parent) {
eventStream_ = uiEventStream;
- settings_ = settings;;
+ settings_ = settings;
bookmarksEnabled_ = false;
model_ = new ChatListModel();
setModel(model_);
diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp
index a119043..de1ee7c 100644
--- a/Swift/QtUI/QtChatTabs.cpp
+++ b/Swift/QtUI/QtChatTabs.cpp
@@ -181,10 +181,9 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {
}
QString tabText = tabbable->windowTitle().simplified();
-
// look for spectrum-generated and other long JID localparts, and
// try to abbreviate the resulting long tab texts
- QRegExp hasTrailingGarbage("^(.[-\\w\\s&]+)([^\\s\\w].*)$");
+ QRegExp hasTrailingGarbage("^(.[-\\w\\s,&]+)([^\\s\\,w].*)$");
if (hasTrailingGarbage.exactMatch(tabText) &&
hasTrailingGarbage.cap(1).simplified().length() >= 2 &&
hasTrailingGarbage.cap(2).length() >= 7) {
@@ -193,10 +192,8 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {
// least a couple of characters.
tabText = hasTrailingGarbage.cap(1).simplified();
}
-
// QTabBar interprets &, so escape that
tabText.replace("&", "&&");
-
// see which alt[a-z] keys other tabs use
bool accelsTaken[26];
int i = 0;
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index d81de61..2dfef5a 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -16,7 +16,6 @@
#include "QtTextEdit.h"
#include "QtSettingsProvider.h"
#include "QtScaledAvatarCache.h"
-#include "QtInviteToChatWindow.h"
#include <Swift/QtUI/QtUISettingConstants.h>
#include <Swiften/StringCodecs/Base64.h>
@@ -68,7 +67,7 @@ const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetrans
const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite");
-QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), blockingState_(BlockingUnsupported) {
+QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false) {
settings_ = settings;
unreadCount_ = 0;
idCounter_ = 0;
@@ -189,11 +188,10 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
jsBridge = new QtChatWindowJSBridge();
messageLog_->addToJSEnvironment("chatwindow", jsBridge);
- connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString)));
+ connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString,QString,QString)));
settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1));
showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
-
}
QtChatWindow::~QtChatWindow() {
@@ -425,10 +423,11 @@ void QtChatWindow::closeEvent(QCloseEvent* event) {
onClosed();
}
-void QtChatWindow::convertToMUC() {
- setAcceptDrops(false);
+void QtChatWindow::convertToMUC(bool impromptuMUC) {
+ impromptu_ = impromptuMUC;
+ isMUC_ = true;
treeWidget_->show();
- subject_->show();
+ subject_->setVisible(!impromptu_);
}
void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
@@ -680,10 +679,10 @@ static QString decodeButtonArgument(const QString& str) {
return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str))));
}
-QString QtChatWindow::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3) {
+QString QtChatWindow::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3, const QString& arg4, const QString& arg5) {
QRegExp regex("[A-Za-z][A-Za-z0-9\\-\\_]+");
Q_ASSERT(regex.exactMatch(id));
- QString html = QString("<input id='%2' type='submit' value='%1' onclick='chatwindow.buttonClicked(\"%2\", \"%3\", \"%4\", \"%5\");' />").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3));
+ QString html = QString("<input id='%2' type='submit' value='%1' onclick='chatwindow.buttonClicked(\"%2\", \"%3\", \"%4\", \"%5\", \"%6\", \"%7\");' />").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3)).arg(encodeButtonArgument(arg4)).arg(encodeButtonArgument(arg5));
return html;
}
@@ -776,10 +775,12 @@ void QtChatWindow::setWhiteboardSessionStatus(std::string id, const ChatWindow::
messageLog_->setWhiteboardSessionStatus(P2QSTRING(id), state);
}
-void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) {
+void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3, QString encodedArgument4, QString encodedArgument5) {
QString arg1 = decodeButtonArgument(encodedArgument1);
QString arg2 = decodeButtonArgument(encodedArgument2);
QString arg3 = decodeButtonArgument(encodedArgument3);
+ QString arg4 = decodeButtonArgument(encodedArgument4);
+ QString arg5 = decodeButtonArgument(encodedArgument5);
if (id.startsWith(ButtonFileTransferCancel)) {
QString ft_id = arg1;
@@ -826,8 +827,9 @@ void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1,
QString roomJID = arg1;
QString password = arg2;
QString elementID = arg3;
-
- eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password)));
+ QString isImpromptu = arg4;
+ QString isContinuation = arg5;
+ eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password), boost::optional<std::string>(), false, false, isImpromptu.contains("true"), isContinuation.contains("true")));
messageLog_->setMUCInvitationJoined(elementID);
}
else {
@@ -957,18 +959,32 @@ void QtChatWindow::moveEvent(QMoveEvent*) {
void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) {
if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {
// TODO: check whether contact actually supports file transfer
- event->acceptProposedAction();
+ if (!isMUC_) {
+ event->acceptProposedAction();
+ }
+ } else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid")) {
+ if (isMUC_ || supportsImpromptuChat_) {
+ event->acceptProposedAction();
+ }
}
}
void QtChatWindow::dropEvent(QDropEvent *event) {
- if (event->mimeData()->urls().size() == 1) {
- onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile()));
- } else {
- std::string messageText(Q2PSTRING(tr("Sending of multiple files at once isn't supported at this time.")));
- ChatMessage message;
- message.append(boost::make_shared<ChatTextMessagePart>(messageText));
- addSystemMessage(message, DefaultDirection);
+ if (event->mimeData()->hasUrls()) {
+ if (event->mimeData()->urls().size() == 1) {
+ onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile()));
+ } else {
+ std::string messageText(Q2PSTRING(tr("Sending of multiple files at once isn't supported at this time.")));
+ ChatMessage message;
+ 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");
+ QDataStream dataStream(&dataBytes, QIODevice::ReadOnly);
+ QString jidString;
+ dataStream >> jidString;
+ onInviteToChat(std::vector<JID>(1, JID(Q2PSTRING(jidString))));
}
}
@@ -1004,9 +1020,23 @@ void QtChatWindow::handleActionButtonClicked() {
} else if (blockingState_ == IsUnblocked) {
block = contextMenu.addAction(tr("Block"));
}
+
+ if (supportsImpromptuChat_) {
+ invite = contextMenu.addAction(tr("Invite person to this chat…"));
+ }
+
} else {
foreach(ChatWindow::RoomAction availableAction, availableRoomActions_)
{
+ if (impromptu_) {
+ // hide options we don't need in impromptu chats
+ if (availableAction == ChatWindow::ChangeSubject ||
+ availableAction == ChatWindow::Configure ||
+ availableAction == ChatWindow::Affiliations ||
+ availableAction == ChatWindow::Destroy) {
+ continue;
+ }
+ }
switch(availableAction)
{
case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break;
@@ -1052,7 +1082,7 @@ void QtChatWindow::handleActionButtonClicked() {
}
}
else if (result == invite) {
- onInvitePersonToThisMUCRequest();
+ onInviteToChat(std::vector<JID>());
}
else if (result == block) {
onBlockUserRequest();
@@ -1079,6 +1109,10 @@ void QtChatWindow::setBlockingState(BlockingState state) {
blockingState_ = state;
}
+void QtChatWindow::setCanInitiateImpromptuChats(bool supportsImpromptu) {
+ supportsImpromptuChat_ = supportsImpromptu;
+}
+
void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
if (mucConfigurationWindow_) {
delete mucConfigurationWindow_.data();
@@ -1088,12 +1122,17 @@ void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
mucConfigurationWindow_->onFormCancelled.connect(boost::bind(boost::ref(onConfigurationFormCancelled)));
}
-void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) {
+void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct, bool isImpromptu, bool isContinuation) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
- QString message = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "\n";
+ QString message;
+ if (isImpromptu) {
+ message = QObject::tr("You've been invited to join a chat.") + "\n";
+ } else {
+ message = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "\n";
+ }
QString htmlString = message;
if (!reason.empty()) {
htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "\n";
@@ -1106,7 +1145,7 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji
QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
htmlString += "<div id='" + id + "'>" +
- buildChatWindowButton(chatMessageToHTML(ChatMessage(Q2PSTRING((tr("Accept Invite"))))), ButtonMUCInvite, QtUtilities::htmlEscape(P2QSTRING(jid.toString())), QtUtilities::htmlEscape(P2QSTRING(password)), id) +
+ buildChatWindowButton(chatMessageToHTML(ChatMessage(Q2PSTRING((tr("Accept Invite"))))), ButtonMUCInvite, QtUtilities::htmlEscape(P2QSTRING(jid.toString())), QtUtilities::htmlEscape(P2QSTRING(password)), id, QtUtilities::htmlEscape(isImpromptu ? "true" : "false"), QtUtilities::htmlEscape(isContinuation ? "true" : "false")) +
"</div>";
bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false);
@@ -1124,10 +1163,4 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji
previousMessageKind_ = PreviousMessageWasMUCInvite;
}
-
-InviteToChatWindow* QtChatWindow::createInviteToChatWindow() {
- return new QtInviteToChatWindow(this);
-}
-
-
}
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index a29edad..ba16cfe 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -109,7 +109,7 @@ namespace Swift {
void show();
void activate();
void setUnreadMessageCount(int count);
- void convertToMUC();
+ void convertToMUC(bool impromptuMUC = false);
// TreeWidget *getTreeWidget();
void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels);
void setSecurityLabelsEnabled(bool enabled);
@@ -133,14 +133,13 @@ namespace Swift {
virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions);
void setSubject(const std::string& subject);
void showRoomConfigurationForm(Form::ref);
- void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true);
+ void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true, bool isImpromptu = false, bool isContinuation = false);
void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&);
void setAvailableRoomActions(const std::vector<RoomAction>& actions);
void setBlockingState(BlockingState state);
+ virtual void setCanInitiateImpromptuChats(bool supportsImpromptu);
- InviteToChatWindow* createInviteToChatWindow();
-
- static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString());
+ static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString(), const QString& arg4 = QString(), const QString& arg5 = QString());
public slots:
void handleChangeSplitterState(QByteArray state);
@@ -176,7 +175,7 @@ namespace Swift {
void handleAlertButtonClicked();
void handleActionButtonClicked();
- void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3);
+ void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3, QString arg4, QString arg5);
void handleAffiliationEditorAccepted();
void handleCurrentLabelChanged(int);
@@ -259,5 +258,8 @@ namespace Swift {
QPalette defaultLabelsPalette_;
LabelModel* labelModel_;
BlockingState blockingState_;
+ bool impromptu_;
+ bool isMUC_;
+ bool supportsImpromptuChat_;
};
}
diff --git a/Swift/QtUI/QtChatWindowJSBridge.h b/Swift/QtUI/QtChatWindowJSBridge.h
index 8e6f0c2..5a26302 100644
--- a/Swift/QtUI/QtChatWindowJSBridge.h
+++ b/Swift/QtUI/QtChatWindowJSBridge.h
@@ -20,7 +20,7 @@ public:
QtChatWindowJSBridge();
virtual ~QtChatWindowJSBridge();
signals:
- void buttonClicked(QString id, QString arg1, QString arg2, QString arg3);
+ void buttonClicked(QString id, QString arg1, QString arg2, QString arg3, QString arg4, QString arg5);
};
}
diff --git a/Swift/QtUI/QtInviteToChatWindow.cpp b/Swift/QtUI/QtInviteToChatWindow.cpp
deleted file mode 100644
index ce6dea0..0000000
--- a/Swift/QtUI/QtInviteToChatWindow.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (c) 2012 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#include <Swift/QtUI/QtInviteToChatWindow.h>
-
-#include <QHBoxLayout>
-#include <QCompleter>
-#include <QLabel>
-#include <QLineEdit>
-#include <QPushButton>
-#include <QDialogButtonBox>
-
-#include <Swift/QtUI/QtSwiftUtil.h>
-
-namespace Swift {
-
-QtInviteToChatWindow::QtInviteToChatWindow(QWidget* parent) : QDialog(parent) {
- QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
- //layout->setContentsMargins(0,0,0,0);
- //layout->setSpacing(2);
-
- QLabel* description = new QLabel(tr("Users to invite to this chat (one per line):"));
- layout->addWidget(description);
-
- jidsLayout_ = new QBoxLayout(QBoxLayout::TopToBottom);
- layout->addLayout(jidsLayout_);
-
- QLabel* reasonLabel = new QLabel(tr("If you want to provide a reason for the invitation, enter it here"));
- layout->addWidget(reasonLabel);
- reason_ = new QLineEdit(this);
- layout->addWidget(reason_);
-
- connect(this, SIGNAL(accepted()), this, SLOT(handleAccepting()));
- connect(this, SIGNAL(rejected()), this, SLOT(handleRejecting()));
-
-
- buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
-
- connect(buttonBox_, SIGNAL(accepted()), this, SLOT(accept()));
- connect(buttonBox_, SIGNAL(rejected()), this, SLOT(reject()));
-
- layout->addWidget(buttonBox_);
- addJIDLine();
-
- jids_[0]->setFocus();
-
- setModal(false);
- show();
-}
-
-QtInviteToChatWindow::~QtInviteToChatWindow() {
-
-}
-
-void QtInviteToChatWindow::handleAccepting() {
- onCompleted();
-}
-
-void QtInviteToChatWindow::handleRejecting() {
- onDismissed();
-}
-
-std::string QtInviteToChatWindow::getReason() const {
- return Q2PSTRING(reason_->text());
-}
-
-std::vector<JID> QtInviteToChatWindow::getJIDs() const {
- std::vector<JID> results;
- foreach (QLineEdit* jidEdit, jids_) {
- QStringList parts = jidEdit->text().split(" ");
- if (parts.size() > 0) {
- JID jid(Q2PSTRING(parts.last()));
- if (jid.isValid() && !jid.getNode().empty()) {
- results.push_back(jid);
- }
- }
- }
- return results;
-}
-
-void QtInviteToChatWindow::addJIDLine() {
- QLineEdit* jid = new QLineEdit(this);
- QCompleter* completer = new QCompleter(&completions_, this);
- completer->setCaseSensitivity(Qt::CaseInsensitive);
- jid->setCompleter(completer);
- jidsLayout_->addWidget(jid);
- connect(jid, SIGNAL(textChanged(const QString&)), this, SLOT(handleJIDTextChanged()));
- if (!jids_.empty()) {
- setTabOrder(jids_.back(), jid);
- }
- jids_.push_back(jid);
- setTabOrder(jid, reason_);
- setTabOrder(reason_, buttonBox_);
- //setTabOrder(buttonBox_, jids_[0]);
-}
-
-void QtInviteToChatWindow::handleJIDTextChanged() {
- bool gotEmpty = false;
- foreach(QLineEdit* edit, jids_) {
- if (edit->text().isEmpty()) {
- gotEmpty = true;
- }
- }
- if (!gotEmpty) {
- addJIDLine();
- }
-}
-
-typedef std::pair<JID, std::string> JIDString;
-
-void QtInviteToChatWindow::setAutoCompletions(std::vector<std::pair<JID, std::string> > completions) {
- QStringList list;
- foreach (JIDString jidPair, completions) {
- QString line = P2QSTRING(jidPair.first.toString());
- if (jidPair.second != jidPair.first.toString() && !jidPair.second.empty()) {
- line = P2QSTRING(jidPair.second) + " - " + line;
- }
- list.append(line);
- }
- completions_.setStringList(list);
-}
-
-}
-
-
-
diff --git a/Swift/QtUI/QtInviteToChatWindow.h b/Swift/QtUI/QtInviteToChatWindow.h
deleted file mode 100644
index dd8743a..0000000
--- a/Swift/QtUI/QtInviteToChatWindow.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2012 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#pragma once
-
-#include <Swift/Controllers/UIInterfaces/InviteToChatWindow.h>
-
-#include <QDialog>
-#include <QStringListModel>
-
-class QLineEdit;
-class QBoxLayout;
-class QDialogButtonBox;
-
-namespace Swift {
- class QtInviteToChatWindow : public QDialog, public InviteToChatWindow {
- Q_OBJECT
- public:
- QtInviteToChatWindow(QWidget* parent = NULL);
- virtual ~QtInviteToChatWindow();
-
- virtual std::string getReason() const;
-
- virtual std::vector<JID> getJIDs() const;
- virtual void setAutoCompletions(std::vector<std::pair<JID, std::string> > completions);
- private:
- void addJIDLine();
- private slots:
- void handleJIDTextChanged();
- void handleAccepting();
- void handleRejecting();
- private:
- QStringListModel completions_;
- QLineEdit* reason_;
- QBoxLayout* jidsLayout_;
- std::vector<QLineEdit*> jids_;
- QDialogButtonBox* buttonBox_;
- };
-}
-
-
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 572b06f..6f87a88 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -144,6 +144,7 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
actionsMenu->addAction(openBlockingListEditor_);
openBlockingListEditor_->setVisible(false);
addUserAction_ = new QAction(tr("&Add Contact…"), this);
+ addUserAction_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D));
connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool)));
actionsMenu->addAction(addUserAction_);
editUserAction_ = new QAction(tr("&Edit Selected Contact…"), this);
@@ -151,6 +152,7 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
actionsMenu->addAction(editUserAction_);
editUserAction_->setEnabled(false);
chatUserAction_ = new QAction(tr("Start &Chat…"), this);
+ chatUserAction_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
connect(chatUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleChatUserActionTriggered(bool)));
actionsMenu->addAction(chatUserAction_);
serverAdHocMenu_ = new QMenu(tr("Run Server Command"), this);
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index 0c7fbc2..e5db22d 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -145,7 +145,7 @@ void QtUIFactory::handleChatWindowFontResized(int size) {
}
UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set<std::string>& groups) {
- return new QtUserSearchWindow(eventStream, type, groups);
+ return new QtUserSearchWindow(eventStream, type, groups, qtOnlySettings);
}
JoinMUCWindow* QtUIFactory::createJoinMUCWindow(UIEventStream* uiEventStream) {
diff --git a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp
index b586444..90520ad 100644
--- a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp
+++ b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp
@@ -15,12 +15,12 @@ QtRemovableItemDelegate::QtRemovableItemDelegate(const QStyle* style) : style(st
}
-void QtRemovableItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex&) const {
+void QtRemovableItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) 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());
+ drawBackground(painter, option, index);
painter->translate(option.rect.x(), option.rect.y()+(option.rect.height() - 12)/2);
style->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, painter);
painter->restore();
diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp
index 64d0fcf..99f1f34 100644
--- a/Swift/QtUI/Roster/QtTreeWidget.cpp
+++ b/Swift/QtUI/Roster/QtTreeWidget.cpp
@@ -42,6 +42,7 @@ QtTreeWidget::QtTreeWidget(UIEventStream* eventStream, SettingsProvider* setting
#ifdef SWIFT_EXPERIMENTAL_FT
setAcceptDrops(true);
#endif
+ setDragEnabled(true);
setRootIsDecorated(true);
connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&)));
connect(model_, SIGNAL(itemExpanded(const QModelIndex&, bool)), this, SLOT(handleModelItemExpanded(const QModelIndex&, bool)));
@@ -157,7 +158,7 @@ void QtTreeWidget::dragMoveEvent(QDragMoveEvent* event) {
}
}
}
- event->ignore();
+ QTreeView::dragMoveEvent(event);
}
void QtTreeWidget::handleExpanded(const QModelIndex& index) {
diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp
index 1b93047..3791ffa 100644
--- a/Swift/QtUI/Roster/RosterModel.cpp
+++ b/Swift/QtUI/Roster/RosterModel.cpp
@@ -10,6 +10,7 @@
#include <QColor>
#include <QIcon>
+#include <QMimeData>
#include <qdebug.h>
#include "Swiften/Elements/StatusShow.h"
@@ -55,7 +56,7 @@ void RosterModel::reLayout() {
void RosterModel::handleChildrenChanged(GroupRosterItem* /*group*/) {
reLayout();
-}
+}
void RosterModel::handleDataChanged(RosterItem* item) {
Q_ASSERT(item);
@@ -65,6 +66,14 @@ void RosterModel::handleDataChanged(RosterItem* item) {
}
}
+Qt::ItemFlags RosterModel::flags(const QModelIndex& index) const {
+ Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+ if (dynamic_cast<GroupRosterItem*>(getItem(index)) == NULL) {
+ flags |= Qt::ItemIsDragEnabled;
+ }
+ return flags;
+}
+
int RosterModel::columnCount(const QModelIndex& /*parent*/) const {
return 1;
}
@@ -230,4 +239,25 @@ int RosterModel::rowCount(const QModelIndex& parent) const {
return count;
}
+QMimeData* RosterModel::mimeData(const QModelIndexList& indexes) const {
+ QMimeData* data = QAbstractItemModel::mimeData(indexes);
+
+ ContactRosterItem *item = dynamic_cast<ContactRosterItem*>(getItem(indexes.first()));
+ if (item == NULL) {
+ return data;
+ }
+
+ 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);
+ return data;
+}
+
}
diff --git a/Swift/QtUI/Roster/RosterModel.h b/Swift/QtUI/Roster/RosterModel.h
index 23d54f8..cae80c4 100644
--- a/Swift/QtUI/Roster/RosterModel.h
+++ b/Swift/QtUI/Roster/RosterModel.h
@@ -29,12 +29,15 @@ namespace Swift {
RosterModel(QtTreeWidget* view);
~RosterModel();
void setRoster(Roster* swiftRoster);
+ Qt::ItemFlags flags(const QModelIndex& index) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
QModelIndex index(RosterItem* item) const;
QModelIndex parent(const QModelIndex& index) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
+ QMimeData* mimeData(const QModelIndexList& indexes) const;
+
signals:
void itemExpanded(const QModelIndex& item, bool expanded);
private:
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 1c3fd70..86efb51 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -136,7 +136,6 @@ sources = [
"QtFormResultItemModel.cpp",
"QtLineEdit.cpp",
"QtJoinMUCWindow.cpp",
- "QtInviteToChatWindow.cpp",
"QtConnectionSettingsWindow.cpp",
"Roster/RosterModel.cpp",
"Roster/QtTreeWidget.cpp",
@@ -162,7 +161,12 @@ sources = [
"MUCSearch/MUCSearchRoomItem.cpp",
"MUCSearch/MUCSearchEmptyItem.cpp",
"MUCSearch/MUCSearchDelegate.cpp",
+ "UserSearch/ContactListDelegate.cpp",
+ "UserSearch/ContactListModel.cpp",
+ "UserSearch/QtContactListWidget.cpp",
+ "UserSearch/QtSuggestingJIDInput.cpp",
"UserSearch/QtUserSearchFirstPage.cpp",
+ "UserSearch/QtUserSearchFirstMultiJIDPage.cpp",
"UserSearch/QtUserSearchFieldsPage.cpp",
"UserSearch/QtUserSearchResultsPage.cpp",
"UserSearch/QtUserSearchDetailsPage.cpp",
@@ -267,6 +271,7 @@ else :
myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui")
myenv.Uic4("UserSearch/QtUserSearchWizard.ui")
myenv.Uic4("UserSearch/QtUserSearchFirstPage.ui")
+myenv.Uic4("UserSearch/QtUserSearchFirstMultiJIDPage.ui")
myenv.Uic4("UserSearch/QtUserSearchFieldsPage.ui")
myenv.Uic4("UserSearch/QtUserSearchResultsPage.ui")
myenv.Uic4("QtBookmarkDetailWindow.ui")
diff --git a/Swift/QtUI/UserSearch/ContactListDelegate.cpp b/Swift/QtUI/UserSearch/ContactListDelegate.cpp
new file mode 100644
index 0000000..29cab83
--- /dev/null
+++ b/Swift/QtUI/UserSearch/ContactListDelegate.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swift/QtUI/UserSearch/ContactListDelegate.h>
+#include <Swift/QtUI/UserSearch/ContactListModel.h>
+#include <Swift/Controllers/Contact.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+ContactListDelegate::ContactListDelegate(bool compact) : compact_(compact) {
+}
+
+ContactListDelegate::~ContactListDelegate() {
+}
+
+void ContactListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
+ if (!index.isValid()) {
+ return;
+ }
+ const Contact* contact = static_cast<Contact*>(index.internalPointer());
+ QColor nameColor = index.data(Qt::TextColorRole).value<QColor>();
+ QString avatarPath = index.data(ContactListModel::AvatarRole).value<QString>();
+ QIcon presenceIcon =index.data(ChatListRecentItem::PresenceIconRole).isValid() && !index.data(ChatListRecentItem::PresenceIconRole).value<QIcon>().isNull()
+ ? index.data(ChatListRecentItem::PresenceIconRole).value<QIcon>()
+ : QIcon(":/icons/offline.png");
+ QString name = P2QSTRING(contact->name);
+ QString statusText = P2QSTRING(contact->jid.toString());
+ common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, false, 0, compact_);
+}
+
+QSize ContactListDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
+ QFontMetrics nameMetrics(common_.nameFont);
+ QFontMetrics statusMetrics(common_.detailFont);
+ int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height();
+ return QSize(150, sizeByText);
+}
+
+void ContactListDelegate::setCompact(bool compact) {
+ compact_ = compact;
+}
+
+}
diff --git a/Swift/QtUI/UserSearch/ContactListDelegate.h b/Swift/QtUI/UserSearch/ContactListDelegate.h
new file mode 100644
index 0000000..7680aba
--- /dev/null
+++ b/Swift/QtUI/UserSearch/ContactListDelegate.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QStyledItemDelegate>
+
+#include <Swift/QtUI/Roster/DelegateCommons.h>
+
+namespace Swift {
+
+class ContactListDelegate : public QStyledItemDelegate {
+ public:
+ ContactListDelegate(bool compact);
+ virtual ~ContactListDelegate();
+
+ QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
+
+ public slots:
+ void setCompact(bool compact);
+
+ private:
+ bool compact_;
+ DelegateCommons common_;
+};
+}
diff --git a/Swift/QtUI/UserSearch/ContactListModel.cpp b/Swift/QtUI/UserSearch/ContactListModel.cpp
new file mode 100644
index 0000000..6523a4d
--- /dev/null
+++ b/Swift/QtUI/UserSearch/ContactListModel.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swift/QtUI/UserSearch/ContactListModel.h>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swiften/Base/Path.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Elements/StatusShow.h>
+
+#include <QMimeData>
+
+namespace Swift {
+
+QDataStream& operator >>(QDataStream& in, StatusShow::Type& e){
+ quint32 buffer;
+ in >> buffer;
+ switch(buffer) {
+ case StatusShow::Online:
+ e = StatusShow::Online;
+ break;
+ case StatusShow::Away:
+ e = StatusShow::Away;
+ break;
+ case StatusShow::FFC:
+ e = StatusShow::FFC;
+ break;
+ case StatusShow::XA:
+ e = StatusShow::XA;
+ break;
+ case StatusShow::DND:
+ e = StatusShow::DND;
+ break;
+ default:
+ e = StatusShow::None;
+ break;
+ }
+ return in;
+}
+
+ContactListModel::ContactListModel(bool editable) : QAbstractItemModel(), editable_(editable) {
+}
+
+void ContactListModel::setList(const std::vector<Contact>& list) {
+ emit layoutAboutToBeChanged();
+ contacts_ = list;
+ emit layoutChanged();
+}
+
+const std::vector<Contact>& ContactListModel::getList() const {
+ return contacts_;
+}
+
+Qt::ItemFlags ContactListModel::flags(const QModelIndex& index) const {
+ Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+ if (index.isValid()) {
+ flags = flags & ~Qt::ItemIsDropEnabled;
+ } else {
+ flags = Qt::ItemIsDropEnabled | flags;
+ }
+ return flags;
+}
+
+int ContactListModel::columnCount(const QModelIndex&) const {
+ return editable_ ? 2 : 1;
+}
+
+QVariant ContactListModel::data(const QModelIndex& index, int role) const {
+ if (boost::numeric_cast<size_t>(index.row()) < contacts_.size()) {
+ const Contact& contact = contacts_[index.row()];
+ if (role == Qt::EditRole) {
+ return P2QSTRING(contact.jid.toString());
+ }
+ return dataForContact(contact, role);
+ } else {
+ return QVariant();
+ }
+}
+
+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();
+ }
+
+ return boost::numeric_cast<size_t>(row) < contacts_.size() ? createIndex(row, column, (void*)&(contacts_[row])) : QModelIndex();
+}
+
+QModelIndex ContactListModel::parent(const QModelIndex& index) const {
+ if (!index.isValid()) {
+ return QModelIndex();
+ }
+ return QModelIndex();
+}
+
+int ContactListModel::rowCount(const QModelIndex& /*parent*/) const {
+ return contacts_.size();
+}
+
+bool ContactListModel::removeRows(int row, int /*count*/, const QModelIndex& /*parent*/) {
+ if (boost::numeric_cast<size_t>(row) < contacts_.size()) {
+ emit layoutAboutToBeChanged();
+ contacts_.erase(contacts_.begin() + row);
+ emit layoutChanged();
+ onListChanged(getList());
+ return true;
+ }
+ return false;
+}
+
+QVariant ContactListModel::dataForContact(const Contact& contact, int role) const {
+ switch (role) {
+ case Qt::DisplayRole: return P2QSTRING(contact.name);
+ case DetailTextRole: return P2QSTRING(contact.jid.toString());
+ case AvatarRole: return QVariant(P2QSTRING(pathToString(contact.avatarPath)));
+ case PresenceIconRole: return getPresenceIconForContact(contact);
+ default: return QVariant();
+ }
+}
+
+QIcon ContactListModel::getPresenceIconForContact(const Contact& contact) const {
+ QString iconString;
+ switch (contact.statusType) {
+ case StatusShow::Online: iconString = "online";break;
+ case StatusShow::Away: iconString = "away";break;
+ case StatusShow::XA: iconString = "away";break;
+ case StatusShow::FFC: iconString = "online";break;
+ case StatusShow::DND: iconString = "dnd";break;
+ case StatusShow::None: iconString = "offline";break;
+ }
+ return QIcon(":/icons/" + iconString + ".png");
+}
+
+}
diff --git a/Swift/QtUI/UserSearch/ContactListModel.h b/Swift/QtUI/UserSearch/ContactListModel.h
new file mode 100644
index 0000000..e7f4a0b
--- /dev/null
+++ b/Swift/QtUI/UserSearch/ContactListModel.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <boost/bind.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <QAbstractItemModel>
+
+#include <Swift/Controllers/Contact.h>
+#include <Swift/QtUI/ChatList/ChatListItem.h>
+#include <Swift/QtUI/ChatList/ChatListGroupItem.h>
+#include <Swift/QtUI/ChatList/ChatListRecentItem.h>
+
+namespace Swift {
+ class ContactListModel : public QAbstractItemModel {
+ Q_OBJECT
+ public:
+ enum ContactRoles {
+ DetailTextRole = Qt::UserRole,
+ AvatarRole = Qt::UserRole + 1,
+ PresenceIconRole = Qt::UserRole + 2
+ };
+
+ public:
+ ContactListModel(bool editable);
+
+ void setList(const std::vector<Contact>& list);
+ const std::vector<Contact>& getList() const;
+
+ 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;
+ bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
+
+ private:
+ QVariant dataForContact(const Contact& contact, int role) const;
+ QIcon getPresenceIconForContact(const Contact& contact) const;
+
+ signals:
+ void onListChanged(std::vector<Contact> list);
+ void onJIDsDropped(const std::vector<JID>& contact);
+
+ private:
+ bool editable_;
+ std::vector<Contact> contacts_;
+ };
+
+}
diff --git a/Swift/QtUI/UserSearch/QtContactListWidget.cpp b/Swift/QtUI/UserSearch/QtContactListWidget.cpp
new file mode 100644
index 0000000..899c592
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtContactListWidget.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swift/QtUI/UserSearch/QtContactListWidget.h>
+
+#include <Swift/QtUI/UserSearch/ContactListModel.h>
+#include <Swift/QtUI/UserSearch/ContactListDelegate.h>
+#include <Swift/QtUI/QtUISettingConstants.h>
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h>
+
+#include <QHeaderView>
+
+namespace Swift {
+
+QtContactListWidget::QtContactListWidget(QWidget* parent, SettingsProvider* settings) : QTreeView(parent), settings_(settings), limited_(false) {
+ 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);
+ setIndentation(0);
+ setHeaderHidden(true);
+ setExpandsOnDoubleClick(false);
+ setItemsExpandable(false);
+ setEditTriggers(QAbstractItemView::DoubleClicked);
+
+ contactListDelegate_ = new ContactListDelegate(settings->getSetting(QtUISettingConstants::COMPACT_ROSTER));
+ removableItemDelegate_ = new QtRemovableItemDelegate(style());
+
+ setItemDelegateForColumn(0, contactListDelegate_);
+ setItemDelegateForColumn(1, removableItemDelegate_);
+
+ int closeIconWidth = fontMetrics().height();
+ header()->resizeSection(1, closeIconWidth);
+
+ header()->setStretchLastSection(false);
+#if QT_VERSION >= 0x050000
+ header()->setSectionResizeMode(0, QHeaderView::Stretch);
+#else
+ header()->setResizeMode(0, QHeaderView::Stretch);
+#endif
+}
+
+QtContactListWidget::~QtContactListWidget() {
+ delete contactListDelegate_;
+ delete removableItemDelegate_;
+}
+
+void QtContactListWidget::setList(const std::vector<Contact>& list) {
+ contactListModel_->setList(list);
+}
+
+std::vector<Contact> QtContactListWidget::getList() const {
+ return contactListModel_->getList();
+}
+
+void QtContactListWidget::setMaximumNoOfContactsToOne(bool limited) {
+ limited_ = limited;
+ if (limited) {
+ handleListChanged(getList());
+ } else {
+ setAcceptDrops(true);
+ setDropIndicatorShown(true);
+ }
+}
+
+void QtContactListWidget::updateContacts(const std::vector<Contact>& contactUpdates) {
+ std::vector<Contact> contacts = contactListModel_->getList();
+ foreach(const Contact& contactUpdate, contactUpdates) {
+ for(size_t n = 0; n < contacts.size(); n++) {
+ if (contactUpdate.jid == contacts[n].jid) {
+ contacts[n] = contactUpdate;
+ break;
+ }
+ }
+ }
+ 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
new file mode 100644
index 0000000..f360a91
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtContactListWidget.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <QTreeView>
+
+#include <Swift/Controllers/Contact.h>
+#include <Swiften/Base/Log.h>
+
+#include <QDragEnterEvent>
+#include <QDragMoveEvent>
+#include <QDropEvent>
+
+namespace Swift {
+
+class ContactListDelegate;
+class ContactListModel;
+class SettingsProvider;
+class QtRemovableItemDelegate;
+
+class QtContactListWidget : public QTreeView {
+ Q_OBJECT
+public:
+ QtContactListWidget(QWidget* parent, SettingsProvider* settings);
+ virtual ~QtContactListWidget();
+
+ void setList(const std::vector<Contact>& list);
+ std::vector<Contact> getList() const;
+ void setMaximumNoOfContactsToOne(bool limited);
+
+public slots:
+ void updateContacts(const std::vector<Contact>& contactUpdates);
+
+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&);
+
+private:
+ SettingsProvider* settings_;
+ ContactListModel* contactListModel_;
+ ContactListDelegate* contactListDelegate_;
+ QtRemovableItemDelegate* removableItemDelegate_;
+ bool limited_;
+};
+
+}
diff --git a/Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp b/Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp
new file mode 100644
index 0000000..ca65dca
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swift/QtUI/UserSearch/QtSuggestingJIDInput.h>
+#include <Swift/QtUI/UserSearch/ContactListDelegate.h>
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/QtUI/QtUISettingConstants.h>
+#include <Swift/QtUI/UserSearch/ContactListModel.h>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <boost/bind.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+#include <QAbstractItemView>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QKeyEvent>
+
+
+namespace Swift {
+
+QtSuggestingJIDInput::QtSuggestingJIDInput(QWidget* parent, SettingsProvider* settings) : QLineEdit(parent), settings_(settings), currentContact_(NULL) {
+ treeViewPopup_ = new QTreeView();
+ treeViewPopup_->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
+ //treeViewPopup_->setAttribute(Qt::WA_ShowWithoutActivating);
+ treeViewPopup_->setAlternatingRowColors(true);
+ treeViewPopup_->setIndentation(0);
+ treeViewPopup_->setHeaderHidden(true);
+ treeViewPopup_->setExpandsOnDoubleClick(false);
+ treeViewPopup_->setItemsExpandable(false);
+ treeViewPopup_->setSelectionMode(QAbstractItemView::SingleSelection);
+ treeViewPopup_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ treeViewPopup_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ QSizePolicy policy(treeViewPopup_->sizePolicy());
+ policy.setVerticalPolicy(QSizePolicy::Expanding);
+ treeViewPopup_->setSizePolicy(policy);
+ treeViewPopup_->hide();
+ treeViewPopup_->setFocusProxy(this);
+ connect(treeViewPopup_, SIGNAL(clicked(QModelIndex)), this, SLOT(handleClicked(QModelIndex)));
+ connect(treeViewPopup_, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(handleClicked(QModelIndex)));
+
+ contactListModel_ = new ContactListModel(false);
+ treeViewPopup_->setModel(contactListModel_);
+
+ contactListDelegate_ = new ContactListDelegate(settings->getSetting(QtUISettingConstants::COMPACT_ROSTER));
+ treeViewPopup_->setItemDelegate(contactListDelegate_);
+
+ settings_->onSettingChanged.connect(boost::bind(&QtSuggestingJIDInput::handleSettingsChanged, this, _1));
+}
+
+QtSuggestingJIDInput::~QtSuggestingJIDInput() {
+ settings_->onSettingChanged.disconnect(boost::bind(&QtSuggestingJIDInput::handleSettingsChanged, this, _1));
+ delete treeViewPopup_;
+}
+
+const Contact* QtSuggestingJIDInput::getContact() {
+ if (currentContact_ != NULL) {
+ return currentContact_;
+ } else {
+ if (!text().isEmpty()) {
+ JID jid(Q2PSTRING(text()));
+ if (jid.isValid()) {
+ manualContact_.name = jid.toString();
+ manualContact_.jid = jid;
+ return &manualContact_;
+ }
+ }
+ return NULL;
+ }
+}
+
+void QtSuggestingJIDInput::setSuggestions(const std::vector<Contact>& suggestions) {
+ contactListModel_->setList(suggestions);
+ positionPopup();
+ if (!suggestions.empty()) {
+ treeViewPopup_->setCurrentIndex(contactListModel_->index(0, 0));
+ showPopup();
+ } else {
+ currentContact_ = NULL;
+ }
+}
+
+void QtSuggestingJIDInput::keyPressEvent(QKeyEvent* event) {
+ if (event->key() == Qt::Key_Up) {
+ if (contactListModel_->rowCount() > 0) {
+ int row = treeViewPopup_->currentIndex().row();
+ row = (row + contactListModel_->rowCount() - 1) % contactListModel_->rowCount();
+ treeViewPopup_->setCurrentIndex(contactListModel_->index(row, 0));
+ }
+ } else if (event->key() == Qt::Key_Down) {
+ if (contactListModel_->rowCount() > 0) {
+ int row = treeViewPopup_->currentIndex().row();
+ row = (row + contactListModel_->rowCount() + 1) % contactListModel_->rowCount();
+ treeViewPopup_->setCurrentIndex(contactListModel_->index(row, 0));
+ }
+ } else if (event->key() == Qt::Key_Return && treeViewPopup_->isVisible()) {
+ QModelIndex index = treeViewPopup_->currentIndex();
+ if (!contactListModel_->getList().empty() && index.isValid()) {
+ currentContact_ = &contactListModel_->getList()[index.row()];
+ setText(P2QSTRING(currentContact_->jid.toString()));
+ hidePopup();
+ clearFocus();
+ } else {
+ currentContact_ = NULL;
+ }
+ editingDone();
+ } else {
+ QLineEdit::keyPressEvent(event);
+ }
+}
+
+void QtSuggestingJIDInput::handleApplicationFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
+ /* Using the now argument gives use the wrong widget. This is part of the code needed
+ to prevent stealing of focus when opening a the suggestion window. */
+ QWidget* now = qApp->focusWidget();
+ if (!now || (now != treeViewPopup_ && now != this && !now->isAncestorOf(this) && !now->isAncestorOf(treeViewPopup_) && !this->isAncestorOf(now) && !treeViewPopup_->isAncestorOf(now))) {
+ hidePopup();
+ }
+}
+
+void QtSuggestingJIDInput::handleSettingsChanged(const std::string& setting) {
+ if (setting == QtUISettingConstants::COMPACT_ROSTER.getKey()) {
+ contactListDelegate_->setCompact(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER));
+ }
+}
+
+void QtSuggestingJIDInput::handleClicked(const QModelIndex& index) {
+ if (index.isValid()) {
+ currentContact_ = &contactListModel_->getList()[index.row()];
+ setText(P2QSTRING(currentContact_->jid.toString()));
+ hidePopup();
+ }
+}
+
+void QtSuggestingJIDInput::positionPopup() {
+ QDesktopWidget* desktop = QApplication::desktop();
+ int screen = desktop->screenNumber(this);
+ QPoint point = mapToGlobal(QPoint(0, height()));
+ QRect geometry = desktop->availableGeometry(screen);
+ int x = point.x();
+ int y = point.y();
+ int width = this->width();
+ int height = 80;
+
+ int screenWidth = geometry.x() + geometry.width();
+ if (x + width > screenWidth) {
+ x = screenWidth - width;
+ }
+
+ height = treeViewPopup_->sizeHintForRow(0) * contactListModel_->rowCount();
+ height = height > 200 ? 200 : height;
+
+ int marginLeft;
+ int marginTop;
+ int marginRight;
+ int marginBottom;
+ treeViewPopup_->getContentsMargins(&marginLeft, &marginTop, &marginRight, &marginBottom);
+ height += marginTop + marginBottom;
+ width += marginLeft + marginRight;
+
+ treeViewPopup_->setGeometry(x, y, width, height);
+ treeViewPopup_->move(x, y);
+ treeViewPopup_->setMaximumWidth(width);
+}
+
+void QtSuggestingJIDInput::showPopup() {
+ treeViewPopup_->show();
+ activateWindow();
+ connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*)), Qt::QueuedConnection);
+ setFocus();
+}
+
+void QtSuggestingJIDInput::hidePopup() {
+ disconnect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*)));
+ treeViewPopup_->hide();
+}
+
+}
diff --git a/Swift/QtUI/UserSearch/QtSuggestingJIDInput.h b/Swift/QtUI/UserSearch/QtSuggestingJIDInput.h
new file mode 100644
index 0000000..673621c
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtSuggestingJIDInput.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QLineEdit>
+#include <QTreeView>
+
+#include <Swift/Controllers/Contact.h>
+
+namespace Swift {
+
+class ContactListDelegate;
+class SettingsProvider;
+class ContactListModel;
+
+class QtSuggestingJIDInput : public QLineEdit {
+ Q_OBJECT
+ public:
+ QtSuggestingJIDInput(QWidget* parent, SettingsProvider* settings);
+ virtual ~QtSuggestingJIDInput();
+
+ const Contact* getContact();
+
+ void setSuggestions(const std::vector<Contact>& suggestions);
+
+ signals:
+ void editingDone();
+
+ protected:
+ virtual void keyPressEvent(QKeyEvent* event);
+
+ private:
+ void handleSettingsChanged(const std::string& setting);
+
+ private slots:
+ void handleClicked(const QModelIndex& index);
+ void handleApplicationFocusChanged(QWidget* old, QWidget* now);
+
+ private:
+ void positionPopup();
+ void showPopup();
+ void hidePopup();
+
+ private:
+ SettingsProvider* settings_;
+ ContactListModel* contactListModel_;
+ QTreeView* treeViewPopup_;
+ ContactListDelegate* contactListDelegate_;
+ Contact manualContact_;
+ const Contact* currentContact_;
+};
+
+}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp
new file mode 100644
index 0000000..b1e9a12
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h"
+
+#include "Swift/QtUI/QtSwiftUtil.h"
+#include <Swift/QtUI/UserSearch/QtContactListWidget.h>
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/QtUI/UserSearch/QtSuggestingJIDInput.h>
+
+namespace Swift {
+
+QtUserSearchFirstMultiJIDPage::QtUserSearchFirstMultiJIDPage(UserSearchWindow::Type type, const QString& title, SettingsProvider* settings) {
+ setupUi(this);
+ setTitle(title);
+ QString introText = "";
+ switch (type) {
+ case UserSearchWindow::AddContact:
+ introText = tr("Add another user to your contact list");
+ break;
+ case UserSearchWindow::ChatToContact:
+ introText = tr("Chat to another user");
+ break;
+ case UserSearchWindow::InviteToChat:
+ introText = tr("Invite contact to chat");
+ break;
+ }
+
+ setSubTitle(QString(tr("%1. If you know their address you can enter it directly, or you can search for them.")).arg(introText));
+
+ contactList_ = new QtContactListWidget(this, settings);
+ horizontalLayout_5->addWidget(contactList_);
+
+ jid_ = new QtSuggestingJIDInput(this, settings);
+ horizontalLayout_6->insertWidget(0, jid_);
+
+ connect(contactList_, SIGNAL(onListChanged(std::vector<Contact>)), this, SLOT(emitCompletenessCheck()));
+ connect(jid_, SIGNAL(editingDone()), this, SLOT(handleEditingDone()));
+}
+
+bool QtUserSearchFirstMultiJIDPage::isComplete() const {
+ return !contactList_->getList().empty();
+}
+
+void QtUserSearchFirstMultiJIDPage::emitCompletenessCheck() {
+ emit completeChanged();
+}
+
+void QtUserSearchFirstMultiJIDPage::handleEditingDone() {
+ addContactButton_->setFocus();
+}
+
+}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h
new file mode 100644
index 0000000..427995e
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QWizardPage>
+
+#include <Swift/QtUI/UserSearch/ui_QtUserSearchFirstMultiJIDPage.h>
+#include <Swift/Controllers/UIInterfaces/UserSearchWindow.h>
+
+namespace Swift {
+ class UserSearchModel;
+ class UserSearchDelegate;
+ class UserSearchResult;
+ class UIEventStream;
+ class QtContactListWidget;
+ class ContactSuggester;
+ class AvatarManager;
+ class VCardManager;
+ class SettingsProvider;
+ class QtSuggestingJIDInput;
+
+ class QtUserSearchFirstMultiJIDPage : public QWizardPage, public Ui::QtUserSearchFirstMultiJIDPage {
+ Q_OBJECT
+ public:
+ QtUserSearchFirstMultiJIDPage(UserSearchWindow::Type type, const QString& title, SettingsProvider* settings);
+ virtual bool isComplete() const;
+
+ public slots:
+ void emitCompletenessCheck();
+ void handleEditingDone();
+
+ public:
+ QtContactListWidget* contactList_;
+ QtSuggestingJIDInput* jid_;
+ };
+}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.ui b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.ui
new file mode 100644
index 0000000..4a87f41
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.ui
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtUserSearchFirstMultiJIDPage</class>
+ <widget class="QWizardPage" name="QtUserSearchFirstMultiJIDPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>477</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string/>
+ </property>
+ <property name="title">
+ <string>Add a user</string>
+ </property>
+ <property name="subTitle">
+ <string>Add another user to your contact list. If you know their address you can add them directly, or you can search for them.</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="howLabel_">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_5"/>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Choose another contact</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>-1</number>
+ </property>
+ <property name="margin">
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6" stretch="0">
+ <property name="spacing">
+ <number>-1</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="addContactButton_">
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Add Contact</string>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QRadioButton" name="byLocalSearch_">
+ <property name="text">
+ <string>I'd like to search my server</string>
+ </property>
+ <property name="autoExclusive">
+ <bool>true</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>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QRadioButton" name="byRemoteSearch_">
+ <property name="text">
+ <string>I'd like to search another server:</string>
+ </property>
+ <property name="autoExclusive">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="service_">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <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>
+ <item>
+ <widget class="QPushButton" name="addViaSearchButton_">
+ <property name="text">
+ <string>Add via Search</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Reason:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="reason_"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>77</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="errorLabel_">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstPage.cpp b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.cpp
index 7a91a98..af53a26 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFirstPage.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.cpp
@@ -8,12 +8,19 @@
#include "Swift/QtUI/QtSwiftUtil.h"
+#include <Swiften/Base/Log.h>
+
namespace Swift {
-QtUserSearchFirstPage::QtUserSearchFirstPage(UserSearchWindow::Type type, const QString& title) {
+QtUserSearchFirstPage::QtUserSearchFirstPage(UserSearchWindow::Type type, const QString& title, SettingsProvider* settings) {
setupUi(this);
setTitle(title);
setSubTitle(QString(tr("%1. If you know their address you can enter it directly, or you can search for them.")).arg(type == UserSearchWindow::AddContact ? tr("Add another user to your contact list") : tr("Chat to another user")));
+ jid_ = new QtSuggestingJIDInput(this, settings);
+ horizontalLayout_2->addWidget(jid_);
+ setTabOrder(byJID_, jid_);
+ setTabOrder(jid_, byLocalSearch_);
+ setTabOrder(byLocalSearch_, byRemoteSearch_);
connect(jid_, SIGNAL(textChanged(const QString&)), this, SLOT(emitCompletenessCheck()));
connect(service_->lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(emitCompletenessCheck()));
}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstPage.h b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.h
index d23b87d..d7487b0 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFirstPage.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.h
@@ -11,6 +11,8 @@
#include <Swift/QtUI/UserSearch/ui_QtUserSearchFirstPage.h>
#include <Swift/Controllers/UIInterfaces/UserSearchWindow.h>
+#include <Swift/QtUI/UserSearch/QtSuggestingJIDInput.h>
+
namespace Swift {
class UserSearchModel;
class UserSearchDelegate;
@@ -20,9 +22,11 @@ namespace Swift {
class QtUserSearchFirstPage : public QWizardPage, public Ui::QtUserSearchFirstPage {
Q_OBJECT
public:
- QtUserSearchFirstPage(UserSearchWindow::Type type, const QString& title);
+ QtUserSearchFirstPage(UserSearchWindow::Type type, const QString& title, SettingsProvider* settings);
virtual bool isComplete() const;
public slots:
void emitCompletenessCheck();
+ public:
+ QtSuggestingJIDInput* jid_;
};
}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui
index bb0a625..24d401e 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui
@@ -34,11 +34,11 @@
<property name="text">
<string>I know their address:</string>
</property>
+ <property name="autoExclusive">
+ <bool>true</bool>
+ </property>
</widget>
</item>
- <item>
- <widget class="QLineEdit" name="jid_"/>
- </item>
</layout>
</item>
<item>
@@ -48,6 +48,9 @@
<property name="text">
<string>I'd like to search my server</string>
</property>
+ <property name="autoExclusive">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
@@ -72,6 +75,9 @@
<property name="text">
<string>I'd like to search another server:</string>
</property>
+ <property name="autoExclusive">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index 73514fd..d06fa19 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -13,57 +13,50 @@
#include <boost/smart_ptr/make_shared.hpp>
#include <Swiften/Base/foreach.h>
-#include "Swift/Controllers/UIEvents/UIEventStream.h"
-#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
-#include "Swift/Controllers/UIEvents/AddContactUIEvent.h"
-#include "Swift/QtUI/UserSearch/UserSearchModel.h"
-#include "Swift/QtUI/UserSearch/UserSearchDelegate.h"
-#include "Swift/QtUI/QtSwiftUtil.h"
-#include "Swift/QtUI/QtFormResultItemModel.h"
-#include "QtUserSearchFirstPage.h"
-#include "QtUserSearchFieldsPage.h"
-#include "QtUserSearchResultsPage.h"
-#include "QtUserSearchDetailsPage.h"
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
+#include <Swift/Controllers/UIEvents/AddContactUIEvent.h>
+#include <Swift/Controllers/UIEvents/CreateImpromptuMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h>
+#include <Swift/QtUI/UserSearch/UserSearchModel.h>
+#include <Swift/QtUI/UserSearch/UserSearchDelegate.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swift/QtUI/QtFormResultItemModel.h>
+#include <Swift/QtUI/UserSearch/QtUserSearchFirstPage.h>
+#include <Swift/QtUI/UserSearch/QtUserSearchFirstMultiJIDPage.h>
+#include <Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h>
+#include <Swift/QtUI/UserSearch/QtUserSearchResultsPage.h>
+#include <Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h>
+#include <Swift/QtUI/UserSearch/QtContactListWidget.h>
+
+#include <Swiften/Base/Log.h>
namespace Swift {
-QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups) : eventStream_(eventStream), type_(type), model_(NULL) {
+QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups, SettingsProvider* settingsProvider) : eventStream_(eventStream), type_(type), model_(NULL), settings_(settingsProvider), searchNext_(false), supportsImpromptu_(false) {
setupUi(this);
#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/logo-icon-16.png"));
#endif
- QString title(type == UserSearchWindow::AddContact ? tr("Add Contact") : tr("Chat to User"));
+ QString title;
+ switch(type) {
+ case AddContact:
+ title = tr("Add Contact");
+ break;
+ case ChatToContact:
+ title = tr("Chat to Users");
+ break;
+ case InviteToChat:
+ title = tr("Add Users to Chat");
+ break;
+ }
setWindowTitle(title);
delegate_ = new UserSearchDelegate();
- firstPage_ = new QtUserSearchFirstPage(type, title);
- connect(firstPage_->byJID_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
- connect(firstPage_->byLocalSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
- connect(firstPage_->byRemoteSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
-#if QT_VERSION >= 0x040700
- firstPage_->jid_->setPlaceholderText(tr("alice@wonderland.lit"));
-#endif
- firstPage_->service_->setEnabled(false);
- setPage(1, firstPage_);
-
- fieldsPage_ = new QtUserSearchFieldsPage();
- fieldsPage_->fetchingThrobber_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this));
- fieldsPage_->fetchingThrobber_->movie()->stop();
- setPage(2, fieldsPage_);
-
- resultsPage_ = new QtUserSearchResultsPage();
-
-#ifdef SWIFT_PLATFORM_MACOSX
- resultsPage_->results_->setAlternatingRowColors(true);
-#endif
- if (type == AddContact) {
- connect(resultsPage_, SIGNAL(onUserTriggersContinue()), this, SLOT(next()));
- }
- else {
- connect(resultsPage_, SIGNAL(onUserTriggersContinue()), this, SLOT(accept()));
- }
- setPage(3, resultsPage_);
+ setFirstPage(title);
+ setSecondPage();
+ setThirdPage();
detailsPage_ = new QtUserSearchDetailsPage(groups);
setPage(4, detailsPage_);
@@ -78,16 +71,34 @@ QtUserSearchWindow::~QtUserSearchWindow() {
}
void QtUserSearchWindow::handleCurrentChanged(int page) {
+ searchNext_ = false;
resultsPage_->emitCompletenessCheck();
- if (page == 2 && lastPage_ == 1) {
+ if (page == 1 && lastPage_ == 3) {
+ addSearchedJIDToList(getContactJID());
+ setSecondPage();
+ }
+ else if (page == 2 && lastPage_ == 1) {
setError("");
/* next won't be called if JID is selected */
JID server = getServerToSearch();
clearForm();
onFormRequested(server);
+ setThirdPage();
}
else if (page == 3 && lastPage_ == 2) {
+ JID server = getServerToSearch();
handleSearch();
+
+ if (type_ == AddContact) {
+ bool remote = firstPage_->byRemoteSearch_->isChecked();
+ firstPage_->byRemoteSearch_->setChecked(remote);
+ firstPage_->service_->setEditText(P2QSTRING(server.toString()));
+ } else {
+ bool remote = firstMultiJIDPage_->byRemoteSearch_->isChecked();
+ setFirstPage();
+ firstMultiJIDPage_->byRemoteSearch_->setChecked(remote);
+ firstMultiJIDPage_->service_->setEditText(P2QSTRING(server.toString()));
+ }
}
else if (page == 4) {
detailsPage_->clear();
@@ -98,28 +109,77 @@ void QtUserSearchWindow::handleCurrentChanged(int page) {
}
JID QtUserSearchWindow::getServerToSearch() {
- return firstPage_->byRemoteSearch_->isChecked() ? JID(Q2PSTRING(firstPage_->service_->currentText().trimmed())) : myServer_;
+ if (type_ == AddContact) {
+ return firstPage_->byRemoteSearch_->isChecked() ? JID(Q2PSTRING(firstPage_->service_->currentText().trimmed())) : myServer_;
+ } else {
+ return firstMultiJIDPage_->byRemoteSearch_->isChecked() ? JID(Q2PSTRING(firstMultiJIDPage_->service_->currentText().trimmed())) : myServer_;
+ }
}
void QtUserSearchWindow::handleAccepted() {
- JID jid = getContactJID();
+ JID jid;
+ std::vector<JID> jids;
+ switch(type_) {
+ case AddContact:
+ jid = getContactJID();
+ eventStream_->send(boost::make_shared<AddContactUIEvent>(jid, detailsPage_->getName(), detailsPage_->getSelectedGroups()));
+ break;
+ case ChatToContact:
+ if (contactVector_.size() == 1) {
+ boost::shared_ptr<UIEvent> event(new RequestChatUIEvent(contactVector_[0].jid));
+ eventStream_->send(event);
+ break;
+ }
- if (type_ == AddContact) {
- eventStream_->send(boost::make_shared<AddContactUIEvent>(jid, detailsPage_->getName(), detailsPage_->getSelectedGroups()));
+ foreach(const Contact& contact, contactVector_) {
+ jids.push_back(contact.jid);
+ }
+
+ eventStream_->send(boost::make_shared<CreateImpromptuMUCUIEvent>(jids, JID(), Q2PSTRING(firstMultiJIDPage_->reason_->text())));
+ break;
+ case InviteToChat:
+ foreach(const Contact& contact, contactVector_) {
+ jids.push_back(contact.jid);
+ }
+ eventStream_->send(boost::make_shared<InviteToMUCUIEvent>(roomJID_, jids, Q2PSTRING(firstMultiJIDPage_->reason_->text())));
+ break;
}
- else {
- boost::shared_ptr<UIEvent> event(new RequestChatUIEvent(jid));
- eventStream_->send(event);
+}
+
+void QtUserSearchWindow::handleContactSuggestionRequested(const QString& text) {
+ std::string stdText = Q2PSTRING(text);
+ onContactSuggestionsRequested(stdText);
+}
+
+void QtUserSearchWindow::addContact() {
+ if (firstMultiJIDPage_->jid_->getContact() != 0) {
+ Contact contact = *(firstMultiJIDPage_->jid_->getContact());
+ contactVector_.push_back(contact);
+ }
+ firstMultiJIDPage_->contactList_->setList(contactVector_);
+ firstMultiJIDPage_->emitCompletenessCheck();
+ if (type_ == ChatToContact) {
+ firstMultiJIDPage_->groupBox->setEnabled(supportsImpromptu_ ? 1 : (contactVector_.size() < 1));
}
}
int QtUserSearchWindow::nextId() const {
- switch (currentId()) {
- case 1: return firstPage_->byJID_->isChecked() ? (type_ == AddContact ? 4 : -1) : 2;
- case 2: return 3;
- case 3: return type_ == AddContact ? 4 : -1;
- case 4: return -1;
- default: return -1;
+ if (type_ == AddContact) {
+ switch (currentId()) {
+ case 1: return firstPage_->byJID_->isChecked() ? (type_ == AddContact ? 4 : -1) : 2;
+ case 2: return 3;
+ case 3: return type_ == AddContact ? 4 : -1;
+ case 4: return -1;
+ default: return -1;
+ }
+ } else {
+ switch (currentId()) {
+ case 1: return searchNext_ ? 2 : -1;
+ case 2: return 3;
+ case 3: return 1;
+ case 4: return -1;
+ default: return -1;
+ }
}
}
@@ -167,7 +227,15 @@ void QtUserSearchWindow::handleSearch() {
JID QtUserSearchWindow::getContactJID() const {
JID jid;
- if (!firstPage_->byJID_->isChecked()) {
+
+ bool useSearchResult;
+ if (type_ == AddContact) {
+ useSearchResult = !firstPage_->byJID_->isChecked();
+ } else {
+ useSearchResult = true;
+ }
+
+ if (useSearchResult) {
if (dynamic_cast<UserSearchModel*>(model_)) {
UserSearchResult* userItem = static_cast<UserSearchResult*>(resultsPage_->results_->currentIndex().internalPointer());
if (userItem) { /* Remember to leave this if we change to dynamic cast */
@@ -198,17 +266,35 @@ JID QtUserSearchWindow::getContactJID() const {
return jid;
}
+void QtUserSearchWindow::addSearchedJIDToList(const JID& jid) {
+ Contact contact(jid, jid.toString(), StatusShow::None, "");
+ contactVector_.push_back(contact);
+ firstMultiJIDPage_->contactList_->setList(contactVector_);
+ firstMultiJIDPage_->emitCompletenessCheck();
+ if (type_ == ChatToContact) {
+ firstMultiJIDPage_->groupBox->setEnabled(supportsImpromptu_ ? 1 : (contactVector_.size() < 1));
+ }
+}
+
void QtUserSearchWindow::show() {
clear();
QWidget::show();
}
void QtUserSearchWindow::addSavedServices(const std::vector<JID>& services) {
- firstPage_->service_->clear();
- foreach (JID jid, services) {
- firstPage_->service_->addItem(P2QSTRING(jid.toString()));
+ if (type_ == AddContact) {
+ firstPage_->service_->clear();
+ foreach (JID jid, services) {
+ firstPage_->service_->addItem(P2QSTRING(jid.toString()));
+ }
+ firstPage_->service_->clearEditText();
+ } else {
+ firstMultiJIDPage_->service_->clear();
+ foreach (JID jid, services) {
+ firstMultiJIDPage_->service_->addItem(P2QSTRING(jid.toString()));
+ }
+ firstMultiJIDPage_->service_->clearEditText();
}
- firstPage_->service_->clearEditText();
}
void QtUserSearchWindow::setSearchFields(boost::shared_ptr<SearchPayload> fields) {
@@ -246,6 +332,66 @@ void QtUserSearchWindow::prepopulateJIDAndName(const JID& jid, const std::string
detailsPage_->setName(name);
}
+void QtUserSearchWindow::setContactSuggestions(const std::vector<Contact>& suggestions) {
+ if (type_ == AddContact) {
+ firstPage_->jid_->setSuggestions(suggestions);
+ } else {
+ firstMultiJIDPage_->jid_->setSuggestions(suggestions);
+ }
+}
+
+void QtUserSearchWindow::setJIDs(const std::vector<JID> &jids) {
+ foreach(JID jid, jids) {
+ addSearchedJIDToList(jid);
+ }
+ onJIDUpdateRequested(jids);
+}
+
+void QtUserSearchWindow::setRoomJID(const JID& roomJID) {
+ roomJID_ = roomJID;
+}
+
+std::string QtUserSearchWindow::getReason() const {
+ return Q2PSTRING(firstMultiJIDPage_->reason_->text());
+}
+
+std::vector<JID> QtUserSearchWindow::getJIDs() const {
+ std::vector<JID> jids;
+ foreach (const Contact& contact, contactVector_) {
+ jids.push_back(contact.jid);
+ }
+ return jids;
+}
+
+void QtUserSearchWindow::setCanStartImpromptuChats(bool supportsImpromptu) {
+ supportsImpromptu_ = supportsImpromptu;
+ if (type_ == ChatToContact) {
+ firstMultiJIDPage_->contactList_->setMaximumNoOfContactsToOne(!supportsImpromptu_);
+ }
+}
+
+void QtUserSearchWindow::updateContacts(const std::vector<Contact>& contacts) {
+ if (type_ != AddContact) {
+ firstMultiJIDPage_->contactList_->updateContacts(contacts);
+ }
+}
+
+void QtUserSearchWindow::handleAddViaSearch() {
+ searchNext_ = true;
+ next();
+}
+
+void QtUserSearchWindow::handleListChanged(std::vector<Contact> list) {
+ contactVector_ = list;
+ if (type_ == ChatToContact) {
+ firstMultiJIDPage_->groupBox->setEnabled(supportsImpromptu_ ? 1 : (contactVector_.size() < 1));
+ }
+}
+
+void QtUserSearchWindow::handleJIDsAdded(std::vector<JID> jids) {
+ onJIDUpdateRequested(jids);
+}
+
void QtUserSearchWindow::setResults(const std::vector<UserSearchResult>& results) {
UserSearchModel *newModel = new UserSearchModel();
newModel->setResults(results);
@@ -279,6 +425,60 @@ void QtUserSearchWindow::setSelectedService(const JID& jid) {
myServer_ = jid;
}
+void QtUserSearchWindow::setFirstPage(QString title) {
+ if (page(1) != 0) {
+ removePage(1);
+ }
+ if (type_ == AddContact) {
+ firstPage_ = new QtUserSearchFirstPage(type_, title.isEmpty() ? firstPage_->title() : title, settings_);
+ connect(firstPage_->jid_, SIGNAL(textEdited(QString)), this, SLOT(handleContactSuggestionRequested(QString)));
+ connect(firstPage_->byJID_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
+ connect(firstPage_->byLocalSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
+ connect(firstPage_->byRemoteSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
+#if QT_VERSION >= 0x040700
+ firstPage_->jid_->setPlaceholderText(tr("alice@wonderland.lit"));
+#endif
+ firstPage_->service_->setEnabled(false);
+ setPage(1, firstPage_);
+ } else {
+ firstMultiJIDPage_ = new QtUserSearchFirstMultiJIDPage(type_, title.isEmpty() ? firstMultiJIDPage_->title() : title, settings_);
+ connect(firstMultiJIDPage_->addContactButton_, SIGNAL(clicked()), this, SLOT(addContact()));
+ connect(firstMultiJIDPage_->jid_, SIGNAL(textEdited(QString)), this, SLOT(handleContactSuggestionRequested(QString)));
+ 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>)));
+ setPage(1, firstMultiJIDPage_);
+ }
+}
+
+void QtUserSearchWindow::setSecondPage() {
+ if (page(2) != 0) {
+ removePage(2);
+ }
+ fieldsPage_ = new QtUserSearchFieldsPage();
+ fieldsPage_->fetchingThrobber_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this));
+ fieldsPage_->fetchingThrobber_->movie()->stop();
+ setPage(2, fieldsPage_);
+}
+
+void QtUserSearchWindow::setThirdPage() {
+ if (page(3) != 0) {
+ removePage(3);
+ }
+ resultsPage_ = new QtUserSearchResultsPage();
+
+#ifdef SWIFT_PLATFORM_MACOSX
+ resultsPage_->results_->setAlternatingRowColors(true);
+#endif
+ if (type_ == AddContact) {
+ connect(resultsPage_, SIGNAL(onUserTriggersContinue()), this, SLOT(next()));
+ }
+ else {
+ connect(resultsPage_, SIGNAL(onUserTriggersContinue()), this, SLOT(next()));
+ }
+ setPage(3, resultsPage_);
+}
+
void QtUserSearchWindow::clearForm() {
fieldsPage_->fetchingThrobber_->show();
fieldsPage_->fetchingThrobber_->movie()->start();
@@ -294,32 +494,48 @@ void QtUserSearchWindow::clearForm() {
}
void QtUserSearchWindow::clear() {
- firstPage_->errorLabel_->setVisible(false);
QString howText;
if (type_ == AddContact) {
+ firstPage_->errorLabel_->setVisible(false);
howText = QString(tr("How would you like to find the user to add?"));
+ firstPage_->howLabel_->setText(howText);
+ firstPage_->byJID_->setChecked(true);
+ handleFirstPageRadioChange();
+ } else {
+ contactVector_.clear();
+ firstMultiJIDPage_->contactList_->setList(contactVector_);
+ firstMultiJIDPage_->errorLabel_->setVisible(false);
+ if (type_ == ChatToContact) {
+ howText = QString(tr("Who would you like to chat to?"));
+ } else if (type_ == InviteToChat) {
+ howText = QString(tr("Who do you want to invite to the chat?"));
+ }
+ firstMultiJIDPage_->howLabel_->setText(howText);
}
- else {
- howText = QString(tr("How would you like to find the user to chat to?"));
- }
- firstPage_->howLabel_->setText(howText);
- firstPage_->byJID_->setChecked(true);
clearForm();
resultsPage_->results_->setModel(NULL);
delete model_;
model_ = NULL;
- handleFirstPageRadioChange();
restart();
lastPage_ = 1;
}
void QtUserSearchWindow::setError(const QString& error) {
if (error.isEmpty()) {
- firstPage_->errorLabel_->hide();
+ if (type_ == AddContact) {
+ firstPage_->errorLabel_->hide();
+ } else {
+ firstMultiJIDPage_->errorLabel_->hide();
+ }
}
else {
- firstPage_->errorLabel_->setText(QString("<font color='red'>%1</font>").arg(error));
- firstPage_->errorLabel_->show();
+ if (type_ == AddContact) {
+ firstPage_->errorLabel_->setText(QString("<font color='red'>%1</font>").arg(error));
+ firstPage_->errorLabel_->show();
+ } else {
+ firstMultiJIDPage_->errorLabel_->setText(QString("<font color='red'>%1</font>").arg(error));
+ firstMultiJIDPage_->errorLabel_->show();
+ }
restart();
lastPage_ = 1;
}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
index 32e851a..e5a9f80 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
@@ -18,15 +18,17 @@ namespace Swift {
class UserSearchResult;
class UIEventStream;
class QtUserSearchFirstPage;
+ class QtUserSearchFirstMultiJIDPage;
class QtUserSearchFieldsPage;
class QtUserSearchResultsPage;
class QtUserSearchDetailsPage;
class QtFormResultItemModel;
+ class SettingsProvider;
class QtUserSearchWindow : public QWizard, public UserSearchWindow, private Ui::QtUserSearchWizard {
Q_OBJECT
public:
- QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups);
+ QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups, SettingsProvider* settingsProvider);
virtual ~QtUserSearchWindow();
virtual void addSavedServices(const std::vector<JID>& services);
@@ -41,19 +43,39 @@ namespace Swift {
virtual void setSearchFields(boost::shared_ptr<SearchPayload> fields);
virtual void setNameSuggestions(const std::vector<std::string>& suggestions);
virtual void prepopulateJIDAndName(const JID& jid, const std::string& name);
+ virtual void setContactSuggestions(const std::vector<Contact>& suggestions);
+ virtual void setJIDs(const std::vector<JID> &jids);
+ virtual void setRoomJID(const JID &roomJID);
+ virtual std::string getReason() const;
+ virtual std::vector<JID> getJIDs() const;
+ virtual void setCanStartImpromptuChats(bool supportsImpromptu);
+ virtual void updateContacts(const std::vector<Contact> &contacts);
protected:
virtual int nextId() const;
+
private slots:
void handleFirstPageRadioChange();
virtual void handleCurrentChanged(int);
virtual void handleAccepted();
+ void handleContactSuggestionRequested(const QString& text);
+ void addContact();
+ void handleAddViaSearch();
+ void handleListChanged(std::vector<Contact> list);
+ void handleJIDsAdded(std::vector<JID> jids);
+
+ private:
+ void setFirstPage(QString title = "");
+ void setSecondPage();
+ void setThirdPage();
+
private:
void clearForm();
void setError(const QString& error);
JID getServerToSearch();
void handleSearch();
JID getContactJID() const;
+ void addSearchedJIDToList(const JID& jid);
private:
UIEventStream* eventStream_;
@@ -61,10 +83,16 @@ namespace Swift {
QAbstractItemModel* model_;
UserSearchDelegate* delegate_;
QtUserSearchFirstPage* firstPage_;
+ QtUserSearchFirstMultiJIDPage* firstMultiJIDPage_;
QtUserSearchFieldsPage* fieldsPage_;
QtUserSearchResultsPage* resultsPage_;
QtUserSearchDetailsPage* detailsPage_;
JID myServer_;
+ JID roomJID_;
int lastPage_;
+ std::vector<Contact> contactVector_;
+ SettingsProvider* settings_;
+ bool searchNext_;
+ bool supportsImpromptu_;
};
}