• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.10.5 API Reference
  • KDE Home
  • Contact Us
 

KPIMTextedit Library

  • kpimtextedit
textedit.cpp
1 /*
2  Copyright (c) 2009 Thomas McGuire <mcguire@kde.org>
3 
4  Based on KMail and libkdepim code by:
5  Copyright 2007 - 2012 Laurent Montel <montel@kde.org>
6 
7  This library is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Library General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version.
11 
12  This library is distributed in the hope that it will be useful, but WITHOUT
13  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15  License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to the
19  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  02110-1301, USA.
21 */
22 #include "textedit.h"
23 
24 #include "emailquotehighlighter.h"
25 #include "emoticontexteditaction.h"
26 #include "inserthtmldialog.h"
27 #include "tableactionmenu.h"
28 #include "insertimagedialog.h"
29 
30 #include <kmime/kmime_codecs.h>
31 
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>
39 #include <KDE/KUrl>
40 #include <KDE/KImageIO>
41 
42 #include <QtCore/QBuffer>
43 #include <QtCore/QDateTime>
44 #include <QtCore/QMimeData>
45 #include <QtCore/QFileInfo>
46 #include <QtCore/QPointer>
47 #include <QKeyEvent>
48 #include <QTextLayout>
49 
50 #include "textutils.h"
51 #include <QPlainTextEdit>
52 
53 namespace KPIMTextEdit {
54 
55 class TextEditPrivate
56 {
57  public:
58 
59  TextEditPrivate( TextEdit *parent )
60  : actionAddImage( 0 ),
61  actionDeleteLine( 0 ),
62  actionAddEmoticon( 0 ),
63  actionInsertHtml( 0 ),
64  actionTable( 0 ),
65  actionFormatReset( 0 ),
66  q( parent ),
67  imageSupportEnabled( false ),
68  emoticonSupportEnabled( false ),
69  insertHtmlSupportEnabled( false ),
70  insertTableSupportEnabled( false ),
71  spellCheckingEnabled( false )
72  {
73  }
74 
83  void addImageHelper(const QString &imageName, const QImage &image, int width = -1, int height = -1);
84 
88  QList<QTextImageFormat> embeddedImageFormats() const;
89 
94  void fixupTextEditString( QString &text ) const;
95 
99  void init();
100 
105  void _k_slotAddImage();
106 
107  void _k_slotDeleteLine();
108 
109  void _k_slotAddEmoticon(const QString&);
110 
111  void _k_slotInsertHtml();
112 
113  void _k_slotFormatReset();
114 
115  void _k_slotTextModeChanged(KRichTextEdit::Mode);
116 
118  KAction *actionAddImage;
119 
121  KAction *actionDeleteLine;
122 
123  EmoticonTextEditAction *actionAddEmoticon;
124 
125  KAction *actionInsertHtml;
126 
127  TableActionMenu *actionTable;
128 
129  KAction *actionFormatReset;
130 
132  TextEdit *q;
133 
135  bool imageSupportEnabled;
136 
137  bool emoticonSupportEnabled;
138 
139  bool insertHtmlSupportEnabled;
140 
141  bool insertTableSupportEnabled;
147  QStringList mImageNames;
148 
160  bool spellCheckingEnabled;
161 
162  QString configFile;
163  QFont saveFont;
164 };
165 
166 } // namespace
167 
168 using namespace KPIMTextEdit;
169 
170 void TextEditPrivate::fixupTextEditString( QString &text ) const
171 {
172  // Remove line separators. Normal \n chars are still there, so no linebreaks get lost here
173  text.remove( QChar::LineSeparator );
174 
175  // Get rid of embedded images, see QTextImageFormat documentation:
176  // "Inline images are represented by an object replacement character (0xFFFC in Unicode) "
177  text.remove( 0xFFFC );
178 
179  // In plaintext mode, each space is non-breaking.
180  text.replace( QChar::Nbsp, QChar::fromLatin1( ' ' ) );
181 }
182 
183 TextEdit::TextEdit( const QString &text, QWidget *parent )
184  : KRichTextWidget( text, parent ),
185  d( new TextEditPrivate( this ) )
186 {
187  d->init();
188 }
189 
190 TextEdit::TextEdit( QWidget *parent )
191  : KRichTextWidget( parent ),
192  d( new TextEditPrivate( this ) )
193 {
194  d->init();
195 }
196 
197 TextEdit::TextEdit( QWidget *parent, const QString &configFile )
198  : KRichTextWidget( parent ),
199  d( new TextEditPrivate( this ) )
200 {
201  d->init();
202  d->configFile = configFile;
203 }
204 
205 TextEdit::~TextEdit()
206 {
207 }
208 
209 bool TextEdit::eventFilter( QObject *o, QEvent *e )
210 {
211 #ifndef QT_NO_CURSOR
212  if ( o == this ) {
213  KCursor::autoHideEventFilter( o, e );
214  }
215 #endif
216  return KRichTextWidget::eventFilter( o, e );
217 }
218 
219 void TextEditPrivate::init()
220 {
221  q->connect(q,SIGNAL(textModeChanged(KRichTextEdit::Mode)),q,SLOT(_k_slotTextModeChanged(KRichTextEdit::Mode)));
222  q->setSpellInterface( q );
223  // We tell the KRichTextWidget to enable spell checking, because only then it will
224  // call createHighlighter() which will create our own highlighter which also
225  // does quote highlighting.
226  // However, *our* spellchecking is still disabled. Our own highlighter only
227  // cares about our spellcheck status, it will not highlight missspelled words
228  // if our spellchecking is disabled.
229  // See also KEMailQuotingHighlighter::highlightBlock().
230  spellCheckingEnabled = false;
231  q->setCheckSpellingEnabledInternal( true );
232 
233 #ifndef QT_NO_CURSOR
234  KCursor::setAutoHideCursor( q, true, true );
235 #endif
236  q->installEventFilter( q );
237 }
238 
239 QString TextEdit::configFile() const
240 {
241  return d->configFile;
242 }
243 
244 void TextEdit::keyPressEvent ( QKeyEvent * e )
245 {
246  if ( e->key() == Qt::Key_Return ) {
247  QTextCursor cursor = textCursor();
248  int oldPos = cursor.position();
249  int blockPos = cursor.block().position();
250 
251  //selection all the line.
252  cursor.movePosition( QTextCursor::StartOfBlock );
253  cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
254  QString lineText = cursor.selectedText();
255  if ( ( ( oldPos - blockPos ) > 0 ) &&
256  ( ( oldPos - blockPos ) < int( lineText.length() ) ) ) {
257  bool isQuotedLine = false;
258  int bot = 0; // bot = begin of text after quote indicators
259  while ( bot < lineText.length() ) {
260  if ( ( lineText[bot] == QChar::fromLatin1( '>' ) ) ||
261  ( lineText[bot] == QChar::fromLatin1( '|' ) ) ) {
262  isQuotedLine = true;
263  ++bot;
264  } else if ( lineText[bot].isSpace() ) {
265  ++bot;
266  } else {
267  break;
268  }
269  }
270  KRichTextWidget::keyPressEvent( e );
271  // duplicate quote indicators of the previous line before the new
272  // line if the line actually contained text (apart from the quote
273  // indicators) and the cursor is behind the quote indicators
274  if ( isQuotedLine &&
275  ( bot != lineText.length() ) &&
276  ( ( oldPos - blockPos ) >= int( bot ) ) ) {
277  // The cursor position might have changed unpredictably if there was selected
278  // text which got replaced by a new line, so we query it again:
279  cursor.movePosition( QTextCursor::StartOfBlock );
280  cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
281  QString newLine = cursor.selectedText();
282 
283  // remove leading white space from the new line and instead
284  // add the quote indicators of the previous line
285  int leadingWhiteSpaceCount = 0;
286  while ( ( leadingWhiteSpaceCount < newLine.length() ) &&
287  newLine[leadingWhiteSpaceCount].isSpace() ) {
288  ++leadingWhiteSpaceCount;
289  }
290  newLine = newLine.replace( 0, leadingWhiteSpaceCount, lineText.left( bot ) );
291  cursor.insertText( newLine );
292  //cursor.setPosition( cursor.position() + 2 );
293  cursor.movePosition( QTextCursor::StartOfBlock );
294  setTextCursor( cursor );
295  }
296  } else {
297  KRichTextWidget::keyPressEvent( e );
298  }
299  } else {
300  KRichTextWidget::keyPressEvent( e );
301  }
302 }
303 
304 bool TextEdit::isSpellCheckingEnabled() const
305 {
306  return d->spellCheckingEnabled;
307 }
308 
309 void TextEdit::setSpellCheckingEnabled( bool enable )
310 {
311  EMailQuoteHighlighter *hlighter = dynamic_cast<EMailQuoteHighlighter*>( highlighter() );
312  if ( hlighter ) {
313  hlighter->toggleSpellHighlighting( enable );
314  }
315 
316  d->spellCheckingEnabled = enable;
317  emit checkSpellingChanged( enable );
318 }
319 
320 bool TextEdit::shouldBlockBeSpellChecked( const QString &block ) const
321 {
322  return !isLineQuoted( block );
323 }
324 
325 bool KPIMTextEdit::TextEdit::isLineQuoted( const QString &line ) const
326 {
327  return quoteLength( line ) > 0;
328 }
329 
330 int KPIMTextEdit::TextEdit::quoteLength( const QString &line ) const
331 {
332  bool quoteFound = false;
333  int startOfText = -1;
334  const int lineLength(line.length());
335  for ( int i = 0; i < lineLength; ++i ) {
336  if ( line[i] == QLatin1Char( '>' ) || line[i] == QLatin1Char( '|' ) ) {
337  quoteFound = true;
338  } else if ( line[i] != QLatin1Char( ' ' ) ) {
339  startOfText = i;
340  break;
341  }
342  }
343  if ( quoteFound ) {
344  if ( startOfText == -1 ) {
345  startOfText = line.length() - 1;
346  }
347  return startOfText;
348  } else {
349  return 0;
350  }
351 }
352 
353 const QString KPIMTextEdit::TextEdit::defaultQuoteSign() const
354 {
355  return QLatin1String( "> " );
356 }
357 
358 void TextEdit::createHighlighter()
359 {
360  EMailQuoteHighlighter *emailHighLighter = new EMailQuoteHighlighter( this );
361 
362  setHighlighterColors( emailHighLighter );
363 
364  //TODO change config
365  KRichTextWidget::setHighlighter( emailHighLighter );
366 
367  if ( !spellCheckingLanguage().isEmpty() ) {
368  setSpellCheckingLanguage( spellCheckingLanguage() );
369  }
370  setSpellCheckingEnabled( isSpellCheckingEnabled() );
371 }
372 
373 void TextEdit::setHighlighterColors( EMailQuoteHighlighter *highlighter )
374 {
375  Q_UNUSED( highlighter );
376 }
377 
378 
379 QString TextEdit::toWrappedPlainText() const
380 {
381  QTextDocument *doc = document();
382  return toWrappedPlainText(doc);
383 }
384 
385 QString TextEdit::toWrappedPlainText(QTextDocument* doc) const
386 {
387  QString temp;
388  QRegExp rx( QLatin1String( "(http|ftp|ldap)s?\\S+-$" ) );
389  QTextBlock block = doc->begin();
390  while ( block.isValid() ) {
391  QTextLayout *layout = block.layout();
392  const int numberOfLine( layout->lineCount() );
393  bool urlStart = false;
394  for ( int i = 0; i < numberOfLine; ++i ) {
395  QTextLine line = layout->lineAt( i );
396  QString lineText = block.text().mid( line.textStart(), line.textLength() );
397 
398  if ( lineText.contains(rx) || ( urlStart && !lineText.contains( QLatin1Char(' ') ) && lineText.endsWith( QLatin1Char('-') ) ) ) {
399  // don't insert line break in URL
400  temp += lineText;
401  urlStart = true;
402  } else {
403  temp += lineText + QLatin1Char( '\n' );
404  }
405  }
406  block = block.next();
407  }
408 
409  // Remove the last superfluous newline added above
410  if ( temp.endsWith( QLatin1Char( '\n' ) ) ) {
411  temp.chop( 1 );
412  }
413 
414  d->fixupTextEditString( temp );
415  return temp;
416 }
417 
418 QString TextEdit::toCleanPlainText(const QString& plainText) const
419 {
420  QString temp = plainText;
421  d->fixupTextEditString( temp );
422  return temp;
423 }
424 
425 QString TextEdit::toCleanPlainText() const
426 {
427  return toCleanPlainText(toPlainText());
428 }
429 
430 void TextEdit::createActions( KActionCollection *actionCollection )
431 {
432  KRichTextWidget::createActions( actionCollection );
433 
434  if ( d->imageSupportEnabled ) {
435  d->actionAddImage = new KAction( KIcon( QLatin1String( "insert-image" ) ),
436  i18n( "Add Image" ), this );
437  actionCollection->addAction( QLatin1String( "add_image" ), d->actionAddImage );
438  connect( d->actionAddImage, SIGNAL(triggered(bool)), SLOT(_k_slotAddImage()) );
439  }
440  if ( d->emoticonSupportEnabled ) {
441  d->actionAddEmoticon = new EmoticonTextEditAction( this );
442  actionCollection->addAction( QLatin1String( "add_emoticon" ), d->actionAddEmoticon );
443  connect( d->actionAddEmoticon, SIGNAL(emoticonActivated(QString)), SLOT(_k_slotAddEmoticon(QString)) );
444  }
445 
446  if ( d->insertHtmlSupportEnabled ) {
447  d->actionInsertHtml = new KAction( i18n( "Insert HTML" ), this );
448  actionCollection->addAction( QLatin1String( "insert_html" ), d->actionInsertHtml );
449  connect( d->actionInsertHtml, SIGNAL(triggered(bool)), SLOT(_k_slotInsertHtml()) );
450  }
451 
452  if ( d->insertTableSupportEnabled ) {
453  d->actionTable = new TableActionMenu( actionCollection, this );
454  d->actionTable->setIcon( KIcon( QLatin1String( "table" ) ) );
455  d->actionTable->setText( i18n( "Table" ) );
456  d->actionTable->setDelayed( false );
457  actionCollection->addAction( QLatin1String( "insert_table" ), d->actionTable );
458  }
459 
460 
461  d->actionDeleteLine = new KAction( i18n( "Delete Line" ), this );
462  d->actionDeleteLine->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ) );
463  actionCollection->addAction( QLatin1String( "delete_line" ), d->actionDeleteLine );
464  connect( d->actionDeleteLine, SIGNAL(triggered(bool)), SLOT(_k_slotDeleteLine()) );
465 
466  d->actionFormatReset = new KAction( KIcon( QLatin1String("draw-eraser") ), i18n("Reset Font Settings"), this );
467  d->actionFormatReset->setIconText( i18n("Reset Font") );
468  actionCollection->addAction( QLatin1String("format_reset"), d->actionFormatReset );
469  connect( d->actionFormatReset, SIGNAL(triggered(bool)), SLOT(_k_slotFormatReset()) );
470 
471 
472 }
473 
474 void TextEdit::addImage(const KUrl &url, int width, int height)
475 {
476  addImageHelper( url, width, height );
477 }
478 
479 void TextEdit::addImage( const KUrl &url)
480 {
481  addImageHelper( url );
482 }
483 
484 void TextEdit::addImageHelper(const KUrl &url, int width, int height)
485 {
486  QImage image;
487  if ( !image.load( url.path() ) ) {
488  KMessageBox::error( this,
489  i18nc( "@info",
490  "Unable to load image <filename>%1</filename>.",
491  url.path() ) );
492  return;
493  }
494  QFileInfo fi( url.path() );
495  QString imageName = fi.baseName().isEmpty() ? QLatin1String( "image.png" )
496  : QString( fi.baseName() + QLatin1String( ".png" ) );
497  d->addImageHelper( imageName, image, width, height );
498 }
499 
500 void TextEdit::loadImage ( const QImage &image, const QString &matchName,
501  const QString &resourceName )
502 {
503  QSet<int> cursorPositionsToSkip;
504  QTextBlock currentBlock = document()->begin();
505  QTextBlock::iterator it;
506  while ( currentBlock.isValid() ) {
507  for ( it = currentBlock.begin(); !it.atEnd(); ++it ) {
508  QTextFragment fragment = it.fragment();
509  if ( fragment.isValid() ) {
510  QTextImageFormat imageFormat = fragment.charFormat().toImageFormat();
511  if ( imageFormat.isValid() && imageFormat.name() == matchName ) {
512  int pos = fragment.position();
513  if ( !cursorPositionsToSkip.contains( pos ) ) {
514  QTextCursor cursor( document() );
515  cursor.setPosition( pos );
516  cursor.setPosition( pos + 1, QTextCursor::KeepAnchor );
517  cursor.removeSelectedText();
518  document()->addResource( QTextDocument::ImageResource,
519  QUrl( resourceName ), QVariant( image ) );
520  QTextImageFormat format;
521  format.setName( resourceName );
522  if ( (imageFormat.width()!=0) && (imageFormat.height()!=0) ) {
523  format.setWidth( imageFormat.width() );
524  format.setHeight( imageFormat.height() );
525  }
526  cursor.insertImage( format );
527 
528 
529  // The textfragment iterator is now invalid, restart from the beginning
530  // Take care not to replace the same fragment again, or we would be in
531  // an infinite loop.
532  cursorPositionsToSkip.insert( pos );
533  it = currentBlock.begin();
534  }
535  }
536  }
537  }
538  currentBlock = currentBlock.next();
539  }
540 }
541 
542 void TextEditPrivate::addImageHelper( const QString &imageName, const QImage &image,int width, int height )
543 {
544  QString imageNameToAdd = imageName;
545  QTextDocument *document = q->document();
546 
547  // determine the imageNameToAdd
548  int imageNumber = 1;
549  while ( mImageNames.contains( imageNameToAdd ) ) {
550  QVariant qv = document->resource( QTextDocument::ImageResource, QUrl( imageNameToAdd ) );
551  if ( qv == image ) {
552  // use the same name
553  break;
554  }
555  int firstDot = imageName.indexOf( QLatin1Char( '.' ) );
556  if ( firstDot == -1 ) {
557  imageNameToAdd = imageName + QString::number( imageNumber++ );
558  } else {
559  imageNameToAdd = imageName.left( firstDot ) + QString::number( imageNumber++ ) +
560  imageName.mid( firstDot );
561  }
562  }
563 
564  if ( !mImageNames.contains( imageNameToAdd ) ) {
565  document->addResource( QTextDocument::ImageResource, QUrl( imageNameToAdd ), image );
566  mImageNames << imageNameToAdd;
567  }
568  if ( width != -1 && height != -1 ) {
569  QTextImageFormat format;
570  format.setName( imageNameToAdd );
571  format.setWidth( width );
572  format.setHeight( height );
573  q->textCursor().insertImage( format );
574  } else {
575  q->textCursor().insertImage( imageNameToAdd );
576  }
577  q->enableRichTextMode();
578 }
579 
580 ImageWithNameList TextEdit::imagesWithName() const
581 {
582  ImageWithNameList retImages;
583  QStringList seenImageNames;
584  QList<QTextImageFormat> imageFormats = d->embeddedImageFormats();
585  foreach ( const QTextImageFormat &imageFormat, imageFormats ) {
586  if ( !seenImageNames.contains( imageFormat.name() ) ) {
587  QVariant resourceData = document()->resource( QTextDocument::ImageResource,
588  QUrl( imageFormat.name() ) );
589  QImage image = qvariant_cast<QImage>( resourceData );
590  QString name = imageFormat.name();
591  ImageWithNamePtr newImage( new ImageWithName );
592  newImage->image = image;
593  newImage->name = name;
594  retImages.append( newImage );
595  seenImageNames.append( imageFormat.name() );
596  }
597  }
598  return retImages;
599 }
600 
601 QList< QSharedPointer<EmbeddedImage> > TextEdit::embeddedImages() const
602 {
603  ImageWithNameList normalImages = imagesWithName();
604  QList< QSharedPointer<EmbeddedImage> > retImages;
605  foreach ( const ImageWithNamePtr &normalImage, normalImages ) {
606  QBuffer buffer;
607  buffer.open( QIODevice::WriteOnly );
608  normalImage->image.save( &buffer, "PNG" );
609 
610  qsrand( QDateTime::currentDateTime().toTime_t() + qHash( normalImage->name ) );
611  QSharedPointer<EmbeddedImage> embeddedImage( new EmbeddedImage() );
612  retImages.append( embeddedImage );
613  embeddedImage->image = KMime::Codec::codecForName( "base64" )->encode( buffer.buffer() );
614  embeddedImage->imageName = normalImage->name;
615  embeddedImage->contentID = QString( QLatin1String( "%1@KDE" ) ).arg( qrand() );
616  }
617  return retImages;
618 }
619 
620 QList<QTextImageFormat> TextEditPrivate::embeddedImageFormats() const
621 {
622  QTextDocument *doc = q->document();
623  QList<QTextImageFormat> retList;
624 
625  QTextBlock currentBlock = doc->begin();
626  while ( currentBlock.isValid() ) {
627  QTextBlock::iterator it;
628  for ( it = currentBlock.begin(); !it.atEnd(); ++it ) {
629  QTextFragment fragment = it.fragment();
630  if ( fragment.isValid() ) {
631  QTextImageFormat imageFormat = fragment.charFormat().toImageFormat();
632  if ( imageFormat.isValid() ) {
633  //TODO: Replace with a way to see if an image is an embedded image or a remote
634  QUrl url( imageFormat.name() );
635  if ( !url.isValid() || !url.scheme().startsWith( QLatin1String( "http" ) ) ) {
636  retList.append( imageFormat );
637  }
638  }
639  }
640  }
641  currentBlock = currentBlock.next();
642  }
643  return retList;
644 }
645 
646 void TextEditPrivate::_k_slotAddEmoticon( const QString& text)
647 {
648  QTextCursor cursor = q->textCursor();
649  cursor.insertText( text );
650 }
651 
652 void TextEditPrivate::_k_slotInsertHtml()
653 {
654  if ( q->textMode() == KRichTextEdit::Rich ) {
655  QPointer<InsertHtmlDialog> dialog = new InsertHtmlDialog( q );
656  if ( dialog->exec() ) {
657  const QString str = dialog->html();
658  if ( !str.isEmpty() ) {
659  QTextCursor cursor = q->textCursor();
660  cursor.insertHtml( str );
661  }
662  }
663  delete dialog;
664  }
665 }
666 
667 void TextEditPrivate::_k_slotAddImage()
668 {
669  QPointer<InsertImageDialog> dlg = new InsertImageDialog( q );
670  if ( dlg->exec() == KDialog::Accepted && dlg ) {
671  const KUrl url = dlg->imageUrl();
672  int imageWidth = -1;
673  int imageHeight = -1;
674  if ( !dlg->keepOriginalSize() ) {
675  imageWidth = dlg->imageWidth();
676  imageHeight = dlg->imageHeight();
677  }
678  q->addImage( url, imageWidth, imageHeight );
679  }
680  delete dlg;
681 }
682 
683 void TextEditPrivate::_k_slotTextModeChanged(KRichTextEdit::Mode mode)
684 {
685  if(mode == KRichTextEdit::Rich) {
686  saveFont = q->currentFont();
687  }
688 }
689 
690 
691 void TextEditPrivate::_k_slotFormatReset()
692 {
693  q->setTextBackgroundColor( q->palette().highlightedText().color() );
694  q->setTextForegroundColor( q->palette().text().color() );
695  q->setFont( saveFont );
696 
697 }
698 
699 void KPIMTextEdit::TextEdit::enableImageActions()
700 {
701  d->imageSupportEnabled = true;
702 }
703 
704 bool KPIMTextEdit::TextEdit::isEnableImageActions() const
705 {
706  return d->imageSupportEnabled;
707 }
708 
709 void KPIMTextEdit::TextEdit::enableEmoticonActions()
710 {
711  d->emoticonSupportEnabled = true;
712 }
713 
714 bool KPIMTextEdit::TextEdit::isEnableEmoticonActions() const
715 {
716  return d->emoticonSupportEnabled;
717 }
718 
719 void KPIMTextEdit::TextEdit::enableInsertHtmlActions()
720 {
721  d->insertHtmlSupportEnabled = true;
722 }
723 
724 bool KPIMTextEdit::TextEdit::isEnableInsertHtmlActions() const
725 {
726  return d->insertHtmlSupportEnabled;
727 }
728 
729 bool KPIMTextEdit::TextEdit::isEnableInsertTableActions() const
730 {
731  return d->insertTableSupportEnabled;
732 }
733 
734 void KPIMTextEdit::TextEdit::enableInsertTableActions()
735 {
736  d->insertTableSupportEnabled = true;
737 }
738 
739 
740 QByteArray KPIMTextEdit::TextEdit::imageNamesToContentIds(
741  const QByteArray &htmlBody, const KPIMTextEdit::ImageList &imageList )
742 {
743  QByteArray result = htmlBody;
744  if ( !imageList.isEmpty() ) {
745  foreach ( const QSharedPointer<EmbeddedImage> &image, imageList ) {
746  const QString newImageName = QLatin1String( "cid:" ) + image->contentID;
747  QByteArray quote( "\"" );
748  result.replace( QByteArray( quote + image->imageName.toLocal8Bit() + quote ),
749  QByteArray( quote + newImageName.toLocal8Bit() + quote ) );
750  }
751  }
752  return result;
753 }
754 
755 void TextEdit::insertImage( const QImage &image, const QFileInfo &fileInfo )
756 {
757  QString imageName = fileInfo.baseName().isEmpty() ?
758  i18nc( "Start of the filename for an image", "image" ) :
759  fileInfo.baseName();
760  d->addImageHelper( imageName, image );
761 }
762 
763 void TextEdit::insertFromMimeData( const QMimeData *source )
764 {
765  // Add an image if that is on the clipboard
766  if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled ) {
767  QImage image = qvariant_cast<QImage>( source->imageData() );
768  QFileInfo fi( source->text() );
769  insertImage( image, fi );
770  return;
771  }
772 
773  // Attempt to paste HTML contents into the text edit in plain text mode,
774  // prevent this and prevent plain text instead.
775  if ( textMode() == KRichTextEdit::Plain && source->hasHtml() ) {
776  if ( source->hasText() ) {
777  insertPlainText( source->text() );
778  return;
779  }
780  }
781 
782  KRichTextWidget::insertFromMimeData( source );
783 }
784 
785 bool KPIMTextEdit::TextEdit::canInsertFromMimeData( const QMimeData *source ) const
786 {
787  if ( source->hasHtml() && textMode() == KRichTextEdit::Rich ) {
788  return true;
789  }
790 
791  if ( source->hasText() ) {
792  return true;
793  }
794 
795  if ( textMode() == KRichTextEdit::Rich && source->hasImage() && d->imageSupportEnabled ) {
796  return true;
797  }
798 
799  return KRichTextWidget::canInsertFromMimeData( source );
800 }
801 
802 bool TextEdit::isFormattingUsed() const
803 {
804  if ( textMode() == Plain ) {
805  return false;
806  }
807 
808  return TextUtils::containsFormatting( document() );
809 }
810 
811 void TextEditPrivate::_k_slotDeleteLine()
812 {
813  if ( q->hasFocus() ) {
814  q->deleteCurrentLine();
815  }
816 }
817 
818 void TextEdit::deleteCurrentLine()
819 {
820  QTextCursor cursor = textCursor();
821  QTextBlock block = cursor.block();
822  const QTextLayout *layout = block.layout();
823 
824  // The current text block can have several lines due to word wrapping.
825  // Search the line the cursor is in, and then delete it.
826  for ( int lineNumber = 0; lineNumber < layout->lineCount(); lineNumber++ ) {
827  QTextLine line = layout->lineAt( lineNumber );
828  const bool lastLineInBlock = ( line.textStart() + line.textLength() == block.length() - 1 );
829  const bool oneLineBlock = ( layout->lineCount() == 1 );
830  const int startOfLine = block.position() + line.textStart();
831  int endOfLine = block.position() + line.textStart() + line.textLength();
832  if ( !lastLineInBlock ) {
833  endOfLine -= 1;
834  }
835 
836  // Found the line where the cursor is in
837  if ( cursor.position() >= startOfLine && cursor.position() <= endOfLine ) {
838  int deleteStart = startOfLine;
839  int deleteLength = line.textLength();
840  if ( oneLineBlock ) {
841  deleteLength++; // The trailing newline
842  }
843 
844  // When deleting the last line in the document,
845  // remove the newline of the line before the last line instead
846  if ( deleteStart + deleteLength >= document()->characterCount() &&
847  deleteStart > 0 ) {
848  deleteStart--;
849  }
850 
851  cursor.beginEditBlock();
852  cursor.setPosition( deleteStart );
853  cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor, deleteLength );
854  cursor.removeSelectedText();
855  cursor.endEditBlock();
856  return;
857  }
858  }
859 }
860 
861 
862 #include "moc_textedit.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:26:01 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KPIMTextedit Library

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

kdepimlibs-4.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal