• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • widgets
krichtextedit.cpp
Go to the documentation of this file.
1 /*
2  * krichtextedit
3  *
4  * Copyright 2007 Laurent Montel <montel@kde.org>
5  * Copyright 2008 Thomas McGuire <thomas.mcguire@gmx.net>
6  * Copyright 2008 Stephen Kelly <steveire@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23 
24 #include "krichtextedit.h"
25 
26 // Own includes
27 #include "nestedlisthelper.h"
28 #include "klinkdialog.h"
29 
30 // kdelibs includes
31 #include <kcursor.h>
32 #include <kcolorscheme.h>
33 
34 // Qt includes
35 #include <QtGui/QTextDocumentFragment>
36 #include <QtGui/QMouseEvent>
37 
42 //@cond PRIVATE
43 class KRichTextEditPrivate : public QObject
44 {
45 public:
46  KRichTextEditPrivate(KRichTextEdit *parent)
47  : q(parent),
48  mMode(KRichTextEdit::Plain) {
49  nestedListHelper = new NestedListHelper(q);
50  }
51 
52  ~KRichTextEditPrivate() {
53  delete nestedListHelper;
54  }
55 
56  //
57  // Normal functions
58  //
59 
60  // If the text under the cursor is a link, the cursor's selection is set to
61  // the complete link text. Otherwise selects the current word if there is no
62  // selection.
63  void selectLinkText() const;
64 
65  void init();
66 
67  // Switches to rich text mode and emits the mode changed signal if the
68  // mode really changed.
69  void activateRichText();
70 
71  // Applies formatting to the current word if there is no selection.
72  void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
73 
74  void setTextCursor(QTextCursor &cursor);
75 
76 
77  // Data members
78 
79  KRichTextEdit *q;
80  KRichTextEdit::Mode mMode;
81 
82  NestedListHelper *nestedListHelper;
83 
84 };
85 
86 void KRichTextEditPrivate::activateRichText()
87 {
88  if (mMode == KRichTextEdit::Plain) {
89  q->setAcceptRichText(true);
90  mMode = KRichTextEdit::Rich;
91  emit q->textModeChanged(mMode);
92  }
93 }
94 
95 void KRichTextEditPrivate::setTextCursor(QTextCursor &cursor)
96 {
97  q->setTextCursor(cursor);
98 }
99 
100 void KRichTextEditPrivate::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
101 {
102  QTextCursor cursor = q->textCursor();
103  QTextCursor wordStart(cursor);
104  QTextCursor wordEnd(cursor);
105 
106  wordStart.movePosition(QTextCursor::StartOfWord);
107  wordEnd.movePosition(QTextCursor::EndOfWord);
108 
109  cursor.beginEditBlock();
110  if (!cursor.hasSelection() && cursor.position() != wordStart.position() && cursor.position() != wordEnd.position())
111  cursor.select(QTextCursor::WordUnderCursor);
112  cursor.mergeCharFormat(format);
113  q->mergeCurrentCharFormat(format);
114  cursor.endEditBlock();
115 }
116 //@endcond
117 
118 KRichTextEdit::KRichTextEdit(const QString& text, QWidget *parent)
119  : KTextEdit(text, parent), d(new KRichTextEditPrivate(this))
120 {
121  d->init();
122 }
123 
124 KRichTextEdit::KRichTextEdit(QWidget *parent)
125  : KTextEdit(parent), d(new KRichTextEditPrivate(this))
126 {
127  d->init();
128 }
129 
130 KRichTextEdit::~KRichTextEdit()
131 {
132  delete d;
133 }
134 
135 //@cond PRIVATE
136 void KRichTextEditPrivate::init()
137 {
138  q->setAcceptRichText(false);
139  KCursor::setAutoHideCursor(q, true, true);
140 }
141 //@endcond
142 
143 void KRichTextEdit::setListStyle(int _styleIndex)
144 {
145  d->nestedListHelper->handleOnBulletType(-_styleIndex);
146  setFocus();
147  d->activateRichText();
148 }
149 
150 void KRichTextEdit::indentListMore()
151 {
152  d->nestedListHelper->handleOnIndentMore();
153  d->activateRichText();
154 }
155 
156 void KRichTextEdit::indentListLess()
157 {
158  d->nestedListHelper->handleOnIndentLess();
159 }
160 
161 void KRichTextEdit::insertHorizontalRule()
162 {
163  QTextCursor cursor = textCursor();
164  QTextBlockFormat bf = cursor.blockFormat();
165  QTextCharFormat cf = cursor.charFormat();
166 
167  cursor.beginEditBlock();
168  cursor.insertHtml("<hr>");
169  cursor.insertBlock(bf, cf);
170  setTextCursor(cursor);
171  d->activateRichText();
172  cursor.endEditBlock();
173 }
174 
175 void KRichTextEdit::alignLeft()
176 {
177  setAlignment(Qt::AlignLeft);
178  setFocus();
179  d->activateRichText();
180 }
181 
182 void KRichTextEdit::alignCenter()
183 {
184  setAlignment(Qt::AlignHCenter);
185  setFocus();
186  d->activateRichText();
187 }
188 
189 void KRichTextEdit::alignRight()
190 {
191  setAlignment(Qt::AlignRight);
192  setFocus();
193  d->activateRichText();
194 }
195 
196 void KRichTextEdit::alignJustify()
197 {
198  setAlignment(Qt::AlignJustify);
199  setFocus();
200  d->activateRichText();
201 }
202 
203 void KRichTextEdit::makeRightToLeft()
204 {
205  QTextBlockFormat format;
206  format.setLayoutDirection(Qt::RightToLeft);
207  QTextCursor cursor = textCursor();
208  cursor.mergeBlockFormat(format);
209  setTextCursor(cursor);
210  setFocus();
211  d->activateRichText();
212 }
213 
214 void KRichTextEdit::makeLeftToRight()
215 {
216  QTextBlockFormat format;
217  format.setLayoutDirection(Qt::LeftToRight);
218  QTextCursor cursor = textCursor();
219  cursor.mergeBlockFormat(format);
220  setTextCursor(cursor);
221  setFocus();
222  d->activateRichText();
223 }
224 
225 void KRichTextEdit::setTextBold(bool bold)
226 {
227  QTextCharFormat fmt;
228  fmt.setFontWeight(bold ? QFont::Bold : QFont::Normal);
229  d->mergeFormatOnWordOrSelection(fmt);
230  setFocus();
231  d->activateRichText();
232 }
233 
234 void KRichTextEdit::setTextItalic(bool italic)
235 {
236  QTextCharFormat fmt;
237  fmt.setFontItalic(italic);
238  d->mergeFormatOnWordOrSelection(fmt);
239  setFocus();
240  d->activateRichText();
241 }
242 
243 void KRichTextEdit::setTextUnderline(bool underline)
244 {
245  QTextCharFormat fmt;
246  fmt.setFontUnderline(underline);
247  d->mergeFormatOnWordOrSelection(fmt);
248  setFocus();
249  d->activateRichText();
250 }
251 
252 void KRichTextEdit::setTextStrikeOut(bool strikeOut)
253 {
254  QTextCharFormat fmt;
255  fmt.setFontStrikeOut(strikeOut);
256  d->mergeFormatOnWordOrSelection(fmt);
257  setFocus();
258  d->activateRichText();
259 }
260 
261 void KRichTextEdit::setTextForegroundColor(const QColor &color)
262 {
263  QTextCharFormat fmt;
264  fmt.setForeground(color);
265  d->mergeFormatOnWordOrSelection(fmt);
266  setFocus();
267  d->activateRichText();
268 }
269 
270 void KRichTextEdit::setTextBackgroundColor(const QColor &color)
271 {
272  QTextCharFormat fmt;
273  fmt.setBackground(color);
274  d->mergeFormatOnWordOrSelection(fmt);
275  setFocus();
276  d->activateRichText();
277 }
278 
279 void KRichTextEdit::setFontFamily(const QString &fontFamily)
280 {
281  QTextCharFormat fmt;
282  fmt.setFontFamily(fontFamily);
283  d->mergeFormatOnWordOrSelection(fmt);
284  setFocus();
285  d->activateRichText();
286 }
287 
288 void KRichTextEdit::setFontSize(int size)
289 {
290  QTextCharFormat fmt;
291  fmt.setFontPointSize(size);
292  d->mergeFormatOnWordOrSelection(fmt);
293  setFocus();
294  d->activateRichText();
295 }
296 
297 void KRichTextEdit::setFont(const QFont &font)
298 {
299  QTextCharFormat fmt;
300  fmt.setFont(font);
301  d->mergeFormatOnWordOrSelection(fmt);
302  setFocus();
303  d->activateRichText();
304 }
305 
306 void KRichTextEdit::switchToPlainText()
307 {
308  if (d->mMode == Rich) {
309  d->mMode = Plain;
310  // TODO: Warn the user about this?
311  document()->setPlainText(document()->toPlainText());
312  setAcceptRichText(false);
313  emit textModeChanged(d->mMode);
314  }
315 }
316 
317 void KRichTextEdit::setTextSuperScript(bool superscript)
318 {
319  QTextCharFormat fmt;
320  fmt.setVerticalAlignment(superscript ? QTextCharFormat::AlignSuperScript : QTextCharFormat::AlignNormal);
321  d->mergeFormatOnWordOrSelection(fmt);
322  setFocus();
323  d->activateRichText();
324 }
325 
326 void KRichTextEdit::setTextSubScript(bool subscript)
327 {
328  QTextCharFormat fmt;
329  fmt.setVerticalAlignment(subscript ? QTextCharFormat::AlignSubScript : QTextCharFormat::AlignNormal);
330  d->mergeFormatOnWordOrSelection(fmt);
331  setFocus();
332  d->activateRichText();
333 }
334 
335 void KRichTextEdit::enableRichTextMode()
336 {
337  d->activateRichText();
338 }
339 
340 KRichTextEdit::Mode KRichTextEdit::textMode() const
341 {
342  return d->mMode;
343 }
344 
345 QString KRichTextEdit::textOrHtml() const
346 {
347  if (textMode() == Rich)
348  return toCleanHtml();
349  else
350  return toPlainText();
351 }
352 
353 void KRichTextEdit::setTextOrHtml(const QString &text)
354 {
355  // might be rich text
356  if (Qt::mightBeRichText(text)) {
357  if (d->mMode == KRichTextEdit::Plain) {
358  d->activateRichText();
359  }
360  setHtml(text);
361  } else {
362  setPlainText(text);
363  }
364 }
365 
366 QString KRichTextEdit::currentLinkText() const
367 {
368  QTextCursor cursor = textCursor();
369  selectLinkText(&cursor);
370  return cursor.selectedText();
371 }
372 
373 void KRichTextEdit::selectLinkText() const
374 {
375  QTextCursor cursor = textCursor();
376  selectLinkText(&cursor);
377  d->setTextCursor(cursor);
378 }
379 
380 void KRichTextEdit::selectLinkText(QTextCursor *cursor) const
381 {
382  // If the cursor is on a link, select the text of the link.
383  if (cursor->charFormat().isAnchor()) {
384  QString aHref = cursor->charFormat().anchorHref();
385 
386  // Move cursor to start of link
387  while (cursor->charFormat().anchorHref() == aHref) {
388  if (cursor->atStart())
389  break;
390  cursor->setPosition(cursor->position() - 1);
391  }
392  if (cursor->charFormat().anchorHref() != aHref)
393  cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
394 
395  // Move selection to the end of the link
396  while (cursor->charFormat().anchorHref() == aHref) {
397  if (cursor->atEnd())
398  break;
399  cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
400  }
401  if (cursor->charFormat().anchorHref() != aHref)
402  cursor->setPosition(cursor->position() - 1, QTextCursor::KeepAnchor);
403  } else if (cursor->hasSelection()) {
404  // Nothing to to. Using the currently selected text as the link text.
405  } else {
406 
407  // Select current word
408  cursor->movePosition(QTextCursor::StartOfWord);
409  cursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
410  }
411 }
412 
413 QString KRichTextEdit::currentLinkUrl() const
414 {
415  return textCursor().charFormat().anchorHref();
416 }
417 
418 void KRichTextEdit::updateLink(const QString &linkUrl, const QString &linkText)
419 {
420  selectLinkText();
421 
422  QTextCursor cursor = textCursor();
423  cursor.beginEditBlock();
424 
425  if (!cursor.hasSelection()) {
426  cursor.select(QTextCursor::WordUnderCursor);
427  }
428 
429  QTextCharFormat format = cursor.charFormat();
430  // Save original format to create an extra space with the existing char
431  // format for the block
432  const QTextCharFormat originalFormat = format;
433  if (!linkUrl.isEmpty()) {
434  // Add link details
435  format.setAnchor(true);
436  format.setAnchorHref(linkUrl);
437  // Workaround for QTBUG-1814:
438  // Link formatting does not get applied immediately when setAnchor(true)
439  // is called. So the formatting needs to be applied manually.
440  format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
441  format.setUnderlineColor(KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::LinkText).color());
442  format.setForeground(KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::LinkText).color());
443  d->activateRichText();
444  } else {
445  // Remove link details
446  format.setAnchor(false);
447  format.setAnchorHref(QString());
448  // Workaround for QTBUG-1814:
449  // Link formatting does not get removed immediately when setAnchor(false)
450  // is called. So the formatting needs to be applied manually.
451  QTextDocument defaultTextDocument;
452  QTextCharFormat defaultCharFormat = defaultTextDocument.begin().charFormat();
453 
454  format.setUnderlineStyle( defaultCharFormat.underlineStyle() );
455  format.setUnderlineColor( defaultCharFormat.underlineColor() );
456  format.setForeground( defaultCharFormat.foreground() );
457  }
458 
459  // Insert link text specified in dialog, otherwise write out url.
460  QString _linkText;
461  if (!linkText.isEmpty()) {
462  _linkText = linkText;
463  } else {
464  _linkText = linkUrl;
465  }
466  cursor.insertText(_linkText, format);
467 
468 
469  // Insert a space after the link if at the end of the block so that
470  // typing some text after the link does not carry link formatting
471  if (!linkUrl.isEmpty() && cursor.atBlockEnd()) {
472  cursor.setPosition(cursor.selectionEnd());
473  cursor.setCharFormat(originalFormat);
474  cursor.insertText(QString(" "));
475  }
476 
477  cursor.endEditBlock();
478 }
479 
480 void KRichTextEdit::keyPressEvent(QKeyEvent *event)
481 {
482  bool handled = false;
483  if (textCursor().currentList()) {
484  // handled is False if the key press event was not handled or not completely
485  // handled by the Helper class.
486  handled = d->nestedListHelper->handleBeforeKeyPressEvent(event);
487  }
488 
489  if (!handled) {
490  KTextEdit::keyPressEvent(event);
491  }
492 
493  if (textCursor().currentList()) {
494  d->nestedListHelper->handleAfterKeyPressEvent(event);
495  }
496  emit cursorPositionChanged();
497 }
498 
499 // void KRichTextEdit::dropEvent(QDropEvent *event)
500 // {
501 // int dropSize = event->mimeData()->text().size();
502 //
503 // dropEvent( event );
504 // QTextCursor cursor = textCursor();
505 // int cursorPosition = cursor.position();
506 // cursor.setPosition( cursorPosition - dropSize );
507 // cursor.setPosition( cursorPosition, QTextCursor::KeepAnchor );
508 // setTextCursor( cursor );
509 // d->nestedListHelper->handleAfterDropEvent( event );
510 // }
511 
512 
513 bool KRichTextEdit::canIndentList() const
514 {
515  return d->nestedListHelper->canIndent();
516 }
517 
518 bool KRichTextEdit::canDedentList() const
519 {
520  return d->nestedListHelper->canDedent();
521 }
522 
523 QString KRichTextEdit::toCleanHtml() const
524 {
525  QString result = toHtml();
526 
527  static const QString EMPTYLINEHTML = QLatin1String(
528  "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; "
529  "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; \">&nbsp;</p>" );
530 
531  // Qt inserts various style properties based on the current mode of the editor (underline,
532  // bold, etc), but only empty paragraphs *also* have qt-paragraph-type set to 'empty'.
533  static const QString EMPTYLINEREGEX = QLatin1String(
534  "<p style=\"-qt-paragraph-type:empty;(.*)</p>" );
535 
536  static const QString OLLISTPATTERNQT = QLatin1String(
537  "<ol style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;" );
538 
539  static const QString ULLISTPATTERNQT = QLatin1String(
540  "<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;" );
541 
542  static const QString ORDEREDLISTHTML = QLatin1String(
543  "<ol style=\"margin-top: 0px; margin-bottom: 0px;" );
544 
545  static const QString UNORDEREDLISTHTML = QLatin1String(
546  "<ul style=\"margin-top: 0px; margin-bottom: 0px;" );
547 
548  // fix 1 - empty lines should show as empty lines - MS Outlook treats margin-top:0px; as
549  // a non-existing line.
550  // Although we can simply remove the margin-top style property, we still get unwanted results
551  // if you have three or more empty lines. It's best to replace empty <p> elements with <p>&nbsp;</p>.
552 
553  QRegExp emptyLineFinder( EMPTYLINEREGEX );
554  emptyLineFinder.setMinimal( true );
555 
556  // find the first occurance
557  int offset = emptyLineFinder.indexIn( result, 0 );
558  while (offset != -1) {
559  // replace all the matching text with the new line text
560  result.replace( offset, emptyLineFinder.matchedLength(), EMPTYLINEHTML );
561  // advance the search offset to just beyond the last replace
562  offset += EMPTYLINEHTML.length();
563  // find the next occurance
564  offset = emptyLineFinder.indexIn( result, offset );
565  }
566 
567  // fix 2a - ordered lists - MS Outlook treats margin-left:0px; as
568  // a non-existing number; e.g: "1. First item" turns into "First Item"
569  result.replace(OLLISTPATTERNQT, ORDEREDLISTHTML);
570 
571  // fix 2b - unordered lists - MS Outlook treats margin-left:0px; as
572  // a non-existing bullet; e.g: "* First bullet" turns into "First Bullet"
573  result.replace(ULLISTPATTERNQT, UNORDEREDLISTHTML);
574 
575  return result;
576 }
577 
578 #include "krichtextedit.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Dec 7 2012 16:04:43 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
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