• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

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 namespace KPIMTextEdit {
00046 
00047 class TextEditPrivate
00048 {
00049   public:
00050 
00051     TextEditPrivate( TextEdit *parent )
00052       : actionAddImage( 0 ),
00053         actionDeleteLine( 0 ),
00054         q( parent ),
00055         imageSupportEnabled( false )
00056     {
00057     }
00058 
00067     void addImageHelper( const QString &imageName, const QImage &image );
00068 
00072     QList<QTextImageFormat> embeddedImageFormats() const;
00073 
00078     void fixupTextEditString( QString &text ) const;
00079 
00083     void init();
00084 
00089     void _k_slotAddImage();
00090 
00091     void _k_slotDeleteLine();
00092 
00094     KAction *actionAddImage;
00095 
00097     KAction *actionDeleteLine;
00098 
00100     TextEdit *q;
00101 
00103     bool imageSupportEnabled;
00104 
00110     QStringList mImageNames;
00111 
00123     bool spellCheckingEnabled;
00124 };
00125 
00126 } // namespace
00127 
00128 using namespace KPIMTextEdit;
00129 
00130 void TextEditPrivate::fixupTextEditString( QString &text ) const
00131 {
00132   // Remove line separators. Normal \n chars are still there, so no linebreaks get lost here
00133   text.remove( QChar::LineSeparator );
00134 
00135   // Get rid of embedded images, see QTextImageFormat documentation:
00136   // "Inline images are represented by an object replacement character (0xFFFC in Unicode) "
00137   text.remove( 0xFFFC );
00138 
00139   // In plaintext mode, each space is non-breaking.
00140   text.replace( QChar::Nbsp, QChar::fromAscii( ' ' ) );
00141 }
00142 
00143 TextEdit::TextEdit( const QString& text, QWidget *parent )
00144   : KRichTextWidget( text, parent ),
00145     d( new TextEditPrivate( this ) )
00146 {
00147   d->init();
00148 }
00149 
00150 TextEdit::TextEdit( QWidget *parent )
00151   : KRichTextWidget( parent ),
00152     d( new TextEditPrivate( this ) )
00153 {
00154   d->init();
00155 }
00156 
00157 TextEdit::~TextEdit()
00158 {
00159 }
00160 
00161 bool TextEdit::eventFilter( QObject*o, QEvent* e )
00162 {
00163   if ( o == this )
00164     KCursor::autoHideEventFilter( o, e );
00165   return KRichTextWidget::eventFilter( o, e );
00166 }
00167 
00168 void TextEditPrivate::init()
00169 {
00170   q->setSpellInterface( q );
00171   // We tell the KRichTextWidget to enable spell checking, because only then it will
00172   // call createHighlighter() which will create our own highlighter which also
00173   // does quote highlighting.
00174   // However, *our* spellchecking is still disabled. Our own highlighter only
00175   // cares about our spellcheck status, it will not highlight missspelled words
00176   // if our spellchecking is disabled.
00177   // See also KEMailQuotingHighlighter::highlightBlock().
00178   spellCheckingEnabled = false;
00179   q->setCheckSpellingEnabledInternal( true );
00180 
00181   KCursor::setAutoHideCursor( q, true, true );
00182   q->installEventFilter( q );
00183 }
00184 
00185 void TextEdit::keyPressEvent ( QKeyEvent * e )
00186 {
00187   if ( e->key() ==  Qt::Key_Return ) {
00188     QTextCursor cursor = textCursor();
00189     int oldPos = cursor.position();
00190     int blockPos = cursor.block().position();
00191 
00192     //selection all the line.
00193     cursor.movePosition( QTextCursor::StartOfBlock );
00194     cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
00195     QString lineText = cursor.selectedText();
00196     if ( ( ( oldPos -blockPos )  > 0 ) &&
00197          ( ( oldPos-blockPos ) < int( lineText.length() ) ) ) {
00198       bool isQuotedLine = false;
00199       int bot = 0; // bot = begin of text after quote indicators
00200       while ( bot < lineText.length() ) {
00201         if( ( lineText[bot] == QChar::fromAscii( '>' ) ) ||
00202             ( lineText[bot] == QChar::fromAscii( '|' ) ) ) {
00203           isQuotedLine = true;
00204           ++bot;
00205         }
00206         else if ( lineText[bot].isSpace() ) {
00207           ++bot;
00208         }
00209         else {
00210           break;
00211         }
00212       }
00213       KRichTextWidget::keyPressEvent( e );
00214       // duplicate quote indicators of the previous line before the new
00215       // line if the line actually contained text (apart from the quote
00216       // indicators) and the cursor is behind the quote indicators
00217       if ( isQuotedLine
00218            && ( bot != lineText.length() )
00219            && ( ( oldPos-blockPos ) >= int( bot ) ) ) {
00220         // The cursor position might have changed unpredictably if there was selected
00221         // text which got replaced by a new line, so we query it again:
00222         cursor.movePosition( QTextCursor::StartOfBlock );
00223         cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
00224         QString newLine = cursor.selectedText();
00225 
00226         // remove leading white space from the new line and instead
00227         // add the quote indicators of the previous line
00228         int leadingWhiteSpaceCount = 0;
00229         while ( ( leadingWhiteSpaceCount < newLine.length() )
00230                   && newLine[leadingWhiteSpaceCount].isSpace() ) {
00231           ++leadingWhiteSpaceCount;
00232         }
00233         newLine = newLine.replace( 0, leadingWhiteSpaceCount,
00234                                    lineText.left( bot ) );
00235         cursor.insertText( newLine );
00236         //cursor.setPosition( cursor.position() + 2);
00237         cursor.movePosition( QTextCursor::StartOfBlock );
00238         setTextCursor( cursor );
00239       }
00240     }
00241     else
00242       KRichTextWidget::keyPressEvent( e );
00243   }
00244   else
00245   {
00246     KRichTextWidget::keyPressEvent( e );
00247   }
00248 }
00249 
00250 
00251 bool TextEdit::isSpellCheckingEnabled() const
00252 {
00253   return d->spellCheckingEnabled;
00254 }
00255 
00256 void TextEdit::setSpellCheckingEnabled( bool enable )
00257 {
00258   EMailQuoteHighlighter *hlighter =
00259       dynamic_cast<EMailQuoteHighlighter*>( highlighter() );
00260   if ( hlighter )
00261     hlighter->toggleSpellHighlighting( enable );
00262 
00263   d->spellCheckingEnabled = enable;
00264   emit checkSpellingChanged( enable );
00265 }
00266 
00267 bool TextEdit::shouldBlockBeSpellChecked( const QString& block ) const
00268 {
00269   return !isLineQuoted( block );
00270 }
00271 
00272 bool KPIMTextEdit::TextEdit::isLineQuoted( const QString& line ) const
00273 {
00274   return quoteLength( line ) > 0;
00275 }
00276 
00277 int KPIMTextEdit::TextEdit::quoteLength( const QString& line ) const
00278 {
00279   bool quoteFound = false;
00280   int startOfText = -1;
00281   for ( int i = 0; i < line.length(); i++ ) {
00282     if ( line[i] == QLatin1Char( '>' ) || line[i] == QLatin1Char( '|' ) )
00283       quoteFound = true;
00284     else if ( line[i] != QLatin1Char( ' ' ) ) {
00285       startOfText = i;
00286       break;
00287     }
00288   }
00289   if ( quoteFound ) {
00290     if ( startOfText == -1 )
00291       startOfText = line.length() - 1;
00292     return startOfText;
00293   }
00294   else
00295     return 0;
00296 }
00297 
00298 const QString KPIMTextEdit::TextEdit::defaultQuoteSign() const
00299 {
00300   return QLatin1String( "> " );
00301 }
00302 
00303 void TextEdit::createHighlighter()
00304 {
00305   EMailQuoteHighlighter *emailHighLighter =
00306       new EMailQuoteHighlighter( this );
00307 
00308   setHighlighterColors( emailHighLighter );
00309 
00310   //TODO change config
00311   KRichTextWidget::setHighlighter( emailHighLighter );
00312 
00313   if ( !spellCheckingLanguage().isEmpty() )
00314     setSpellCheckingLanguage( spellCheckingLanguage() );
00315   setSpellCheckingEnabled( isSpellCheckingEnabled() );
00316 }
00317 
00318 void TextEdit::setHighlighterColors( EMailQuoteHighlighter *highlighter )
00319 {
00320   Q_UNUSED( highlighter );
00321 }
00322 
00323 QString TextEdit::toWrappedPlainText() const
00324 {
00325   QString temp;
00326   QTextDocument* doc = document();
00327   QTextBlock block = doc->begin();
00328   while ( block.isValid() ) {
00329     QTextLayout* layout = block.layout();
00330     for ( int i = 0; i < layout->lineCount(); i++ ) {
00331       QTextLine line = layout->lineAt( i );
00332       temp += block.text().mid( line.textStart(), line.textLength() ) + QLatin1Char( '\n' );
00333     }
00334     block = block.next();
00335   }
00336 
00337   // Remove the last superfluous newline added above
00338   if ( temp.endsWith( QLatin1Char( '\n' ) ) )
00339     temp.chop( 1 );
00340 
00341   d->fixupTextEditString( temp );
00342   return temp;
00343 }
00344 
00345 QString TextEdit::toCleanPlainText() const
00346 {
00347   QString temp = toPlainText();
00348   d->fixupTextEditString( temp );
00349   return temp;
00350 }
00351 
00352 void TextEdit::createActions( KActionCollection *actionCollection )
00353 {
00354   KRichTextWidget::createActions( actionCollection );
00355 
00356   if ( d->imageSupportEnabled ) {
00357     d->actionAddImage = new KAction( KIcon( QLatin1String( "insert-image" ) ),
00358                                     i18n( "Add Image" ), this );
00359     actionCollection->addAction( QLatin1String( "add_image" ), d->actionAddImage );
00360     connect( d->actionAddImage, SIGNAL(triggered(bool) ), SLOT( _k_slotAddImage() ) );
00361   }
00362 
00363   d->actionDeleteLine = new KAction( i18n( "Delete Line" ), this );
00364   d->actionDeleteLine->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ) );
00365   actionCollection->addAction( QLatin1String( "delete_line" ), d->actionDeleteLine );
00366   connect( d->actionDeleteLine, SIGNAL(triggered(bool)), SLOT(_k_slotDeleteLine()) );
00367 }
00368 
00369 void TextEdit::addImage( const KUrl &url )
00370 {
00371   QImage image;
00372   if ( !image.load( url.path() ) ) {
00373     KMessageBox::error( this,
00374                         i18nc( "@info", "Unable to load image <filename>%1</filename>.", url.path() ) );
00375     return;
00376   }
00377   QFileInfo fi( url.path() );
00378   QString imageName = fi.baseName().isEmpty() ? QLatin1String( "image.png" )
00379                                               : fi.baseName() + QLatin1String( ".png" );
00380   d->addImageHelper( imageName, image );
00381 }
00382 
00383 void TextEdit::loadImage ( const QImage& image, const QString& matchName, const QString& resourceName )
00384 {
00385   QSet<int> cursorPositionsToSkip;
00386   QTextBlock currentBlock = document()->begin();
00387   QTextBlock::iterator it;
00388   while ( currentBlock.isValid() ) {
00389     for (it = currentBlock.begin(); !(it.atEnd()); ++it) {
00390       QTextFragment fragment = it.fragment();
00391       if ( fragment.isValid() ) {
00392         QTextImageFormat imageFormat = fragment.charFormat().toImageFormat();
00393         if ( imageFormat.isValid() && imageFormat.name() == matchName ) {
00394           int pos = fragment.position();
00395           if ( !cursorPositionsToSkip.contains( pos ) ) {
00396             QTextCursor cursor( document() );
00397             cursor.setPosition( pos );
00398             cursor.setPosition( pos + 1, QTextCursor::KeepAnchor );
00399             cursor.removeSelectedText();
00400             document()->addResource( QTextDocument::ImageResource, QUrl( resourceName ), QVariant( image ) );
00401             cursor.insertImage( resourceName );
00402 
00403             // The textfragment iterator is now invalid, restart from the beginning
00404             // Take care not to replace the same fragment again, or we would be in an infinite loop.
00405             cursorPositionsToSkip.insert( pos );
00406             it = currentBlock.begin();
00407           }
00408         }
00409       }
00410     }
00411     currentBlock = currentBlock.next();
00412   }
00413 }
00414 
00415 void TextEditPrivate::addImageHelper( const QString &imageName, const QImage &image )
00416 {
00417   QString imageNameToAdd = imageName;
00418   QTextDocument *document = q->document();
00419 
00420   // determine the imageNameToAdd
00421   int imageNumber = 1;
00422   while ( mImageNames.contains( imageNameToAdd ) ) {
00423     QVariant qv = document->resource( QTextDocument::ImageResource, QUrl( imageNameToAdd ) );
00424     if ( qv == image ) {
00425       // use the same name
00426       break;
00427     }
00428     int firstDot = imageName.indexOf( QLatin1Char( '.' ) );
00429     if ( firstDot == -1 )
00430       imageNameToAdd = imageName + QString::number( imageNumber++ );
00431     else
00432       imageNameToAdd = imageName.left( firstDot ) + QString::number( imageNumber++ ) +
00433                        imageName.mid( firstDot );
00434   }
00435 
00436   if ( !mImageNames.contains( imageNameToAdd ) ) {
00437     document->addResource( QTextDocument::ImageResource, QUrl( imageNameToAdd ), image );
00438     mImageNames << imageNameToAdd;
00439   }
00440   q->textCursor().insertImage( imageNameToAdd );
00441   q->enableRichTextMode();
00442 }
00443 
00444 ImageWithNameList TextEdit::imagesWithName() const
00445 {
00446   ImageWithNameList retImages;
00447   QStringList seenImageNames;
00448   QList<QTextImageFormat> imageFormats = d->embeddedImageFormats();
00449   foreach( const QTextImageFormat &imageFormat, imageFormats ) {
00450     if ( !seenImageNames.contains( imageFormat.name() ) ) {
00451       QVariant data = document()->resource( QTextDocument::ImageResource, QUrl( imageFormat.name() ) );
00452       QImage image = qvariant_cast<QImage>( data );
00453       QString name = imageFormat.name();
00454       ImageWithNamePtr newImage( new ImageWithName );
00455       newImage->image = image;
00456       newImage->name = name;
00457       retImages.append( newImage );
00458       seenImageNames.append( imageFormat.name() );
00459     }
00460   }
00461   return retImages;
00462 }
00463 
00464 QList< QSharedPointer<EmbeddedImage> > TextEdit::embeddedImages() const
00465 {
00466   ImageWithNameList normalImages = imagesWithName();
00467   QList< QSharedPointer<EmbeddedImage> > retImages;
00468   foreach( const ImageWithNamePtr &normalImage, normalImages ) {
00469     QBuffer buffer;
00470     buffer.open( QIODevice::WriteOnly );
00471     normalImage->image.save( &buffer, "PNG" );
00472 
00473     qsrand( QDateTime::currentDateTime().toTime_t() + qHash( normalImage->name ) );
00474     QSharedPointer<EmbeddedImage> embeddedImage( new EmbeddedImage() );
00475     retImages.append( embeddedImage );
00476     embeddedImage->image = KMime::Codec::codecForName( "base64" )->encode( buffer.buffer() );
00477     embeddedImage->imageName = normalImage->name;
00478     embeddedImage->contentID = QString( QLatin1String( "%1@KDE" ) ).arg( qrand() );
00479   }
00480   return retImages;
00481 }
00482 
00483 QList<QTextImageFormat> TextEditPrivate::embeddedImageFormats() const
00484 {
00485   QTextDocument *doc = q->document();
00486   QList<QTextImageFormat> retList;
00487 
00488   QTextBlock currentBlock = doc->begin();
00489   while ( currentBlock.isValid() ) {
00490     QTextBlock::iterator it;
00491     for ( it = currentBlock.begin(); !it.atEnd(); ++it ) {
00492       QTextFragment fragment = it.fragment();
00493       if ( fragment.isValid() ) {
00494         QTextImageFormat imageFormat = fragment.charFormat().toImageFormat();
00495         if ( imageFormat.isValid() ) {
00496           retList.append( imageFormat );
00497         }
00498       }
00499     }
00500     currentBlock = currentBlock.next();
00501   }
00502   return retList;
00503 }
00504 
00505 void TextEditPrivate::_k_slotAddImage()
00506 {
00507   QPointer<KFileDialog> fdlg = new KFileDialog( QString(), QString(), q );
00508   fdlg->setOperationMode( KFileDialog::Other );
00509   fdlg->setCaption( i18n("Add Image") );
00510   fdlg->okButton()->setGuiItem( KGuiItem( i18n("&Add"), QLatin1String( "document-open" ) ) );
00511   fdlg->setMode( KFile::Files );
00512   if ( fdlg->exec() != KDialog::Accepted ) {
00513     delete fdlg;
00514     return;
00515   }
00516 
00517   const KUrl::List files = fdlg->selectedUrls();
00518   foreach ( const KUrl& url, files ) {
00519     q->addImage( url );
00520   }
00521   delete fdlg;
00522 }
00523 
00524 void KPIMTextEdit::TextEdit::enableImageActions()
00525 {
00526   d->imageSupportEnabled = true;
00527 }
00528 
00529 QByteArray KPIMTextEdit::TextEdit::imageNamesToContentIds( const QByteArray &htmlBody, const KPIMTextEdit::ImageList &imageList )
00530 {
00531   QByteArray result = htmlBody;
00532   if ( imageList.size() > 0 ) {
00533     foreach( const QSharedPointer<EmbeddedImage> &image, imageList ) {
00534       const QString newImageName = QLatin1String( "cid:" ) + image->contentID;
00535       QByteArray quote( "\"" );
00536       result.replace( QByteArray( quote + image->imageName.toLocal8Bit() + quote ),
00537                       QByteArray( quote + newImageName.toLocal8Bit() + quote ) );
00538     }
00539   }
00540   return result;
00541 }
00542 
00543 void TextEdit::insertFromMimeData( const QMimeData *source )
00544 {
00545   // Add an image if that is on the clipboard
00546   if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled ) {
00547     QImage image = qvariant_cast<QImage>( source->imageData() );
00548     QFileInfo fi( source->text() );
00549     QString imageName = fi.baseName().isEmpty() ? i18nc( "Start of the filename for an image", "image" ) : fi.baseName();
00550     d->addImageHelper( imageName, image );
00551     return;
00552   }
00553 
00554   // Attempt to paste HTML contents into the text edit in plain text mode,
00555   // prevent this and prevent plain text instead.
00556   if ( textMode() == KRichTextEdit::Plain && source->hasHtml() ) {
00557     if ( source->hasText() ) {
00558       insertPlainText( source->text() );
00559       return;
00560     }
00561   }
00562 
00563   KRichTextWidget::insertFromMimeData( source );
00564 }
00565 
00566 bool KPIMTextEdit::TextEdit::canInsertFromMimeData( const QMimeData *source ) const
00567 {
00568   if ( source->hasHtml() && textMode() == KRichTextEdit::Rich )
00569     return true;
00570   if ( source->hasText() )
00571     return true;
00572   if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled )
00573     return true;
00574 
00575   return KRichTextWidget::canInsertFromMimeData( source );
00576 }
00577 
00578 static bool isCharFormatFormatted( const QTextCharFormat &format, const QFont &defaultFont,
00579                                    const QTextCharFormat &defaultBlockFormat )
00580 {
00581   if ( !format.anchorHref().isEmpty() ||
00582        format.font() != defaultFont ||
00583        format.isAnchor() ||
00584        format.verticalAlignment() != defaultBlockFormat.verticalAlignment() ||
00585        format.underlineStyle() != defaultBlockFormat.underlineStyle() ||
00586        format.foreground().color() != defaultBlockFormat.foreground().color() ||
00587        format.background().color() != defaultBlockFormat.background().color() )
00588     return true;
00589 
00590   return false;
00591 }
00592 
00593 static bool isBlockFormatFormatted( const QTextBlockFormat &format,
00594                                     const QTextBlockFormat &defaultFormat )
00595 {
00596   if ( format.alignment() != defaultFormat.alignment() ||
00597        format.indent() != defaultFormat.indent() ||
00598        format.textIndent() != defaultFormat.textIndent() )
00599     return true;
00600 
00601   return false;
00602 }
00603 
00605 static bool isSpecial( const QTextFormat &charFormat )
00606 {
00607   return charFormat.isFrameFormat() || charFormat.isImageFormat() ||
00608          charFormat.isListFormat() || charFormat.isTableFormat();
00609 }
00610 
00611 bool TextEdit::isFormattingUsed() const
00612 {
00613   if ( textMode() == Plain )
00614     return false;
00615 
00616   // Below, we walk through all text blocks and through all text fragments in them
00617   // and check if any of those has any formatting.
00618   // To check if they have formatting, we use the functions isBlockFormatFormatted() and
00619   // isCharFormatFormatted(). Those do not check all the exising formatting possibilities on
00620   // earth, but everything that KRichTextEdit supports at the moment.
00621   //
00622   // Also, we have to compare the formats against those of a default text edit. For example,
00623   // we can't compare the foreground color against black, because the user might have another
00624   // color scheme. Therefore we compare the foreground color against a default text edit.
00625 
00626   QTextEdit defaultTextEdit;
00627   QTextCharFormat defaultCharFormat = defaultTextEdit.document()->begin().charFormat();
00628   QTextBlockFormat defaultBlockFormat = defaultTextEdit.document()->begin().blockFormat();
00629   QFont defaultFont = document()->defaultFont();
00630 
00631   QTextBlock block = document()->firstBlock();
00632   while ( block.isValid() ) {
00633 
00634     if ( isBlockFormatFormatted( block.blockFormat(), defaultBlockFormat ) ) {
00635       return true;
00636     }
00637 
00638     if ( isSpecial( block.charFormat() ) || isSpecial( block.blockFormat() ) ||
00639          block.textList() ) {
00640       return true;
00641     }
00642 
00643     QTextBlock::iterator it = block.begin();
00644     while ( !it.atEnd() ) {
00645       QTextFragment fragment = it.fragment();
00646       QTextCharFormat charFormat = fragment.charFormat();
00647       if ( isSpecial( charFormat ) ) {
00648         return true;
00649       }
00650       if ( isCharFormatFormatted( fragment.charFormat(), defaultFont, defaultCharFormat ) ) {
00651         return true;
00652       }
00653 
00654       it++;
00655     }
00656     block = block.next();
00657   }
00658 
00659   if ( toHtml().contains( QLatin1String( "<hr />" ) ) )
00660     return true;
00661 
00662   return false;
00663 }
00664 
00665 void TextEditPrivate::_k_slotDeleteLine()
00666 {
00667   q->deleteCurrentLine();
00668 }
00669 
00670 void TextEdit::deleteCurrentLine()
00671 {
00672   QTextCursor cursor = textCursor();
00673   QTextBlock block = cursor.block();
00674   const QTextLayout* layout = block.layout();
00675 
00676   // The current text block can have several lines due to word wrapping.
00677   // Search the line the cursor is in, and then delete it.
00678   for ( int lineNumber = 0; lineNumber < layout->lineCount(); lineNumber++ ) {
00679     QTextLine line = layout->lineAt( lineNumber );
00680     const bool lastLineInBlock = ( line.textStart() + line.textLength() == block.length() - 1 );
00681     const bool oneLineBlock = ( layout->lineCount() == 1 );
00682     const int startOfLine = block.position() + line.textStart();
00683     int endOfLine = block.position() + line.textStart() + line.textLength();
00684     if ( !lastLineInBlock )
00685       endOfLine -= 1;
00686 
00687     // Found the line where the cursor is in
00688     if ( cursor.position() >= startOfLine && cursor.position() <= endOfLine ) {
00689       int deleteStart = startOfLine;
00690       int deleteLength = line.textLength();
00691       if ( oneLineBlock )
00692         deleteLength++; // The trailing newline
00693 
00694       // When deleting the last line in the document,
00695       // remove the newline of the line before the last line instead
00696       if ( deleteStart + deleteLength >= document()->characterCount() &&
00697            deleteStart > 0 )
00698         deleteStart--;
00699 
00700       cursor.beginEditBlock();
00701       cursor.setPosition( deleteStart );
00702       cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor, deleteLength );
00703       cursor.removeSelectedText();
00704       cursor.endEditBlock();
00705       return;
00706     }
00707   }
00708 
00709 }
00710 
00711 
00712 #include "textedit.moc"

KPIMTextedit Library

Skip menu "KPIMTextedit Library"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal