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

KDEUI

krichtextedit.cpp

Go to the documentation of this file.
00001 /*
00002  * krichtextedit
00003  *
00004  * Copyright 2007 Laurent Montel <montel@kde.org>
00005  * Copyright 2008 Thomas McGuire <thomas.mcguire@gmx.net>
00006  * Copyright 2008 Stephen Kelly  <steveire@gmail.com>
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00021  * 02110-1301  USA
00022  */
00023 
00024 #include "krichtextedit.h"
00025 
00026 // Own includes
00027 #include "nestedlisthelper.h"
00028 #include "klinkdialog.h"
00029 
00030 // kdelibs includes
00031 #include <kcursor.h>
00032 #include <kcolorscheme.h>
00033 
00034 // Qt includes
00035 #include <QtGui/QTextDocumentFragment>
00036 #include <QtGui/QMouseEvent>
00037 
00042 //@cond PRIVATE
00043 class KRichTextEditPrivate : public QObject
00044 {
00045 public:
00046     KRichTextEditPrivate(KRichTextEdit *parent)
00047             : q(parent),
00048             mMode(KRichTextEdit::Plain) {
00049         nestedListHelper = new NestedListHelper(q);
00050     }
00051 
00052     ~KRichTextEditPrivate() {
00053         delete nestedListHelper;
00054     }
00055 
00056     //
00057     // Normal functions
00058     //
00059 
00060     // If the text under the cursor is a link, the cursor's selection is set to
00061     // the complete link text. Otherwise selects the current word if there is no
00062     // selection.
00063     void selectLinkText() const;
00064 
00065     void init();
00066 
00067     // Switches to rich text mode and emits the mode changed signal if the
00068     // mode really changed.
00069     void activateRichText();
00070 
00071     // Applies formatting to the current word if there is no selection.
00072     void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
00073 
00074     void setTextCursor(QTextCursor &cursor);
00075 
00076 
00077     // Data members
00078 
00079     KRichTextEdit *q;
00080     KRichTextEdit::Mode mMode;
00081 
00082     NestedListHelper *nestedListHelper;
00083 
00084 };
00085 
00086 void KRichTextEditPrivate::activateRichText()
00087 {
00088     if (mMode == KRichTextEdit::Plain) {
00089         q->setAcceptRichText(true);
00090         mMode = KRichTextEdit::Rich;
00091         emit q->textModeChanged(mMode);
00092     }
00093 }
00094 
00095 void KRichTextEditPrivate::setTextCursor(QTextCursor &cursor)
00096 {
00097     q->setTextCursor(cursor);
00098 }
00099 
00100 void KRichTextEditPrivate::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
00101 {
00102     QTextCursor cursor = q->textCursor();
00103     QTextCursor wordStart(cursor);
00104     QTextCursor wordEnd(cursor);
00105 
00106     wordStart.movePosition(QTextCursor::StartOfWord);
00107     wordEnd.movePosition(QTextCursor::EndOfWord);
00108 
00109     cursor.beginEditBlock();
00110     if (!cursor.hasSelection() && cursor.position() != wordStart.position() && cursor.position() != wordEnd.position())
00111         cursor.select(QTextCursor::WordUnderCursor);
00112     cursor.mergeCharFormat(format);
00113     q->mergeCurrentCharFormat(format);
00114     cursor.endEditBlock();
00115 }
00116 //@endcond
00117 
00118 KRichTextEdit::KRichTextEdit(const QString& text, QWidget *parent)
00119         : KTextEdit(text, parent), d(new KRichTextEditPrivate(this))
00120 {
00121     d->init();
00122 }
00123 
00124 KRichTextEdit::KRichTextEdit(QWidget *parent)
00125         : KTextEdit(parent), d(new KRichTextEditPrivate(this))
00126 {
00127     d->init();
00128 }
00129 
00130 KRichTextEdit::~KRichTextEdit()
00131 {
00132     delete d;
00133 }
00134 
00135 //@cond PRIVATE
00136 void KRichTextEditPrivate::init()
00137 {
00138     q->setAcceptRichText(false);
00139     KCursor::setAutoHideCursor(q, true, true);
00140 }
00141 //@endcond
00142 
00143 void KRichTextEdit::setListStyle(int _styleIndex)
00144 {
00145     d->nestedListHelper->handleOnBulletType(-_styleIndex);
00146     setFocus();
00147     d->activateRichText();
00148 }
00149 
00150 void KRichTextEdit::indentListMore()
00151 {
00152     d->nestedListHelper->handleOnIndentMore();
00153     d->activateRichText();
00154 }
00155 
00156 void KRichTextEdit::indentListLess()
00157 {
00158     d->nestedListHelper->handleOnIndentLess();
00159 }
00160 
00161 void KRichTextEdit::insertHorizontalRule()
00162 {
00163     QTextCursor cursor = textCursor();
00164     QTextBlockFormat bf = cursor.blockFormat();
00165     QTextCharFormat cf = cursor.charFormat();
00166 
00167     cursor.beginEditBlock();
00168     cursor.insertHtml("<hr>");
00169     cursor.insertBlock(bf, cf);
00170     setTextCursor(cursor);
00171     d->activateRichText();
00172     cursor.endEditBlock();
00173 }
00174 
00175 void KRichTextEdit::alignLeft()
00176 {
00177     setAlignment(Qt::AlignLeft);
00178     setFocus();
00179     d->activateRichText();
00180 }
00181 
00182 void KRichTextEdit::alignCenter()
00183 {
00184     setAlignment(Qt::AlignHCenter);
00185     setFocus();
00186     d->activateRichText();
00187 }
00188 
00189 void KRichTextEdit::alignRight()
00190 {
00191     setAlignment(Qt::AlignRight);
00192     setFocus();
00193     d->activateRichText();
00194 }
00195 
00196 void KRichTextEdit::alignJustify()
00197 {
00198     setAlignment(Qt::AlignJustify);
00199     setFocus();
00200     d->activateRichText();
00201 }
00202 
00203 void KRichTextEdit::makeRightToLeft()
00204 {
00205     QTextBlockFormat format;
00206     format.setLayoutDirection(Qt::RightToLeft);
00207     QTextCursor cursor = textCursor();
00208     cursor.mergeBlockFormat(format);
00209     setTextCursor(cursor);
00210     setFocus();
00211     d->activateRichText();
00212 }
00213 
00214 void KRichTextEdit::makeLeftToRight()
00215 {
00216     QTextBlockFormat format;
00217     format.setLayoutDirection(Qt::LeftToRight);
00218     QTextCursor cursor = textCursor();
00219     cursor.mergeBlockFormat(format);
00220     setTextCursor(cursor);
00221     setFocus();
00222     d->activateRichText();
00223 }
00224 
00225 void KRichTextEdit::setTextBold(bool bold)
00226 {
00227     QTextCharFormat fmt;
00228     fmt.setFontWeight(bold ? QFont::Bold : QFont::Normal);
00229     d->mergeFormatOnWordOrSelection(fmt);
00230     setFocus();
00231     d->activateRichText();
00232 }
00233 
00234 void KRichTextEdit::setTextItalic(bool italic)
00235 {
00236     QTextCharFormat fmt;
00237     fmt.setFontItalic(italic);
00238     d->mergeFormatOnWordOrSelection(fmt);
00239     setFocus();
00240     d->activateRichText();
00241 }
00242 
00243 void KRichTextEdit::setTextUnderline(bool underline)
00244 {
00245     QTextCharFormat fmt;
00246     fmt.setFontUnderline(underline);
00247     d->mergeFormatOnWordOrSelection(fmt);
00248     setFocus();
00249     d->activateRichText();
00250 }
00251 
00252 void KRichTextEdit::setTextStrikeOut(bool strikeOut)
00253 {
00254     QTextCharFormat fmt;
00255     fmt.setFontStrikeOut(strikeOut);
00256     d->mergeFormatOnWordOrSelection(fmt);
00257     setFocus();
00258     d->activateRichText();
00259 }
00260 
00261 void KRichTextEdit::setTextForegroundColor(const QColor &color)
00262 {
00263     QTextCharFormat fmt;
00264     fmt.setForeground(color);
00265     d->mergeFormatOnWordOrSelection(fmt);
00266     setFocus();
00267     d->activateRichText();
00268 }
00269 
00270 void KRichTextEdit::setTextBackgroundColor(const QColor &color)
00271 {
00272     QTextCharFormat fmt;
00273     fmt.setBackground(color);
00274     d->mergeFormatOnWordOrSelection(fmt);
00275     setFocus();
00276     d->activateRichText();
00277 }
00278 
00279 void KRichTextEdit::setFontFamily(const QString &fontFamily)
00280 {
00281     QTextCharFormat fmt;
00282     fmt.setFontFamily(fontFamily);
00283     d->mergeFormatOnWordOrSelection(fmt);
00284     setFocus();
00285     d->activateRichText();
00286 }
00287 
00288 void KRichTextEdit::setFontSize(int size)
00289 {
00290     QTextCharFormat fmt;
00291     fmt.setFontPointSize(size);
00292     d->mergeFormatOnWordOrSelection(fmt);
00293     setFocus();
00294     d->activateRichText();
00295 }
00296 
00297 void KRichTextEdit::setFont(const QFont &font)
00298 {
00299     QTextCharFormat fmt;
00300     fmt.setFont(font);
00301     d->mergeFormatOnWordOrSelection(fmt);
00302     setFocus();
00303     d->activateRichText();
00304 }
00305 
00306 void KRichTextEdit::switchToPlainText()
00307 {
00308     if (d->mMode == Rich) {
00309         d->mMode = Plain;
00310         // TODO: Warn the user about this?
00311         document()->setPlainText(document()->toPlainText());
00312         setAcceptRichText(false);
00313         emit textModeChanged(d->mMode);
00314     }
00315 }
00316 
00317 void KRichTextEdit::setTextSuperScript(bool superscript)
00318 {
00319     QTextCharFormat fmt;
00320     fmt.setVerticalAlignment(superscript ? QTextCharFormat::AlignSuperScript : QTextCharFormat::AlignNormal);
00321     d->mergeFormatOnWordOrSelection(fmt);
00322     setFocus();
00323     d->activateRichText();
00324 }
00325 
00326 void KRichTextEdit::setTextSubScript(bool subscript)
00327 {
00328     QTextCharFormat fmt;
00329     fmt.setVerticalAlignment(subscript ? QTextCharFormat::AlignSubScript : QTextCharFormat::AlignNormal);
00330     d->mergeFormatOnWordOrSelection(fmt);
00331     setFocus();
00332     d->activateRichText();
00333 }
00334 
00335 void KRichTextEdit::enableRichTextMode()
00336 {
00337     d->activateRichText();
00338 }
00339 
00340 KRichTextEdit::Mode KRichTextEdit::textMode() const
00341 {
00342     return d->mMode;
00343 }
00344 
00345 QString KRichTextEdit::textOrHtml() const
00346 {
00347     if (textMode() == Rich)
00348         return toCleanHtml();
00349     else
00350         return toPlainText();
00351 }
00352 
00353 void KRichTextEdit::setTextOrHtml(const QString &text)
00354 {
00355     // might be rich text
00356     if (Qt::mightBeRichText(text)) {
00357         if (d->mMode == KRichTextEdit::Plain) {
00358             d->activateRichText();
00359         }
00360         setHtml(text);
00361     } else {
00362         setPlainText(text);
00363     }
00364 }
00365 
00366 QString KRichTextEdit::currentLinkText() const
00367 {
00368     QTextCursor cursor = textCursor();
00369     selectLinkText(&cursor);
00370     return cursor.selectedText();
00371 }
00372 
00373 void KRichTextEdit::selectLinkText() const
00374 {
00375     QTextCursor cursor = textCursor();
00376     selectLinkText(&cursor);
00377     d->setTextCursor(cursor);
00378 }
00379 
00380 void KRichTextEdit::selectLinkText(QTextCursor *cursor) const
00381 {
00382     // If the cursor is on a link, select the text of the link.
00383     if (cursor->charFormat().isAnchor()) {
00384         QString aHref = cursor->charFormat().anchorHref();
00385 
00386         // Move cursor to start of link
00387         while (cursor->charFormat().anchorHref() == aHref) {
00388             if (cursor->atStart())
00389                 break;
00390             cursor->setPosition(cursor->position() - 1);
00391         }
00392         if (cursor->charFormat().anchorHref() != aHref)
00393             cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
00394 
00395         // Move selection to the end of the link
00396         while (cursor->charFormat().anchorHref() == aHref) {
00397             if (cursor->atEnd())
00398                 break;
00399             cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
00400         }
00401         if (cursor->charFormat().anchorHref() != aHref)
00402             cursor->setPosition(cursor->position() - 1, QTextCursor::KeepAnchor);
00403     } else if (cursor->hasSelection()) {
00404         // Nothing to to. Using the currently selected text as the link text.
00405     } else {
00406 
00407         // Select current word
00408         cursor->movePosition(QTextCursor::StartOfWord);
00409         cursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00410     }
00411 }
00412 
00413 QString KRichTextEdit::currentLinkUrl() const
00414 {
00415     return textCursor().charFormat().anchorHref();
00416 }
00417 
00418 void KRichTextEdit::updateLink(const QString &linkUrl, const QString &linkText)
00419 {
00420     selectLinkText();
00421 
00422     QTextCursor cursor = textCursor();
00423     cursor.beginEditBlock();
00424 
00425     if (!cursor.hasSelection()) {
00426         cursor.select(QTextCursor::WordUnderCursor);
00427     }
00428 
00429     QTextCharFormat format = cursor.charFormat();
00430     // Save original format to create an extra space with the existing char
00431     // format for the block
00432     const QTextCharFormat originalFormat = format;
00433     if (!linkUrl.isEmpty()) {
00434         // Add link details
00435         format.setAnchor(true);
00436         format.setAnchorHref(linkUrl);
00437         // Workaround for QTBUG-1814:
00438         // Link formatting does not get applied immediately when setAnchor(true)
00439         // is called.  So the formatting needs to be applied manually.
00440         format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
00441         format.setUnderlineColor(KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::LinkText).color());
00442         format.setForeground(KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::LinkText).color());
00443         d->activateRichText();
00444     } else {
00445         // Remove link details
00446         format.setAnchor(false);
00447         format.setAnchorHref(QString());
00448         // Workaround for QTBUG-1814:
00449         // Link formatting does not get removed immediately when setAnchor(false)
00450         // is called. So the formatting needs to be applied manually.
00451         QTextDocument defaultTextDocument;
00452         QTextCharFormat defaultCharFormat = defaultTextDocument.begin().charFormat();
00453 
00454         format.setUnderlineStyle( defaultCharFormat.underlineStyle() );
00455         format.setUnderlineColor( defaultCharFormat.underlineColor() );
00456         format.setForeground( defaultCharFormat.foreground() );
00457     }
00458 
00459     // Insert link text specified in dialog, otherwise write out url.
00460     QString _linkText;
00461     if (!linkText.isEmpty()) {
00462         _linkText = linkText;
00463     } else {
00464         _linkText = linkUrl;
00465     }
00466     cursor.insertText(_linkText, format);
00467 
00468 
00469     // Insert a space after the link if at the end of the block so that
00470     // typing some text after the link does not carry link formatting
00471     if (!linkUrl.isEmpty() && cursor.atBlockEnd()) {
00472         cursor.setPosition(cursor.selectionEnd());
00473         cursor.setCharFormat(originalFormat);
00474         cursor.insertText(QString(" "));
00475     }
00476 
00477     cursor.endEditBlock();
00478 }
00479 
00480 void KRichTextEdit::keyPressEvent(QKeyEvent *event)
00481 {
00482     bool handled = false;
00483     if (textCursor().currentList()) {
00484         // handled is False if the key press event was not handled or not completely
00485         // handled by the Helper class.
00486         handled = d->nestedListHelper->handleBeforeKeyPressEvent(event);
00487     }
00488 
00489     if (!handled) {
00490         KTextEdit::keyPressEvent(event);
00491     }
00492 
00493     if (textCursor().currentList()) {
00494         d->nestedListHelper->handleAfterKeyPressEvent(event);
00495     }
00496     emit cursorPositionChanged();
00497 }
00498 
00499 // void KRichTextEdit::dropEvent(QDropEvent *event)
00500 // {
00501 //     int dropSize = event->mimeData()->text().size();
00502 //
00503 //     dropEvent( event );
00504 //     QTextCursor cursor = textCursor();
00505 //     int cursorPosition = cursor.position();
00506 //     cursor.setPosition( cursorPosition - dropSize );
00507 //     cursor.setPosition( cursorPosition, QTextCursor::KeepAnchor );
00508 //     setTextCursor( cursor );
00509 //     d->nestedListHelper->handleAfterDropEvent( event );
00510 // }
00511 
00512 
00513 bool KRichTextEdit::canIndentList() const
00514 {
00515     return d->nestedListHelper->canIndent();
00516 }
00517 
00518 bool KRichTextEdit::canDedentList() const
00519 {
00520     return d->nestedListHelper->canDedent();
00521 }
00522 
00523 QString KRichTextEdit::toCleanHtml() const
00524 {
00525   QString result = toHtml();
00526 
00527   static const QString EMPTYLINEFROMQT = QLatin1String(
00528   "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; "
00529   "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "
00530   "-qt-user-state:0;\"></p>" );
00531 
00532   static const QString EMPTYLINEHTML = QLatin1String(
00533   "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; "
00534   "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "
00535   "-qt-user-state:0;\"><br /></p>" );
00536 
00537   static const QString OLLISTPATTERNQT = QLatin1String(
00538   "<ol style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;" );
00539 
00540   static const QString ULLISTPATTERNQT = QLatin1String(
00541   "<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;" );
00542 
00543   static const QString ORDEREDLISTHTML = QLatin1String(
00544   "<ol style=\"margin-top: 0px; margin-bottom: 0px;" );
00545 
00546   static const QString UNORDEREDLISTHTML = QLatin1String(
00547   "<ul style=\"margin-top: 0px; margin-bottom: 0px;" );
00548 
00549   // fix 1 - empty lines should show as empty lines - MS Outlook treats margin-top:0px; as
00550   // a non-existing line.
00551   // Although we can simply remove the margin-top style property, we still get unwanted results
00552   // if you have three or more empty lines. It's best to replace empty <p> elements with <p><br /></p>.
00553   // As per http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_XHTML-1.0-Strict, <br> elements are still proper
00554   // HTML.
00555   result.replace(EMPTYLINEFROMQT, EMPTYLINEHTML);
00556 
00557   // fix 2a - ordered lists - MS Outlook treats margin-left:0px; as
00558   // a non-existing number; e.g: "1. First item" turns into "First Item"
00559   result.replace(OLLISTPATTERNQT, ORDEREDLISTHTML);
00560 
00561   // fix 2b - unordered lists - MS Outlook treats margin-left:0px; as
00562   // a non-existing bullet; e.g: "* First bullet" turns into "First Bullet"
00563   result.replace(ULLISTPATTERNQT, UNORDEREDLISTHTML);
00564 
00565   return result;
00566 }
00567 
00568 #include "krichtextedit.moc"

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal