Messagelib

richtextcomposersignatures.cpp
1 /*
2  SPDX-FileCopyrightText: 2015-2021 Laurent Montel <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "richtextcomposersignatures.h"
8 #include "richtextcomposerng.h"
9 
10 #include "messagecomposer_debug.h"
11 #include <KIdentityManagement/Signature>
12 #include <QTextBlock>
13 using namespace MessageComposer;
14 
15 class RichTextComposerSignatures::RichTextComposerSignaturesPrivate
16 {
17 public:
18  RichTextComposerSignaturesPrivate(RichTextComposerNg *composer)
19  : richTextComposer(composer)
20  {
21  }
22 
23  void cleanWhitespaceHelper(const QRegExp &regExp, const QString &newText, const KIdentityManagement::Signature &sig);
24  Q_REQUIRED_RESULT QVector<QPair<int, int>> signaturePositions(const KIdentityManagement::Signature &sig) const;
25  RichTextComposerNg *const richTextComposer;
26 };
27 
28 RichTextComposerSignatures::RichTextComposerSignatures(MessageComposer::RichTextComposerNg *composer, QObject *parent)
29  : QObject(parent)
30  , d(new RichTextComposerSignaturesPrivate(composer))
31 {
32 }
33 
34 RichTextComposerSignatures::~RichTextComposerSignatures() = default;
35 
36 void RichTextComposerSignatures::RichTextComposerSignaturesPrivate::cleanWhitespaceHelper(const QRegExp &regExp,
37  const QString &newText,
39 {
40  int currentSearchPosition = 0;
41 
42  for (;;) {
43  // Find the text
44  const QString text = richTextComposer->document()->toPlainText();
45  const int currentMatch = regExp.indexIn(text, currentSearchPosition);
46  currentSearchPosition = currentMatch;
47  if (currentMatch == -1) {
48  break;
49  }
50 
51  // Select the text
52  QTextCursor cursor(richTextComposer->document());
53  cursor.setPosition(currentMatch);
55  // Skip quoted text
56  if (richTextComposer->isLineQuoted(cursor.block().text())) {
57  currentSearchPosition += regExp.matchedLength();
58  continue;
59  }
60  // Skip text inside signatures
61  bool insideSignature = false;
62  const QVector<QPair<int, int>> sigPositions = signaturePositions(sig);
63  for (const QPair<int, int> &position : sigPositions) {
64  if (cursor.position() >= position.first && cursor.position() <= position.second) {
65  insideSignature = true;
66  }
67  }
68  if (insideSignature) {
69  currentSearchPosition += regExp.matchedLength();
70  continue;
71  }
72 
73  // Replace the text
74  cursor.removeSelectedText();
75  cursor.insertText(newText);
76  currentSearchPosition += newText.length();
77  }
78 }
79 
80 void RichTextComposerSignatures::cleanWhitespace(const KIdentityManagement::Signature &sig)
81 {
82  QTextCursor cursor(d->richTextComposer->document());
83  cursor.beginEditBlock();
84 
85  // Squeeze tabs and spaces
86  d->cleanWhitespaceHelper(QRegExp(QLatin1String("[\t ]+")), QStringLiteral(" "), sig);
87 
88  // Remove trailing whitespace
89  d->cleanWhitespaceHelper(QRegExp(QLatin1String("[\t ][\n]")), QStringLiteral("\n"), sig);
90 
91  // Single space lines
92  d->cleanWhitespaceHelper(QRegExp(QLatin1String("[\n]{3,}")), QStringLiteral("\n\n"), sig);
93 
94  if (!d->richTextComposer->textCursor().hasSelection()) {
95  d->richTextComposer->textCursor().clearSelection();
96  }
97 
98  cursor.endEditBlock();
99 }
100 
101 QVector<QPair<int, int>> RichTextComposerSignatures::RichTextComposerSignaturesPrivate::signaturePositions(const KIdentityManagement::Signature &sig) const
102 {
103  QVector<QPair<int, int>> signaturePositions;
104  if (!sig.rawText().isEmpty()) {
105  QString sigText = sig.toPlainText();
106 
107  int currentSearchPosition = 0;
108  for (;;) {
109  // Find the next occurrence of the signature text
110  const QString text = richTextComposer->document()->toPlainText();
111  const int currentMatch = text.indexOf(sigText, currentSearchPosition);
112  currentSearchPosition = currentMatch + sigText.length();
113  if (currentMatch == -1) {
114  break;
115  }
116 
117  signaturePositions.append(QPair<int, int>(currentMatch, currentMatch + sigText.length()));
118  }
119  }
120  return signaturePositions;
121 }
122 
123 bool RichTextComposerSignatures::replaceSignature(const KIdentityManagement::Signature &oldSig, const KIdentityManagement::Signature &newSig)
124 {
125  bool found = false;
126  if (oldSig == newSig) {
127  return false;
128  }
129  QString oldSigText = oldSig.toPlainText();
130  if (oldSigText.isEmpty()) {
131  return false;
132  }
133  QTextCursor cursor(d->richTextComposer->document());
134  cursor.beginEditBlock();
135  int currentSearchPosition = 0;
136  for (;;) {
137  // Find the next occurrence of the signature text
138  const QString text = d->richTextComposer->document()->toPlainText();
139  const int currentMatch = text.indexOf(oldSigText, currentSearchPosition);
140  currentSearchPosition = currentMatch;
141  if (currentMatch == -1) {
142  break;
143  }
144 
145  // Select the signature
146  cursor.setPosition(currentMatch);
147 
148  // If the new signature is completely empty, we also want to remove the
149  // signature separator, so include it in the selection
150  int additionalMove = 0;
151  if (newSig.rawText().isEmpty() && text.mid(currentMatch - 4, 4) == QLatin1String("-- \n")) {
153  additionalMove = 4;
154  } else if (newSig.rawText().isEmpty() && text.mid(currentMatch - 1, 1) == QLatin1Char('\n')) {
156  additionalMove = 1;
157  }
158  cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, oldSigText.length() + additionalMove);
159  // Skip quoted signatures
160  if (d->richTextComposer->isLineQuoted(cursor.block().text())) {
161  currentSearchPosition += oldSig.toPlainText().length();
162  continue;
163  }
164  // Remove the old and insert the new signature
165  cursor.removeSelectedText();
166  d->richTextComposer->setTextCursor(cursor);
167  d->richTextComposer->insertSignature(newSig, KIdentityManagement::Signature::AtCursor, KIdentityManagement::Signature::AddNothing);
168  found = true;
169 
170  currentSearchPosition += newSig.toPlainText().length();
171  }
172 
173  cursor.endEditBlock();
174  return found;
175 }
The RichTextComposerNg class.
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QCursor cursor() const const
void append(const T &value)
QString rawText(bool *ok=nullptr) const
int matchedLength() const const
int indexIn(const QString &str, int offset, QRegExp::CaretMode caretMode) const const
bool isEmpty() const const
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
QString mid(int position, int n) const const
int length() const const
QObject * parent() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Dec 4 2021 23:12:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.