summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/QtUI')
-rw-r--r--Swift/QtUI/CAPICertificateSelector.cpp33
-rw-r--r--Swift/QtUI/ChatList/QtChatListWindow.cpp25
-rw-r--r--Swift/QtUI/ChatList/QtChatListWindow.h6
-rw-r--r--Swift/QtUI/ChattablesModel.cpp63
-rw-r--r--Swift/QtUI/ChattablesModel.h34
-rw-r--r--Swift/QtUI/FlowLayout.cpp7
-rw-r--r--Swift/QtUI/QtAboutWidget.cpp4
-rw-r--r--Swift/QtUI/QtBookmarkDetailWindow.cpp8
-rw-r--r--Swift/QtUI/QtBookmarkDetailWindow.ui23
-rw-r--r--Swift/QtUI/QtCachedImageScaler.cpp2
-rw-r--r--Swift/QtUI/QtChatOverview.cpp66
-rw-r--r--Swift/QtUI/QtChatOverview.h28
-rw-r--r--Swift/QtUI/QtChatOverviewBundle.cpp146
-rw-r--r--Swift/QtUI/QtChatOverviewBundle.h61
-rw-r--r--Swift/QtUI/QtChatOverviewDelegate.cpp88
-rw-r--r--Swift/QtUI/QtChatOverviewDelegate.h23
-rw-r--r--Swift/QtUI/QtChatTabs.cpp24
-rw-r--r--Swift/QtUI/QtChatTabs.h6
-rw-r--r--Swift/QtUI/QtChatTabsShortcutOnlySubstitute.cpp108
-rw-r--r--Swift/QtUI/QtChatTabsShortcutOnlySubstitute.h40
-rw-r--r--Swift/QtUI/QtChatWindow.cpp117
-rw-r--r--Swift/QtUI/QtChatWindow.h17
-rw-r--r--Swift/QtUI/QtChatWindowFactory.cpp22
-rw-r--r--Swift/QtUI/QtChatWindowFactory.h8
-rw-r--r--Swift/QtUI/QtColorToolButton.cpp1
-rw-r--r--Swift/QtUI/QtDBUSURIHandler.cpp4
-rw-r--r--Swift/QtUI/QtEditBookmarkWindow.cpp1
-rw-r--r--Swift/QtUI/QtEmojiCell.cpp15
-rw-r--r--Swift/QtUI/QtEmojisScroll.h4
-rw-r--r--Swift/QtUI/QtEmojisSelector.cpp9
-rw-r--r--Swift/QtUI/QtEmojisSelector.h4
-rw-r--r--Swift/QtUI/QtExpandedListView.cpp111
-rw-r--r--Swift/QtUI/QtExpandedListView.h34
-rw-r--r--Swift/QtUI/QtFdpFormSubmitWindow.cpp215
-rw-r--r--Swift/QtUI/QtFdpFormSubmitWindow.h75
-rw-r--r--Swift/QtUI/QtFormWidget.cpp2
-rw-r--r--Swift/QtUI/QtHighlightNotificationConfigDialog.cpp6
-rw-r--r--Swift/QtUI/QtHistoryWindow.cpp4
-rw-r--r--Swift/QtUI/QtJoinMUCWindow.cpp2
-rw-r--r--Swift/QtUI/QtJoinMUCWindow.ui8
-rw-r--r--Swift/QtUI/QtListWidget.cpp20
-rw-r--r--Swift/QtUI/QtListWidget.h23
-rw-r--r--Swift/QtUI/QtLoginWindow.cpp4
-rw-r--r--Swift/QtUI/QtLoginWindow.h5
-rw-r--r--Swift/QtUI/QtMainWindow.cpp37
-rw-r--r--Swift/QtUI/QtMainWindow.h48
-rw-r--r--Swift/QtUI/QtPlainChatView.cpp8
-rw-r--r--Swift/QtUI/QtScaledAvatarCache.cpp8
-rw-r--r--Swift/QtUI/QtSingleWindow.cpp91
-rw-r--r--Swift/QtUI/QtSingleWindow.h25
-rw-r--r--Swift/QtUI/QtSoundSelectionStyledItemDelegate.h1
-rw-r--r--Swift/QtUI/QtSpellCheckerWindow.cpp4
-rw-r--r--Swift/QtUI/QtStatusWidget.cpp12
-rw-r--r--Swift/QtUI/QtStrings.h12
-rw-r--r--Swift/QtUI/QtSwift.cpp267
-rw-r--r--Swift/QtUI/QtSwift.h47
-rw-r--r--Swift/QtUI/QtTabWidget.cpp4
-rw-r--r--Swift/QtUI/QtTabbable.h2
-rw-r--r--Swift/QtUI/QtTextEdit.cpp4
-rw-r--r--Swift/QtUI/QtUIFactory.cpp100
-rw-r--r--Swift/QtUI/QtUIFactory.h40
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h4
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp4
-rw-r--r--Swift/QtUI/QtWebKitChatView.cpp80
-rw-r--r--Swift/QtUI/QtWebKitChatView.h10
-rw-r--r--Swift/QtUI/QtWebView.cpp6
-rw-r--r--Swift/QtUI/Roster/QtFilterWidget.cpp2
-rw-r--r--Swift/QtUI/SConscript246
-rw-r--r--Swift/QtUI/ServerList/QtServerListView.cpp31
-rw-r--r--Swift/QtUI/ServerList/QtServerListView.h25
-rw-r--r--Swift/QtUI/ServerList/ServerListDelegate.cpp117
-rw-r--r--Swift/QtUI/ServerList/ServerListDelegate.h31
-rw-r--r--Swift/QtUI/ServerList/ServerListModel.cpp68
-rw-r--r--Swift/QtUI/ServerList/ServerListModel.h87
-rw-r--r--Swift/QtUI/Trellis/QtDynamicGridLayout.cpp33
-rw-r--r--Swift/QtUI/Trellis/QtDynamicGridLayout.h4
-rw-r--r--Swift/QtUI/Trellis/QtGridSelectionDialog.cpp6
-rw-r--r--Swift/QtUI/UserSearch/ContactListModel.cpp28
-rw-r--r--Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp2
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchWindow.cpp1
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchWindow.h3
-rw-r--r--Swift/QtUI/main.cpp2
82 files changed, 2276 insertions, 730 deletions
diff --git a/Swift/QtUI/CAPICertificateSelector.cpp b/Swift/QtUI/CAPICertificateSelector.cpp
index 36d8c54..7e4bc0b 100644
--- a/Swift/QtUI/CAPICertificateSelector.cpp
+++ b/Swift/QtUI/CAPICertificateSelector.cpp
@@ -18,7 +18,9 @@
#include <boost/algorithm/string.hpp>
#include <Swift/Controllers/Intl.h>
#include <Swift/QtUI/QtSwiftUtil.h>
+
#include <Swiften/Base/Log.h>
+#include <Swiften/TLS/Schannel/SchannelUtil.h>
namespace Swift {
@@ -78,32 +80,31 @@ std::string selectCAPICertificate() {
if (titleLength == 0 || promptLength == 0) {
int error = GetLastError();
switch (error) {
- case ERROR_INSUFFICIENT_BUFFER: SWIFT_LOG(error) << "Insufficient buffer for rendering cert dialog" << std::endl;break;
- case ERROR_INVALID_FLAGS: SWIFT_LOG(error) << "Invalid flags for rendering cert dialog" << std::endl;break;
- case ERROR_INVALID_PARAMETER: SWIFT_LOG(error) << "Invalid parameter for rendering cert dialog" << std::endl;break;
- case ERROR_NO_UNICODE_TRANSLATION: SWIFT_LOG(error) << "Invalid unicode for rendering cert dialog" << std::endl;break;
- default: SWIFT_LOG(error) << "Unexpected multibyte conversion errorcode" << std::endl;
+ case ERROR_INSUFFICIENT_BUFFER: SWIFT_LOG(error) << "Insufficient buffer for rendering cert dialog"; break;
+ case ERROR_INVALID_FLAGS: SWIFT_LOG(error) << "Invalid flags for rendering cert dialog"; break;
+ case ERROR_INVALID_PARAMETER: SWIFT_LOG(error) << "Invalid parameter for rendering cert dialog"; break;
+ case ERROR_NO_UNICODE_TRANSLATION: SWIFT_LOG(error) << "Invalid unicode for rendering cert dialog"; break;
+ default: SWIFT_LOG(error) << "Unexpected multibyte conversion errorcode";
}
}
-
-
+ std::string result;
/* Call Windows dialog to select a suitable certificate */
- PCCERT_CONTEXT cert = CryptUIDlgSelectCertificateFromStore(hstore, hwnd, titleChars, promptChars, exclude_columns, 0, NULL);
+ {
+ ScopedCertContext cert(CryptUIDlgSelectCertificateFromStore(hstore, hwnd, titleChars, promptChars, exclude_columns, 0, NULL));
+ if (cert) {
+ result = getCertUri(cert, certStoreName);
+ }
+ }
delete[] titleChars;
delete[] promptChars;
if (hstore) {
- CertCloseStore(hstore, 0);
- }
-
- std::string result;
-
- if (cert) {
- result = getCertUri(cert, certStoreName);
- CertFreeCertificateContext(cert);
+ if (CertCloseStore(hstore, 0) == FALSE) {
+ SWIFT_LOG(debug) << "Failed to close the certificate store handle.";
+ }
}
return result;
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp
index 3caed57..2fd05c4 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.cpp
+++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp
@@ -86,11 +86,9 @@ void QtChatListWindow::handleClicked(const QModelIndex& index) {
void QtChatListWindow::setupContextMenus() {
mucMenu_ = new QMenu();
- onlineOnlyActions_ << mucMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark()));
onlineOnlyActions_ << mucMenu_->addAction(tr("Edit Bookmark"), this, SLOT(handleEditBookmark()));
onlineOnlyActions_ << mucMenu_->addAction(tr("Remove Bookmark"), this, SLOT(handleRemoveBookmark()));
emptyMenu_ = new QMenu();
- onlineOnlyActions_ << emptyMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark()));
}
void QtChatListWindow::handleItemActivated(const QModelIndex& index) {
@@ -132,7 +130,7 @@ void QtChatListWindow::setRecents(const std::list<ChatListWindow::Chat>& recents
model_->setRecents(recents);
}
-void QtChatListWindow::setUnreadCount(int unread) {
+void QtChatListWindow::setUnreadCount(size_t unread) {
emit onCountUpdated(unread);
}
@@ -146,22 +144,6 @@ void QtChatListWindow::handleRemoveBookmark() {
eventStream_->send(std::make_shared<RemoveMUCBookmarkUIEvent>(mucItem->getBookmark()));
}
-void QtChatListWindow::handleAddBookmarkFromRecents() {
- const ChatListRecentItem* item = dynamic_cast<const ChatListRecentItem*>(contextMenuItem_);
- if (item) {
- const ChatListWindow::Chat& chat = item->getChat();
- MUCBookmark bookmark(chat.jid, chat.jid.toBare().toString());
- bookmark.setNick(chat.nick);
- bookmark.setPassword(chat.password);
- eventStream_->send(std::make_shared<AddMUCBookmarkUIEvent>(bookmark));
- }
-}
-
-void QtChatListWindow::handleAddBookmark() {
- (new QtAddBookmarkWindow(eventStream_))->show();
-}
-
-
void QtChatListWindow::handleEditBookmark() {
const ChatListMUCItem* mucItem = dynamic_cast<const ChatListMUCItem*>(contextMenuItem_);
if (!mucItem) return;
@@ -208,11 +190,8 @@ void QtChatListWindow::contextMenuEvent(QContextMenuEvent* event) {
if (mucItem) {
contextMenuItem_ = mucItem;
bookmarkAction = mucRecentsMenu.addAction(tr("Edit Bookmark"), this, SLOT(handleEditBookmark()));
+ bookmarkAction->setEnabled(isOnline_);
}
- else {
- bookmarkAction = mucRecentsMenu.addAction(tr("Add to Bookmarks"), this, SLOT(handleAddBookmarkFromRecents()));
- }
- bookmarkAction->setEnabled(isOnline_);
mucRecentsMenu.addAction(tr("Clear recents"), this, SLOT(handleClearRecentsRequested()));
mucRecentsMenu.exec(QCursor::pos());
return;
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h
index 834e318..3322001 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.h
+++ b/Swift/QtUI/ChatList/QtChatListWindow.h
@@ -27,18 +27,16 @@ namespace Swift {
void removeWhiteboardSession(const JID& jid);
void setBookmarksEnabled(bool enabled);
void setRecents(const std::list<ChatListWindow::Chat>& recents);
- void setUnreadCount(int unread);
+ void setUnreadCount(size_t unread);
void clearBookmarks();
virtual void setOnline(bool isOnline);
signals:
- void onCountUpdated(int count);
+ void onCountUpdated(size_t count);
private slots:
void handleItemActivated(const QModelIndex&);
- void handleAddBookmark();
void handleEditBookmark();
void handleRemoveBookmark();
- void handleAddBookmarkFromRecents();
void handleClicked(const QModelIndex& index);
void handleSettingChanged(const std::string& setting);
void handleClearRecentsRequested();
diff --git a/Swift/QtUI/ChattablesModel.cpp b/Swift/QtUI/ChattablesModel.cpp
new file mode 100644
index 0000000..67d0579
--- /dev/null
+++ b/Swift/QtUI/ChattablesModel.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/ChattablesModel.h>
+
+#include <QDebug>
+
+#include <Swift/Controllers/Chat/Chattables.h>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+ChattablesModel::ChattablesModel(Chattables& chattables, QObject* parent) : QAbstractListModel(parent), chattables_(chattables) {
+ using scoped_connection = boost::signals2::scoped_connection;
+ connectionList_.emplace_back(std::make_unique<scoped_connection>(
+ chattables_.onBeginAdd.connect([this](int index) {beginInsertRows(QModelIndex(), index, index);})
+ ));
+ connectionList_.emplace_back(std::make_unique<scoped_connection>(
+ chattables_.onAdded.connect([this]() {endInsertRows();})
+ ));
+ connectionList_.emplace_back(std::make_unique<scoped_connection>(
+ chattables_.onChanged.connect(
+ [this](const JID& jid, int index) {
+ auto modelIndex = createIndex(index, 0, const_cast<JID*>(&jid));
+ dataChanged(modelIndex, modelIndex, {});
+ }
+ )
+ ));
+}
+
+int ChattablesModel::rowCount(const QModelIndex& /*parent*/) const {
+ return chattables_.get().size();
+}
+
+QVariant ChattablesModel::data(const QModelIndex& index, int role) const {
+ //FIXME: Check validity
+ auto state = chattables_.getState(chattables_.get()[index.row()]);
+ if (role == Qt::DisplayRole) {
+ return P2QSTRING((state.name.empty() ? state.jid.toString() : state.name));
+ }
+ if (role == UnreadCountRole) {
+ return QString::number(state.unreadCount);
+ }
+ if (role == TypeRole) {
+ switch (state.type) {
+ case Chattables::State::Type::Room: return "ROOM";
+ case Chattables::State::Type::Person: return "PERSON";
+ }
+ }
+ if (role == StatusRole) {
+ return QVariant(static_cast<int>(state.status));
+ }
+ if (role == JIDRole) {
+ return P2QSTRING(state.jid.toString());
+ }
+ return QVariant();
+}
+
+}
diff --git a/Swift/QtUI/ChattablesModel.h b/Swift/QtUI/ChattablesModel.h
new file mode 100644
index 0000000..6617d97
--- /dev/null
+++ b/Swift/QtUI/ChattablesModel.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <boost/signals2/connection.hpp>
+
+#include <QAbstractListModel>
+
+namespace Swift {
+class Chattables;
+class ChattablesModel : public QAbstractListModel {
+ public:
+ enum ChattablesRoles {
+ UnreadCountRole = Qt::UserRole,
+ TypeRole = Qt::UserRole + 1,
+ StatusRole = Qt::UserRole + 2,
+ JIDRole = Qt::UserRole + 3
+ };
+ ChattablesModel(Chattables& chattables, QObject* parent);
+ int rowCount(const QModelIndex& parent = QModelIndex()) const;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+ private:
+ Chattables& chattables_;
+ std::vector<std::unique_ptr<boost::signals2::scoped_connection>> connectionList_;
+};
+
+}
diff --git a/Swift/QtUI/FlowLayout.cpp b/Swift/QtUI/FlowLayout.cpp
index c42b7e1..8a12841 100644
--- a/Swift/QtUI/FlowLayout.cpp
+++ b/Swift/QtUI/FlowLayout.cpp
@@ -49,7 +49,6 @@
****************************************************************************/
#include <QtWidgets>
-
#include "FlowLayout.h"
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
@@ -108,12 +107,12 @@ QLayoutItem *FlowLayout::takeAt(int index)
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
- return 0;
+ return nullptr;
}
Qt::Orientations FlowLayout::expandingDirections() const
{
- return 0;
+ return nullptr;
}
bool FlowLayout::hasHeightForWidth() const
@@ -192,7 +191,7 @@ int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
return -1;
} else if (parent->isWidgetType()) {
QWidget *pw = static_cast<QWidget *>(parent);
- return pw->style()->pixelMetric(pm, 0, pw);
+ return pw->style()->pixelMetric(pm, nullptr, pw);
} else {
return static_cast<QLayout *>(parent)->spacing();
}
diff --git a/Swift/QtUI/QtAboutWidget.cpp b/Swift/QtUI/QtAboutWidget.cpp
index 2db0c9d..0a4e0ba 100644
--- a/Swift/QtUI/QtAboutWidget.cpp
+++ b/Swift/QtUI/QtAboutWidget.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -150,7 +150,7 @@ void QtAboutWidget::openPlainTextWindow(const QString& path) {
text->activateWindow();
}
else {
- SWIFT_LOG(error) << "Failed to open " << Q2PSTRING(path) << "." << std::endl;
+ SWIFT_LOG(error) << "Failed to open " << Q2PSTRING(path) << ".";
}
}
diff --git a/Swift/QtUI/QtBookmarkDetailWindow.cpp b/Swift/QtUI/QtBookmarkDetailWindow.cpp
index 920e94e..efa0e25 100644
--- a/Swift/QtUI/QtBookmarkDetailWindow.cpp
+++ b/Swift/QtUI/QtBookmarkDetailWindow.cpp
@@ -42,7 +42,7 @@ boost::optional<MUCBookmark> QtBookmarkDetailWindow::createBookmarkFromForm() {
MUCBookmark bookmark(room, name);
std::string nick(Q2PSTRING(nick_->text()));
std::string password(Q2PSTRING(password_->text()));
- bookmark.setAutojoin(autojoin_->isChecked());
+ bookmark.setAutojoin(true);
if (!nick.empty()) {
bookmark.setNick(nick);
}
@@ -68,12 +68,6 @@ void QtBookmarkDetailWindow::createFormFromBookmark(const MUCBookmark& bookmark)
if (bookmark.getPassword()) {
password_->setText(P2QSTRING((*bookmark.getPassword())));
}
-
- if (bookmark.getAutojoin()) {
- autojoin_->setCheckState(Qt::Checked);
- } else {
- autojoin_->setCheckState(Qt::Unchecked);
- }
}
}
diff --git a/Swift/QtUI/QtBookmarkDetailWindow.ui b/Swift/QtUI/QtBookmarkDetailWindow.ui
index be55686..affb7e4 100644
--- a/Swift/QtUI/QtBookmarkDetailWindow.ui
+++ b/Swift/QtUI/QtBookmarkDetailWindow.ui
@@ -82,29 +82,6 @@
<item row="3" column="1">
<widget class="QLineEdit" name="password_"/>
</item>
- <item row="4" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="4" column="1">
- <widget class="QCheckBox" name="autojoin_">
- <property name="text">
- <string>Enter automatically</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
</layout>
</item>
<item>
diff --git a/Swift/QtUI/QtCachedImageScaler.cpp b/Swift/QtUI/QtCachedImageScaler.cpp
index e1f540b..445d113 100644
--- a/Swift/QtUI/QtCachedImageScaler.cpp
+++ b/Swift/QtUI/QtCachedImageScaler.cpp
@@ -21,7 +21,7 @@ QtCachedImageScaler::QtCachedImageScaler() {
boost::filesystem::path QtCachedImageScaler::getScaledImage(const boost::filesystem::path& imagePath, int size) {
boost::filesystem::path scaledImagePath(imagePath);
- std::string suffix = "." + boost::lexical_cast<std::string>(size);
+ std::string suffix = "." + std::to_string(size);
scaledImagePath = stringToPath(pathToString(scaledImagePath) + suffix);
if (!boost::filesystem::exists(scaledImagePath)) {
QImage image(P2QSTRING(pathToString(imagePath)));
diff --git a/Swift/QtUI/QtChatOverview.cpp b/Swift/QtUI/QtChatOverview.cpp
new file mode 100644
index 0000000..76943e9
--- /dev/null
+++ b/Swift/QtUI/QtChatOverview.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtChatOverview.h>
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPalette>
+#include <QVBoxLayout>
+
+#include <Swift/Controllers/Chat/Chattables.h>
+
+#include <Swift/QtUI/ChattablesModel.h>
+#include <Swift/QtUI/QtChatOverviewBundle.h>
+
+namespace Swift {
+
+QtChatOverview::QtChatOverview(Chattables& chattables, QWidget* parent) : QWidget(parent), chattables_(chattables) {
+ QPalette newPalette = palette();
+ newPalette.setColor(QPalette::Background, {38, 81, 112});
+ setAutoFillBackground(true);
+ newPalette.setColor(QPalette::Foreground, {255, 255, 255});
+ setPalette(newPalette);
+
+ auto mainLayout = new QVBoxLayout();
+ setLayout(mainLayout);
+
+ auto headerLayout = new QHBoxLayout();
+ mainLayout->addLayout(headerLayout);
+ auto allLabel = new QLabel(tr("All"), this);
+ allLabel->setStyleSheet("color: white;");
+ auto peopleLabel = new QLabel(tr("People"), this);
+ peopleLabel->setStyleSheet("color: white;");
+ auto roomsLabel = new QLabel(tr("Rooms"), this);
+ roomsLabel->setStyleSheet("color: white;");
+ headerLayout->addWidget(allLabel);
+ headerLayout->addWidget(peopleLabel);
+ headerLayout->addWidget(roomsLabel);
+
+ rootModel_ = new ChattablesModel(chattables_, this);
+
+ auto unreadBundle = new QtChatOverviewBundle(rootModel_, "UNREAD", true, this);
+ connect(unreadBundle, SIGNAL(clicked(JID)), this, SLOT(handleItemClicked(JID)));
+ mainLayout->addWidget(unreadBundle);
+
+ auto peopleBundle = new QtChatOverviewBundle(rootModel_, "PEOPLE", false, this);
+ connect(peopleBundle, SIGNAL(clicked(JID)), this, SLOT(handleItemClicked(JID)));
+ mainLayout->addWidget(peopleBundle);
+
+ auto roomsBundle = new QtChatOverviewBundle(rootModel_, "ROOMS", false, this);
+ connect(roomsBundle, SIGNAL(clicked(JID)), this, SLOT(handleItemClicked(JID)));
+ mainLayout->addWidget(roomsBundle);
+
+ mainLayout->addStretch();
+}
+
+QtChatOverview::~QtChatOverview() {}
+
+void QtChatOverview::handleItemClicked(JID jid) {
+ chattables_.onActivated(jid);
+}
+
+} // namespace Swift
diff --git a/Swift/QtUI/QtChatOverview.h b/Swift/QtUI/QtChatOverview.h
new file mode 100644
index 0000000..8cd7762
--- /dev/null
+++ b/Swift/QtUI/QtChatOverview.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QWidget>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+ class Chattables;
+ class ChattablesModel;
+ class QtChatOverview : public QWidget {
+ Q_OBJECT
+ public:
+ QtChatOverview(Chattables&, QWidget* parent);
+ ~QtChatOverview() override;
+
+ private slots:
+ void handleItemClicked(JID jid);
+ private:
+ Chattables& chattables_;
+ ChattablesModel* rootModel_;
+ };
+}
diff --git a/Swift/QtUI/QtChatOverviewBundle.cpp b/Swift/QtUI/QtChatOverviewBundle.cpp
new file mode 100644
index 0000000..121ae2e
--- /dev/null
+++ b/Swift/QtUI/QtChatOverviewBundle.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtChatOverviewBundle.h>
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPalette>
+#include <QSortFilterProxyModel>
+#include <QVBoxLayout>
+
+#include <Swiften/Elements/StatusShow.h>
+
+#include <Swift/QtUI/ChattablesModel.h>
+#include <Swift/QtUI/QtChatOverviewDelegate.h>
+#include <Swift/QtUI/QtClickableLabel.h>
+#include <Swift/QtUI/QtExpandedListView.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+BundleFilter::BundleFilter(QObject* parent) : QSortFilterProxyModel(parent) {
+ sort(0, Qt::AscendingOrder);
+ setDynamicSortFilter(true);
+ setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
+}
+
+void BundleFilter::addFilter(Filter filter) {
+ filters_.emplace(filter);
+ invalidateFilter();
+}
+
+bool BundleFilter::hasFilter(Filter filter) {
+ return filters_.count(filter) > 0;
+}
+
+void BundleFilter::removeFilter(Filter filter) {
+ filters_.erase(filter);
+ invalidateFilter();
+}
+
+bool BundleFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const {
+ auto row = sourceModel()->index(sourceRow, 0, sourceParent);
+ if (filters_.count(Filter::Unread)) {
+ if (row.data(ChattablesModel::UnreadCountRole).toInt() == 0) {
+ return false;
+ }
+ }
+ if (filters_.count(Filter::People)) {
+ if (row.data(ChattablesModel::TypeRole).toString() != "PERSON") {
+ return false;
+ }
+ }
+ if (filters_.count(Filter::Rooms)) {
+ if (row.data(ChattablesModel::TypeRole).toString() != "ROOM") {
+ return false;
+ }
+ }
+ if (filters_.count(Filter::Online)) {
+ if (static_cast<StatusShow::Type>(row.data(ChattablesModel::StatusRole).toInt()) == StatusShow::None) {
+ return false;
+ }
+ }
+ return true;
+}
+
+QtChatOverviewBundle::QtChatOverviewBundle(ChattablesModel* rootModel, QString name, bool hideWhenEmpty, QWidget* parent) : QWidget(parent), rootModel_(rootModel), hideWhenEmpty_(hideWhenEmpty) {
+ proxyModel_ = new BundleFilter(this);
+ if (name == "UNREAD") { // FIXME: Obviously needs a better approach
+ proxyModel_->addFilter(BundleFilter::Filter::Unread);
+ }
+ if (name == "PEOPLE") {
+ proxyModel_->addFilter(BundleFilter::Filter::People);
+ proxyModel_->addFilter(BundleFilter::Filter::Online);
+ }
+ if (name == "ROOMS") {
+ proxyModel_->addFilter(BundleFilter::Filter::Rooms);
+ proxyModel_->addFilter(BundleFilter::Filter::Online);
+ }
+ proxyModel_->setSourceModel(rootModel);
+
+
+ auto mainLayout = new QVBoxLayout();
+ setLayout(mainLayout);
+
+ auto headerLayout = new QHBoxLayout();
+ mainLayout->addLayout(headerLayout);
+ auto nameLabel = new QLabel(name, this);
+ nameLabel->setStyleSheet("color: white;");
+ headerLayout->addWidget(nameLabel);
+ headerLayout->addStretch();
+ if (!hideWhenEmpty_) {
+ filterLabel_ = new QtClickableLabel(this);
+ filterLabel_->setText(tr("Online"));
+ filterLabel_->setStyleSheet("color: white;");
+ headerLayout->addWidget(filterLabel_);
+ connect(filterLabel_, SIGNAL(clicked()), this, SLOT(handleFilterClicked()));
+ }
+ listView_ = new QtExpandedListView(this);
+ listView_->setModel(proxyModel_);
+ listView_->setFrameStyle(QFrame::NoFrame);
+ listView_->setItemDelegate(new QtChatOverviewDelegate(this));
+ connect(listView_, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleItemClicked(const QModelIndex&)));
+ mainLayout->addWidget(listView_);
+
+ if (hideWhenEmpty_) {
+ connect(proxyModel_, &QAbstractItemModel::modelReset, this, [&](){
+ updateVisibility();
+ });
+ connect(proxyModel_, &QAbstractItemModel::rowsInserted, this, [&](){
+ updateVisibility();
+ });
+ connect(proxyModel_, &QAbstractItemModel::rowsRemoved, this, [&](){
+ updateVisibility();
+ });
+ updateVisibility();
+ }
+}
+
+QtChatOverviewBundle::~QtChatOverviewBundle() {}
+
+void QtChatOverviewBundle::handleFilterClicked() {
+ if (proxyModel_->hasFilter(BundleFilter::Filter::Online)) {
+ proxyModel_->removeFilter(BundleFilter::Filter::Online);
+ filterLabel_->setText(tr("All"));
+ }
+ else {
+ proxyModel_->addFilter(BundleFilter::Filter::Online);
+ filterLabel_->setText(tr("Online"));
+ }
+}
+
+void QtChatOverviewBundle::updateVisibility() {
+ auto shouldBeVisible = (proxyModel_->rowCount(listView_->rootIndex()) > 0);
+ setVisible(shouldBeVisible);
+}
+
+
+void QtChatOverviewBundle::handleItemClicked(const QModelIndex& index) {
+ clicked(JID(Q2PSTRING(index.data(ChattablesModel::JIDRole).toString())));
+}
+
+} // namespace Swift
diff --git a/Swift/QtUI/QtChatOverviewBundle.h b/Swift/QtUI/QtChatOverviewBundle.h
new file mode 100644
index 0000000..95fd5d2
--- /dev/null
+++ b/Swift/QtUI/QtChatOverviewBundle.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <set>
+
+#include <QSortFilterProxyModel>
+#include <QString>
+#include <QWidget>
+
+#include <Swiften/JID/JID.h>
+
+class QListView;
+
+namespace Swift {
+ class ChattablesModel;
+ class QtClickableLabel;
+ class QtExpandedListView;
+
+ class BundleFilter : public QSortFilterProxyModel {
+ Q_OBJECT
+ public:
+ enum class Filter {Unread, People, Rooms, Online};
+ BundleFilter(QObject* parent);
+ void addFilter(Filter);
+ bool hasFilter(Filter);
+ void removeFilter(Filter);
+ protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
+ private:
+ std::set<Filter> filters_;
+ };
+
+ class QtChatOverviewBundle : public QWidget {
+ Q_OBJECT
+ public:
+ QtChatOverviewBundle(ChattablesModel*, QString name, bool hideWhenEmpty, QWidget* parent);
+ ~QtChatOverviewBundle() override;
+
+ signals:
+ void clicked(JID jid);
+
+ private slots:
+ void handleFilterClicked();
+ void handleItemClicked(const QModelIndex&);
+
+ private:
+ void updateVisibility();
+
+ private:
+ ChattablesModel* rootModel_;
+ QtExpandedListView* listView_;
+ BundleFilter* proxyModel_;
+ bool hideWhenEmpty_;
+ QtClickableLabel* filterLabel_ = nullptr;
+ };
+}
diff --git a/Swift/QtUI/QtChatOverviewDelegate.cpp b/Swift/QtUI/QtChatOverviewDelegate.cpp
new file mode 100644
index 0000000..00821fe
--- /dev/null
+++ b/Swift/QtUI/QtChatOverviewDelegate.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtChatOverviewDelegate.h>
+
+#include <Swiften/Elements/StatusShow.h>
+
+#include <Swift/QtUI/ChattablesModel.h>
+#include <Swift/QtUI/Roster/DelegateCommons.h>
+
+namespace Swift {
+
+QtChatOverviewDelegate::QtChatOverviewDelegate(QObject* parent) : QItemDelegate(parent), nameFont(QApplication::font()) {
+
+}
+
+QtChatOverviewDelegate::~QtChatOverviewDelegate() {}
+
+QSize QtChatOverviewDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const {
+ int heightByAvatar = DelegateCommons::avatarSize + DelegateCommons::verticalMargin * 2;
+ QFontMetrics nameMetrics(nameFont);
+ int sizeByText = 2 * DelegateCommons::verticalMargin + nameMetrics.height();
+ return QSize(150, sizeByText > heightByAvatar ? sizeByText : heightByAvatar);
+}
+
+void QtChatOverviewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
+ painter->save();
+ QRect fullRegion(option.rect);
+ const int statusCircleRadius = 3;
+ const int horizontalMargin = 4;
+
+ QColor bgColor(38, 81, 112);
+ QPen fontPen("white"); // FIXME
+
+ if (option.state & QStyle::State_Selected) {
+ //FIXME
+ }
+ painter->fillRect(fullRegion, bgColor);
+ painter->setPen(fontPen);
+
+ QFontMetrics nameMetrics(nameFont);
+ painter->setFont(nameFont);
+ QRect nameRegion(fullRegion.adjusted((horizontalMargin + statusCircleRadius) * 2, DelegateCommons::verticalMargin, 0, 0));
+ DelegateCommons::drawElidedText(painter, nameRegion, index.data(Qt::DisplayRole).toString());
+
+ const auto green = QColor(124, 243, 145);
+ const auto yellow = QColor(243, 243, 0);
+ const auto red = QColor(255, 45, 71);
+ const auto grey = QColor(159,159,159);
+ auto circleColour = grey;
+ auto status = static_cast<StatusShow::Type>(index.data(ChattablesModel::StatusRole).toInt());
+ switch (status) {
+ case StatusShow::Online: circleColour = green; break;
+ case StatusShow::FFC: circleColour = green; break;
+ case StatusShow::Away: circleColour = yellow; break;
+ case StatusShow::XA: circleColour = yellow; break;
+ case StatusShow::DND: circleColour = red; break;
+ case StatusShow::None: circleColour = grey; break;
+ }
+
+ painter->setRenderHint(QPainter::Antialiasing, true);
+
+ int unreadCount = index.data(ChattablesModel::UnreadCountRole).toInt();
+ if (unreadCount > 0) {
+ int unreadCountSize = 16;
+ QRect unreadRect(fullRegion.right() - unreadCountSize - horizontalMargin, fullRegion.top() + (fullRegion.height() - unreadCountSize) / 2, unreadCountSize, unreadCountSize);
+ QPen pen(QColor("white"));
+ pen.setWidth(1);
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(pen);
+ painter->drawEllipse(unreadRect);
+ painter->setBackgroundMode(Qt::TransparentMode);
+ painter->setPen(QColor("white"));
+ DelegateCommons::drawElidedText(painter, unreadRect, QString("%1").arg(unreadCount), Qt::AlignCenter);
+ }
+
+ painter->setPen(circleColour);
+ painter->setBrush(circleColour);
+ painter->drawEllipse(fullRegion.topLeft() + QPointF(horizontalMargin + 4, fullRegion.height() / 2), statusCircleRadius, statusCircleRadius);
+
+
+ painter->restore();
+}
+
+} // namespace Swift
diff --git a/Swift/QtUI/QtChatOverviewDelegate.h b/Swift/QtUI/QtChatOverviewDelegate.h
new file mode 100644
index 0000000..b00337d
--- /dev/null
+++ b/Swift/QtUI/QtChatOverviewDelegate.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QItemDelegate>
+#include <QFont>
+
+namespace Swift {
+ class QtChatOverviewDelegate : public QItemDelegate {
+ Q_OBJECT
+ public:
+ QtChatOverviewDelegate(QObject* parent);
+ ~QtChatOverviewDelegate() override;
+ QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ private:
+ QFont nameFont;
+ };
+}
diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp
index ad95a07..edd0b87 100644
--- a/Swift/QtUI/QtChatTabs.cpp
+++ b/Swift/QtUI/QtChatTabs.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -18,12 +18,13 @@
#include <QMenu>
#include <QTabBar>
#include <QTabWidget>
-#include <QtGlobal>
#include <QWindow>
+#include <QtGlobal>
#include <Swiften/Base/Log.h>
#include <Swift/Controllers/ChatMessageSummarizer.h>
+#include <Swift/Controllers/SettingConstants.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/QtTabWidget.h>
@@ -33,13 +34,13 @@
#include <Swift/QtUI/Trellis/QtGridSelectionDialog.h>
namespace Swift {
-QtChatTabs::QtChatTabs(bool singleWindow, SettingsProvider* settingsProvider, bool trellisMode) : QWidget(), singleWindow_(singleWindow), settingsProvider_(settingsProvider), trellisMode_(trellisMode), dynamicGrid_(nullptr), gridSelectionDialog_(nullptr) {
+QtChatTabs::QtChatTabs(SettingsProvider* settingsProvider, bool trellisMode) : QWidget(), settingsProvider_(settingsProvider), trellisMode_(trellisMode), dynamicGrid_(nullptr), gridSelectionDialog_(nullptr) {
#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/logo-chat-16.png"));
#else
setAttribute(Qt::WA_ShowWithoutActivating);
#endif
- dynamicGrid_ = new QtDynamicGridLayout(this, trellisMode);
+ dynamicGrid_ = new QtDynamicGridLayout(settingsProvider->getSetting(SettingConstants::FUTURE), this, trellisMode);
connect(dynamicGrid_, SIGNAL(tabCloseRequested(int)), this, SLOT(handleTabCloseRequested(int)));
connect(dynamicGrid_, SIGNAL(onCurrentIndexChanged(int)), this, SLOT(handleCurrentTabIndexChanged(int)));
@@ -112,8 +113,6 @@ void QtChatTabs::setViewMenu(QMenu* viewMenu) {
if (trellisMode_) {
viewMenu->addSeparator();
QAction* action = new QAction(tr("Change &layout"), this);
- action->setShortcutContext(Qt::ApplicationShortcut);
- action->setShortcut(QKeySequence(tr("Ctrl+Alt+L")));
connect(action, SIGNAL(triggered()), this, SLOT(handleOpenLayoutChangeDialog()));
viewMenu->addAction(action);
@@ -201,13 +200,8 @@ void QtChatTabs::handleTabClosing() {
if (widget && ((index = dynamicGrid_->indexOf(widget)) >= 0)) {
dynamicGrid_->removeTab(index);
if (dynamicGrid_->count() == 0) {
- if (!singleWindow_) {
- hide();
- }
- else {
- setWindowTitle("");
- onTitleChanged("");
- }
+ setWindowTitle("");
+ onTitleChanged("");
}
else {
handleTabTitleUpdated(dynamicGrid_->currentWidget());
@@ -427,4 +421,8 @@ void QtChatTabs::checkForFirstShow() {
}
}
+QSize QtChatTabs::sizeHint() const {
+ return QSize(600, 600);
+}
+
}
diff --git a/Swift/QtUI/QtChatTabs.h b/Swift/QtUI/QtChatTabs.h
index 0c12d96..6a758ca 100644
--- a/Swift/QtUI/QtChatTabs.h
+++ b/Swift/QtUI/QtChatTabs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -26,13 +26,14 @@ namespace Swift {
class QtChatTabs : public QWidget, public QtChatTabsBase {
Q_OBJECT
public:
- QtChatTabs(bool singleWindow, SettingsProvider* settingsProvider, bool trellisMode);
+ QtChatTabs(SettingsProvider* settingsProvider, bool trellisMode);
virtual ~QtChatTabs();
virtual void addTab(QtTabbable* tab);
void minimise();
QtTabbable* getCurrentTab();
void setViewMenu(QMenu* viewMenu);
+ QSize sizeHint() const;
signals:
void geometryChanged();
@@ -65,7 +66,6 @@ namespace Swift {
void checkForFirstShow();
private:
- bool singleWindow_;
SettingsProvider* settingsProvider_;
bool trellisMode_;
QtDynamicGridLayout* dynamicGrid_;
diff --git a/Swift/QtUI/QtChatTabsShortcutOnlySubstitute.cpp b/Swift/QtUI/QtChatTabsShortcutOnlySubstitute.cpp
deleted file mode 100644
index 40ab17f..0000000
--- a/Swift/QtUI/QtChatTabsShortcutOnlySubstitute.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2015-2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/QtUI/QtChatTabsShortcutOnlySubstitute.h>
-
-#include <cassert>
-
-#include <QApplication>
-#include <QShortcut>
-
-#include <Swiften/Base/Log.h>
-
-#include <Swift/QtUI/QtTabbable.h>
-
-namespace Swift {
-
-QtChatTabsShortcutOnlySubstitute::QtChatTabsShortcutOnlySubstitute() : QWidget() {
-
-}
-
-QtChatTabsShortcutOnlySubstitute::~QtChatTabsShortcutOnlySubstitute() {
-
-}
-
-void QtChatTabsShortcutOnlySubstitute::addTab(QtTabbable* tab) {
- connect(tab, SIGNAL(requestNextTab()), this, SLOT(handleRequestedNextTab()), Qt::UniqueConnection);
- connect(tab, SIGNAL(requestActiveTab()), this, SLOT(handleRequestedActiveTab()), Qt::UniqueConnection);
- connect(tab, SIGNAL(requestPreviousTab()), this, SLOT(handleRequestedPreviousTab()), Qt::UniqueConnection);
-
- connect(new QShortcut(QKeySequence(tr("CTRL+W", "Close chat tab.")), tab), SIGNAL(activated()), this, SLOT(handleCloseTabShortcut()));
- connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageUp), tab), SIGNAL(activated()), tab, SIGNAL(requestPreviousTab()));
- connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageDown), tab), SIGNAL(activated()), tab, SIGNAL(requestNextTab()));
- connect(new QShortcut(QKeySequence(Qt::ALT + Qt::Key_A), tab), SIGNAL(activated()), tab, SIGNAL(requestActiveTab()));
-}
-
-void QtChatTabsShortcutOnlySubstitute::handleCloseTabShortcut() {
- QtTabbable* senderTab = dynamic_cast<QtTabbable*>(sender()->parent());
- SWIFT_LOG_ASSERT(senderTab, debug) << "No window to close." << std::endl;
- if (senderTab) {
- senderTab->close();
- }
-}
-
-void QtChatTabsShortcutOnlySubstitute::handleRequestedNextTab() {
- QtTabbable* senderTab = dynamic_cast<QtTabbable*>(sender());
-
- QList<QtTabbable*> tabs = tabbableWindows();
-
- int currentIndex = tabs.indexOf(senderTab);
- assert(currentIndex >= 0);
-
- QtTabbable* nextTab = tabs.at((currentIndex + 1) % tabs.size());
- nextTab->activateWindow();
-}
-
-void QtChatTabsShortcutOnlySubstitute::handleRequestedActiveTab() {
- QtTabbable* senderTab = dynamic_cast<QtTabbable*>(sender());
-
- QtTabbable::AlertType types[] = {QtTabbable::WaitingActivity, QtTabbable::ImpendingActivity};
-
- QList<QtTabbable*> tabs = tabbableWindows();
-
- for (auto& type : types) {
- int startIndex = tabs.indexOf(senderTab);
- int currentIndex = startIndex;
-
- do {
- currentIndex = (currentIndex + 1) % tabs.size();
- QtTabbable* currentTab = tabs.at(currentIndex);
- if (currentTab->getWidgetAlertState() == type) {
- currentTab->activateWindow();
- return;
- }
- } while (startIndex != currentIndex);
- }
-}
-
-void QtChatTabsShortcutOnlySubstitute::handleRequestedPreviousTab() {
- QtTabbable* senderTab = dynamic_cast<QtTabbable*>(sender());
-
- QList<QtTabbable*> tabs = tabbableWindows();
-
- int currentIndex = tabs.indexOf(senderTab);
- assert(currentIndex >= 0);
-
- QtTabbable* previousTab = tabs.at((currentIndex + tabs.size() - 1) % tabs.size());
- previousTab->activateWindow();
-}
-
-QList<QtTabbable*> QtChatTabsShortcutOnlySubstitute::tabbableWindows() const {
- QList<QWidget*> windows = qApp->topLevelWidgets();
-
- QList<QtTabbable*> tabbables;
- for (auto topLevelWidget : windows) {
- QtTabbable* tabbable = dynamic_cast<QtTabbable*>(topLevelWidget);
- if (tabbable) {
- tabbables << tabbable;
- }
- }
-
- return tabbables;
-}
-
-}
-
diff --git a/Swift/QtUI/QtChatTabsShortcutOnlySubstitute.h b/Swift/QtUI/QtChatTabsShortcutOnlySubstitute.h
deleted file mode 100644
index b330fe7..0000000
--- a/Swift/QtUI/QtChatTabsShortcutOnlySubstitute.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2015-2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <QList>
-#include <QWidget>
-
-#include <Swift/QtUI/QtChatTabsBase.h>
-
-class QShortcut;
-
-namespace Swift {
-
-class QtChatTabsShortcutOnlySubstitute : public QWidget, public QtChatTabsBase {
- Q_OBJECT
-
- public:
- QtChatTabsShortcutOnlySubstitute();
- virtual ~QtChatTabsShortcutOnlySubstitute();
-
- virtual void addTab(QtTabbable* tab);
-
- private slots:
- void handleCloseTabShortcut();
- void handleRequestedNextTab();
- void handleRequestedActiveTab();
- void handleRequestedPreviousTab();
-
- private:
- QList<QtTabbable*> tabbableWindows() const;
-
- private:
- QList<QShortcut*> shortcuts_;
-};
-
-}
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index e750caa..82c65ce 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -111,8 +111,11 @@ QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventSt
messageLog_ = new QtPlainChatView(this, eventStream_);
}
else {
- messageLog_ = new QtWebKitChatView(this, eventStream_, theme, this); // I accept that passing the ChatWindow in so that the view can call the signals is somewhat inelegant, but it saves a lot of boilerplate. This patch is unpleasant enough already. So let's fix this soon (it at least needs fixing by the time history is sorted), but not now.
+ messageLog_ = new QtWebKitChatView(this, eventStream_, theme, this, settings); // I accept that passing the ChatWindow in so that the view can call the signals is somewhat inelegant, but it saves a lot of boilerplate. This patch is unpleasant enough already. So let's fix this soon (it at least needs fixing by the time history is sorted), but not now.
}
+ // When used with QSplitter and setChildrenCollapsible(false), the following prevents
+ // this widget to be hidden, i.e. resized to zero width.
+ messageLog_->setMinimumWidth(20);
logRosterSplitter_->addWidget(messageLog_);
treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, QtTreeWidget::MessageDisplayJID, this);
@@ -152,11 +155,15 @@ QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventSt
connect(input_, SIGNAL(lostFocus()), this, SLOT(handleTextInputLostFocus()));
connect(input_, SIGNAL(itemDropped(QDropEvent*)), this, SLOT(dropEvent(QDropEvent*)));
QPushButton* emojisButton_ = new QPushButton(this);
-
-#ifdef SWIFTEN_PLATFORM_MACOSX
emojisButton_->setText("\xF0\x9F\x98\x83");
-#else
- emojisButton_->setIcon(QIcon(":/emoticons/smile.png"));
+
+#if defined(SWIFTEN_PLATFORM_WINDOWS) || defined(SWIFTEN_PLATFORM_LINUX)
+ //Using a emoji glyph instead of an image makes the button sizes inequal in windows & linux,
+ //so we set fixed size for both buttons, the one that is hinted for actionButton_
+ emojisButton_->setMaximumWidth(actionButton_->sizeHint().width());
+ emojisButton_->setMaximumHeight(actionButton_->sizeHint().height());
+ actionButton_->setMaximumWidth(actionButton_->sizeHint().width());
+ actionButton_->setMaximumHeight(actionButton_->sizeHint().height());
#endif
connect(emojisButton_, SIGNAL(clicked()), this, SLOT(handleEmojisButtonClicked()));
@@ -353,6 +360,10 @@ QByteArray QtChatWindow::getSplitterState() {
void QtChatWindow::handleChangeSplitterState(QByteArray state) {
logRosterSplitter_->restoreState(state);
+#ifdef SWIFTEN_PLATFORM_MACOSX
+ logRosterSplitter_->setHandleWidth(0);
+#endif
+ logRosterSplitter_->setChildrenCollapsible(false);
}
void QtChatWindow::handleSplitterMoved(int, int) {
@@ -494,7 +505,7 @@ void QtChatWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
}
-void QtChatWindow::setUnreadMessageCount(int count) {
+void QtChatWindow::setUnreadMessageCount(size_t count) {
if (unreadCount_ != count) {
unreadCount_ = count;
updateTitleWithUnreadCount();
@@ -536,7 +547,7 @@ void QtChatWindow::flash() {
emit requestFlash();
}
-int QtChatWindow::getCount() {
+size_t QtChatWindow::getCount() {
return unreadCount_;
}
@@ -707,16 +718,26 @@ void QtChatWindow::handleEmojiClicked(QString emoji) {
if (isVisible()) {
input_->textCursor().insertText(emoji);
input_->setFocus();
- // The next line also deletes the emojisGrid_, as it was added to the
- // layout of the emojisMenu_.
- emojisMenu_.reset();
+ // We cannot delete the emojisGrid_
+ // Grid may not close yet and we should not try to destroy it.
+ emojisMenu_->setVisible(false);
}
}
void QtChatWindow::handleTextInputReceivedFocus() {
lastLineTracker_.setHasFocus(true);
input_->setFocus();
- onAllMessagesRead();
+ if (focusTimer_) {
+ focusTimer_->stop();
+ }
+ else {
+ focusTimer_ = std::make_unique<QTimer>(this);
+ focusTimer_->setSingleShot(true);
+ focusTimer_->setTimerType(Qt::CoarseTimer);
+ connect(focusTimer_.get(), &QTimer::timeout, this, &QtChatWindow::handleFocusTimerTick);
+ }
+ focusTimer_->setInterval(1000);
+ focusTimer_->start();
}
void QtChatWindow::handleTextInputLostFocus() {
@@ -730,6 +751,7 @@ void QtChatWindow::handleActionButtonClicked() {
QAction* affiliations = nullptr;
QAction* destroy = nullptr;
QAction* invite = nullptr;
+ QAction* leave = nullptr;
QAction* block = nullptr;
QAction* unblock = nullptr;
@@ -783,6 +805,10 @@ void QtChatWindow::handleActionButtonClicked() {
invite = contextMenu.addAction(tr("Invite person to this room…"));
invite->setEnabled(isOnline_);
break;
+ case ChatWindow::Leave:
+ leave = contextMenu.addAction(tr("Leave room"));
+ leave->setEnabled(isOnline_);
+ break;
}
}
}
@@ -834,6 +860,9 @@ void QtChatWindow::handleActionButtonClicked() {
else if (result == invite) {
onInviteToChat(std::vector<JID>());
}
+ else if (result == leave) {
+ close();
+ }
else if (result == block) {
onBlockUserRequest();
}
@@ -867,14 +896,16 @@ void QtChatWindow::setCanInitiateImpromptuChats(bool supportsImpromptu) {
}
void QtChatWindow::showBookmarkWindow(const MUCBookmark& bookmark) {
- if (roomBookmarkState_ == RoomNotBookmarked) {
- QtAddBookmarkWindow* window = new QtAddBookmarkWindow(eventStream_, bookmark);
+ if (roomBookmarkState_ != RoomNotBookmarked) {
+ QtEditBookmarkWindow* window = new QtEditBookmarkWindow(eventStream_, bookmark);
window->show();
}
+#ifndef NOT_YET
else {
- QtEditBookmarkWindow* window = new QtEditBookmarkWindow(eventStream_, bookmark);
+ QtAddBookmarkWindow* window = new QtAddBookmarkWindow(eventStream_, bookmark);
window->show();
}
+#endif // ! NOT_YET
}
std::string QtChatWindow::getID() const {
@@ -984,4 +1015,60 @@ void QtChatWindow::setBookmarkState(RoomBookmarkState bookmarkState) {
roomBookmarkState_ = bookmarkState;
}
+void QtChatWindow::setChatSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue) {
+ auto layout = static_cast<QBoxLayout*>(this->layout());
+
+ if (securityMarkingLayout_) {
+ layout->removeItem(securityMarkingLayout_);
+ securityMarkingLayout_->removeWidget(securityMarkingDisplay_);
+ delete securityMarkingLayout_;
+ }
+ delete securityMarkingDisplay_;
+
+ securityMarkingLayout_ = new QHBoxLayout();
+ securityMarkingDisplay_ = new QLabel(P2QSTRING(markingValue));
+
+ securityMarkingLayout_->addWidget(securityMarkingDisplay_);
+ layout->insertLayout(1, securityMarkingLayout_);
+
+ auto palette = securityMarkingDisplay_->palette();
+ palette.setColor(securityMarkingDisplay_->foregroundRole(), P2QSTRING(markingForegroundColorValue));
+ palette.setColor(securityMarkingDisplay_->backgroundRole(), P2QSTRING(markingBackgroundColorValue));
+
+ securityMarkingDisplay_->setPalette(palette);
+ securityMarkingDisplay_->setContentsMargins(4,4,4,4);
+ securityMarkingDisplay_->setAutoFillBackground(true);
+ securityMarkingDisplay_->setAlignment(Qt::AlignCenter);
+}
+
+void QtChatWindow::removeChatSecurityMarking() {
+ if (!securityMarkingLayout_) {
+ return;
+ }
+
+ auto layout = static_cast<QBoxLayout*>(this->layout());
+
+ layout->removeItem(securityMarkingLayout_);
+ securityMarkingLayout_->removeWidget(securityMarkingDisplay_);
+
+ delete securityMarkingDisplay_;
+ delete securityMarkingLayout_;
+ securityMarkingDisplay_ = nullptr;
+ securityMarkingLayout_ = nullptr;
+}
+
+void QtChatWindow::handleFocusTimerTick() {
+ if (hasFocus()) {
+ onAllMessagesRead();
+ }
+ focusTimer_.reset();
+}
+
+void QtChatWindow::resendMessage(const std::string& id) {
+ if (!isOnline_ || (blockingState_ == IsBlocked)) {
+ return;
+ }
+ onResendMessageRequest(id);
+}
+
}
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 361d7c6..b876d1e 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -34,6 +34,7 @@ class QComboBox;
class QLabel;
class QSplitter;
class QPushButton;
+class QTimer;
namespace Swift {
class QtChatView;
@@ -93,6 +94,7 @@ namespace Swift {
void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time);
void replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time);
+ void resendMessage(const std::string& id);
// File transfer related stuff
std::string addFileTransfer(const std::string& senderName, const std::string& avatarPath, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes, const std::string& description);
void setFileTransferProgress(std::string id, const int percentageDone);
@@ -104,7 +106,7 @@ namespace Swift {
void show();
bool isVisible() const;
void activate();
- void setUnreadMessageCount(int count);
+ void setUnreadMessageCount(size_t count);
void convertToMUC(MUCType mucType);
// TreeWidget *getTreeWidget();
void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels);
@@ -117,7 +119,7 @@ namespace Swift {
void setContactChatState(ChatState::ChatStateType state);
void setRosterModel(Roster* roster);
void setTabComplete(TabComplete* completer);
- int getCount();
+ size_t getCount();
virtual void replaceSystemMessage(const ChatMessage& message, const std::string& id, const TimestampBehaviour timestampBehaviour);
void replaceLastMessage(const ChatMessage& message, const TimestampBehaviour timestampBehaviour);
void setAckState(const std::string& id, AckState state);
@@ -194,8 +196,12 @@ namespace Swift {
void resetDayChangeTimer();
+ void setChatSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue);
+ void removeChatSecurityMarking();
+ void handleFocusTimerTick();
+
private:
- int unreadCount_;
+ size_t unreadCount_;
bool contactIsTyping_;
LastLineTracker lastLineTracker_;
std::string id_;
@@ -241,5 +247,8 @@ namespace Swift {
QPointer<QtEmojisSelector> emojisGrid_;
std::map<std::string, std::string> emoticonsMap_;
QTimer* dayChangeTimer = nullptr;
+ QHBoxLayout* securityMarkingLayout_ = nullptr;
+ QLabel* securityMarkingDisplay_ = nullptr;
+ std::unique_ptr<QTimer> focusTimer_;
};
}
diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp
index 49cfe4d..33c8b94 100644
--- a/Swift/QtUI/QtChatWindowFactory.cpp
+++ b/Swift/QtUI/QtChatWindowFactory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -11,7 +11,6 @@
#include <SwifTools/EmojiMapper.h>
#include <Swift/QtUI/QtChatTabs.h>
-#include <Swift/QtUI/QtChatTabsBase.h>
#include <Swift/QtUI/QtChatTheme.h>
#include <Swift/QtUI/QtChatWindow.h>
#include <Swift/QtUI/QtEmojisSelector.h>
@@ -23,22 +22,17 @@ namespace Swift {
static const QString SPLITTER_STATE = "mucSplitterState";
static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry";
-QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap) : themePath_(themePath), emoticonsMap_(emoticonsMap) {
+QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap) : themePath_(themePath), emoticonsMap_(emoticonsMap) {
qtOnlySettings_ = qtSettings;
settings_ = settings;
tabs_ = tabs;
theme_ = nullptr;
- QtChatTabs* fullTabs = dynamic_cast<QtChatTabs*>(tabs_);
- if (splitter) {
- assert(fullTabs && "Netbook mode and no-tabs interface is not supported!");
- splitter->addWidget(fullTabs);
- } else if (fullTabs) {
- QVariant chatTabsGeometryVariant = qtOnlySettings_->getQSettings()->value(CHAT_TABS_GEOMETRY);
- if (chatTabsGeometryVariant.isValid()) {
- fullTabs->restoreGeometry(chatTabsGeometryVariant.toByteArray());
- }
- connect(fullTabs, SIGNAL(geometryChanged()), this, SLOT(handleWindowGeometryChanged()));
+ splitter->addWidget(tabs_);
+ QVariant chatTabsGeometryVariant = qtOnlySettings_->getQSettings()->value(CHAT_TABS_GEOMETRY);
+ if (chatTabsGeometryVariant.isValid()) {
+ tabs_->restoreGeometry(chatTabsGeometryVariant.toByteArray());
}
+ connect(tabs_, SIGNAL(geometryChanged()), this, SLOT(handleWindowGeometryChanged()));
}
QtChatWindowFactory::~QtChatWindowFactory() {
@@ -60,7 +54,7 @@ ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStre
connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray)));
QVariant splitterState = qtOnlySettings_->getQSettings()->value(SPLITTER_STATE);
- if(splitterState.isValid()) {
+ if (splitterState.isValid()) {
chatWindow->handleChangeSplitterState(splitterState.toByteArray());
}
diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h
index 2bb6a90..3e4dca3 100644
--- a/Swift/QtUI/QtChatWindowFactory.h
+++ b/Swift/QtUI/QtChatWindowFactory.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -24,7 +24,7 @@
#include <Swift/QtUI/QtSettingsProvider.h>
namespace Swift {
- class QtChatTabsBase;
+ class QtChatTabs;
class QtChatTheme;
class UIEventStream;
class QtUIPreferences;
@@ -32,7 +32,7 @@ namespace Swift {
class QtChatWindowFactory : public QObject, public ChatWindowFactory {
Q_OBJECT
public:
- QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap);
+ QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap);
~QtChatWindowFactory();
ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream);
signals:
@@ -44,7 +44,7 @@ namespace Swift {
QString themePath_;
SettingsProvider* settings_;
QtSettingsProvider* qtOnlySettings_;
- QtChatTabsBase* tabs_;
+ QtChatTabs* tabs_;
QtChatTheme* theme_;
std::map<std::string, std::string> emoticonsMap_;
};
diff --git a/Swift/QtUI/QtColorToolButton.cpp b/Swift/QtUI/QtColorToolButton.cpp
index b349a47..6452cf4 100644
--- a/Swift/QtUI/QtColorToolButton.cpp
+++ b/Swift/QtUI/QtColorToolButton.cpp
@@ -36,6 +36,7 @@ void QtColorToolButton::setColor(const QColor& color)
void QtColorToolButton::onClicked()
{
QColor c = QColorDialog::getColor(color_, this);
+ window()->raise();
if (c.isValid()) {
setColor(c);
}
diff --git a/Swift/QtUI/QtDBUSURIHandler.cpp b/Swift/QtUI/QtDBUSURIHandler.cpp
index 34659f4..a1446c3 100644
--- a/Swift/QtUI/QtDBUSURIHandler.cpp
+++ b/Swift/QtUI/QtDBUSURIHandler.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 Isode Limited.
+ * Copyright (c) 2011-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -19,7 +19,7 @@ using namespace Swift;
namespace {
class DBUSAdaptor: public QDBusAbstractAdaptor {
Q_OBJECT
- Q_CLASSINFO("D-Bus Interface", "im.swift.Swift.URIHandler");
+ Q_CLASSINFO("D-Bus Interface", "im.swift.Swift.URIHandler")
public:
DBUSAdaptor(QtDBUSURIHandler* uriHandler) : QDBusAbstractAdaptor(uriHandler), uriHandler(uriHandler) {
}
diff --git a/Swift/QtUI/QtEditBookmarkWindow.cpp b/Swift/QtUI/QtEditBookmarkWindow.cpp
index 1d6b467..c724c97 100644
--- a/Swift/QtUI/QtEditBookmarkWindow.cpp
+++ b/Swift/QtUI/QtEditBookmarkWindow.cpp
@@ -12,7 +12,6 @@ namespace Swift {
QtEditBookmarkWindow::QtEditBookmarkWindow(UIEventStream* eventStream, const MUCBookmark& bookmark) : eventStream_(eventStream), bookmark_(bookmark) {
name_->setText(P2QSTRING(bookmark.getName()));
room_->setText(P2QSTRING(bookmark.getRoom().toString()));
- autojoin_->setChecked(bookmark.getAutojoin());
nick_->setText(bookmark.getNick() ? P2QSTRING(bookmark.getNick().get()) : "");
password_->setText(bookmark.getPassword() ? P2QSTRING(bookmark.getPassword().get()) : "");
}
diff --git a/Swift/QtUI/QtEmojiCell.cpp b/Swift/QtUI/QtEmojiCell.cpp
index 865f1f6..106e968 100644
--- a/Swift/QtUI/QtEmojiCell.cpp
+++ b/Swift/QtUI/QtEmojiCell.cpp
@@ -1,11 +1,13 @@
/*
- * Copyright (c) 2016-2017 Isode Limited.
+ * Copyright (c) 2016-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/QtUI/QtEmojiCell.h>
+#include <Swiften/Base/Platform.h>
+
#include <QFont>
#include <QFontMetrics>
#include <QPushButton>
@@ -17,13 +19,20 @@ namespace Swift {
QtEmojiCell::QtEmojiCell(QString shortname, QString text, QWidget* parent) : QPushButton(parent) {
setText(text);
QFont font = this->font();
+#ifdef SWIFTEN_PLATFORM_WINDOWS
+ //Windows emoji font miscalculates the bounding rectangular that surrounds the emoji. We set a multiplier value to make it look consistent with linux & Mac
+ const float sizeMultiplier = 1.3;
+ font.setPointSize(18);
+#else
font.setPointSize(22);
+ const float sizeMultiplier = 1;
+#endif
font.setBold(true);
setFont(font);
const auto boundingRect = fontMetrics().boundingRect("\xF0\x9F\x98\x83");
- setFixedWidth(qMax(boundingRect.width(), boundingRect.height()));
- setFixedHeight(qMax(boundingRect.width(), boundingRect.height()));
+ setFixedWidth(qMax(sizeMultiplier*boundingRect.width(), sizeMultiplier*boundingRect.height()));
+ setFixedHeight(qMax(sizeMultiplier*boundingRect.width(), sizeMultiplier*boundingRect.height()));
setFlat(true);
setToolTip(shortname);
diff --git a/Swift/QtUI/QtEmojisScroll.h b/Swift/QtUI/QtEmojisScroll.h
index 959ab5f..f954c2d 100644
--- a/Swift/QtUI/QtEmojisScroll.h
+++ b/Swift/QtUI/QtEmojisScroll.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017 Isode Limited.
+ * Copyright (c) 2016-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -13,6 +13,6 @@ namespace Swift {
class QtEmojisScroll : public QWidget {
Q_OBJECT
public:
- QtEmojisScroll(QLayout* emojiLayout, QWidget *parent = 0);
+ QtEmojisScroll(QLayout* emojiLayout, QWidget* parent = nullptr);
};
}
diff --git a/Swift/QtUI/QtEmojisSelector.cpp b/Swift/QtUI/QtEmojisSelector.cpp
index 62ed862..fe2f235 100644
--- a/Swift/QtUI/QtEmojisSelector.cpp
+++ b/Swift/QtUI/QtEmojisSelector.cpp
@@ -24,7 +24,6 @@
namespace Swift {
QtEmojisSelector::QtEmojisSelector(QSettings* settings, const std::map<std::string, std::string>& emoticonsMap, QWidget* parent) : QTabWidget(parent), settings_(settings), emoticonsMap_(emoticonsMap) {
-#ifdef SWIFTEN_PLATFORM_MACOSX
recentEmojisGrid_ = addRecentTab();
connect(recentEmojisGrid_, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
@@ -34,17 +33,13 @@ namespace Swift {
connect(grid, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
}
}
-
loadSettings();
-#else
- setupEmoticonsTab();
-#endif
+ //The size of an emoji cell varies depending the OS, 42 is the ceil value.
+ setFixedSize(QSize(EmojiMapper::emojisInCategory.size() * 42, 300));
}
QtEmojisSelector::~QtEmojisSelector() {
-#ifdef SWIFTEN_PLATFORM_MACOSX
writeSettings();
-#endif
}
QtRecentEmojisGrid* QtEmojisSelector::addRecentTab() {
diff --git a/Swift/QtUI/QtEmojisSelector.h b/Swift/QtUI/QtEmojisSelector.h
index 7ac11d0..1a64cf4 100644
--- a/Swift/QtUI/QtEmojisSelector.h
+++ b/Swift/QtUI/QtEmojisSelector.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017 Isode Limited.
+ * Copyright (c) 2016-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -20,7 +20,7 @@ namespace Swift {
class QtEmojisSelector : public QTabWidget {
Q_OBJECT
public:
- QtEmojisSelector(QSettings* settings, const std::map<std::string, std::string>& emoticonsMap, QWidget * parent = 0);
+ QtEmojisSelector(QSettings* settings, const std::map<std::string, std::string>& emoticonsMap, QWidget* parent = nullptr);
~QtEmojisSelector();
public slots:
diff --git a/Swift/QtUI/QtExpandedListView.cpp b/Swift/QtUI/QtExpandedListView.cpp
new file mode 100644
index 0000000..84769c5
--- /dev/null
+++ b/Swift/QtUI/QtExpandedListView.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtExpandedListView.h>
+
+#include <QWheelEvent>
+#include <QScrollArea>
+#include <QDebug>
+
+namespace Swift {
+
+QtExpandedListView::QtExpandedListView(QWidget* parent) : QListView(parent) {
+ // Disable macOS focus rectangle due to bad performance.
+ setAttribute(Qt::WA_MacShowFocusRect, 0);
+ setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed);
+}
+
+void QtExpandedListView::setModel(QAbstractItemModel* newModel) {
+ if (model()) {
+ disconnectFromModel(model());
+ }
+ if (newModel) {
+ connectToModel(newModel);
+ }
+ QListView::setModel(newModel);
+ adjustHeightToModelChange();
+}
+
+QtExpandedListView::~QtExpandedListView() {
+ if (model()) {
+ disconnectFromModel(model());
+ }
+}
+
+bool QtExpandedListView::viewportEvent(QEvent* event) {
+ // Ignore wheel events for faster mouse scrolling.
+ if (event && event->type() == QEvent::Wheel) {
+ return false;
+ }
+
+ return QListView::viewportEvent(event);
+}
+
+template <typename T>
+T getParentOfType(QWidget* start) {
+ auto parentW = start->parentWidget();
+ if (parentW == nullptr) {
+ return nullptr;
+ }
+ T result = dynamic_cast<T>(parentW);
+ if (result) {
+ return result;
+ }
+ return getParentOfType<T>(parentW);
+}
+
+void QtExpandedListView::currentChanged(const QModelIndex &current, const QModelIndex &) {
+ // Make sure that the current selected index is visible in the parent QScrollArea.
+ auto scrollArea = getParentOfType<QScrollArea*>(parentWidget());
+ if (scrollArea) {
+ auto scrollWidget = scrollArea->widget();
+ QList<QPoint> points;
+ auto visRect = visualRect(current);
+ points << mapTo(scrollWidget, visRect.topLeft());
+ points << mapTo(scrollWidget, visRect.topRight());
+ points << mapTo(scrollWidget, visRect.bottomLeft());
+ points << mapTo(scrollWidget, visRect.bottomRight());
+
+ for (auto&& point : points) {
+ scrollArea->ensureVisible(point.x(), point.y(), 0, 0);
+ }
+ }
+}
+
+void QtExpandedListView::adjustHeightToModelChange() {
+ updateGeometry();
+}
+
+QSize QtExpandedListView::minimumSizeHint() const {
+ auto sh = sizeHint();
+ return QSize(0, sh.height());
+}
+
+QSize QtExpandedListView::sizeHint() const {
+ auto listViewSH = QListView::sizeHint();
+ if (model()) {
+ auto lastRect = rectForIndex(model()->index(model()->rowCount()-1, 0, rootIndex()));
+ auto idealHeight = lastRect.y() + lastRect.height() + frameWidth() * 2;
+ listViewSH.setHeight(idealHeight);
+ }
+ return listViewSH;
+}
+
+void QtExpandedListView::connectToModel(QAbstractItemModel* model) {
+ connect(model, &QAbstractItemModel::dataChanged, this, &QtExpandedListView::adjustHeightToModelChange);
+ connect(model, &QAbstractItemModel::modelReset, this, &QtExpandedListView::adjustHeightToModelChange);
+ connect(model, &QAbstractItemModel::rowsInserted, this, &QtExpandedListView::adjustHeightToModelChange);
+ connect(model, &QAbstractItemModel::rowsRemoved, this, &QtExpandedListView::adjustHeightToModelChange);
+}
+
+void QtExpandedListView::disconnectFromModel(QAbstractItemModel* model) {
+ disconnect(model, &QAbstractItemModel::dataChanged, this, &QtExpandedListView::adjustHeightToModelChange);
+ disconnect(model, &QAbstractItemModel::modelReset, this, &QtExpandedListView::adjustHeightToModelChange);
+ disconnect(model, &QAbstractItemModel::rowsInserted, this, &QtExpandedListView::adjustHeightToModelChange);
+ disconnect(model, &QAbstractItemModel::rowsRemoved, this, &QtExpandedListView::adjustHeightToModelChange);
+}
+
+}
diff --git a/Swift/QtUI/QtExpandedListView.h b/Swift/QtUI/QtExpandedListView.h
new file mode 100644
index 0000000..df78376
--- /dev/null
+++ b/Swift/QtUI/QtExpandedListView.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QListView>
+
+namespace Swift {
+
+class QtExpandedListView : public QListView {
+public:
+ QtExpandedListView(QWidget* parent);
+ ~QtExpandedListView() override;
+
+ void setModel(QAbstractItemModel* model) override;
+ bool viewportEvent(QEvent* event) override;
+ QSize minimumSizeHint() const override;
+ QSize sizeHint() const override;
+
+protected slots:
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
+
+private slots:
+ void adjustHeightToModelChange();
+
+private:
+ void connectToModel(QAbstractItemModel* model);
+ void disconnectFromModel(QAbstractItemModel* model);
+};
+
+}
diff --git a/Swift/QtUI/QtFdpFormSubmitWindow.cpp b/Swift/QtUI/QtFdpFormSubmitWindow.cpp
new file mode 100644
index 0000000..5719f87
--- /dev/null
+++ b/Swift/QtUI/QtFdpFormSubmitWindow.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtFdpFormSubmitWindow.h>
+
+#include <QCloseEvent>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QSpacerItem>
+#include <QVBoxLayout>
+
+#include <Swift/QtUI/QtFormWidget.h>
+#include <Swift/QtUI/QtListWidget.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+QtFdpFormSubmitWindow::QtFdpFormSubmitWindow(QWidget* parent) : QDialog(parent), FdpFormSubmitWindow() {
+ layout_ = new QVBoxLayout(this);
+ subLayout_ = new QHBoxLayout(this);
+
+ initNodeViewLayout();
+ initFormLayout();
+ subLayout_->addLayout(nodeViewLayout_);
+ subLayout_->addLayout(formLayout_);
+ subLayout_->setStretchFactor(nodeViewLayout_, 2);
+ subLayout_->setStretchFactor(formLayout_, 3);
+ layout_->addLayout(subLayout_);
+
+ submitButton_ = new QPushButton(tr("Submit Form"), this);
+ submitButton_->setEnabled(false);
+ okButton_ = new QPushButton(tr("Ok"), this);
+ okButton_->hide();
+ cancelButton_ = new QPushButton(tr("Cancel"), this);
+ connect(submitButton_, &QPushButton::clicked, this, &QtFdpFormSubmitWindow::handleSubmitClicked);
+ connect(okButton_, &QPushButton::clicked, this, &QWidget::close);
+ connect(cancelButton_, &QPushButton::clicked, this, &QWidget::close);
+ auto buttonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ buttonLayout_ = new QHBoxLayout(this);
+ buttonLayout_->addItem(buttonSpacer);
+ buttonLayout_->addWidget(submitButton_);
+ buttonLayout_->addWidget(cancelButton_);
+ layout_->addLayout(buttonLayout_);
+
+ setMinimumWidth(800);
+ setMinimumHeight(600);
+
+ this->setWindowTitle(tr("FDP Form Submission"));
+}
+
+QtFdpFormSubmitWindow::~QtFdpFormSubmitWindow() {
+}
+
+void QtFdpFormSubmitWindow::closeEvent(QCloseEvent* event) {
+ event->ignore();
+ onCloseEvent();
+}
+
+void QtFdpFormSubmitWindow::initNodeViewLayout() {
+ nodeViewLayout_ = new QVBoxLayout(this);
+ auto domainSearchLayout = new QHBoxLayout(this);
+ pubSubDomainEdit_ = new QLineEdit(this);
+ loadDomainButton_ = new QPushButton(tr("Load"), this);
+ pubSubNodeView_ = new QtListWidget(this);
+ pubSubDomainEdit_->setPlaceholderText(tr("Enter pubsub domain here"));
+ pubSubNodeView_->setMinimumWidth(300);
+ connect(loadDomainButton_, &QPushButton::clicked, this, &QtFdpFormSubmitWindow::handleLoadDomainButtonClicked);
+ connect(pubSubNodeView_, &QListWidget::itemDoubleClicked, this, &QtFdpFormSubmitWindow::handlePubSubNodeViewItemDoubleClicked);
+ domainSearchLayout->addWidget(pubSubDomainEdit_);
+ domainSearchLayout->addWidget(loadDomainButton_);
+ nodeViewLayout_->addLayout(domainSearchLayout);
+ nodeViewLayout_->addWidget(pubSubNodeView_);
+}
+
+void QtFdpFormSubmitWindow::initFormLayout() {
+ formPlaceholder_ = new QLabel(tr("No form loaded"));
+ formPlaceholder_->setAlignment(Qt::AlignCenter);
+ formPlaceholder_->setMinimumWidth(200);
+ formPlaceholder_->setStyleSheet("QLabel { color : #AAAAAA; }");
+ formLayout_ = new QVBoxLayout(this);
+ formLayout_->addWidget(formPlaceholder_, Qt::AlignCenter);
+}
+
+void QtFdpFormSubmitWindow::show() {
+ QDialog::show();
+}
+
+void QtFdpFormSubmitWindow::raise() {
+ QDialog::raise();
+}
+
+void QtFdpFormSubmitWindow::addNode(const std::string& node, const std::string& nodeName) {
+ auto listItem = new QListWidgetItem(P2QSTRING(nodeName));
+ listItem->setData(Qt::UserRole, P2QSTRING(node));
+ pubSubNodeView_->addItem(listItem);
+}
+
+void QtFdpFormSubmitWindow::clearNodeData() {
+ pubSubNodeView_->clear();
+ pubSubNodeView_->setWordWrap(false);
+ disconnect(pubSubNodeView_, &QtListWidget::onResize, this, &QtFdpFormSubmitWindow::handleNodeListResize);
+}
+
+void QtFdpFormSubmitWindow::setFormData(const std::shared_ptr<Form>& form) {
+ if (formWidget_) {
+ formLayout_->removeWidget(formWidget_);
+ formWidget_->deleteLater();
+ formWidget_ = nullptr;
+ }
+ else if (!formPlaceholder_->isHidden()) {
+ formLayout_->removeWidget(formPlaceholder_);
+ formPlaceholder_->hide();
+ }
+ formWidget_ = new QtFormWidget(form);
+ formWidget_->setEditable(true);
+ formLayout_->addWidget(formWidget_);
+
+ if (!okButton_->isHidden()) {
+ buttonLayout_->removeWidget(okButton_);
+ okButton_->hide();
+ }
+ if (submitButton_->isHidden()) {
+ buttonLayout_->insertWidget(1, submitButton_);
+ submitButton_->show();
+ }
+ submitButton_->setEnabled(true);
+ cancelButton_->setEnabled(true);
+}
+
+void QtFdpFormSubmitWindow::showNodePlaceholder(NodeError nodeError) {
+ pubSubNodeView_->clear();
+ pubSubNodeView_->setWordWrap(true);
+ auto listItem = new QListWidgetItem;
+ auto placeholderText = P2QSTRING(getNodeErrorText(nodeError));
+ listItem->setText(placeholderText);
+ listItem->setTextAlignment(Qt::AlignCenter);
+ listItem->setFlags(Qt::NoItemFlags);
+ listItem->setSizeHint(QSize(listItem->sizeHint().width(), pubSubNodeView_->height()));
+ connect(pubSubNodeView_, &QtListWidget::onResize, this, &QtFdpFormSubmitWindow::handleNodeListResize);
+ pubSubNodeView_->addItem(listItem);
+}
+
+void QtFdpFormSubmitWindow::showFormPlaceholder(TemplateError templateError) {
+ if (formWidget_) {
+ formLayout_->removeWidget(formWidget_);
+ formWidget_->deleteLater();
+ formWidget_ = nullptr;
+ }
+ auto placeholderText = P2QSTRING(getTemplateErrorText(templateError));
+ formPlaceholder_->setText(placeholderText);
+ if (formPlaceholder_->isHidden()) {
+ formLayout_->addWidget(formPlaceholder_, Qt::AlignCenter);
+ formPlaceholder_->show();
+ }
+
+ if (!okButton_->isHidden()) {
+ buttonLayout_->removeWidget(okButton_);
+ okButton_->hide();
+ }
+ if (submitButton_->isHidden()) {
+ buttonLayout_->insertWidget(1, submitButton_);
+ submitButton_->show();
+ }
+ submitButton_->setEnabled(false);
+ cancelButton_->setEnabled(true);
+}
+
+void QtFdpFormSubmitWindow::handleLoadDomainButtonClicked() {
+ std::string domainUri = Q2PSTRING(pubSubDomainEdit_->text());
+ onRequestPubSubNodeData(domainUri);
+}
+
+void QtFdpFormSubmitWindow::handlePubSubListViewTemplateSelected(const std::string& nodeName) {
+ onRequestTemplateForm(nodeName);
+}
+
+void QtFdpFormSubmitWindow::handlePubSubNodeViewItemDoubleClicked(QListWidgetItem* item) {
+ handlePubSubListViewTemplateSelected(Q2PSTRING(item->data(Qt::UserRole).toString()));
+}
+
+void QtFdpFormSubmitWindow::handleSubmitClicked() {
+ auto form = formWidget_->getCompletedForm();
+ formWidget_->setDisabled(true);
+ submitButton_->setEnabled(false);
+ onSubmitForm(form);
+}
+
+void QtFdpFormSubmitWindow::handleSubmitServerResponse(bool submissionSuccess) {
+ if (submissionSuccess) {
+ if (!submitButton_->isHidden()) {
+ buttonLayout_->removeWidget(submitButton_);
+ submitButton_->hide();
+ }
+ if (okButton_->isHidden()) {
+ buttonLayout_->insertWidget(1, okButton_);
+ okButton_->show();
+ }
+ cancelButton_->setEnabled(false);
+ }
+ else {
+ formWidget_->setDisabled(false);
+ submitButton_->setEnabled(true);
+ }
+}
+
+void QtFdpFormSubmitWindow::handleNodeListResize() {
+ auto placeholderItem = pubSubNodeView_->item(0);
+ placeholderItem->setSizeHint(QSize(placeholderItem->sizeHint().width(), pubSubNodeView_->height()));
+}
+
+}
diff --git a/Swift/QtUI/QtFdpFormSubmitWindow.h b/Swift/QtUI/QtFdpFormSubmitWindow.h
new file mode 100644
index 0000000..b429927
--- /dev/null
+++ b/Swift/QtUI/QtFdpFormSubmitWindow.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <QDialog>
+
+#include <Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h>
+
+class QHBoxLayout;
+class QLabel;
+class QLineEdit;
+class QListWidgetItem;
+class QPushButton;
+class QTextEdit;
+class QVBoxLayout;
+
+namespace Swift {
+
+ class Form;
+ class QtFormWidget;
+ class QtListWidget;
+ class QtPubSubNodeController;
+
+ class QtFdpFormSubmitWindow : public QDialog, public FdpFormSubmitWindow {
+ Q_OBJECT
+
+ public:
+ QtFdpFormSubmitWindow(QWidget* parent = nullptr);
+ virtual ~QtFdpFormSubmitWindow() override;
+
+ protected:
+ virtual void closeEvent(QCloseEvent* event) override;
+
+ private:
+ void initNodeViewLayout();
+ void initFormLayout();
+ virtual void show() override;
+ virtual void raise() override;
+ virtual void addNode(const std::string& node, const std::string& nodeName) override;
+ virtual void clearNodeData() override;
+ virtual void setFormData(const std::shared_ptr<Form>& form) override;
+ virtual void showNodePlaceholder(NodeError nodeError) override;
+ virtual void showFormPlaceholder(TemplateError templateError) override;
+ virtual void handleSubmitServerResponse(bool submissionSuccess) override;
+
+ private slots:
+ void handleLoadDomainButtonClicked();
+ void handlePubSubListViewTemplateSelected(const std::string& nodeName);
+ void handlePubSubNodeViewItemDoubleClicked(QListWidgetItem* item);
+ void handleSubmitClicked();
+ void handleNodeListResize();
+
+ private:
+ QVBoxLayout* layout_;
+ QHBoxLayout* subLayout_;
+ QHBoxLayout* buttonLayout_;
+ QVBoxLayout* nodeViewLayout_;
+ QVBoxLayout* formLayout_;
+ QLineEdit* pubSubDomainEdit_;
+ QPushButton* loadDomainButton_;
+ QtListWidget* pubSubNodeView_;
+ QLabel* formPlaceholder_;
+ QTextEdit* nodePlaceholderTextEdit_ = nullptr;
+ QtFormWidget* formWidget_ = nullptr;
+ QPushButton* cancelButton_;
+ QPushButton* submitButton_ = nullptr;
+ QPushButton* okButton_ = nullptr;
+ };
+}
diff --git a/Swift/QtUI/QtFormWidget.cpp b/Swift/QtUI/QtFormWidget.cpp
index 96c2da0..860ddfa 100644
--- a/Swift/QtUI/QtFormWidget.cpp
+++ b/Swift/QtUI/QtFormWidget.cpp
@@ -147,7 +147,7 @@ QWidget* QtFormWidget::createWidget(FormField::ref field, const FormField::Type
std::string indexString;
if (index) {
/* for multi-item forms we need to distinguish between the different rows */
- indexString = boost::lexical_cast<std::string>(index);
+ indexString = std::to_string(index);
}
fields_[field->getName() + indexString] = widget;
return widget;
diff --git a/Swift/QtUI/QtHighlightNotificationConfigDialog.cpp b/Swift/QtUI/QtHighlightNotificationConfigDialog.cpp
index 19274a2..0521a2d 100644
--- a/Swift/QtUI/QtHighlightNotificationConfigDialog.cpp
+++ b/Swift/QtUI/QtHighlightNotificationConfigDialog.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017 Isode Limited.
+ * Copyright (c) 2016-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -50,7 +50,7 @@ QtHighlightNotificationConfigDialog::QtHighlightNotificationConfigDialog(QtSetti
}
});
connect(ui_.userHighlightTreeWidget, &QTreeWidget::currentItemChanged, [&](QTreeWidgetItem* current, QTreeWidgetItem* ) {
- ui_.removeUserHighlightPushButton->setEnabled(current != 0);
+ ui_.removeUserHighlightPushButton->setEnabled(current != nullptr);
});
// keyword highlight edit slots
@@ -72,7 +72,7 @@ QtHighlightNotificationConfigDialog::QtHighlightNotificationConfigDialog(QtSetti
}
});
connect(ui_.keywordHighlightTreeWidget, &QTreeWidget::currentItemChanged, [&](QTreeWidgetItem* current, QTreeWidgetItem* ) {
- ui_.removeKeywordHighlightPushButton->setEnabled(current != 0);
+ ui_.removeKeywordHighlightPushButton->setEnabled(current != nullptr);
});
// setup slots for main dialog buttons
diff --git a/Swift/QtUI/QtHistoryWindow.cpp b/Swift/QtUI/QtHistoryWindow.cpp
index 77a7f12..0e7e89d 100644
--- a/Swift/QtUI/QtHistoryWindow.cpp
+++ b/Swift/QtUI/QtHistoryWindow.cpp
@@ -49,7 +49,7 @@ QtHistoryWindow::QtHistoryWindow(SettingsProvider* settings, UIEventStream* even
idCounter_ = 0;
delete ui_.conversation_;
- conversation_ = new QtWebKitChatView(nullptr, nullptr, theme_, this, true); // Horrible unsafe. Do not do this. FIXME
+ conversation_ = new QtWebKitChatView(nullptr, nullptr, theme_, this, settings, true); // Horrible unsafe. Do not do this. FIXME
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(80);
sizePolicy.setVerticalStretch(0);
@@ -131,7 +131,7 @@ void QtHistoryWindow::addMessage(const std::string &message, const std::string &
QTime dayTime = QTime(time.time_of_day().hours(), time.time_of_day().minutes(), time.time_of_day().seconds());
QDateTime qTime = QDateTime(date, dayTime);
- std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+ std::string id = "id" + std::to_string(idCounter_++);
QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
diff --git a/Swift/QtUI/QtJoinMUCWindow.cpp b/Swift/QtUI/QtJoinMUCWindow.cpp
index 13de1c9..550ae4a 100644
--- a/Swift/QtUI/QtJoinMUCWindow.cpp
+++ b/Swift/QtUI/QtJoinMUCWindow.cpp
@@ -49,7 +49,7 @@ void QtJoinMUCWindow::handleJoin() {
lastSetNick = Q2PSTRING(ui.nickName->text());
std::string password = Q2PSTRING(ui.password->text());
JID room(Q2PSTRING(ui.room->text()));
- uiEventStream->send(std::make_shared<JoinMUCUIEvent>(room, password, lastSetNick, ui.joinAutomatically->isChecked(), !ui.instantRoom->isChecked()));
+ uiEventStream->send(std::make_shared<JoinMUCUIEvent>(room, password, lastSetNick, !ui.instantRoom->isChecked()));
hide();
}
diff --git a/Swift/QtUI/QtJoinMUCWindow.ui b/Swift/QtUI/QtJoinMUCWindow.ui
index 24d6ab8..96f1d17 100644
--- a/Swift/QtUI/QtJoinMUCWindow.ui
+++ b/Swift/QtUI/QtJoinMUCWindow.ui
@@ -101,13 +101,6 @@
</spacer>
</item>
<item>
- <widget class="QCheckBox" name="joinAutomatically">
- <property name="text">
- <string>Enter automatically in future</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QPushButton" name="joinButton">
<property name="text">
<string>Enter Room</string>
@@ -124,7 +117,6 @@
<tabstop>nickName</tabstop>
<tabstop>password</tabstop>
<tabstop>instantRoom</tabstop>
- <tabstop>joinAutomatically</tabstop>
<tabstop>joinButton</tabstop>
</tabstops>
<resources/>
diff --git a/Swift/QtUI/QtListWidget.cpp b/Swift/QtUI/QtListWidget.cpp
new file mode 100644
index 0000000..e35bbbb
--- /dev/null
+++ b/Swift/QtUI/QtListWidget.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtListWidget.h>
+
+#include <QListWidget>
+
+namespace Swift {
+
+QtListWidget::QtListWidget(QWidget* parent) : QListWidget(parent) {
+}
+
+void QtListWidget::resizeEvent(QResizeEvent*) {
+ emit onResize();
+}
+
+}
diff --git a/Swift/QtUI/QtListWidget.h b/Swift/QtUI/QtListWidget.h
new file mode 100644
index 0000000..b7380c4
--- /dev/null
+++ b/Swift/QtUI/QtListWidget.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QListWidget>
+
+namespace Swift {
+
+class QtListWidget : public QListWidget {
+ Q_OBJECT
+ public:
+ QtListWidget(QWidget* parent = nullptr);
+ protected:
+ virtual void resizeEvent(QResizeEvent*);
+ signals:
+ void onResize();
+};
+
+}
diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp
index 654e921..8fdce4d 100644
--- a/Swift/QtUI/QtLoginWindow.cpp
+++ b/Swift/QtUI/QtLoginWindow.cpp
@@ -573,4 +573,8 @@ void QtLoginWindow::handleOpenConnectionOptions() {
}
}
+QSize QtLoginWindow::sizeHint() const {
+ return QSize(250, 600);
+}
+
}
diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h
index 674e1e3..d3c2601 100644
--- a/Swift/QtUI/QtLoginWindow.h
+++ b/Swift/QtUI/QtLoginWindow.h
@@ -54,12 +54,15 @@ namespace Swift {
void hide();
QtMenus getMenus() const;
virtual void quit();
+ QSize sizeHint() const;
signals:
void geometryChanged();
- private slots:
+ public slots:
void loginClicked();
+
+ private slots:
void handleCertficateChecked(bool);
void handleQuit();
void handleShowXMLConsole();
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 0c1dd97..92488ae 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -19,6 +19,7 @@
#include <QListWidgetItem>
#include <QMenuBar>
#include <QPushButton>
+#include <QScrollArea>
#include <QTabWidget>
#include <QToolBar>
@@ -26,6 +27,7 @@
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h>
@@ -35,6 +37,7 @@
#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h>
#include <Swift/QtUI/QtAdHocCommandWithJIDWindow.h>
+#include <Swift/QtUI/QtChatOverview.h>
#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtSettingsProvider.h>
#include <Swift/QtUI/QtSwiftUtil.h>
@@ -52,7 +55,7 @@
namespace Swift {
-QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist, bool enableAdHocCommandOnJID) : QWidget(), MainWindow(false), loginMenus_(loginMenus) {
+QtMainWindow::QtMainWindow(Chattables& chattables, SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist, bool enableAdHocCommandOnJID) : QWidget(), MainWindow(false), chattables_(chattables), loginMenus_(loginMenus) {
uiEventStream_ = uiEventStream;
settings_ = settings;
setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
@@ -66,11 +69,22 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
connect(meView_, SIGNAL(onShowCertificateInfo()), this, SLOT(handleShowCertificateInfo()));
tabs_ = new QtTabWidget(this);
-#if QT_VERSION >= 0x040500
tabs_->setDocumentMode(true);
-#endif
tabs_->setTabPosition(QTabWidget::South);
mainLayout->addWidget(tabs_);
+
+ if (settings->getSetting(SettingConstants::FUTURE)) {
+ chatOverview_ = new QtChatOverview(chattables, this);
+ auto overviewScroll = new QScrollArea(this);
+ overviewScroll->setWidgetResizable(true);
+ overviewScroll->setWidget(chatOverview_);
+ tabs_->addTab(overviewScroll, tr("&All"));
+
+ // When used with QSplitter and setChildrenCollapsible(false), the following prevents
+ // this widget to be hidden, i.e. resized to zero width.
+ chatOverview_->setMinimumWidth(20);
+ }
+
contactsTabWidget_ = new QWidget(this);
contactsTabWidget_->setContentsMargins(0, 0, 0, 0);
QBoxLayout *contactTabLayout = new QBoxLayout(QBoxLayout::TopToBottom, contactsTabWidget_);
@@ -82,9 +96,7 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
contactTabLayout->addWidget(treeWidget_);
new QtFilterWidget(this, treeWidget_, uiEventStream_, contactTabLayout);
-
tabs_->addTab(contactsTabWidget_, tr("&Contacts"));
-
eventWindow_ = new QtEventWindow(uiEventStream_);
connect(eventWindow_, SIGNAL(onNewEventCountUpdated(int)), this, SLOT(handleEventCountUpdated(int)));
@@ -103,8 +115,11 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
tabs_->tabBar()->hide();
tabBarCombo_ = new QComboBox(this);
tabBarCombo_->setAccessibleName("Current View");
+ tabBarCombo_->addItem(tr("All"));
+#ifndef NOT_YET
tabBarCombo_->addItem(tr("Contacts"));
tabBarCombo_->addItem(tr("Chats"));
+#endif
tabBarCombo_->addItem(tr("Notices"));
tabBarCombo_->setCurrentIndex(tabs_->currentIndex());
mainLayout->addWidget(tabBarCombo_);
@@ -185,6 +200,13 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
}
serverAdHocMenu_ = new QMenu(tr("Run Server Command"), this);
actionsMenu->addMenu(serverAdHocMenu_);
+ if (settings_->getSetting(SettingConstants::FUTURE)) {
+ actionsMenu->addSeparator();
+ submitFormAction_ = new QAction(tr("Submit Form"), this);
+ connect(submitFormAction_, &QAction::triggered, this, &QtMainWindow::handleSubmitFormActionTriggered);
+ actionsMenu->addAction(submitFormAction_);
+ onlineOnlyActions_ << submitFormAction_;
+ }
actionsMenu->addSeparator();
QAction* signOutAction = new QAction(tr("&Sign Out"), this);
connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction()));
@@ -423,5 +445,8 @@ void QtMainWindow::setBlockingCommandAvailable(bool isAvailable) {
openBlockingListEditor_->setVisible(isAvailable);
}
+void QtMainWindow::handleSubmitFormActionTriggered() {
+ uiEventStream_->send(std::make_shared<FdpFormSubmitWindowOpenUIEvent>());
}
+}
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index c46fdfc..b285831 100644
--- a/Swift/QtUI/QtMainWindow.h
+++ b/Swift/QtUI/QtMainWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -12,6 +12,7 @@
#include <QMenu>
#include <QWidget>
+#include <Swift/Controllers/Chat/Chattables.h>
#include <Swift/Controllers/UIInterfaces/MainWindow.h>
#include <Swift/QtUI/ChatList/QtChatListWindow.h>
@@ -19,44 +20,45 @@
#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtRosterHeader.h>
+class QAction;
class QComboBox;
class QLineEdit;
-class QPushButton;
-class QToolBar;
-class QAction;
class QMenu;
+class QPushButton;
class QTabWidget;
+class QToolBar;
namespace Swift {
+ class QtChatOverview;
class QtRosterWidget;
- class TreeWidget;
- class UIEventStream;
class QtTabWidget;
- class SettingsProvider;
class QtUIPreferences;
+ class SettingsProvider;
class StatusCache;
+ class TreeWidget;
+ class UIEventStream;
class QtMainWindow : public QWidget, public MainWindow {
Q_OBJECT
public:
- QtMainWindow(SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist, bool enableAdHocCommandOnJID);
- virtual ~QtMainWindow();
+ QtMainWindow(Chattables&, SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist, bool enableAdHocCommandOnJID);
+ virtual ~QtMainWindow() override;
std::vector<QMenu*> getMenus() {return menus_;}
- void setMyNick(const std::string& name);
- void setMyJID(const JID& jid);
- void setMyAvatarPath(const std::string& path);
- void setMyStatusText(const std::string& status);
- void setMyStatusType(StatusShow::Type type);
- void setMyContactRosterItem(std::shared_ptr<ContactRosterItem> contact);
- void setConnecting();
- void setStreamEncryptionStatus(bool tlsInPlaceAndValid);
- void openCertificateDialog(const std::vector<Certificate::ref>& chain);
+ void setMyNick(const std::string& name) override;
+ void setMyJID(const JID& jid) override;
+ void setMyAvatarPath(const std::string& path) override;
+ void setMyStatusText(const std::string& status) override;
+ void setMyStatusType(StatusShow::Type type) override;
+ void setMyContactRosterItem(std::shared_ptr<ContactRosterItem> contact) override;
+ void setConnecting() override;
+ void setStreamEncryptionStatus(bool tlsInPlaceAndValid) override;
+ void openCertificateDialog(const std::vector<Certificate::ref>& chain) override;
static void openCertificateDialog(const std::vector<Certificate::ref>& chain, QWidget* parent);
QtEventWindow* getEventWindow();
QtChatListWindow* getChatListWindow();
- void setRosterModel(Roster* roster);
- void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands);
- void setBlockingCommandAvailable(bool isAvailable);
+ void setRosterModel(Roster* roster) override;
+ void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) override;
+ void setBlockingCommandAvailable(bool isAvailable) override;
private slots:
void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);
void handleSettingChanged(const std::string& settingPath);
@@ -79,8 +81,10 @@ namespace Swift {
void handleShowCertificateInfo();
void handleEditBlockingList();
void handleSomethingSelectedChanged(bool itemSelected);
+ void handleSubmitFormActionTriggered();
private:
+ Chattables& chattables_;
SettingsProvider* settings_;
QtLoginWindow::QtMenus loginMenus_;
std::vector<QMenu*> menus_;
@@ -98,6 +102,7 @@ namespace Swift {
QMenu* serverAdHocMenu_;
QtTabWidget* tabs_;
QComboBox* tabBarCombo_;
+ QtChatOverview* chatOverview_;
QWidget* contactsTabWidget_;
QWidget* eventsTabWidget_;
QtEventWindow* eventWindow_;
@@ -106,5 +111,6 @@ namespace Swift {
std::vector<DiscoItems::Item> serverAdHocCommands_;
QList<QAction*> serverAdHocCommandActions_;
QList<QAction*> onlineOnlyActions_;
+ QAction* submitFormAction_;
};
}
diff --git a/Swift/QtUI/QtPlainChatView.cpp b/Swift/QtUI/QtPlainChatView.cpp
index 7e9c857..cdee1e6 100644
--- a/Swift/QtUI/QtPlainChatView.cpp
+++ b/Swift/QtUI/QtPlainChatView.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017 Isode Limited.
+ * Copyright (c) 2013-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -42,7 +42,7 @@ QtPlainChatView::QtPlainChatView(QtChatWindow *window, UIEventStream* eventStrea
QtPlainChatView::~QtPlainChatView() {
}
-QString chatMessageToString(const ChatWindow::ChatMessage& message) {
+static QString chatMessageToString(const ChatWindow::ChatMessage& message) {
QString result;
for (auto&& part : message.getParts()) {
std::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
@@ -178,7 +178,7 @@ void QtPlainChatView::setAckState(const std::string& /*id*/, ChatWindow::AckStat
std::string QtPlainChatView::addFileTransfer(const std::string& senderName, const std::string& /*avatarPath*/, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes, const std::string& description)
{
- const std::string ftId = "ft" + boost::lexical_cast<std::string>(idGenerator_++);
+ const std::string ftId = "ft" + std::to_string(idGenerator_++);
const std::string sizeString = formatSize(sizeInBytes);
FileTransfer* transfer;
@@ -327,7 +327,7 @@ void QtPlainChatView::acceptMUCInvite()
{
AcceptMUCInviteAction *action = dynamic_cast<AcceptMUCInviteAction*>(sender());
if (action) {
- eventStream_->send(std::make_shared<JoinMUCUIEvent>(action->jid_.toString(), action->password_, boost::optional<std::string>(), false, false, action->isImpromptu_, action->isContinuation_));
+ eventStream_->send(std::make_shared<JoinMUCUIEvent>(action->jid_.toString(), action->password_, boost::optional<std::string>(), false, action->isImpromptu_, action->isContinuation_));
delete action->parent_;
}
}
diff --git a/Swift/QtUI/QtScaledAvatarCache.cpp b/Swift/QtUI/QtScaledAvatarCache.cpp
index 37ea6a9..e3a28d6 100644
--- a/Swift/QtUI/QtScaledAvatarCache.cpp
+++ b/Swift/QtUI/QtScaledAvatarCache.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 Isode Limited.
+ * Copyright (c) 2011-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -53,8 +53,8 @@ QString QtScaledAvatarCache::getScaledAvatarPath(const QString& path) {
if (avatarFile.exists() && !avatarFile.absolutePath().startsWith(":/")) {
QString cacheSubPath = QString("ScaledAvatarCacheV%1/%2").arg(QString::number(QT_SCALED_AVATAR_CACHE_VERSION), QString::number(size));
if (!avatarFile.dir().mkpath(cacheSubPath)) {
- SWIFT_LOG(error) << "avatarFile.dir(): " << Q2PSTRING(avatarFile.dir().absolutePath()) << std::endl;
- SWIFT_LOG(error) << "Failed creating cache folder: " << Q2PSTRING(cacheSubPath) << std::endl;
+ SWIFT_LOG(error) << "avatarFile.dir(): " << Q2PSTRING(avatarFile.dir().absolutePath());
+ SWIFT_LOG(error) << "Failed creating cache folder: " << Q2PSTRING(cacheSubPath);
return path;
}
QDir targetDir(avatarFile.dir().absoluteFilePath(cacheSubPath));
@@ -75,7 +75,7 @@ QString QtScaledAvatarCache::getScaledAvatarPath(const QString& path) {
return path;
}
} else {
- SWIFT_LOG(warning) << "Failed to load " << Q2PSTRING(path) << std::endl;
+ SWIFT_LOG(warning) << "Failed to load " << Q2PSTRING(path);
}
}
return targetFile;
diff --git a/Swift/QtUI/QtSingleWindow.cpp b/Swift/QtUI/QtSingleWindow.cpp
index 1fba497..af7e552 100644
--- a/Swift/QtUI/QtSingleWindow.cpp
+++ b/Swift/QtUI/QtSingleWindow.cpp
@@ -1,13 +1,21 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/QtUI/QtSingleWindow.h>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+#include <Swiften/Base/Platform.h>
+
#include <Swift/QtUI/QtChatTabs.h>
+#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtSettingsProvider.h>
+#include <Swift/QtUI/ServerList/QtServerListView.h>
+#include <Swift/QtUI/ServerList/ServerListModel.h>
namespace Swift {
@@ -16,31 +24,49 @@ static const QString SINGLE_WINDOW_SPLITS = QString("SINGLE_WINDOW_SPLITS");
QtSingleWindow::QtSingleWindow(QtSettingsProvider* settings) : QSplitter() {
settings_ = settings;
- QVariant geometryVariant = settings_->getQSettings()->value(SINGLE_WINDOW_GEOMETRY);
+ auto geometryVariant = settings_->getQSettings()->value(SINGLE_WINDOW_GEOMETRY);
if (geometryVariant.isValid()) {
restoreGeometry(geometryVariant.toByteArray());
}
- connect(this, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(int, int)));
+ connect(this, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(/*int, int*/)));
+ setChildrenCollapsible(false);
+
+ auto left = new QWidget(this);
+ serverList_ = new QtServerListView();
+ serverListModel_ = new ServerListModel();
+ serverList_->setModel(serverListModel_);
+ serverListModel_->setModelData(&accountData_);
+ accountData_.onDataChanged.connect(boost::bind(&ServerListModel::handleDataChanged, serverListModel_));
+ auto addButton = new QPushButton("+", left);
+ QVBoxLayout* leftLayout = new QVBoxLayout();
+ leftLayout->addWidget(serverList_);
+ leftLayout->addWidget(addButton);
+ left->setLayout(leftLayout);
+ QSplitter::addWidget(left);
+ loginWindows_ = new QStackedWidget(this);
+ QSplitter::addWidget(loginWindows_);
+ tabs_ = new QStackedWidget(this);
+ QSplitter::addWidget(tabs_);
restoreSplitters();
+ setStretchFactor(0, 0);
+ setStretchFactor(1, 0);
+ setStretchFactor(2, 1);
+ connect(serverList_, &QtServerListView::clicked, this, &QtSingleWindow::handleListItemClicked);
+ connect(addButton, &QPushButton::clicked, this, &QtSingleWindow::wantsToAddAccount);
+#ifdef SWIFTEN_PLATFORM_MACOSX
+ setHandleWidth(0);
+#endif
}
QtSingleWindow::~QtSingleWindow() {
}
-void QtSingleWindow::addWidget(QWidget* widget) {
- QtChatTabs* tabs = dynamic_cast<QtChatTabs*>(widget);
- if (tabs) {
- connect(tabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
- }
- QSplitter::addWidget(widget);
-}
-
void QtSingleWindow::handleTabsTitleChanged(const QString& title) {
setWindowTitle(title);
}
-void QtSingleWindow::handleSplitterMoved(int, int) {
+void QtSingleWindow::handleSplitterMoved() {
QList<QVariant> variantValues;
QList<int> intValues = sizes();
for (const auto& value : intValues) {
@@ -50,17 +76,18 @@ void QtSingleWindow::handleSplitterMoved(int, int) {
}
void QtSingleWindow::restoreSplitters() {
- QList<QVariant> variantValues = settings_->getQSettings()->value(SINGLE_WINDOW_SPLITS).toList();
- QList<int> intValues;
- for (auto&& value : variantValues) {
- intValues.append(value.toInt());
+ auto splitsVariant = settings_->getQSettings()->value(SINGLE_WINDOW_SPLITS);
+ if (splitsVariant.isValid()) {
+ auto variantValues = splitsVariant.toList();
+ QList<int> intValues;
+ for (auto&& value : variantValues) {
+ intValues.append(value.toInt());
+ }
+ setSizes(intValues);
+ }
+ else {
+ handleSplitterMoved();
}
- setSizes(intValues);
-}
-
-void QtSingleWindow::insertAtFront(QWidget* widget) {
- insertWidget(0, widget);
- restoreSplitters();
}
void QtSingleWindow::handleGeometryChanged() {
@@ -76,4 +103,24 @@ void QtSingleWindow::moveEvent(QMoveEvent*) {
handleGeometryChanged();
}
+void QtSingleWindow::addAccount(QtLoginWindow* loginWindow, QtChatTabs* tabs) {
+ loginWindows_->addWidget(loginWindow);
+ tabs_->addWidget(tabs);
+ std::string account = QString("Account %1").arg(loginWindows_->count()).toStdString();
+ accountData_.addAccount(account);
+ emit serverListModel_->layoutChanged();
+}
+
+void QtSingleWindow::handleListItemClicked(const QModelIndex& item) {
+ auto currentTabs = tabs_->widget(tabs_->currentIndex());
+ disconnect(currentTabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
+ loginWindows_->setCurrentIndex(item.row());
+ tabs_->setCurrentIndex(item.row());
+ currentTabs = tabs_->widget(tabs_->currentIndex());
+ connect(currentTabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
+ //TODO change the title of the window.
+ handleTabsTitleChanged(QString("Swift"));
+
+}
+
}
diff --git a/Swift/QtUI/QtSingleWindow.h b/Swift/QtUI/QtSingleWindow.h
index 804be65..a707cd3 100644
--- a/Swift/QtUI/QtSingleWindow.h
+++ b/Swift/QtUI/QtSingleWindow.h
@@ -1,29 +1,41 @@
/*
- * Copyright (c) 2010-2012 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <QListWidget>
#include <QSplitter>
+#include <QStackedWidget>
+
+#include <Swift/QtUI/ServerList/ServerListModel.h>
namespace Swift {
+ class QtChatTabs;
+ class QtLoginWindow;
class QtSettingsProvider;
+ class QtServerListView;
+ class ServerListModel;
class QtSingleWindow : public QSplitter {
Q_OBJECT
public:
QtSingleWindow(QtSettingsProvider* settings);
virtual ~QtSingleWindow();
- void insertAtFront(QWidget* widget);
- void addWidget(QWidget* widget);
+ void addAccount(QtLoginWindow* widget, QtChatTabs* tabs);
+
+ signals:
+ void wantsToAddAccount();
+
protected:
void resizeEvent(QResizeEvent*);
void moveEvent(QMoveEvent*);
private slots:
- void handleSplitterMoved(int, int);
+ void handleSplitterMoved();
void handleTabsTitleChanged(const QString& title);
+ void handleListItemClicked(const QModelIndex&);
private:
void handleGeometryChanged();
void restoreSplitters();
@@ -31,6 +43,11 @@ namespace Swift {
private:
QtSettingsProvider* settings_;
+ SwiftAccountData accountData_;
+ QtServerListView* serverList_;
+ ServerListModel* serverListModel_;
+ QStackedWidget* loginWindows_;
+ QStackedWidget* tabs_;
};
}
diff --git a/Swift/QtUI/QtSoundSelectionStyledItemDelegate.h b/Swift/QtUI/QtSoundSelectionStyledItemDelegate.h
index fabf668..f03cacc 100644
--- a/Swift/QtUI/QtSoundSelectionStyledItemDelegate.h
+++ b/Swift/QtUI/QtSoundSelectionStyledItemDelegate.h
@@ -11,6 +11,7 @@
namespace Swift {
class QtSoundSelectionStyledItemDelegate : public QStyledItemDelegate {
+ Q_OBJECT
public:
QtSoundSelectionStyledItemDelegate(QObject* parent = nullptr);
diff --git a/Swift/QtUI/QtSpellCheckerWindow.cpp b/Swift/QtUI/QtSpellCheckerWindow.cpp
index a8178c4..23b0963 100644
--- a/Swift/QtUI/QtSpellCheckerWindow.cpp
+++ b/Swift/QtUI/QtSpellCheckerWindow.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (c) 2016 Isode Limited.
+ * Copyright (c) 2016-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -64,7 +64,7 @@ void QtSpellCheckerWindow::setSupportedLanguages(const std::vector<std::string>&
}
void QtSpellCheckerWindow::setActiveLanguage(const std::string& language) {
- SWIFT_LOG_ASSERT(languageItems_.find(language) != languageItems_.end(), warning) << "Language '" << language << "' is not available." << std::endl;
+ SWIFT_LOG_ASSERT(languageItems_.find(language) != languageItems_.end(), warning) << "Language '" << language << "' is not available.";
if (languageItems_.find(language) != languageItems_.end()) {
languageItems_[language]->setSelected(true);
}
diff --git a/Swift/QtUI/QtStatusWidget.cpp b/Swift/QtUI/QtStatusWidget.cpp
index b175e5c..5e2ba5f 100644
--- a/Swift/QtUI/QtStatusWidget.cpp
+++ b/Swift/QtUI/QtStatusWidget.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -8,9 +8,6 @@
#include <algorithm>
-#include <boost/lambda/bind.hpp>
-#include <boost/lambda/lambda.hpp>
-
#include <QApplication>
#include <QBoxLayout>
#include <QComboBox>
@@ -32,8 +29,6 @@
#include <Swift/QtUI/QtLineEdit.h>
#include <Swift/QtUI/QtSwiftUtil.h>
-namespace lambda = boost::lambda;
-
namespace Swift {
QtStatusWidget::QtStatusWidget(StatusCache* statusCache, QWidget *parent) : QWidget(parent), statusCache_(statusCache), editCursor_(Qt::IBeamCursor), viewCursor_(Qt::PointingHandCursor) {
@@ -153,8 +148,9 @@ void QtStatusWidget::generateList() {
}
std::vector<StatusCache::PreviousStatus> previousStatuses = statusCache_->getMatches(Q2PSTRING(text), 8);
for (const auto& savedStatus : previousStatuses) {
- if (savedStatus.first.empty() || std::find_if(allTypes_.begin(), allTypes_.end(),
- savedStatus.second == lambda::_1 && savedStatus.first == lambda::bind(&statusShowTypeToFriendlyName, lambda::_1)) != allTypes_.end()) {
+ if (savedStatus.first.empty() || std::find_if(allTypes_.begin(), allTypes_.end(), [&](StatusShow::Type type) {
+ return (savedStatus.second == type) && (savedStatus.first == statusShowTypeToFriendlyName(type));
+ }) != allTypes_.end()) {
continue;
}
QListWidgetItem* item = new QListWidgetItem(P2QSTRING(savedStatus.first), menu_);
diff --git a/Swift/QtUI/QtStrings.h b/Swift/QtUI/QtStrings.h
index d0cd421..84bc7f9 100644
--- a/Swift/QtUI/QtStrings.h
+++ b/Swift/QtUI/QtStrings.h
@@ -26,6 +26,14 @@ QT_TRANSLATE_NOOP("QLineEdit", "&Copy");
QT_TRANSLATE_NOOP("QLineEdit", "&Paste");
QT_TRANSLATE_NOOP("QLineEdit", "Delete");
+QT_TRANSLATE_NOOP("QWidgetTextControl", "Select All");
+QT_TRANSLATE_NOOP("QWidgetTextControl", "&Undo");
+QT_TRANSLATE_NOOP("QWidgetTextControl", "&Redo");
+QT_TRANSLATE_NOOP("QWidgetTextControl", "Cu&t");
+QT_TRANSLATE_NOOP("QWidgetTextControl", "&Copy");
+QT_TRANSLATE_NOOP("QWidgetTextControl", "&Paste");
+QT_TRANSLATE_NOOP("QWidgetTextControl", "Delete");
+
QT_TRANSLATE_NOOP("QScrollBar", "Scroll here");
QT_TRANSLATE_NOOP("QScrollBar", "Top");
QT_TRANSLATE_NOOP("QScrollBar", "Bottom");
@@ -76,6 +84,10 @@ QT_TRANSLATE_NOOP("QDialogButtonBox", "Cancel");
QT_TRANSLATE_NOOP("QMessageBox", "Show Details...");
QT_TRANSLATE_NOOP("QMessageBox", "Hide Details...");
+QT_TRANSLATE_NOOP("QPlatformTheme", "OK");
+QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel");
+QT_TRANSLATE_NOOP("QPlatformTheme", "Restore Defaults");
+
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Services");
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Hide %1");
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Hide Others");
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 7b4e2c3..73fd733 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -22,8 +22,10 @@
#include <Swiften/Base/Path.h>
#include <Swiften/Base/Paths.h>
#include <Swiften/Base/Platform.h>
+#include <Swiften/Base/String.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Presence.h>
+#include <Swiften/StringCodecs/Base64.h>
#include <Swiften/TLS/TLSContextFactory.h>
#include <SwifTools/Application/PlatformApplicationPathProvider.h>
@@ -33,7 +35,7 @@
#include <Swift/Controllers/ApplicationInfo.h>
#include <Swift/Controllers/BuildVersion.h>
-#include <Swift/Controllers/MainController.h>
+#include <Swift/Controllers/AccountController.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/Settings/SettingsProviderHierachy.h>
#include <Swift/Controllers/Settings/XMLSettingsProvider.h>
@@ -42,8 +44,6 @@
#include <Swift/Controllers/Storages/FileStoragesFactory.h>
#include <Swift/QtUI/QtChatTabs.h>
-#include <Swift/QtUI/QtChatTabsBase.h>
-#include <Swift/QtUI/QtChatTabsShortcutOnlySubstitute.h>
#include <Swift/QtUI/QtChatWindowFactory.h>
#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtSingleWindow.h>
@@ -92,15 +92,15 @@ po::options_description QtSwift::getOptionsDescription() {
("debug", "Turn on debug logging")
("help", "Show this help message")
("version", "Show version information")
- ("netbook-mode", "Use netbook mode display (unsupported)")
("no-tabs", "Don't manage chat windows in tabs (unsupported)")
("latency-debug", "Use latency debugging (unsupported)")
- ("multi-account", po::value<int>()->default_value(1), "Number of accounts to open windows for (unsupported)")
- ("start-minimized", "Don't show the login/roster window at startup")
- ("enable-jid-adhocs", "Enable AdHoc commands to custom JID's.")
+ ("enable-jid-adhocs", "Enable AdHoc commands to custom JIDs.")
#if QT_VERSION >= 0x040800
("language", po::value<std::string>(), "Use a specific language, instead of the system-wide one")
#endif
+ ("logfile", po::value<std::string>()->implicit_value(""), "Save all logging information to a file")
+ ("enable-future", "Enable future features (unsupported). This will persist across restarts")
+ ("disable-future", "Disable future features. This will persist across restarts")
;
return result;
}
@@ -161,34 +161,42 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
qtSettings_ = new QtSettingsProvider();
xmlSettings_ = loadSettingsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "system-settings.xml")));
- settingsHierachy_ = new SettingsProviderHierachy();
- settingsHierachy_->addProviderToTopOfStack(xmlSettings_);
- settingsHierachy_->addProviderToTopOfStack(qtSettings_);
+ settingsHierarchy_ = new SettingsProviderHierachy();
+ settingsHierarchy_->addProviderToTopOfStack(xmlSettings_);
+ settingsHierarchy_->addProviderToTopOfStack(qtSettings_);
- networkFactories_.getTLSContextFactory()->setDisconnectOnCardRemoval(settingsHierachy_->getSetting(SettingConstants::DISCONNECT_ON_CARD_REMOVAL));
+ networkFactories_.getTLSContextFactory()->setDisconnectOnCardRemoval(settingsHierarchy_->getSetting(SettingConstants::DISCONNECT_ON_CARD_REMOVAL));
- std::map<std::string, std::string> emoticons;
- loadEmoticonsFile(":/emoticons/emoticons.txt", emoticons);
- loadEmoticonsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "emoticons.txt")), emoticons);
+ loadEmoticonsFile(":/emoticons/emoticons.txt", emoticons_);
+ loadEmoticonsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "emoticons.txt")), emoticons_);
- if (options.count("netbook-mode")) {
- splitter_ = new QtSingleWindow(qtSettings_);
- } else {
- splitter_ = nullptr;
+ splitter_ = new QtSingleWindow(qtSettings_);
+ connect(splitter_, SIGNAL(wantsToAddAccount()), this, SLOT(handleWantsToAddAccount()));
+
+ if (options.count("debug")) {
+ Log::setLogLevel(Swift::Log::debug);
}
- int numberOfAccounts = 1;
- try {
- numberOfAccounts = options["multi-account"].as<int>();
- } catch (...) {
- /* This seems to fail on a Mac when the .app is launched directly (the usual path).*/
- numberOfAccounts = 1;
+ if (options.count("enable-future")) {
+ settingsHierarchy_->storeSetting(SettingConstants::FUTURE, true);
}
- if (options.count("debug")) {
- Log::setLogLevel(Swift::Log::debug);
+ if (options.count("disable-future")) {
+ settingsHierarchy_->storeSetting(SettingConstants::FUTURE, false);
}
+ if (options.count("logfile")) {
+ try {
+ std::string fileName = options["logfile"].as<std::string>();
+ Log::setLogFile(fileName);
+ }
+ catch (...) {
+ SWIFT_LOG(error) << "Error while retrieving the specified log file name from the command line";
+ }
+ }
+ //TODO this old option can be purged
+ useDelayForLatency_ = options.count("latency-debug") > 0;
+
// Load fonts
std::vector<std::string> fontNames = {
"themes/Default/Lato2OFL/Lato-Black.ttf",
@@ -214,22 +222,22 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
for (auto&& fontName : fontNames) {
std::string fontPath = std::string(":/") + fontName;
int error = QFontDatabase::addApplicationFont(P2QSTRING(fontPath));
- assert((error != -1) && "Failed to load font.");
+ SWIFT_LOG_ASSERT(error != -1, error) << "Failed to load font " << fontPath;
}
- bool enableAdHocCommandOnJID = options.count("enable-jid-adhocs") > 0;
- tabs_ = nullptr;
- if (options.count("no-tabs") && !splitter_) {
- tabs_ = new QtChatTabsShortcutOnlySubstitute();
- }
- else {
- tabs_ = new QtChatTabs(splitter_ != nullptr, settingsHierachy_, true);
- }
- bool startMinimized = options.count("start-minimized") > 0;
+#ifdef SWIFTEN_PLATFORM_LINUX
+ std::string fontPath = std::string(":/themes/Default/Noto/NotoColorEmoji.ttf");
+ int error = QFontDatabase::addApplicationFont(P2QSTRING(fontPath));
+ SWIFT_LOG_ASSERT(error != -1, error) << "Failed to load font " << fontPath;
+ QFont::insertSubstitution(QApplication::font().family(),"NotoColorEmoji");
+#endif
+#ifdef SWIFTEN_PLATFORM_WINDOWS
+ QFont::insertSubstitution(QApplication::font().family(), "Segoe UI Emoji");
+#endif
+ enableAdHocCommandOnJID_ = options.count("enable-jid-adhocs") > 0;
applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);
storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider());
certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory(), networkFactories_.getCryptoProvider());
- chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/", emoticons);
soundPlayer_ = new QtSoundPlayer(applicationPathProvider_);
// Ugly, because the dock depends on the tray, but the temporary
@@ -265,50 +273,24 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
statusCache_ = new StatusCache(applicationPathProvider_);
- if (splitter_) {
- splitter_->show();
- }
+ splitter_->show();
PlatformAutoUpdaterFactory autoUpdaterFactory;
- if (autoUpdaterFactory.isSupported() && settingsHierachy_->getSetting(QtUISettingConstants::ENABLE_SOFTWARE_UPDATES)
- && !settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL).empty()) {
- autoUpdater_ = autoUpdaterFactory.createAutoUpdater(updateChannelToFeed(settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL)));
+ if (autoUpdaterFactory.isSupported() && settingsHierarchy_->getSetting(QtUISettingConstants::ENABLE_SOFTWARE_UPDATES)
+ && !settingsHierarchy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL).empty()) {
+ autoUpdater_ = autoUpdaterFactory.createAutoUpdater(updateChannelToFeed(settingsHierarchy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL)));
autoUpdater_->checkForUpdates();
autoUpdater_->onUpdateStateChanged.connect(boost::bind(&QtSwift::handleAutoUpdaterStateChanged, this, _1));
- settingsHierachy_->onSettingChanged.connect([&](const std::string& path) {
+ settingsHierarchy_->onSettingChanged.connect([&](const std::string& path) {
if (path == QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL.getKey()) {
- autoUpdater_->setAppcastFeed(updateChannelToFeed(settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL)));
+ autoUpdater_->setAppcastFeed(updateChannelToFeed(settingsHierarchy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL)));
autoUpdater_->checkForUpdates();
}
});
}
-
- for (int i = 0; i < numberOfAccounts; i++) {
- if (i > 0) {
- // Don't add the first tray (see note above)
- systemTrays_.push_back(new QtSystemTray());
- }
- QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), statusCache_, autoUpdater_, startMinimized, !emoticons.empty(), enableAdHocCommandOnJID);
- uiFactories_.push_back(uiFactory);
- MainController* mainController = new MainController(
- &clientMainThreadCaller_,
- &networkFactories_,
- uiFactory,
- settingsHierachy_,
- systemTrays_[i],
- soundPlayer_,
- storagesFactory_,
- certificateStorageFactory_,
- dock_,
- notifier_,
- uriHandler_,
- &idleDetector_,
- emoticons,
- options.count("latency-debug") > 0);
- mainControllers_.push_back(mainController);
- }
-
+ migrateLastLoginAccount();
+ restoreAccounts();
connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(handleAboutToQuit()));
}
@@ -317,17 +299,15 @@ QtSwift::~QtSwift() {
for (auto* factory : uiFactories_) {
delete factory;
}
- for (auto* controller : mainControllers_) {
+ for (auto* controller : accountControllers_) {
delete controller;
}
delete notifier_;
for (auto* tray : systemTrays_) {
delete tray;
}
- delete tabs_;
- delete chatWindowFactory_;
delete splitter_;
- delete settingsHierachy_;
+ delete settingsHierarchy_;
delete qtSettings_;
delete xmlSettings_;
delete statusCache_;
@@ -364,4 +344,139 @@ void QtSwift::handleAutoUpdaterStateChanged(AutoUpdater::State updatedState) {
}
}
+void QtSwift::handleWantsToAddAccount() {
+ auto loginWindow = addAccount();
+ if (!settingsHierarchy_->getSetting(SettingConstants::FORGET_PASSWORDS)) {
+ for (const auto& profile : settingsHierarchy_->getAvailableProfiles()) {
+ ProfileSettingsProvider profileSettings(profile, settingsHierarchy_);
+ if (profileSettings.getIntSetting("enabled", 0)) {
+ // No point showing accounts that're already logged in
+ continue;
+ }
+ const auto& password = profileSettings.getStringSetting("pass");
+ const auto& certificate = profileSettings.getStringSetting("certificate");
+ const auto& jid = profileSettings.getStringSetting("jid");
+ const auto& clientOptions = parseClientOptions(profileSettings.getStringSetting("options"));
+ loginWindow->addAvailableAccount(jid, password, certificate, clientOptions);
+ }
+ }
+}
+
+void QtSwift::restoreAccounts() {
+ if (!settingsHierarchy_->getSetting(SettingConstants::FORGET_PASSWORDS)) {
+ for (const auto& profile : settingsHierarchy_->getAvailableProfiles()) {
+ ProfileSettingsProvider profileSettings(profile, settingsHierarchy_);
+ if (!profileSettings.getIntSetting("enabled", 0)) {
+ continue;
+ }
+ const auto& jid = profileSettings.getStringSetting("jid");
+ const auto& password = profileSettings.getStringSetting("pass");
+ const auto& certificate = profileSettings.getStringSetting("certificate");
+ const auto& clientOptions = parseClientOptions(profileSettings.getStringSetting("options"));
+ auto loginWindow = addAccount();
+ loginWindow->addAvailableAccount(jid, password, certificate, clientOptions);
+ loginWindow->loginClicked();
+ }
+ }
+}
+
+void QtSwift::migrateLastLoginAccount() {
+ const SettingsProvider::Setting<bool> loginAutomatically = SettingsProvider::Setting<bool>("loginAutomatically", false);
+ if (settingsHierarchy_->getSetting(loginAutomatically)) {
+ auto selectedLoginJID = settingsHierarchy_->getSetting(SettingsProvider::Setting<std::string>("lastLoginJID", ""));
+ for (const auto& profile : settingsHierarchy_->getAvailableProfiles()) {
+ ProfileSettingsProvider profileSettings(profile, settingsHierarchy_);
+ if (profileSettings.getStringSetting("jid") == selectedLoginJID) {
+ profileSettings.storeInt("enabled", 1);
+ break;
+ }
+ }
+ settingsHierarchy_->storeSetting(loginAutomatically, false);
+ }
+}
+
+QtLoginWindow* QtSwift::addAccount() {
+ if (uiFactories_.size() > 0) {
+ // Don't add the first tray (see note above)
+ systemTrays_.push_back(new QtSystemTray());
+ }
+ auto tabs = new QtChatTabs(settingsHierarchy_, true);
+ QtUIFactory* uiFactory = new QtUIFactory(settingsHierarchy_, qtSettings_, tabs, splitter_, systemTrays_[systemTrays_.size() - 1], networkFactories_.getTimerFactory(), statusCache_, autoUpdater_, emoticons_, enableAdHocCommandOnJID_);
+ uiFactories_.push_back(uiFactory);
+ AccountController* accountController = new AccountController(
+ &clientMainThreadCaller_,
+ &networkFactories_,
+ uiFactory,
+ settingsHierarchy_,
+ systemTrays_[systemTrays_.size() - 1],
+ soundPlayer_,
+ storagesFactory_,
+ certificateStorageFactory_,
+ dock_,
+ notifier_,
+ uriHandler_,
+ &idleDetector_,
+ emoticons_,
+ useDelayForLatency_);
+ accountControllers_.push_back(accountController);
+
+ //FIXME - accountController has already created the window, so we can pass null here and get the old one
+ auto loginWindow = uiFactory->createLoginWindow(nullptr);
+
+ return dynamic_cast<QtLoginWindow*>(loginWindow);
+}
+
+//FIXME: Switch all this to boost::serialise
+
+#define CHECK_PARSE_LENGTH if (i >= segments.size()) {return result;}
+#define PARSE_INT_RAW(defaultValue) CHECK_PARSE_LENGTH intVal = defaultValue; try {intVal = boost::lexical_cast<int>(segments[i]);} catch(const boost::bad_lexical_cast&) {};i++;
+#define PARSE_STRING_RAW CHECK_PARSE_LENGTH stringVal = byteArrayToString(Base64::decode(segments[i]));i++;
+
+#define PARSE_BOOL(option, defaultValue) PARSE_INT_RAW(defaultValue); result.option = (intVal == 1);
+#define PARSE_INT(option, defaultValue) PARSE_INT_RAW(defaultValue); result.option = intVal;
+#define PARSE_STRING(option) PARSE_STRING_RAW; result.option = stringVal;
+#define PARSE_SAFE_STRING(option) PARSE_STRING_RAW; result.option = SafeString(createSafeByteArray(stringVal));
+#define PARSE_URL(option) {PARSE_STRING_RAW; result.option = URL::fromString(stringVal);}
+
+
+ClientOptions QtSwift::parseClientOptions(const std::string& optionString) {
+ ClientOptions result;
+ size_t i = 0;
+ int intVal = 0;
+ std::string stringVal;
+ std::vector<std::string> segments = String::split(optionString, ',');
+
+ PARSE_BOOL(useStreamCompression, 1)
+ PARSE_INT_RAW(-1)
+ switch (intVal) {
+ case 1: result.useTLS = ClientOptions::NeverUseTLS; break;
+ case 2: result.useTLS = ClientOptions::UseTLSWhenAvailable; break;
+ case 3: result.useTLS = ClientOptions::RequireTLS; break;
+ default:;
+ }
+ PARSE_BOOL(allowPLAINWithoutTLS, 0)
+ PARSE_BOOL(useStreamResumption, 0)
+ PARSE_BOOL(useAcks, 1)
+ PARSE_STRING(manualHostname)
+ PARSE_INT(manualPort, -1)
+ PARSE_INT_RAW(-1)
+ switch (intVal) {
+ case 1: result.proxyType = ClientOptions::NoProxy; break;
+ case 2: result.proxyType = ClientOptions::SystemConfiguredProxy; break;
+ case 3: result.proxyType = ClientOptions::SOCKS5Proxy; break;
+ case 4: result.proxyType = ClientOptions::HTTPConnectProxy; break;
+ }
+ PARSE_STRING(manualProxyHostname)
+ PARSE_INT(manualProxyPort, -1)
+ PARSE_URL(boshURL)
+ PARSE_URL(boshHTTPConnectProxyURL)
+ PARSE_SAFE_STRING(boshHTTPConnectProxyAuthID)
+ PARSE_SAFE_STRING(boshHTTPConnectProxyAuthPassword)
+ PARSE_BOOL(tlsOptions.schannelTLS1_0Workaround, false)
+ PARSE_BOOL(singleSignOn, false)
+
+ return result;
+}
+
+
}
diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h
index a7dc3cf..811b6e4 100644
--- a/Swift/QtUI/QtSwift.h
+++ b/Swift/QtUI/QtSwift.h
@@ -1,17 +1,19 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <map>
#include <string>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <Swiften/Base/Platform.h>
+#include <Swiften/Client/ClientOptions.h>
#include <Swiften/EventLoop/Qt/QtEventLoop.h>
#include <Swiften/Network/BoostNetworkFactories.h>
#include <Swiften/TLS/PlatformTLSFactories.h>
@@ -35,27 +37,28 @@ namespace po = boost::program_options;
class QSplitter;
namespace Swift {
- class QtUIFactory;
- class CertificateStorageFactory;
- class Dock;
- class Notifier;
- class StoragesFactory;
class ApplicationPathProvider;
class AvatarStorage;
class CapsStorage;
- class MainController;
- class QtSystemTray;
- class QtChatTabsBase;
+ class CertificateStorageFactory;
+ class Dock;
+ class EventLoop;
+ class AccountController;
+ class Notifier;
+ class QtChatTabs;
class QtChatWindowFactory;
- class QtSoundPlayer;
+ class QtLoginWindow;
class QtMUCSearchWindowFactory;
+ class QtSingleWindow;
+ class QtSoundPlayer;
+ class QtSystemTray;
+ class QtUIFactory;
class QtUserSearchWindowFactory;
- class EventLoop;
- class URIHandler;
class SettingsProviderHierachy;
- class XMLSettingsProvider;
class StatusCache;
- class QtSingleWindow;
+ class StoragesFactory;
+ class URIHandler;
+ class XMLSettingsProvider;
class QtSwift : public QObject {
Q_OBJECT
@@ -67,28 +70,35 @@ namespace Swift {
private slots:
void handleAboutToQuit();
void handleAutoUpdaterStateChanged(AutoUpdater::State updatedState);
+ void handleWantsToAddAccount();
private:
XMLSettingsProvider* loadSettingsFile(const QString& fileName);
void loadEmoticonsFile(const QString& fileName, std::map<std::string, std::string>& emoticons);
static const std::string& updateChannelToFeed(const std::string& channel);
+ QtLoginWindow* addAccount();
+ ClientOptions parseClientOptions(const std::string& optionString);
+ void restoreAccounts();
+ /**
+ * Upgrades the config from pre-multi-account to post-multi-account format (added in 5.0).
+ */
+ void migrateLastLoginAccount();
private:
QtEventLoop clientMainThreadCaller_;
PlatformTLSFactories tlsFactories_;
BoostNetworkFactories networkFactories_;
QtChatWindowFactory* chatWindowFactory_;
- std::vector<MainController*> mainControllers_;
+ std::vector<AccountController*> accountControllers_;
std::vector<QtSystemTray*> systemTrays_;
std::vector<QtUIFactory*> uiFactories_;
QtSettingsProvider* qtSettings_;
XMLSettingsProvider* xmlSettings_;
- SettingsProviderHierachy* settingsHierachy_;
+ SettingsProviderHierachy* settingsHierarchy_;
QtSingleWindow* splitter_;
QtSoundPlayer* soundPlayer_;
Dock* dock_;
URIHandler* uriHandler_;
- QtChatTabsBase* tabs_;
ApplicationPathProvider* applicationPathProvider_;
StoragesFactory* storagesFactory_;
CertificateStorageFactory* certificateStorageFactory_;
@@ -97,6 +107,9 @@ namespace Swift {
StatusCache* statusCache_;
PlatformIdleQuerier idleQuerier_;
ActualIdleDetector idleDetector_;
+ std::map<std::string, std::string> emoticons_;
+ bool enableAdHocCommandOnJID_ = false;
+ bool useDelayForLatency_;
#if defined(SWIFTEN_PLATFORM_MACOSX)
CocoaApplication cocoaApplication_;
CocoaApplicationActivateHelper cocoaApplicationActivateHelper_;
diff --git a/Swift/QtUI/QtTabWidget.cpp b/Swift/QtUI/QtTabWidget.cpp
index 67e3ae9..99ef6ee 100644
--- a/Swift/QtUI/QtTabWidget.cpp
+++ b/Swift/QtUI/QtTabWidget.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -57,7 +57,7 @@ void QtTabWidget::paintEvent(QPaintEvent * event) {
label.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
label.setGeometry(QRect(QPoint(0,0), size()) - QMargins(10,10,10,10));
label.setWordWrap(true);
- label.setText(tr("This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu or by using the %1 shortcut.").arg(QKeySequence(tr("Ctrl+Alt+L")).toString(QKeySequence::NativeText)));
+ label.setText(tr("This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu."));
QPainter painter(this);
painter.drawPixmap(label.geometry().topLeft(), label.grab());
}
diff --git a/Swift/QtUI/QtTabbable.h b/Swift/QtUI/QtTabbable.h
index 5837702..63c60f4 100644
--- a/Swift/QtUI/QtTabbable.h
+++ b/Swift/QtUI/QtTabbable.h
@@ -19,7 +19,7 @@ namespace Swift {
bool isWidgetSelected();
virtual AlertType getWidgetAlertState() {return NoActivity;}
- virtual int getCount() {return 0;}
+ virtual size_t getCount() {return 0;}
virtual std::string getID() const = 0;
virtual void setEmphasiseFocus(bool /*emphasise*/) {}
diff --git a/Swift/QtUI/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp
index e63cd4f..b3c57a7 100644
--- a/Swift/QtUI/QtTextEdit.cpp
+++ b/Swift/QtUI/QtTextEdit.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -240,7 +240,7 @@ void QtTextEdit::setUpSpellChecker() {
}
else {
// Spellchecking is not working, as we did not get a valid checker from the factory. Disable spellchecking.
- SWIFT_LOG(warning) << "Spellchecking is currently misconfigured in Swift (e.g. missing dictionary or broken dictionary file). Disable spellchecking." << std::endl;
+ SWIFT_LOG(warning) << "Spellchecking is currently misconfigured in Swift (e.g. missing dictionary or broken dictionary file). Disable spellchecking.";
settings_->storeSetting(QtUISettingConstants::SPELL_CHECKER, false);
}
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index ece29ec..49f55dd 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -10,6 +10,7 @@
#include <QSplitter>
+#include <Swiften/Base/Log.h>
#include <Swiften/Whiteboard/WhiteboardSession.h>
#include <Swift/Controllers/Settings/SettingsProviderHierachy.h>
@@ -18,10 +19,10 @@
#include <Swift/QtUI/QtAdHocCommandWindow.h>
#include <Swift/QtUI/QtBlockListEditorWindow.h>
#include <Swift/QtUI/QtChatTabs.h>
-#include <Swift/QtUI/QtChatTabsBase.h>
#include <Swift/QtUI/QtChatWindow.h>
#include <Swift/QtUI/QtChatWindowFactory.h>
#include <Swift/QtUI/QtContactEditWindow.h>
+#include <Swift/QtUI/QtFdpFormSubmitWindow.h>
#include <Swift/QtUI/QtFileTransferListWidget.h>
#include <Swift/QtUI/QtHighlightNotificationConfigDialog.h>
#include <Swift/QtUI/QtHistoryWindow.h>
@@ -40,23 +41,32 @@
namespace Swift {
-QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabsBase* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, bool startMinimized, bool emoticonsExist, bool enableAdHocCommandOnJID) : settings(settings), qtOnlySettings(qtOnlySettings), tabsBase(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(nullptr), loginWindow(nullptr), statusCache(statusCache), autoUpdater(autoUpdater), startMinimized(startMinimized), emoticonsExist_(emoticonsExist), enableAdHocCommandOnJID_(enableAdHocCommandOnJID) {
- chatFontSize = settings->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE);
- historyFontSize_ = settings->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE);
- this->tabs = dynamic_cast<QtChatTabs*>(tabsBase);
+QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, std::map<std::string, std::string>& emoticons, bool enableAdHocCommandOnJID) : settings_(settings), qtOnlySettings_(qtOnlySettings), tabs_(tabs), netbookSplitter_(netbookSplitter), systemTray_(systemTray), timerFactory_(timerFactory), lastMainWindow_(nullptr), loginWindow_(nullptr), statusCache_(statusCache), autoUpdater_(autoUpdater), emoticons_(emoticons), enableAdHocCommandOnJID_(enableAdHocCommandOnJID) {
+ emoticonsExist_ = !emoticons_.empty();
+ chatWindowFactory_ = new QtChatWindowFactory(netbookSplitter_, settings, qtOnlySettings, tabs_, ":/themes/Default/", emoticons_);
+ chatFontSize_ = settings_->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE);
+ historyFontSize_ = settings_->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE);
+}
+
+QtUIFactory::~QtUIFactory() {
+ SWIFT_LOG(debug) << "Entering QtUIFactory destructor. chatWindows size:" << chatWindows_.size();
+ for (auto chat : chatWindows_) {
+ SWIFT_LOG_ASSERT(chat.isNull(), debug) << "QtUIFactory has active chat windows and has not been reset properly";
+ }
+ delete chatWindowFactory_;
}
XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() {
QtXMLConsoleWidget* widget = new QtXMLConsoleWidget();
- tabsBase->addTab(widget);
+ tabs_->addTab(widget);
showTabs();
widget->show();
return widget;
}
HistoryWindow* QtUIFactory::createHistoryWindow(UIEventStream* uiEventStream) {
- QtHistoryWindow* window = new QtHistoryWindow(settings, uiEventStream);
- tabsBase->addTab(window);
+ QtHistoryWindow* window = new QtHistoryWindow(settings_, uiEventStream);
+ tabs_->addTab(window);
showTabs();
connect(window, SIGNAL(fontResized(int)), this, SLOT(handleHistoryWindowFontResized(int)));
@@ -67,53 +77,39 @@ HistoryWindow* QtUIFactory::createHistoryWindow(UIEventStream* uiEventStream) {
void QtUIFactory::handleHistoryWindowFontResized(int size) {
historyFontSize_ = size;
- settings->storeSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE, size);
+ settings_->storeSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE, size);
}
FileTransferListWidget* QtUIFactory::createFileTransferListWidget() {
QtFileTransferListWidget* widget = new QtFileTransferListWidget();
- tabsBase->addTab(widget);
+ tabs_->addTab(widget);
showTabs();
widget->show();
return widget;
}
-MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) {
- lastMainWindow = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), statusCache, emoticonsExist_, enableAdHocCommandOnJID_);
- if (tabs) {
- tabs->setViewMenu(lastMainWindow->getMenus()[0]);
- }
- return lastMainWindow;
+MainWindow* QtUIFactory::createMainWindow(Chattables& chattables, UIEventStream* eventStream) {
+ lastMainWindow_ = new QtMainWindow(chattables, settings_, eventStream, loginWindow_->getMenus(), statusCache_, emoticonsExist_, enableAdHocCommandOnJID_);
+ tabs_->setViewMenu(lastMainWindow_->getMenus()[0]);
+ return lastMainWindow_;
}
LoginWindow* QtUIFactory::createLoginWindow(UIEventStream* eventStream) {
- loginWindow = new QtLoginWindow(eventStream, settings, timerFactory_, autoUpdater);
- if (netbookSplitter) {
- netbookSplitter->insertAtFront(loginWindow);
- }
- connect(systemTray, SIGNAL(clicked()), loginWindow, SLOT(toggleBringToFront()));
-
-#ifndef SWIFT_MOBILE
- QVariant loginWindowGeometryVariant = qtOnlySettings->getQSettings()->value("loginWindowGeometry");
- if (loginWindowGeometryVariant.isValid()) {
- loginWindow->restoreGeometry(loginWindowGeometryVariant.toByteArray());
+ if (loginWindow_) {
+ return loginWindow_;
}
- connect(loginWindow, SIGNAL(geometryChanged()), this, SLOT(handleLoginWindowGeometryChanged()));
- if (startMinimized) loginWindow->hide();
-#endif
- return loginWindow;
-}
-
-void QtUIFactory::handleLoginWindowGeometryChanged() {
- qtOnlySettings->getQSettings()->setValue("loginWindowGeometry", loginWindow->saveGeometry());
+ loginWindow_ = new QtLoginWindow(eventStream, settings_, timerFactory_, autoUpdater_);
+ netbookSplitter_->addAccount(loginWindow_, tabs_);
+ connect(systemTray_, SIGNAL(clicked()), loginWindow_, SLOT(toggleBringToFront()));
+ return loginWindow_;
}
EventWindow* QtUIFactory::createEventWindow() {
- return lastMainWindow->getEventWindow();
+ return lastMainWindow_->getEventWindow();
}
ChatListWindow* QtUIFactory::createChatListWindow(UIEventStream*) {
- return lastMainWindow->getChatListWindow();
+ return lastMainWindow_->getChatListWindow();
}
MUCSearchWindow* QtUIFactory::createMUCSearchWindow() {
@@ -121,27 +117,27 @@ MUCSearchWindow* QtUIFactory::createMUCSearchWindow() {
}
ChatWindow* QtUIFactory::createChatWindow(const JID& contact, UIEventStream* eventStream) {
- QtChatWindow* window = dynamic_cast<QtChatWindow*>(chatWindowFactory->createChatWindow(contact, eventStream));
+ QtChatWindow* window = dynamic_cast<QtChatWindow*>(chatWindowFactory_->createChatWindow(contact, eventStream));
// remove already closed and thereby deleted chat windows
- chatWindows.erase(std::remove_if(chatWindows.begin(), chatWindows.end(),
+ chatWindows_.erase(std::remove_if(chatWindows_.begin(), chatWindows_.end(),
[](QPointer<QtChatWindow>& window) {
return window.isNull();
- }), chatWindows.end());
+ }), chatWindows_.end());
- chatWindows.push_back(window);
+ chatWindows_.push_back(window);
connect(window, SIGNAL(fontResized(int)), this, SLOT(handleChatWindowFontResized(int)));
- window->handleFontResized(chatFontSize);
+ window->handleFontResized(chatFontSize_);
return window;
}
void QtUIFactory::handleChatWindowFontResized(int size) {
- chatFontSize = size;
- settings->storeSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE, size);
+ chatFontSize_ = size;
+ settings_->storeSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE, size);
// resize font in other chat windows
- for (auto&& existingWindow : chatWindows) {
+ for (auto&& existingWindow : chatWindows_) {
if (!existingWindow.isNull()) {
existingWindow->handleFontResized(size);
}
@@ -149,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, qtOnlySettings);
+ return new QtUserSearchWindow(eventStream, type, groups, qtOnlySettings_);
}
JoinMUCWindow* QtUIFactory::createJoinMUCWindow(UIEventStream* uiEventStream) {
@@ -169,7 +165,7 @@ WhiteboardWindow* QtUIFactory::createWhiteboardWindow(std::shared_ptr<Whiteboard
}
HighlightEditorWindow* QtUIFactory::createHighlightEditorWindow() {
- return new QtHighlightNotificationConfigDialog(qtOnlySettings);
+ return new QtHighlightNotificationConfigDialog(qtOnlySettings_);
}
BlockListEditorWidget *QtUIFactory::createBlockListEditorWidget() {
@@ -180,11 +176,13 @@ AdHocCommandWindow* QtUIFactory::createAdHocCommandWindow(std::shared_ptr<Outgoi
return new QtAdHocCommandWindow(command);
}
+std::unique_ptr<FdpFormSubmitWindow> QtUIFactory::createFdpFormSubmitWindow() {
+ return std::make_unique<QtFdpFormSubmitWindow>();
+}
+
void QtUIFactory::showTabs() {
- if (tabs) {
- if (!tabs->isVisible()) {
- tabs->show();
- }
+ if (!tabs_->isVisible()) {
+ tabs_->show();
}
}
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index 18bf9fe..04836fe 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -17,8 +17,9 @@ class QSplitter;
namespace Swift {
class AutoUpdater;
+ class Chattables;
+ class FdpFormSubmitWindow;
class QtChatTabs;
- class QtChatTabsBase;
class QtChatTheme;
class QtChatWindow;
class QtChatWindowFactory;
@@ -35,11 +36,11 @@ namespace Swift {
class QtUIFactory : public QObject, public UIFactory {
Q_OBJECT
public:
- QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabsBase* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, bool startMinimized, bool emoticonsExist, bool enableAdHocCommandOnJID);
-
+ QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, std::map<std::string, std::string>& emoticons, bool enableAdHocCommandOnJID);
+ ~QtUIFactory();
virtual XMLConsoleWidget* createXMLConsoleWidget();
virtual HistoryWindow* createHistoryWindow(UIEventStream*);
- virtual MainWindow* createMainWindow(UIEventStream* eventStream);
+ virtual MainWindow* createMainWindow(Chattables& chattables, UIEventStream* eventStream);
virtual LoginWindow* createLoginWindow(UIEventStream* eventStream);
virtual EventWindow* createEventWindow();
virtual ChatListWindow* createChatListWindow(UIEventStream*);
@@ -54,9 +55,9 @@ namespace Swift {
virtual HighlightEditorWindow* createHighlightEditorWindow();
virtual BlockListEditorWidget* createBlockListEditorWidget();
virtual AdHocCommandWindow* createAdHocCommandWindow(std::shared_ptr<OutgoingAdHocCommandSession> command);
+ virtual std::unique_ptr<FdpFormSubmitWindow> createFdpFormSubmitWindow();
private slots:
- void handleLoginWindowGeometryChanged();
void handleChatWindowFontResized(int);
void handleHistoryWindowFontResized(int);
@@ -64,23 +65,22 @@ namespace Swift {
void showTabs();
private:
- SettingsProviderHierachy* settings;
- QtSettingsProvider* qtOnlySettings;
- QtChatTabsBase* tabsBase;
- QtChatTabs* tabs;
- QtSingleWindow* netbookSplitter;
- QtSystemTray* systemTray;
- QtChatWindowFactory* chatWindowFactory;
+ SettingsProviderHierachy* settings_;
+ QtSettingsProvider* qtOnlySettings_;
+ QtChatTabs* tabs_;
+ QtSingleWindow* netbookSplitter_;
+ QtSystemTray* systemTray_;
+ QtChatWindowFactory* chatWindowFactory_;
TimerFactory* timerFactory_;
- QtMainWindow* lastMainWindow;
- QtLoginWindow* loginWindow;
- StatusCache* statusCache;
- AutoUpdater* autoUpdater;
- std::vector<QPointer<QtChatWindow> > chatWindows;
- bool startMinimized;
- int chatFontSize;
+ QtMainWindow* lastMainWindow_;
+ QtLoginWindow* loginWindow_;
+ StatusCache* statusCache_;
+ AutoUpdater* autoUpdater_;
+ std::vector<QPointer<QtChatWindow> > chatWindows_;
+ int chatFontSize_;
int historyFontSize_;
bool emoticonsExist_;
+ std::map<std::string, std::string>& emoticons_;
bool enableAdHocCommandOnJID_;
};
}
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h
index 093357a..fa42c49 100644
--- a/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h
+++ b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (c) 2016 Isode Limited.
+ * Copyright (c) 2016-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -36,7 +36,7 @@
} \
\
virtual bool testInstance(QWidget* widget) const { \
- return dynamic_cast<FIELD_CLASS*>(widget) != 0; \
+ return dynamic_cast<FIELD_CLASS*>(widget) != nullptr; \
} \
};
diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp
index 290feca..1cd5505 100644
--- a/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp
+++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (c) 2014-2016 Isode Limited.
+ * Copyright (c) 2014-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -358,7 +358,7 @@ int QtVCardWidget::fieldTypeInstances(std::shared_ptr<QtVCardFieldInfo> fieldTyp
return instances;
}
-void layoutDeleteChildren(QLayout *layout) {
+static void layoutDeleteChildren(QLayout *layout) {
while(layout->count() > 0) {
QLayoutItem* child;
if ((child = layout->takeAt(0)) != nullptr) {
diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp
index ea9a9c6..75a23f8 100644
--- a/Swift/QtUI/QtWebKitChatView.cpp
+++ b/Swift/QtUI/QtWebKitChatView.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -11,9 +11,9 @@
#include <QDesktopWidget>
#include <QEventLoop>
#include <QFile>
+#include <QFileDevice>
#include <QFileDialog>
#include <QFileInfo>
-#include <QFileDevice>
#include <QInputDialog>
#include <QKeyEvent>
#include <QMessageBox>
@@ -28,6 +28,8 @@
#include <Swiften/Base/Log.h>
#include <Swiften/StringCodecs/Base64.h>
+#include <Swift/Controllers/SettingConstants.h>
+#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
@@ -51,12 +53,15 @@ const QString QtWebKitChatView::ButtonFileTransferSendRequest = QString("filetra
const QString QtWebKitChatView::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest");
const QString QtWebKitChatView::ButtonFileTransferOpenFile = QString("filetransfer-openfile");
const QString QtWebKitChatView::ButtonMUCInvite = QString("mucinvite");
+const QString QtWebKitChatView::ButtonResendMessage = QString("resend-message");
+const QString QtWebKitChatView::ButtonResendPopup = QString("popup-resend");
+
namespace {
const double minimalFontScaling = 0.7;
}
-QtWebKitChatView::QtWebKitChatView(QtChatWindow* window, UIEventStream* eventStream, QtChatTheme* theme, QWidget* parent, bool disableAutoScroll) : QtChatView(parent), window_(window), eventStream_(eventStream), fontSizeSteps_(0), disableAutoScroll_(disableAutoScroll), previousMessageKind_(PreviosuMessageWasNone), previousMessageWasSelf_(false), showEmoticons_(false), insertingLastLine_(false), idCounter_(0) {
+QtWebKitChatView::QtWebKitChatView(QtChatWindow* window, UIEventStream* eventStream, QtChatTheme* theme, QWidget* parent, SettingsProvider* settings, bool disableAutoScroll /*= false*/) : QtChatView(parent), window_(window), eventStream_(eventStream), fontSizeSteps_(0), disableAutoScroll_(disableAutoScroll), previousMessageKind_(PreviosuMessageWasNone), previousMessageWasSelf_(false), showEmoticons_(false), insertingLastLine_(false), idCounter_(0), settings_(settings) {
theme_ = theme;
QVBoxLayout* mainLayout = new QVBoxLayout(this);
@@ -136,7 +141,7 @@ void QtWebKitChatView::addMessageBottom(std::shared_ptr<ChatSnippet> snippet) {
void QtWebKitChatView::addMessageTop(std::shared_ptr<ChatSnippet> /* snippet */) {
// TODO: Implement this in a sensible manner later.
- SWIFT_LOG(error) << "Not yet implemented!" << std::endl;
+ SWIFT_LOG(error) << "Not yet implemented!";
}
void QtWebKitChatView::addToDOM(std::shared_ptr<ChatSnippet> snippet) {
@@ -379,7 +384,7 @@ void QtWebKitChatView::setFileTransferProgress(QString id, const int percentageD
rememberScrolledToBottom();
QWebElement ftElement = findElementWithID(document_, "div", id);
if (ftElement.isNull()) {
- SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl;
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!";
return;
}
QWebElement progressBar = ftElement.findFirst("div.progressbar");
@@ -393,7 +398,7 @@ void QtWebKitChatView::setFileTransferStatus(QString id, const ChatWindow::FileT
rememberScrolledToBottom();
QWebElement ftElement = findElementWithID(document_, "div", id);
if (ftElement.isNull()) {
- SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl;
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id);
return;
}
@@ -478,7 +483,7 @@ int QtWebKitChatView::getSnippetPositionByDate(const QDate& date) {
void QtWebKitChatView::resetTopInsertPoint() {
// TODO: Implement or refactor later.
- SWIFT_LOG(error) << "Not yet implemented!" << std::endl;
+ SWIFT_LOG(error) << "Not yet implemented!";
}
std::string QtWebKitChatView::addMessage(
@@ -555,10 +560,13 @@ std::string QtWebKitChatView::addMessage(
QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());
+ std::string messageMarkingValue = "";
+
QString htmlString;
if (label) {
+ messageMarkingValue = label->getDisplayMarking();
htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(QtUtilities::htmlEscape(P2QSTRING(label->getForegroundColor()))).arg(QtUtilities::htmlEscape(P2QSTRING(label->getBackgroundColor())));
- htmlString += QString("%1</span> ").arg(QtUtilities::htmlEscape(P2QSTRING(label->getDisplayMarking())));
+ htmlString += QString("%1</span> ").arg(QtUtilities::htmlEscape(P2QSTRING(messageMarkingValue)));
}
QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
@@ -569,15 +577,16 @@ std::string QtWebKitChatView::addMessage(
QString highlightSpanEnd = highlightWholeMessage ? "</span>" : "";
htmlString += "<span class='swift_inner_message'>" + styleSpanStart + highlightSpanStart + message + highlightSpanEnd + styleSpanEnd + "</span>" ;
- bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
+ bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf, label);
QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.svg" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
- std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+ std::string id = "id" + std::to_string(idCounter_++);
addMessageBottom(std::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), direction));
previousMessageWasSelf_ = senderIsSelf;
previousSenderName_ = P2QSTRING(senderName);
previousMessageKind_ = PreviousMessageWasMessage;
+ previousMessageDisplayMarking_ = messageMarkingValue;
return id;
}
@@ -600,6 +609,10 @@ QString QtWebKitChatView::buildChatWindowButton(const QString& name, const QStri
return html;
}
+QString QtWebKitChatView::buildChatWindowPopupImageButton(const QString& title, const QString& path, const QString& arg1, const QString& arg2, const QString& arg3, const QString& arg4, const QString& arg5) {
+ return QString("<div class=\"popup\" onclick='chatwindow.buttonClicked(\"%3\", \"%5\", \"6\", \"7\", \"8\", \"9\")' \"><img src='%1' title='%2'/><span class=\"popuptext\" id=\"resendPopup\" ><div class=\"resendButton\" onclick='chatwindow.buttonClicked(\"%4\", \"%5\", \"6\", \"7\", \"8\", \"9\")'>Resend</div></span></div>").arg(path).arg(title).arg(QtWebKitChatView::ButtonResendPopup).arg(QtWebKitChatView::ButtonResendMessage).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3)).arg(encodeButtonArgument(arg4)).arg(encodeButtonArgument(arg5));
+}
+
void QtWebKitChatView::resizeEvent(QResizeEvent* event) {
// This code ensures that if the user is scrolled all to the bottom of a chat view,
// the view stays scrolled to the bottom if the view is resized or if the message
@@ -611,7 +624,7 @@ void QtWebKitChatView::resizeEvent(QResizeEvent* event) {
}
std::string QtWebKitChatView::addFileTransfer(const std::string& senderName, const std::string& avatarPath, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes, const std::string& description) {
- SWIFT_LOG(debug) << "addFileTransfer" << std::endl;
+ SWIFT_LOG(debug) << "addFileTransfer";
QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
QString actionText;
@@ -645,7 +658,7 @@ std::string QtWebKitChatView::addFileTransfer(const std::string& senderName, con
bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf);
QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());
QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.svg" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
- std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++);
+ std::string id = "ftmessage" + std::to_string(idCounter_++);
addMessageBottom(std::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::universal_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText)));
previousMessageWasSelf_ = senderIsSelf;
@@ -679,7 +692,7 @@ std::string QtWebKitChatView::addWhiteboardRequest(const QString& contact, bool
"</div>";
}
QString qAvatarPath = "qrc:/icons/avatar.svg";
- std::string id = "wbmessage" + boost::lexical_cast<std::string>(idCounter_++);
+ std::string id = "wbmessage" + std::to_string(idCounter_++);
addMessageBottom(std::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(contact), B2QDATE(boost::posix_time::second_clock::universal_time()), qAvatarPath, false, false, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText)));
previousMessageWasSelf_ = false;
previousSenderName_ = contact;
@@ -713,7 +726,7 @@ static bool isFilePathWritable(const QString& path) {
void QtWebKitChatView::setFileTransferWarning(QString id, QString warningText) {
QWebElement ftElement = findElementWithID(document_, "div", id);
if (ftElement.isNull()) {
- SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl;
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id);
return;
}
@@ -724,7 +737,7 @@ void QtWebKitChatView::setFileTransferWarning(QString id, QString warningText) {
void QtWebKitChatView::removeFileTransferWarning(QString id) {
QWebElement ftElement = findElementWithID(document_, "div", id);
if (ftElement.isNull()) {
- SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl;
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id);
return;
}
@@ -798,11 +811,25 @@ void QtWebKitChatView::handleHTMLButtonClicked(QString id, QString encodedArgume
QString elementID = arg3;
QString isImpromptu = arg4;
QString isContinuation = arg5;
- eventStream_->send(std::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password), boost::optional<std::string>(), false, false, isImpromptu.contains("true"), isContinuation.contains("true")));
+ eventStream_->send(std::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password), boost::optional<std::string>(), false, isImpromptu.contains("true"), isContinuation.contains("true")));
setMUCInvitationJoined(elementID);
}
+ else if (id.startsWith(ButtonResendPopup)) {
+ QString chatID = arg1;
+ QWebElement message = document_.findFirst("#" + arg1);
+ if (!message.isNull()) {
+ QWebElement popuptext = message.findFirst("span#resendPopup.popuptext");
+ if (!popuptext.isNull()) {
+ popuptext.toggleClass("show");
+ }
+ }
+ }
+ else if (id.startsWith(ButtonResendMessage)) {
+ QString chatID = arg1;
+ window_->resendMessage(Q2PSTRING(chatID));
+ }
else {
- SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl;
+ SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )";
}
}
@@ -822,7 +849,7 @@ void QtWebKitChatView::addErrorMessage(const ChatWindow::ChatMessage& errorMessa
}
QString errorMessageHTML(chatMessageToHTML(errorMessage));
- std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+ std::string id = "id" + std::to_string(idCounter_++);
addMessageBottom(std::make_shared<SystemMessageSnippet>("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_, P2QSTRING(id), ChatSnippet::getDirection(errorMessage)));
previousMessageWasSelf_ = false;
@@ -835,7 +862,7 @@ std::string QtWebKitChatView::addSystemMessage(const ChatWindow::ChatMessage& me
}
QString messageHTML = chatMessageToHTML(message);
- std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+ std::string id = "id" + std::to_string(idCounter_++);
addMessageBottom(std::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, P2QSTRING(id), getActualDirection(message, direction)));
previousMessageKind_ = PreviousMessageWasSystem;
@@ -901,7 +928,7 @@ void QtWebKitChatView::addPresenceMessage(const ChatWindow::ChatMessage& message
}
QString messageHTML = chatMessageToHTML(message);
- std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+ std::string id = "id" + std::to_string(idCounter_++);
addMessageBottom(std::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, P2QSTRING(id), getActualDirection(message, direction)));
previousMessageKind_ = PreviousMessageWasPresence;
@@ -958,7 +985,9 @@ void QtWebKitChatView::setAckState(std::string const& id, ChatWindow::AckState s
xml = "";
displayReceiptInfo(P2QSTRING(id), true);
break;
- case ChatWindow::Failed: xml = "<img src='qrc:/icons/error.png' title='" + tr("This message may not have been transmitted.") + "'/>"; break;
+ case ChatWindow::Failed:
+ xml = buildChatWindowPopupImageButton(tr("This message may not have been sent. Click to resend."), "qrc:/icons/error.png", P2QSTRING(id));
+ break;
}
setAckXML(P2QSTRING(id), xml);
}
@@ -978,12 +1007,19 @@ void QtWebKitChatView::setMessageReceiptState(const std::string& id, ChatWindow:
setReceiptXML(P2QSTRING(id), xml);
}
-bool QtWebKitChatView::appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) {
+bool QtWebKitChatView::appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf, const std::shared_ptr<SecurityLabel>& label /*=nullptr*/) {
bool result = previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName)));
if (insertingLastLine_) {
insertingLastLine_ = false;
return false;
}
+ if (settings_->getSetting(SettingConstants::MUC_MARKING_ELISION)) {
+ if (label && label->getDisplayMarking() != previousMessageDisplayMarking_) {
+ if (label->getDisplayMarking() != "") {
+ return false;
+ }
+ }
+ }
return result;
}
diff --git a/Swift/QtUI/QtWebKitChatView.h b/Swift/QtUI/QtWebKitChatView.h
index f816942..f0b4459 100644
--- a/Swift/QtUI/QtWebKitChatView.h
+++ b/Swift/QtUI/QtWebKitChatView.h
@@ -28,6 +28,7 @@ namespace Swift {
class QtChatWindowJSBridge;
class UIEventStream;
class QtChatWindow;
+ class SettingsProvider;
class QtWebKitChatView : public QtChatView {
Q_OBJECT
@@ -41,8 +42,10 @@ namespace Swift {
static const QString ButtonFileTransferAcceptRequest;
static const QString ButtonFileTransferOpenFile;
static const QString ButtonMUCInvite;
+ static const QString ButtonResendMessage;
+ static const QString ButtonResendPopup;
public:
- QtWebKitChatView(QtChatWindow* window, UIEventStream* eventStream, QtChatTheme* theme, QWidget* parent, bool disableAutoScroll = false);
+ QtWebKitChatView(QtChatWindow* window, UIEventStream* eventStream, QtChatTheme* theme, QWidget* parent, SettingsProvider* settings, bool disableAutoScroll = false);
~QtWebKitChatView() override;
/** Add message to window.
@@ -150,12 +153,13 @@ namespace Swift {
const boost::posix_time::ptime& time,
const QString& style,
const HighlightAction& highlight);
- bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf);
+ bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf, const std::shared_ptr<SecurityLabel>& label = nullptr);
static ChatSnippet::Direction getActualDirection(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction);
QString getHighlightSpanStart(const std::string& text, const std::string& background);
QString getHighlightSpanStart(const HighlightAction& highlight);
QString chatMessageToHTML(const ChatWindow::ChatMessage& message);
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());
+ static QString buildChatWindowPopupImageButton(const QString& title, const QString& path, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString(), const QString& arg4 = QString(), const QString& arg5 = QString());
protected:
void resizeEvent(QResizeEvent* event) override;
@@ -189,5 +193,7 @@ namespace Swift {
QString previousSenderName_;
std::map<QString, QString> descriptions_;
std::map<QString, QString> filePaths_;
+ std::string previousMessageDisplayMarking_;
+ SettingsProvider* settings_ = nullptr;
};
}
diff --git a/Swift/QtUI/QtWebView.cpp b/Swift/QtUI/QtWebView.cpp
index 967be1a..24636ed 100644
--- a/Swift/QtUI/QtWebView.cpp
+++ b/Swift/QtUI/QtWebView.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -7,8 +7,6 @@
#include <Swift/QtUI/QtWebView.h>
-#include <boost/numeric/conversion/cast.hpp>
-
#include <QFocusEvent>
#include <QKeyEvent>
#include <QKeySequence>
@@ -48,7 +46,7 @@ void QtWebView::keyPressEvent(QKeyEvent* event) {
modifiers,
event->text(),
event->isAutoRepeat(),
- boost::numeric_cast<unsigned short>(event->count()));
+ event->count());
QWebView::keyPressEvent(translatedEvent);
delete translatedEvent;
}
diff --git a/Swift/QtUI/Roster/QtFilterWidget.cpp b/Swift/QtUI/Roster/QtFilterWidget.cpp
index 2f561bd..c017d29 100644
--- a/Swift/QtUI/Roster/QtFilterWidget.cpp
+++ b/Swift/QtUI/Roster/QtFilterWidget.cpp
@@ -82,7 +82,7 @@ bool QtFilterWidget::eventFilter(QObject*, QEvent* event) {
} else if ((keyEvent->key() == Qt::Key_Alt && event->type() == QEvent::KeyRelease && isModifierSinglePressed_)
|| (keyEvent->key() == Qt::Key_Menu)) {
QPoint itemOffset(2,2);
- QPoint contextMenuPosition = treeView_->visualRect(treeView_->currentIndex()).topLeft() + itemOffset;;
+ QPoint contextMenuPosition = treeView_->visualRect(treeView_->currentIndex()).topLeft() + itemOffset;
QApplication::postEvent(treeView_, new QContextMenuEvent(QContextMenuEvent::Keyboard, contextMenuPosition, treeView_->mapToGlobal(contextMenuPosition)));
return true;
} else if (keyEvent->key() == Qt::Key_Return) {
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index ff97b42..96979c0 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -1,5 +1,6 @@
import os, datetime, re, time
import Version
+import SCons.Util
def generateQRCTheme(dir, prefix) :
sourceDir = dir.abspath
@@ -7,6 +8,11 @@ def generateQRCTheme(dir, prefix) :
result += "<RCC version =\"1.0\">"
result += "<qresource prefix=\"/themes/" + prefix + "\">"
for (path, dirs, files) in os.walk(sourceDir) :
+ #skip the Noto emoji fonts in Windows. No need to package them since they aren't used
+ if "Noto" in path and not env["PLATFORM"] == "linux" :
+ continue
+ dirs.sort()
+ files.sort()
for file in files :
filePath = os.path.join(path,file)
result += "<file alias=\"%(alias)s\">%(path)s</file>" % {
@@ -19,12 +25,12 @@ def generateQRCTheme(dir, prefix) :
Import("env")
-myenv = env.Clone()
+myenv = env.Clone(tools = [ 'textfile' ])
# Disable warnings that affect Qt
-myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"])
+myenv["CXXFLAGS"] = list(filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]))
if "clang" in env["CC"] :
- myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-missing-prototypes", "-Wno-unreachable-code", "-Wno-disabled-macro-expansion", "-Wno-unused-private-field", "-Wno-extra-semi", "-Wno-duplicate-enum", "-Wno-missing-variable-declarations", "-Wno-conversion", "-Wno-undefined-reinterpret-cast"])
+ myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-conversion"])
myenv.UseFlags(env["SWIFT_CONTROLLERS_FLAGS"])
myenv.UseFlags(env["SWIFTOOLS_FLAGS"])
@@ -57,7 +63,7 @@ myenv.Tool("textfile", toolpath = ["#/BuildTools/SCons/Tools"])
qt4modules = ['QtCore', 'QtWebKit', 'QtGui']
if myenv["qt5"] :
qt_version = '5'
- # QtSvg is required so the image format plugin for SVG images is installed
+ # QtSvg is required so the image format plugin for SVG images is installed
# correctly by Qt's deployment tools.
qt4modules += ['QtWidgets', 'QtWebKitWidgets', 'QtMultimedia', 'QtSvg']
if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" :
@@ -110,131 +116,140 @@ myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateQRCTheme(myenv.Dir("#/Swi
sources = [
"main.cpp",
+ "ChatList/ChatListDelegate.cpp",
+ "ChatList/ChatListModel.cpp",
+ "ChatList/ChatListMUCItem.cpp",
+ "ChatList/ChatListRecentItem.cpp",
+ "ChatList/ChatListWhiteboardItem.cpp",
+ "ChatList/QtChatListWindow.cpp",
+ "ChattablesModel.cpp",
+ "ChatSnippet.cpp",
+ "EventViewer/EventDelegate.cpp",
+ "EventViewer/EventModel.cpp",
+ "EventViewer/QtEvent.cpp",
+ "EventViewer/QtEventWindow.cpp",
+ "EventViewer/TwoLineDelegate.cpp",
"FlowLayout.cpp",
+ "MessageSnippet.cpp",
+ "MUCSearch/MUCSearchDelegate.cpp",
+ "MUCSearch/MUCSearchEmptyItem.cpp",
+ "MUCSearch/MUCSearchModel.cpp",
+ "MUCSearch/MUCSearchRoomItem.cpp",
+ "MUCSearch/MUCSearchServiceItem.cpp",
+ "MUCSearch/QtLeafSortFilterProxyModel.cpp",
+ "MUCSearch/QtMUCSearchWindow.cpp",
+ "ServerList/ServerListDelegate.cpp",
+ "ServerList/ServerListModel.cpp",
+ "ServerList/QtServerListView.cpp",
+ "qrc_DefaultTheme.cc",
+ "qrc_Swift.cc",
"QtAboutWidget.cpp",
- "QtSpellCheckerWindow.cpp",
+ "QtAddBookmarkWindow.cpp",
+ "QtAdHocCommandWindow.cpp",
+ "QtAdHocCommandWithJIDWindow.cpp",
+ "QtAffiliationEditor.cpp",
"QtAvatarWidget.cpp",
- "QtUIFactory.cpp",
+ "QtBlockListEditorWindow.cpp",
+ "QtBookmarkDetailWindow.cpp",
+ "QtCachedImageScaler.cpp",
+ "QtChatOverview.cpp",
+ "QtChatOverviewBundle.cpp",
+ "QtChatOverviewDelegate.cpp",
+ "QtChatTabs.cpp",
+ "QtChatTabsBase.cpp",
+ "QtChatTheme.cpp",
+ "QtChatView.cpp",
+ "QtChatWindow.cpp",
"QtChatWindowFactory.cpp",
+ "QtChatWindowJSBridge.cpp",
+ "QtCheckBoxStyledItemDelegate.cpp",
"QtClickableLabel.cpp",
+ "QtClosableLineEdit.cpp",
+ "QtColorSelectionStyledItemDelegate.cpp",
+ "QtColorToolButton.cpp",
+ "QtConnectionSettingsWindow.cpp",
+ "QtContactEditWidget.cpp",
+ "QtContactEditWindow.cpp",
+ "QtEditBookmarkWindow.cpp",
+ "QtElidingLabel.cpp",
+ "QtEmojiCell.cpp",
+ "QtEmojisGrid.cpp",
+ "QtEmojisScroll.cpp",
+ "QtEmojisSelector.cpp",
+ "QtEmoticonsGrid.cpp",
+ "QtExpandedListView.cpp",
+ "QtFdpFormSubmitWindow.cpp",
+ "QtFileTransferListItemModel.cpp",
+ "QtFileTransferListWidget.cpp",
+ "QtFormResultItemModel.cpp",
+ "QtFormWidget.cpp",
+ "QtHighlightNotificationConfigDialog.cpp",
+ "QtHistoryWindow.cpp",
+ "QtJoinMUCWindow.cpp",
+ "QtLineEdit.cpp",
+ "QtListWidget.cpp",
"QtLoginWindow.cpp",
"QtMainWindow.cpp",
- "QtProfileWindow.cpp",
- "QtBlockListEditorWindow.cpp",
+ "QtMUCConfigurationWindow.cpp",
"QtNameWidget.cpp",
+ "QtPlainChatView.cpp",
+ "QtProfileWindow.cpp",
+ "QtRecentEmojisGrid.cpp",
+ "QtResourceHelper.cpp",
+ "QtRosterHeader.cpp",
+ "QtScaledAvatarCache.cpp",
"QtSettingsProvider.cpp",
+ "QtSingleWindow.cpp",
+ "QtSoundPlayer.cpp",
+ "QtSoundSelectionStyledItemDelegate.cpp",
+ "QtSpellCheckerWindow.cpp",
+ "QtSpellCheckHighlighter.cpp",
"QtStatusWidget.cpp",
- "QtScaledAvatarCache.cpp",
+ "QtSubscriptionRequestWindow.cpp",
"QtSwift.cpp",
- "QtURIHandler.cpp",
- "QtChatWindow.cpp",
- "QtChatView.cpp",
- "QtWebKitChatView.cpp",
- "QtPlainChatView.cpp",
- "QtChatTheme.cpp",
- "QtChatTabs.cpp",
- "QtChatTabsBase.cpp",
- "QtChatTabsShortcutOnlySubstitute.cpp",
- "QtSoundPlayer.cpp",
"QtSystemTray.cpp",
- "QtCachedImageScaler.cpp",
"QtTabbable.cpp",
"QtTabWidget.cpp",
"QtTextEdit.cpp",
- "QtXMLConsoleWidget.cpp",
- "QtHistoryWindow.cpp",
- "QtFileTransferListWidget.cpp",
- "QtFileTransferListItemModel.cpp",
- "QtAdHocCommandWindow.cpp",
- "QtAdHocCommandWithJIDWindow.cpp",
+ "QtUIFactory.cpp",
+ "QtUISettingConstants.cpp",
+ "QtUpdateFeedSelectionDialog.cpp",
+ "QtURIHandler.cpp",
+ "QtURLValidator.cpp",
"QtUtilities.cpp",
- "QtBookmarkDetailWindow.cpp",
- "QtAddBookmarkWindow.cpp",
- "QtEditBookmarkWindow.cpp",
- "QtEmojisGrid.cpp",
- "QtEmojiCell.cpp",
- "QtEmojisScroll.cpp",
- "QtEmojisSelector.cpp",
- "QtRecentEmojisGrid.cpp",
- "QtEmoticonsGrid.cpp",
- "QtContactEditWindow.cpp",
- "QtContactEditWidget.cpp",
- "QtSingleWindow.cpp",
- "QtColorToolButton.cpp",
- "QtClosableLineEdit.cpp",
- "QtHighlightNotificationConfigDialog.cpp",
- "ChatSnippet.cpp",
- "MessageSnippet.cpp",
- "SystemMessageSnippet.cpp",
- "QtElidingLabel.cpp",
- "QtFormWidget.cpp",
- "QtFormResultItemModel.cpp",
- "QtLineEdit.cpp",
- "QtJoinMUCWindow.cpp",
- "QtConnectionSettingsWindow.cpp",
- "Roster/RosterModel.cpp",
- "Roster/QtTreeWidget.cpp",
- "Roster/RosterDelegate.cpp",
- "Roster/GroupItemDelegate.cpp",
+ "QtWebKitChatView.cpp",
+ "QtWebView.cpp",
+ "QtXMLConsoleWidget.cpp",
"Roster/DelegateCommons.cpp",
+ "Roster/GroupItemDelegate.cpp",
"Roster/QtFilterWidget.cpp",
- "Roster/QtRosterWidget.cpp",
"Roster/QtOccupantListWidget.cpp",
+ "Roster/QtRosterWidget.cpp",
+ "Roster/QtTreeWidget.cpp",
+ "Roster/RosterDelegate.cpp",
+ "Roster/RosterModel.cpp",
"Roster/RosterTooltip.cpp",
- "EventViewer/EventModel.cpp",
- "EventViewer/EventDelegate.cpp",
- "EventViewer/TwoLineDelegate.cpp",
- "EventViewer/QtEventWindow.cpp",
- "EventViewer/QtEvent.cpp",
- "ChatList/QtChatListWindow.cpp",
- "ChatList/ChatListModel.cpp",
- "ChatList/ChatListDelegate.cpp",
- "ChatList/ChatListMUCItem.cpp",
- "ChatList/ChatListRecentItem.cpp",
- "ChatList/ChatListWhiteboardItem.cpp",
- "MUCSearch/MUCSearchDelegate.cpp",
- "MUCSearch/MUCSearchEmptyItem.cpp",
- "MUCSearch/MUCSearchModel.cpp",
- "MUCSearch/MUCSearchRoomItem.cpp",
- "MUCSearch/MUCSearchServiceItem.cpp",
- "MUCSearch/QtLeafSortFilterProxyModel.cpp",
- "MUCSearch/QtMUCSearchWindow.cpp",
+ "SystemMessageSnippet.cpp",
+ "Trellis/QtDNDTabBar.cpp",
+ "Trellis/QtDynamicGridLayout.cpp",
+ "Trellis/QtGridSelectionDialog.cpp",
"UserSearch/ContactListDelegate.cpp",
"UserSearch/ContactListModel.cpp",
"UserSearch/QtContactListWidget.cpp",
"UserSearch/QtSuggestingJIDInput.cpp",
- "UserSearch/QtUserSearchFirstPage.cpp",
- "UserSearch/QtUserSearchFirstMultiJIDPage.cpp",
+ "UserSearch/QtUserSearchDetailsPage.cpp",
"UserSearch/QtUserSearchFieldsPage.cpp",
+ "UserSearch/QtUserSearchFirstMultiJIDPage.cpp",
+ "UserSearch/QtUserSearchFirstPage.cpp",
"UserSearch/QtUserSearchResultsPage.cpp",
- "UserSearch/QtUserSearchDetailsPage.cpp",
"UserSearch/QtUserSearchWindow.cpp",
- "UserSearch/UserSearchModel.cpp",
"UserSearch/UserSearchDelegate.cpp",
+ "UserSearch/UserSearchModel.cpp",
+ "Whiteboard/ColorWidget.cpp",
"Whiteboard/FreehandLineItem.cpp",
"Whiteboard/GView.cpp",
- "Whiteboard/TextDialog.cpp",
"Whiteboard/QtWhiteboardWindow.cpp",
- "Whiteboard/ColorWidget.cpp",
- "QtSubscriptionRequestWindow.cpp",
- "QtRosterHeader.cpp",
- "QtWebView.cpp",
- "qrc_DefaultTheme.cc",
- "qrc_Swift.cc",
- "QtChatWindowJSBridge.cpp",
- "QtMUCConfigurationWindow.cpp",
- "QtAffiliationEditor.cpp",
- "QtUISettingConstants.cpp",
- "QtURLValidator.cpp",
- "QtResourceHelper.cpp",
- "QtSpellCheckHighlighter.cpp",
- "QtUpdateFeedSelectionDialog.cpp",
- "Trellis/QtDynamicGridLayout.cpp",
- "Trellis/QtDNDTabBar.cpp",
- "Trellis/QtGridSelectionDialog.cpp",
- "QtCheckBoxStyledItemDelegate.cpp",
- "QtColorSelectionStyledItemDelegate.cpp",
- "QtSoundSelectionStyledItemDelegate.cpp"
+ "Whiteboard/TextDialog.cpp"
]
if env["PLATFORM"] == "win32" :
@@ -351,7 +366,7 @@ myenv["TEXTFILESUFFIX"] = ""
copying_files = [myenv.File("../../COPYING.gpl"), myenv.File("../../COPYING.thirdparty"), myenv.File("../../COPYING.dependencies")]
if env["PLATFORM"] == "darwin" and env["HAVE_SPARKLE"] :
copying_files.append(env["SPARKLE_COPYING"])
-myenv.MyTextfile(target = "COPYING", source = copying_files, LINESEPARATOR = "\n\n========\n\n\n")
+myenv.Textfile(target = "COPYING", source = copying_files, LINESEPARATOR = "\n\n========\n\n\n")
################################################################################
# Translation
@@ -461,11 +476,16 @@ if env["PLATFORM"] == "win32" :
#myenv.Nsis("../Packaging/nsis/swift.nsi")
if env["SCONS_STAGE"] == "build" and env.get("wix_bindir", None):
def convertToRTF(env, target, source) :
- infile = open(source[0].abspath, 'r')
- outfile = open(target[0].abspath, 'w')
+ if SCons.Util.PY3:
+ infile = open(source[0].abspath, 'r', encoding="utf8")
+ outfile = open(target[0].abspath, 'w', encoding="utf8")
+ else:
+ infile = open(source[0].abspath, 'r')
+ outfile = open(target[0].abspath, 'w')
outfile.write('{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\fs16\\f0\\pard\n')
for line in infile:
- for char in line.decode("utf-8") :
+ line = line if SCons.Util.PY3 else line.decode("utf-8")
+ for char in line :
if ord(char) > 127 :
# FIXME: This is incorrect, because it only works for latin1.
# The correct way is \u<decimal utf16 point>? , but this is more
@@ -478,9 +498,16 @@ if env["PLATFORM"] == "win32" :
outfile.close()
infile.close()
copying = env.Command(["Swift/COPYING.rtf"], ["COPYING"], convertToRTF)
+ if env.get("vcredistdir", "") :
+ vcredistdir = os.path.dirname(env["vcredistdir"])
+ else:
+ vcredistdir = os.path.dirname(env["vcredist"])+"\\..\\" + ("x86" if env["win_target_arch"] == "x86" else "x64") + "\\Microsoft.VC"+env.get("MSVC_VERSION", "").replace(".","")[:3]+".CRT\\"
wixvariables = {
'VCCRTFile': env["vcredist"],
- 'Version': str(myenv["SWIFT_VERSION_MAJOR"]) + "." + str(myenv["SWIFT_VERSION_MINOR"]) + "." + str(myenv["SWIFT_VERSION_PATCH"])
+ 'VCCRTPath': vcredistdir,
+ 'Version': str(myenv["SWIFT_VERSION_MAJOR"]) + "." + str(myenv["SWIFT_VERSION_MINOR"]) + "." + str(myenv["SWIFT_VERSION_PATCH"]),
+ 'MsvcVersion': str(env.get("MSVC_VERSION", "").replace(".","")[:3]),
+ 'MsvcDotVersion': str(env.get("MSVC_VERSION", "")[:4])
}
wixincludecontent = "<Include>"
for key in wixvariables:
@@ -494,7 +521,18 @@ if env["PLATFORM"] == "win32" :
lightTask = myenv.WiX_Light('#/Packages/Swift/Swift-' + myenv["SWIFT_VERSION"] + '.msi', ['..\\Packaging\\WiX\\gen_files.wixobj','..\\Packaging\\WiX\\Swift.wixobj'])
if myenv.get("SIGNTOOL_KEY_PFX", None) and myenv.get("SIGNTOOL_TIMESTAMP_URL", None) :
def signToolAction(target = None, source = None, env = None):
- env.Execute('signtool.exe sign /fd SHA256 /f "${SIGNTOOL_KEY_PFX}" /t "${SIGNTOOL_TIMESTAMP_URL}" ' + str(target[0]))
+ signresult = 0
+ for x in range (1, 4) :
+ print("Attemping to sign the packages [%s]" % x)
+ signresult = env.Execute('signtool.exe sign /fd SHA256 /f "${SIGNTOOL_KEY_PFX}" /t "${SIGNTOOL_TIMESTAMP_URL}" /d "Swift Installer" ' + str(target[0]))
+ if signresult != 1 :
+ break
+ #If all 3 attemps to sign the package failed, stop the build.
+ if signresult == 1 :
+ print("Error: The build has failed to sign the installer package")
+ Exit(1)
+ if signresult == 2 :
+ print("Signing was completed with warnings.")
myenv.AddPostAction(lightTask, signToolAction)
diff --git a/Swift/QtUI/ServerList/QtServerListView.cpp b/Swift/QtUI/ServerList/QtServerListView.cpp
new file mode 100644
index 0000000..c22680f
--- /dev/null
+++ b/Swift/QtUI/ServerList/QtServerListView.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/ServerList/QtServerListView.h>
+
+namespace Swift {
+
+QtServerListView::QtServerListView() {
+ QPalette newPalette = palette();
+ //TODO move color and theme variables to a shared location.
+ newPalette.setColor(QPalette::Base, { 38, 81, 112 });
+ setAutoFillBackground(true);
+ setPalette(newPalette);
+ delegate_ = std::make_unique<ServerListDelegate>();
+ setItemDelegate(delegate_.get());
+ setMaximumWidth(widgetWidth);
+ setMinimumWidth(widgetWidth);
+ setFrameStyle(QFrame::NoFrame);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setSelectionMode(QAbstractItemView::NoSelection);
+}
+
+QtServerListView::~QtServerListView() {
+
+}
+
+}
diff --git a/Swift/QtUI/ServerList/QtServerListView.h b/Swift/QtUI/ServerList/QtServerListView.h
new file mode 100644
index 0000000..bd625aa
--- /dev/null
+++ b/Swift/QtUI/ServerList/QtServerListView.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <QListView>
+
+#include <Swift/QtUI/ServerList/ServerListDelegate.h>
+
+namespace Swift {
+ class QtServerListView : public QListView {
+ Q_OBJECT
+ public:
+ QtServerListView();
+ virtual ~QtServerListView();
+ private:
+ std::unique_ptr<ServerListDelegate> delegate_;
+ static const int widgetWidth = 82;
+ };
+}
diff --git a/Swift/QtUI/ServerList/ServerListDelegate.cpp b/Swift/QtUI/ServerList/ServerListDelegate.cpp
new file mode 100644
index 0000000..2afb4ea
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListDelegate.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/ServerList/ServerListDelegate.h>
+
+#include <QApplication>
+#include <QBitmap>
+#include <QBrush>
+#include <QColor>
+#include <QDebug>
+#include <QFileInfo>
+#include <QFontMetrics>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPen>
+#include <QPolygon>
+
+#include <Swift/QtUI/QtScaledAvatarCache.h>
+#include <Swift/QtUI/ServerList/ServerListModel.h>
+
+namespace Swift {
+
+ServerListDelegate::ServerListDelegate() {
+
+}
+
+ServerListDelegate::~ServerListDelegate() {
+
+}
+
+void ServerListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
+ QColor bgColor(38, 81, 112);
+ painter->fillRect(option.rect, bgColor);
+ SwiftAccountData::SwiftAccountDataItem* item = static_cast<SwiftAccountData::SwiftAccountDataItem*>(index.internalPointer());
+ paintServerAvatar(painter, option, item->iconPath_, item->status_, false, item->unreadCount_);
+}
+
+QSize ServerListDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const {
+ //TODO Make this configurable.
+ return QSize(75, 75);
+}
+
+void ServerListDelegate::paintServerAvatar(QPainter* painter, const QStyleOptionViewItem& option, const QString& avatarPath, const StatusShow& /*serverPresence*/, bool isIdle, size_t unreadCount) const {
+ painter->save();
+ QRect fullRegion(option.rect);
+ if (option.state & QStyle::State_Selected) {
+ painter->fillRect(fullRegion, option.palette.highlight());
+ painter->setPen(option.palette.highlightedText().color());
+ }
+ auto secondLineColor = painter->pen().color();
+ secondLineColor.setAlphaF(0.7);
+
+ QRect presenceRegion(QPoint(common_.farLeftMargin, fullRegion.top() + common_.horizontalMargin), QSize(presenceIconWidth, presenceIconHeight));
+ QRect idleIconRegion(QPoint(common_.farLeftMargin, fullRegion.top()), QSize(presenceIconWidth * 2, presenceIconHeight - common_.verticalMargin));
+ int calculatedAvatarSize = fullRegion.height() - common_.verticalMargin;
+ //This overlaps the presenceIcon, so must be painted first
+ QRect avatarRegion(QPoint(presenceRegion.right() - common_.presenceIconWidth / 2, presenceRegion.top()), QSize(calculatedAvatarSize, calculatedAvatarSize));
+
+ QPixmap avatarPixmap;
+ if (!avatarPath.isEmpty()) {
+ QString scaledAvatarPath = QtScaledAvatarCache(avatarRegion.height()).getScaledAvatarPath(avatarPath);
+ if (QFileInfo(scaledAvatarPath).exists()) {
+ avatarPixmap.load(scaledAvatarPath);
+ }
+ }
+ if (avatarPixmap.isNull()) {
+ avatarPixmap = QPixmap(":/icons/avatar.svg").scaled(avatarRegion.height(), avatarRegion.width(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ painter->drawPixmap(avatarRegion.topLeft() + QPoint(((avatarRegion.width() - avatarPixmap.width()) / 2), (avatarRegion.height() - avatarPixmap.height()) / 2), avatarPixmap);
+ //Paint the presence status over the top of the avatar
+ //FIXME enable drawing status when ServerPresence data are available.
+ /*{
+ //TODO make the colors consistent with chattables work from QtChatOverviewDelegate::paint, copying for now
+ const auto green = QColor(124, 243, 145);
+ const auto yellow = QColor(243, 243, 0);
+ const auto red = QColor(255, 45, 71);
+ const auto grey = QColor(159, 159, 159);
+ QColor color = grey;
+ switch (serverPresence.getType()) {
+ case StatusShow::Online: color = green; break;
+ case StatusShow::FFC: color = green; break;
+ case StatusShow::Away: color = yellow; break;
+ case StatusShow::XA: color = yellow; break;
+ case StatusShow::DND: color = red; break;
+ case StatusShow::None: color = grey; break;
+ }
+ QPen pen(color);
+ pen.setWidth(1);
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(pen);
+ painter->setBrush(QBrush(color, Qt::SolidPattern));
+ painter->drawEllipse(presenceRegion);
+ }*/
+
+ if (isIdle) {
+ common_.idleIcon.paint(painter, idleIconRegion, Qt::AlignBottom | Qt::AlignHCenter);
+ }
+
+ if (unreadCount > 0) {
+ QRect unreadRect(avatarRegion.right() - common_.unreadCountSize - common_.horizontalMargin, avatarRegion.top(), common_.unreadCountSize, common_.unreadCountSize);
+ QPen pen(QColor("black"));
+ pen.setWidth(1);
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(pen);
+ painter->setBrush(QBrush(QColor("red"), Qt::SolidPattern));
+ painter->drawEllipse(unreadRect);
+ painter->setBackgroundMode(Qt::TransparentMode);
+ painter->setPen(QColor("white"));
+ common_.drawElidedText(painter, unreadRect, QString("%1").arg(unreadCount), Qt::AlignCenter);
+ }
+ painter->restore();
+}
+
+}
diff --git a/Swift/QtUI/ServerList/ServerListDelegate.h b/Swift/QtUI/ServerList/ServerListDelegate.h
new file mode 100644
index 0000000..3f8b72b
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListDelegate.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QStyledItemDelegate>
+
+#include <Swiften/Elements/StatusShow.h>
+
+#include <Swift/QtUI/Roster/DelegateCommons.h>
+
+namespace Swift {
+
+ class ServerListDelegate : public QStyledItemDelegate {
+ public:
+ ServerListDelegate();
+ ~ServerListDelegate() override;
+ QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ private:
+ void paintServerAvatar(QPainter* painter, const QStyleOptionViewItem& option, const QString& avatarPath, const StatusShow& presence, bool isIdle, size_t unreadCount) const;
+ private:
+ DelegateCommons common_;
+ static const int presenceIconHeight = 12;
+ static const int presenceIconWidth = 12;
+ };
+
+}
diff --git a/Swift/QtUI/ServerList/ServerListModel.cpp b/Swift/QtUI/ServerList/ServerListModel.cpp
new file mode 100644
index 0000000..e5dc35e
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListModel.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+//#include <Swift/QtUI/ServerList/ServerListModel.h>
+#include <QBrush>
+#include <QColor>
+#include <QMimeData>
+
+#include "ServerListModel.h"
+
+namespace Swift {
+
+ServerListModel::ServerListModel() {
+}
+
+ServerListModel::~ServerListModel() {
+}
+
+QVariant ServerListModel::data(const QModelIndex& index, int role) const {
+ if (!index.isValid()) {
+ return QVariant();
+ }
+ SwiftAccountData::SwiftAccountDataItem* item = static_cast<SwiftAccountData::SwiftAccountDataItem*>(index.internalPointer());
+ switch (role) {
+ case Qt::DisplayRole: return QString(item->userJID_.toBare().toString().c_str());
+ case Qt::BackgroundRole: return QBrush(Qt::transparent);
+ case Qt::ToolTipRole: return QString(item->userJID_.toBare().toString().c_str());
+ default: return QVariant();
+ }
+}
+
+QModelIndex ServerListModel::index(int row, int column, const QModelIndex& /*parent*/) const {
+ if (!modelData_ || static_cast<size_t>(row) >= modelData_->size()) {
+ return QModelIndex();
+ }
+ return createIndex(row, column, modelData_->getAccount(row));
+}
+
+QModelIndex ServerListModel::parent(const QModelIndex& /*index*/) const {
+ return QModelIndex();
+}
+
+QMimeData* ServerListModel::mimeData(const QModelIndexList& indexes) const {
+ return QAbstractItemModel::mimeData(indexes);
+}
+
+int ServerListModel::rowCount(const QModelIndex& /*parent*/) const {
+ if (!modelData_) {
+ return 0;
+ }
+ return modelData_->size();
+}
+
+int ServerListModel::columnCount(const QModelIndex& /*parent*/) const {
+ if (!modelData_) {
+ return 0;
+ }
+ return 1;
+}
+
+void ServerListModel::handleDataChanged() {
+ emit layoutChanged();
+}
+
+}
diff --git a/Swift/QtUI/ServerList/ServerListModel.h b/Swift/QtUI/ServerList/ServerListModel.h
new file mode 100644
index 0000000..ae89ade
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListModel.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/signals2.hpp>
+
+#include <QAbstractItemModel>
+
+#include <Swiften/Elements/StatusShow.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+ class SwiftAccountData {
+ public:
+ struct SwiftAccountDataItem {
+ SwiftAccountDataItem(std::string serverID) : serverID_(serverID) {}
+ //FIXME eliminate serverID_, the userJID_ will be the ID when we connect with the actual data.
+ std::string serverID_;
+ QString iconPath_;
+ JID userJID_;
+ size_t unreadCount_ = 0;
+ StatusShow status_ = StatusShow::None;
+ boost::signals2::scoped_connection dataChangedConnection_;
+ boost::signals2::signal<void ()> onDataChanged;
+ void handleChangeStatusRequest(StatusShow::Type show, const std::string& /*statusText*/) {
+ status_ = show;
+ onDataChanged();
+ }
+ };
+ public:
+ SwiftAccountData() {}
+ ~SwiftAccountData() {
+ for (auto account : accounts_) {
+ delete account;
+ }
+ }
+ //FIXME make addAccount with SwiftAccountDataItem, and added after a succesfull connection to the server has been established.
+ void addAccount(JID userJID) {
+ SwiftAccountDataItem* newItem = new SwiftAccountDataItem(userJID);
+ newItem->dataChangedConnection_ = newItem->onDataChanged.connect(boost::bind(&SwiftAccountData::handleDataChanged, this));
+ accounts_.push_back(newItem);
+ }
+ SwiftAccountDataItem* getAccount(int index) {
+ if (index >= accounts_.size()) {
+ return nullptr;
+ }
+ return accounts_[index];
+ }
+ size_t size() {
+ return accounts_.size();
+ }
+ public:
+ boost::signals2::signal<void()> onDataChanged;
+ private:
+ void handleDataChanged() {
+ onDataChanged();
+ }
+ private:
+ QList<SwiftAccountDataItem*> accounts_;
+ };
+
+ class ServerListModel : public QAbstractItemModel {
+ Q_OBJECT
+ public:
+ ServerListModel();
+ ~ServerListModel() override;
+
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex& index) const override;
+
+ QMimeData* mimeData(const QModelIndexList& indexes) const override;
+
+ virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+
+ void setModelData(SwiftAccountData* data) { modelData_ = data; }
+ void handleDataChanged();
+ private:
+ SwiftAccountData* modelData_;
+ };
+}
diff --git a/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp
index b753ffa..53e2733 100644
--- a/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp
+++ b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016 Isode Limited.
+ * Copyright (c) 2014-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -23,7 +23,7 @@
namespace Swift {
-QtDynamicGridLayout::QtDynamicGridLayout(QWidget* parent, bool enableDND) : QWidget(parent), dndEnabled_(enableDND), movingTab_(nullptr) {
+QtDynamicGridLayout::QtDynamicGridLayout(bool future, QWidget* parent, bool enableDND) : QWidget(parent), dndEnabled_(enableDND), movingTab_(nullptr), future_(future) {
gridLayout_ = new QGridLayout(this);
setContentsMargins(0,0,0,0);
setDimensions(QSize(1,1));
@@ -49,6 +49,9 @@ int QtDynamicGridLayout::addTab(QtTabbable* tab, const QString& title) {
tabWidget->addTab(tab, title);
}
tab->setEmphasiseFocus(getDimension().width() > 1 || getDimension().height() > 1);
+ if (future_) {
+ showHideFirstTabs(); // FIXME: Putting it here as a workaround until I work out why it doesn't work initially
+ }
return tabWidget ? indexOf(tab) : -1;
}
@@ -144,7 +147,9 @@ void QtDynamicGridLayout::removeTab(int index) {
int tabIndex = -1;
QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex);
if (tabWidget) {
+ QWidget* tab = tabWidget->widget(tabIndex);
tabWidget->removeTab(tabIndex);
+ tab->setParent(nullptr);
}
}
@@ -326,6 +331,24 @@ void QtDynamicGridLayout::setDimensions(const QSize& dim) {
setCurrentWidget(restoredWidget);
updateEmphasiseFocusOnTabs();
+
+ if (future_) {
+ showHideFirstTabs();
+ }
+}
+
+void QtDynamicGridLayout::showHideFirstTabs() {
+ int tmp;
+ auto firstTabs = indexToTabWidget(0, tmp);
+
+ if (firstTabs) {
+ if (gridLayout_->columnCount() == 1 && gridLayout_->rowCount() == 1) {
+ firstTabs->tabBar()->hide();
+ }
+ else {
+ firstTabs->tabBar()->show();
+ }
+ }
}
void QtDynamicGridLayout::updateEmphasiseFocusOnTabs() {
@@ -491,9 +514,9 @@ void QtDynamicGridLayout::updateTabPositions() {
void QtDynamicGridLayout::moveTab(QtTabWidget* tabWidget, int oldIndex, int newIndex) {
#if QT_VERSION >= 0x040500
- SWIFT_LOG_ASSERT(movingTab_ == nullptr, error) << std::endl;
+ SWIFT_LOG_ASSERT(movingTab_ == nullptr, error);
movingTab_ = qobject_cast<QtTabbable*>(tabWidget->widget(oldIndex));
- SWIFT_LOG_ASSERT(movingTab_ != nullptr, error) << std::endl;
+ SWIFT_LOG_ASSERT(movingTab_ != nullptr, error);
if (movingTab_) {
// Install event filter that filters out events issued during the internal movement of the
@@ -503,7 +526,7 @@ void QtDynamicGridLayout::moveTab(QtTabWidget* tabWidget, int oldIndex, int newI
tabWidget->tabBar()->moveTab(oldIndex, newIndex);
qApp->removeEventFilter(this);
- SWIFT_LOG_ASSERT(movingTab_ == tabWidget->widget(newIndex), error) << std::endl;
+ SWIFT_LOG_ASSERT(movingTab_ == tabWidget->widget(newIndex), error);
}
movingTab_ = nullptr;
tabWidget->widget(newIndex)->setFocus();
diff --git a/Swift/QtUI/Trellis/QtDynamicGridLayout.h b/Swift/QtUI/Trellis/QtDynamicGridLayout.h
index 682ae41..f3a2e96 100644
--- a/Swift/QtUI/Trellis/QtDynamicGridLayout.h
+++ b/Swift/QtUI/Trellis/QtDynamicGridLayout.h
@@ -20,7 +20,7 @@ namespace Swift {
class QtDynamicGridLayout : public QWidget {
Q_OBJECT
public:
- explicit QtDynamicGridLayout(QWidget* parent = nullptr, bool enableDND = false);
+ explicit QtDynamicGridLayout(bool future, QWidget* parent = nullptr, bool enableDND = false);
virtual ~QtDynamicGridLayout();
QSize getDimension() const;
@@ -71,6 +71,7 @@ namespace Swift {
void moveTab(QtTabWidget* tabWidget, int oldIndex, int newIndex);
QtTabWidget* createDNDTabWidget(QWidget* parent);
void updateEmphasiseFocusOnTabs();
+ void showHideFirstTabs();
private:
QGridLayout *gridLayout_;
@@ -78,5 +79,6 @@ namespace Swift {
QHash<QString, QPoint> tabPositions_;
QtTabbable* movingTab_;
bool resizing_ = false;
+ bool future_ = false;
};
}
diff --git a/Swift/QtUI/Trellis/QtGridSelectionDialog.cpp b/Swift/QtUI/Trellis/QtGridSelectionDialog.cpp
index 0533edf..e922e07 100644
--- a/Swift/QtUI/Trellis/QtGridSelectionDialog.cpp
+++ b/Swift/QtUI/Trellis/QtGridSelectionDialog.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016 Isode Limited.
+ * Copyright (c) 2014-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -76,7 +76,7 @@ int QtGridSelectionDialog::getDescriptionTextHeight() const {
int QtGridSelectionDialog::getDescriptionTextHeight(int width) const {
// Height of descriptive centered text below trellis
auto fontMetrics = QFontMetrics(QApplication::font());
- auto descriptionBB = fontMetrics.boundingRect(QRect(0, 0, width - 2 * horizontalMargin, 1000), Qt::TextWordWrap, descriptionText, 0, 0);
+ auto descriptionBB = fontMetrics.boundingRect(QRect(0, 0, width - 2 * horizontalMargin, 1000), Qt::TextWordWrap, descriptionText, 0, nullptr);
return (descriptionBB.height() + descriptionBB.y());
}
@@ -136,7 +136,7 @@ void QtGridSelectionDialog::paintEvent(QPaintEvent*) {
// draw description text
auto fontMetrics = QFontMetrics(QApplication::font());
- auto descriptionBB = fontMetrics.boundingRect(QRect(0,0, width() - 2 * horizontalMargin,0), Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, descriptionText, 0, 0);
+ auto descriptionBB = fontMetrics.boundingRect(QRect(0,0, width() - 2 * horizontalMargin,0), Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, descriptionText, 0, nullptr);
QStyleOption opt;
opt.initFrom(this);
diff --git a/Swift/QtUI/UserSearch/ContactListModel.cpp b/Swift/QtUI/UserSearch/ContactListModel.cpp
index 6ef85d7..5d8aa6c 100644
--- a/Swift/QtUI/UserSearch/ContactListModel.cpp
+++ b/Swift/QtUI/UserSearch/ContactListModel.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (c) 2014-2016 Isode Limited.
+ * Copyright (c) 2014-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -22,32 +22,6 @@
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) {
}
diff --git a/Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp b/Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp
index 8c46e38..e55ee80 100644
--- a/Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp
+++ b/Swift/QtUI/UserSearch/QtSuggestingJIDInput.cpp
@@ -205,7 +205,7 @@ void QtSuggestingJIDInput::hidePopup() {
// Give focus back to input widget because the hide() call passes the focus to the wrong widget.
setFocus();
-#if defined(Q_WS_MAC)
+#if defined(Q_OS_MAC)
// This workaround is needed on OS X, to bring the dialog containing this widget back to the front after
// the popup is hidden. Ubuntu 16.04 and Windows 8 do not have this issue.
window()->raise();
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index 8d15739..4489bc0 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -606,6 +606,7 @@ void QtUserSearchWindow::clear() {
howText = QString(tr("Who do you want to invite to the chat?"));
}
firstMultiJIDPage_->howLabel_->setText(howText);
+ firstMultiJIDPage_->groupBox->setEnabled(true);
}
clearForm();
resultsPage_->results_->setModel(nullptr);
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
index d487a91..f67712e 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -8,6 +8,7 @@
#include <set>
+#include <QAbstractItemModel>
#include <QWizard>
#include <Swift/Controllers/UIInterfaces/UserSearchWindow.h>
diff --git a/Swift/QtUI/main.cpp b/Swift/QtUI/main.cpp
index 81dc670..3e20e23 100644
--- a/Swift/QtUI/main.cpp
+++ b/Swift/QtUI/main.cpp
@@ -100,7 +100,7 @@ int main(int argc, char* argv[]) {
qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + P2QSTRING(language), P2QSTRING(Swift::pathToString(someTranslationPath.parent_path())));
}
else {
- qtTranslator.load(QLocale::system(), QString(SWIFT_APPLICATION_NAME).toLower(), "_", P2QSTRING(Swift::pathToString(someTranslationPath)));
+ qtTranslator.load(QLocale::system(), QString(SWIFT_APPLICATION_NAME).toLower(), "_", P2QSTRING(Swift::pathToString(someTranslationPath.parent_path())));
}
#else
//std::cout << "Loading " << std::string(QLocale::system().name().toUtf8()) << std::endl;