KPIMTextedit Library
textedit.cpp
00001 /* 00002 Copyright (c) 2009 Thomas McGuire <mcguire@kde.org> 00003 00004 Based on KMail and libkdepim code by: 00005 Copyright 2007 Laurent Montel <montel@kde.org> 00006 00007 This library is free software; you can redistribute it and/or modify it 00008 under the terms of the GNU Library General Public License as published by 00009 the Free Software Foundation; either version 2 of the License, or (at your 00010 option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, but WITHOUT 00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00015 License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to the 00019 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00020 02110-1301, USA. 00021 */ 00022 #include "textedit.h" 00023 00024 #include "emailquotehighlighter.h" 00025 00026 #include <kmime/kmime_codecs.h> 00027 00028 #include <KDE/KAction> 00029 #include <KDE/KActionCollection> 00030 #include <KDE/KCursor> 00031 #include <KDE/KFileDialog> 00032 #include <KDE/KLocalizedString> 00033 #include <KDE/KMessageBox> 00034 #include <KDE/KPushButton> 00035 #include <KDE/KUrl> 00036 00037 #include <QtCore/QBuffer> 00038 #include <QtCore/QDateTime> 00039 #include <QtCore/QMimeData> 00040 #include <QtCore/QFileInfo> 00041 #include <QtCore/QPointer> 00042 #include <QtGui/QKeyEvent> 00043 #include <QtGui/QTextLayout> 00044 00045 #include "textutils.h" 00046 #include <QPlainTextEdit> 00047 00048 namespace KPIMTextEdit { 00049 00050 class TextEditPrivate 00051 { 00052 public: 00053 00054 TextEditPrivate( TextEdit *parent ) 00055 : actionAddImage( 0 ), 00056 actionDeleteLine( 0 ), 00057 q( parent ), 00058 imageSupportEnabled( false ) 00059 { 00060 } 00061 00070 void addImageHelper( const QString &imageName, const QImage &image ); 00071 00075 QList<QTextImageFormat> embeddedImageFormats() const; 00076 00081 void fixupTextEditString( QString &text ) const; 00082 00086 void init(); 00087 00092 void _k_slotAddImage(); 00093 00094 void _k_slotDeleteLine(); 00095 00097 KAction *actionAddImage; 00098 00100 KAction *actionDeleteLine; 00101 00103 TextEdit *q; 00104 00106 bool imageSupportEnabled; 00107 00113 QStringList mImageNames; 00114 00126 bool spellCheckingEnabled; 00127 00128 QString configFile; 00129 }; 00130 00131 } // namespace 00132 00133 using namespace KPIMTextEdit; 00134 00135 void TextEditPrivate::fixupTextEditString( QString &text ) const 00136 { 00137 // Remove line separators. Normal \n chars are still there, so no linebreaks get lost here 00138 text.remove( QChar::LineSeparator ); 00139 00140 // Get rid of embedded images, see QTextImageFormat documentation: 00141 // "Inline images are represented by an object replacement character (0xFFFC in Unicode) " 00142 text.remove( 0xFFFC ); 00143 00144 // In plaintext mode, each space is non-breaking. 00145 text.replace( QChar::Nbsp, QChar::fromAscii( ' ' ) ); 00146 } 00147 00148 TextEdit::TextEdit( const QString &text, QWidget *parent ) 00149 : KRichTextWidget( text, parent ), 00150 d( new TextEditPrivate( this ) ) 00151 { 00152 d->init(); 00153 } 00154 00155 TextEdit::TextEdit( QWidget *parent ) 00156 : KRichTextWidget( parent ), 00157 d( new TextEditPrivate( this ) ) 00158 { 00159 d->init(); 00160 } 00161 00162 TextEdit::TextEdit( QWidget *parent, const QString &configFile ) 00163 : KRichTextWidget( parent ), 00164 d( new TextEditPrivate( this ) ) 00165 { 00166 d->init(); 00167 d->configFile = configFile; 00168 } 00169 00170 TextEdit::~TextEdit() 00171 { 00172 } 00173 00174 bool TextEdit::eventFilter( QObject *o, QEvent *e ) 00175 { 00176 #ifndef QT_NO_CURSOR 00177 if ( o == this ) { 00178 KCursor::autoHideEventFilter( o, e ); 00179 } 00180 #endif 00181 return KRichTextWidget::eventFilter( o, e ); 00182 } 00183 00184 void TextEditPrivate::init() 00185 { 00186 q->setSpellInterface( q ); 00187 // We tell the KRichTextWidget to enable spell checking, because only then it will 00188 // call createHighlighter() which will create our own highlighter which also 00189 // does quote highlighting. 00190 // However, *our* spellchecking is still disabled. Our own highlighter only 00191 // cares about our spellcheck status, it will not highlight missspelled words 00192 // if our spellchecking is disabled. 00193 // See also KEMailQuotingHighlighter::highlightBlock(). 00194 spellCheckingEnabled = false; 00195 q->setCheckSpellingEnabledInternal( true ); 00196 00197 #ifndef QT_NO_CURSOR 00198 KCursor::setAutoHideCursor( q, true, true ); 00199 #endif 00200 q->installEventFilter( q ); 00201 } 00202 00203 QString TextEdit::configFile() const 00204 { 00205 return d->configFile; 00206 } 00207 00208 void TextEdit::keyPressEvent ( QKeyEvent * e ) 00209 { 00210 if ( e->key() == Qt::Key_Return ) { 00211 QTextCursor cursor = textCursor(); 00212 int oldPos = cursor.position(); 00213 int blockPos = cursor.block().position(); 00214 00215 //selection all the line. 00216 cursor.movePosition( QTextCursor::StartOfBlock ); 00217 cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); 00218 QString lineText = cursor.selectedText(); 00219 if ( ( ( oldPos - blockPos ) > 0 ) && 00220 ( ( oldPos - blockPos ) < int( lineText.length() ) ) ) { 00221 bool isQuotedLine = false; 00222 int bot = 0; // bot = begin of text after quote indicators 00223 while ( bot < lineText.length() ) { 00224 if( ( lineText[bot] == QChar::fromAscii( '>' ) ) || 00225 ( lineText[bot] == QChar::fromAscii( '|' ) ) ) { 00226 isQuotedLine = true; 00227 ++bot; 00228 } else if ( lineText[bot].isSpace() ) { 00229 ++bot; 00230 } else { 00231 break; 00232 } 00233 } 00234 KRichTextWidget::keyPressEvent( e ); 00235 // duplicate quote indicators of the previous line before the new 00236 // line if the line actually contained text (apart from the quote 00237 // indicators) and the cursor is behind the quote indicators 00238 if ( isQuotedLine && 00239 ( bot != lineText.length() ) && 00240 ( ( oldPos - blockPos ) >= int( bot ) ) ) { 00241 // The cursor position might have changed unpredictably if there was selected 00242 // text which got replaced by a new line, so we query it again: 00243 cursor.movePosition( QTextCursor::StartOfBlock ); 00244 cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); 00245 QString newLine = cursor.selectedText(); 00246 00247 // remove leading white space from the new line and instead 00248 // add the quote indicators of the previous line 00249 int leadingWhiteSpaceCount = 0; 00250 while ( ( leadingWhiteSpaceCount < newLine.length() ) && 00251 newLine[leadingWhiteSpaceCount].isSpace() ) { 00252 ++leadingWhiteSpaceCount; 00253 } 00254 newLine = newLine.replace( 0, leadingWhiteSpaceCount, lineText.left( bot ) ); 00255 cursor.insertText( newLine ); 00256 //cursor.setPosition( cursor.position() + 2 ); 00257 cursor.movePosition( QTextCursor::StartOfBlock ); 00258 setTextCursor( cursor ); 00259 } 00260 } else { 00261 KRichTextWidget::keyPressEvent( e ); 00262 } 00263 } else { 00264 KRichTextWidget::keyPressEvent( e ); 00265 } 00266 } 00267 00268 bool TextEdit::isSpellCheckingEnabled() const 00269 { 00270 return d->spellCheckingEnabled; 00271 } 00272 00273 void TextEdit::setSpellCheckingEnabled( bool enable ) 00274 { 00275 EMailQuoteHighlighter *hlighter = dynamic_cast<EMailQuoteHighlighter*>( highlighter() ); 00276 if ( hlighter ) { 00277 hlighter->toggleSpellHighlighting( enable ); 00278 } 00279 00280 d->spellCheckingEnabled = enable; 00281 emit checkSpellingChanged( enable ); 00282 } 00283 00284 bool TextEdit::shouldBlockBeSpellChecked( const QString &block ) const 00285 { 00286 return !isLineQuoted( block ); 00287 } 00288 00289 bool KPIMTextEdit::TextEdit::isLineQuoted( const QString &line ) const 00290 { 00291 return quoteLength( line ) > 0; 00292 } 00293 00294 int KPIMTextEdit::TextEdit::quoteLength( const QString &line ) const 00295 { 00296 bool quoteFound = false; 00297 int startOfText = -1; 00298 for ( int i = 0; i < line.length(); i++ ) { 00299 if ( line[i] == QLatin1Char( '>' ) || line[i] == QLatin1Char( '|' ) ) { 00300 quoteFound = true; 00301 } else if ( line[i] != QLatin1Char( ' ' ) ) { 00302 startOfText = i; 00303 break; 00304 } 00305 } 00306 if ( quoteFound ) { 00307 if ( startOfText == -1 ) { 00308 startOfText = line.length() - 1; 00309 } 00310 return startOfText; 00311 } else { 00312 return 0; 00313 } 00314 } 00315 00316 const QString KPIMTextEdit::TextEdit::defaultQuoteSign() const 00317 { 00318 return QLatin1String( "> " ); 00319 } 00320 00321 void TextEdit::createHighlighter() 00322 { 00323 EMailQuoteHighlighter *emailHighLighter = new EMailQuoteHighlighter( this ); 00324 00325 setHighlighterColors( emailHighLighter ); 00326 00327 //TODO change config 00328 KRichTextWidget::setHighlighter( emailHighLighter ); 00329 00330 if ( !spellCheckingLanguage().isEmpty() ) { 00331 setSpellCheckingLanguage( spellCheckingLanguage() ); 00332 } 00333 setSpellCheckingEnabled( isSpellCheckingEnabled() ); 00334 } 00335 00336 void TextEdit::setHighlighterColors( EMailQuoteHighlighter *highlighter ) 00337 { 00338 Q_UNUSED( highlighter ); 00339 } 00340 00341 QString TextEdit::toWrappedPlainText() const 00342 { 00343 QString temp; 00344 QTextDocument *doc = document(); 00345 QTextBlock block = doc->begin(); 00346 while ( block.isValid() ) { 00347 QTextLayout *layout = block.layout(); 00348 const int numberOfLine( layout->lineCount() ); 00349 for ( int i = 0; i < numberOfLine; i++ ) { 00350 QTextLine line = layout->lineAt( i ); 00351 temp += block.text().mid( line.textStart(), line.textLength() ) + QLatin1Char( '\n' ); 00352 } 00353 block = block.next(); 00354 } 00355 00356 // Remove the last superfluous newline added above 00357 if ( temp.endsWith( QLatin1Char( '\n' ) ) ) { 00358 temp.chop( 1 ); 00359 } 00360 00361 d->fixupTextEditString( temp ); 00362 return temp; 00363 } 00364 00365 QString TextEdit::toCleanPlainText() const 00366 { 00367 QString temp = toPlainText(); 00368 d->fixupTextEditString( temp ); 00369 return temp; 00370 } 00371 00372 void TextEdit::createActions( KActionCollection *actionCollection ) 00373 { 00374 KRichTextWidget::createActions( actionCollection ); 00375 00376 if ( d->imageSupportEnabled ) { 00377 d->actionAddImage = new KAction( KIcon( QLatin1String( "insert-image" ) ), 00378 i18n( "Add Image" ), this ); 00379 actionCollection->addAction( QLatin1String( "add_image" ), d->actionAddImage ); 00380 connect( d->actionAddImage, SIGNAL(triggered(bool)), SLOT(_k_slotAddImage()) ); 00381 } 00382 00383 d->actionDeleteLine = new KAction( i18n( "Delete Line" ), this ); 00384 d->actionDeleteLine->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ) ); 00385 actionCollection->addAction( QLatin1String( "delete_line" ), d->actionDeleteLine ); 00386 connect( d->actionDeleteLine, SIGNAL(triggered(bool)), SLOT(_k_slotDeleteLine()) ); 00387 } 00388 00389 void TextEdit::addImage( const KUrl &url ) 00390 { 00391 QImage image; 00392 if ( !image.load( url.path() ) ) { 00393 KMessageBox::error( this, 00394 i18nc( "@info", 00395 "Unable to load image <filename>%1</filename>.", 00396 url.path() ) ); 00397 return; 00398 } 00399 QFileInfo fi( url.path() ); 00400 QString imageName = fi.baseName().isEmpty() ? QLatin1String( "image.png" ) 00401 : QString( fi.baseName() + QLatin1String( ".png" ) ); 00402 d->addImageHelper( imageName, image ); 00403 } 00404 00405 void TextEdit::loadImage ( const QImage &image, const QString &matchName, 00406 const QString &resourceName ) 00407 { 00408 QSet<int> cursorPositionsToSkip; 00409 QTextBlock currentBlock = document()->begin(); 00410 QTextBlock::iterator it; 00411 while ( currentBlock.isValid() ) { 00412 for ( it = currentBlock.begin(); !it.atEnd(); ++it ) { 00413 QTextFragment fragment = it.fragment(); 00414 if ( fragment.isValid() ) { 00415 QTextImageFormat imageFormat = fragment.charFormat().toImageFormat(); 00416 if ( imageFormat.isValid() && imageFormat.name() == matchName ) { 00417 int pos = fragment.position(); 00418 if ( !cursorPositionsToSkip.contains( pos ) ) { 00419 QTextCursor cursor( document() ); 00420 cursor.setPosition( pos ); 00421 cursor.setPosition( pos + 1, QTextCursor::KeepAnchor ); 00422 cursor.removeSelectedText(); 00423 document()->addResource( QTextDocument::ImageResource, 00424 QUrl( resourceName ), QVariant( image ) ); 00425 cursor.insertImage( resourceName ); 00426 00427 // The textfragment iterator is now invalid, restart from the beginning 00428 // Take care not to replace the same fragment again, or we would be in 00429 // an infinite loop. 00430 cursorPositionsToSkip.insert( pos ); 00431 it = currentBlock.begin(); 00432 } 00433 } 00434 } 00435 } 00436 currentBlock = currentBlock.next(); 00437 } 00438 } 00439 00440 void TextEditPrivate::addImageHelper( const QString &imageName, const QImage &image ) 00441 { 00442 QString imageNameToAdd = imageName; 00443 QTextDocument *document = q->document(); 00444 00445 // determine the imageNameToAdd 00446 int imageNumber = 1; 00447 while ( mImageNames.contains( imageNameToAdd ) ) { 00448 QVariant qv = document->resource( QTextDocument::ImageResource, QUrl( imageNameToAdd ) ); 00449 if ( qv == image ) { 00450 // use the same name 00451 break; 00452 } 00453 int firstDot = imageName.indexOf( QLatin1Char( '.' ) ); 00454 if ( firstDot == -1 ) { 00455 imageNameToAdd = imageName + QString::number( imageNumber++ ); 00456 } else { 00457 imageNameToAdd = imageName.left( firstDot ) + QString::number( imageNumber++ ) + 00458 imageName.mid( firstDot ); 00459 } 00460 } 00461 00462 if ( !mImageNames.contains( imageNameToAdd ) ) { 00463 document->addResource( QTextDocument::ImageResource, QUrl( imageNameToAdd ), image ); 00464 mImageNames << imageNameToAdd; 00465 } 00466 q->textCursor().insertImage( imageNameToAdd ); 00467 q->enableRichTextMode(); 00468 } 00469 00470 ImageWithNameList TextEdit::imagesWithName() const 00471 { 00472 ImageWithNameList retImages; 00473 QStringList seenImageNames; 00474 QList<QTextImageFormat> imageFormats = d->embeddedImageFormats(); 00475 foreach ( const QTextImageFormat &imageFormat, imageFormats ) { 00476 if ( !seenImageNames.contains( imageFormat.name() ) ) { 00477 QVariant resourceData = document()->resource( QTextDocument::ImageResource, 00478 QUrl( imageFormat.name() ) ); 00479 QImage image = qvariant_cast<QImage>( resourceData ); 00480 QString name = imageFormat.name(); 00481 ImageWithNamePtr newImage( new ImageWithName ); 00482 newImage->image = image; 00483 newImage->name = name; 00484 retImages.append( newImage ); 00485 seenImageNames.append( imageFormat.name() ); 00486 } 00487 } 00488 return retImages; 00489 } 00490 00491 QList< QSharedPointer<EmbeddedImage> > TextEdit::embeddedImages() const 00492 { 00493 ImageWithNameList normalImages = imagesWithName(); 00494 QList< QSharedPointer<EmbeddedImage> > retImages; 00495 foreach ( const ImageWithNamePtr &normalImage, normalImages ) { 00496 QBuffer buffer; 00497 buffer.open( QIODevice::WriteOnly ); 00498 normalImage->image.save( &buffer, "PNG" ); 00499 00500 qsrand( QDateTime::currentDateTime().toTime_t() + qHash( normalImage->name ) ); 00501 QSharedPointer<EmbeddedImage> embeddedImage( new EmbeddedImage() ); 00502 retImages.append( embeddedImage ); 00503 embeddedImage->image = KMime::Codec::codecForName( "base64" )->encode( buffer.buffer() ); 00504 embeddedImage->imageName = normalImage->name; 00505 embeddedImage->contentID = QString( QLatin1String( "%1@KDE" ) ).arg( qrand() ); 00506 } 00507 return retImages; 00508 } 00509 00510 QList<QTextImageFormat> TextEditPrivate::embeddedImageFormats() const 00511 { 00512 QTextDocument *doc = q->document(); 00513 QList<QTextImageFormat> retList; 00514 00515 QTextBlock currentBlock = doc->begin(); 00516 while ( currentBlock.isValid() ) { 00517 QTextBlock::iterator it; 00518 for ( it = currentBlock.begin(); !it.atEnd(); ++it ) { 00519 QTextFragment fragment = it.fragment(); 00520 if ( fragment.isValid() ) { 00521 QTextImageFormat imageFormat = fragment.charFormat().toImageFormat(); 00522 if ( imageFormat.isValid() ) { 00523 //TODO: Replace with a way to see if an image is an embedded image or a remote 00524 QUrl url(imageFormat.name()); 00525 if( !url.isValid() || !url.scheme().startsWith( QLatin1String( "http" ) ) ) { 00526 retList.append( imageFormat ); 00527 } 00528 } 00529 } 00530 } 00531 currentBlock = currentBlock.next(); 00532 } 00533 return retList; 00534 } 00535 00536 void TextEditPrivate::_k_slotAddImage() 00537 { 00538 QPointer<KFileDialog> fdlg = new KFileDialog( QString(), QString(), q ); 00539 fdlg->setOperationMode( KFileDialog::Other ); 00540 fdlg->setCaption( i18n( "Add Image" ) ); 00541 fdlg->okButton()->setGuiItem( KGuiItem( i18n( "&Add" ), QLatin1String( "document-open" ) ) ); 00542 fdlg->setMode( KFile::Files ); 00543 if ( fdlg->exec() != KDialog::Accepted ) { 00544 delete fdlg; 00545 return; 00546 } 00547 00548 const KUrl::List files = fdlg->selectedUrls(); 00549 foreach ( const KUrl &url, files ) { 00550 q->addImage( url ); 00551 } 00552 delete fdlg; 00553 } 00554 00555 void KPIMTextEdit::TextEdit::enableImageActions() 00556 { 00557 d->imageSupportEnabled = true; 00558 } 00559 00560 bool KPIMTextEdit::TextEdit::isEnableImageActions() const 00561 { 00562 return d->imageSupportEnabled; 00563 } 00564 00565 QByteArray KPIMTextEdit::TextEdit::imageNamesToContentIds( 00566 const QByteArray &htmlBody, const KPIMTextEdit::ImageList &imageList ) 00567 { 00568 QByteArray result = htmlBody; 00569 if ( !imageList.isEmpty() ) { 00570 foreach ( const QSharedPointer<EmbeddedImage> &image, imageList ) { 00571 const QString newImageName = QLatin1String( "cid:" ) + image->contentID; 00572 QByteArray quote( "\"" ); 00573 result.replace( QByteArray( quote + image->imageName.toLocal8Bit() + quote ), 00574 QByteArray( quote + newImageName.toLocal8Bit() + quote ) ); 00575 } 00576 } 00577 return result; 00578 } 00579 00580 void TextEdit::insertImage( const QImage &image, const QFileInfo &fileInfo ) 00581 { 00582 QString imageName = fileInfo.baseName().isEmpty() ? 00583 i18nc( "Start of the filename for an image", "image" ) : 00584 fileInfo.baseName(); 00585 d->addImageHelper( imageName, image ); 00586 } 00587 00588 void TextEdit::insertFromMimeData( const QMimeData *source ) 00589 { 00590 // Add an image if that is on the clipboard 00591 if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled ) { 00592 QImage image = qvariant_cast<QImage>( source->imageData() ); 00593 QFileInfo fi( source->text() ); 00594 insertImage( image, fi ); 00595 return; 00596 } 00597 00598 // Attempt to paste HTML contents into the text edit in plain text mode, 00599 // prevent this and prevent plain text instead. 00600 if ( textMode() == KRichTextEdit::Plain && source->hasHtml() ) { 00601 if ( source->hasText() ) { 00602 insertPlainText( source->text() ); 00603 return; 00604 } 00605 } 00606 00607 KRichTextWidget::insertFromMimeData( source ); 00608 } 00609 00610 bool KPIMTextEdit::TextEdit::canInsertFromMimeData( const QMimeData *source ) const 00611 { 00612 if ( source->hasHtml() && textMode() == KRichTextEdit::Rich ) { 00613 return true; 00614 } 00615 00616 if ( source->hasText() ) { 00617 return true; 00618 } 00619 00620 if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled ) { 00621 return true; 00622 } 00623 00624 return KRichTextWidget::canInsertFromMimeData( source ); 00625 } 00626 00627 bool TextEdit::isFormattingUsed() const 00628 { 00629 if ( textMode() == Plain ) { 00630 return false; 00631 } 00632 00633 return TextUtils::containsFormatting( document() ); 00634 } 00635 00636 void TextEditPrivate::_k_slotDeleteLine() 00637 { 00638 if ( q->hasFocus() ) { 00639 q->deleteCurrentLine(); 00640 } 00641 } 00642 00643 void TextEdit::deleteCurrentLine() 00644 { 00645 QTextCursor cursor = textCursor(); 00646 QTextBlock block = cursor.block(); 00647 const QTextLayout *layout = block.layout(); 00648 00649 // The current text block can have several lines due to word wrapping. 00650 // Search the line the cursor is in, and then delete it. 00651 for ( int lineNumber = 0; lineNumber < layout->lineCount(); lineNumber++ ) { 00652 QTextLine line = layout->lineAt( lineNumber ); 00653 const bool lastLineInBlock = ( line.textStart() + line.textLength() == block.length() - 1 ); 00654 const bool oneLineBlock = ( layout->lineCount() == 1 ); 00655 const int startOfLine = block.position() + line.textStart(); 00656 int endOfLine = block.position() + line.textStart() + line.textLength(); 00657 if ( !lastLineInBlock ) { 00658 endOfLine -= 1; 00659 } 00660 00661 // Found the line where the cursor is in 00662 if ( cursor.position() >= startOfLine && cursor.position() <= endOfLine ) { 00663 int deleteStart = startOfLine; 00664 int deleteLength = line.textLength(); 00665 if ( oneLineBlock ) { 00666 deleteLength++; // The trailing newline 00667 } 00668 00669 // When deleting the last line in the document, 00670 // remove the newline of the line before the last line instead 00671 if ( deleteStart + deleteLength >= document()->characterCount() && 00672 deleteStart > 0 ) { 00673 deleteStart--; 00674 } 00675 00676 cursor.beginEditBlock(); 00677 cursor.setPosition( deleteStart ); 00678 cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor, deleteLength ); 00679 cursor.removeSelectedText(); 00680 cursor.endEditBlock(); 00681 return; 00682 } 00683 } 00684 00685 } 00686 00687 #include "textedit.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 22:17:38 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 22:17:38 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.