20#include "TextEditChecker_p.hpp"
21#include "UndoRedoStack.hpp"
24#include <QPlainTextEdit>
30TextEditCheckerPrivate::TextEditCheckerPrivate()
35TextEditCheckerPrivate::~TextEditCheckerPrivate()
41QString TextCursor::nextChar(
int num)
const
45 testCursor.movePosition(NextCharacter, MoveAnchor, num - 1);
47 testCursor.setPosition(testCursor.position());
48 testCursor.movePosition(NextCharacter, KeepAnchor);
49 return testCursor.selectedText();
52QString TextCursor::prevChar(
int num)
const
56 testCursor.movePosition(PreviousCharacter, MoveAnchor, num - 1);
58 testCursor.setPosition(testCursor.position());
59 testCursor.movePosition(PreviousCharacter, KeepAnchor);
60 return testCursor.selectedText();
63void TextCursor::moveWordStart(MoveMode moveMode)
65 movePosition(StartOfWord, moveMode);
66 qDebug() <<
"Start: " << position() <<
": " << prevChar(2) << prevChar() <<
"|" << nextChar();
68 if(nextChar() ==
"'"){
70 if(prevChar().contains(m_wordRegEx)){
71 movePosition(WordLeft, moveMode);
73 movePosition(NextCharacter, moveMode);
77 else if(prevChar() ==
"'" && prevChar(2).contains(m_wordRegEx)){
78 movePosition(WordLeft, moveMode, 2);
82void TextCursor::moveWordEnd(MoveMode moveMode)
84 movePosition(EndOfWord, moveMode);
85 qDebug() <<
"End: " << position() <<
": " << prevChar() <<
" | " << nextChar() <<
"|" << nextChar(2);
87 if(prevChar() ==
"'"){
89 if(nextChar().contains(m_wordRegEx)){
90 movePosition(WordRight, moveMode);
92 movePosition(PreviousCharacter, moveMode);
96 else if(nextChar() ==
"'" && nextChar(2).contains(m_wordRegEx)){
97 movePosition(WordRight, moveMode, 2);
103TextEditChecker::TextEditChecker(QObject* parent)
104 :
Checker(*new TextEditCheckerPrivate(), parent)
111 d->setTextEdit(
nullptr);
117 d->setTextEdit(textEdit ?
new TextEditProxyT<QTextEdit>(textEdit) :
nullptr);
123 d->setTextEdit(textEdit ?
new TextEditProxyT<QPlainTextEdit>(textEdit) :
nullptr);
126void TextEditCheckerPrivate::setTextEdit(TextEditProxy *newTextEdit)
130 QObject::disconnect(textEdit, &TextEditProxy::editDestroyed, q, &TextEditChecker::slotDetachTextEdit);
131 QObject::disconnect(textEdit, &TextEditProxy::textChanged, q, &TextEditChecker::slotCheckDocumentChanged);
132 QObject::disconnect(textEdit, &TextEditProxy::customContextMenuRequested, q, &TextEditChecker::slotShowContextMenu);
133 QObject::disconnect(textEdit->document(), &QTextDocument::contentsChange, q, &TextEditChecker::slotCheckRange);
134 textEdit->setContextMenuPolicy(oldContextMenuPolicy);
135 textEdit->removeEventFilter(q);
138 QTextCursor cursor = textEdit->textCursor();
139 cursor.movePosition(QTextCursor::Start);
140 cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
141 QTextCharFormat fmt = cursor.charFormat();
142 QTextCharFormat defaultFormat = QTextCharFormat();
143 fmt.setFontUnderline(defaultFormat.fontUnderline());
144 fmt.setUnderlineColor(defaultFormat.underlineColor());
145 fmt.setUnderlineStyle(defaultFormat.underlineStyle());
146 cursor.setCharFormat(fmt);
148 bool undoWasEnabled = undoRedoStack !=
nullptr;
149 q->setUndoRedoEnabled(
false);
152 textEdit = newTextEdit;
154 bool wasModified = textEdit->document()->isModified();
155 document = textEdit->document();
156 QObject::connect(textEdit, &TextEditProxy::editDestroyed, q, &TextEditChecker::slotDetachTextEdit);
157 QObject::connect(textEdit, &TextEditProxy::textChanged, q, &TextEditChecker::slotCheckDocumentChanged);
158 QObject::connect(textEdit, &TextEditProxy::customContextMenuRequested, q, &TextEditChecker::slotShowContextMenu);
159 QObject::connect(textEdit->document(), &QTextDocument::contentsChange, q, &TextEditChecker::slotCheckRange);
160 oldContextMenuPolicy = textEdit->contextMenuPolicy();
161 q->setUndoRedoEnabled(undoWasEnabled);
162 textEdit->setContextMenuPolicy(Qt::CustomContextMenu);
163 textEdit->installEventFilter(q);
165 textEdit->document()->setModified(wasModified);
169 q->setUndoRedoEnabled(
true);
177 d->noSpellingProperty = propertyId;
183 return d->noSpellingProperty;
186bool TextEditChecker::eventFilter(QObject* obj, QEvent* event)
188 if(event->type() == QEvent::KeyPress){
189 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>(event);
190 if(keyEvent->key() == Qt::Key_Z && keyEvent->modifiers() == Qt::CTRL){
193 }
else if(keyEvent->key() == Qt::Key_Z && keyEvent->modifiers() == (Qt::CTRL | Qt::SHIFT)){
198 return QObject::eventFilter(obj, event);
208 QTextCursor tmpCursor(d->textEdit->textCursor());
209 tmpCursor.movePosition(QTextCursor::End);
210 end = tmpCursor.position();
214 d->textEdit->document()->blockSignals(
true);
216 qDebug() <<
"Checking range " << start <<
" - " << end;
218 QTextCharFormat errorFmt;
219 errorFmt.setFontUnderline(
true);
220 errorFmt.setUnderlineColor(Qt::red);
221 errorFmt.setUnderlineStyle(QTextCharFormat::WaveUnderline);
222 QTextCharFormat defaultFormat = QTextCharFormat();
225 cursor.beginEditBlock();
226 cursor.setPosition(start);
227 while(cursor.position() < end) {
230 QString word = cursor.selectedText();
231 if(d->noSpellingPropertySet(cursor)) {
233 qDebug() <<
"Skipping word:" << word <<
"(" << cursor.anchor() <<
"-" << cursor.position() <<
")";
236 qDebug() <<
"Checking word:" << word <<
"(" << cursor.anchor() <<
"-" << cursor.position() <<
"), correct:" << correct;
239 cursor.mergeCharFormat(errorFmt);
241 QTextCharFormat fmt = cursor.charFormat();
242 fmt.setFontUnderline(defaultFormat.fontUnderline());
243 fmt.setUnderlineColor(defaultFormat.underlineColor());
244 fmt.setUnderlineStyle(defaultFormat.underlineStyle());
245 cursor.setCharFormat(fmt);
249 cursor.movePosition(QTextCursor::NextCharacter);
252 cursor.endEditBlock();
254 d->textEdit->document()->blockSignals(
false);
257bool TextEditCheckerPrivate::noSpellingPropertySet(
const QTextCursor &cursor)
const
259 if(noSpellingProperty < QTextFormat::UserProperty) {
262 if(cursor.charFormat().intProperty(noSpellingProperty) == 1) {
265 const QVector<QTextLayout::FormatRange>& formats = cursor.block().layout()->formats();
266 int pos = cursor.positionInBlock();
267 foreach(
const QTextLayout::FormatRange& range, formats) {
268 if(pos > range.start && pos <= range.start + range.length && range.format.intProperty(noSpellingProperty) == 1) {
278 if(d->undoRedoStack){
279 d->undoRedoStack->clear();
286 if(enabled == (d->undoRedoStack !=
nullptr)){
290 delete d->undoRedoStack;
291 d->undoRedoStack =
nullptr;
295 d->undoRedoStack =
new UndoRedoStack(d->textEdit);
305 cursor.setPosition(pos);
309 *start = cursor.anchor();
311 *end = cursor.position();
312 return cursor.selectedText();
318 QTextCursor cursor(d->textEdit->textCursor());
319 cursor.setPosition(start);
320 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, end - start);
321 cursor.insertText(word);
324void TextEditChecker::slotShowContextMenu(
const QPoint &pos)
327 QPoint globalPos = d->textEdit->mapToGlobal(pos);
328 QMenu* menu = d->textEdit->createStandardContextMenu();
329 int wordPos = d->textEdit->cursorForPosition(pos).position();
330 showContextMenu(menu, globalPos, wordPos);
333void TextEditChecker::slotCheckDocumentChanged()
336 if(d->document != d->textEdit->document()) {
337 bool undoWasEnabled = d->undoRedoStack !=
nullptr;
340 disconnect(d->document, &QTextDocument::contentsChange,
this, &TextEditChecker::slotCheckRange);
342 d->document = d->textEdit->document();
343 connect(d->document, &QTextDocument::contentsChange,
this, &TextEditChecker::slotCheckRange);
348void TextEditChecker::slotDetachTextEdit()
351 bool undoWasEnabled = d->undoRedoStack !=
nullptr;
354 d->textEdit =
nullptr;
355 d->document =
nullptr;
362void TextEditChecker::slotCheckRange(
int pos,
int removed,
int added)
365 if(d->undoRedoStack !=
nullptr && !d->undoRedoInProgress){
366 d->undoRedoStack->handleContentsChange(pos, removed, added);
370 TextCursor c(d->textEdit->textCursor());
371 c.movePosition(QTextCursor::End);
372 int len = c.position();
373 if(pos == 0 && added > len){
381 c.setPosition(pos + added, QTextCursor::KeepAnchor);
382 c.moveWordEnd(QTextCursor::KeepAnchor);
383 QTextCharFormat fmt = c.charFormat();
384 QTextCharFormat defaultFormat = QTextCharFormat();
385 fmt.setFontUnderline(defaultFormat.fontUnderline());
386 fmt.setUnderlineColor(defaultFormat.underlineColor());
387 fmt.setUnderlineStyle(defaultFormat.underlineStyle());
388 c.setCharFormat(fmt);
396 if(d->undoRedoStack !=
nullptr){
397 d->undoRedoInProgress =
true;
398 d->undoRedoStack->undo();
399 d->textEdit->ensureCursorVisible();
400 d->undoRedoInProgress =
false;
407 if(d->undoRedoStack !=
nullptr){
408 d->undoRedoInProgress =
true;
409 d->undoRedoStack->redo();
410 d->textEdit->ensureCursorVisible();
411 d->undoRedoInProgress =
false;
418 return d->textEdit != 0;
An abstract class providing spell checking support.
bool checkWord(const QString &word) const
Check the specified word.
void moveWordStart(MoveMode moveMode=MoveAnchor)
Move the cursor to the start of the current word. Cursor must be inside a word. This method correctly...
bool isWordChar(const QString &character) const
Returns whether the specified character is a word character.
void moveWordEnd(MoveMode moveMode=MoveAnchor)
Move the cursor to the end of the current word. Cursor must be inside a word. This method correctly h...
QString nextChar(int num=1) const
Retreive the num-th next character.
Checker class for QTextEdit widgets.
TextEditChecker(QObject *parent=0)
TextEditChecker object constructor.
void setUndoRedoEnabled(bool enabled)
Sets whether undo/redo functionality is enabled.
void clearUndoRedo()
Clears the undo/redo stack.
void setTextEdit(QTextEdit *textEdit)
Set the QTextEdit to check.
void checkSpelling(int start=0, int end=-1)
Check the spelling.
~TextEditChecker()
TextEditChecker object destructor.
void setNoSpellingPropertyId(int propertyId)
Set the QTextCharFormat property identifier which marks whether a word ought to be spell-checked.
bool isAttached() const
Returns whether a widget is attached to the checker.
void redo()
Redo the last edit operation.
void redoAvailable(bool available)
Emitted when the redo stak changes.
void undo()
Undo the last edit operation.
void undoAvailable(bool available)
Emitted when the undo stack changes.
int noSpellingPropertyId() const
Returns the current QTextCharFormat property identifier which marks whether a word ought to be spell-...
void insertWord(int start, int end, const QString &word)
Replaces the specified range with the specified word.
QString getWord(int pos, int *start=0, int *end=0) const
Get the word at the specified cursor position.