• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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 
00033 // Qt includes
00034 #include <QtGui/QTextDocumentFragment>
00035 #include <QtGui/QMouseEvent>
00036 
00041 //@cond PRIVATE
00042 class KRichTextEditPrivate : public QObject
00043 {
00044 public:
00045     KRichTextEditPrivate(KRichTextEdit *parent)
00046             : q(parent),
00047             mMode(KRichTextEdit::Plain) {
00048         nestedListHelper = new NestedListHelper(q);
00049     }
00050 
00051     ~KRichTextEditPrivate() {
00052         delete nestedListHelper;
00053     }
00054 
00055     //
00056     // Normal functions
00057     //
00058 
00059     // If the text under the cursor is a link, the cursor's selection is set to
00060     // the complete link text. Otherwise selects the current word if there is no
00061     // selection.
00062     void selectLinkText() const;
00063 
00064     void init();
00065 
00066     // Switches to rich text mode and emits the mode changed signal if the
00067     // mode really changed.
00068     void activateRichText();
00069 
00070     // Applies formatting to the current word if there is no selection.
00071     void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
00072 
00073     void setTextCursor(QTextCursor &cursor);
00074 
00075 
00076     // Data members
00077 
00078     KRichTextEdit *q;
00079     KRichTextEdit::Mode mMode;
00080 
00081     NestedListHelper *nestedListHelper;
00082 
00083 };
00084 
00085 void KRichTextEditPrivate::activateRichText()
00086 {
00087     if (mMode == KRichTextEdit::Plain) {
00088         q->setAcceptRichText(true);
00089         mMode = KRichTextEdit::Rich;
00090         emit q->textModeChanged(mMode);
00091     }
00092 }
00093 
00094 void KRichTextEditPrivate::setTextCursor(QTextCursor &cursor)
00095 {
00096     q->setTextCursor(cursor);
00097 }
00098 
00099 void KRichTextEditPrivate::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
00100 {
00101     QTextCursor cursor = q->textCursor();
00102     cursor.beginEditBlock();
00103     if (!cursor.hasSelection())
00104         cursor.select(QTextCursor::WordUnderCursor);
00105     cursor.mergeCharFormat(format);
00106     q->mergeCurrentCharFormat(format);
00107     cursor.endEditBlock();
00108 }
00109 //@endcond
00110 
00111 KRichTextEdit::KRichTextEdit(const QString& text, QWidget *parent)
00112         : KTextEdit(text, parent), d(new KRichTextEditPrivate(this))
00113 {
00114     d->init();
00115 }
00116 
00117 KRichTextEdit::KRichTextEdit(QWidget *parent)
00118         : KTextEdit(parent), d(new KRichTextEditPrivate(this))
00119 {
00120     d->init();
00121 }
00122 
00123 KRichTextEdit::~KRichTextEdit()
00124 {
00125     delete d;
00126 }
00127 
00128 //@cond PRIVATE
00129 void KRichTextEditPrivate::init()
00130 {
00131     KCursor::setAutoHideCursor(q, true, true);
00132 }
00133 //@endcond
00134 
00135 void KRichTextEdit::setListStyle(int _styleIndex)
00136 {
00137     d->nestedListHelper->handleOnBulletType(-_styleIndex);
00138     setFocus();
00139     d->activateRichText();
00140 }
00141 
00142 void KRichTextEdit::indentListMore()
00143 {
00144     d->nestedListHelper->handleOnIndentMore();
00145     d->activateRichText();
00146 }
00147 
00148 void KRichTextEdit::indentListLess()
00149 {
00150     d->nestedListHelper->handleOnIndentLess();
00151 }
00152 
00153 void KRichTextEdit::insertHorizontalRule()
00154 {
00155     QTextCursor cursor = textCursor();
00156     QTextBlockFormat bf = cursor.blockFormat();
00157     QTextCharFormat cf = cursor.charFormat();
00158 
00159     cursor.beginEditBlock();
00160     cursor.insertHtml("<hr>");
00161     cursor.insertBlock(bf, cf);
00162     setTextCursor(cursor);
00163     d->activateRichText();
00164     cursor.endEditBlock();
00165 }
00166 
00167 void KRichTextEdit::alignLeft()
00168 {
00169     setAlignment(Qt::AlignLeft);
00170     setFocus();
00171     d->activateRichText();
00172 }
00173 
00174 void KRichTextEdit::alignCenter()
00175 {
00176     setAlignment(Qt::AlignHCenter);
00177     setFocus();
00178     d->activateRichText();
00179 }
00180 
00181 void KRichTextEdit::alignRight()
00182 {
00183     setAlignment(Qt::AlignRight);
00184     setFocus();
00185     d->activateRichText();
00186 }
00187 
00188 void KRichTextEdit::alignJustify()
00189 {
00190     setAlignment(Qt::AlignJustify);
00191     setFocus();
00192     d->activateRichText();
00193 }
00194 
00195 void KRichTextEdit::setTextBold(bool bold)
00196 {
00197     QTextCharFormat fmt;
00198     fmt.setFontWeight(bold ? QFont::Bold : QFont::Normal);
00199     d->mergeFormatOnWordOrSelection(fmt);
00200     setFocus();
00201     d->activateRichText();
00202 }
00203 
00204 void KRichTextEdit::setTextItalic(bool italic)
00205 {
00206     QTextCharFormat fmt;
00207     fmt.setFontItalic(italic);
00208     d->mergeFormatOnWordOrSelection(fmt);
00209     setFocus();
00210     d->activateRichText();
00211 }
00212 
00213 void KRichTextEdit::setTextUnderline(bool underline)
00214 {
00215     QTextCharFormat fmt;
00216     fmt.setFontUnderline(underline);
00217     d->mergeFormatOnWordOrSelection(fmt);
00218     setFocus();
00219     d->activateRichText();
00220 }
00221 
00222 void KRichTextEdit::setTextStrikeOut(bool strikeOut)
00223 {
00224     QTextCharFormat fmt;
00225     fmt.setFontStrikeOut(strikeOut);
00226     d->mergeFormatOnWordOrSelection(fmt);
00227     setFocus();
00228     d->activateRichText();
00229 }
00230 
00231 void KRichTextEdit::setTextForegroundColor(const QColor &color)
00232 {
00233     QTextCharFormat fmt;
00234     fmt.setForeground(color);
00235     d->mergeFormatOnWordOrSelection(fmt);
00236     setFocus();
00237     d->activateRichText();
00238 }
00239 
00240 void KRichTextEdit::setTextBackgroundColor(const QColor &color)
00241 {
00242     QTextCharFormat fmt;
00243     fmt.setBackground(color);
00244     d->mergeFormatOnWordOrSelection(fmt);
00245     setFocus();
00246     d->activateRichText();
00247 }
00248 
00249 void KRichTextEdit::setFontFamily(const QString &fontFamily)
00250 {
00251     QTextCharFormat fmt;
00252     fmt.setFontFamily(fontFamily);
00253     d->mergeFormatOnWordOrSelection(fmt);
00254     setFocus();
00255     d->activateRichText();
00256 }
00257 
00258 void KRichTextEdit::setFontSize(int size)
00259 {
00260     QTextCharFormat fmt;
00261     fmt.setFontPointSize(size);
00262     d->mergeFormatOnWordOrSelection(fmt);
00263     setFocus();
00264     d->activateRichText();
00265 }
00266 
00267 void KRichTextEdit::setFont(const QFont &font)
00268 {
00269     QTextCharFormat fmt;
00270     fmt.setFont(font);
00271     d->mergeFormatOnWordOrSelection(fmt);
00272     setFocus();
00273     d->activateRichText();
00274 }
00275 
00276 void KRichTextEdit::switchToPlainText()
00277 {
00278     if (d->mMode == Rich) {
00279         d->mMode = Plain;
00280         // TODO: Warn the user about this?
00281         document()->setPlainText(document()->toPlainText());
00282         setAcceptRichText(false);
00283         emit textModeChanged(d->mMode);
00284     }
00285 }
00286 
00287 void KRichTextEdit::enableRichTextMode()
00288 {
00289     d->activateRichText();
00290 }
00291 
00292 KRichTextEdit::Mode KRichTextEdit::textMode() const
00293 {
00294     return d->mMode;
00295 }
00296 
00297 QString KRichTextEdit::textOrHtml() const
00298 {
00299     if (textMode() == Rich)
00300         return toCleanHtml();
00301     else
00302         return toPlainText();
00303 }
00304 
00305 void KRichTextEdit::setTextOrHtml(const QString &text)
00306 {
00307     // might be rich text
00308     if (Qt::mightBeRichText(text)) {
00309         if (d->mMode == KRichTextEdit::Plain) {
00310             d->activateRichText();
00311         }
00312         setHtml(text);
00313     } else {
00314         setPlainText(text);
00315     }
00316 }
00317 
00318 QString KRichTextEdit::currentLinkText() const
00319 {
00320     QTextCursor cursor = textCursor();
00321     selectLinkText(&cursor);
00322     return cursor.selectedText();
00323 }
00324 
00325 void KRichTextEdit::selectLinkText() const
00326 {
00327     QTextCursor cursor = textCursor();
00328     selectLinkText(&cursor);
00329     d->setTextCursor(cursor);
00330 }
00331 
00332 void KRichTextEdit::selectLinkText(QTextCursor *cursor) const
00333 {
00334     // If the cursor is on a link, select the text of the link.
00335     if (cursor->charFormat().isAnchor()) {
00336         QString aHref = cursor->charFormat().anchorHref();
00337 
00338         // Move cursor to start of link
00339         while (cursor->charFormat().anchorHref() == aHref) {
00340             if (cursor->atStart())
00341                 break;
00342             cursor->setPosition(cursor->position() - 1);
00343         }
00344         if (cursor->charFormat().anchorHref() != aHref)
00345             cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
00346 
00347         // Move selection to the end of the link
00348         while (cursor->charFormat().anchorHref() == aHref) {
00349             if (cursor->atEnd())
00350                 break;
00351             cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
00352         }
00353         if (cursor->charFormat().anchorHref() != aHref)
00354             cursor->setPosition(cursor->position() - 1, QTextCursor::KeepAnchor);
00355     } else if (cursor->hasSelection()) {
00356         // Nothing to to. Using the currently selected text as the link text.
00357     } else {
00358 
00359         // Select current word
00360         cursor->movePosition(QTextCursor::StartOfWord);
00361         cursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00362     }
00363 }
00364 
00365 QString KRichTextEdit::currentLinkUrl() const
00366 {
00367     return textCursor().charFormat().anchorHref();
00368 }
00369 
00370 void KRichTextEdit::updateLink(const QString &linkUrl, const QString &linkText)
00371 {
00372     QTextCursor cursor = textCursor();
00373     cursor.beginEditBlock();
00374     QTextCharFormat format = cursor.charFormat();
00375 
00376     selectLinkText();
00377     if (!cursor.hasSelection()) {
00378         cursor.select(QTextCursor::WordUnderCursor);
00379     }
00380 
00381     if (!linkUrl.isEmpty()) {
00382         format.setAnchor(true);
00383         format.setAnchorHref(linkUrl);
00384     } else {
00385         format = cursor.block().charFormat();
00386         format.setAnchor(false);
00387         format.setAnchorHref(QString());
00388     }
00389 
00390     QString _linkText;
00391 
00392     int lowPos = qMin(cursor.selectionStart(), cursor.selectionEnd());
00393     if (!linkText.isEmpty()) {
00394         _linkText = linkText;
00395     } else {
00396         _linkText = linkUrl;
00397     }
00398     // Can't simply insertHtml("<a href=\"%1\">%2</a>").arg(linkUrl).arg(_linkText);
00399     // That would remove all existing text formatting on the selection (bold etc).
00400     // The href information is stored in the QTextCharFormat, but qt bugs must
00401     // still be worked around below.
00402     cursor.insertText(_linkText, format);
00403 
00404 
00405     // Workaround for qt bug 203510:
00406     // Link formatting does not get applied immediately. Removing and reinserting
00407     // the marked up html does format the text correctly.
00408     // -- Stephen Kelly, 15th March 2008
00409     if (!linkUrl.isEmpty()) {
00410         cursor.setPosition(lowPos);
00411         cursor.setPosition(lowPos + _linkText.length(), QTextCursor::KeepAnchor);
00412 
00413         if (!cursor.currentList()) {
00414             cursor.insertHtml(cursor.selection().toHtml());
00415         } else {
00416             // Workaround for qt bug 215576:
00417             // If the cursor is currently on a list, inserting html will create a new block.
00418             // This seems to be because toHtml() does not create a <!-- StartFragment --> tag in
00419             // this case and text style information is stored in the list item rather than a span tag.
00420             // -- Stephen Kelly, 8th June 2008
00421 
00422             QString selectionHtml = cursor.selection().toHtml();
00423             QString style = selectionHtml.split("<li style=\"").takeAt(1).split("\"").first();
00424             QString linkTag = "<a" + selectionHtml.split("<a").takeAt(1).split('>').first() + '>'
00425                 + "<span style=\"" + style + "\">" + _linkText + "</span></a>";
00426             cursor.insertHtml(linkTag);
00427         }
00428 
00429         // Insert a space after the link if at the end of the block so that
00430         // typing some text after the link does not carry link formatting
00431         if (cursor.position() == cursor.block().position() + cursor.block().length() - 1) {
00432             cursor.setCharFormat(cursor.block().charFormat());
00433             cursor.insertText(QString(' '));
00434         }
00435 
00436         d->activateRichText();
00437     } else {
00438         // Remove link formatting. This is a workaround for the same qt bug (203510).
00439         // Just remove all formatting from the link text.
00440         QTextCharFormat charFormat;
00441         cursor.setCharFormat(charFormat);
00442     }
00443 
00444     cursor.endEditBlock();
00445 }
00446 
00447 void KRichTextEdit::keyPressEvent(QKeyEvent *event)
00448 {
00449     bool handled = false;
00450     if (textCursor().currentList()) {
00451         // handled is False if the key press event was not handled or not completely
00452         // handled by the Helper class.
00453         handled = d->nestedListHelper->handleBeforeKeyPressEvent(event);
00454     }
00455 
00456     if (!handled) {
00457         KTextEdit::keyPressEvent(event);
00458     }
00459 
00460     if (textCursor().currentList()) {
00461         d->nestedListHelper->handleAfterKeyPressEvent(event);
00462     }
00463     emit cursorPositionChanged();
00464 }
00465 
00466 // void KRichTextEdit::dropEvent(QDropEvent *event)
00467 // {
00468 //     int dropSize = event->mimeData()->text().size();
00469 //
00470 //     dropEvent( event );
00471 //     QTextCursor cursor = textCursor();
00472 //     int cursorPosition = cursor.position();
00473 //     cursor.setPosition( cursorPosition - dropSize );
00474 //     cursor.setPosition( cursorPosition, QTextCursor::KeepAnchor );
00475 //     setTextCursor( cursor );
00476 //     d->nestedListHelper->handleAfterDropEvent( event );
00477 // }
00478 
00479 
00480 bool KRichTextEdit::canIndentList() const
00481 {
00482     return d->nestedListHelper->canIndent();
00483 }
00484 
00485 bool KRichTextEdit::canDedentList() const
00486 {
00487     return d->nestedListHelper->canDedent();
00488 }
00489 
00490 QString KRichTextEdit::toCleanHtml() const
00491 {
00492     static QString evilline = "<p style=\" margin-top:0px; margin-bottom:0px; "
00493                       "margin-left:0px; margin-right:0px; -qt-block-indent:0; "
00494                       "text-indent:0px; -qt-user-state:0;\">";
00495         
00496     QString result;
00497     QStringList lines = toHtml().split("\n");
00498     foreach(QString tempLine, lines ) {
00499         if (tempLine.startsWith(evilline)) { 
00500             tempLine.remove(evilline);
00501             if (tempLine.endsWith("</p>")) {
00502                 tempLine.remove(QRegExp("</p>$"));
00503                 tempLine.append("<br>\n");
00504             }
00505             result += tempLine;
00506         } else {
00507             result += tempLine;
00508         }
00509     }
00510     return result;
00511 }
00512 
00513 #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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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