26 #include <QApplication>
32 #include <QTextCursor>
33 #include <QTextDocumentFragment>
34 #include <QDBusInterface>
35 #include <QDBusConnection>
36 #include <QDBusConnectionInterface>
59 class KTextEdit::Private
64 customPalette( false ),
66 findReplaceEnabled(true),
68 showAutoCorrectionButton(false),
70 lastReplacedPosition(-1)
73 sonnetKConfig =
new KConfig(
"sonnetrc");
82 QString metaMsg =
i18nc(
"Italic placeholder text in line edits: 0 no, 1 yes",
"1");
83 italicizePlaceholder = (metaMsg.trimmed() !=
QString(
'0'));
101 bool overrideShortcut(
const QKeyEvent* e);
105 bool handleShortcut(
const QKeyEvent* e);
107 void spellCheckerMisspelling(
const QString &text,
int pos );
108 void spellCheckerCorrected(
const QString &,
int,
const QString &);
110 void spellCheckerCanceled();
111 void spellCheckerFinished();
112 void toggleAutoSpellCheck();
114 void slotFindHighlight(
const QString& text,
int matchingIndex,
int matchingLength);
115 void slotReplaceText(
const QString &text,
int replacementIndex,
int ,
int matchedLength);
121 void undoableClear();
124 void menuActivated(
QAction* action );
126 QRect clickMessageRect()
const;
137 bool italicizePlaceholder : 1;
138 bool customPalette : 1;
141 bool findReplaceEnabled: 1;
143 bool showAutoCorrectionButton: 1;
144 QTextDocumentFragment originalDoc;
145 QString spellCheckingConfigFileName;
152 int findIndex, repIndex;
153 int lastReplacedPosition;
159 if(parent->document()->isEmpty())
163 emit parent->spellCheckingFinished();
168 if(!spellCheckingLanguage.isEmpty())
171 backgroundSpellCheck, force ? parent : 0);
172 backgroundSpellCheck->setParent(spellDialog);
173 spellDialog->setAttribute(Qt::WA_DeleteOnClose,
true);
177 connect(spellDialog, SIGNAL(misspelling(
QString,
int)),
178 parent, SLOT(spellCheckerMisspelling(
QString,
int)));
181 connect(spellDialog, SIGNAL(done(
QString)),
182 parent, SLOT(spellCheckerFinished()));
183 connect(spellDialog, SIGNAL(
cancel()),
184 parent, SLOT(spellCheckerCanceled()));
190 connect(spellDialog, SIGNAL(spellCheckStatus(
QString)),
191 parent, SIGNAL(spellCheckStatus(
QString)));
192 connect(spellDialog, SIGNAL(languageChanged(
QString)),
193 parent, SIGNAL(languageChanged(
QString)));
195 connect(spellDialog, SIGNAL(done(
QString)),parent, SIGNAL(spellCheckingFinished()));
196 connect(spellDialog, SIGNAL(
cancel()), parent, SIGNAL(spellCheckingCanceled()));
200 originalDoc = QTextDocumentFragment(parent->document());
201 spellDialog->
setBuffer(parent->toPlainText());
206 void KTextEdit::Private::spellCheckerCanceled()
208 QTextDocument *doc = parent->document();
210 QTextCursor cursor(doc);
211 cursor.insertFragment(originalDoc);
212 spellCheckerFinished();
217 emit parent->spellCheckerAutoCorrect(currentWord, autoCorrectWord);
220 void KTextEdit::Private::spellCheckerMisspelling(
const QString &text,
int pos )
223 parent->highlightWord( text.length(), pos );
226 void KTextEdit::Private::spellCheckerCorrected(
const QString& oldWord,
int pos,
const QString& newWord)
229 if (oldWord != newWord ) {
230 QTextCursor cursor(parent->document());
231 cursor.setPosition(pos);
232 cursor.setPosition(pos+oldWord.length(),QTextCursor::KeepAnchor);
233 cursor.insertText(newWord);
237 void KTextEdit::Private::spellCheckerFinished()
239 QTextCursor cursor(parent->document());
240 cursor.clearSelection();
241 parent->setTextCursor(cursor);
242 if (parent->highlighter())
243 parent->highlighter()->rehighlight();
246 void KTextEdit::Private::toggleAutoSpellCheck()
248 parent->setCheckSpellingEnabled( !parent->checkSpellingEnabled() );
251 void KTextEdit::Private::undoableClear()
253 QTextCursor cursor = parent->textCursor();
254 cursor.beginEditBlock();
255 cursor.movePosition(QTextCursor::Start);
257 cursor.removeSelectedText();
258 cursor.endEditBlock();
261 void KTextEdit::Private::slotAllowTab()
263 parent->setTabChangesFocus( !parent->tabChangesFocus() );
266 void KTextEdit::Private::menuActivated(
QAction* action )
268 if ( action == spellCheckAction )
269 parent->checkSpelling();
270 else if ( action == autoSpellCheckAction )
271 toggleAutoSpellCheck();
272 else if ( action == allowTab )
277 void KTextEdit::Private::slotFindHighlight(
const QString& text,
int matchingIndex,
int matchingLength)
281 QTextCursor tc = parent->textCursor();
282 tc.setPosition(matchingIndex);
283 tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchingLength);
284 parent->setTextCursor(tc);
285 parent->ensureCursorVisible();
289 void KTextEdit::Private::slotReplaceText(const
QString &text,
int replacementIndex,
int replacedLength,
int matchedLength) {
291 QTextCursor tc = parent->textCursor();
292 tc.setPosition(replacementIndex);
293 tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchedLength);
294 tc.removeSelectedText();
295 tc.insertText(text.mid(replacementIndex, replacedLength));
297 parent->setTextCursor(tc);
298 parent->ensureCursorVisible();
300 lastReplacedPosition = replacementIndex;
303 QRect KTextEdit::Private::clickMessageRect()
const
305 int margin = int(parent->document()->documentMargin());
306 QRect rect = parent->viewport()->rect().adjusted(margin, margin, -margin, -margin);
307 return parent->fontMetrics().boundingRect(rect, Qt::AlignTop | Qt::TextWordWrap, clickMessage);
310 void KTextEdit::Private::init()
314 parent->connect(parent, SIGNAL(languageChanged(
QString)),
315 parent, SLOT(setSpellCheckingLanguage(
QString)));
319 :
QTextEdit( text, parent ), d( new Private( this ) )
325 :
QTextEdit( parent ), d( new Private( this ) )
337 d->spellCheckingConfigFileName = _fileName;
342 return d->spellCheckingLanguage;
352 if (_language != d->spellCheckingLanguage) {
353 d->spellCheckingLanguage = _language;
363 if (!d->spellCheckingLanguage.isEmpty())
365 if (!windowIcon.isEmpty())
366 dialog.setWindowIcon(
KIcon(windowIcon));
374 if (ev->type() == QEvent::ShortcutOverride) {
375 QKeyEvent *e =
static_cast<QKeyEvent *
>( ev );
376 if (d->overrideShortcut(e)) {
384 bool KTextEdit::Private::handleShortcut(
const QKeyEvent* event)
386 const int key =
event->key() |
event->modifiers();
398 if(!parent->isReadOnly())
402 if(!parent->isReadOnly())
406 parent->deleteWordBack();
409 parent->deleteWordForward();
412 QTextCursor cursor = parent->textCursor();
413 cursor.movePosition( QTextCursor::PreviousWord );
414 parent->setTextCursor( cursor );
417 QTextCursor cursor = parent->textCursor();
418 cursor.movePosition( QTextCursor::NextWord );
419 parent->setTextCursor( cursor );
422 QTextCursor cursor = parent->textCursor();
424 qreal lastY = parent->cursorRect(cursor).bottom();
427 qreal y = parent->cursorRect(cursor).bottom();
428 distance += qAbs(y - lastY);
430 moved = cursor.movePosition(QTextCursor::Down);
431 }
while (moved && distance < parent->viewport()->height());
435 parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
437 parent->setTextCursor(cursor);
440 QTextCursor cursor = parent->textCursor();
442 qreal lastY = parent->cursorRect(cursor).bottom();
445 qreal y = parent->cursorRect(cursor).bottom();
446 distance += qAbs(y - lastY);
449 }
while (moved && distance < parent->viewport()->height());
452 cursor.movePosition(QTextCursor::Down);
453 parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
455 parent->setTextCursor(cursor);
458 QTextCursor cursor = parent->textCursor();
459 cursor.movePosition( QTextCursor::Start );
460 parent->setTextCursor( cursor );
463 QTextCursor cursor = parent->textCursor();
465 parent->setTextCursor( cursor );
468 QTextCursor cursor = parent->textCursor();
469 cursor.movePosition( QTextCursor::StartOfLine );
470 parent->setTextCursor( cursor );
473 QTextCursor cursor = parent->textCursor();
475 parent->setTextCursor( cursor );
481 parent->slotFindNext();
484 if (!parent->isReadOnly())
485 parent->slotReplace();
488 QString text = QApplication::clipboard()->text( QClipboard::Selection );
489 if ( !text.isEmpty() )
490 parent->insertPlainText( text );
496 static void deleteWord(QTextCursor cursor, QTextCursor::MoveOperation op)
498 cursor.clearSelection();
499 cursor.movePosition( op, QTextCursor::KeepAnchor );
500 cursor.removeSelectedText();
505 deleteWord(textCursor(), QTextCursor::PreviousWord);
510 deleteWord(textCursor(), QTextCursor::WordRight);
515 QMenu *popup = createStandardContextMenu();
516 if (!popup)
return 0;
517 connect( popup, SIGNAL(triggered(
QAction*)),
518 this, SLOT(menuActivated(
QAction*)) );
520 const bool emptyDocument = document()->isEmpty();
523 QList<QAction *> actionList = popup->actions();
524 enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
526 int idx = actionList.indexOf( actionList[SelectAllAct] ) + 1;
527 if ( idx < actionList.count() )
528 separatorAction = actionList.at( idx );
529 if ( separatorAction )
533 clearAllAction->setEnabled(
false );
534 popup->insertAction( separatorAction, clearAllAction );
543 popup->addSeparator();
544 d->spellCheckAction = popup->addAction(
KIcon(
"tools-check-spelling" ),
545 i18n(
"Check Spelling..." ) );
547 d->spellCheckAction->setEnabled(
false );
548 d->autoSpellCheckAction = popup->addAction(
i18n(
"Auto Spell Check" ) );
549 d->autoSpellCheckAction->setCheckable(
true );
551 popup->addSeparator();
552 if (d->showTabAction) {
553 d->allowTab = popup->addAction(
i18n(
"Allow Tabulations") );
554 d->allowTab->setCheckable(
true );
555 d->allowTab->setChecked( !tabChangesFocus() );
559 if (d->findReplaceEnabled) {
563 findAction->setEnabled(
false);
564 findNextAction->setEnabled(
false);
566 findNextAction->setEnabled(d->find != 0);
568 popup->addSeparator();
569 popup->addAction(findAction);
570 popup->addAction(findNextAction);
575 replaceAction->setEnabled(
false);
577 popup->addAction(replaceAction);
580 popup->addSeparator();
581 QAction *speakAction = popup->addAction(
i18n(
"Speak Text"));
582 speakAction->setIcon(
KIcon(
"preferences-desktop-text-to-speech"));
583 speakAction->setEnabled(!emptyDocument );
584 connect( speakAction, SIGNAL(triggered(
bool)),
this, SLOT(
slotSpeakText()) );
591 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(
"org.kde.kttsd"))
600 QDBusInterface ktts(
"org.kde.kttsd",
"/KSpeech",
"org.kde.KSpeech");
602 if(textCursor().hasSelection())
603 text = textCursor().selectedText();
605 text = toPlainText();
606 ktts.asyncCall(
"say", text, 0);
612 QTextCursor cursorAtMouse = cursorForPosition(event->pos());
613 const int mousePos = cursorAtMouse.position();
614 QTextCursor cursor = textCursor();
617 const bool selectedWordClicked = cursor.hasSelection() &&
618 mousePos >= cursor.selectionStart() &&
619 mousePos <= cursor.selectionEnd();
623 QTextCursor wordSelectCursor(cursorAtMouse);
624 wordSelectCursor.clearSelection();
625 wordSelectCursor.select(QTextCursor::WordUnderCursor);
626 QString selectedWord = wordSelectCursor.selectedText();
628 bool isMouseCursorInsideWord =
true;
629 if ((mousePos < wordSelectCursor.selectionStart() ||
630 mousePos >= wordSelectCursor.selectionEnd())
631 && (selectedWord.length() > 1)) {
632 isMouseCursorInsideWord =
false;
636 wordSelectCursor.setPosition(wordSelectCursor.position()-selectedWord.size());
637 if (selectedWord.startsWith(
'\'') || selectedWord.startsWith(
'\"')) {
638 selectedWord = selectedWord.right(selectedWord.size() - 1);
639 wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
641 if (selectedWord.endsWith(
'\'') || selectedWord.endsWith(
'\"'))
642 selectedWord.chop(1);
644 wordSelectCursor.movePosition(QTextCursor::NextCharacter,
645 QTextCursor::KeepAnchor, selectedWord.size());
647 const bool wordIsMisspelled = isMouseCursorInsideWord &&
649 !selectedWord.isEmpty() &&
657 bool inQuote =
false;
658 if (d->spellInterface &&
659 !d->spellInterface->shouldBlockBeSpellChecked(cursorAtMouse.block().text()))
661 if (!selectedWordClicked) {
662 if (wordIsMisspelled && !inQuote)
663 setTextCursor(wordSelectCursor);
665 setTextCursor(cursorAtMouse);
666 cursor = textCursor();
671 if (!wordIsMisspelled || selectedWordClicked || inQuote) {
672 QMetaObject::invokeMethod(
this,
"mousePopupMenuImplementation", Q_ARG(
QPoint, event->globalPos()));
679 if (reps.isEmpty()) {
680 QAction *suggestionsAction = menu.addAction(
i18n(
"No suggestions for %1", selectedWord));
681 suggestionsAction->setEnabled(
false);
684 QStringList::const_iterator
end(reps.constEnd());
685 for (QStringList::const_iterator it = reps.constBegin(); it !=
end; ++it) {
692 QAction *ignoreAction = menu.addAction(
i18n(
"Ignore"));
693 QAction *addToDictAction = menu.addAction(
i18n(
"Add to Dictionary"));
695 const QAction *selectedAction = menu.exec(event->globalPos());
697 if (selectedAction) {
698 Q_ASSERT(cursor.selectedText() == selectedWord);
700 if (selectedAction == ignoreAction) {
704 else if (selectedAction == addToDictAction) {
711 const QString replacement = selectedAction->text();
712 Q_ASSERT(reps.contains(replacement));
713 cursor.insertText(replacement);
714 setTextCursor(cursor);
735 return d->highlighter;
740 delete d->highlighter;
741 d->highlighter = _highLighter;
746 if (d->spellInterface)
747 d->spellInterface->setSpellCheckingEnabled(check);
755 if ( check == d->checkSpellingEnabled )
762 d->checkSpellingEnabled = check;
773 delete d->highlighter;
780 if ( d->checkSpellingEnabled && !isReadOnly() && !d->highlighter )
788 if (d->spellInterface)
789 return d->spellInterface->isSpellCheckingEnabled();
796 return d->checkSpellingEnabled;
801 if ( !readOnly && hasFocus() && d->checkSpellingEnabled && !d->highlighter )
804 if ( readOnly == isReadOnly() )
808 delete d->highlighter;
811 d->customPalette = testAttribute( Qt::WA_SetPalette );
812 QPalette p = palette();
813 QColor color = p.color( QPalette::Disabled, QPalette::Background );
814 p.setColor( QPalette::Base, color );
815 p.setColor( QPalette::Background, color );
818 if ( d->customPalette && testAttribute( Qt::WA_SetPalette ) ) {
819 QPalette p = palette();
820 QColor color = p.color( QPalette::Normal, QPalette::Base );
821 p.setColor( QPalette::Base, color );
822 p.setColor( QPalette::Background, color );
825 setPalette( QPalette() );
833 d->checkSpelling(
false);
838 d->checkSpelling(
true);
843 QTextCursor cursor(document());
844 cursor.setPosition(pos);
845 cursor.setPosition(pos+length,QTextCursor::KeepAnchor);
846 setTextCursor (cursor);
847 ensureCursorVisible();
852 if ( document()->isEmpty() )
860 connect( d->repDlg, SIGNAL(okClicked()),
this, SLOT(
slotDoReplace()) );
872 if(d->repDlg->pattern().isEmpty()) {
875 ensureCursorVisible();
880 d->replace =
new KReplace(d->repDlg->pattern(), d->repDlg->replacement(), d->repDlg->options(),
this);
883 d->repIndex = textCursor().anchor();
888 connect(d->replace, SIGNAL(highlight(
QString,
int,
int)),
889 this, SLOT(slotFindHighlight(
QString,
int,
int)));
892 this, SLOT(slotReplaceText(
QString,
int,
int,
int)));
904 d->lastReplacedPosition = -1;
906 textCursor().beginEditBlock();
907 viewport()->setUpdatesEnabled(
false);
912 if (d->replace->needData())
913 d->replace->setData(toPlainText(), d->repIndex);
914 res = d->replace->replace();
916 textCursor().endEditBlock();
917 if (d->lastReplacedPosition >= 0) {
918 QTextCursor tc = textCursor();
919 tc.setPosition(d->lastReplacedPosition);
921 ensureCursorVisible();
924 viewport()->setUpdatesEnabled(
true);
925 viewport()->update();
929 d->replace->displayFinalDialog();
930 d->replace->disconnect(
this);
931 d->replace->deleteLater();
933 ensureCursorVisible();
947 if( d->findDlg->pattern().isEmpty())
954 d->find =
new KFind(d->findDlg->pattern(), d->findDlg->options(),
this);
957 d->findIndex = textCursor().anchor();
962 connect(d->find, SIGNAL(highlight(
QString,
int,
int)),
963 this, SLOT(slotFindHighlight(
QString,
int,
int)));
967 d->find->closeFindNextDialog();
976 if(document()->isEmpty())
978 d->find->disconnect(
this);
979 d->find->deleteLater();
985 if (d->find->needData())
986 d->find->setData(toPlainText(), d->findIndex);
987 res = d->find->find();
990 d->find->displayFinalDialog();
991 d->find->disconnect(
this);
992 d->find->deleteLater();
1003 if ( document()->isEmpty() )
1010 connect( d->findDlg, SIGNAL(okClicked()),
this, SLOT(
slotDoFind()) );
1018 if ( document()->isEmpty() )
1026 connect( d->repDlg, SIGNAL(okClicked()),
this, SLOT(
slotDoReplace()) );
1033 d->findReplaceEnabled = enabled;
1038 d->showTabAction = show;
1043 d->spellInterface = spellInterface;
1046 bool KTextEdit::Private::overrideShortcut(
const QKeyEvent* event)
1048 const int key =
event->key() |
event->modifiers();
1090 }
else if (event->modifiers() == Qt::ControlModifier &&
1091 (
event->key() == Qt::Key_Return ||
event->key() == Qt::Key_Enter) &&
1092 qobject_cast<KDialog*>(parent->window()) ) {
1101 if (d->handleShortcut(event)) {
1103 }
else if (event->modifiers() == Qt::ControlModifier &&
1104 (
event->key() == Qt::Key_Return ||
event->key() == Qt::Key_Enter) &&
1105 qobject_cast<KDialog*>(window()) ) {
1114 if (msg != d->clickMessage) {
1115 if (!d->clickMessage.isEmpty()) {
1116 viewport()->update(d->clickMessageRect());
1118 d->clickMessage = msg;
1119 if (!d->clickMessage.isEmpty()) {
1120 viewport()->update(d->clickMessageRect());
1127 return d->clickMessage;
1134 if (!d->clickMessage.isEmpty() && document()->isEmpty()) {
1135 QPainter p(viewport());
1138 f.setItalic(d->italicizePlaceholder);
1141 QColor color(palette().color(viewport()->foregroundRole()));
1142 color.setAlphaF(0.5);
1145 QRect cr = d->clickMessageRect();
1146 p.drawText(cr, Qt::AlignTop | Qt::TextWordWrap, d->clickMessage);
1157 d->showAutoCorrectionButton = show;
1170 #include "ktextedit.moc"