diff --git a/kpimtextedit/textedit.cpp b/kpimtextedit/textedit.cpp index 7584da7f3..1ddf9205b 100644 --- a/kpimtextedit/textedit.cpp +++ b/kpimtextedit/textedit.cpp @@ -1,666 +1,704 @@ /* Copyright (c) 2009 Thomas McGuire Based on KMail and libkdepim code by: Copyright 2007 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "textedit.h" #include "emailquotehighlighter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPIMTextEdit { class TextEditPrivate { public: TextEditPrivate( TextEdit *parent ) : actionAddImage( 0 ), actionDeleteLine( 0 ), q( parent ), imageSupportEnabled( false ) { } /** * Helper function for addImage(), which does the actual work of adding the QImage as a * resource to the document, pasting it and adding it to the image name list. * * @param imageName the desired image name. If it is already taken, a number will * be appended to it * @param image the actual image to add */ void addImageHelper( const QString &imageName, const QImage &image ); /** * Helper function to get the list of all QTextImageFormats in the document. */ QList embeddedImageFormats() const; /** * Removes embedded image markers, converts non-breaking spaces to normal spaces * and other fixes for strings that came from toPlainText()/toHtml(). */ void fixupTextEditString( QString &text ) const; /** * Does the constructor work */ void init(); /** * Opens a file dialog to let the user choose an image and then pastes that * image to the editor */ void _k_slotAddImage(); void _k_slotDeleteLine(); /// The action that triggers _k_slotAddImage() KAction *actionAddImage; /// The action that triggers _k_slotDeleteLine() KAction *actionDeleteLine; /// The parent class TextEdit *q; /// Whether or not adding or pasting images is supported bool imageSupportEnabled; /** * The names of embedded images. * Used to easily obtain the names of the images. * New images are compared to the the list and not added as resource if already present. */ QStringList mImageNames; /** * Although KTextEdit keeps track of the spell checking state, we override * it here, because we have a highlighter which does quote highlighting. * And since disabling spellchecking in KTextEdit simply would turn off our * quote highlighter, we never actually deactivate spell checking in the * base class, but only tell our own email highlighter to not highlight * spelling mistakes. * For this, we use the KTextEditSpellInterface, which is basically a hack * that makes it possible to have our own enabled/disabled state in a binary * compatible way. */ bool spellCheckingEnabled; }; } // namespace using namespace KPIMTextEdit; void TextEditPrivate::fixupTextEditString( QString &text ) const { // Remove line separators. Normal \n chars are still there, so no linebreaks get lost here text.remove( QChar::LineSeparator ); // Get rid of embedded images, see QTextImageFormat documentation: // "Inline images are represented by an object replacement character (0xFFFC in Unicode) " text.remove( 0xFFFC ); // In plaintext mode, each space is non-breaking. text.replace( QChar::Nbsp, QChar::fromAscii( ' ' ) ); } TextEdit::TextEdit( const QString& text, QWidget *parent ) : KRichTextWidget( text, parent ), d( new TextEditPrivate( this ) ) { d->init(); } TextEdit::TextEdit( QWidget *parent ) : KRichTextWidget( parent ), d( new TextEditPrivate( this ) ) { d->init(); } TextEdit::~TextEdit() { } bool TextEdit::eventFilter( QObject*o, QEvent* e ) { if ( o == this ) KCursor::autoHideEventFilter( o, e ); return KRichTextWidget::eventFilter( o, e ); } void TextEditPrivate::init() { q->setSpellInterface( q ); // We tell the KRichTextWidget to enable spell checking, because only then it will // call createHighlighter() which will create our own highlighter which also // does quote highlighting. // However, *our* spellchecking is still disabled. Our own highlighter only // cares about our spellcheck status, it will not highlight missspelled words // if our spellchecking is disabled. // See also KEMailQuotingHighlighter::highlightBlock(). spellCheckingEnabled = false; q->setCheckSpellingEnabledInternal( true ); KCursor::setAutoHideCursor( q, true, true ); q->installEventFilter( q ); } void TextEdit::keyPressEvent ( QKeyEvent * e ) { if ( e->key() == Qt::Key_Return ) { QTextCursor cursor = textCursor(); int oldPos = cursor.position(); int blockPos = cursor.block().position(); //selection all the line. cursor.movePosition( QTextCursor::StartOfBlock ); cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); QString lineText = cursor.selectedText(); if ( ( ( oldPos -blockPos ) > 0 ) && ( ( oldPos-blockPos ) < int( lineText.length() ) ) ) { bool isQuotedLine = false; int bot = 0; // bot = begin of text after quote indicators while ( bot < lineText.length() ) { if( ( lineText[bot] == QChar::fromAscii( '>' ) ) || ( lineText[bot] == QChar::fromAscii( '|' ) ) ) { isQuotedLine = true; ++bot; } else if ( lineText[bot].isSpace() ) { ++bot; } else { break; } } KRichTextWidget::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() ) && ( ( oldPos-blockPos ) >= 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: cursor.movePosition( QTextCursor::StartOfBlock ); cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); QString newLine = cursor.selectedText(); // remove leading white space from the new line and instead // add the quote indicators of the previous line int leadingWhiteSpaceCount = 0; while ( ( leadingWhiteSpaceCount < newLine.length() ) && newLine[leadingWhiteSpaceCount].isSpace() ) { ++leadingWhiteSpaceCount; } newLine = newLine.replace( 0, leadingWhiteSpaceCount, lineText.left( bot ) ); cursor.insertText( newLine ); //cursor.setPosition( cursor.position() + 2); cursor.movePosition( QTextCursor::StartOfBlock ); setTextCursor( cursor ); } } else KRichTextWidget::keyPressEvent( e ); } else { KRichTextWidget::keyPressEvent( e ); } } bool TextEdit::isSpellCheckingEnabled() const { return d->spellCheckingEnabled; } void TextEdit::setSpellCheckingEnabled( bool enable ) { EMailQuoteHighlighter *hlighter = dynamic_cast( highlighter() ); if ( hlighter ) hlighter->toggleSpellHighlighting( enable ); d->spellCheckingEnabled = enable; emit checkSpellingChanged( enable ); } bool TextEdit::shouldBlockBeSpellChecked( const QString& block ) const { return !isLineQuoted( block ); } bool KPIMTextEdit::TextEdit::isLineQuoted( const QString& line ) const { return quoteLength( line ) > 0; } int KPIMTextEdit::TextEdit::quoteLength( const QString& line ) const { bool quoteFound = false; int startOfText = -1; for ( int i = 0; i < line.length(); i++ ) { if ( line[i] == QLatin1Char( '>' ) || line[i] == QLatin1Char( '|' ) ) quoteFound = true; else if ( line[i] != QLatin1Char( ' ' ) ) { startOfText = i; break; } } if ( quoteFound ) { if ( startOfText == -1 ) startOfText = line.length() - 1; return startOfText; } else return 0; } const QString KPIMTextEdit::TextEdit::defaultQuoteSign() const { return QLatin1String( "> " ); } void TextEdit::createHighlighter() { EMailQuoteHighlighter *emailHighLighter = new EMailQuoteHighlighter( this ); setHighlighterColors( emailHighLighter ); //TODO change config KRichTextWidget::setHighlighter( emailHighLighter ); if ( !spellCheckingLanguage().isEmpty() ) setSpellCheckingLanguage( spellCheckingLanguage() ); setSpellCheckingEnabled( isSpellCheckingEnabled() ); } void TextEdit::setHighlighterColors( EMailQuoteHighlighter *highlighter ) { Q_UNUSED( highlighter ); } QString TextEdit::toWrappedPlainText() const { QString temp; QTextDocument* doc = document(); QTextBlock block = doc->begin(); while ( block.isValid() ) { QTextLayout* layout = block.layout(); for ( int i = 0; i < layout->lineCount(); i++ ) { QTextLine line = layout->lineAt( i ); temp += block.text().mid( line.textStart(), line.textLength() ) + QLatin1Char( '\n' ); } block = block.next(); } // Remove the last superfluous newline added above if ( temp.endsWith( QLatin1Char( '\n' ) ) ) temp.chop( 1 ); d->fixupTextEditString( temp ); return temp; } QString TextEdit::toCleanPlainText() const { QString temp = toPlainText(); d->fixupTextEditString( temp ); return temp; } void TextEdit::createActions( KActionCollection *actionCollection ) { KRichTextWidget::createActions( actionCollection ); if ( d->imageSupportEnabled ) { d->actionAddImage = new KAction( KIcon( QLatin1String( "insert-image" ) ), i18n( "Add Image" ), this ); actionCollection->addAction( QLatin1String( "add_image" ), d->actionAddImage ); connect( d->actionAddImage, SIGNAL(triggered(bool) ), SLOT( _k_slotAddImage() ) ); } d->actionDeleteLine = new KAction( i18n( "Delete Line" ), this ); d->actionDeleteLine->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ) ); actionCollection->addAction( QLatin1String( "delete_line" ), d->actionDeleteLine ); connect( d->actionDeleteLine, SIGNAL(triggered(bool)), SLOT(_k_slotDeleteLine()) ); } void TextEdit::addImage( const KUrl &url ) { QImage image; if ( !image.load( url.path() ) ) { KMessageBox::error( this, i18nc( "@info", "Unable to load image %1.", url.path() ) ); return; } QFileInfo fi( url.path() ); QString imageName = fi.baseName().isEmpty() ? QLatin1String( "image.png" ) : fi.baseName() + QLatin1String( ".png" ); d->addImageHelper( imageName, image ); } +void TextEdit::loadImage ( const QImage& image, const QString& matchName, const QString& resourceName ) +{ + QTextBlock currentBlock = document()->begin(); + QTextBlock::iterator it; + while ( currentBlock.isValid() ) { + for (it = currentBlock.begin(); !(it.atEnd()); ++it) { + QTextFragment fragment = it.fragment(); + if ( fragment.isValid() ) { + QTextImageFormat imageFormat = fragment.charFormat().toImageFormat(); + if ( imageFormat.isValid() && imageFormat.name() == matchName ) { + int pos = fragment.position(); + QTextCursor cursor( document() ); + cursor.setPosition( pos ); + cursor.setPosition( pos + 1, QTextCursor::KeepAnchor ); + cursor.removeSelectedText(); + document()->addResource( QTextDocument::ImageResource, QUrl( resourceName ), QVariant( image ) ); + cursor.insertImage( resourceName ); + } + } + } + currentBlock = currentBlock.next(); + } +} + void TextEditPrivate::addImageHelper( const QString &imageName, const QImage &image ) { QString imageNameToAdd = imageName; QTextDocument *document = q->document(); // determine the imageNameToAdd int imageNumber = 1; while ( mImageNames.contains( imageNameToAdd ) ) { QVariant qv = document->resource( QTextDocument::ImageResource, QUrl( imageNameToAdd ) ); if ( qv == image ) { // use the same name break; } int firstDot = imageName.indexOf( QLatin1Char( '.' ) ); if ( firstDot == -1 ) imageNameToAdd = imageName + QString::number( imageNumber++ ); else imageNameToAdd = imageName.left( firstDot ) + QString::number( imageNumber++ ) + imageName.mid( firstDot ); } if ( !mImageNames.contains( imageNameToAdd ) ) { document->addResource( QTextDocument::ImageResource, QUrl( imageNameToAdd ), image ); mImageNames << imageNameToAdd; } q->textCursor().insertImage( imageNameToAdd ); q->enableRichTextMode(); } -QList< QSharedPointer > TextEdit::embeddedImages() const +ImageWithNameList TextEdit::imagesWithName() const { - QList< QSharedPointer > retImages; + ImageWithNameList retImages; QStringList seenImageNames; QList imageFormats = d->embeddedImageFormats(); foreach( const QTextImageFormat &imageFormat, imageFormats ) { if ( !seenImageNames.contains( imageFormat.name() ) ) { QVariant data = document()->resource( QTextDocument::ImageResource, QUrl( imageFormat.name() ) ); QImage image = qvariant_cast( data ); - QBuffer buffer; - buffer.open( QIODevice::WriteOnly ); - image.save( &buffer, "PNG" ); - - qsrand( QDateTime::currentDateTime().toTime_t() + qHash( imageFormat.name() ) ); - QSharedPointer embeddedImage( new EmbeddedImage() ); - retImages.append( embeddedImage ); - embeddedImage->image = KMime::Codec::codecForName( "base64" )->encode( buffer.buffer() ); - embeddedImage->imageName = imageFormat.name(); - embeddedImage->contentID = QString( QLatin1String( "%1@KDE" ) ).arg( qrand() ); + QString name = imageFormat.name(); + ImageWithNamePtr newImage( new ImageWithName ); + newImage->image = image; + newImage->name = name; + retImages.append( newImage ); seenImageNames.append( imageFormat.name() ); } } return retImages; } +QList< QSharedPointer > TextEdit::embeddedImages() const +{ + ImageWithNameList normalImages = imagesWithName(); + QList< QSharedPointer > retImages; + foreach( const ImageWithNamePtr &normalImage, normalImages ) { + QBuffer buffer; + buffer.open( QIODevice::WriteOnly ); + normalImage->image.save( &buffer, "PNG" ); + + qsrand( QDateTime::currentDateTime().toTime_t() + qHash( normalImage->name ) ); + QSharedPointer embeddedImage( new EmbeddedImage() ); + retImages.append( embeddedImage ); + embeddedImage->image = KMime::Codec::codecForName( "base64" )->encode( buffer.buffer() ); + embeddedImage->imageName = normalImage->name; + embeddedImage->contentID = QString( QLatin1String( "%1@KDE" ) ).arg( qrand() ); + } + return retImages; +} + QList TextEditPrivate::embeddedImageFormats() const { QTextDocument *doc = q->document(); QList retList; QTextBlock currentBlock = doc->begin(); while ( currentBlock.isValid() ) { QTextBlock::iterator it; for ( it = currentBlock.begin(); !it.atEnd(); ++it ) { QTextFragment fragment = it.fragment(); if ( fragment.isValid() ) { QTextImageFormat imageFormat = fragment.charFormat().toImageFormat(); if ( imageFormat.isValid() ) { retList.append( imageFormat ); } } } currentBlock = currentBlock.next(); } return retList; } void TextEditPrivate::_k_slotAddImage() { QPointer fdlg = new KFileDialog( QString(), QString(), q ); fdlg->setOperationMode( KFileDialog::Other ); fdlg->setCaption( i18n("Add Image") ); fdlg->okButton()->setGuiItem( KGuiItem( i18n("&Add"), QLatin1String( "document-open" ) ) ); fdlg->setMode( KFile::Files ); if ( fdlg->exec() != KDialog::Accepted ) { delete fdlg; return; } const KUrl::List files = fdlg->selectedUrls(); foreach ( const KUrl& url, files ) { q->addImage( url ); } delete fdlg; } void KPIMTextEdit::TextEdit::enableImageActions() { d->imageSupportEnabled = true; } QByteArray KPIMTextEdit::TextEdit::imageNamesToContentIds( const QByteArray &htmlBody, const KPIMTextEdit::ImageList &imageList ) { QByteArray result = htmlBody; if ( imageList.size() > 0 ) { foreach( const QSharedPointer &image, imageList ) { const QString newImageName = QLatin1String( "cid:" ) + image->contentID; QByteArray quote( "\"" ); result.replace( QByteArray( quote + image->imageName.toLocal8Bit() + quote ), QByteArray( quote + newImageName.toLocal8Bit() + quote ) ); } } return result; } void TextEdit::insertFromMimeData( const QMimeData *source ) { // Add an image if that is on the clipboard if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled ) { QImage image = qvariant_cast( source->imageData() ); QFileInfo fi( source->text() ); QString imageName = fi.baseName().isEmpty() ? i18nc( "Start of the filename for an image", "image" ) : fi.baseName(); d->addImageHelper( imageName, image ); return; } // Attempt to paste HTML contents into the text edit in plain text mode, // prevent this and prevent plain text instead. if ( textMode() == KRichTextEdit::Plain && source->hasHtml() ) { if ( source->hasText() ) { insertPlainText( source->text() ); return; } } KRichTextWidget::insertFromMimeData( source ); } bool KPIMTextEdit::TextEdit::canInsertFromMimeData( const QMimeData *source ) const { if ( source->hasHtml() && textMode() == KRichTextEdit::Rich ) return true; if ( source->hasText() ) return true; if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled ) return true; return KRichTextWidget::canInsertFromMimeData( source ); } static bool isCharFormatFormatted( const QTextCharFormat &format, const QFont &defaultFont, const QTextCharFormat &defaultBlockFormat ) { if ( !format.anchorHref().isEmpty() || format.font() != defaultFont || format.isAnchor() || format.verticalAlignment() != defaultBlockFormat.verticalAlignment() || format.underlineStyle() != defaultBlockFormat.underlineStyle() || format.foreground().color() != defaultBlockFormat.foreground().color() || format.background().color() != defaultBlockFormat.background().color() ) return true; return false; } static bool isBlockFormatFormatted( const QTextBlockFormat &format, const QTextBlockFormat &defaultFormat ) { if ( format.alignment() != defaultFormat.alignment() || format.indent() != defaultFormat.indent() || format.textIndent() != defaultFormat.textIndent() ) return true; return false; } /// @return true if the format represents a list, table, image or something like that. static bool isSpecial( const QTextFormat &charFormat ) { return charFormat.isFrameFormat() || charFormat.isImageFormat() || charFormat.isListFormat() || charFormat.isTableFormat(); } bool TextEdit::isFormattingUsed() const { if ( textMode() == Plain ) return false; // Below, we walk through all text blocks and through all text fragments in them // and check if any of those has any formatting. // To check if they have formatting, we use the functions isBlockFormatFormatted() and // isCharFormatFormatted(). Those do not check all the exising formatting possibilities on // earth, but everything that KRichTextEdit supports at the moment. // // Also, we have to compare the formats against those of a default text edit. For example, // we can't compare the foreground color against black, because the user might have another // color scheme. Therefore we compare the foreground color against a default text edit. QTextEdit defaultTextEdit; QTextCharFormat defaultCharFormat = defaultTextEdit.document()->begin().charFormat(); QTextBlockFormat defaultBlockFormat = defaultTextEdit.document()->begin().blockFormat(); QFont defaultFont = document()->defaultFont(); QTextBlock block = document()->firstBlock(); while ( block.isValid() ) { if ( isBlockFormatFormatted( block.blockFormat(), defaultBlockFormat ) ) { return true; } if ( isSpecial( block.charFormat() ) || isSpecial( block.blockFormat() ) || block.textList() ) { return true; } QTextBlock::iterator it = block.begin(); while ( !it.atEnd() ) { QTextFragment fragment = it.fragment(); QTextCharFormat charFormat = fragment.charFormat(); if ( isSpecial( charFormat ) ) { return true; } if ( isCharFormatFormatted( fragment.charFormat(), defaultFont, defaultCharFormat ) ) { return true; } it++; } block = block.next(); } if ( toHtml().contains( QLatin1String( "
" ) ) ) return true; return false; } void TextEditPrivate::_k_slotDeleteLine() { q->deleteCurrentLine(); } void TextEdit::deleteCurrentLine() { QTextCursor cursor = textCursor(); QTextBlock block = cursor.block(); const QTextLayout* layout = block.layout(); // The current text block can have several lines due to word wrapping. // Search the line the cursor is in, and then delete it. for ( int lineNumber = 0; lineNumber < layout->lineCount(); lineNumber++ ) { QTextLine line = layout->lineAt( lineNumber ); const bool lastLineInBlock = ( line.textStart() + line.textLength() == block.length() - 1 ); const bool oneLineBlock = ( layout->lineCount() == 1 ); const int startOfLine = block.position() + line.textStart(); int endOfLine = block.position() + line.textStart() + line.textLength(); if ( !lastLineInBlock ) endOfLine -= 1; // Found the line where the cursor is in if ( cursor.position() >= startOfLine && cursor.position() <= endOfLine ) { int deleteStart = startOfLine; int deleteLength = line.textLength(); if ( oneLineBlock ) deleteLength++; // The trailing newline // When deleting the last line in the document, // remove the newline of the line before the last line instead if ( deleteStart + deleteLength >= document()->characterCount() && deleteStart > 0 ) deleteStart--; cursor.beginEditBlock(); cursor.setPosition( deleteStart ); cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor, deleteLength ); cursor.removeSelectedText(); cursor.endEditBlock(); return; } } } #include "textedit.moc" diff --git a/kpimtextedit/textedit.h b/kpimtextedit/textedit.h index 5358e689a..0100903ed 100644 --- a/kpimtextedit/textedit.h +++ b/kpimtextedit/textedit.h @@ -1,271 +1,305 @@ /* Copyright (c) 2009 Thomas McGuire Based on KMail and libkdepim code by: Copyright 2007 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPIMTEXTEDIT_TEXTEDIT_H #define KPIMTEXTEDIT_TEXTEDIT_H #include "kpimtextedit_export.h" #include #include #include #include class KUrl; namespace KPIMTextEdit { class TextEditPrivate; class EMailQuoteHighlighter; /** - * Holds information about an embedded HTML image. + * Holds information about an embedded HTML image that will be useful for mail clients. * A list with all images can be retrieved with TextEdit::embeddedImages(). */ struct EmbeddedImage { QByteArray image; ///< The image, encoded as PNG with base64 encoding QString contentID; ///< The content id of the embedded image QString imageName; ///< Name of the image as it is available as a resource in the editor }; +/** + * Holds information about an embedded HTML image that will be generally useful. + * A list with all images can be retrieved with TextEdit::imagesWithName(). + * + * @since 4.4 + */ +struct ImageWithName +{ + QImage image; ///< The image + QString name; ///< The name of the image as it is available as a resource in the editor +}; + +typedef QSharedPointer ImageWithNamePtr; +typedef QList< ImageWithNamePtr > ImageWithNameList; typedef QList< QSharedPointer > ImageList; /** * Special textedit that provides additional features which are useful for PIM applications * like mail clients. * Additional features this class provides: * - Highlighting quoted text * - Handling of inline images * - Auto-Hiding the cursor * - Handling of pastes and drops of images * * @since 4.3 */ class KPIMTEXTEDIT_EXPORT TextEdit : public KRichTextWidget, protected KTextEditSpellInterface // TODO: KDE5: get rid of the spell interface { Q_OBJECT public: /** * Constructs a TextEdit object * @param text the initial plain text of the text edit, interpreted as HTML * @param parent the parent widget */ explicit TextEdit( const QString& text, QWidget *parent = 0 ); /** * Constructs a TextEdit object. * @param parent the parent widget */ explicit TextEdit( QWidget *parent = 0 ); /** * Calling this allows createActions() to create the add image actions. - * Call this method before callilng createActions(), otherwise the action + * Call this method before calling createActions(), otherwise the action * will not be added. * Also, if image actions is enabled, the user can paste PNG images. * * Don't call this if you don't want to support adding images. */ void enableImageActions(); /** * Destructor */ ~TextEdit(); /** * Reimplemented from KMEditor, to support more actions. * * The additional action XML names are: * - add_image * - delete_line * * The add_image actions is only added if enableImageActions() is called before. */ virtual void createActions( KActionCollection *actionCollection ); /** * Adds an image. The image is loaded from file and then pasted to the current * cursor position. * * @param url The URL of the file which contains the image */ void addImage( const KUrl &url ); + /** + * Loads an image into the textedit. The difference to addImage() is that this function expects + * that the image tag is already present in the HTML source. + * + * So what this message does is that it scans the HTML source for the image tag that matches the + * @p matchName, and then inserts the @p image as a resource, giving that resource the name + * @P resourceName. + * + * @since 4.4 + */ + void loadImage( const QImage &image, const QString &matchName, const QString &resourceName ); + /** * Deletes the line at the current cursor position. * @since 4.4 */ void deleteCurrentLine(); /** * Get a list with all embedded HTML images. * If the same image is contained twice or more in the editor, it will have only * one entry in this list. * * @return a list of embedded HTML images of the editor. */ ImageList embeddedImages() const; + /** + * Same as embeddedImages(), only that this returns a list of general purpose information, + * whereas the embeddedImages() function returns a list with mail-specific information. + * + * @since 4.4 + */ + ImageWithNameList imagesWithName() const; + /** * Returns the text of the editor as plain text, with linebreaks inserted * where word-wrapping occurred. */ QString toWrappedPlainText() const; /** * Same as toPlainText() from QTextEdit, only that it removes embedded images and * converts non-breaking space characters to normal spaces. */ QString toCleanPlainText() const; /** * This method is called after the highlighter is created. * If you use custom colors for highlighting, override this method and set the colors * to the highlighter in it. * * The default implementation does nothing, therefore the default colors of the * EMailQuoteHighlighter class will be used. * * @param highlighter the highlighter that was just created. You need to set the colors * of this highlighter. */ virtual void setHighlighterColors( EMailQuoteHighlighter *highlighter ); /** * Convenience method for qouteLength( line ) > 0 */ bool isLineQuoted( const QString &line ) const; /** * This is called whenever the editor needs to find out the length of the quote, * i.e. the length of the quote prefix before the real text starts. * The default implementation counts the number of spaces, '>' and '|' chars in * front of the line. * * @param line the line of which the length of the quote prefix should be returned * @return 0 if the line is not quoted, the length of the quote prefix otherwise * FIXME: Not yet used in all places, e.g. keypressEvent() or the quote highlighter */ virtual int quoteLength( const QString &line ) const; /** * Returns the prefix that is added to a line that is quoted. * By default, this is "> ". */ virtual const QString defaultQuoteSign() const; /** * For all given embedded images, this function replace the image name in the tag of the * HTML body with cid:content-id, * so that the HTML references the image body parts, see RFC 2557. * * This is useful when building a MIME message with inline images. * * Note that this function works on encoded content already. * * @param htmlBody the HTML code in which the tag will be modified. * The HTML code here could come from toHtml(), for example. * * @param imageList the list of images of which the tag will be modified. * You can get such a list from the embeddedImages() function. * * @return a modified HTML code, where the tags got replaced */ static QByteArray imageNamesToContentIds( const QByteArray &htmlBody, const ImageList &imageList ); /** * Checks if rich text formatting is used anywhere. * This is not the same as checking whether textMode() returns "Rich", since * that only tells that rich text mode is enabled, but not if any special formatting * is actually used. * * @return true if formatting is used anywhere */ bool isFormattingUsed() const; protected: /** * Reimplemented for inline image support */ virtual bool canInsertFromMimeData( const QMimeData *source ) const; /** * Reimplemented for inline image support */ virtual void insertFromMimeData( const QMimeData *source ); /** * Reimplemented from KRichTextWidget to hide the mouse cursor when there * was no mouse movement for some time, using KCursor */ virtual bool eventFilter( QObject*o, QEvent* e ); /** * Reimplemented to add qoute signs when the user presses enter * on a quoted line. */ virtual void keyPressEvent ( QKeyEvent * e ); // For the explaination for these four methods, see the comment at the // spellCheckingEnabled variable of the private class. /** * Reimplemented from KTextEditSpellInterface */ virtual bool isSpellCheckingEnabled() const; /** * Reimplemented from KTextEditSpellInterface */ virtual void setSpellCheckingEnabled( bool enable ); /** * Reimplemented from KTextEditSpellInterface, to avoid spellchecking * quoted text. */ virtual bool shouldBlockBeSpellChecked( const QString& block ) const; /** * Reimplemented to create our own highlighter which does quote and * spellcheck highlighting */ virtual void createHighlighter(); private: std::auto_ptr const d; friend class TextEditPrivate; Q_PRIVATE_SLOT( d, void _k_slotAddImage() ) Q_PRIVATE_SLOT( d, void _k_slotDeleteLine() ) }; } // namespace #endif