24 #include "emailquotehighlighter.h"
25 #include "emoticontexteditaction.h"
26 #include "inserthtmldialog.h"
27 #include "tableactionmenu.h"
28 #include "insertimagedialog.h"
30 #include <kmime/kmime_codecs.h>
32 #include <KDE/KAction>
33 #include <KDE/KActionCollection>
34 #include <KDE/KCursor>
35 #include <KDE/KFileDialog>
36 #include <KDE/KLocalizedString>
37 #include <KDE/KMessageBox>
38 #include <KDE/KPushButton>
42 #include <QtCore/QBuffer>
43 #include <QtCore/QDateTime>
44 #include <QtCore/QMimeData>
45 #include <QtCore/QFileInfo>
46 #include <QtCore/QPointer>
48 #include <QTextLayout>
50 #include "textutils.h"
52 namespace KPIMTextEdit {
59 : actionAddImage( 0 ),
60 actionDeleteLine( 0 ),
61 actionAddEmoticon( 0 ),
62 actionInsertHtml( 0 ),
64 actionFormatReset( 0 ),
66 imageSupportEnabled( false ),
67 emoticonSupportEnabled( false ),
68 insertHtmlSupportEnabled( false ),
69 insertTableSupportEnabled( false ),
70 spellCheckingEnabled( false )
82 void addImageHelper(
const QString &imageName,
const QImage &image,
83 int width = -1,
int height = -1 );
94 void fixupTextEditString(
QString &text )
const;
105 void _k_slotAddImage();
107 void _k_slotDeleteLine();
109 void _k_slotAddEmoticon(
const QString & );
111 void _k_slotInsertHtml();
113 void _k_slotFormatReset();
115 void _k_slotTextModeChanged( KRichTextEdit::Mode );
118 KAction *actionAddImage;
121 KAction *actionDeleteLine;
123 EmoticonTextEditAction *actionAddEmoticon;
125 KAction *actionInsertHtml;
127 TableActionMenu *actionTable;
129 KAction *actionFormatReset;
144 bool imageSupportEnabled;
146 bool emoticonSupportEnabled;
148 bool insertHtmlSupportEnabled;
150 bool insertTableSupportEnabled;
163 bool spellCheckingEnabled;
169 using namespace KPIMTextEdit;
171 void TextEditPrivate::fixupTextEditString(
QString &text )
const
174 text.
remove( QChar::LineSeparator );
185 : KRichTextWidget( text, parent ),
186 d( new TextEditPrivate( this ) )
192 : KRichTextWidget( parent ),
193 d( new TextEditPrivate( this ) )
199 : KRichTextWidget( parent ),
200 d( new TextEditPrivate( this ) )
214 KCursor::autoHideEventFilter( o, e );
217 return KRichTextWidget::eventFilter( o, e );
220 void TextEditPrivate::init()
222 q->connect( q, SIGNAL(textModeChanged(KRichTextEdit::Mode)),
223 q, SLOT(_k_slotTextModeChanged(KRichTextEdit::Mode)) );
224 q->setSpellInterface( q );
232 spellCheckingEnabled =
false;
233 q->setCheckSpellingEnabledInternal(
true );
236 KCursor::setAutoHideCursor( q,
true,
true );
238 q->installEventFilter( q );
243 return d->configFile;
248 if ( e->
key() == Qt::Key_Return ) {
255 cursor.
movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
257 if ( ( ( oldPos - blockPos ) > 0 ) &&
258 ( ( oldPos - blockPos ) <
int( lineText.
length() ) ) ) {
259 bool isQuotedLine =
false;
261 while ( bot < lineText.
length() ) {
266 }
else if ( lineText[bot].isSpace() ) {
272 KRichTextWidget::keyPressEvent( e );
277 ( bot != lineText.
length() ) &&
278 ( ( oldPos - blockPos ) >= int( bot ) ) ) {
282 cursor.
movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
287 int leadingWhiteSpaceCount = 0;
288 while ( ( leadingWhiteSpaceCount < newLine.
length() ) &&
289 newLine[leadingWhiteSpaceCount].isSpace() ) {
290 ++leadingWhiteSpaceCount;
292 newLine = newLine.
replace( 0, leadingWhiteSpaceCount, lineText.
left( bot ) );
296 setTextCursor( cursor );
299 KRichTextWidget::keyPressEvent( e );
302 KRichTextWidget::keyPressEvent( e );
308 return d->spellCheckingEnabled;
318 d->spellCheckingEnabled = enable;
319 emit checkSpellingChanged( enable );
329 return quoteLength( line ) > 0;
334 bool quoteFound =
false;
335 int startOfText = -1;
336 const int lineLength( line.
length() );
337 for (
int i = 0; i < lineLength; ++i ) {
346 if ( startOfText == -1 ) {
347 startOfText = line.
length() - 1;
367 KRichTextWidget::setHighlighter( emailHighLighter );
369 if ( !spellCheckingLanguage().isEmpty() ) {
370 setSpellCheckingLanguage( spellCheckingLanguage() );
377 Q_UNUSED( highlighter );
393 const int numberOfLine( layout->
lineCount() );
394 bool urlStart =
false;
395 for (
int i = 0; i < numberOfLine; ++i ) {
409 block = block.
next();
417 d->fixupTextEditString( temp );
424 d->fixupTextEditString( temp );
435 KRichTextWidget::createActions( actionCollection );
437 if ( d->imageSupportEnabled ) {
438 d->actionAddImage =
new KAction( KIcon(
QLatin1String(
"insert-image" ) ),
439 i18n(
"Add Image" ),
this );
440 actionCollection->addAction(
QLatin1String(
"add_image" ), d->actionAddImage );
441 connect( d->actionAddImage, SIGNAL(triggered(
bool)), SLOT(_k_slotAddImage()) );
443 if ( d->emoticonSupportEnabled ) {
444 d->actionAddEmoticon =
new EmoticonTextEditAction(
this );
445 actionCollection->addAction(
QLatin1String(
"add_emoticon" ), d->actionAddEmoticon );
446 connect( d->actionAddEmoticon, SIGNAL(emoticonActivated(
QString)),
447 SLOT(_k_slotAddEmoticon(
QString)) );
450 if ( d->insertHtmlSupportEnabled ) {
451 d->actionInsertHtml =
new KAction( i18n(
"Insert HTML" ),
this );
452 actionCollection->addAction(
QLatin1String(
"insert_html" ), d->actionInsertHtml );
453 connect( d->actionInsertHtml, SIGNAL(triggered(
bool)), SLOT(_k_slotInsertHtml()) );
456 if ( d->insertTableSupportEnabled ) {
457 d->actionTable =
new TableActionMenu( actionCollection,
this );
458 d->actionTable->setIcon( KIcon(
QLatin1String(
"insert-table" ) ) );
459 d->actionTable->setText( i18n(
"Table" ) );
460 d->actionTable->setDelayed(
false );
461 actionCollection->addAction(
QLatin1String(
"insert_table" ), d->actionTable );
464 d->actionDeleteLine =
new KAction( i18n(
"Delete Line" ),
this );
465 d->actionDeleteLine->setShortcut(
QKeySequence( Qt::CTRL + Qt::Key_K ) );
466 actionCollection->addAction(
QLatin1String(
"delete_line" ), d->actionDeleteLine );
467 connect( d->actionDeleteLine, SIGNAL(triggered(
bool)), SLOT(_k_slotDeleteLine()) );
469 d->actionFormatReset =
470 new KAction( KIcon(
QLatin1String(
"draw-eraser" ) ), i18n(
"Reset Font Settings" ),
this );
471 d->actionFormatReset->setIconText( i18n(
"Reset Font" ) );
472 actionCollection->addAction(
QLatin1String(
"format_reset" ), d->actionFormatReset );
473 connect( d->actionFormatReset, SIGNAL(triggered(
bool)), SLOT(_k_slotFormatReset()) );
478 addImageHelper( url, width, height );
483 addImageHelper( url );
486 void TextEdit::addImageHelper(
const KUrl &url,
int width,
int height )
489 if ( !image.
load( url.path() ) ) {
493 "Unable to load image <filename>%1</filename>.",
502 d->addImageHelper( imageName, image, width, height );
511 while ( currentBlock.
isValid() ) {
512 for ( it = currentBlock.
begin(); !it.atEnd(); ++it ) {
516 if ( imageFormat.
isValid() && imageFormat.
name() == matchName ) {
518 if ( !cursorPositionsToSkip.
contains( pos ) ) {
521 cursor.
setPosition( pos + 1, QTextCursor::KeepAnchor );
523 document()->addResource( QTextDocument::ImageResource,
526 format.
setName( resourceName );
527 if ( ( imageFormat.
width() != 0 ) && ( imageFormat.
height() != 0 ) ) {
536 cursorPositionsToSkip.
insert( pos );
537 it = currentBlock.
begin();
542 currentBlock = currentBlock.
next();
546 void TextEditPrivate::addImageHelper(
const QString &imageName,
const QImage &image,
547 int width,
int height )
549 QString imageNameToAdd = imageName;
554 while ( mImageNames.contains( imageNameToAdd ) ) {
561 if ( firstDot == -1 ) {
565 imageName.
mid( firstDot );
569 if ( !mImageNames.contains( imageNameToAdd ) ) {
570 document->
addResource( QTextDocument::ImageResource,
QUrl( imageNameToAdd ), image );
571 mImageNames << imageNameToAdd;
573 if ( width != -1 && height != -1 ) {
575 format.
setName( imageNameToAdd );
578 q->textCursor().insertImage( format );
580 q->textCursor().insertImage( imageNameToAdd );
582 q->enableRichTextMode();
597 newImage->image = image;
598 newImage->name = name;
599 retImages.
append( newImage );
612 buffer.
open( QIODevice::WriteOnly );
613 normalImage->image.save( &buffer,
"PNG" );
617 retImages.
append( embeddedImage );
618 embeddedImage->image = KMime::Codec::codecForName(
"base64" )->encode( buffer.
buffer() );
619 embeddedImage->imageName = normalImage->name;
631 while ( currentBlock.
isValid() ) {
633 for ( it = currentBlock.
begin(); !it.atEnd(); ++it ) {
640 if ( !url.isValid() || !url.scheme().startsWith(
QLatin1String(
"http" ) ) ) {
641 retList.
append( imageFormat );
646 currentBlock = currentBlock.
next();
651 void TextEditPrivate::_k_slotAddEmoticon(
const QString &text )
657 void TextEditPrivate::_k_slotInsertHtml()
659 if ( q->textMode() == KRichTextEdit::Rich ) {
661 if ( dialog->exec() ) {
662 const QString str = dialog->html();
672 void TextEditPrivate::_k_slotAddImage()
675 if ( dlg->exec() == KDialog::Accepted && dlg ) {
676 const KUrl url = dlg->imageUrl();
678 int imageHeight = -1;
679 if ( !dlg->keepOriginalSize() ) {
680 imageWidth = dlg->imageWidth();
681 imageHeight = dlg->imageHeight();
683 q->addImage( url, imageWidth, imageHeight );
688 void TextEditPrivate::_k_slotTextModeChanged( KRichTextEdit::Mode mode )
690 if ( mode == KRichTextEdit::Rich ) {
691 saveFont = q->currentFont();
695 void TextEditPrivate::_k_slotFormatReset()
697 q->setTextBackgroundColor( q->palette().highlightedText().color() );
698 q->setTextForegroundColor( q->palette().text().color() );
699 q->setFont( saveFont );
705 d->imageSupportEnabled =
true;
710 return d->imageSupportEnabled;
715 d->emoticonSupportEnabled =
true;
720 return d->emoticonSupportEnabled;
723 void KPIMTextEdit::TextEdit::enableInsertHtmlActions()
725 d->insertHtmlSupportEnabled =
true;
730 return d->insertHtmlSupportEnabled;
735 return d->insertTableSupportEnabled;
738 void KPIMTextEdit::TextEdit::enableInsertTableActions()
740 d->insertTableSupportEnabled =
true;
761 i18nc(
"Start of the filename for an image",
"image" ) :
763 d->addImageHelper( imageName, image );
769 if ( textMode() == KRichTextEdit::Rich && source->
hasImage() && d->imageSupportEnabled ) {
778 if ( textMode() == KRichTextEdit::Plain && source->
hasHtml() ) {
780 insertPlainText( source->
text() );
785 KRichTextWidget::insertFromMimeData( source );
790 if ( source->
hasHtml() && textMode() == KRichTextEdit::Rich ) {
798 if ( textMode() == KRichTextEdit::Rich && source->
hasImage() && d->imageSupportEnabled ) {
802 return KRichTextWidget::canInsertFromMimeData( source );
807 if ( textMode() == Plain ) {
814 void TextEditPrivate::_k_slotDeleteLine()
816 if ( q->hasFocus() ) {
817 q->deleteCurrentLine();
829 for (
int lineNumber = 0; lineNumber < layout->
lineCount(); lineNumber++ ) {
832 const bool oneLineBlock = ( layout->
lineCount() == 1 );
835 if ( !lastLineInBlock ) {
841 int deleteStart = startOfLine;
843 if ( oneLineBlock ) {
849 if ( deleteStart + deleteLength >= document()->characterCount() &&
856 cursor.
movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor, deleteLength );
864 #include "moc_textedit.cpp"
virtual void setHighlighterColors(EMailQuoteHighlighter *highlighter)
This method is called after the highlighter is created.
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
void removeSelectedText()
bool load(QIODevice *device, const char *format)
Holds information about an embedded HTML image that will be useful for mail clients.
void insertImage(const QImage &image, const QFileInfo &info)
virtual bool canInsertFromMimeData(const QMimeData *source) const
Reimplemented for inline image support.
void insertHtml(const QString &html)
virtual void insertFromMimeData(const QMimeData *source)
Reimplemented for inline image support.
QVariant resource(int type, const QUrl &name) const
bool isLineQuoted(const QString &line) const
Convenience method for qouteLength( line ) > 0.
bool isEnableImageActions() const
Return true if richtext mode support image.
void enableImageActions()
Calling this allows createActions() to create the add image actions.
QString selectedText() const
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QString toWrappedPlainText() const
Returns the text of the editor as plain text, with linebreaks inserted where word-wrapping occurred...
ImageList embeddedImages() const
Get a list with all embedded HTML images.
const_iterator insert(const T &value)
QString & remove(int position, int n)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
void setName(const QString &name)
void loadImage(const QImage &image, const QString &matchName, const QString &resourceName)
Loads an image into the textedit.
QTextCharFormat charFormat() const
virtual void createHighlighter()
Reimplemented to create our own highlighter which does quote and spellcheck highlighting.
virtual int quoteLength(const QString &line) const
This is called whenever the editor needs to find out the length of the quote, i.e.
virtual bool eventFilter(QObject *o, QEvent *e)
Reimplemented from KRichTextWidget to hide the mouse cursor when there was no mouse movement for some...
QString number(int n, int base)
TextEdit(const QString &text, QWidget *parent=0)
Constructs a TextEdit object.
virtual void setSpellCheckingEnabled(bool enable)
Reimplemented from KTextEditSpellInterface.
void append(const T &value)
bool isEnableInsertHtmlActions() const
void deleteCurrentLine()
Deletes the line at the current cursor position.
void addResource(int type, const QUrl &name, const QVariant &resource)
virtual void createActions(KActionCollection *actionCollection)
Reimplemented from KMEditor, to support more actions.
void setWidth(qreal width)
void insertText(const QString &text)
QByteArray & replace(int pos, int len, const char *after)
bool isFormattingUsed() const
Checks if rich text formatting is used anywhere.
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
virtual bool open(QFlags< QIODevice::OpenModeFlag > flags)
QTextLayout * layout() const
This highlighter highlights spelling mistakes and also highlightes quotes.
QByteArray toLocal8Bit() const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
virtual bool shouldBlockBeSpellChecked(const QString &block) const
Reimplemented from KTextEditSpellInterface, to avoid spellchecking quoted text.
bool contains(const T &value) const
virtual bool isSpellCheckingEnabled() const
Reimplemented from KTextEditSpellInterface.
QString & replace(int position, int n, QChar after)
QDateTime currentDateTime()
QString mid(int position, int n) const
KPIMTEXTEDIT_EXPORT bool containsFormatting(const QTextDocument *document)
Returns whether the QTextDocument document contains rich text formatting.
void toggleSpellHighlighting(bool on)
Turns spellcheck highlighting on or off.
QTextLine lineAt(int i) const
Special textedit that provides additional features which are useful for PIM applications like mail cl...
Holds information about an embedded HTML image that will be generally useful.
QString toCleanPlainText() const
Same as toPlainText() from QTextEdit, only that it removes embedded images and converts non-breaking ...
QVariant imageData() const
virtual const QString defaultQuoteSign() const
Returns the prefix that is added to a line that is quoted.
bool isEnableInsertTableActions() const
ImageWithNameList imagesWithName() 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.
void addImage(const KUrl &url)
Adds an image.
QString left(int n) const
QTextImageFormat toImageFormat() const
void insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment)
virtual void keyPressEvent(QKeyEvent *e)
Reimplemented to add qoute signs when the user presses enter on a quoted line.
QString configFile() const
Return config file.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
bool isEnableEmoticonActions() const
Return true if emoticons actions supported.
void setHeight(qreal height)
void enableEmoticonActions()
Calling this allows createActions() to create the add emoticons actions.
static QByteArray imageNamesToContentIds(const QByteArray &htmlBody, const ImageList &imageList)
For all given embedded images, this function replace the image name in the.
void setPosition(int pos, MoveMode m)