19 #include "QtSpell.hpp"
20 #include "TextEditChecker_p.hpp"
21 #include "UndoRedoStack.hpp"
24 #include <QPlainTextEdit>
34 testCursor.movePosition(NextCharacter, MoveAnchor, num - 1);
36 testCursor.setPosition(testCursor.position());
37 testCursor.movePosition(NextCharacter, KeepAnchor);
38 return testCursor.selectedText();
45 testCursor.movePosition(PreviousCharacter, MoveAnchor, num - 1);
47 testCursor.setPosition(testCursor.position());
48 testCursor.movePosition(PreviousCharacter, KeepAnchor);
49 return testCursor.selectedText();
54 movePosition(StartOfWord, moveMode);
59 if(
prevChar().contains(m_wordRegEx)){
60 movePosition(WordLeft, moveMode);
62 movePosition(NextCharacter, moveMode);
67 movePosition(WordLeft, moveMode, 2);
73 movePosition(EndOfWord, moveMode);
78 if(
nextChar().contains(m_wordRegEx)){
79 movePosition(WordRight, moveMode);
81 movePosition(PreviousCharacter, moveMode);
86 movePosition(WordRight, moveMode, 2);
104 setTextEdit(textEdit ?
new TextEditProxyT<QTextEdit>(textEdit) :
static_cast<TextEditProxyT<QTextEdit>*
>(
nullptr));
109 setTextEdit(textEdit ?
new TextEditProxyT<QPlainTextEdit>(textEdit) :
static_cast<TextEditProxyT<QPlainTextEdit>*
>(
nullptr));
115 disconnect(m_textEdit, &TextEditProxy::editDestroyed,
this, &TextEditChecker::slotDetachTextEdit);
116 disconnect(m_textEdit, &TextEditProxy::textChanged,
this, &TextEditChecker::slotCheckDocumentChanged);
117 disconnect(m_textEdit, &TextEditProxy::customContextMenuRequested,
this, &TextEditChecker::slotShowContextMenu);
118 disconnect(m_textEdit->document(), &QTextDocument::contentsChange,
this, &TextEditChecker::slotCheckRange);
119 m_textEdit->setContextMenuPolicy(m_oldContextMenuPolicy);
120 m_textEdit->removeEventFilter(
this);
123 QTextCursor cursor = m_textEdit->textCursor();
124 cursor.movePosition(QTextCursor::Start);
125 cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
126 QTextCharFormat fmt = cursor.charFormat();
127 QTextCharFormat defaultFormat = QTextCharFormat();
128 fmt.setFontUnderline(defaultFormat.fontUnderline());
129 fmt.setUnderlineColor(defaultFormat.underlineColor());
130 fmt.setUnderlineStyle(defaultFormat.underlineStyle());
131 cursor.setCharFormat(fmt);
133 bool undoWasEnabled = m_undoRedoStack !=
nullptr;
136 m_document =
nullptr;
137 m_textEdit = textEdit;
139 m_document = m_textEdit->document();
140 connect(m_textEdit, &TextEditProxy::editDestroyed,
this, &TextEditChecker::slotDetachTextEdit);
141 connect(m_textEdit, &TextEditProxy::textChanged,
this, &TextEditChecker::slotCheckDocumentChanged);
142 connect(m_textEdit, &TextEditProxy::customContextMenuRequested,
this, &TextEditChecker::slotShowContextMenu);
143 connect(m_textEdit->document(), &QTextDocument::contentsChange,
this, &TextEditChecker::slotCheckRange);
144 m_oldContextMenuPolicy = m_textEdit->contextMenuPolicy();
146 m_textEdit->setContextMenuPolicy(Qt::CustomContextMenu);
147 m_textEdit->installEventFilter(
this);
152 bool TextEditChecker::eventFilter(QObject* obj, QEvent* event)
154 if(event->type() == QEvent::KeyPress){
155 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>(event);
156 if(keyEvent->key() == Qt::Key_Z && keyEvent->modifiers() == Qt::CTRL){
159 }
else if(keyEvent->key() == Qt::Key_Z && keyEvent->modifiers() == (Qt::CTRL | Qt::SHIFT)){
164 return QObject::eventFilter(obj, event);
170 QTextCursor tmpCursor(m_textEdit->textCursor());
171 tmpCursor.movePosition(QTextCursor::End);
172 end = tmpCursor.position();
176 m_textEdit->document()->blockSignals(
true);
178 qDebug() <<
"Checking range " << start <<
" - " << end;
180 QTextCharFormat errorFmt;
181 errorFmt.setFontUnderline(
true);
182 errorFmt.setUnderlineColor(Qt::red);
183 errorFmt.setUnderlineStyle(QTextCharFormat::WaveUnderline);
184 QTextCharFormat defaultFormat = QTextCharFormat();
187 cursor.beginEditBlock();
188 cursor.setPosition(start);
189 while(cursor.position() < end) {
192 QString word = cursor.selectedText();
193 if(noSpellingPropertySet(cursor)) {
195 qDebug() <<
"Skipping word:" << word <<
"(" << cursor.anchor() <<
"-" << cursor.position() <<
")";
198 qDebug() <<
"Checking word:" << word <<
"(" << cursor.anchor() <<
"-" << cursor.position() <<
"), correct:" << correct;
201 cursor.mergeCharFormat(errorFmt);
203 QTextCharFormat fmt = cursor.charFormat();
204 fmt.setFontUnderline(defaultFormat.fontUnderline());
205 fmt.setUnderlineColor(defaultFormat.underlineColor());
206 fmt.setUnderlineStyle(defaultFormat.underlineStyle());
207 cursor.setCharFormat(fmt);
211 cursor.movePosition(QTextCursor::NextCharacter);
214 cursor.endEditBlock();
216 m_textEdit->document()->blockSignals(
false);
219 bool TextEditChecker::noSpellingPropertySet(
const QTextCursor &cursor)
const
221 if(m_noSpellingProperty < QTextFormat::UserProperty) {
224 if(cursor.charFormat().intProperty(m_noSpellingProperty) == 1) {
227 const QVector<QTextLayout::FormatRange>& formats = cursor.block().layout()->formats();
228 int pos = cursor.positionInBlock();
229 foreach(
const QTextLayout::FormatRange& range, formats) {
230 if(pos > range.start && pos <= range.start + range.length && range.format.intProperty(m_noSpellingProperty) == 1) {
240 m_undoRedoStack->clear();
246 if(enabled == (m_undoRedoStack !=
nullptr)){
250 delete m_undoRedoStack;
251 m_undoRedoStack =
nullptr;
255 m_undoRedoStack =
new UndoRedoStack(m_textEdit);
264 cursor.setPosition(pos);
268 *start = cursor.anchor();
270 *end = cursor.position();
271 return cursor.selectedText();
276 QTextCursor cursor(m_textEdit->textCursor());
277 cursor.setPosition(start);
278 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, end - start);
279 cursor.insertText(word);
282 void TextEditChecker::slotShowContextMenu(
const QPoint &pos)
284 QPoint globalPos = m_textEdit->mapToGlobal(pos);
285 QMenu* menu = m_textEdit->createStandardContextMenu();
286 int wordPos = m_textEdit->cursorForPosition(pos).position();
287 showContextMenu(menu, globalPos, wordPos);
290 void TextEditChecker::slotCheckDocumentChanged()
292 if(m_document != m_textEdit->document()) {
293 bool undoWasEnabled = m_undoRedoStack !=
nullptr;
296 disconnect(m_document, &QTextDocument::contentsChange,
this, &TextEditChecker::slotCheckRange);
298 m_document = m_textEdit->document();
299 connect(m_document, &QTextDocument::contentsChange,
this, &TextEditChecker::slotCheckRange);
304 void TextEditChecker::slotDetachTextEdit()
306 bool undoWasEnabled = m_undoRedoStack !=
nullptr;
309 m_textEdit =
nullptr;
310 m_document =
nullptr;
317 void TextEditChecker::slotCheckRange(
int pos,
int removed,
int added)
319 if(m_undoRedoStack !=
nullptr && !m_undoRedoInProgress){
320 m_undoRedoStack->handleContentsChange(pos, removed, added);
324 TextCursor c(m_textEdit->textCursor());
325 c.movePosition(QTextCursor::End);
326 int len = c.position();
327 if(pos == 0 && added > len){
335 c.setPosition(pos + added, QTextCursor::KeepAnchor);
336 c.moveWordEnd(QTextCursor::KeepAnchor);
337 QTextCharFormat fmt = c.charFormat();
338 QTextCharFormat defaultFormat = QTextCharFormat();
339 fmt.setFontUnderline(defaultFormat.fontUnderline());
340 fmt.setUnderlineColor(defaultFormat.underlineColor());
341 fmt.setUnderlineStyle(defaultFormat.underlineStyle());
342 c.setCharFormat(fmt);
349 if(m_undoRedoStack !=
nullptr){
350 m_undoRedoInProgress =
true;
351 m_undoRedoStack->undo();
352 m_textEdit->ensureCursorVisible();
353 m_undoRedoInProgress =
false;
359 if(m_undoRedoStack !=
nullptr){
360 m_undoRedoInProgress =
true;
361 m_undoRedoStack->redo();
362 m_textEdit->ensureCursorVisible();
363 m_undoRedoInProgress =
false;
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...
QString prevChar(int num=1) const
Retreive the num-th previous character.
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.
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 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.
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.