summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCătălin Badea <catalin.badea392@gmail.com>2012-08-21 19:06:05 (GMT)
committerKevin Smith <git@kismith.co.uk>2012-08-30 20:54:18 (GMT)
commit7d0cd94de71d9e55e573e28206470439ecde3db5 (patch)
treec361caa96dac71d53a74ba76aa3dc1d349a0c59e /Swift/QtUI
parent6856199274e9c5e581220fccf520b8f011519d17 (diff)
downloadswift-contrib-7d0cd94de71d9e55e573e28206470439ecde3db5.zip
swift-contrib-7d0cd94de71d9e55e573e28206470439ecde3db5.tar.bz2
History dialog
Add history dialog as an experimental feature. License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.
Diffstat (limited to 'Swift/QtUI')
-rw-r--r--Swift/QtUI/MessageSnippet.cpp1
-rw-r--r--Swift/QtUI/QtChatView.cpp94
-rw-r--r--Swift/QtUI/QtChatView.h18
-rw-r--r--Swift/QtUI/QtChatWindow.cpp12
-rw-r--r--Swift/QtUI/QtHistoryWindow.cpp250
-rw-r--r--Swift/QtUI/QtHistoryWindow.h66
-rw-r--r--Swift/QtUI/QtHistoryWindow.ui101
-rw-r--r--Swift/QtUI/QtMainWindow.cpp10
-rw-r--r--Swift/QtUI/QtMainWindow.h1
-rw-r--r--Swift/QtUI/QtUIFactory.cpp21
-rw-r--r--Swift/QtUI/QtUIFactory.h4
-rw-r--r--Swift/QtUI/QtUISettingConstants.cpp1
-rw-r--r--Swift/QtUI/QtUISettingConstants.h1
-rw-r--r--Swift/QtUI/SConscript2
14 files changed, 570 insertions, 12 deletions
diff --git a/Swift/QtUI/MessageSnippet.cpp b/Swift/QtUI/MessageSnippet.cpp
index 7505905..a10ee2c 100644
--- a/Swift/QtUI/MessageSnippet.cpp
+++ b/Swift/QtUI/MessageSnippet.cpp
@@ -37,6 +37,7 @@ MessageSnippet::MessageSnippet(const QString& message, const QString& sender, co
content_.replace("%time%", wrapResizable("<span class='swift_time'>" + timeToEscapedString(time) + "</span>"));
content_.replace("%userIconPath%", escape(iconURI));
content_ = "<div id='" + id + "'>" + content_ + "</div>";
+ content_ = "<span class='date" + time.date().toString(Qt::ISODate) + "'>" + content_ + "</span>";
}
MessageSnippet::~MessageSnippet() {
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
index 49e5974..eaec3b6 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -28,7 +28,7 @@
namespace Swift {
-QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), fontSizeSteps_(0) {
+QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent, bool disableAutoScroll) : QWidget(parent), fontSizeSteps_(0), disableAutoScroll_(disableAutoScroll) {
theme_ = theme;
QVBoxLayout* mainLayout = new QVBoxLayout(this);
@@ -61,6 +61,7 @@ QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), f
//webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
webView_->setPage(webPage_);
connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
+ connect(webPage_, SIGNAL(scrollRequested(int, int, const QRect&)), SLOT(handleScrollRequested(int, int, const QRect&)));
viewReady_ = false;
isAtBottom_ = true;
@@ -85,7 +86,7 @@ void QtChatView::handleKeyPressEvent(QKeyEvent* event) {
webView_->keyPressEvent(event);
}
-void QtChatView::addMessage(boost::shared_ptr<ChatSnippet> snippet) {
+void QtChatView::addMessageBottom(boost::shared_ptr<ChatSnippet> snippet) {
if (viewReady_) {
addToDOM(snippet);
} else {
@@ -94,6 +95,45 @@ void QtChatView::addMessage(boost::shared_ptr<ChatSnippet> snippet) {
}
}
+void QtChatView::addMessageTop(boost::shared_ptr<ChatSnippet> snippet) {
+ // save scrollbar maximum value
+ if (!topMessageAdded_) {
+ scrollBarMaximum_ = webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
+ }
+ topMessageAdded_ = true;
+
+ QWebElement continuationElement = firstElement_.findFirst("#insert");
+
+ bool insert = snippet->getAppendToPrevious();
+ bool fallback = continuationElement.isNull();
+
+ boost::shared_ptr<ChatSnippet> newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet;
+ QWebElement newElement = snippetToDOM(newSnippet);
+
+ if (insert && !fallback) {
+ Q_ASSERT(!continuationElement.isNull());
+ continuationElement.replace(newElement);
+ } else {
+ continuationElement.removeFromDocument();
+ topInsertPoint_.prependOutside(newElement);
+ }
+
+ firstElement_ = newElement;
+
+ if (lastElement_.isNull()) {
+ lastElement_ = firstElement_;
+ }
+
+ if (fontSizeSteps_ != 0) {
+ double size = 1.0 + 0.2 * fontSizeSteps_;
+ QString sizeString(QString().setNum(size, 'g', 3) + "em");
+ const QWebElementCollection spans = firstElement_.findAll("span.swift_resizable");
+ foreach (QWebElement span, spans) {
+ span.setStyleProperty("font-size", sizeString);
+ }
+ }
+}
+
QWebElement QtChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) {
QWebElement newElement = newInsertPoint_.clone();
newElement.setInnerXml(snippet->getContent());
@@ -230,7 +270,7 @@ void QtChatView::displayReceiptInfo(const QString& id, bool showIt) {
}
void QtChatView::rememberScrolledToBottom() {
- isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
+ isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) >= (webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical) - 1);
}
void QtChatView::scrollToBottom() {
@@ -240,7 +280,14 @@ void QtChatView::scrollToBottom() {
}
void QtChatView::handleFrameSizeChanged() {
- if (isAtBottom_) {
+ if (topMessageAdded_) {
+ // adjust new scrollbar position
+ int newMaximum = webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
+ webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, newMaximum - scrollBarMaximum_);
+ topMessageAdded_ = false;
+ }
+
+ if (isAtBottom_ && !disableAutoScroll_) {
scrollToBottom();
}
}
@@ -282,6 +329,9 @@ void QtChatView::resizeFont(int fontSizeSteps) {
void QtChatView::resetView() {
lastElement_ = QWebElement();
+ firstElement_ = lastElement_;
+ topMessageAdded_ = false;
+ scrollBarMaximum_ = 0;
QString pageHTML = theme_->getTemplate();
pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3");
pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase());
@@ -302,12 +352,16 @@ void QtChatView::resetView() {
syncLoop.exec();
}
document_ = webPage_->mainFrame()->documentElement();
+
+ resetTopInsertPoint();
QWebElement chatElement = document_.findFirst("#Chat");
newInsertPoint_ = chatElement.clone();
newInsertPoint_.setOuterXml("<div id='swift_insert'/>");
chatElement.appendInside(newInsertPoint_);
Q_ASSERT(!newInsertPoint_.isNull());
+ scrollToBottom();
+
connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);
}
@@ -384,4 +438,36 @@ void QtChatView::setMUCInvitationJoined(QString id) {
}
}
+void QtChatView::handleScrollRequested(int, int dy, const QRect&) {
+ rememberScrolledToBottom();
+
+ int pos = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) - dy;
+ emit scrollRequested(pos);
+
+ if (pos == 0) {
+ emit scrollReachedTop();
+ }
+ else if (pos == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical)) {
+ emit scrollReachedBottom();
+ }
+}
+
+int QtChatView::getSnippetPositionByDate(const QDate& date) {
+ QWebElement message = webPage_->mainFrame()->documentElement().findFirst(".date" + date.toString(Qt::ISODate));
+
+ return message.geometry().top();
+}
+
+void QtChatView::resetTopInsertPoint() {
+ QWebElement continuationElement = firstElement_.findFirst("#insert");
+ continuationElement.removeFromDocument();
+ firstElement_ = QWebElement();
+
+ topInsertPoint_.removeFromDocument();
+ QWebElement chatElement = document_.findFirst("#Chat");
+ topInsertPoint_ = chatElement.clone();
+ topInsertPoint_.setOuterXml("<div id='swift_insert'/>");
+ chatElement.prependInside(topInsertPoint_);
+}
+
}
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
index fdbdd5a..118f14b 100644
--- a/Swift/QtUI/QtChatView.h
+++ b/Swift/QtUI/QtChatView.h
@@ -20,6 +20,7 @@
class QWebPage;
class QUrl;
+class QDate;
namespace Swift {
class QtWebView;
@@ -27,8 +28,9 @@ namespace Swift {
class QtChatView : public QWidget {
Q_OBJECT
public:
- QtChatView(QtChatTheme* theme, QWidget* parent);
- void addMessage(boost::shared_ptr<ChatSnippet> snippet);
+ QtChatView(QtChatTheme* theme, QWidget* parent, bool disableAutoScroll = false);
+ void addMessageTop(boost::shared_ptr<ChatSnippet> snippet);
+ void addMessageBottom(boost::shared_ptr<ChatSnippet> snippet);
void addLastSeenLine();
void replaceLastMessage(const QString& newMessage);
void replaceLastMessage(const QString& newMessage, const QString& note);
@@ -44,10 +46,15 @@ namespace Swift {
void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
void setMUCInvitationJoined(QString id);
void showEmoticons(bool show);
+ int getSnippetPositionByDate(const QDate& date);
+
signals:
void gotFocus();
void fontResized(int);
void logCleared();
+ void scrollRequested(int pos);
+ void scrollReachedTop();
+ void scrollReachedBottom();
public slots:
void copySelectionToClipboard();
@@ -55,6 +62,7 @@ namespace Swift {
void handleLinkClicked(const QUrl&);
void handleKeyPressEvent(QKeyEvent* event);
void resetView();
+ void resetTopInsertPoint();
void increaseFontSize(int numSteps = 1);
void decreaseFontSize();
void resizeFont(int fontSizeSteps);
@@ -63,6 +71,7 @@ namespace Swift {
void handleViewLoadFinished(bool);
void handleFrameSizeChanged();
void handleClearRequested();
+ void handleScrollRequested(int dx, int dy, const QRect& rectToScroll);
private:
void headerEncode();
@@ -72,14 +81,19 @@ namespace Swift {
bool viewReady_;
bool isAtBottom_;
+ bool topMessageAdded_;
+ int scrollBarMaximum_;
QtWebView* webView_;
QWebPage* webPage_;
int fontSizeSteps_;
QtChatTheme* theme_;
QWebElement newInsertPoint_;
+ QWebElement topInsertPoint_;
QWebElement lineSeparator_;
QWebElement lastElement_;
+ QWebElement firstElement_;
QWebElement document_;
+ bool disableAutoScroll_;
};
}
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index f42469b..ddfe158 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -524,7 +524,7 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string &
}
QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+ messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
previousMessageWasSelf_ = senderIsSelf;
previousSenderName_ = P2QSTRING(senderName);
@@ -633,7 +633,7 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se
}
QString qAvatarPath = "qrc:/icons/avatar.png";
std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+ messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
previousMessageWasSelf_ = senderIsSelf;
previousSenderName_ = P2QSTRING(senderName);
@@ -701,7 +701,7 @@ void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage)));
errorMessageHTML.replace("\n","<br/>");
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_)));
+ messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_)));
previousMessageWasSelf_ = false;
previousMessageKind_ = PreviousMessageWasSystem;
@@ -714,7 +714,7 @@ void QtChatWindow::addSystemMessage(const std::string& message) {
QString messageHTML(P2QSTRING(message));
messageHTML = linkimoticonify(messageHTML);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+ messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
previousMessageKind_ = PreviousMessageWasSystem;
}
@@ -753,7 +753,7 @@ void QtChatWindow::addPresenceMessage(const std::string& message) {
QString messageHTML(P2QSTRING(message));
messageHTML = linkimoticonify(messageHTML);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+ messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
previousMessageKind_ = PreviousMessageWasPresence;
}
@@ -950,7 +950,7 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji
}
QString qAvatarPath = "qrc:/icons/avatar.png";
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id)));
+ messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id)));
previousMessageWasSelf_ = false;
previousSenderName_ = P2QSTRING(senderName);
previousMessageKind_ = PreviousMessageWasMUCInvite;
diff --git a/Swift/QtUI/QtHistoryWindow.cpp b/Swift/QtUI/QtHistoryWindow.cpp
new file mode 100644
index 0000000..e54bd51
--- /dev/null
+++ b/Swift/QtUI/QtHistoryWindow.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2012 Catalin Badea
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <QtHistoryWindow.h>
+#include <QtTabbable.h>
+
+#include <QtSwiftUtil.h>
+#include <MessageSnippet.h>
+#include <Swiften/History/HistoryMessage.h>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <QTime>
+#include <QUrl>
+#include <QMenu>
+#include <QTextDocument>
+#include <QDateTime>
+#include <Swift/QtUI/QtScaledAvatarCache.h>
+#include <QLineEdit>
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+
+namespace Swift {
+
+QtHistoryWindow::QtHistoryWindow(SettingsProvider* settings, UIEventStream* eventStream) :
+ previousTopMessageWasSelf_(false),
+ previousBottomMessageWasSelf_(false) {
+ ui_.setupUi(this);
+
+ theme_ = new QtChatTheme("");
+ idCounter_ = 0;
+
+ delete ui_.conversation_;
+ conversation_ = new QtChatView(theme_, this, true);
+ QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ sizePolicy.setHorizontalStretch(80);
+ sizePolicy.setVerticalStretch(0);
+ conversation_->setSizePolicy(sizePolicy);
+
+ ui_.conversation_ = conversation_;
+ ui_.bottomLayout_->addWidget(conversation_);
+
+ delete ui_.conversationRoster_;
+ conversationRoster_ = new QtTreeWidget(eventStream, settings, this);
+ QSizePolicy sizePolicy2(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ sizePolicy2.setVerticalStretch(80);
+ conversationRoster_->setSizePolicy(sizePolicy2);
+ ui_.conversationRoster_ = conversationRoster_;
+ ui_.bottomLeftLayout_->setDirection(QBoxLayout::BottomToTop);
+ ui_.bottomLeftLayout_->addWidget(conversationRoster_);
+
+ setWindowTitle(tr("History"));
+
+ conversationRoster_->onSomethingSelectedChanged.connect(boost::bind(&QtHistoryWindow::handleSomethingSelectedChanged, this, _1));
+ connect(conversation_, SIGNAL(scrollRequested(int)), this, SLOT(handleScrollRequested(int)));
+ connect(conversation_, SIGNAL(scrollReachedTop()), this, SLOT(handleScrollReachedTop()));
+ connect(conversation_, SIGNAL(scrollReachedBottom()), this, SLOT(handleScrollReachedBottom()));
+ connect(conversation_, SIGNAL(fontResized(int)), this, SLOT(handleFontResized(int)));
+ connect(ui_.searchBox_->lineEdit(), SIGNAL(returnPressed()), this, SLOT(handleReturnPressed()));
+ connect(ui_.calendarWidget_, SIGNAL(clicked(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&)));
+ connect(ui_.calendarWidget_, SIGNAL(activated(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&)));
+ connect(ui_.previousButton_, SIGNAL(clicked(bool)), this, SLOT(handlePreviousButtonClicked()));
+ connect(ui_.nextButton_, SIGNAL(clicked(bool)), this, SLOT(handleNextButtonClicked()));
+}
+
+QtHistoryWindow::~QtHistoryWindow() {
+ disconnect(conversation_, SIGNAL(scrollRequested(int)), this, SLOT(handleScrollRequested(int)));
+ disconnect(conversation_, SIGNAL(scrollReachedTop()), this, SLOT(handleScrollReachedTop()));
+ disconnect(conversation_, SIGNAL(scrollReachedBottom()), this, SLOT(handleScrollReachedBottom()));
+ disconnect(conversation_, SIGNAL(fontResized(int)), this, SLOT(handleFontResized(int)));
+ disconnect(ui_.searchBox_->lineEdit(), SIGNAL(returnPressed()), this, SLOT(handleReturnPressed()));
+ disconnect(ui_.calendarWidget_, SIGNAL(clicked(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&)));
+ disconnect(ui_.calendarWidget_, SIGNAL(activated(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&)));
+ disconnect(ui_.previousButton_, SIGNAL(clicked(bool)), this, SLOT(handlePreviousButtonClicked()));
+ disconnect(ui_.nextButton_, SIGNAL(clicked(bool)), this, SLOT(handleNextButtonClicked()));
+
+ delete theme_;
+ delete conversation_;
+ // TODO: delete ui_
+}
+
+void QtHistoryWindow::activate() {
+ emit wantsToActivate();
+}
+
+void QtHistoryWindow::showEvent(QShowEvent* event) {
+ emit windowOpening();
+ emit titleUpdated();
+ QWidget::showEvent(event);
+}
+
+void QtHistoryWindow::closeEvent(QCloseEvent* event) {
+ emit windowClosing();
+ event->accept();
+}
+
+void QtHistoryWindow::setRosterModel(Roster* model) {
+ conversationRoster_->setRosterModel(model);
+}
+
+void QtHistoryWindow::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, const std::string& avatarPath, const boost::posix_time::ptime& time, bool addAtTheTop) {
+ QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());
+
+ QString messageHTML(P2QSTRING(message));
+ messageHTML = Qt::escape(messageHTML);
+ QString searchTerm = ui_.searchBox_->lineEdit()->text();
+ if (searchTerm.length()) {
+ messageHTML.replace(searchTerm, "<span style='background-color: yellow'>" + searchTerm + "</span>");
+ }
+
+ // note: time uses localtime
+ QDate date = QDate(time.date().year(), time.date().month(), time.date().day());
+ 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_++);
+
+ QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
+
+ if (addAtTheTop) {
+ bool appendToPrevious = ((senderIsSelf && previousTopMessageWasSelf_) || (!senderIsSelf && !previousTopMessageWasSelf_&& previousTopSenderName_ == P2QSTRING(senderName)));
+ conversation_->addMessageTop(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+
+ previousTopMessageWasSelf_ = senderIsSelf;
+ previousTopSenderName_ = P2QSTRING(senderName);
+ }
+ else {
+ bool appendToPrevious = ((senderIsSelf && previousBottomMessageWasSelf_) || (!senderIsSelf && !previousBottomMessageWasSelf_&& previousBottomSenderName_ == P2QSTRING(senderName)));
+ conversation_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+ previousBottomMessageWasSelf_ = senderIsSelf;
+ previousBottomSenderName_ = P2QSTRING(senderName);
+ }
+
+ // keep track of the days viewable in the chatView
+ if (!dates_.count(date)) {
+ dates_.insert(date);
+ }
+}
+
+void QtHistoryWindow::handleSomethingSelectedChanged(RosterItem* item) {
+ onSelectedContactChanged(item);
+}
+
+void QtHistoryWindow::resetConversationView() {
+ previousTopMessageWasSelf_ = false;
+ previousBottomMessageWasSelf_ = false;
+ previousTopSenderName_.clear();
+ previousBottomSenderName_.clear();
+
+ dates_.clear();
+ conversation_->resetView();
+}
+
+void QtHistoryWindow::handleScrollRequested(int pos) {
+ // first message starts with offset 5
+ if (pos < 5) {
+ pos = 5;
+ }
+
+ QDate currentDate;
+ foreach (const QDate& date, dates_) {
+ int snippetPosition = conversation_->getSnippetPositionByDate(date);
+ if (snippetPosition <= pos) {
+ currentDate = date;
+ }
+ }
+
+ if (ui_.calendarWidget_->selectedDate() != currentDate) {
+ ui_.calendarWidget_->setSelectedDate(currentDate);
+ }
+}
+
+void QtHistoryWindow::handleScrollReachedTop() {
+ if (dates_.empty()) {
+ return;
+ }
+
+ int year, month, day;
+ QDate firstDate = *dates_.begin();
+ firstDate.getDate(&year, &month, &day);
+ onScrollReachedTop(boost::gregorian::date(year, month, day));
+}
+
+void QtHistoryWindow::handleScrollReachedBottom() {
+ if (dates_.empty()) {
+ return;
+ }
+
+ int year, month, day;
+ QDate lastDate = *dates_.rbegin();
+ lastDate.getDate(&year, &month, &day);
+ onScrollReachedBottom(boost::gregorian::date(year, month, day));
+}
+
+void QtHistoryWindow::handleReturnPressed() {
+ onReturnPressed(ui_.searchBox_->lineEdit()->text().toStdString());
+}
+
+void QtHistoryWindow::handleCalendarClicked(const QDate& date) {
+ int year, month, day;
+ QDate tempDate = date; // getDate discards const qualifier
+ tempDate.getDate(&year, &month, &day);
+ onCalendarClicked(boost::gregorian::date(year, month, day));
+}
+
+void QtHistoryWindow::setDate(const boost::gregorian::date& date) {
+ ui_.calendarWidget_->setSelectedDate(QDate::fromJulianDay(date.julian_day()));
+}
+
+void QtHistoryWindow::handleNextButtonClicked() {
+ onNextButtonClicked();
+}
+
+void QtHistoryWindow::handlePreviousButtonClicked() {
+ onPreviousButtonClicked();
+}
+
+void QtHistoryWindow::handleFontResized(int fontSizeSteps) {
+ conversation_->resizeFont(fontSizeSteps);
+
+ emit fontResized(fontSizeSteps);
+}
+
+void QtHistoryWindow::resetConversationViewTopInsertPoint() {
+ previousTopMessageWasSelf_ = false;
+ previousTopSenderName_ = QString();
+ conversation_->resetTopInsertPoint();
+}
+
+std::string QtHistoryWindow::getSearchBoxText() {
+ return ui_.searchBox_->lineEdit()->text().toStdString();
+}
+
+boost::gregorian::date QtHistoryWindow::getLastVisibleDate() {
+ if (!dates_.empty()) {
+ QDate lastDate = *dates_.rbegin();
+ int year, month, day;
+ lastDate.getDate(&year, &month, &day);
+
+ return boost::gregorian::date(year, month, day);
+ }
+ return boost::gregorian::date(boost::gregorian::not_a_date_time);
+}
+
+}
diff --git a/Swift/QtUI/QtHistoryWindow.h b/Swift/QtUI/QtHistoryWindow.h
new file mode 100644
index 0000000..49de098
--- /dev/null
+++ b/Swift/QtUI/QtHistoryWindow.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012 Catalin Badea
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/Controllers/UIInterfaces/HistoryWindow.h>
+#include <Swift/QtUI/ui_QtHistoryWindow.h>
+#include <QtChatView.h>
+#include <QtTabbable.h>
+#include <Swift/QtUI/Roster/QtTreeWidget.h>
+#include <set>
+#include <QDate>
+
+namespace Swift {
+ class QtHistoryWindow : public QtTabbable, public HistoryWindow {
+ Q_OBJECT
+
+ public:
+ QtHistoryWindow(SettingsProvider*, UIEventStream*);
+ ~QtHistoryWindow();
+ void activate();
+ void setRosterModel(Roster*);
+ void addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const std::string& avatarPath, const boost::posix_time::ptime& time, bool addAtTheTop);
+ void resetConversationView();
+ void resetConversationViewTopInsertPoint();
+ void setDate(const boost::gregorian::date& date);
+
+ void closeEvent(QCloseEvent* event);
+ void showEvent(QShowEvent* event);
+
+ std::string getSearchBoxText();
+ boost::gregorian::date getLastVisibleDate();
+
+ signals:
+ void fontResized(int);
+
+ public slots:
+ void handleFontResized(int fontSizeSteps);
+
+ protected slots:
+ void handleScrollRequested(int pos);
+ void handleScrollReachedTop();
+ void handleScrollReachedBottom();
+ void handleReturnPressed();
+ void handleCalendarClicked(const QDate& date);
+ void handlePreviousButtonClicked();
+ void handleNextButtonClicked();
+
+ private:
+ void handleSomethingSelectedChanged(RosterItem* item);
+
+ Ui::QtHistoryWindow ui_;
+ QtChatTheme* theme_;
+ QtChatView* conversation_;
+ QtTreeWidget* conversationRoster_;
+ std::set<QDate> dates_;
+ int idCounter_;
+ bool previousTopMessageWasSelf_;
+ QString previousTopSenderName_;
+ bool previousBottomMessageWasSelf_;
+ QString previousBottomSenderName_;
+ };
+}
diff --git a/Swift/QtUI/QtHistoryWindow.ui b/Swift/QtUI/QtHistoryWindow.ui
new file mode 100644
index 0000000..77d592f
--- /dev/null
+++ b/Swift/QtUI/QtHistoryWindow.ui
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtHistoryWindow</class>
+ <widget class="QWidget" name="QtHistoryWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>608</width>
+ <height>522</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="topLayout_">
+ <item>
+ <widget class="QLabel" name="label_">
+ <property name="text">
+ <string>Search:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="searchBox_">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="nextButton_">
+ <property name="text">
+ <string>Next</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="previousButton_">
+ <property name="text">
+ <string>Previous</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QSplitter" name="bottomLayout_">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QWidget" name="layoutWidget">
+ <layout class="QVBoxLayout" name="bottomLeftLayout_" stretch="0,0">
+ <item>
+ <widget class="QWidget" name="conversationRoster_" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>5</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCalendarWidget" name="calendarWidget_">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="conversation_" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>85</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index d312546..ced375f 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -27,6 +27,7 @@
#include <Swift/QtUI/QtLoginWindow.h>
#include <Roster/QtRosterWidget.h>
#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestHistoryUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h>
@@ -122,6 +123,11 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
QAction* joinMUCAction = new QAction(tr("Enter &Room…"), this);
connect(joinMUCAction, SIGNAL(triggered()), SLOT(handleJoinMUCAction()));
actionsMenu->addAction(joinMUCAction);
+#ifdef SWIFT_EXPERIMENTAL_HISTORY
+ QAction* viewLogsAction = new QAction(tr("&View History…"), this);
+ connect(viewLogsAction, SIGNAL(triggered()), SLOT(handleViewLogsAction()));
+ actionsMenu->addAction(viewLogsAction);
+#endif
addUserAction_ = new QAction(tr("&Add Contact…"), this);
connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool)));
actionsMenu->addAction(addUserAction_);
@@ -235,6 +241,10 @@ void QtMainWindow::handleJoinMUCAction() {
uiEventStream_->send(boost::make_shared<RequestJoinMUCUIEvent>());
}
+void QtMainWindow::handleViewLogsAction() {
+ uiEventStream_->send(boost::make_shared<RequestHistoryUIEvent>());
+}
+
void QtMainWindow::handleStatusChanged(StatusShow::Type showType, const QString &statusMessage) {
onChangeStatusRequest(showType, Q2PSTRING(statusMessage));
}
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index 251c346..26d25e1 100644
--- a/Swift/QtUI/QtMainWindow.h
+++ b/Swift/QtUI/QtMainWindow.h
@@ -58,6 +58,7 @@ namespace Swift {
void handleShowOfflineToggled(bool);
void handleShowEmoticonsToggled(bool);
void handleJoinMUCAction();
+ void handleViewLogsAction();
void handleSignOutAction();
void handleEditProfileAction();
void handleAddUserActionTriggered(bool checked);
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index 78de7aa..2197ec6 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -27,11 +27,13 @@
#include "QtFileTransferListWidget.h"
#include <Swift/Controllers/Settings/SettingsProviderHierachy.h>
#include <Swift/QtUI/QtUISettingConstants.h>
+#include <QtHistoryWindow.h>
namespace Swift {
QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized), emoticonsExist_(emoticonsExist) {
chatFontSize = settings->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE);
+ historyFontSize_ = settings->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE);
}
XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() {
@@ -44,6 +46,25 @@ XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() {
return widget;
}
+HistoryWindow* QtUIFactory::createHistoryWindow(UIEventStream* uiEventStream) {
+ QtHistoryWindow* window = new QtHistoryWindow(settings, uiEventStream);
+ tabs->addTab(window);
+ if (!tabs->isVisible()) {
+ tabs->show();
+ }
+
+ connect(window, SIGNAL(fontResized(int)), this, SLOT(handleHistoryWindowFontResized(int)));
+
+ window->handleFontResized(historyFontSize_);
+ window->show();
+ return window;
+}
+
+void QtUIFactory::handleHistoryWindowFontResized(int size) {
+ historyFontSize_ = size;
+ settings->storeSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE, size);
+}
+
FileTransferListWidget* QtUIFactory::createFileTransferListWidget() {
QtFileTransferListWidget* widget = new QtFileTransferListWidget();
tabs->addTab(widget);
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index edb89ad..1b2431f 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -24,6 +24,7 @@ namespace Swift {
class QtChatWindowFactory;
class QtChatWindow;
class TimerFactory;
+ class historyWindow_;
class QtUIFactory : public QObject, public UIFactory {
Q_OBJECT
@@ -31,6 +32,7 @@ namespace Swift {
QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist);
virtual XMLConsoleWidget* createXMLConsoleWidget();
+ virtual HistoryWindow* createHistoryWindow(UIEventStream*);
virtual MainWindow* createMainWindow(UIEventStream* eventStream);
virtual LoginWindow* createLoginWindow(UIEventStream* eventStream);
virtual EventWindow* createEventWindow();
@@ -47,6 +49,7 @@ namespace Swift {
private slots:
void handleLoginWindowGeometryChanged();
void handleChatWindowFontResized(int);
+ void handleHistoryWindowFontResized(int);
private:
SettingsProviderHierachy* settings;
@@ -61,6 +64,7 @@ namespace Swift {
std::vector<QPointer<QtChatWindow> > chatWindows;
bool startMinimized;
int chatFontSize;
+ int historyFontSize_;
bool emoticonsExist_;
};
}
diff --git a/Swift/QtUI/QtUISettingConstants.cpp b/Swift/QtUI/QtUISettingConstants.cpp
index 81022ec..68001d7 100644
--- a/Swift/QtUI/QtUISettingConstants.cpp
+++ b/Swift/QtUI/QtUISettingConstants.cpp
@@ -13,5 +13,6 @@ const SettingsProvider::Setting<std::string> QtUISettingConstants::CLICKTHROUGH_
const SettingsProvider::Setting<int> QtUISettingConstants::CURRENT_ROSTER_TAB("currentRosterTab", 0);
const SettingsProvider::Setting<bool> QtUISettingConstants::SHOW_NICK_IN_ROSTER_HEADER("showNickInRosterHeader", true);
const SettingsProvider::Setting<int> QtUISettingConstants::CHATWINDOW_FONT_SIZE("chatWindowFontSize", 0);
+const SettingsProvider::Setting<int> QtUISettingConstants::HISTORYWINDOW_FONT_SIZE("historyWindowFontSize", 0);
const SettingsProvider::Setting<bool> QtUISettingConstants::SHOW_EMOTICONS("showEmoticons", true);
}
diff --git a/Swift/QtUI/QtUISettingConstants.h b/Swift/QtUI/QtUISettingConstants.h
index 2740abb..8ac835f 100644
--- a/Swift/QtUI/QtUISettingConstants.h
+++ b/Swift/QtUI/QtUISettingConstants.h
@@ -16,6 +16,7 @@ namespace Swift {
static const SettingsProvider::Setting<int> CURRENT_ROSTER_TAB;
static const SettingsProvider::Setting<bool> SHOW_NICK_IN_ROSTER_HEADER;
static const SettingsProvider::Setting<int> CHATWINDOW_FONT_SIZE;
+ static const SettingsProvider::Setting<int> HISTORYWINDOW_FONT_SIZE;
static const SettingsProvider::Setting<bool> SHOW_EMOTICONS;
};
}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 064faab..27ff237 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -94,6 +94,7 @@ sources = [
"QtTabWidget.cpp",
"QtTextEdit.cpp",
"QtXMLConsoleWidget.cpp",
+ "QtHistoryWindow.cpp",
"QtFileTransferListWidget.cpp",
"QtFileTransferListItemModel.cpp",
"QtAdHocCommandWindow.cpp",
@@ -198,6 +199,7 @@ myenv.Uic4("UserSearch/QtUserSearchResultsPage.ui")
myenv.Uic4("QtBookmarkDetailWindow.ui")
myenv.Uic4("QtAffiliationEditor.ui")
myenv.Uic4("QtJoinMUCWindow.ui")
+myenv.Uic4("QtHistoryWindow.ui")
myenv.Qrc("DefaultTheme.qrc")
myenv.Qrc("Swift.qrc")