1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge pull request #5672

TriKriSta (10):
      feat: load messages from the database before date
      feat: load messages from the database after date
      feat: edit function "Load chat history"
      feat: edit load history in search
      feat: add action "Go to current date"
      feat: edit position chat after load history
      feat: remove part messages from chat
      refactor: simple edit code
      refactor: edit load history when scrolling
      feat: prohibition to remove messages in group chat
This commit is contained in:
sudden6 2019-07-22 22:49:15 +02:00
commit be55759555
No known key found for this signature in database
GPG Key ID: 279509B499E032B9
23 changed files with 436 additions and 76 deletions

View File

@ -49,8 +49,8 @@ T clamp(T x, T min, T max)
return x;
}
ChatLog::ChatLog(QWidget* parent)
: QGraphicsView(parent)
ChatLog::ChatLog(const bool canRemove, QWidget* parent)
: QGraphicsView(parent), canRemove(canRemove)
{
// Create the scene
busyScene = new QGraphicsScene(this);
@ -168,8 +168,9 @@ void ChatLog::updateSceneRect()
void ChatLog::layout(int start, int end, qreal width)
{
if (lines.empty())
if (lines.empty()) {
return;
}
qreal h = 0.0;
@ -312,8 +313,9 @@ void ChatLog::mouseMoveEvent(QMouseEvent* ev)
// Much faster than QGraphicsScene::itemAt()!
ChatLineContent* ChatLog::getContentFromPos(QPointF scenePos) const
{
if (lines.empty())
if (lines.empty()) {
return nullptr;
}
auto itr =
std::lower_bound(lines.cbegin(), lines.cend(), scenePos.y(), ChatLine::lessThanBSRectBottom);
@ -361,6 +363,7 @@ void ChatLog::reposition(int start, int end, qreal deltaY)
void ChatLog::insertChatlineAtBottom(ChatLine::Ptr l)
{
numRemove = 0;
if (!l.get())
return;
@ -382,8 +385,35 @@ void ChatLog::insertChatlineAtBottom(ChatLine::Ptr l)
updateTypingNotification();
}
void ChatLog::insertChatlineAtBottom(const QList<ChatLine::Ptr>& newLines)
{
numRemove = 0;
if (newLines.isEmpty())
return;
if (canRemove && lines.size() + DEF_NUM_MSG_TO_LOAD >= maxMessages) {
removeFirsts(DEF_NUM_MSG_TO_LOAD);
}
for (ChatLine::Ptr l : newLines) {
l->setRow(lines.size());
l->addToScene(scene);
l->visibilityChanged(false);
lines.append(l);
}
layout(lines.last()->getRow(), lines.size(), useableWidth());
if (visibleLines.size() > 1) {
startResizeWorker(visibleLines[1]);
} else {
startResizeWorker();
}
}
void ChatLog::insertChatlineOnTop(ChatLine::Ptr l)
{
numRemove = 0;
if (!l.get())
return;
@ -392,6 +422,7 @@ void ChatLog::insertChatlineOnTop(ChatLine::Ptr l)
void ChatLog::insertChatlinesOnTop(const QList<ChatLine::Ptr>& newLines)
{
numRemove = 0;
if (newLines.isEmpty())
return;
@ -411,6 +442,10 @@ void ChatLog::insertChatlinesOnTop(const QList<ChatLine::Ptr>& newLines)
combLines.push_back(l);
}
if (canRemove && lines.size() + DEF_NUM_MSG_TO_LOAD >= maxMessages) {
removeLasts(DEF_NUM_MSG_TO_LOAD);
}
// add the old lines
for (ChatLine::Ptr l : lines) {
l->setRow(i++);
@ -422,9 +457,14 @@ void ChatLog::insertChatlinesOnTop(const QList<ChatLine::Ptr>& newLines)
scene->setItemIndexMethod(oldIndexMeth);
// redo layout
if (visibleLines.size() > 1) {
startResizeWorker(visibleLines[1]);
} else {
startResizeWorker();
}
}
bool ChatLog::stickToBottom() const
{
return verticalScrollBar()->value() == verticalScrollBar()->maximum();
@ -436,18 +476,22 @@ void ChatLog::scrollToBottom()
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}
void ChatLog::startResizeWorker()
void ChatLog::startResizeWorker(ChatLine::Ptr anchorLine)
{
if (lines.empty())
if (lines.empty()) {
isScroll = true;
return;
}
// (re)start the worker
if (!workerTimer->isActive()) {
// these values must not be reevaluated while the worker is running
if (anchorLine) {
workerAnchorLine = anchorLine;
workerStb = false;
} else {
workerStb = stickToBottom();
if (!visibleLines.empty())
workerAnchorLine = visibleLines.first();
}
}
// switch to busy scene displaying the busy notification if there is a lot
@ -634,14 +678,20 @@ void ChatLog::scrollToLine(ChatLine::Ptr line)
if (!line.get())
return;
if (workerTimer->isActive()) {
workerAnchorLine = line;
workerStb = false;
} else {
updateSceneRect();
verticalScrollBar()->setValue(line->sceneBoundingRect().top());
verticalScrollBar()->setValue(line->sceneBoundingRect().top()); // NOTE: start here
}
}
void ChatLog::selectAll()
{
if (lines.empty())
if (lines.empty()) {
return;
}
clearSelection();
@ -672,15 +722,50 @@ void ChatLog::reloadTheme()
}
}
void ChatLog::removeFirsts(const int num)
{
if (lines.size() > num) {
lines.erase(lines.begin(), lines.begin()+num);
numRemove = num;
} else {
lines.clear();
}
for (int i = 0; i < lines.size(); ++i) {
lines[i]->setRow(i);
}
}
void ChatLog::removeLasts(const int num)
{
if (lines.size() > num) {
lines.erase(lines.end()-num, lines.end());
numRemove = num;
} else {
lines.clear();
}
}
void ChatLog::setScroll(const bool scroll)
{
isScroll = scroll;
}
int ChatLog::getNumRemove() const
{
return numRemove;
}
void ChatLog::forceRelayout()
{
startResizeWorker();
}
void ChatLog::checkVisibility()
void ChatLog::checkVisibility(bool causedWheelEvent)
{
if (lines.empty())
if (lines.empty()) {
return;
}
// find first visible line
auto lowerBound = std::lower_bound(lines.cbegin(), lines.cend(), getVisibleRect().top(),
@ -717,6 +802,14 @@ void ChatLog::checkVisibility()
if (!visibleLines.isEmpty()) {
emit firstVisibleLineChanged(visibleLines.at(0));
}
if (causedWheelEvent) {
if (lowerBound != lines.cend() && lowerBound->get()->row == 0) {
emit loadHistoryLower();
} else if (upperBound == lines.cend()) {
emit loadHistoryUpper();
}
}
}
void ChatLog::scrollContentsBy(int dx, int dy)
@ -767,8 +860,9 @@ void ChatLog::updateTypingNotification()
qreal posY = 0.0;
if (!lines.empty())
if (!lines.empty()) {
posY = lines.last()->sceneBoundingRect().bottom() + lineSpacing;
}
notification->layout(useableWidth(), QPointF(0.0, posY));
}
@ -853,6 +947,7 @@ void ChatLog::onWorkerTimeout()
// hidden during busy screen
verticalScrollBar()->show();
isScroll = true;
emit workerTimeoutFinished();
}
}
@ -918,6 +1013,16 @@ void ChatLog::focusOutEvent(QFocusEvent* ev)
}
}
void ChatLog::wheelEvent(QWheelEvent *event)
{
if (!isScroll) {
return;
}
QGraphicsView::wheelEvent(event);
checkVisibility(true);
}
void ChatLog::retranslateUi()
{
copyAction->setText(tr("Copy"));

View File

@ -35,14 +35,17 @@ class QTimer;
class ChatLineContent;
struct ToxFile;
static const auto DEF_NUM_MSG_TO_LOAD = 100;
class ChatLog : public QGraphicsView
{
Q_OBJECT
public:
explicit ChatLog(QWidget* parent = nullptr);
explicit ChatLog(const bool canRemove, QWidget* parent = nullptr);
virtual ~ChatLog();
void insertChatlineAtBottom(ChatLine::Ptr l);
void insertChatlineAtBottom(const QList<ChatLine::Ptr>& newLines);
void insertChatlineOnTop(ChatLine::Ptr l);
void insertChatlinesOnTop(const QList<ChatLine::Ptr>& newLines);
void clearSelection();
@ -55,6 +58,10 @@ public:
void selectAll();
void fontChanged(const QFont& font);
void reloadTheme();
void removeFirsts(const int num);
void removeLasts(const int num);
void setScroll(const bool scroll);
int getNumRemove() const;
QString getSelectedText() const;
@ -72,6 +79,8 @@ signals:
void selectionChanged();
void workerTimeoutFinished();
void firstVisibleLineChanged(const ChatLine::Ptr&);
void loadHistoryLower();
void loadHistoryUpper();
public slots:
void forceRelayout();
@ -94,9 +103,9 @@ protected:
void reposition(int start, int end, qreal deltaY);
void updateSceneRect();
void checkVisibility();
void checkVisibility(bool causedWheelEvent = false);
void scrollToBottom();
void startResizeWorker();
void startResizeWorker(ChatLine::Ptr anchorLine = nullptr);
virtual void mouseDoubleClickEvent(QMouseEvent* ev) final override;
virtual void mousePressEvent(QMouseEvent* ev) final override;
@ -107,6 +116,7 @@ protected:
virtual void showEvent(QShowEvent*) final override;
virtual void focusInEvent(QFocusEvent* ev) final override;
virtual void focusOutEvent(QFocusEvent* ev) final override;
virtual void wheelEvent(QWheelEvent *event) final override;
void updateMultiSelectionRect();
void updateTypingNotification();
@ -159,6 +169,7 @@ private:
int clickCount = 0;
QPoint lastClickPos;
Qt::MouseButton lastClickButton;
bool isScroll{true};
// worker vars
int workerLastIndex = 0;
@ -168,6 +179,10 @@ private:
// layout
QMargins margins = QMargins(10, 10, 10, 10);
qreal lineSpacing = 5.0f;
int numRemove{0};
const int maxMessages{300};
bool canRemove;
};
#endif // CHATLOG_H

View File

@ -66,9 +66,11 @@ void Text::selectText(const QString& txt, const std::pair<int, int>& point)
return;
}
auto cursor = doc->find(txt, point.first);
selectCursor = doc->find(txt, point.first);
selectPoint = point;
selectText(cursor, point);
regenerate();
update();
}
void Text::selectText(const QRegularExpression &exp, const std::pair<int, int>& point)
@ -79,14 +81,20 @@ void Text::selectText(const QRegularExpression &exp, const std::pair<int, int>&
return;
}
auto cursor = doc->find(exp, point.first);
selectCursor = doc->find(exp, point.first);
selectPoint = point;
selectText(cursor, point);
regenerate();
update();
}
void Text::deselectText()
{
dirty = true;
selectCursor = QTextCursor();
selectPoint = {0, 0};
regenerate();
update();
}
@ -355,6 +363,10 @@ void Text::regenerate()
dirty = false;
}
if (!selectCursor.isNull()) {
selectText(selectCursor, selectPoint);
}
// if we are not visible -> free mem
if (!keepInMemory)
freeResources();
@ -462,9 +474,6 @@ void Text::selectText(QTextCursor& cursor, const std::pair<int, int>& point)
QTextCharFormat format;
format.setBackground(QBrush(Style::getColor(Style::SearchHighlighted)));
cursor.mergeCharFormat(format);
regenerate();
update();
}
}

