00001
00002
00003
00004
00005
00006 #include <config.h>
00007
00008 #include "kmedit.h"
00009 #include "kmlineeditspell.h"
00010
00011 #define REALLY_WANT_KMCOMPOSEWIN_H
00012 #include "kmcomposewin.h"
00013 #undef REALLY_WANT_KMCOMPOSEWIN_H
00014 #include "kmmsgdict.h"
00015 #include "kmfolder.h"
00016 #include "kmcommands.h"
00017
00018 #include <maillistdrag.h>
00019 using KPIM::MailListDrag;
00020
00021 #include <libkdepim/kfileio.h>
00022 #include <libemailfunctions/email.h>
00023
00024 #include <kcursor.h>
00025 #include <kprocess.h>
00026
00027 #include <kpopupmenu.h>
00028 #include <kdebug.h>
00029 #include <kmessagebox.h>
00030 #include <kurldrag.h>
00031
00032 #include <ktempfile.h>
00033 #include <klocale.h>
00034 #include <kapplication.h>
00035 #include <kdirwatch.h>
00036 #include <kiconloader.h>
00037
00038 #include "globalsettings.h"
00039 #include "replyphrases.h"
00040
00041 #include <kspell.h>
00042 #include <kspelldlg.h>
00043 #include <spellingfilter.h>
00044 #include <ksyntaxhighlighter.h>
00045
00046 #include <qregexp.h>
00047 #include <qbuffer.h>
00048 #include <qevent.h>
00049
00050 #include <sys/stat.h>
00051 #include <sys/types.h>
00052 #include <stdlib.h>
00053 #include <unistd.h>
00054 #include <errno.h>
00055 #include <fcntl.h>
00056 #include <assert.h>
00057
00058
00059 void KMEdit::contentsDragEnterEvent(QDragEnterEvent *e)
00060 {
00061 if (e->provides(MailListDrag::format()))
00062 e->accept(true);
00063 else if (e->provides("image/png"))
00064 e->accept();
00065 else
00066 return KEdit::contentsDragEnterEvent(e);
00067 }
00068
00069 void KMEdit::contentsDragMoveEvent(QDragMoveEvent *e)
00070 {
00071 if (e->provides(MailListDrag::format()))
00072 e->accept();
00073 else if (e->provides("image/png"))
00074 e->accept();
00075 else
00076 return KEdit::contentsDragMoveEvent(e);
00077 }
00078
00079 void KMEdit::keyPressEvent( QKeyEvent* e )
00080 {
00081 if( e->key() == Key_Return ) {
00082 int line, col;
00083 getCursorPosition( &line, &col );
00084 QString lineText = text( line );
00085
00086 lineText.truncate( lineText.length() - 1 );
00087
00088
00089 if( ( col > 0 ) && ( col < int( lineText.length() ) ) ) {
00090 bool isQuotedLine = false;
00091 uint bot = 0;
00092 while( bot < lineText.length() ) {
00093 if( ( lineText[bot] == '>' ) || ( lineText[bot] == '|' ) ) {
00094 isQuotedLine = true;
00095 ++bot;
00096 }
00097 else if( lineText[bot].isSpace() ) {
00098 ++bot;
00099 }
00100 else {
00101 break;
00102 }
00103 }
00104
00105 KEdit::keyPressEvent( e );
00106
00107
00108
00109
00110 if( isQuotedLine
00111 && ( bot != lineText.length() )
00112 && ( col >= int( bot ) ) ) {
00113
00114
00115
00116 getCursorPosition( &line, &col );
00117 QString newLine = text( line );
00118
00119
00120 unsigned int leadingWhiteSpaceCount = 0;
00121 while( ( leadingWhiteSpaceCount < newLine.length() )
00122 && newLine[leadingWhiteSpaceCount].isSpace() ) {
00123 ++leadingWhiteSpaceCount;
00124 }
00125 newLine = newLine.replace( 0, leadingWhiteSpaceCount,
00126 lineText.left( bot ) );
00127 removeParagraph( line );
00128 insertParagraph( newLine, line );
00129
00130
00131
00132 setCursorPosition( line, 0 );
00133 }
00134 }
00135 else
00136 KEdit::keyPressEvent( e );
00137 }
00138 else
00139 KEdit::keyPressEvent( e );
00140 }
00141
00142 void KMEdit::contentsDropEvent(QDropEvent *e)
00143 {
00144 if (e->provides(MailListDrag::format())) {
00145
00146 QByteArray serNums;
00147 MailListDrag::decode( e, serNums );
00148 QBuffer serNumBuffer(serNums);
00149 serNumBuffer.open(IO_ReadOnly);
00150 QDataStream serNumStream(&serNumBuffer);
00151 Q_UINT32 serNum;
00152 KMFolder *folder = 0;
00153 int idx;
00154 QPtrList<KMMsgBase> messageList;
00155 while (!serNumStream.atEnd()) {
00156 KMMsgBase *msgBase = 0;
00157 serNumStream >> serNum;
00158 KMMsgDict::instance()->getLocation(serNum, &folder, &idx);
00159 if (folder)
00160 msgBase = folder->getMsgBase(idx);
00161 if (msgBase)
00162 messageList.append( msgBase );
00163 }
00164 serNumBuffer.close();
00165 uint identity = folder ? folder->identity() : 0;
00166 KMCommand *command =
00167 new KMForwardAttachedCommand(mComposer, messageList,
00168 identity, mComposer);
00169 command->start();
00170 }
00171 else if( e->provides("image/png") ) {
00172 emit attachPNGImageData(e->encodedData("image/png"));
00173 }
00174 else if( KURLDrag::canDecode( e ) ) {
00175 KURL::List urlList;
00176 if( KURLDrag::decode( e, urlList ) ) {
00177 KPopupMenu p;
00178 p.insertItem( i18n("Add as Text"), 0 );
00179 p.insertItem( i18n("Add as Attachment"), 1 );
00180 int id = p.exec( mapToGlobal( e->pos() ) );
00181 switch ( id) {
00182 case 0:
00183 for ( KURL::List::Iterator it = urlList.begin();
00184 it != urlList.end(); ++it ) {
00185 insert( (*it).url() );
00186 }
00187 break;
00188 case 1:
00189 for ( KURL::List::Iterator it = urlList.begin();
00190 it != urlList.end(); ++it ) {
00191 mComposer->addAttach( *it );
00192 }
00193 break;
00194 }
00195 }
00196 else if ( QTextDrag::canDecode( e ) ) {
00197 QString s;
00198 if ( QTextDrag::decode( e, s ) )
00199 insert( s );
00200 }
00201 else
00202 kdDebug(5006) << "KMEdit::contentsDropEvent, unable to add dropped object" << endl;
00203 }
00204 else if( e->provides("text/x-textsnippet") ) {
00205 emit insertSnippet();
00206 }
00207 else {
00208 KEdit::contentsDropEvent(e);
00209 }
00210 }
00211
00212 KMEdit::KMEdit(QWidget *parent, KMComposeWin* composer,
00213 KSpellConfig* autoSpellConfig,
00214 const char *name)
00215 : KEdit( parent, name ),
00216 mComposer( composer ),
00217 mKSpellForDialog( 0 ),
00218 mSpeller( 0 ),
00219 mSpellConfig( autoSpellConfig ),
00220 mSpellingFilter( 0 ),
00221 mExtEditorTempFile( 0 ),
00222 mExtEditorTempFileWatcher( 0 ),
00223 mExtEditorProcess( 0 ),
00224 mUseExtEditor( false ),
00225 mWasModifiedBeforeSpellCheck( false ),
00226 mHighlighter( 0 ),
00227 mSpellLineEdit( false ),
00228 mPasteMode( QClipboard::Clipboard )
00229 {
00230 connect( this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()) );
00231 installEventFilter(this);
00232 KCursor::setAutoHideCursor( this, true, true );
00233 setOverwriteEnabled( true );
00234 createSpellers();
00235 connect( mSpellConfig, SIGNAL( configChanged() ),
00236 this, SLOT( createSpellers() ) );
00237 connect( mSpeller, SIGNAL( death() ),
00238 this, SLOT( spellerDied() ) );
00239 }
00240
00241 void KMEdit::createSpellers()
00242 {
00243 delete mSpeller;
00244 mSpeller = new KMSpell( this, SLOT( spellerReady( KSpell * ) ), mSpellConfig );
00245 }
00246
00247 void KMEdit::initializeAutoSpellChecking()
00248 {
00249 if ( mHighlighter )
00250 return;
00251 QColor defaultColor1( 0x00, 0x80, 0x00 );
00252 QColor defaultColor2( 0x00, 0x70, 0x00 );
00253 QColor defaultColor3( 0x00, 0x60, 0x00 );
00254 QColor defaultForeground( kapp->palette().active().text() );
00255
00256 QColor c = Qt::red;
00257 KConfigGroup readerConfig( KMKernel::config(), "Reader" );
00258 QColor col1;
00259 if ( !readerConfig.readBoolEntry( "defaultColors", true ) )
00260 col1 = readerConfig.readColorEntry( "ForegroundColor", &defaultForeground );
00261 else
00262 col1 = defaultForeground;
00263 QColor col2 = readerConfig.readColorEntry( "QuotedText3", &defaultColor3 );
00264 QColor col3 = readerConfig.readColorEntry( "QuotedText2", &defaultColor2 );
00265 QColor col4 = readerConfig.readColorEntry( "QuotedText1", &defaultColor1 );
00266 QColor misspelled = readerConfig.readColorEntry( "MisspelledColor", &c );
00267 mHighlighter = new KMSyntaxHighter( this, true,
00268 false,
00269 misspelled,
00270 true,
00271 col1, col2, col3, col4,
00272 mSpellConfig );
00273
00274 connect( mHighlighter, SIGNAL(newSuggestions(const QString&, const QStringList&, unsigned int)),
00275 this, SLOT(addSuggestion(const QString&, const QStringList&, unsigned int)) );
00276 }
00277
00278
00279 QPopupMenu *KMEdit::createPopupMenu( const QPoint& pos )
00280 {
00281 enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };
00282
00283 QPopupMenu *menu = KEdit::createPopupMenu( pos );
00284 if ( !QApplication::clipboard()->image().isNull() ) {
00285 int id = menu->idAt(0);
00286 menu->setItemEnabled( id - IdPaste, true);
00287 }
00288
00289 return menu;
00290 }
00291
00292 void KMEdit::deleteAutoSpellChecking()
00293 {
00294 delete mHighlighter;
00295 mHighlighter =0;
00296 }
00297
00298 void KMEdit::addSuggestion(const QString& text, const QStringList& lst, unsigned int )
00299 {
00300 mReplacements[text] = lst;
00301 }
00302
00303 void KMEdit::setSpellCheckingActive(bool spellCheckingActive)
00304 {
00305 if ( mHighlighter ) {
00306 mHighlighter->setActive(spellCheckingActive);
00307 }
00308 }
00309
00310
00311 KMEdit::~KMEdit()
00312 {
00313 removeEventFilter(this);
00314
00315 if ( mSpeller ) {
00316
00317 mSpeller->setAutoDelete( true );
00318 mSpeller->cleanUp();
00319 mSpeller = 0;
00320 }
00321
00322 delete mKSpellForDialog;
00323 delete mHighlighter;
00324 mHighlighter = 0;
00325 }
00326
00327
00328
00329 QString KMEdit::brokenText()
00330 {
00331 QString temp, line;
00332
00333 int num_lines = numLines();
00334 for (int i = 0; i < num_lines; ++i)
00335 {
00336 int lastLine = 0;
00337 line = textLine(i);
00338 for (int j = 0; j < (int)line.length(); ++j)
00339 {
00340 if (lineOfChar(i, j) > lastLine)
00341 {
00342 lastLine = lineOfChar(i, j);
00343 temp += '\n';
00344 }
00345 temp += line[j];
00346 }
00347 if (i + 1 < num_lines) temp += '\n';
00348 }
00349
00350 return temp;
00351 }
00352
00353
00354 unsigned int KMEdit::lineBreakColumn() const
00355 {
00356 unsigned int lineBreakColumn = 0;
00357 unsigned int numlines = numLines();
00358 while ( numlines-- ) {
00359 lineBreakColumn = QMAX( lineBreakColumn, textLine( numlines ).length() );
00360 }
00361 return lineBreakColumn;
00362 }
00363
00364 KMSpell::KMSpell( QObject *receiver, const char *slot, KSpellConfig *spellConfig )
00365 : KSpell( 0, QString(), receiver, slot, spellConfig )
00366 {
00367 }
00368
00369 KMSyntaxHighter::KMSyntaxHighter( QTextEdit *textEdit,
00370 bool spellCheckingActive,
00371 bool autoEnable,
00372 const QColor& spellColor,
00373 bool colorQuoting,
00374 const QColor& QuoteColor0,
00375 const QColor& QuoteColor1,
00376 const QColor& QuoteColor2,
00377 const QColor& QuoteColor3,
00378 KSpellConfig *spellConfig )
00379 : KDictSpellingHighlighter( textEdit, spellCheckingActive, autoEnable, spellColor, colorQuoting,
00380 QuoteColor0, QuoteColor1, QuoteColor2, QuoteColor3, spellConfig )
00381 {
00382 }
00383
00384 bool KMSyntaxHighter::isMisspelled( const QString &word )
00385 {
00386 if ( mIgnoredWords.contains( word ) ) {
00387 return false;
00388 }
00389 else {
00390 return KDictSpellingHighlighter::isMisspelled( word );
00391 }
00392 }
00393
00394 void KMSyntaxHighter::ignoreWord( const QString &word )
00395 {
00396 mIgnoredWords << word;
00397 }
00398
00399 QStringList KMSyntaxHighter::ignoredWords() const
00400 {
00401 return mIgnoredWords;
00402 }
00403
00404 void KMEdit::spellerDied()
00405 {
00406 mSpeller = 0;
00407 }
00408
00409 void KMEdit::spellerReady( KSpell *spell )
00410 {
00411 Q_ASSERT( mSpeller == spell );
00412 }
00413
00414 bool KMEdit::eventFilter(QObject*o, QEvent* e)
00415 {
00416 if (o == this)
00417 KCursor::autoHideEventFilter(o, e);
00418
00419 if (e->type() == QEvent::KeyPress)
00420 {
00421 QKeyEvent *k = (QKeyEvent*)e;
00422
00423 if (mUseExtEditor) {
00424 if (k->key() == Key_Up)
00425 {
00426 emit focusUp();
00427 return true;
00428 }
00429
00430
00431 if ( (k->key() == Key_Shift) || (k->key() == Key_Control) ||
00432 (k->key() == Key_Meta) || (k->key() == Key_Alt) )
00433 return true;
00434 if (mExtEditorTempFile) return true;
00435 QString sysLine = mExtEditor;
00436 mExtEditorTempFile = new KTempFile();
00437
00438 mExtEditorTempFile->setAutoDelete(true);
00439
00440 (*mExtEditorTempFile->textStream()) << text();
00441
00442 mExtEditorTempFile->close();
00443
00444 sysLine.replace( "%f", mExtEditorTempFile->name() );
00445 mExtEditorProcess = new KProcess();
00446 mExtEditorProcess->setUseShell( true );
00447 sysLine += " ";
00448 while (!sysLine.isEmpty())
00449 {
00450 *mExtEditorProcess << sysLine.left(sysLine.find(" ")).local8Bit();
00451 sysLine.remove(0, sysLine.find(" ") + 1);
00452 }
00453 connect(mExtEditorProcess, SIGNAL(processExited(KProcess*)),
00454 SLOT(slotExternalEditorDone(KProcess*)));
00455 if (!mExtEditorProcess->start())
00456 {
00457 KMessageBox::error( topLevelWidget(),
00458 i18n("Unable to start external editor.") );
00459 killExternalEditor();
00460 } else {
00461 mExtEditorTempFileWatcher = new KDirWatch( this, "mExtEditorTempFileWatcher" );
00462 connect( mExtEditorTempFileWatcher, SIGNAL(dirty(const QString&)),
00463 SLOT(slotExternalEditorTempFileChanged(const QString&)) );
00464 mExtEditorTempFileWatcher->addFile( mExtEditorTempFile->name() );
00465 }
00466 return true;
00467 } else {
00468
00469
00470 if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0
00471 && lineOfChar(0, currentColumn()) == 0)
00472 {
00473 deselect();
00474 emit focusUp();
00475 return true;
00476 }
00477
00478
00479 if (k->key() == Key_Backtab && k->state() == ShiftButton)
00480 {
00481 deselect();
00482 emit focusUp();
00483 return true;
00484 }
00485
00486 }
00487 } else if ( e->type() == QEvent::ContextMenu ) {
00488 QContextMenuEvent *event = (QContextMenuEvent*) e;
00489
00490 int para = 1, charPos, firstSpace, lastSpace;
00491
00492
00493 charPos = charAt( viewportToContents(event->pos()), ¶ );
00494 QString paraText = text( para );
00495
00496 if( !paraText.at(charPos).isSpace() )
00497 {
00498
00499 const QRegExp wordBoundary( "[\\s\\W]" );
00500 firstSpace = paraText.findRev( wordBoundary, charPos ) + 1;
00501 lastSpace = paraText.find( wordBoundary, charPos );
00502 if( lastSpace == -1 )
00503 lastSpace = paraText.length();
00504 QString word = paraText.mid( firstSpace, lastSpace - firstSpace );
00505
00506 if( !word.isEmpty() && mReplacements.contains( word ) )
00507 {
00508 KPopupMenu p;
00509
00510
00511 QStringList reps = mReplacements[word];
00512 if( reps.count() > 0 )
00513 {
00514 int listPos = 0;
00515 for ( QStringList::Iterator it = reps.begin(); it != reps.end(); ++it ) {
00516 p.insertItem( *it, listPos );
00517 listPos++;
00518 }
00519 }
00520 else
00521 {
00522 p.setItemEnabled( p.insertItem( i18n( "No Suggestions" ), -2 ), false );
00523 }
00524
00525 int addToDictionaryId = -42;
00526 int ignoreId = -43;
00527 if ( mSpeller && mSpeller->status() == KSpell::Running ) {
00528 p.insertSeparator();
00529 addToDictionaryId = p.insertItem( i18n( "Add to Dictionary" ) );
00530 ignoreId = p.insertItem( i18n( "Ignore All" ) );
00531 }
00532
00533
00534 const int id = p.exec( mapToGlobal( event->pos() ) );
00535
00536 if ( id == ignoreId ) {
00537 mHighlighter->ignoreWord( word );
00538 mHighlighter->rehighlight();
00539 }
00540 if ( id == addToDictionaryId ) {
00541 mSpeller->addPersonal( word );
00542 mSpeller->writePersonalDictionary();
00543 if ( mHighlighter ) {
00544
00545
00546 QTimer::singleShot( 200, mHighlighter, SLOT( slotLocalSpellConfigChanged() ) );
00547 }
00548 }
00549 else if( id > -1 )
00550 {
00551
00552 int parIdx = 1, txtIdx = 1;
00553 getCursorPosition(&parIdx, &txtIdx);
00554 setSelection(para, firstSpace, para, lastSpace);
00555 insert(mReplacements[word][id]);
00556
00557
00558 if ( para == parIdx && txtIdx >= lastSpace )
00559 txtIdx += mReplacements[word][id].length() - word.length();
00560 setCursorPosition(parIdx, txtIdx);
00561 }
00562
00563 if ( id == addToDictionaryId || id == ignoreId ) {
00564
00565 mReplacements.remove( word );
00566 }
00567
00568
00569 return true;
00570 }
00571 }
00572 } else if ( e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut ) {
00573 QFocusEvent *fe = static_cast<QFocusEvent*>(e);
00574 if(! (fe->reason() == QFocusEvent::ActiveWindow || fe->reason() == QFocusEvent::Popup) )
00575 emit focusChanged( fe->gotFocus() );
00576 }
00577
00578 return KEdit::eventFilter(o, e);
00579 }
00580
00581
00582 int KMEdit::autoSpellChecking( bool on )
00583 {
00584 if ( textFormat() == Qt::RichText ) {
00585
00586 if ( on )
00587 KMessageBox::sorry(this, i18n("Automatic spellchecking is not possible on text with markup."));
00588 return -1;
00589 }
00590 if ( mHighlighter ) {
00591
00592 mHighlighter->setAutomatic( on );
00593 mHighlighter->setActive( on );
00594 }
00595 return 1;
00596 }
00597
00598
00599 void KMEdit::slotExternalEditorTempFileChanged( const QString & fileName ) {
00600 if ( !mExtEditorTempFile )
00601 return;
00602 if ( fileName != mExtEditorTempFile->name() )
00603 return;
00604
00605 setAutoUpdate(false);
00606 clear();
00607
00608 insertLine(QString::fromLocal8Bit(KPIM::kFileToString( fileName, true, false )), -1);
00609 setAutoUpdate(true);
00610 repaint();
00611 }
00612
00613 void KMEdit::slotExternalEditorDone( KProcess * proc ) {
00614 assert(proc == mExtEditorProcess);
00615
00616 slotExternalEditorTempFileChanged( mExtEditorTempFile->name() );
00617 killExternalEditor();
00618 }
00619
00620 void KMEdit::killExternalEditor() {
00621 delete mExtEditorTempFileWatcher; mExtEditorTempFileWatcher = 0;
00622 delete mExtEditorTempFile; mExtEditorTempFile = 0;
00623 delete mExtEditorProcess; mExtEditorProcess = 0;
00624 }
00625
00626
00627 bool KMEdit::checkExternalEditorFinished() {
00628 if ( !mExtEditorProcess )
00629 return true;
00630 switch ( KMessageBox::warningYesNoCancel( topLevelWidget(),
00631 i18n("The external editor is still running.\n"
00632 "Abort the external editor or leave it open?"),
00633 i18n("External Editor"),
00634 i18n("Abort Editor"), i18n("Leave Editor Open") ) ) {
00635 case KMessageBox::Yes:
00636 killExternalEditor();
00637 return true;
00638 case KMessageBox::No:
00639 return true;
00640 default:
00641 return false;
00642 }
00643 }
00644
00645 void KMEdit::spellcheck()
00646 {
00647 if ( mKSpellForDialog )
00648 return;
00649 mWasModifiedBeforeSpellCheck = isModified();
00650 mSpellLineEdit = !mSpellLineEdit;
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661 mKSpellForDialog = new KSpell( this, i18n("Spellcheck - KMail"), this,
00662 SLOT(slotSpellcheck2(KSpell*)) );
00663
00664
00665 QStringList l = KSpellingHighlighter::personalWords();
00666 for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
00667 mKSpellForDialog->addPersonal( *it );
00668 }
00669 connect (mKSpellForDialog, SIGNAL( death()),
00670 this, SLOT (slotSpellDone()));
00671 connect (mKSpellForDialog, SIGNAL (misspelling (const QString &, const QStringList &, unsigned int)),
00672 this, SLOT (slotMisspelling (const QString &, const QStringList &, unsigned int)));
00673 connect (mKSpellForDialog, SIGNAL (corrected (const QString &, const QString &, unsigned int)),
00674 this, SLOT (slotCorrected (const QString &, const QString &, unsigned int)));
00675 connect (mKSpellForDialog, SIGNAL (done(const QString &)),
00676 this, SLOT (slotSpellResult (const QString&)));
00677 }
00678
00679 void KMEdit::cut()
00680 {
00681 KEdit::cut();
00682 if ( textFormat() != Qt::RichText && mHighlighter )
00683 mHighlighter->restartBackgroundSpellCheck();
00684 }
00685
00686 void KMEdit::clear()
00687 {
00688 KEdit::clear();
00689 if ( textFormat() != Qt::RichText && mHighlighter )
00690 mHighlighter->restartBackgroundSpellCheck();
00691 }
00692
00693 void KMEdit::del()
00694 {
00695 KEdit::del();
00696 if ( textFormat() != Qt::RichText && mHighlighter )
00697 mHighlighter->restartBackgroundSpellCheck();
00698 }
00699
00700 void KMEdit::paste()
00701 {
00702 mComposer->paste( mPasteMode );
00703 }
00704
00705
00706
00707
00708
00709
00710 void KMEdit::contentsMouseReleaseEvent( QMouseEvent * e )
00711 {
00712 if( e->button() != Qt::MidButton )
00713 return KEdit::contentsMouseReleaseEvent( e );
00714 mPasteMode = QClipboard::Selection;
00715 KEdit::contentsMouseReleaseEvent( e );
00716 mPasteMode = QClipboard::Clipboard;
00717 }
00718
00719 void KMEdit::contentsMouseDoubleClickEvent( QMouseEvent *e )
00720 {
00721 bool handled = false;
00722 if ( e->button() == Qt::LeftButton ) {
00723
00724
00725 int paragraphPos;
00726 int charPos = charAt ( e->pos(), ¶graphPos );
00727 QString paraText = text( paragraphPos );
00728
00729
00730 if ( charPos >= 0 && static_cast<unsigned int>( charPos ) <= paraText.length() ) {
00731
00732
00733 int start = charPos;
00734 unsigned int end = charPos;
00735
00736
00737 for (;;) {
00738 if ( ( start - 1 ) < 0 )
00739 break;
00740 QChar charToTheLeft = paraText.at( start - 1 );
00741 if ( charToTheLeft.isLetter() || charToTheLeft.isDigit() )
00742 start--;
00743 else
00744 break;
00745 }
00746
00747
00748 for (;;) {
00749 if ( ( end + 1 ) >= paraText.length() )
00750 break;
00751 QChar charToTheRight = paraText.at( end + 1 );
00752 if ( charToTheRight.isLetter() || charToTheRight.isDigit() )
00753 end++;
00754 else
00755 break;
00756 }
00757
00758 setSelection( paragraphPos, start, paragraphPos, end + 1 );
00759 handled = true;
00760 }
00761 }
00762
00763 if ( !handled )
00764 return KEdit::contentsMouseDoubleClickEvent( e );
00765 }
00766
00767 void KMEdit::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos)
00768 {
00769 kdDebug(5006)<<"void KMEdit::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos) : "<<text <<endl;
00770 if( mSpellLineEdit )
00771 mComposer->sujectLineWidget()->spellCheckerMisspelling( text, lst, pos);
00772 else
00773 misspelling(text, lst, pos);
00774 }
00775
00776 void KMEdit::slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos)
00777 {
00778 kdDebug(5006)<<"slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos) : "<<oldWord<<endl;
00779 if( mSpellLineEdit )
00780 mComposer->sujectLineWidget()->spellCheckerCorrected( oldWord, newWord, pos);
00781 else {
00782 unsigned int l = 0;
00783 unsigned int cnt = 0;
00784 bool _bold,_underline,_italic;
00785 QColor _color;
00786 QFont _font;
00787 posToRowCol (pos, l, cnt);
00788 setCursorPosition(l, cnt+1);
00789 _bold = bold();
00790 _underline = underline();
00791 _italic = italic();
00792 _color = color();
00793 _font = currentFont();
00794 corrected(oldWord, newWord, pos);
00795 setSelection (l, cnt, l, cnt+newWord.length());
00796 setBold(_bold);
00797 setItalic(_italic);
00798 setUnderline(_underline);
00799 setColor(_color);
00800 setCurrentFont(_font);
00801 }
00802
00803 }
00804
00805 void KMEdit::slotSpellcheck2(KSpell*)
00806 {
00807
00808 if ( mHighlighter ) {
00809 for ( uint i = 0; i < mHighlighter->ignoredWords().size(); i++ )
00810 mKSpellForDialog->ignore( mHighlighter->ignoredWords()[i] );
00811 }
00812
00813 if( !mSpellLineEdit)
00814 {
00815 spellcheck_start();
00816
00817 QString quotePrefix;
00818 if(mComposer && mComposer->msg())
00819 {
00820 int languageNr = GlobalSettings::self()->replyCurrentLanguage();
00821 ReplyPhrases replyPhrases( QString::number(languageNr) );
00822 replyPhrases.readConfig();
00823
00824 quotePrefix = mComposer->msg()->formatString(
00825 replyPhrases.indentPrefix() );
00826 }
00827
00828 kdDebug(5006) << "spelling: new SpellingFilter with prefix=\"" << quotePrefix << "\"" << endl;
00829 QTextEdit plaintext;
00830 plaintext.setText(text());
00831 plaintext.setTextFormat(Qt::PlainText);
00832 mSpellingFilter = new SpellingFilter(plaintext.text(), quotePrefix, SpellingFilter::FilterUrls,
00833 SpellingFilter::FilterEmailAddresses);
00834
00835 mKSpellForDialog->check(mSpellingFilter->filteredText());
00836 }
00837 else if( mComposer )
00838 mKSpellForDialog->check( mComposer->sujectLineWidget()->text());
00839 }
00840
00841 void KMEdit::slotSpellResult(const QString &s)
00842 {
00843 if( !mSpellLineEdit)
00844 spellcheck_stop();
00845
00846 int dlgResult = mKSpellForDialog->dlgResult();
00847 if ( dlgResult == KS_CANCEL )
00848 {
00849 if( mSpellLineEdit)
00850 {
00851
00852 mSpellLineEdit = false;
00853 QString tmpText( s );
00854 tmpText = tmpText.remove('\n');
00855
00856 if( tmpText != mComposer->sujectLineWidget()->text() )
00857 mComposer->sujectLineWidget()->setText( tmpText );
00858 }
00859 else
00860 {
00861 setModified(true);
00862 }
00863 }
00864 mKSpellForDialog->cleanUp();
00865 KDictSpellingHighlighter::dictionaryChanged();
00866
00867 emit spellcheck_done( dlgResult );
00868 }
00869
00870 void KMEdit::slotSpellDone()
00871 {
00872 kdDebug(5006)<<" void KMEdit::slotSpellDone()\n";
00873 KSpell::spellStatus status = mKSpellForDialog->status();
00874 delete mKSpellForDialog;
00875 mKSpellForDialog = 0;
00876
00877 kdDebug(5006) << "spelling: delete SpellingFilter" << endl;
00878 delete mSpellingFilter;
00879 mSpellingFilter = 0;
00880 mComposer->sujectLineWidget()->deselect();
00881 if (status == KSpell::Error)
00882 {
00883 KMessageBox::sorry( topLevelWidget(),
00884 i18n("ISpell/Aspell could not be started. Please "
00885 "make sure you have ISpell or Aspell properly "
00886 "configured and in your PATH.") );
00887 emit spellcheck_done( KS_CANCEL );
00888 }
00889 else if (status == KSpell::Crashed)
00890 {
00891 spellcheck_stop();
00892 KMessageBox::sorry( topLevelWidget(),
00893 i18n("ISpell/Aspell seems to have crashed.") );
00894 emit spellcheck_done( KS_CANCEL );
00895 }
00896 else
00897 {
00898 if( mSpellLineEdit )
00899 spellcheck();
00900 else if( !mComposer->subjectTextWasSpellChecked() && status == KSpell::FinishedNoMisspellingsEncountered )
00901 KMessageBox::information( topLevelWidget(),
00902 i18n("No misspellings encountered.") );
00903 }
00904 }
00905
00906 void KMEdit::setCursorPositionFromStart( unsigned int pos ) {
00907 unsigned int l = 0;
00908 unsigned int c = 0;
00909 posToRowCol( pos, l, c );
00910
00911
00912 setCursorPosition( l, c );
00913 ensureCursorVisible();
00914 }
00915
00916 int KMEdit::indexOfCurrentLineStart( int paragraph, int index )
00917 {
00918 Q_ASSERT( paragraph >= 0 && paragraph < paragraphs() );
00919 Q_ASSERT( index >= 0 && index < paragraphLength( paragraph ) );
00920
00921 kdDebug(5006) << "indexOfCurrentLineStart(): para=" << paragraph << " index=" << index << endl;
00922 const int startLine = lineOfChar( paragraph, index );
00923 Q_ASSERT( startLine >= 0 && startLine < linesOfParagraph( paragraph ) );
00924 kdDebug(5006) << "indexOfCurrentLineStart(): startLine=" << startLine << endl;
00925 for ( int curIndex = index; curIndex >= 0; curIndex-- ) {
00926 const int line = lineOfChar( paragraph, curIndex );
00927 if ( line != startLine ) {
00928 kdDebug(5006) << "At index " << curIndex << ", the current line changed to " << line << endl;
00929 return curIndex + 1;
00930 }
00931 }
00932 return 0;
00933 }
00934
00935 #include "kmedit.moc"