// kmcomposewin.cpp // Author: Markus Wuebben // This code is published under the GPL. #include #include "kmedit.h" #include "kmlineeditspell.h" #define REALLY_WANT_KMCOMPOSEWIN_H #include "kmcomposewin.h" #undef REALLY_WANT_KMCOMPOSEWIN_H #include "kmmsgdict.h" #include "kmfolder.h" #include "kmcommands.h" #include using KPIM::MailListDrag; #include #include #include #include #include #include #include #include #include #include #include #include #include #include "globalsettings.h" #include "replyphrases.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include void KMEdit::contentsDragEnterEvent(TQDragEnterEvent *e) { if (e->provides(MailListDrag::format())) e->accept(true); else if (e->provides("image/png")) e->accept(); else return KEdit::contentsDragEnterEvent(e); } void KMEdit::contentsDragMoveEvent(TQDragMoveEvent *e) { if (e->provides(MailListDrag::format())) e->accept(); else if (e->provides("image/png")) e->accept(); else return KEdit::contentsDragMoveEvent(e); } void KMEdit::keyPressEvent( TQKeyEvent* e ) { if( e->key() == Key_Return ) { int line, col; getCursorPosition( &line, &col ); TQString lineText = text( line ); // returns line with additional trailing space (bug in TQt?), cut it off lineText.truncate( lineText.length() - 1 ); // special treatment of quoted lines only if the cursor is neither at // the begin nor at the end of the line if( ( col > 0 ) && ( col < int( lineText.length() ) ) ) { bool isQuotedLine = false; uint bot = 0; // bot = begin of text after quote indicators while( bot < lineText.length() ) { if( ( lineText[bot] == '>' ) || ( lineText[bot] == '|' ) ) { isQuotedLine = true; ++bot; } else if( lineText[bot].isSpace() ) { ++bot; } else { break; } } KEdit::keyPressEvent( e ); // duplicate quote indicators of the previous line before the new // line if the line actually contained text (apart from the quote // indicators) and the cursor is behind the quote indicators if( isQuotedLine && ( bot != lineText.length() ) && ( col >= int( bot ) ) ) { // The cursor position might have changed unpredictably if there was selected // text which got replaced by a new line, so we query it again: getCursorPosition( &line, &col ); TQString newLine = text( line ); // remove leading white space from the new line and instead // add the quote indicators of the previous line unsigned int leadingWhiteSpaceCount = 0; while( ( leadingWhiteSpaceCount < newLine.length() ) && newLine[leadingWhiteSpaceCount].isSpace() ) { ++leadingWhiteSpaceCount; } newLine = newLine.replace( 0, leadingWhiteSpaceCount, lineText.left( bot ) ); removeParagraph( line ); insertParagraph( newLine, line ); // place the cursor at the begin of the new line since // we assume that the user split the quoted line in order // to add a comment to the first part of the quoted line setCursorPosition( line, 0 ); } } else KEdit::keyPressEvent( e ); } else KEdit::keyPressEvent( e ); } void KMEdit::contentsDropEvent(TQDropEvent *e) { if (e->provides(MailListDrag::format())) { // Decode the list of serial numbers stored as the drag data TQByteArray serNums; MailListDrag::decode( e, serNums ); TQBuffer serNumBuffer(serNums); serNumBuffer.open(IO_ReadOnly); TQDataStream serNumStream(&serNumBuffer); TQ_UINT32 serNum; KMFolder *folder = 0; int idx; TQPtrList messageList; while (!serNumStream.atEnd()) { KMMsgBase *msgBase = 0; serNumStream >> serNum; KMMsgDict::instance()->getLocation(serNum, &folder, &idx); if (folder) msgBase = folder->getMsgBase(idx); if (msgBase) messageList.append( msgBase ); } serNumBuffer.close(); uint identity = folder ? folder->identity() : 0; KMCommand *command = new KMForwardAttachedCommand(mComposer, messageList, identity, mComposer); command->start(); } else if( e->provides("image/png") ) { emit attachPNGImageData(e->encodedData("image/png")); } else if( KURLDrag::canDecode( e ) ) { KURL::List urlList; if( KURLDrag::decode( e, urlList ) ) { TDEPopupMenu p; p.insertItem( i18n("Add as Text"), 0 ); p.insertItem( i18n("Add as Attachment"), 1 ); int id = p.exec( mapToGlobal( e->pos() ) ); switch ( id) { case 0: for ( KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it ) { insert( (*it).url() ); } break; case 1: for ( KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it ) { mComposer->addAttach( *it ); } break; } } else if ( TQTextDrag::canDecode( e ) ) { TQString s; if ( TQTextDrag::decode( e, s ) ) insert( s ); } else kdDebug(5006) << "KMEdit::contentsDropEvent, unable to add dropped object" << endl; } else if( e->provides("text/x-textsnippet") ) { emit insertSnippet(); } else { KEdit::contentsDropEvent(e); } } KMEdit::KMEdit(TQWidget *parent, KMComposeWin* composer, KSpellConfig* autoSpellConfig, const char *name) : KEdit( parent, name ), mComposer( composer ), mKSpellForDialog( 0 ), mSpeller( 0 ), mSpellConfig( autoSpellConfig ), mSpellingFilter( 0 ), mExtEditorTempFile( 0 ), mExtEditorTempFileWatcher( 0 ), mExtEditorProcess( 0 ), mUseExtEditor( false ), mWasModifiedBeforeSpellCheck( false ), mHighlighter( 0 ), mSpellLineEdit( false ), mPasteMode( TQClipboard::Clipboard ) { connect( this, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectionChanged()) ); installEventFilter(this); KCursor::setAutoHideCursor( this, true, true ); setOverwriteEnabled( true ); createSpellers(); connect( mSpellConfig, TQT_SIGNAL( configChanged() ), this, TQT_SLOT( createSpellers() ) ); connect( mSpeller, TQT_SIGNAL( death() ), this, TQT_SLOT( spellerDied() ) ); } void KMEdit::createSpellers() { delete mSpeller; mSpeller = new KMSpell( TQT_TQOBJECT(this), TQT_SLOT( spellerReady( KSpell * ) ), mSpellConfig ); } void KMEdit::initializeAutoSpellChecking() { if ( mHighlighter ) return; // already initialized TQColor defaultColor1( 0x00, 0x80, 0x00 ); // defaults from kmreaderwin.cpp TQColor defaultColor2( 0x00, 0x70, 0x00 ); TQColor defaultColor3( 0x00, 0x60, 0x00 ); TQColor defaultForeground( kapp->palette().active().text() ); TQColor c = TQt::red; TDEConfigGroup readerConfig( KMKernel::config(), "Reader" ); TQColor col1; if ( !readerConfig.readBoolEntry( "defaultColors", true ) ) col1 = readerConfig.readColorEntry( "ForegroundColor", &defaultForeground ); else col1 = defaultForeground; TQColor col2 = readerConfig.readColorEntry( "QuotedText3", &defaultColor3 ); TQColor col3 = readerConfig.readColorEntry( "QuotedText2", &defaultColor2 ); TQColor col4 = readerConfig.readColorEntry( "QuotedText1", &defaultColor1 ); TQColor misspelled = readerConfig.readColorEntry( "MisspelledColor", &c ); mHighlighter = new KMSyntaxHighter( this, /*active*/ true, /*autoEnabled*/ false, /*spellColor*/ misspelled, /*colorQuoting*/ true, col1, col2, col3, col4, mSpellConfig ); connect( mHighlighter, TQT_SIGNAL(newSuggestions(const TQString&, const TQStringList&, unsigned int)), this, TQT_SLOT(addSuggestion(const TQString&, const TQStringList&, unsigned int)) ); } TQPopupMenu *KMEdit::createPopupMenu( const TQPoint& pos ) { enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll }; TQPopupMenu *menu = KEdit::createPopupMenu( pos ); if ( !TQApplication::clipboard()->image().isNull() ) { int id = menu->idAt(0); menu->setItemEnabled( id - IdPaste, true); } return menu; } void KMEdit::deleteAutoSpellChecking() { // because the highlighter doesn't support RichText, delete its instance. delete mHighlighter; mHighlighter =0; } void KMEdit::addSuggestion(const TQString& text, const TQStringList& lst, unsigned int ) { mReplacements[text] = lst; } void KMEdit::setSpellCheckingActive(bool spellCheckingActive) { if ( mHighlighter ) { mHighlighter->setActive(spellCheckingActive); } } KMEdit::~KMEdit() { removeEventFilter(this); if ( mSpeller ) { // The speller needs some time to clean up, so trigger cleanup and let it delete itself mSpeller->setAutoDelete( true ); mSpeller->cleanUp(); mSpeller = 0; } delete mKSpellForDialog; delete mHighlighter; mHighlighter = 0; } TQString KMEdit::brokenText() { TQString temp, line; int num_lines = numLines(); for (int i = 0; i < num_lines; ++i) { int lastLine = 0; line = textLine(i); for (int j = 0; j < (int)line.length(); ++j) { if (lineOfChar(i, j) > lastLine) { lastLine = lineOfChar(i, j); temp += '\n'; } temp += line[j]; } if (i + 1 < num_lines) temp += '\n'; } return temp; } unsigned int KMEdit::lineBreakColumn() const { unsigned int lineBreakColumn = 0; unsigned int numlines = numLines(); while ( numlines-- ) { lineBreakColumn = TQMAX( lineBreakColumn, textLine( numlines ).length() ); } return lineBreakColumn; } KMSpell::KMSpell( TQObject *receiver, const char *slot, KSpellConfig *spellConfig ) : KSpell( 0, TQString(), receiver, slot, spellConfig ) { } KMSyntaxHighter::KMSyntaxHighter( TQTextEdit *textEdit, bool spellCheckingActive, bool autoEnable, const TQColor& spellColor, bool colorQuoting, const TQColor& QuoteColor0, const TQColor& QuoteColor1, const TQColor& QuoteColor2, const TQColor& QuoteColor3, KSpellConfig *spellConfig ) : KDictSpellingHighlighter( textEdit, spellCheckingActive, autoEnable, spellColor, colorQuoting, QuoteColor0, QuoteColor1, QuoteColor2, QuoteColor3, spellConfig ) { } bool KMSyntaxHighter::isMisspelled( const TQString &word ) { if ( mIgnoredWords.contains( word ) ) { return false; } else { return KDictSpellingHighlighter::isMisspelled( word ); } } void KMSyntaxHighter::ignoreWord( const TQString &word ) { mIgnoredWords << word; } TQStringList KMSyntaxHighter::ignoredWords() const { return mIgnoredWords; } void KMEdit::spellerDied() { mSpeller = 0; } void KMEdit::spellerReady( KSpell *spell ) { Q_ASSERT( mSpeller == spell ); } bool KMEdit::eventFilter(TQObject*o, TQEvent* e) { if (o == this) KCursor::autoHideEventFilter(o, e); if (e->type() == TQEvent::KeyPress) { TQKeyEvent *k = (TQKeyEvent*)e; if (mUseExtEditor) { if (k->key() == Key_Up) { emit focusUp(); return true; } // ignore modifier keys (cf. bug 48841) if ( (k->key() == Key_Shift) || (k->key() == Key_Control) || (k->key() == Key_Meta) || (k->key() == Key_Alt) ) return true; if (mExtEditorTempFile) return true; TQString sysLine = mExtEditor; mExtEditorTempFile = new KTempFile(); mExtEditorTempFile->setAutoDelete(true); (*mExtEditorTempFile->textStream()) << text(); mExtEditorTempFile->close(); // replace %f in the system line sysLine.replace( "%f", mExtEditorTempFile->name() ); mExtEditorProcess = new TDEProcess(); mExtEditorProcess->setUseShell( true ); sysLine += " "; while (!sysLine.isEmpty()) { *mExtEditorProcess << sysLine.left(sysLine.find(" ")).local8Bit(); sysLine.remove(0, sysLine.find(" ") + 1); } connect(mExtEditorProcess, TQT_SIGNAL(processExited(TDEProcess*)), TQT_SLOT(slotExternalEditorDone(TDEProcess*))); if (!mExtEditorProcess->start()) { KMessageBox::error( topLevelWidget(), i18n("Unable to start external editor.") ); killExternalEditor(); } else { mExtEditorTempFileWatcher = new KDirWatch( TQT_TQOBJECT(this), "mExtEditorTempFileWatcher" ); connect( mExtEditorTempFileWatcher, TQT_SIGNAL(dirty(const TQString&)), TQT_SLOT(slotExternalEditorTempFileChanged(const TQString&)) ); mExtEditorTempFileWatcher->addFile( mExtEditorTempFile->name() ); } return true; } else { // ---sven's Arrow key navigation start --- // Key Up in first line takes you to Subject line. if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0 && lineOfChar(0, currentColumn()) == 0) { deselect(); emit focusUp(); return true; } // ---sven's Arrow key navigation end --- if (k->key() == Key_Backtab && k->state() == ShiftButton) { deselect(); emit focusUp(); return true; } } } else if ( e->type() == TQEvent::ContextMenu ) { TQContextMenuEvent *event = (TQContextMenuEvent*) e; int para = 1, charPos, firstSpace, lastSpace; //Get the character at the position of the click charPos = charAt( viewportToContents(event->pos()), ¶ ); TQString paraText = text( para ); if( !paraText.at(charPos).isSpace() ) { //Get word right clicked on const TQRegExp wordBoundary( "[\\s\\W]" ); firstSpace = paraText.findRev( wordBoundary, charPos ) + 1; lastSpace = paraText.find( wordBoundary, charPos ); if( lastSpace == -1 ) lastSpace = paraText.length(); TQString word = paraText.mid( firstSpace, lastSpace - firstSpace ); //Continue if this word was misspelled if( !word.isEmpty() && mReplacements.contains( word ) ) { TDEPopupMenu p; //Add the suggestions to the popup menu TQStringList reps = mReplacements[word]; if( reps.count() > 0 ) { int listPos = 0; for ( TQStringList::Iterator it = reps.begin(); it != reps.end(); ++it ) { p.insertItem( *it, listPos ); listPos++; } } else { p.setItemEnabled( p.insertItem( i18n( "No Suggestions" ), -2 ), false ); } int addToDictionaryId = -42; int ignoreId = -43; if ( mSpeller && mSpeller->status() == KSpell::Running ) { p.insertSeparator(); addToDictionaryId = p.insertItem( i18n( "Add to Dictionary" ) ); ignoreId = p.insertItem( i18n( "Ignore All" ) ); } //Execute the popup inline const int id = p.exec( mapToGlobal( event->pos() ) ); if ( id == ignoreId ) { mHighlighter->ignoreWord( word ); mHighlighter->rehighlight(); } if ( id == addToDictionaryId ) { mSpeller->addPersonal( word ); mSpeller->writePersonalDictionary(); if ( mHighlighter ) { // Wait a bit until reloading the highlighter, the mSpeller first needs to finish saving // the personal word list. TQTimer::singleShot( 200, mHighlighter, TQT_SLOT( slotLocalSpellConfigChanged() ) ); } } else if( id > -1 ) { //Save the cursor position int parIdx = 1, txtIdx = 1; getCursorPosition(&parIdx, &txtIdx); setSelection(para, firstSpace, para, lastSpace); insert(mReplacements[word][id]); // Restore the cursor position; if the cursor was behind the // misspelled word then adjust the cursor position if ( para == parIdx && txtIdx >= lastSpace ) txtIdx += mReplacements[word][id].length() - word.length(); setCursorPosition(parIdx, txtIdx); } if ( id == addToDictionaryId || id == ignoreId ) { // No longer misspelled: Either added to dictionary or ignored mReplacements.remove( word ); } //Cancel original event return true; } } } else if ( e->type() == TQEvent::FocusIn || e->type() == TQEvent::FocusOut ) { TQFocusEvent *fe = TQT_TQFOCUSEVENT(e); if(! (fe->reason() == TQFocusEvent::ActiveWindow || fe->reason() == TQFocusEvent::Popup) ) emit focusChanged( fe->gotFocus() ); } return KEdit::eventFilter(o, e); } int KMEdit::autoSpellChecking( bool on ) { if ( textFormat() == TQt::RichText ) { // syntax highlighter doesn't support extended text properties if ( on ) KMessageBox::sorry(this, i18n("Automatic spellchecking is not possible on text with markup.")); return -1; } if ( mHighlighter ) { // don't autoEnable spell checking if the user turned spell checking off mHighlighter->setAutomatic( on ); mHighlighter->setActive( on ); } return 1; } void KMEdit::slotExternalEditorTempFileChanged( const TQString & fileName ) { if ( !mExtEditorTempFile ) return; if ( fileName != mExtEditorTempFile->name() ) return; // read data back in from file setAutoUpdate(false); clear(); insertLine(TQString::fromLocal8Bit(KPIM::kFileToString( fileName, true, false )), -1); setAutoUpdate(true); repaint(); } void KMEdit::slotExternalEditorDone( TDEProcess * proc ) { assert(proc == mExtEditorProcess); // make sure, we update even when KDirWatcher is too slow: slotExternalEditorTempFileChanged( mExtEditorTempFile->name() ); killExternalEditor(); } void KMEdit::killExternalEditor() { delete mExtEditorTempFileWatcher; mExtEditorTempFileWatcher = 0; delete mExtEditorTempFile; mExtEditorTempFile = 0; delete mExtEditorProcess; mExtEditorProcess = 0; } bool KMEdit::checkExternalEditorFinished() { if ( !mExtEditorProcess ) return true; switch ( KMessageBox::warningYesNoCancel( topLevelWidget(), i18n("The external editor is still running.\n" "Abort the external editor or leave it open?"), i18n("External Editor"), i18n("Abort Editor"), i18n("Leave Editor Open") ) ) { case KMessageBox::Yes: killExternalEditor(); return true; case KMessageBox::No: return true; default: return false; } } void KMEdit::spellcheck() { if ( mKSpellForDialog ) return; mWasModifiedBeforeSpellCheck = isModified(); mSpellLineEdit = !mSpellLineEdit; // maybe for later, for now plaintext is given to KSpell // if (textFormat() == TQt::RichText ) { // kdDebug(5006) << "KMEdit::spellcheck, spellchecking for RichText" << endl; // mKSpellForDialog = new KSpell(this, i18n("Spellcheck - KMail"), this, // TQT_SLOT(slotSpellcheck2(KSpell*)),0,true,false,KSpell::HTML); // } // else { // Don't use mSpellConfig here. Reason is that the spell dialog, KSpellDlg, uses its own // spell config, and therefore the two wouldn't be in sync. mKSpellForDialog = new KSpell( TQT_TQWIDGET(this), i18n("Spellcheck - KMail"), TQT_TQOBJECT(this), TQT_SLOT(slotSpellcheck2(KSpell*))/*, mSpellConfig*/ ); // } TQStringList l = KSpellingHighlighter::personalWords(); for ( TQStringList::Iterator it = l.begin(); it != l.end(); ++it ) { mKSpellForDialog->addPersonal( *it ); } connect (mKSpellForDialog, TQT_SIGNAL( death()), this, TQT_SLOT (slotSpellDone())); connect (mKSpellForDialog, TQT_SIGNAL (misspelling (const TQString &, const TQStringList &, unsigned int)), this, TQT_SLOT (slotMisspelling (const TQString &, const TQStringList &, unsigned int))); connect (mKSpellForDialog, TQT_SIGNAL (corrected (const TQString &, const TQString &, unsigned int)), this, TQT_SLOT (slotCorrected (const TQString &, const TQString &, unsigned int))); connect (mKSpellForDialog, TQT_SIGNAL (done(const TQString &)), this, TQT_SLOT (slotSpellResult (const TQString&))); } void KMEdit::cut() { KEdit::cut(); if ( textFormat() != TQt::RichText && mHighlighter ) mHighlighter->restartBackgroundSpellCheck(); } void KMEdit::clear() { KEdit::clear(); if ( textFormat() != TQt::RichText && mHighlighter ) mHighlighter->restartBackgroundSpellCheck(); } void KMEdit::del() { KEdit::del(); if ( textFormat() != TQt::RichText && mHighlighter ) mHighlighter->restartBackgroundSpellCheck(); } void KMEdit::paste() { mComposer->paste( mPasteMode ); } // KMEdit indirectly inherits from TQTextEdit, which has virtual paste() method, // but it controls whether it pastes clipboard or selection by an internal // flag that is not accessible in any way, so paste() being virtual is actually // useless, because reimplementations can't known where to paste from anyway. // Roll our own internal flag. void KMEdit::contentsMouseReleaseEvent( TQMouseEvent * e ) { if( e->button() != Qt::MidButton ) return KEdit::contentsMouseReleaseEvent( e ); mPasteMode = TQClipboard::Selection; KEdit::contentsMouseReleaseEvent( e ); mPasteMode = TQClipboard::Clipboard; } void KMEdit::contentsMouseDoubleClickEvent( TQMouseEvent *e ) { bool handled = false; if ( e->button() == Qt::LeftButton ) { // Get the cursor position for the place where the user clicked to int paragraphPos; int charPos = charAt ( e->pos(), ¶graphPos ); TQString paraText = text( paragraphPos ); // Now select the word under the cursor if ( charPos >= 0 && static_cast( charPos ) <= paraText.length() ) { // Start the selection where the user clicked int start = charPos; unsigned int end = charPos; // Extend the selection to the left, until we reach a non-letter and non-digit char for (;;) { if ( ( start - 1 ) < 0 ) break; TQChar charToTheLeft = paraText.at( start - 1 ); if ( charToTheLeft.isLetter() || charToTheLeft.isDigit() ) start--; else break; } // Extend the selection to the left, until we reach a non-letter and non-digit char for (;;) { if ( ( end + 1 ) >= paraText.length() ) break; TQChar charToTheRight = paraText.at( end + 1 ); if ( charToTheRight.isLetter() || charToTheRight.isDigit() ) end++; else break; } setSelection( paragraphPos, start, paragraphPos, end + 1 ); handled = true; } } if ( !handled ) return KEdit::contentsMouseDoubleClickEvent( e ); } void KMEdit::slotMisspelling(const TQString &text, const TQStringList &lst, unsigned int pos) { kdDebug(5006)<<"void KMEdit::slotMisspelling(const TQString &text, const TQStringList &lst, unsigned int pos) : "<sujectLineWidget()->spellCheckerMisspelling( text, lst, pos); else misspelling(text, lst, pos); } void KMEdit::slotCorrected (const TQString &oldWord, const TQString &newWord, unsigned int pos) { kdDebug(5006)<<"slotCorrected (const TQString &oldWord, const TQString &newWord, unsigned int pos) : "<sujectLineWidget()->spellCheckerCorrected( oldWord, newWord, pos); else { unsigned int l = 0; unsigned int cnt = 0; bool _bold,_underline,_italic; TQColor _color; TQFont _font; posToRowCol (pos, l, cnt); setCursorPosition(l, cnt+1); // the new word will get the same markup now as the first character of the word _bold = bold(); _underline = underline(); _italic = italic(); _color = color(); _font = currentFont(); corrected(oldWord, newWord, pos); setSelection (l, cnt, l, cnt+newWord.length()); setBold(_bold); setItalic(_italic); setUnderline(_underline); setColor(_color); setCurrentFont(_font); } } void KMEdit::slotSpellcheck2(KSpell*) { // Make sure words ignored by the highlighter are ignored by KSpell as well if ( mHighlighter ) { for ( uint i = 0; i < mHighlighter->ignoredWords().size(); i++ ) mKSpellForDialog->ignore( mHighlighter->ignoredWords()[i] ); } if( !mSpellLineEdit) { spellcheck_start(); TQString quotePrefix; if(mComposer && mComposer->msg()) { int languageNr = GlobalSettings::self()->replyCurrentLanguage(); ReplyPhrases replyPhrases( TQString::number(languageNr) ); replyPhrases.readConfig(); quotePrefix = mComposer->msg()->formatString( replyPhrases.indentPrefix() ); } kdDebug(5006) << "spelling: new SpellingFilter with prefix=\"" << quotePrefix << "\"" << endl; TQTextEdit plaintext; plaintext.setText(text()); plaintext.setTextFormat(TQt::PlainText); mSpellingFilter = new SpellingFilter(plaintext.text(), quotePrefix, SpellingFilter::FilterUrls, SpellingFilter::FilterEmailAddresses); mKSpellForDialog->check(mSpellingFilter->filteredText()); } else if( mComposer ) mKSpellForDialog->check( mComposer->sujectLineWidget()->text()); } void KMEdit::slotSpellResult(const TQString &s) { if( !mSpellLineEdit) spellcheck_stop(); int dlgResult = mKSpellForDialog->dlgResult(); if ( dlgResult == KS_CANCEL ) { if( mSpellLineEdit) { //stop spell check mSpellLineEdit = false; TQString tmpText( s ); tmpText = tmpText.remove('\n'); if( tmpText != mComposer->sujectLineWidget()->text() ) mComposer->sujectLineWidget()->setText( tmpText ); } else { setModified(true); } } mKSpellForDialog->cleanUp(); KDictSpellingHighlighter::dictionaryChanged(); emit spellcheck_done( dlgResult ); } void KMEdit::slotSpellDone() { kdDebug(5006)<<" void KMEdit::slotSpellDone()\n"; KSpell::spellStatus status = mKSpellForDialog->status(); delete mKSpellForDialog; mKSpellForDialog = 0; kdDebug(5006) << "spelling: delete SpellingFilter" << endl; delete mSpellingFilter; mSpellingFilter = 0; mComposer->sujectLineWidget()->deselect(); if (status == KSpell::Error) { KMessageBox::sorry( topLevelWidget(), i18n("ISpell/Aspell could not be started. Please " "make sure you have ISpell or Aspell properly " "configured and in your PATH.") ); emit spellcheck_done( KS_CANCEL ); } else if (status == KSpell::Crashed) { spellcheck_stop(); KMessageBox::sorry( topLevelWidget(), i18n("ISpell/Aspell seems to have crashed.") ); emit spellcheck_done( KS_CANCEL ); } else { if( mSpellLineEdit ) spellcheck(); else if( !mComposer->subjectTextWasSpellChecked() && status == KSpell::FinishedNoMisspellingsEncountered ) KMessageBox::information( topLevelWidget(), i18n("No misspellings encountered.") ); } } void KMEdit::setCursorPositionFromStart( unsigned int pos ) { unsigned int l = 0; unsigned int c = 0; posToRowCol( pos, l, c ); // kdDebug() << "Num lines: " << numLines() << endl; // kdDebug() << "Position " << pos << " converted to " << l << ":" << c << endl; setCursorPosition( l, c ); ensureCursorVisible(); } int KMEdit::indexOfCurrentLineStart( int paragraph, int index ) { Q_ASSERT( paragraph >= 0 && paragraph < paragraphs() ); Q_ASSERT( index >= 0 && ( index == 0 || index < paragraphLength( paragraph ) ) ); const int startLine = lineOfChar( paragraph, index ); Q_ASSERT( startLine >= 0 && startLine < linesOfParagraph( paragraph ) ); for ( int curIndex = index; curIndex >= 0; curIndex-- ) { const int line = lineOfChar( paragraph, curIndex ); if ( line != startLine ) { return curIndex + 1; } } return 0; } #include "kmedit.moc"