summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/QtUI')
-rw-r--r--Swift/QtUI/QtChatWindow.cpp20
-rw-r--r--Swift/QtUI/QtChatWindow.h5
-rw-r--r--Swift/QtUI/QtUtilities.cpp15
-rw-r--r--Swift/QtUI/QtUtilities.h11
-rw-r--r--Swift/QtUI/SConscript15
-rw-r--r--Swift/QtUI/UnitTest/QtUtilitiesTest.cpp30
6 files changed, 90 insertions, 6 deletions
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index c509ab3..48d331e 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -9,60 +9,61 @@
#include <map>
#include <memory>
#include <string>
#include <boost/cstdint.hpp>
#include <boost/lexical_cast.hpp>
#include <QApplication>
#include <QBoxLayout>
#include <QCloseEvent>
#include <QComboBox>
#include <QCursor>
#include <QDebug>
#include <QFileDialog>
#include <QFileInfo>
#include <QFontMetrics>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QPoint>
#include <QPushButton>
#include <QSize>
#include <QSplitter>
#include <QString>
#include <QTextDocument>
#include <QTextEdit>
#include <QTime>
+#include <QTimer>
#include <QToolButton>
#include <QUrl>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/Platform.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/RosterItem.h>
#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <SwifTools/EmojiMapper.h>
#include <SwifTools/TabComplete.h>
#include <Swift/QtUI/QtAddBookmarkWindow.h>
#include <Swift/QtUI/QtEditBookmarkWindow.h>
#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtPlainChatView.h>
#include <Swift/QtUI/QtScaledAvatarCache.h>
#include <Swift/QtUI/QtSettingsProvider.h>
#include <Swift/QtUI/QtTextEdit.h>
#include <Swift/QtUI/QtUISettingConstants.h>
#include <Swift/QtUI/QtUtilities.h>
#include <Swift/QtUI/QtWebKitChatView.h>
#include <Swift/QtUI/Roster/QtOccupantListWidget.h>
namespace Swift {
@@ -162,78 +163,89 @@ QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventSt
QHBoxLayout* actionLayout = new QHBoxLayout();
actionLayout->addWidget(emojisButton_);
actionLayout->addWidget(actionButton_);
inputBarLayout->addLayout(actionLayout);
layout->addLayout(inputBarLayout);
inputClearing_ = false;
contactIsTyping_ = false;
tabCompletion_ = false;
connect(input_, SIGNAL(unhandledKeyPressEvent(QKeyEvent*)), this, SLOT(handleKeyPressEvent(QKeyEvent*)));
connect(input_, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
connect(input_, SIGNAL(textChanged()), this, SLOT(handleInputChanged()));
connect(input_, SIGNAL(cursorPositionChanged()), this, SLOT(handleCursorPositionChanged()));
setFocusProxy(input_);
logRosterSplitter_->setFocusProxy(input_);
midBar_->setFocusProxy(input_);
messageLog_->setFocusProxy(input_);
connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus()));
resize(400,300);
connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));
connect(messageLog_, SIGNAL(logCleared()), this, SLOT(handleLogCleared()));
treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));
settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1));
messageLog_->showEmoticons(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS));
setMinimumSize(100, 100);
+
+ dayChangeTimer = new QTimer(this);
+ dayChangeTimer->setSingleShot(true);
+ connect(dayChangeTimer, &QTimer::timeout, [this](){
+ addSystemMessage(ChatMessage(Q2PSTRING(tr("The day is now %1").arg(QDateTime::currentDateTime().date().toString(Qt::SystemLocaleLongDate)))), ChatWindow::DefaultDirection);
+ onContinuationsBroken();
+ resetDayChangeTimer();
+ });
+
+ resetDayChangeTimer();
}
QtChatWindow::~QtChatWindow() {
+ dayChangeTimer->stop();
settings_->onSettingChanged.disconnect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1));
if (mucConfigurationWindow_) {
delete mucConfigurationWindow_.data();
}
}
void QtChatWindow::handleSettingChanged(const std::string& setting) {
if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) {
bool showEmoticons = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
messageLog_->showEmoticons(showEmoticons);
}
}
void QtChatWindow::handleLogCleared() {
- onLogCleared();
+ onContinuationsBroken();
}
void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
}
void QtChatWindow::handleFontResized(int fontSizeSteps) {
messageLog_->resizeFont(fontSizeSteps);
}
void QtChatWindow::handleAlertButtonClicked() {
const QObject* alertWidget = QObject::sender()->parent();
std::map<AlertID, QWidget*>::const_iterator i = alertWidgets_.begin();
for ( ; i != alertWidgets_.end(); ++i) {
if (i->second == alertWidget) {
removeAlert(i->first);
break;
}
}
}
QtChatWindow::AlertID QtChatWindow::addAlert(const std::string& alertText) {
QWidget* alertWidget = new QWidget(this);
QHBoxLayout* alertLayout = new QHBoxLayout(alertWidget);
alertLayout_->addWidget(alertWidget);
QLabel* alertLabel = new QLabel(this);
alertLabel->setText(alertText.c_str());
alertLayout->addWidget(alertLabel);
QToolButton* closeButton = new QToolButton(alertWidget);
@@ -629,60 +641,66 @@ void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) {
void QtChatWindow::dropEvent(QDropEvent *event) {
if (fileTransferEnabled_ == Yes && event->mimeData()->hasUrls()) {
if (event->mimeData()->urls().size() == 1) {
onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile()));
}
else {
std::string messageText(Q2PSTRING(tr("Sending of multiple files at once isn't supported at this time.")));
ChatMessage message;
message.append(std::make_shared<ChatTextMessagePart>(messageText));
addSystemMessage(message, DefaultDirection);
}
}
else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) {
std::vector<JID> invites = jidListFromQByteArray(event->mimeData()->data("application/vnd.swift.contact-jid-list"));
onInviteToChat(invites);
}
}
std::vector<JID> QtChatWindow::jidListFromQByteArray(const QByteArray& dataBytes) {
QDataStream dataStream(dataBytes);
std::vector<JID> invites;
while (!dataStream.atEnd()) {
QString jidString;
dataStream >> jidString;
invites.push_back(Q2PSTRING(jidString));
}
return invites;
}
+void QtChatWindow::resetDayChangeTimer() {
+ assert(dayChangeTimer);
+ // Add a second so the handled is definitly called on the next day, and not multiple times exactly at midnight.
+ dayChangeTimer->start(QtUtilities::secondsToNextMidnight(QDateTime::currentDateTime()) * 1000 + 1000);
+}
+
void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
treeWidget_->setAvailableOccupantActions(actions);
}
void QtChatWindow::setSubject(const std::string& subject) {
//subject_->setVisible(!subject.empty());
subject_->setText(P2QSTRING(subject));
subject_->setToolTip(P2QSTRING(subject));
subject_->setCursorPosition(0);
}
void QtChatWindow::handleEmojisButtonClicked() {
// Create QtEmojisSelector and QMenu
emojisGrid_ = new QtEmojisSelector(qtOnlySettings_->getQSettings(), emoticonsMap_);
auto emojisLayout = new QVBoxLayout();
emojisLayout->setContentsMargins(style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin),
style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin));
emojisLayout->addWidget(emojisGrid_);
emojisMenu_ = std::unique_ptr<QMenu>(new QMenu());
emojisMenu_->setLayout(emojisLayout);
emojisMenu_->adjustSize();
connect(emojisGrid_, SIGNAL(emojiClicked(QString)), this, SLOT(handleEmojiClicked(QString)));
QSize menuSize = emojisMenu_->size();
emojisMenu_->exec(QPoint(QCursor::pos().x() - menuSize.width(), QCursor::pos().y() - menuSize.height()));
}
void QtChatWindow::handleEmojiClicked(QString emoji) {
if (isVisible()) {
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 46186ba..361d7c6 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -165,78 +165,81 @@ namespace Swift {
protected:
void showEvent(QShowEvent* event);
private slots:
void handleLogCleared();
void returnPressed();
void handleInputChanged();
void handleCursorPositionChanged();
void handleKeyPressEvent(QKeyEvent* event);
void handleSplitterMoved(int pos, int index);
void handleAlertButtonClicked();
void handleActionButtonClicked();
void handleAffiliationEditorAccepted();
void handleCurrentLabelChanged(int);
void handleEmojisButtonClicked();
void handleTextInputReceivedFocus();
void handleTextInputLostFocus();
private:
void updateTitleWithUnreadCount();
void tabComplete();
void beginCorrection();
void cancelCorrection();
void handleSettingChanged(const std::string& setting);
void handleOccupantSelectionChanged(RosterItem* item);
void handleAppendedToLog();
static std::vector<JID> jidListFromQByteArray(const QByteArray& dataBytes);
+ void resetDayChangeTimer();
+
private:
int unreadCount_;
bool contactIsTyping_;
LastLineTracker lastLineTracker_;
std::string id_;
QString contact_;
QString lastSentMessage_;
QTextCursor tabCompleteCursor_;
QtChatView* messageLog_;
QtChatTheme* theme_;
QtTextEdit* input_;
QWidget* midBar_;
QBoxLayout* subjectLayout_;
QComboBox* labelsWidget_;
QtOccupantListWidget* treeWidget_;
QLabel* correctingLabel_;
boost::optional<AlertID> correctingAlert_;
QVBoxLayout* alertLayout_;
std::map<AlertID, QWidget*> alertWidgets_;
AlertID nextAlertId_;
TabComplete* completer_;
QLineEdit* subject_;
bool isCorrection_;
bool inputClearing_;
bool tabCompletion_;
UIEventStream* eventStream_;
bool isOnline_;
- QSplitter *logRosterSplitter_;
+ QSplitter* logRosterSplitter_;
Tristate correctionEnabled_;
Tristate fileTransferEnabled_;
QString alertStyleSheet_;
QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_;
QPointer<QtAffiliationEditor> affiliationEditor_;
SettingsProvider* settings_ = nullptr;
QtSettingsProvider* qtOnlySettings_ = nullptr;
std::vector<ChatWindow::RoomAction> availableRoomActions_;
QPalette defaultLabelsPalette_;
LabelModel* labelModel_;
BlockingState blockingState_;
bool impromptu_;
bool isMUC_;
bool supportsImpromptuChat_;
RoomBookmarkState roomBookmarkState_;
std::unique_ptr<QMenu> emojisMenu_;
QPointer<QtEmojisSelector> emojisGrid_;
std::map<std::string, std::string> emoticonsMap_;
+ QTimer* dayChangeTimer = nullptr;
};
}
diff --git a/Swift/QtUI/QtUtilities.cpp b/Swift/QtUI/QtUtilities.cpp
index 401af17..6eb0b04 100644
--- a/Swift/QtUI/QtUtilities.cpp
+++ b/Swift/QtUI/QtUtilities.cpp
@@ -1,45 +1,54 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
-#include "QtUtilities.h"
+#include <Swift/QtUI/QtUtilities.h>
#include <QtGui>
#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC)
#include <QX11Info>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
#include <QTextDocument>
#include <QWidget>
+#include <QDateTime>
-#include "Swift/Controllers/ApplicationInfo.h"
+#include <Swift/Controllers/ApplicationInfo.h>
namespace QtUtilities {
void setX11Resource(QWidget* widget, const QString& c) {
#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) && QT_VERSION < 0x050000
char res_class[] = SWIFT_APPLICATION_NAME;
XClassHint hint;
QByteArray resName = (QString(SWIFT_APPLICATION_NAME) + "-" + c).toUtf8();
hint.res_name = resName.data();
hint.res_class = res_class;
XSetClassHint(widget->x11Info().display(), widget->winId(), &hint);
#else
(void) widget;
(void) c;
#endif
}
QString htmlEscape(const QString& s) {
#if QT_VERSION >= 0x050000
return s.toHtmlEscaped();
#else
return Qt::escape(s);
#endif
}
+long long secondsToNextMidnight(const QDateTime& currentTime) {
+ auto secondsToMidnight = 0ll;
+ auto nextMidnight = currentTime.addDays(1);
+ nextMidnight.setTime(QTime(0,0));
+ secondsToMidnight = currentTime.secsTo(nextMidnight);
+ return secondsToMidnight;
+}
+
}
diff --git a/Swift/QtUI/QtUtilities.h b/Swift/QtUI/QtUtilities.h
index ad58499..c6f4311 100644
--- a/Swift/QtUI/QtUtilities.h
+++ b/Swift/QtUI/QtUtilities.h
@@ -1,22 +1,31 @@
/*
- * Copyright (c) 2010-2013 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
class QWidget;
class QString;
+class QDateTime;
#include <QKeyEvent>
namespace QtUtilities {
void setX11Resource(QWidget* widget, const QString& c);
QString htmlEscape(const QString& s);
#ifdef SWIFTEN_PLATFORM_MACOSX
const Qt::KeyboardModifier ctrlHardwareKeyModifier = Qt::MetaModifier;
#else
const Qt::KeyboardModifier ctrlHardwareKeyModifier = Qt::ControlModifier;
#endif
+
+ /**
+ * @brief secondsToNextMidnight calculates the seconds until next midnight.
+ * @param currentTime This is the current time, which SHOULD have a Qt::TimeSpec
+ * of Qt::LocalTime to correctly handle DST changes in the current locale.
+ * @return
+ */
+ long long secondsToNextMidnight(const QDateTime& currentTime);
}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 2f95b3e..3ce2057 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -44,60 +44,75 @@ if myenv.get("HAVE_GROWL", False) :
if myenv["swift_mobile"] :
myenv.Append(CPPDEFINES = ["SWIFT_MOBILE"])
if myenv.get("HAVE_HUNSPELL", True):
myenv.Append(CPPDEFINES = ["HAVE_HUNSPELL"])
myenv.UseFlags(myenv["HUNSPELL_FLAGS"])
if env["PLATFORM"] == "win32" :
myenv.Append(LIBS = ["cryptui"])
myenv.UseFlags(myenv["PLATFORM_FLAGS"])
myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"])
myenv.Tool("nsis", toolpath = ["#/BuildTools/SCons/Tools"])
myenv.Tool("wix", toolpath = ["#/BuildTools/SCons/Tools"])
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
# correctly by Qt's deployment tools.
qt4modules += ['QtWidgets', 'QtWebKitWidgets', 'QtMultimedia', 'QtSvg']
if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" :
qt4modules += ['QtX11Extras']
else :
qt_version = '4'
if env["PLATFORM"] == "posix" :
qt4modules += ["QtDBus"]
if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" :
qt4modules += ["QtNetwork"]
myenv.EnableQt4Modules(qt4modules, debug = False, version = qt_version)
+
+## Qt related unit tests
+testQtEnv = env.Clone();
+testQtEnv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"])
+testQtEnv.EnableQt4Modules(qt4modules, debug = False, version = qt_version)
+env["SWIFT_QTUI_TEST_FLAGS"] = {
+ "CPPFLAGS": testQtEnv["CPPFLAGS"],
+ "LIBS": testQtEnv["LIBS"],
+ "LINKFLAGS": testQtEnv["LINKFLAGS"],
+}
+
+env.Append(UNITTEST_SOURCES = [
+ File("UnitTest/QtUtilitiesTest.cpp")
+])
+
myenv.Append(CPPPATH = ["."])
# Qt requires applications to be build with the -fPIC flag on some 32-bit Linux distributions.
if env["PLATFORM"] == "posix" :
testEnv = myenv.Clone()
conf = Configure(testEnv)
if conf.CheckDeclaration("QT_REDUCE_RELOCATIONS", "#include <QtCore/qconfig.h>") and conf.CheckDeclaration("__i386__"):
myenv.AppendUnique(CXXFLAGS = "-fPIC")
testEnv = conf.Finish()
if env["PLATFORM"] == "win32" :
#myenv.Append(LINKFLAGS = ["/SUBSYSTEM:CONSOLE"])
myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"])
myenv.Append(LIBS = "qtmain")
if myenv.get("HAVE_SCHANNEL", 0) :
myenv.Append(LIBS = "Cryptui")
myenv.Append(CPPDEFINES = "HAVE_SCHANNEL")
if env["PLATFORM"] == "darwin" and env["HAVE_SPARKLE"] :
myenv.Append(LINKFLAGS = ["-Wl,-rpath,@loader_path/../Frameworks"])
myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateQRCTheme(myenv.Dir("#/Swift/resources/themes/Default"), "Default")))
sources = [
"main.cpp",
"FlowLayout.cpp",
"QtAboutWidget.cpp",
"QtSpellCheckerWindow.cpp",
"QtAvatarWidget.cpp",
"QtUIFactory.cpp",
diff --git a/Swift/QtUI/UnitTest/QtUtilitiesTest.cpp b/Swift/QtUI/UnitTest/QtUtilitiesTest.cpp
new file mode 100644
index 0000000..45d2239
--- /dev/null
+++ b/Swift/QtUI/UnitTest/QtUtilitiesTest.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <gtest/gtest.h>
+
+#include <QDateTime>
+#include <QLocale>
+
+#include <Swift/QtUI/QtUtilities.h>
+#include <Swift/QtUI/QtUtilities.cpp>
+
+TEST(QtUtilitiesTest, testDSTawareness) {
+ QLocale::setDefault(QLocale(QLocale::English, QLocale::Germany));
+
+ auto beforeDSTpoint = QDateTime(QDate(2017, 3, 26), QTime(0, 0));
+
+ ASSERT_EQ(23 * 60 * 60, QtUtilities::secondsToNextMidnight(beforeDSTpoint));
+}
+
+
+TEST(QtUtilitiesTest, testNoDSTChange) {
+ QLocale::setDefault(QLocale(QLocale::English, QLocale::Germany));
+
+ auto beforeDSTpoint = QDateTime(QDate(2017, 3, 23), QTime(0, 0));
+
+ ASSERT_EQ(24 * 60 * 60, QtUtilities::secondsToNextMidnight(beforeDSTpoint));
+}