View File

@ -24,6 +24,7 @@
#include "src/widget/style.h"
#include <QFont>
#include <QTextCursor>
class QTextDocument;
@ -110,6 +111,9 @@ private:
TextType textType;
QColor color;
QColor customColor;
QTextCursor selectCursor;
std::pair<int, int> selectPoint{0, 0};
};
#endif // TEXT_H

View File

@ -220,6 +220,15 @@ std::vector<IChatLog::DateChatLogIdxPair> ChatHistory::getDateIdxs(const QDate&
}
}
std::size_t ChatHistory::size() const
{
if (canUseHistory()) {
return history->getNumMessagesForFriend(f.getPublicKey());
}
return sessionChatLog.size();
}
void ChatHistory::onFileUpdated(const ToxPk& sender, const ToxFile& file)
{
if (canUseHistory()) {

View File

@ -42,6 +42,7 @@ public:
ChatLogIdx getFirstIdx() const override;
ChatLogIdx getNextIdx() const override;
std::vector<DateChatLogIdxPair> getDateIdxs(const QDate& startDate, size_t maxDates) const override;
std::size_t size() const override;
public slots:
void onFileUpdated(const ToxPk& sender, const ToxFile& file);

View File

@ -37,6 +37,8 @@ public:
virtual void setEventFlag(bool flag) = 0;
virtual bool getEventFlag() const = 0;
virtual bool useHistory() const = 0; // TODO: remove after added history in group chat
signals:
void displayedNameChanged(const QString& newName);
};

View File

@ -166,3 +166,8 @@ bool Friend::isOnline() const
{
return friendStatus != Status::Status::Offline && friendStatus != Status::Status::Blocked;
}
bool Friend::useHistory() const
{
return true;
}

View File

@ -55,6 +55,8 @@ public:
Status::Status getStatus() const;
bool isOnline() const;
bool useHistory() const override final;
signals:
void nameChanged(const ToxPk& friendId, const QString& name);
void aliasChanged(const ToxPk& friendId, QString alias);

View File

@ -205,6 +205,11 @@ QString Group::getSelfName() const
return selfName;
}
bool Group::useHistory() const
{
return false;
}
void Group::stopAudioOfDepartedPeers(const ToxPk& peerPk)
{
if (avGroupchat) {

View File

@ -61,6 +61,8 @@ public:
void setSelfName(const QString& name);
QString getSelfName() const;
bool useHistory() const override final;
signals:
void titleChangedByUser(const QString& title);
void titleChanged(const QString& author, const QString& title);

View File

@ -138,6 +138,8 @@ public:
virtual std::vector<DateChatLogIdxPair> getDateIdxs(const QDate& startDate,
size_t maxDates) const = 0;
virtual std::size_t size() const = 0;
signals:
void itemUpdated(ChatLogIdx idx);
};

View File

@ -289,6 +289,11 @@ std::vector<IChatLog::DateChatLogIdxPair> SessionChatLog::getDateIdxs(const QDat
return ret;
}
std::size_t SessionChatLog::size() const
{
return items.size();
}
void SessionChatLog::insertMessageAtIdx(ChatLogIdx idx, ToxPk sender, QString senderName,
ChatLogMessage message)
{

View File

@ -45,6 +45,7 @@ public:
ChatLogIdx getFirstIdx() const override;
ChatLogIdx getNextIdx() const override;
std::vector<DateChatLogIdxPair> getDateIdxs(const QDate& startDate, size_t maxDates) const override;
std::size_t size() const override;
void insertMessageAtIdx(ChatLogIdx idx, ToxPk sender, QString senderName, ChatLogMessage message);
void insertFileAtIdx(ChatLogIdx idx, ToxPk sender, QString senderName, ChatLogFile file);

View File

@ -681,14 +681,14 @@ QDateTime History::getDateWhereFindPhrase(const QString& friendPk, const QDateTi
break;
}
QDateTime date = from;
QDateTime time = from;
if (!date.isValid()) {
date = QDateTime::currentDateTime();
if (!time.isValid()) {
time = QDateTime::currentDateTime();
}
if (parameter.period == PeriodSearch::AfterDate || parameter.period == PeriodSearch::BeforeDate) {
date = QDateTime(parameter.date);
time = parameter.time;
}
QString period;
@ -698,15 +698,15 @@ QDateTime History::getDateWhereFindPhrase(const QString& friendPk, const QDateTi
break;
case PeriodSearch::AfterDate:
period = QStringLiteral("AND timestamp > '%1' ORDER BY timestamp ASC LIMIT 1;")
.arg(date.toMSecsSinceEpoch());
.arg(time.toMSecsSinceEpoch());
break;
case PeriodSearch::BeforeDate:
period = QStringLiteral("AND timestamp < '%1' ORDER BY timestamp DESC LIMIT 1;")
.arg(date.toMSecsSinceEpoch());
.arg(time.toMSecsSinceEpoch());
break;
default:
period = QStringLiteral("AND timestamp < '%1' ORDER BY timestamp DESC LIMIT 1;")
.arg(date.toMSecsSinceEpoch());
.arg(time.toMSecsSinceEpoch());
break;
}

View File

@ -37,7 +37,6 @@
#include "src/widget/contentlayout.h"
#include "src/widget/emoticonswidget.h"
#include "src/widget/form/chatform.h"
#include "src/widget/form/loadhistorydialog.h"
#include "src/widget/maskablepixmapwidget.h"
#include "src/widget/searchform.h"
#include "src/widget/style.h"
@ -54,6 +53,8 @@
#include <QStringBuilder>
#include <QtGlobal>
#include <QDebug>
#ifdef SPELL_CHECKING
#include <KF5/SonnetUi/sonnet/spellcheckdecorator.h>
#endif
@ -260,7 +261,7 @@ GenericChatForm::GenericChatForm(const Contact* contact, IChatLog& chatLog,
headWidget = new ChatFormHeader();
searchForm = new SearchForm();
dateInfo = new QLabel(this);
chatWidget = new ChatLog(this);
chatWidget = new ChatLog(contact->useHistory(), this);
chatWidget->setBusyNotification(ChatMessage::createBusyNotification());
searchForm->hide();
dateInfo->setAlignment(Qt::AlignHCenter);
@ -329,6 +330,13 @@ GenericChatForm::GenericChatForm(const Contact* contact, IChatLog& chatLog,
quoteAction = menu.addAction(QIcon(), QString(), this, SLOT(quoteSelectedText()),
QKeySequence(Qt::ALT + Qt::Key_Q));
addAction(quoteAction);
menu.addSeparator();
goCurrentDateAction = menu.addAction(QIcon(), QString(), this, SLOT(goToCurrentDate()),
QKeySequence(Qt::CTRL + Qt::Key_G));
addAction(goCurrentDateAction);
menu.addSeparator();
searchAction = menu.addAction(QIcon(), QString(), this, SLOT(searchFormShow()),
@ -355,6 +363,8 @@ GenericChatForm::GenericChatForm(const Contact* contact, IChatLog& chatLog,
connect(chatWidget, &ChatLog::customContextMenuRequested, this,
&GenericChatForm::onChatContextMenuRequested);
connect(chatWidget, &ChatLog::firstVisibleLineChanged, this, &GenericChatForm::updateShowDateInfo);
connect(chatWidget, &ChatLog::loadHistoryLower, this, &GenericChatForm::loadHistoryLower);
connect(chatWidget, &ChatLog::loadHistoryUpper, this, &GenericChatForm::loadHistoryUpper);
connect(searchForm, &SearchForm::searchInBegin, this, &GenericChatForm::searchInBegin);
connect(searchForm, &SearchForm::searchUp, this, &GenericChatForm::onSearchUp);
@ -380,7 +390,7 @@ GenericChatForm::GenericChatForm(const Contact* contact, IChatLog& chatLog,
connect(contact, &Contact::displayedNameChanged, this, &GenericChatForm::setName);
auto chatLogIdxRange = chatLog.getNextIdx() - chatLog.getFirstIdx();
auto firstChatLogIdx = (chatLogIdxRange < 100) ? chatLog.getFirstIdx() : chatLog.getNextIdx() - 100;
auto firstChatLogIdx = (chatLogIdxRange < DEF_NUM_MSG_TO_LOAD) ? chatLog.getFirstIdx() : chatLog.getNextIdx() - DEF_NUM_MSG_TO_LOAD;
renderMessages(firstChatLogIdx, chatLog.getNextIdx());
@ -644,6 +654,79 @@ QDateTime GenericChatForm::getTime(const ChatLine::Ptr &chatLine) const
return QDateTime();
}
void GenericChatForm::loadHistory(const QDateTime &time, const LoadHistoryDialog::LoadType type)
{
chatWidget->clear();
messages.clear();
if (type == LoadHistoryDialog::from) {
loadHistoryFrom(time);
auto msg = messages.cbegin()->second;
chatWidget->setScroll(true);
chatWidget->scrollToLine(msg);
} else {
loadHistoryTo(time);
}
}
void GenericChatForm::loadHistoryTo(const QDateTime &time)
{
chatWidget->setScroll(false);
auto end = ChatLogIdx(0);
if (time.isNull()) {
end = messages.begin()->first;
} else {
end = firstItemAfterDate(time.date(), chatLog);
}
auto begin = ChatLogIdx(0);
if (end.get() > DEF_NUM_MSG_TO_LOAD) {
begin = ChatLogIdx(end.get() - DEF_NUM_MSG_TO_LOAD);
}
if (begin != end) {
renderMessages(begin, end);
} else {
chatWidget->setScroll(true);
}
}
void GenericChatForm::loadHistoryFrom(const QDateTime &time)
{
chatWidget->setScroll(false);
auto begin = ChatLogIdx(0);
if (time.isNull()) {
begin = messages.rbegin()->first;
} else {
begin = firstItemAfterDate(time.date(), chatLog);
}
int add = DEF_NUM_MSG_TO_LOAD;
if (begin.get() + DEF_NUM_MSG_TO_LOAD > chatLog.getNextIdx().get()) {
add = chatLog.getNextIdx().get() - begin.get();
}
auto end = ChatLogIdx(begin.get() + add);
renderMessages(begin, end);
}
void GenericChatForm::removeFirstsMessages(const int num)
{
if (messages.size() > num) {
messages.erase(messages.begin(), std::next(messages.begin(), num));
} else {
messages.clear();
}
}
void GenericChatForm::removeLastsMessages(const int num)
{
if (messages.size() > num) {
messages.erase(std::next(messages.end(), -num), messages.end());
} else {
messages.clear();
}
}
void GenericChatForm::disableSearchText()
{
@ -810,8 +893,9 @@ void GenericChatForm::onLoadHistory()
LoadHistoryDialog dlg(&chatLog);
if (dlg.exec()) {
QDateTime time = dlg.getFromDate();
auto idx = firstItemAfterDate(dlg.getFromDate().date(), chatLog);
renderMessages(idx, chatLog.getNextIdx());
auto type = dlg.getLoadType();
loadHistory(time, type);
}
}
@ -858,6 +942,12 @@ void GenericChatForm::searchInBegin(const QString& phrase, const ParameterSearch
{
disableSearchText();
if (!parameter.time.isNull()) {
LoadHistoryDialog::LoadType type = (parameter.period == PeriodSearch::BeforeDate)
? LoadHistoryDialog::to : LoadHistoryDialog::from;
loadHistory(parameter.time, type);
}
bool bForwardSearch = false;
switch (parameter.period) {
case PeriodSearch::WithTheFirst: {
@ -875,13 +965,13 @@ void GenericChatForm::searchInBegin(const QString& phrase, const ParameterSearch
}
case PeriodSearch::AfterDate: {
bForwardSearch = true;
searchPos.logIdx = firstItemAfterDate(parameter.date, chatLog);
searchPos.logIdx = firstItemAfterDate(parameter.time.date(), chatLog);
searchPos.numMatches = 0;
break;
}
case PeriodSearch::BeforeDate: {
bForwardSearch = false;
searchPos.logIdx = firstItemAfterDate(parameter.date, chatLog);
searchPos.logIdx = firstItemAfterDate(parameter.time.date(), chatLog);
searchPos.numMatches = 0;
break;
}
@ -939,6 +1029,11 @@ void GenericChatForm::renderMessages(ChatLogIdx begin, ChatLogIdx end,
QList<ChatLine::Ptr> beforeLines;
QList<ChatLine::Ptr> afterLines;
if (!messages.empty() && !(messages.rbegin()->first == begin || messages.begin()->first == end
|| messages.rbegin()->first.get() + 1 == begin.get())) {
return;
}
for (auto i = begin; i < end; ++i) {
auto chatMessage = getChatMessageForIdx(i, messages);
renderItem(chatLog.at(i), needsToHideName(i), colorizeNames, chatMessage);
@ -956,8 +1051,13 @@ void GenericChatForm::renderMessages(ChatLogIdx begin, ChatLogIdx end,
}
}
for (auto const& line : afterLines) {
chatWidget->insertChatlineAtBottom(line);
if (beforeLines.isEmpty() && afterLines.isEmpty()) {
chatWidget->setScroll(true);
}
chatWidget->insertChatlineAtBottom(afterLines);
if (chatWidget->getNumRemove()) {
removeFirstsMessages(chatWidget->getNumRemove());
}
if (!beforeLines.empty()) {
@ -974,11 +1074,36 @@ void GenericChatForm::renderMessages(ChatLogIdx begin, ChatLogIdx end,
}
chatWidget->insertChatlinesOnTop(beforeLines);
if (chatWidget->getNumRemove()) {
removeLastsMessages(chatWidget->getNumRemove());
}
} else if (onCompletion) {
onCompletion();
}
}
void GenericChatForm::goToCurrentDate()
{
chatWidget->clear();
messages.clear();
auto end = ChatLogIdx(chatLog.size());
auto begin = end.get() > DEF_NUM_MSG_TO_LOAD ? ChatLogIdx(end.get() - DEF_NUM_MSG_TO_LOAD) : ChatLogIdx(0);
renderMessages(begin, end);
}
void GenericChatForm::loadHistoryLower()
{
loadHistoryTo(QDateTime());
}
void GenericChatForm::loadHistoryUpper()
{
auto msg = messages.crbegin()->second;
loadHistoryFrom(QDateTime());
chatWidget->scrollToLine(msg);
}
void GenericChatForm::updateShowDateInfo(const ChatLine::Ptr& line)
{
const auto date = getTime(line);
@ -1002,6 +1127,7 @@ void GenericChatForm::retranslateUi()
quoteAction->setText(tr("Quote selected text"));
copyLinkAction->setText(tr("Copy link address"));
searchAction->setText(tr("Search in text"));
goCurrentDateAction->setText(tr("Go to current date"));
loadHistoryAction->setText(tr("Load chat history..."));
exportChatAction->setText(tr("Export to file"));
}

View File

@ -23,6 +23,7 @@
#include "src/chatlog/chatmessage.h"
#include "src/core/toxpk.h"
#include "src/model/ichatlog.h"
#include "src/widget/form/loadhistorydialog.h"
#include "src/widget/searchtypes.h"
#include <QMenu>
@ -125,11 +126,20 @@ protected slots:
void renderMessage(ChatLogIdx idx);
void renderMessages(ChatLogIdx begin, ChatLogIdx end,
std::function<void(void)> onCompletion = std::function<void(void)>());
void goToCurrentDate();
void loadHistoryLower();
void loadHistoryUpper();
private:
void retranslateUi();
void addSystemDateMessage(const QDate& date);
QDateTime getTime(const ChatLine::Ptr& chatLine) const;
void loadHistory(const QDateTime& time, const LoadHistoryDialog::LoadType type);
void loadHistoryTo(const QDateTime& time);
void loadHistoryFrom(const QDateTime& time);
void removeFirstsMessages(const int num);
void removeLastsMessages(const int num);
protected:
ChatMessage::Ptr createMessage(const ToxPk& author, const QString& message,
@ -160,11 +170,10 @@ protected:
QAction* searchAction;
QAction* loadHistoryAction;
QAction* exportChatAction;
QAction* goCurrentDateAction;
ToxPk previousId;
QDateTime earliestMessage;
QMenu menu;
QPushButton* emoteButton;

View File

@ -38,11 +38,15 @@ LoadHistoryDialog::LoadHistoryDialog(const IChatLog* chatLog, QWidget* parent)
&LoadHistoryDialog::highlightDates);
}
LoadHistoryDialog::LoadHistoryDialog(QWidget* parent)
LoadHistoryDialog::LoadHistoryDialog(Mode mode, QWidget* parent)
: QDialog(parent)
, ui(new Ui::LoadHistoryDialog)
{
ui->setupUi(this);
if (mode == Mode::search) {
enableSearchMode();
}
}
LoadHistoryDialog::~LoadHistoryDialog()
@ -62,14 +66,21 @@ QDateTime LoadHistoryDialog::getFromDate()
return res;
}
void LoadHistoryDialog::setTitle(const QString& title)
LoadHistoryDialog::LoadType LoadHistoryDialog::getLoadType()
{
setWindowTitle(title);
if (ui->loadTypeComboBox->currentIndex() == 0) {
return LoadType::from;
}
void LoadHistoryDialog::setInfoLabel(const QString& info)
return LoadType::to;
}
void LoadHistoryDialog::enableSearchMode()
{
ui->fromLabel->setText(info);
setWindowTitle(tr("Select Date Dialog"));
ui->fromLabel->setText(tr("Select a date"));
ui->loadTypeComboBox->setVisible(false);
ui->infoLabel->setVisible(false);
}
void LoadHistoryDialog::highlightDates(int year, int month)

View File

@ -34,18 +34,29 @@ class LoadHistoryDialog : public QDialog
Q_OBJECT
public:
enum LoadType {
from,
to
};
enum Mode {
common,
search
};
explicit LoadHistoryDialog(const IChatLog* chatLog, QWidget* parent = nullptr);
explicit LoadHistoryDialog(QWidget* parent = nullptr);
explicit LoadHistoryDialog(Mode mode, QWidget* parent = nullptr);
~LoadHistoryDialog();
QDateTime getFromDate();
void setTitle(const QString& title);
void setInfoLabel(const QString& info);
LoadType getLoadType();
public slots:
void highlightDates(int year, int month);
private:
void enableSearchMode();
Ui::LoadHistoryDialog* ui;
const IChatLog* chatLog;
};

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>347</width>
<height>264</height>
<width>410</width>
<height>332</height>
</rect>
</property>
<property name="windowTitle">
@ -16,22 +16,60 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="fromLabel">
<property name="text">
<string>Load history from:</string>
<string>Load history</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="loadTypeComboBox">
<item>
<property name="text">
<string>from</string>
</property>
</item>
<item>
<property name="text">
<string>to</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="infoLabel">
<property name="text">
<string>(about 100 messages are loaded)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>17</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QCalendarWidget" name="fromDate">
<property name="gridVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>

View File

@ -86,7 +86,7 @@ ParameterSearch SearchSettingsForm::getParameterSearch()
break;
}
ps.date = startDate;
ps.time = startTime;
ps.isUpdate = isUpdate;
isUpdate = false;
@ -101,7 +101,7 @@ void SearchSettingsForm::reloadTheme()
void SearchSettingsForm::updateStartDateLabel()
{
ui->startDateLabel->setText(startDate.toString(Settings::getInstance().getDateFormat()));
ui->startDateLabel->setText(startTime.toString(Settings::getInstance().getDateFormat()));
}
void SearchSettingsForm::setUpdate(const bool isUpdate)
@ -119,8 +119,8 @@ void SearchSettingsForm::onStartSearchSelected(const int index)
ui->choiceDateButton->setProperty("state", QStringLiteral("green"));
ui->choiceDateButton->setStyleSheet(Style::getStylesheet(QStringLiteral("chatForm/buttons.css")));
if (startDate.isNull()) {
startDate = QDate::currentDate();
if (startTime.isNull()) {
startTime = QDateTime::currentDateTime();
updateStartDateLabel();
}
@ -161,11 +161,9 @@ void SearchSettingsForm::onRegularClicked(const bool checked)
void SearchSettingsForm::onChoiceDate()
{
LoadHistoryDialog dlg;
dlg.setTitle(tr("Select Date Dialog"));
dlg.setInfoLabel(tr("Select a date"));
LoadHistoryDialog dlg(LoadHistoryDialog::search);
if (dlg.exec()) {
startDate = dlg.getFromDate().date();
startTime = dlg.getFromDate();
updateStartDateLabel();
}

View File

@ -40,7 +40,7 @@ public:
private:
Ui::SearchSettingsForm *ui;
QDate startDate;
QDateTime startTime;
bool isUpdate{false};
void updateStartDateLabel();

View File

@ -48,13 +48,13 @@ enum class SearchDirection {
struct ParameterSearch {
FilterSearch filter{FilterSearch::None};
PeriodSearch period{PeriodSearch::None};
QDate date;
QDateTime time;
bool isUpdate{false};
bool operator ==(const ParameterSearch& other) {
return filter == other.filter &&
period == other.period &&
date == other.date;
time == other.time;
}
bool operator !=(const ParameterSearch& other) {