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:
commit
be55759555
|
@ -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"));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -166,3 +166,8 @@ bool Friend::isOnline() const
|
|||
{
|
||||
return friendStatus != Status::Status::Offline && friendStatus != Status::Status::Blocked;
|
||||
}
|
||||
|
||||
bool Friend::useHistory() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -205,6 +205,11 @@ QString Group::getSelfName() const
|
|||
return selfName;
|
||||
}
|
||||
|
||||
bool Group::useHistory() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Group::stopAudioOfDepartedPeers(const ToxPk& peerPk)
|
||||
{
|
||||
if (avGroupchat) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
|
||||
private:
|
||||
Ui::SearchSettingsForm *ui;
|
||||
QDate startDate;
|
||||
QDateTime startTime;
|
||||
bool isUpdate{false};
|
||||
|
||||
void updateStartDateLabel();
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user