From b9f5588c1f97ebf710a498d68f11be049059d4e6 Mon Sep 17 00:00:00 2001 From: Sergei Ilinykh Date: Thu, 4 Jul 2024 02:05:21 +0300 Subject: [PATCH] Allow any message editing --- src/chatdlg.cpp | 2 +- src/chatview_te.h | 1 + src/chatview_webkit.cpp | 5 ++++ src/chatview_webkit.h | 1 + src/groupchatdlg.cpp | 3 +- src/msgmle.cpp | 25 +++++++++------- src/msgmle.h | 41 ++++++++++++++------------- src/psichatdlg.cpp | 1 + themes/chatview/psi/bubble/index.html | 16 ++++++----- themes/chatview/util.js | 6 +++- 10 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/chatdlg.cpp b/src/chatdlg.cpp index a3add68d66..b1a0d18865 100644 --- a/src/chatdlg.cpp +++ b/src/chatdlg.cpp @@ -700,7 +700,7 @@ void ChatDlg::doSend() QString id = account()->client()->genUniqueId(); m.setId(id); // we need id early for message manipulations in chatview if (chatEdit()->isCorrection()) { - m.setReplaceId(chatEdit()->lastMessageId()); + m.setReplaceId(chatEdit()->correctionId()); } chatEdit()->setLastMessageId(id); chatEdit()->resetCorrection(); diff --git a/src/chatview_te.h b/src/chatview_te.h index 440af9c76f..5d9803de69 100644 --- a/src/chatview_te.h +++ b/src/chatview_te.h @@ -103,6 +103,7 @@ private slots: void nickInsertClick(const QString &nick); void outgoingReactions(const QString &messageId, const QSet &reactions); void outgoingMessageRetraction(const QString &messageId); + void editMessageRequested(const QString &messageId, const QString &text); private: bool isMuc_; diff --git a/src/chatview_webkit.cpp b/src/chatview_webkit.cpp index 18b0172f72..63e86c3252 100644 --- a/src/chatview_webkit.cpp +++ b/src/chatview_webkit.cpp @@ -365,6 +365,11 @@ class ChatViewJSObject : public ChatViewThemeSession { Q_INVOKABLE void kick(const QString &nick) { qDebug() << "kick" << nick; } + Q_INVOKABLE void editMessage(const QString &messageId, const QString &messageHtml) + { + emit _view->editMessageRequested(messageId, TextUtil::rich2plain(messageHtml)); + } + private slots: void onUrlHeadersReady() { diff --git a/src/chatview_webkit.h b/src/chatview_webkit.h index 3b5cd587ad..0d2de369f8 100644 --- a/src/chatview_webkit.h +++ b/src/chatview_webkit.h @@ -99,6 +99,7 @@ private slots: void quote(const QString &text); void outgoingReactions(const QString &messageId, const QSet &reactions); void outgoingMessageRetraction(const QString &messageId); + void editMessageRequested(const QString &messageId, const QString &text); private: friend class ChatViewPrivate; diff --git a/src/groupchatdlg.cpp b/src/groupchatdlg.cpp index 6e7a894e8d..3f1aee23b5 100644 --- a/src/groupchatdlg.cpp +++ b/src/groupchatdlg.cpp @@ -1109,6 +1109,7 @@ GCMainDlg::GCMainDlg(PsiAccount *pa, const Jid &j, TabManager *tabManager) : Tab setConnecting(); connect(ui_.log, &ChatView::quote, ui_.mle->chatEdit(), &ChatEdit::insertAsQuote); + connect(ui_.log, &ChatView::editMessageRequested, ui_.mle->chatEdit(), &ChatEdit::startCorrection); connect(pa->avatarFactory(), &AvatarFactory::avatarChanged, this, &GCMainDlg::avatarUpdated); #ifdef PSI_PLUGINS @@ -1489,7 +1490,7 @@ void GCMainDlg::mle_returnPressed() QString id = account()->client()->genUniqueId(); m.setId(id); // we need id early for message manipulations in chatview if (ui_.mle->chatEdit()->isCorrection()) { - m.setReplaceId(ui_.mle->chatEdit()->lastMessageId()); + m.setReplaceId(ui_.mle->chatEdit()->correctionId()); } ui_.mle->chatEdit()->setLastMessageId(id); ui_.mle->chatEdit()->resetCorrection(); diff --git a/src/msgmle.cpp b/src/msgmle.cpp index 21d5e39c1e..6f0e0f1c10 100644 --- a/src/msgmle.cpp +++ b/src/msgmle.cpp @@ -280,7 +280,7 @@ bool ChatEdit::event(QEvent *event) if (event->type() == QEvent::ShortcutOverride) { return false; } - if (event->type() == QEvent::PaletteChange && recButton_ && !correction) { + if (event->type() == QEvent::PaletteChange && recButton_ && _correctionId.isEmpty()) { setRecButtonIcon(); } return QTextEdit::event(event); @@ -433,7 +433,7 @@ void ChatEdit::optionsChanged(const QString &option) void ChatEdit::showHistoryMessageNext() { - correction = false; + _correctionId.clear(); if (!typedMsgsHistory.isEmpty()) { if (typedMsgsIndex + 1 < typedMsgsHistory.size()) { ++typedMsgsIndex; @@ -463,14 +463,14 @@ void ChatEdit::pasteAsQuote() void ChatEdit::showHistoryMessagePrev() { - if (!typedMsgsHistory.isEmpty() && (typedMsgsIndex > 0 || correction)) { + if (!typedMsgsHistory.isEmpty() && (typedMsgsIndex > 0 || !_correctionId.isEmpty())) { // Save current typed text if (typedMsgsIndex == typedMsgsHistory.size()) { - currentText = toPlainText(); - correction = true; + currentText = toPlainText(); + _correctionId = lastId; } - if (typedMsgsIndex == typedMsgsHistory.size() - 1 && correction) { - correction = false; + if (typedMsgsIndex == typedMsgsHistory.size() - 1 && !_correctionId.isEmpty()) { + _correctionId.clear(); ++typedMsgsIndex; } --typedMsgsIndex; @@ -480,7 +480,7 @@ void ChatEdit::showHistoryMessagePrev() void ChatEdit::showHistoryMessageFirst() { - correction = false; + _correctionId.clear(); if (!typedMsgsHistory.isEmpty()) { if (currentText.isEmpty()) { typedMsgsIndex = typedMsgsHistory.size() - 1; @@ -496,7 +496,7 @@ void ChatEdit::showHistoryMessageFirst() void ChatEdit::showHistoryMessageLast() { - correction = false; + _correctionId.clear(); if (!typedMsgsHistory.isEmpty()) { typedMsgsIndex = 0; showMessageHistory(); @@ -553,7 +553,6 @@ bool ChatEdit::canInsertFromMimeData(const QMimeData *source) const void ChatEdit::updateBackground() { - setProperty("correction", correction); style()->unpolish(this); style()->polish(this); update(); @@ -647,6 +646,12 @@ void ChatEdit::insertAsQuote(const QString &text) setFocus(Qt::OtherFocusReason); } +void ChatEdit::startCorrection(const QString messageId, const QString &text) +{ + _correctionId = messageId; + setEditText(text); +} + void ChatEdit::addSoundRecButton() { if (!recButton_) { diff --git a/src/msgmle.h b/src/msgmle.h index adfd19d9cd..9abe573afd 100644 --- a/src/msgmle.h +++ b/src/msgmle.h @@ -60,12 +60,13 @@ class ChatEdit : public QTextEdit { static bool checkSpellingGloballyEnabled(); void setCheckSpelling(bool); XMPP::HTMLElement toHTMLElement(); - bool isCorrection() { return correction; } + bool isCorrection() { return !_correctionId.isEmpty(); } void setLastMessageId(const QString &id) { lastId = id; } + const QString &correctionId() const { return _correctionId; } const QString &lastMessageId() { return lastId; } void resetCorrection() { - correction = false; + _correctionId.clear(); updateBackground(); } CapitalLettersController *capitalizer(); @@ -80,6 +81,7 @@ public slots: void doHTMLTextMenu(); void setCssString(const QString &css); void insertAsQuote(const QString &text); + void startCorrection(const QString messageId, const QString &text); protected slots: void applySuggestion(); @@ -114,24 +116,25 @@ protected slots: void setRecButtonIcon(); private: - QWidget *dialog_ = nullptr; - bool check_spelling_ = false; - SpellHighlighter *spellhighlighter_ = nullptr; - QPoint last_click_; - int previous_position_ = 0; - QStringList typedMsgsHistory; - int typedMsgsIndex = 0; - QAction *act_showMessagePrev = nullptr; - QAction *act_showMessageNext = nullptr; - QAction *act_showMessageFirst = nullptr; - QAction *act_showMessageLast = nullptr; - QAction *act_changeCase = nullptr; - QAction *actPasteAsQuote_ = nullptr; - QString currentText; - HTMLTextController *controller_ = nullptr; - CapitalLettersController *capitalizer_ = nullptr; - bool correction = false; + QWidget *dialog_ = nullptr; + bool check_spelling_ = false; + SpellHighlighter *spellhighlighter_ = nullptr; + QPoint last_click_; + int previous_position_ = 0; + QStringList typedMsgsHistory; + int typedMsgsIndex = 0; + QAction *act_showMessagePrev = nullptr; + QAction *act_showMessageNext = nullptr; + QAction *act_showMessageFirst = nullptr; + QAction *act_showMessageLast = nullptr; + QAction *act_changeCase = nullptr; + QAction *actPasteAsQuote_ = nullptr; + QString currentText; + HTMLTextController *controller_ = nullptr; + CapitalLettersController *capitalizer_ = nullptr; + // bool correction = false; QString lastId; + QString _correctionId; QPointer layout_; QPointer recButton_; QPointer overlay_; diff --git a/src/psichatdlg.cpp b/src/psichatdlg.cpp index e76f2f9d53..9fd6cb560b 100644 --- a/src/psichatdlg.cpp +++ b/src/psichatdlg.cpp @@ -317,6 +317,7 @@ void PsiChatDlg::initUi() addAction(act_mini_cmd_); connect(ui_.log, &ChatView::quote, ui_.mle->chatEdit(), &ChatEdit::insertAsQuote); + connect(ui_.log, &ChatView::editMessageRequested, ui_.mle->chatEdit(), &ChatEdit::startCorrection); act_pastesend_ = new IconAction(tr("Paste and Send"), "psi/action_paste_and_send", tr("Paste and Send"), 0, this); connect(act_pastesend_, SIGNAL(triggered()), SLOT(doPasteAndSend())); diff --git a/themes/chatview/psi/bubble/index.html b/themes/chatview/psi/bubble/index.html index 847a367208..9497dc71f8 100644 --- a/themes/chatview/psi/bubble/index.html +++ b/themes/chatview/psi/bubble/index.html @@ -142,12 +142,14 @@ msgNode = event.target; while (msgNode) { const mcl = msgNode.classList; - hasGrNext = mcl.contains("grnext"); - if (mcl.contains("avatar")) { - return []; // ignore avatar completely - } - if (msgNode.classList && (hasGrNext || mcl.contains("msg"))) { - break; + if (mcl) { + hasGrNext = mcl.contains("grnext"); + if (mcl.contains("avatar")) { + return []; // ignore avatar completely + } + if (hasGrNext || mcl.contains("msg")) { + break; + } } msgNode = msgNode.parentNode; } @@ -177,7 +179,7 @@ { text: "Reply", action: ()=>{ onReplyClicked(msgNode); } }, { text: "Copy", action: ()=>{ shared.session.copyMessage(msgNode.id); } }, { text: "Forward", action: ()=>{ shared.session.forwardMessage(msgNode.id); } }, - { text: "Edit", action: ()=>{ shared.session.editMessage(msgNode.id); } }, + { text: "Edit", action: ()=>{ shared.session.editMessage(msgNode.id, msgNode.getElementsByClassName("msgtext").item(0).innerHTML); } }, { text: "Delete", action: ()=>{ shared.session.deleteMessage(msgNode.id); } } ] } diff --git a/themes/chatview/util.js b/themes/chatview/util.js index 9da4047764..e694fedd86 100644 --- a/themes/chatview/util.js +++ b/themes/chatview/util.js @@ -642,9 +642,13 @@ function initPsiTheme() { return false; }, - createHtmlNode : function(html, context) { + createHtmlNode : function(html, context, stripIndents=true) { var range = document.createRange(); range.selectNode(context || document.body); + if (stripIndents) { + var re = new RegExp("^ +", "gm"); + html = html.replace(re, ""); + } return range.createContextualFragment(html); },