25 #include <QApplication>
31 #include <QTextCursor>
32 #include <QTextDocumentFragment>
33 #include <QDBusInterface>
34 #include <QDBusConnection>
35 #include <QDBusConnectionInterface>
58 class KTextEdit::Private
63 customPalette( false ),
65 findReplaceEnabled(true),
67 lastReplacedPosition(-1)
70 sonnetKConfig =
new KConfig(
"sonnetrc");
79 QString metaMsg =
i18nc(
"Italic placeholder text in line edits: 0 no, 1 yes",
"1");
80 italicizePlaceholder = (metaMsg.trimmed() !=
QString(
'0'));
98 bool overrideShortcut(
const QKeyEvent* e);
102 bool handleShortcut(
const QKeyEvent* e);
104 void spellCheckerMisspelling(
const QString &text,
int pos );
105 void spellCheckerCorrected(
const QString &,
int,
const QString &);
107 void spellCheckerCanceled();
108 void spellCheckerFinished();
109 void toggleAutoSpellCheck();
111 void slotFindHighlight(
const QString& text,
int matchingIndex,
int matchingLength);
112 void slotReplaceText(
const QString &text,
int replacementIndex,
int ,
int matchedLength);
118 void undoableClear();
121 void menuActivated(
QAction* action );
123 void updateClickMessageRect();
133 bool italicizePlaceholder : 1;
134 bool customPalette : 1;
137 bool findReplaceEnabled: 1;
138 QTextDocumentFragment originalDoc;
139 QString spellCheckingConfigFileName;
146 int findIndex, repIndex;
147 int lastReplacedPosition;
151 void KTextEdit::Private::spellCheckerCanceled()
153 QTextDocument *doc = parent->document();
155 QTextCursor cursor(doc);
156 cursor.insertFragment(originalDoc);
157 spellCheckerFinished();
160 void KTextEdit::Private::spellCheckerAutoCorrect(
const QString&,
const QString&)
165 void KTextEdit::Private::spellCheckerMisspelling(
const QString &text,
int pos )
168 parent->highlightWord( text.length(), pos );
171 void KTextEdit::Private::spellCheckerCorrected(
const QString& oldWord,
int pos,
const QString& newWord)
174 if (oldWord != newWord ) {
175 QTextCursor cursor(parent->document());
176 cursor.setPosition(pos);
177 cursor.setPosition(pos+oldWord.length(),QTextCursor::KeepAnchor);
178 cursor.insertText(newWord);
182 void KTextEdit::Private::spellCheckerFinished()
184 QTextCursor cursor(parent->document());
185 cursor.clearSelection();
186 parent->setTextCursor(cursor);
187 if (parent->highlighter())
188 parent->highlighter()->rehighlight();
191 void KTextEdit::Private::toggleAutoSpellCheck()
193 parent->setCheckSpellingEnabled( !parent->checkSpellingEnabled() );
196 void KTextEdit::Private::undoableClear()
198 QTextCursor cursor = parent->textCursor();
199 cursor.beginEditBlock();
200 cursor.movePosition(QTextCursor::Start);
202 cursor.removeSelectedText();
203 cursor.endEditBlock();
206 void KTextEdit::Private::slotAllowTab()
208 parent->setTabChangesFocus( !parent->tabChangesFocus() );
211 void KTextEdit::Private::menuActivated(
QAction* action )
213 if ( action == spellCheckAction )
214 parent->checkSpelling();
215 else if ( action == autoSpellCheckAction )
216 toggleAutoSpellCheck();
217 else if ( action == allowTab )
222 void KTextEdit::Private::slotFindHighlight(
const QString& text,
int matchingIndex,
int matchingLength)
226 QTextCursor tc = parent->textCursor();
227 tc.setPosition(matchingIndex);
228 tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchingLength);
229 parent->setTextCursor(tc);
230 parent->ensureCursorVisible();
234 void KTextEdit::Private::slotReplaceText(const
QString &text,
int replacementIndex,
int replacedLength,
int matchedLength) {
236 QTextCursor tc = parent->textCursor();
237 tc.setPosition(replacementIndex);
238 tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchedLength);
239 tc.removeSelectedText();
240 tc.insertText(text.mid(replacementIndex, replacedLength));
242 parent->setTextCursor(tc);
243 parent->ensureCursorVisible();
245 lastReplacedPosition = replacementIndex;
248 void KTextEdit::Private::updateClickMessageRect()
250 int margin = int(parent->document()->documentMargin());
251 QRect rect = parent->viewport()->rect().adjusted(margin, margin, -margin, -margin);
252 rect = parent->fontMetrics().boundingRect(rect, Qt::AlignTop | Qt::TextWordWrap, clickMessage);
253 parent->viewport()->update(rect);
256 void KTextEdit::Private::init()
260 parent->connect(parent, SIGNAL(languageChanged(
QString)),
261 parent, SLOT(setSpellCheckingLanguage(
QString)));
265 :
QTextEdit( text, parent ), d( new Private( this ) )
271 :
QTextEdit( parent ), d( new Private( this ) )
283 d->spellCheckingConfigFileName = _fileName;
288 return d->spellCheckingLanguage;
298 if (_language != d->spellCheckingLanguage) {
299 d->spellCheckingLanguage = _language;
309 if (!d->spellCheckingLanguage.isEmpty())
311 if (!windowIcon.isEmpty())
312 dialog.setWindowIcon(
KIcon(windowIcon));
320 if (ev->type() == QEvent::ShortcutOverride) {
321 QKeyEvent *e =
static_cast<QKeyEvent *
>( ev );
322 if (d->overrideShortcut(e)) {
330 bool KTextEdit::Private::handleShortcut(
const QKeyEvent* event)
332 const int key =
event->key() |
event->modifiers();
344 if(!parent->isReadOnly())
348 if(!parent->isReadOnly())
352 parent->deleteWordBack();
355 parent->deleteWordForward();
358 QTextCursor cursor = parent->textCursor();
359 cursor.movePosition( QTextCursor::PreviousWord );
360 parent->setTextCursor( cursor );
363 QTextCursor cursor = parent->textCursor();
364 cursor.movePosition( QTextCursor::NextWord );
365 parent->setTextCursor( cursor );
368 QTextCursor cursor = parent->textCursor();
370 qreal lastY = parent->cursorRect(cursor).bottom();
373 qreal y = parent->cursorRect(cursor).bottom();
374 distance += qAbs(y - lastY);
376 moved = cursor.movePosition(QTextCursor::Down);
377 }
while (moved && distance < parent->viewport()->height());
381 parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
383 parent->setTextCursor(cursor);
386 QTextCursor cursor = parent->textCursor();
388 qreal lastY = parent->cursorRect(cursor).bottom();
391 qreal y = parent->cursorRect(cursor).bottom();
392 distance += qAbs(y - lastY);
395 }
while (moved && distance < parent->viewport()->height());
398 cursor.movePosition(QTextCursor::Down);
399 parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
401 parent->setTextCursor(cursor);
404 QTextCursor cursor = parent->textCursor();
405 cursor.movePosition( QTextCursor::Start );
406 parent->setTextCursor( cursor );
409 QTextCursor cursor = parent->textCursor();
411 parent->setTextCursor( cursor );
414 QTextCursor cursor = parent->textCursor();
415 cursor.movePosition( QTextCursor::StartOfLine );
416 parent->setTextCursor( cursor );
419 QTextCursor cursor = parent->textCursor();
421 parent->setTextCursor( cursor );
427 parent->slotFindNext();
430 if (!parent->isReadOnly())
431 parent->slotReplace();
434 QString text = QApplication::clipboard()->text( QClipboard::Selection );
435 if ( !text.isEmpty() )
436 parent->insertPlainText( text );
442 static void deleteWord(QTextCursor cursor, QTextCursor::MoveOperation op)
444 cursor.clearSelection();
445 cursor.movePosition( op, QTextCursor::KeepAnchor );
446 cursor.removeSelectedText();
451 deleteWord(textCursor(), QTextCursor::PreviousWord);
456 deleteWord(textCursor(), QTextCursor::WordRight);
461 QMenu *popup = createStandardContextMenu();
462 if (!popup)
return 0;
463 connect( popup, SIGNAL(triggered(
QAction*)),
464 this, SLOT(menuActivated(
QAction*)) );
466 const bool emptyDocument = document()->isEmpty();
469 QList<QAction *> actionList = popup->actions();
470 enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
472 int idx = actionList.indexOf( actionList[SelectAllAct] ) + 1;
473 if ( idx < actionList.count() )
474 separatorAction = actionList.at( idx );
475 if ( separatorAction )
479 clearAllAction->setEnabled(
false );
480 popup->insertAction( separatorAction, clearAllAction );
489 popup->addSeparator();
490 d->spellCheckAction = popup->addAction(
KIcon(
"tools-check-spelling" ),
491 i18n(
"Check Spelling..." ) );
493 d->spellCheckAction->setEnabled(
false );
494 d->autoSpellCheckAction = popup->addAction(
i18n(
"Auto Spell Check" ) );
495 d->autoSpellCheckAction->setCheckable(
true );
497 popup->addSeparator();
498 d->allowTab = popup->addAction(
i18n(
"Allow Tabulations") );
499 d->allowTab->setCheckable(
true );
500 d->allowTab->setChecked( !tabChangesFocus() );
503 if (d->findReplaceEnabled) {
507 findAction->setEnabled(
false);
508 findNextAction->setEnabled(
false);
510 findNextAction->setEnabled(d->find != 0);
512 popup->addSeparator();
513 popup->addAction(findAction);
514 popup->addAction(findNextAction);
519 replaceAction->setEnabled(
false);
521 popup->addAction(replaceAction);
524 popup->addSeparator();
525 QAction *speakAction = popup->addAction(
i18n(
"Speak Text"));
526 speakAction->setIcon(
KIcon(
"preferences-desktop-text-to-speech"));
527 speakAction->setEnabled(!emptyDocument );
528 connect( speakAction, SIGNAL(triggered(
bool)),
this, SLOT(
slotSpeakText()) );
535 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(
"org.kde.kttsd"))
544 QDBusInterface ktts(
"org.kde.kttsd",
"/KSpeech",
"org.kde.KSpeech");
546 if(textCursor().hasSelection())
547 text = textCursor().selectedText();
549 text = toPlainText();
550 ktts.asyncCall(
"say", text, 0);
556 QTextCursor cursorAtMouse = cursorForPosition(event->pos());
557 const int mousePos = cursorAtMouse.position();
558 QTextCursor cursor = textCursor();
561 const bool selectedWordClicked = cursor.hasSelection() &&
562 mousePos >= cursor.selectionStart() &&
563 mousePos <= cursor.selectionEnd();
567 QTextCursor wordSelectCursor(cursorAtMouse);
568 wordSelectCursor.clearSelection();
569 wordSelectCursor.select(QTextCursor::WordUnderCursor);
570 QString selectedWord = wordSelectCursor.selectedText();
572 bool isMouseCursorInsideWord =
true;
573 if ((mousePos < wordSelectCursor.selectionStart() ||
574 mousePos >= wordSelectCursor.selectionEnd())
575 && (selectedWord.length() > 1)) {
576 isMouseCursorInsideWord =
false;
580 wordSelectCursor.setPosition(wordSelectCursor.position()-selectedWord.size());
581 if (selectedWord.startsWith(
'\'') || selectedWord.startsWith(
'\"')) {
582 selectedWord = selectedWord.right(selectedWord.size() - 1);
583 wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
585 if (selectedWord.endsWith(
'\'') || selectedWord.endsWith(
'\"'))
586 selectedWord.chop(1);
588 wordSelectCursor.movePosition(QTextCursor::NextCharacter,
589 QTextCursor::KeepAnchor, selectedWord.size());
591 const bool wordIsMisspelled = isMouseCursorInsideWord &&
593 !selectedWord.isEmpty() &&
601 bool inQuote =
false;
602 if (d->spellInterface &&
603 !d->spellInterface->shouldBlockBeSpellChecked(cursorAtMouse.block().text()))
605 if (!selectedWordClicked) {
606 if (wordIsMisspelled && !inQuote)
607 setTextCursor(wordSelectCursor);
609 setTextCursor(cursorAtMouse);
610 cursor = textCursor();
615 if (!wordIsMisspelled || selectedWordClicked || inQuote) {
619 popup->exec( event->globalPos() );
628 if (reps.isEmpty()) {
629 QAction *suggestionsAction = menu.addAction(
i18n(
"No suggestions for %1", selectedWord));
630 suggestionsAction->setEnabled(
false);
633 for (QStringList::const_iterator it = reps.constBegin(); it != reps.constEnd(); ++it) {
640 QAction *ignoreAction = menu.addAction(
i18n(
"Ignore"));
641 QAction *addToDictAction = menu.addAction(
i18n(
"Add to Dictionary"));
643 const QAction *selectedAction = menu.exec(event->globalPos());
645 if (selectedAction) {
646 Q_ASSERT(cursor.selectedText() == selectedWord);
648 if (selectedAction == ignoreAction) {
652 else if (selectedAction == addToDictAction) {
659 const QString replacement = selectedAction->text();
660 Q_ASSERT(reps.contains(replacement));
661 cursor.insertText(replacement);
662 setTextCursor(cursor);
683 return d->highlighter;
688 delete d->highlighter;
689 d->highlighter = _highLighter;
694 if (d->spellInterface)
695 d->spellInterface->setSpellCheckingEnabled(check);
703 if ( check == d->checkSpellingEnabled )
710 d->checkSpellingEnabled = check;
721 delete d->highlighter;
728 if ( d->checkSpellingEnabled && !isReadOnly() && !d->highlighter )
731 if (!d->clickMessage.isEmpty()) {
732 d->updateClickMessageRect();
739 if (d->spellInterface)
740 return d->spellInterface->isSpellCheckingEnabled();
747 return d->checkSpellingEnabled;
752 if ( !readOnly && hasFocus() && d->checkSpellingEnabled && !d->highlighter )
755 if ( readOnly == isReadOnly() )
759 delete d->highlighter;
762 d->customPalette = testAttribute( Qt::WA_SetPalette );
763 QPalette p = palette();
764 QColor color = p.color( QPalette::Disabled, QPalette::Background );
765 p.setColor( QPalette::Base, color );
766 p.setColor( QPalette::Background, color );
769 if ( d->customPalette && testAttribute( Qt::WA_SetPalette ) ) {
770 QPalette p = palette();
771 QColor color = p.color( QPalette::Normal, QPalette::Base );
772 p.setColor( QPalette::Base, color );
773 p.setColor( QPalette::Background, color );
776 setPalette( QPalette() );
784 if(document()->isEmpty())
790 if(!d->spellCheckingLanguage.isEmpty())
793 backgroundSpellCheck, 0);
794 backgroundSpellCheck->setParent(spellDialog);
795 spellDialog->setAttribute(Qt::WA_DeleteOnClose,
true);
798 connect(spellDialog, SIGNAL(misspelling(
QString,
int)),
799 this, SLOT(spellCheckerMisspelling(
QString,
int)));
802 connect(spellDialog, SIGNAL(done(
QString)),
803 this, SLOT(spellCheckerFinished()));
804 connect(spellDialog, SIGNAL(
cancel()),
805 this, SLOT(spellCheckerCanceled()));
806 connect(spellDialog, SIGNAL(
stop()),
807 this, SLOT(spellCheckerFinished()));
812 d->originalDoc = QTextDocumentFragment(document());
819 QTextCursor cursor(document());
820 cursor.setPosition(pos);
821 cursor.setPosition(pos+length,QTextCursor::KeepAnchor);
822 setTextCursor (cursor);
823 ensureCursorVisible();
828 if( document()->isEmpty() )
836 connect( d->repDlg, SIGNAL(okClicked()),
this, SLOT(
slotDoReplace()) );
848 if(d->repDlg->pattern().isEmpty()) {
851 ensureCursorVisible();
856 d->replace =
new KReplace(d->repDlg->pattern(), d->repDlg->replacement(), d->repDlg->options(),
this);
859 d->repIndex = textCursor().anchor();
864 connect(d->replace, SIGNAL(highlight(
QString,
int,
int)),
865 this, SLOT(slotFindHighlight(
QString,
int,
int)));
868 this, SLOT(slotReplaceText(
QString,
int,
int,
int)));
880 d->lastReplacedPosition = -1;
882 textCursor().beginEditBlock();
883 viewport()->setUpdatesEnabled(
false);
888 if (d->replace->needData())
889 d->replace->setData(toPlainText(), d->repIndex);
890 res = d->replace->replace();
892 textCursor().endEditBlock();
893 if (d->lastReplacedPosition >= 0) {
894 QTextCursor tc = textCursor();
895 tc.setPosition(d->lastReplacedPosition);
897 ensureCursorVisible();
900 viewport()->setUpdatesEnabled(
true);
901 viewport()->update();
905 d->replace->displayFinalDialog();
906 d->replace->disconnect(
this);
907 d->replace->deleteLater();
909 ensureCursorVisible();
923 if( d->findDlg->pattern().isEmpty())
930 d->find =
new KFind(d->findDlg->pattern(), d->findDlg->options(),
this);
933 d->findIndex = textCursor().anchor();
938 connect(d->find, SIGNAL(highlight(
QString,
int,
int)),
939 this, SLOT(slotFindHighlight(
QString,
int,
int)));
943 d->find->closeFindNextDialog();
952 if(document()->isEmpty())
954 d->find->disconnect(
this);
955 d->find->deleteLater();
961 if (d->find->needData())
962 d->find->setData(toPlainText(), d->findIndex);
963 res = d->find->find();
966 d->find->displayFinalDialog();
967 d->find->disconnect(
this);
968 d->find->deleteLater();
979 if( document()->isEmpty() )
986 connect( d->findDlg, SIGNAL(okClicked()),
this, SLOT(
slotDoFind()) );
994 if( document()->isEmpty() )
1002 connect( d->repDlg, SIGNAL(okClicked()),
this, SLOT(
slotDoReplace()) );
1009 d->findReplaceEnabled = enabled;
1014 d->spellInterface = spellInterface;
1017 bool KTextEdit::Private::overrideShortcut(
const QKeyEvent* event)
1019 const int key =
event->key() |
event->modifiers();
1061 }
else if (event->modifiers() == Qt::ControlModifier &&
1062 (
event->key() == Qt::Key_Return ||
event->key() == Qt::Key_Enter) &&
1063 qobject_cast<KDialog*>(parent->window()) ) {
1072 if (d->handleShortcut(event)) {
1074 }
else if (event->modifiers() == Qt::ControlModifier &&
1075 (
event->key() == Qt::Key_Return ||
event->key() == Qt::Key_Enter) &&
1076 qobject_cast<KDialog*>(window()) ) {
1085 if (msg != d->clickMessage) {
1086 if (!d->clickMessage.isEmpty()) {
1087 d->updateClickMessageRect();
1089 d->clickMessage = msg;
1090 if (!d->clickMessage.isEmpty()) {
1091 d->updateClickMessageRect();
1098 return d->clickMessage;
1105 if (!d->clickMessage.isEmpty() && !hasFocus() && document()->isEmpty()) {
1106 QPainter p(viewport());
1109 f.setItalic(d->italicizePlaceholder);
1112 QColor color(palette().color(viewport()->foregroundRole()));
1113 color.setAlphaF(0.5);
1116 int margin = int(document()->documentMargin());
1117 QRect cr = viewport()->rect().adjusted(margin, margin, -margin, -margin);
1119 p.drawText(cr, Qt::AlignTop | Qt::TextWordWrap, d->clickMessage);
1125 if (!d->clickMessage.isEmpty()) {
1126 d->updateClickMessageRect();
1131 #include "ktextedit.moc"