• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • widgets
ktextedit.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
3  2005 Michael Brade <brade@kde.org>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "ktextedit.h"
22 #include <ktoolinvocation.h>
23 #include <kdebug.h>
24 
25 #include <QApplication>
26 #include <QClipboard>
27 #include <QKeyEvent>
28 #include <QMenu>
29 #include <QPainter>
30 #include <QScrollBar>
31 #include <QTextCursor>
32 #include <QTextDocumentFragment>
33 #include <QDBusInterface>
34 #include <QDBusConnection>
35 #include <QDBusConnectionInterface>
36 
37 #include <configdialog.h>
38 #include <dialog.h>
39 #include "backgroundchecker.h"
40 #include <kaction.h>
41 #include <kcursor.h>
42 #include <kglobalsettings.h>
43 #include <kstandardaction.h>
44 #include <kstandardshortcut.h>
45 #include <kicon.h>
46 #include <kiconloader.h>
47 #include <klocale.h>
48 #include <kdialog.h>
49 #include <kreplacedialog.h>
50 #include <kfinddialog.h>
51 #include <kfind.h>
52 #include <kreplace.h>
53 #include <kmessagebox.h>
54 #include <kmenu.h>
55 #include <kwindowsystem.h>
56 #include <QDebug>
57 
58 class KTextEdit::Private
59 {
60  public:
61  Private( KTextEdit *_parent )
62  : parent( _parent ),
63  customPalette( false ),
64  checkSpellingEnabled( false ),
65  findReplaceEnabled(true),
66  highlighter( 0 ), findDlg(0),find(0),repDlg(0),replace(0), findIndex(0), repIndex(0),
67  lastReplacedPosition(-1)
68  {
69  //Check the default sonnet settings to see if spellchecking should be enabled.
70  sonnetKConfig = new KConfig("sonnetrc");
71  KConfigGroup group(sonnetKConfig, "Spelling");
72  checkSpellingEnabled = group.readEntry("checkerEnabledByDefault", false);
73 
74  // i18n: Placeholder text in text edit widgets is the text appearing
75  // before any user input, briefly explaining to the user what to type
76  // (e.g. "Enter message").
77  // By default the text is set in italic, which may not be appropriate
78  // for some languages and scripts (e.g. for CJK ideographs).
79  QString metaMsg = i18nc("Italic placeholder text in line edits: 0 no, 1 yes", "1");
80  italicizePlaceholder = (metaMsg.trimmed() != QString('0'));
81  }
82 
83  ~Private()
84  {
85  delete highlighter;
86  delete findDlg;
87  delete find;
88  delete replace;
89  delete repDlg;
90  delete sonnetKConfig;
91  }
92 
98  bool overrideShortcut(const QKeyEvent* e);
102  bool handleShortcut(const QKeyEvent* e);
103 
104  void spellCheckerMisspelling( const QString &text, int pos );
105  void spellCheckerCorrected( const QString &, int,const QString &);
106  void spellCheckerAutoCorrect(const QString&,const QString&);
107  void spellCheckerCanceled();
108  void spellCheckerFinished();
109  void toggleAutoSpellCheck();
110 
111  void slotFindHighlight(const QString& text, int matchingIndex, int matchingLength);
112  void slotReplaceText(const QString &text, int replacementIndex, int /*replacedLength*/, int matchedLength);
113 
118  void undoableClear();
119 
120  void slotAllowTab();
121  void menuActivated( QAction* action );
122 
123  void updateClickMessageRect();
124 
125  void init();
126 
127  KTextEdit *parent;
128  KTextEditSpellInterface *spellInterface;
129  QAction *autoSpellCheckAction;
130  QAction *allowTab;
131  QAction *spellCheckAction;
132  QString clickMessage;
133  bool italicizePlaceholder : 1;
134  bool customPalette : 1;
135 
136  bool checkSpellingEnabled : 1;
137  bool findReplaceEnabled: 1;
138  QTextDocumentFragment originalDoc;
139  QString spellCheckingConfigFileName;
140  QString spellCheckingLanguage;
141  Sonnet::Highlighter *highlighter;
142  KFindDialog *findDlg;
143  KFind *find;
144  KReplaceDialog *repDlg;
145  KReplace *replace;
146  int findIndex, repIndex;
147  int lastReplacedPosition;
148  KConfig *sonnetKConfig;
149 };
150 
151 void KTextEdit::Private::spellCheckerCanceled()
152 {
153  QTextDocument *doc = parent->document();
154  doc->clear();
155  QTextCursor cursor(doc);
156  cursor.insertFragment(originalDoc);
157  spellCheckerFinished();
158 }
159 
160 void KTextEdit::Private::spellCheckerAutoCorrect(const QString&,const QString&)
161 {
162  //TODO
163 }
164 
165 void KTextEdit::Private::spellCheckerMisspelling( const QString &text, int pos )
166 {
167  //kDebug()<<"TextEdit::Private::spellCheckerMisspelling :"<<text<<" pos :"<<pos;
168  parent->highlightWord( text.length(), pos );
169 }
170 
171 void KTextEdit::Private::spellCheckerCorrected( const QString& oldWord, int pos,const QString& newWord)
172 {
173  //kDebug()<<" oldWord :"<<oldWord<<" newWord :"<<newWord<<" pos : "<<pos;
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);
179  }
180 }
181 
182 void KTextEdit::Private::spellCheckerFinished()
183 {
184  QTextCursor cursor(parent->document());
185  cursor.clearSelection();
186  parent->setTextCursor(cursor);
187  if (parent->highlighter())
188  parent->highlighter()->rehighlight();
189 }
190 
191 void KTextEdit::Private::toggleAutoSpellCheck()
192 {
193  parent->setCheckSpellingEnabled( !parent->checkSpellingEnabled() );
194 }
195 
196 void KTextEdit::Private::undoableClear()
197 {
198  QTextCursor cursor = parent->textCursor();
199  cursor.beginEditBlock();
200  cursor.movePosition(QTextCursor::Start);
201  cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
202  cursor.removeSelectedText();
203  cursor.endEditBlock();
204 }
205 
206 void KTextEdit::Private::slotAllowTab()
207 {
208  parent->setTabChangesFocus( !parent->tabChangesFocus() );
209 }
210 
211 void KTextEdit::Private::menuActivated( QAction* action )
212 {
213  if ( action == spellCheckAction )
214  parent->checkSpelling();
215  else if ( action == autoSpellCheckAction )
216  toggleAutoSpellCheck();
217  else if ( action == allowTab )
218  slotAllowTab();
219 }
220 
221 
222 void KTextEdit::Private::slotFindHighlight(const QString& text, int matchingIndex, int matchingLength)
223 {
224  Q_UNUSED(text)
225  //kDebug() << "Highlight: [" << text << "] mi:" << matchingIndex << " ml:" << 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();
231 }
232 
233 
234 void KTextEdit::Private::slotReplaceText(const QString &text, int replacementIndex, int replacedLength, int matchedLength) {
235  //kDebug() << "Replace: [" << text << "] ri:" << replacementIndex << " rl:" << replacedLength << " ml:" << 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));
241  if (replace->options() & KReplaceDialog::PromptOnReplace) {
242  parent->setTextCursor(tc);
243  parent->ensureCursorVisible();
244  }
245  lastReplacedPosition = replacementIndex;
246 }
247 
248 void KTextEdit::Private::updateClickMessageRect()
249 {
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);
254 }
255 
256 void KTextEdit::Private::init()
257 {
258  spellInterface = 0;
259  KCursor::setAutoHideCursor(parent, true, false);
260  parent->connect(parent, SIGNAL(languageChanged(QString)),
261  parent, SLOT(setSpellCheckingLanguage(QString)));
262 }
263 
264 KTextEdit::KTextEdit( const QString& text, QWidget *parent )
265  : QTextEdit( text, parent ), d( new Private( this ) )
266 {
267  d->init();
268 }
269 
270 KTextEdit::KTextEdit( QWidget *parent )
271  : QTextEdit( parent ), d( new Private( this ) )
272 {
273  d->init();
274 }
275 
276 KTextEdit::~KTextEdit()
277 {
278  delete d;
279 }
280 
281 void KTextEdit::setSpellCheckingConfigFileName(const QString &_fileName)
282 {
283  d->spellCheckingConfigFileName = _fileName;
284 }
285 
286 const QString& KTextEdit::spellCheckingLanguage() const
287 {
288  return d->spellCheckingLanguage;
289 }
290 
291 void KTextEdit::setSpellCheckingLanguage(const QString &_language)
292 {
293  if (highlighter()) {
294  highlighter()->setCurrentLanguage(_language);
295  highlighter()->rehighlight();
296  }
297 
298  if (_language != d->spellCheckingLanguage) {
299  d->spellCheckingLanguage = _language;
300  emit languageChanged(_language);
301  }
302  else
303  d->spellCheckingLanguage = _language;
304 }
305 
306 void KTextEdit::showSpellConfigDialog(const QString &configFileName,
307  const QString &windowIcon)
308 {
309  KConfig config(configFileName);
310  Sonnet::ConfigDialog dialog(&config, this);
311  if (!d->spellCheckingLanguage.isEmpty())
312  dialog.setLanguage(d->spellCheckingLanguage);
313  if (!windowIcon.isEmpty())
314  dialog.setWindowIcon(KIcon(windowIcon));
315  if(dialog.exec()) {
316  setSpellCheckingLanguage( dialog.language() );
317  }
318 }
319 
320 bool KTextEdit::event(QEvent* ev)
321 {
322  if (ev->type() == QEvent::ShortcutOverride) {
323  QKeyEvent *e = static_cast<QKeyEvent *>( ev );
324  if (d->overrideShortcut(e)) {
325  e->accept();
326  return true;
327  }
328  }
329  return QTextEdit::event(ev);
330 }
331 
332 bool KTextEdit::Private::handleShortcut(const QKeyEvent* event)
333 {
334  const int key = event->key() | event->modifiers();
335 
336  if ( KStandardShortcut::copy().contains( key ) ) {
337  parent->copy();
338  return true;
339  } else if ( KStandardShortcut::paste().contains( key ) ) {
340  parent->paste();
341  return true;
342  } else if ( KStandardShortcut::cut().contains( key ) ) {
343  parent->cut();
344  return true;
345  } else if ( KStandardShortcut::undo().contains( key ) ) {
346  if(!parent->isReadOnly())
347  parent->undo();
348  return true;
349  } else if ( KStandardShortcut::redo().contains( key ) ) {
350  if(!parent->isReadOnly())
351  parent->redo();
352  return true;
353  } else if ( KStandardShortcut::deleteWordBack().contains( key ) ) {
354  parent->deleteWordBack();
355  return true;
356  } else if ( KStandardShortcut::deleteWordForward().contains( key ) ) {
357  parent->deleteWordForward();
358  return true;
359  } else if ( KStandardShortcut::backwardWord().contains( key ) ) {
360  QTextCursor cursor = parent->textCursor();
361  cursor.movePosition( QTextCursor::PreviousWord );
362  parent->setTextCursor( cursor );
363  return true;
364  } else if ( KStandardShortcut::forwardWord().contains( key ) ) {
365  QTextCursor cursor = parent->textCursor();
366  cursor.movePosition( QTextCursor::NextWord );
367  parent->setTextCursor( cursor );
368  return true;
369  } else if ( KStandardShortcut::next().contains( key ) ) {
370  QTextCursor cursor = parent->textCursor();
371  bool moved = false;
372  qreal lastY = parent->cursorRect(cursor).bottom();
373  qreal distance = 0;
374  do {
375  qreal y = parent->cursorRect(cursor).bottom();
376  distance += qAbs(y - lastY);
377  lastY = y;
378  moved = cursor.movePosition(QTextCursor::Down);
379  } while (moved && distance < parent->viewport()->height());
380 
381  if (moved) {
382  cursor.movePosition(QTextCursor::Up);
383  parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
384  }
385  parent->setTextCursor(cursor);
386  return true;
387  } else if ( KStandardShortcut::prior().contains( key ) ) {
388  QTextCursor cursor = parent->textCursor();
389  bool moved = false;
390  qreal lastY = parent->cursorRect(cursor).bottom();
391  qreal distance = 0;
392  do {
393  qreal y = parent->cursorRect(cursor).bottom();
394  distance += qAbs(y - lastY);
395  lastY = y;
396  moved = cursor.movePosition(QTextCursor::Up);
397  } while (moved && distance < parent->viewport()->height());
398 
399  if (moved) {
400  cursor.movePosition(QTextCursor::Down);
401  parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
402  }
403  parent->setTextCursor(cursor);
404  return true;
405  } else if ( KStandardShortcut::begin().contains( key ) ) {
406  QTextCursor cursor = parent->textCursor();
407  cursor.movePosition( QTextCursor::Start );
408  parent->setTextCursor( cursor );
409  return true;
410  } else if ( KStandardShortcut::end().contains( key ) ) {
411  QTextCursor cursor = parent->textCursor();
412  cursor.movePosition( QTextCursor::End );
413  parent->setTextCursor( cursor );
414  return true;
415  } else if ( KStandardShortcut::beginningOfLine().contains( key ) ) {
416  QTextCursor cursor = parent->textCursor();
417  cursor.movePosition( QTextCursor::StartOfLine );
418  parent->setTextCursor( cursor );
419  return true;
420  } else if ( KStandardShortcut::endOfLine().contains( key ) ) {
421  QTextCursor cursor = parent->textCursor();
422  cursor.movePosition( QTextCursor::EndOfLine );
423  parent->setTextCursor( cursor );
424  return true;
425  } else if (findReplaceEnabled && KStandardShortcut::find().contains(key)) {
426  parent->slotFind();
427  return true;
428  } else if (findReplaceEnabled && KStandardShortcut::findNext().contains(key)) {
429  parent->slotFindNext();
430  return true;
431  } else if (findReplaceEnabled && KStandardShortcut::replace().contains(key)) {
432  if (!parent->isReadOnly())
433  parent->slotReplace();
434  return true;
435  } else if ( KStandardShortcut::pasteSelection().contains( key ) ) {
436  QString text = QApplication::clipboard()->text( QClipboard::Selection );
437  if ( !text.isEmpty() )
438  parent->insertPlainText( text ); // TODO: check if this is html? (MiB)
439  return true;
440  }
441  return false;
442 }
443 
444 static void deleteWord(QTextCursor cursor, QTextCursor::MoveOperation op)
445 {
446  cursor.clearSelection();
447  cursor.movePosition( op, QTextCursor::KeepAnchor );
448  cursor.removeSelectedText();
449 }
450 
451 void KTextEdit::deleteWordBack()
452 {
453  deleteWord(textCursor(), QTextCursor::PreviousWord);
454 }
455 
456 void KTextEdit::deleteWordForward()
457 {
458  deleteWord(textCursor(), QTextCursor::WordRight);
459 }
460 
461 QMenu *KTextEdit::mousePopupMenu()
462 {
463  QMenu *popup = createStandardContextMenu();
464  if (!popup) return 0;
465  connect( popup, SIGNAL(triggered(QAction*)),
466  this, SLOT(menuActivated(QAction*)) );
467 
468  const bool emptyDocument = document()->isEmpty();
469  if( !isReadOnly() )
470  {
471  QList<QAction *> actionList = popup->actions();
472  enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
473  QAction *separatorAction = 0L;
474  int idx = actionList.indexOf( actionList[SelectAllAct] ) + 1;
475  if ( idx < actionList.count() )
476  separatorAction = actionList.at( idx );
477  if ( separatorAction )
478  {
479  KAction *clearAllAction = KStandardAction::clear(this, SLOT(undoableClear()), popup);
480  if ( emptyDocument )
481  clearAllAction->setEnabled( false );
482  popup->insertAction( separatorAction, clearAllAction );
483  }
484  }
485  KIconTheme::assignIconsToContextMenu( isReadOnly() ? KIconTheme::ReadOnlyText
486  : KIconTheme::TextEditor,
487  popup->actions() );
488 
489  if( !isReadOnly() )
490  {
491  popup->addSeparator();
492  d->spellCheckAction = popup->addAction( KIcon( "tools-check-spelling" ),
493  i18n( "Check Spelling..." ) );
494  if ( emptyDocument )
495  d->spellCheckAction->setEnabled( false );
496  d->autoSpellCheckAction = popup->addAction( i18n( "Auto Spell Check" ) );
497  d->autoSpellCheckAction->setCheckable( true );
498  d->autoSpellCheckAction->setChecked( checkSpellingEnabled() );
499  popup->addSeparator();
500  d->allowTab = popup->addAction( i18n("Allow Tabulations") );
501  d->allowTab->setCheckable( true );
502  d->allowTab->setChecked( !tabChangesFocus() );
503  }
504 
505  if (d->findReplaceEnabled) {
506  KAction *findAction = KStandardAction::find(this, SLOT(slotFind()), popup);
507  KAction *findNextAction = KStandardAction::findNext(this, SLOT(slotFindNext()), popup);
508  if (emptyDocument) {
509  findAction->setEnabled(false);
510  findNextAction->setEnabled(false);
511  } else {
512  findNextAction->setEnabled(d->find != 0);
513  }
514  popup->addSeparator();
515  popup->addAction(findAction);
516  popup->addAction(findNextAction);
517 
518  if (!isReadOnly()) {
519  KAction *replaceAction = KStandardAction::replace(this, SLOT(slotReplace()), popup);
520  if (emptyDocument) {
521  replaceAction->setEnabled(false);
522  }
523  popup->addAction(replaceAction);
524  }
525  }
526  popup->addSeparator();
527  QAction *speakAction = popup->addAction(i18n("Speak Text"));
528  speakAction->setIcon(KIcon("preferences-desktop-text-to-speech"));
529  speakAction->setEnabled(!emptyDocument );
530  connect( speakAction, SIGNAL(triggered(bool)), this, SLOT(slotSpeakText()) );
531  return popup;
532 }
533 
534 void KTextEdit::slotSpeakText()
535 {
536  // If KTTSD not running, start it.
537  if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kttsd"))
538  {
539  QString error;
540  if (KToolInvocation::startServiceByDesktopName("kttsd", QStringList(), &error))
541  {
542  KMessageBox::error(this, i18n( "Starting Jovie Text-to-Speech Service Failed"), error );
543  return;
544  }
545  }
546  QDBusInterface ktts("org.kde.kttsd", "/KSpeech", "org.kde.KSpeech");
547  QString text;
548  if(textCursor().hasSelection())
549  text = textCursor().selectedText();
550  else
551  text = toPlainText();
552  ktts.asyncCall("say", text, 0);
553 }
554 
555 void KTextEdit::contextMenuEvent(QContextMenuEvent *event)
556 {
557  // Obtain the cursor at the mouse position and the current cursor
558  QTextCursor cursorAtMouse = cursorForPosition(event->pos());
559  const int mousePos = cursorAtMouse.position();
560  QTextCursor cursor = textCursor();
561 
562  // Check if the user clicked a selected word
563  const bool selectedWordClicked = cursor.hasSelection() &&
564  mousePos >= cursor.selectionStart() &&
565  mousePos <= cursor.selectionEnd();
566 
567  // Get the word under the (mouse-)cursor and see if it is misspelled.
568  // Don't include apostrophes at the start/end of the word in the selection.
569  QTextCursor wordSelectCursor(cursorAtMouse);
570  wordSelectCursor.clearSelection();
571  wordSelectCursor.select(QTextCursor::WordUnderCursor);
572  QString selectedWord = wordSelectCursor.selectedText();
573 
574  bool isMouseCursorInsideWord = true;
575  if ((mousePos < wordSelectCursor.selectionStart() ||
576  mousePos >= wordSelectCursor.selectionEnd())
577  && (selectedWord.length() > 1)) {
578  isMouseCursorInsideWord = false;
579  }
580 
581  // Clear the selection again, we re-select it below (without the apostrophes).
582  wordSelectCursor.setPosition(wordSelectCursor.position()-selectedWord.size());
583  if (selectedWord.startsWith('\'') || selectedWord.startsWith('\"')) {
584  selectedWord = selectedWord.right(selectedWord.size() - 1);
585  wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
586  }
587  if (selectedWord.endsWith('\'') || selectedWord.endsWith('\"'))
588  selectedWord.chop(1);
589 
590  wordSelectCursor.movePosition(QTextCursor::NextCharacter,
591  QTextCursor::KeepAnchor, selectedWord.size());
592 
593  const bool wordIsMisspelled = isMouseCursorInsideWord &&
594  checkSpellingEnabled() &&
595  !selectedWord.isEmpty() &&
596  highlighter() &&
597  highlighter()->isWordMisspelled(selectedWord);
598 
599  // If the user clicked a selected word, do nothing.
600  // If the user clicked somewhere else, move the cursor there.
601  // If the user clicked on a misspelled word, select that word.
602  // Same behavior as in OpenOffice Writer.
603  bool inQuote = false;
604  if (d->spellInterface &&
605  !d->spellInterface->shouldBlockBeSpellChecked(cursorAtMouse.block().text()))
606  inQuote = true;
607  if (!selectedWordClicked) {
608  if (wordIsMisspelled && !inQuote)
609  setTextCursor(wordSelectCursor);
610  else
611  setTextCursor(cursorAtMouse);
612  cursor = textCursor();
613  }
614 
615  // Use standard context menu for already selected words, correctly spelled
616  // words and words inside quotes.
617  if (!wordIsMisspelled || selectedWordClicked || inQuote) {
618  QMenu *popup = mousePopupMenu();
619  if ( popup ) {
620  aboutToShowContextMenu(popup);
621  popup->exec( event->globalPos() );
622  delete popup;
623  }
624  }
625  else {
626  QMenu menu; //don't use KMenu here we don't want auto management accelerator
627 
628  //Add the suggestions to the menu
629  const QStringList reps = highlighter()->suggestionsForWord(selectedWord);
630  if (reps.isEmpty()) {
631  QAction *suggestionsAction = menu.addAction(i18n("No suggestions for %1", selectedWord));
632  suggestionsAction->setEnabled(false);
633  }
634  else {
635  for (QStringList::const_iterator it = reps.constBegin(); it != reps.constEnd(); ++it) {
636  menu.addAction(*it);
637  }
638  }
639 
640  menu.addSeparator();
641 
642  QAction *ignoreAction = menu.addAction(i18n("Ignore"));
643  QAction *addToDictAction = menu.addAction(i18n("Add to Dictionary"));
644  //Execute the popup inline
645  const QAction *selectedAction = menu.exec(event->globalPos());
646 
647  if (selectedAction) {
648  Q_ASSERT(cursor.selectedText() == selectedWord);
649 
650  if (selectedAction == ignoreAction) {
651  highlighter()->ignoreWord(selectedWord);
652  highlighter()->rehighlight();
653  }
654  else if (selectedAction == addToDictAction) {
655  highlighter()->addWordToDictionary(selectedWord);
656  highlighter()->rehighlight();
657  }
658 
659  // Other actions can only be one of the suggested words
660  else {
661  const QString replacement = selectedAction->text();
662  Q_ASSERT(reps.contains(replacement));
663  cursor.insertText(replacement);
664  setTextCursor(cursor);
665  }
666  }
667  }
668 }
669 
670 void KTextEdit::wheelEvent( QWheelEvent *event )
671 {
672  if ( KGlobalSettings::wheelMouseZooms() )
673  QTextEdit::wheelEvent( event );
674  else // thanks, we don't want to zoom, so skip QTextEdit's impl.
675  QAbstractScrollArea::wheelEvent( event );
676 }
677 
678 void KTextEdit::createHighlighter()
679 {
680  setHighlighter(new Sonnet::Highlighter(this, d->spellCheckingConfigFileName));
681 }
682 
683 Sonnet::Highlighter* KTextEdit::highlighter() const
684 {
685  return d->highlighter;
686 }
687 
688 void KTextEdit::setHighlighter(Sonnet::Highlighter *_highLighter)
689 {
690  delete d->highlighter;
691  d->highlighter = _highLighter;
692 }
693 
694 void KTextEdit::setCheckSpellingEnabled(bool check)
695 {
696  if (d->spellInterface)
697  d->spellInterface->setSpellCheckingEnabled(check);
698  else
699  setCheckSpellingEnabledInternal(check);
700 }
701 
702 void KTextEdit::setCheckSpellingEnabledInternal( bool check )
703 {
704  emit checkSpellingChanged( check );
705  if ( check == d->checkSpellingEnabled )
706  return;
707 
708  // From the above statment we know know that if we're turning checking
709  // on that we need to create a new highlighter and if we're turning it
710  // off we should remove the old one.
711 
712  d->checkSpellingEnabled = check;
713  if ( check )
714  {
715  if ( hasFocus() ) {
716  createHighlighter();
717  if (!spellCheckingLanguage().isEmpty())
718  setSpellCheckingLanguage(spellCheckingLanguage());
719  }
720  }
721  else
722  {
723  delete d->highlighter;
724  d->highlighter = 0;
725  }
726 }
727 
728 void KTextEdit::focusInEvent( QFocusEvent *event )
729 {
730  if ( d->checkSpellingEnabled && !isReadOnly() && !d->highlighter )
731  createHighlighter();
732 
733  if (!d->clickMessage.isEmpty()) {
734  d->updateClickMessageRect();
735  }
736  QTextEdit::focusInEvent( event );
737 }
738 
739 bool KTextEdit::checkSpellingEnabled() const
740 {
741  if (d->spellInterface)
742  return d->spellInterface->isSpellCheckingEnabled();
743  else
744  return checkSpellingEnabledInternal();
745 }
746 
747 bool KTextEdit::checkSpellingEnabledInternal() const
748 {
749  return d->checkSpellingEnabled;
750 }
751 
752 void KTextEdit::setReadOnly( bool readOnly )
753 {
754  if ( !readOnly && hasFocus() && d->checkSpellingEnabled && !d->highlighter )
755  createHighlighter();
756 
757  if ( readOnly == isReadOnly() )
758  return;
759 
760  if ( readOnly ) {
761  delete d->highlighter;
762  d->highlighter = 0;
763 
764  d->customPalette = testAttribute( Qt::WA_SetPalette );
765  QPalette p = palette();
766  QColor color = p.color( QPalette::Disabled, QPalette::Background );
767  p.setColor( QPalette::Base, color );
768  p.setColor( QPalette::Background, color );
769  setPalette( p );
770  } else {
771  if ( d->customPalette && testAttribute( Qt::WA_SetPalette ) ) {
772  QPalette p = palette();
773  QColor color = p.color( QPalette::Normal, QPalette::Base );
774  p.setColor( QPalette::Base, color );
775  p.setColor( QPalette::Background, color );
776  setPalette( p );
777  } else
778  setPalette( QPalette() );
779  }
780 
781  QTextEdit::setReadOnly( readOnly );
782 }
783 
784 void KTextEdit::checkSpelling()
785 {
786  if(document()->isEmpty())
787  {
788  KMessageBox::information(this, i18n("Nothing to spell check."));
789  return;
790  }
791  Sonnet::BackgroundChecker *backgroundSpellCheck = new Sonnet::BackgroundChecker(this);
792  if(!d->spellCheckingLanguage.isEmpty())
793  backgroundSpellCheck->changeLanguage(d->spellCheckingLanguage);
794  Sonnet::Dialog *spellDialog = new Sonnet::Dialog(
795  backgroundSpellCheck, 0);
796  connect(spellDialog, SIGNAL(replace(QString,int,QString)),
797  this, SLOT(spellCheckerCorrected(QString,int,QString)));
798  connect(spellDialog, SIGNAL(misspelling(QString,int)),
799  this, SLOT(spellCheckerMisspelling(QString,int)));
800  connect(spellDialog, SIGNAL(autoCorrect(QString,QString)),
801  this, SLOT(spellCheckerAutoCorrect(QString,QString)));
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()));
808  connect(spellDialog, SIGNAL(spellCheckStatus(QString)),
809  this,SIGNAL(spellCheckStatus(QString)));
810  connect(spellDialog, SIGNAL(languageChanged(QString)),
811  this, SIGNAL(languageChanged(QString)));
812  d->originalDoc = QTextDocumentFragment(document());
813  spellDialog->setBuffer(toPlainText());
814  spellDialog->show();
815 }
816 
817 void KTextEdit::highlightWord( int length, int pos )
818 {
819  QTextCursor cursor(document());
820  cursor.setPosition(pos);
821  cursor.setPosition(pos+length,QTextCursor::KeepAnchor);
822  setTextCursor (cursor);
823  ensureCursorVisible();
824 }
825 
826 void KTextEdit::replace()
827 {
828  if( document()->isEmpty() ) // saves having to track the text changes
829  return;
830 
831  if ( d->repDlg ) {
832  KWindowSystem::activateWindow( d->repDlg->winId() );
833  } else {
834  d->repDlg = new KReplaceDialog(this, 0,
835  QStringList(), QStringList(), false);
836  connect( d->repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
837  }
838  d->repDlg->show();
839 }
840 
841 void KTextEdit::slotDoReplace()
842 {
843  if (!d->repDlg) {
844  // Should really assert()
845  return;
846  }
847 
848  if(d->repDlg->pattern().isEmpty()) {
849  delete d->replace;
850  d->replace = 0;
851  ensureCursorVisible();
852  return;
853  }
854 
855  delete d->replace;
856  d->replace = new KReplace(d->repDlg->pattern(), d->repDlg->replacement(), d->repDlg->options(), this);
857  d->repIndex = 0;
858  if (d->replace->options() & KFind::FromCursor || d->replace->options() & KFind::FindBackwards) {
859  d->repIndex = textCursor().anchor();
860  }
861 
862  // Connect highlight signal to code which handles highlighting
863  // of found text.
864  connect(d->replace, SIGNAL(highlight(QString,int,int)),
865  this, SLOT(slotFindHighlight(QString,int,int)));
866  connect(d->replace, SIGNAL(findNext()), this, SLOT(slotReplaceNext()));
867  connect(d->replace, SIGNAL(replace(QString,int,int,int)),
868  this, SLOT(slotReplaceText(QString,int,int,int)));
869 
870  d->repDlg->close();
871  slotReplaceNext();
872 }
873 
874 
875 void KTextEdit::slotReplaceNext()
876 {
877  if (!d->replace)
878  return;
879 
880  d->lastReplacedPosition = -1;
881  if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) {
882  textCursor().beginEditBlock(); // #48541
883  viewport()->setUpdatesEnabled(false);
884  }
885 
886  KFind::Result res = KFind::NoMatch;
887 
888  if (d->replace->needData())
889  d->replace->setData(toPlainText(), d->repIndex);
890  res = d->replace->replace();
891  if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) {
892  textCursor().endEditBlock(); // #48541
893  if (d->lastReplacedPosition >= 0) {
894  QTextCursor tc = textCursor();
895  tc.setPosition(d->lastReplacedPosition);
896  setTextCursor(tc);
897  ensureCursorVisible();
898  }
899 
900  viewport()->setUpdatesEnabled(true);
901  viewport()->update();
902  }
903 
904  if (res == KFind::NoMatch) {
905  d->replace->displayFinalDialog();
906  d->replace->disconnect(this);
907  d->replace->deleteLater(); // we are in a slot connected to m_replace, don't delete it right away
908  d->replace = 0;
909  ensureCursorVisible();
910  //or if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); }
911  } else {
912  //m_replace->closeReplaceNextDialog();
913  }
914 }
915 
916 
917 void KTextEdit::slotDoFind()
918 {
919  if (!d->findDlg) {
920  // Should really assert()
921  return;
922  }
923  if( d->findDlg->pattern().isEmpty())
924  {
925  delete d->find;
926  d->find = 0;
927  return;
928  }
929  delete d->find;
930  d->find = new KFind(d->findDlg->pattern(), d->findDlg->options(), this);
931  d->findIndex = 0;
932  if (d->find->options() & KFind::FromCursor || d->find->options() & KFind::FindBackwards) {
933  d->findIndex = textCursor().anchor();
934  }
935 
936  // Connect highlight signal to code which handles highlighting
937  // of found text.
938  connect(d->find, SIGNAL(highlight(QString,int,int)),
939  this, SLOT(slotFindHighlight(QString,int,int)));
940  connect(d->find, SIGNAL(findNext()), this, SLOT(slotFindNext()));
941 
942  d->findDlg->close();
943  d->find->closeFindNextDialog();
944  slotFindNext();
945 }
946 
947 
948 void KTextEdit::slotFindNext()
949 {
950  if (!d->find)
951  return;
952  if(document()->isEmpty())
953  {
954  d->find->disconnect(this);
955  d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away
956  d->find = 0;
957  return;
958  }
959 
960  KFind::Result res = KFind::NoMatch;
961  if (d->find->needData())
962  d->find->setData(toPlainText(), d->findIndex);
963  res = d->find->find();
964 
965  if (res == KFind::NoMatch) {
966  d->find->displayFinalDialog();
967  d->find->disconnect(this);
968  d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away
969  d->find = 0;
970  //or if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); }
971  } else {
972  //m_find->closeFindNextDialog();
973  }
974 }
975 
976 
977 void KTextEdit::slotFind()
978 {
979  if( document()->isEmpty() ) // saves having to track the text changes
980  return;
981 
982  if ( d->findDlg ) {
983  KWindowSystem::activateWindow( d->findDlg->winId() );
984  } else {
985  d->findDlg = new KFindDialog(this);
986  connect( d->findDlg, SIGNAL(okClicked()), this, SLOT(slotDoFind()) );
987  }
988  d->findDlg->show();
989 }
990 
991 
992 void KTextEdit::slotReplace()
993 {
994  if( document()->isEmpty() ) // saves having to track the text changes
995  return;
996 
997  if ( d->repDlg ) {
998  KWindowSystem::activateWindow( d->repDlg->winId() );
999  } else {
1000  d->repDlg = new KReplaceDialog(this, 0,
1001  QStringList(), QStringList(), false);
1002  connect( d->repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
1003  }
1004  d->repDlg->show();
1005 }
1006 
1007 void KTextEdit::enableFindReplace( bool enabled )
1008 {
1009  d->findReplaceEnabled = enabled;
1010 }
1011 
1012 void KTextEdit::setSpellInterface(KTextEditSpellInterface *spellInterface)
1013 {
1014  d->spellInterface = spellInterface;
1015 }
1016 
1017 bool KTextEdit::Private::overrideShortcut(const QKeyEvent* event)
1018 {
1019  const int key = event->key() | event->modifiers();
1020 
1021  if ( KStandardShortcut::copy().contains( key ) ) {
1022  return true;
1023  } else if ( KStandardShortcut::paste().contains( key ) ) {
1024  return true;
1025  } else if ( KStandardShortcut::cut().contains( key ) ) {
1026  return true;
1027  } else if ( KStandardShortcut::undo().contains( key ) ) {
1028  return true;
1029  } else if ( KStandardShortcut::redo().contains( key ) ) {
1030  return true;
1031  } else if ( KStandardShortcut::deleteWordBack().contains( key ) ) {
1032  return true;
1033  } else if ( KStandardShortcut::deleteWordForward().contains( key ) ) {
1034  return true;
1035  } else if ( KStandardShortcut::backwardWord().contains( key ) ) {
1036  return true;
1037  } else if ( KStandardShortcut::forwardWord().contains( key ) ) {
1038  return true;
1039  } else if ( KStandardShortcut::next().contains( key ) ) {
1040  return true;
1041  } else if ( KStandardShortcut::prior().contains( key ) ) {
1042  return true;
1043  } else if ( KStandardShortcut::begin().contains( key ) ) {
1044  return true;
1045  } else if ( KStandardShortcut::end().contains( key ) ) {
1046  return true;
1047  } else if ( KStandardShortcut::beginningOfLine().contains( key ) ) {
1048  return true;
1049  } else if ( KStandardShortcut::endOfLine().contains( key ) ) {
1050  return true;
1051  } else if ( KStandardShortcut::pasteSelection().contains( key ) ) {
1052  return true;
1053  } else if (findReplaceEnabled && KStandardShortcut::find().contains(key)) {
1054  return true;
1055  } else if (findReplaceEnabled && KStandardShortcut::findNext().contains(key)) {
1056  return true;
1057  } else if (findReplaceEnabled && KStandardShortcut::replace().contains(key)) {
1058  return true;
1059  } else if (event->matches(QKeySequence::SelectAll)) { // currently missing in QTextEdit
1060  return true;
1061  } else if (event->modifiers() == Qt::ControlModifier &&
1062  (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) &&
1063  qobject_cast<KDialog*>(parent->window()) ) {
1064  // ignore Ctrl-Return so that KDialogs can close the dialog
1065  return true;
1066  }
1067  return false;
1068 }
1069 
1070 void KTextEdit::keyPressEvent( QKeyEvent *event )
1071 {
1072  if (d->handleShortcut(event)) {
1073  event->accept();
1074  }else if (event->modifiers() == Qt::ControlModifier &&
1075  (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) &&
1076  qobject_cast<KDialog*>(window()) ) {
1077  event->ignore();
1078  } else {
1079  QTextEdit::keyPressEvent(event);
1080  }
1081 }
1082 
1083 void KTextEdit::setClickMessage(const QString &msg)
1084 {
1085  if (msg != d->clickMessage) {
1086  if (!d->clickMessage.isEmpty()) {
1087  d->updateClickMessageRect();
1088  }
1089  d->clickMessage = msg;
1090  if (!d->clickMessage.isEmpty()) {
1091  d->updateClickMessageRect();
1092  }
1093  }
1094 }
1095 
1096 QString KTextEdit::clickMessage() const
1097 {
1098  return d->clickMessage;
1099 }
1100 
1101 void KTextEdit::paintEvent(QPaintEvent *ev)
1102 {
1103  QTextEdit::paintEvent(ev);
1104 
1105  if (!d->clickMessage.isEmpty() && !hasFocus() && document()->isEmpty()) {
1106  QPainter p(viewport());
1107 
1108  QFont f = font();
1109  f.setItalic(d->italicizePlaceholder);
1110  p.setFont(f);
1111 
1112  QColor color(palette().color(foregroundRole()));
1113  color.setAlphaF(0.5);
1114  p.setPen(color);
1115 
1116  int margin = int(document()->documentMargin());
1117  QRect cr = viewport()->rect().adjusted(margin, margin, -margin, -margin);
1118 
1119  p.drawText(cr, Qt::AlignTop | Qt::TextWordWrap, d->clickMessage);
1120  }
1121 }
1122 
1123 void KTextEdit::focusOutEvent(QFocusEvent *ev)
1124 {
1125  if (!d->clickMessage.isEmpty()) {
1126  d->updateClickMessageRect();
1127  }
1128  QTextEdit::focusOutEvent(ev);
1129 }
1130 
1131 #include "ktextedit.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Thu Feb 21 2013 11:06:56 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal