• Skip to content
  • Skip to link menu
KDE 4.2 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::setTextSuperScript(bool superscript)
00288 {
00289     QTextCharFormat fmt;
00290     fmt.setVerticalAlignment(superscript ? QTextCharFormat::AlignSuperScript : QTextCharFormat::AlignNormal);
00291     d->mergeFormatOnWordOrSelection(fmt);
00292     setFocus();
00293     d->activateRichText();
00294 }
00295 
00296 void KRichTextEdit::setTextSubScript(bool subscript)
00297 {
00298     QTextCharFormat fmt;
00299     fmt.setVerticalAlignment(subscript ? QTextCharFormat::AlignSubScript : QTextCharFormat::AlignNormal);
00300     d->mergeFormatOnWordOrSelection(fmt);
00301     setFocus();
00302     d->activateRichText();
00303 }
00304 
00305 void KRichTextEdit::enableRichTextMode()
00306 {
00307     d->activateRichText();
00308 }
00309 
00310 KRichTextEdit::Mode KRichTextEdit::textMode() const
00311 {
00312     return d->mMode;
00313 }
00314 
00315 QString KRichTextEdit::textOrHtml() const
00316 {
00317     if (textMode() == Rich)
00318         return toCleanHtml();
00319     else
00320         return toPlainText();
00321 }
00322 
00323 void KRichTextEdit::setTextOrHtml(const QString &text)
00324 {
00325     // might be rich text
00326     if (Qt::mightBeRichText(text)) {
00327         if (d->mMode == KRichTextEdit::Plain) {
00328             d->activateRichText();
00329         }
00330         setHtml(text);
00331     } else {
00332         setPlainText(text);
00333     }
00334 }
00335 
00336 QString KRichTextEdit::currentLinkText() const
00337 {
00338     QTextCursor cursor = textCursor();
00339     selectLinkText(&cursor);
00340     return cursor.selectedText();
00341 }
00342 
00343 void KRichTextEdit::selectLinkText() const
00344 {
00345     QTextCursor cursor = textCursor();
00346     selectLinkText(&cursor);
00347     d->setTextCursor(cursor);
00348 }
00349 
00350 void KRichTextEdit::selectLinkText(QTextCursor *cursor) const
00351 {
00352     // If the cursor is on a link, select the text of the link.
00353     if (cursor->charFormat().isAnchor()) {
00354         QString aHref = cursor->charFormat().anchorHref();
00355 
00356         // Move cursor to start of link
00357         while (cursor->charFormat().anchorHref() == aHref) {
00358             if (cursor->atStart())
00359                 break;
00360             cursor->setPosition(cursor->position() - 1);
00361         }
00362         if (cursor->charFormat().anchorHref() != aHref)
00363             cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
00364 
00365         // Move selection to the end of the link
00366         while (cursor->charFormat().anchorHref() == aHref) {
00367             if (cursor->atEnd())
00368                 break;
00369             cursor->setPosition(cursor->position() + 1, QTextCursor::KeepAnchor);
00370         }
00371         if (cursor->charFormat().anchorHref() != aHref)
00372             cursor->setPosition(cursor->position() - 1, QTextCursor::KeepAnchor);
00373     } else if (cursor->hasSelection()) {
00374         // Nothing to to. Using the currently selected text as the link text.
00375     } else {
00376 
00377         // Select current word
00378         cursor->movePosition(QTextCursor::StartOfWord);
00379         cursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00380     }
00381 }
00382 
00383 QString KRichTextEdit::currentLinkUrl() const
00384 {
00385     return textCursor().charFormat().anchorHref();
00386 }
00387 
00388 void KRichTextEdit::updateLink(const QString &linkUrl, const QString &linkText)
00389 {
00390     QTextCursor cursor = textCursor();
00391     cursor.beginEditBlock();
00392     QTextCharFormat format = cursor.charFormat();
00393 
00394     selectLinkText();
00395     if (!cursor.hasSelection()) {
00396         cursor.select(QTextCursor::WordUnderCursor);
00397     }
00398 
00399     if (!linkUrl.isEmpty()) {
00400         format.setAnchor(true);
00401         format.setAnchorHref(linkUrl);
00402     } else {
00403         format = cursor.block().charFormat();
00404         format.setAnchor(false);
00405         format.setAnchorHref(QString());
00406     }
00407 
00408     QString _linkText;
00409 
00410     int lowPos = qMin(cursor.selectionStart(), cursor.selectionEnd());
00411     if (!linkText.isEmpty()) {
00412         _linkText = linkText;
00413     } else {
00414         _linkText = linkUrl;
00415     }
00416     // Can't simply insertHtml("<a href=\"%1\">%2</a>").arg(linkUrl).arg(_linkText);
00417     // That would remove all existing text formatting on the selection (bold etc).
00418     // The href information is stored in the QTextCharFormat, but qt bugs must
00419     // still be worked around below.
00420     cursor.insertText(_linkText, format);
00421 
00422 
00423     // Workaround for qt bug 203510:
00424     // Link formatting does not get applied immediately. Removing and reinserting
00425     // the marked up html does format the text correctly.
00426     // -- Stephen Kelly, 15th March 2008
00427     if (!linkUrl.isEmpty()) {
00428         cursor.setPosition(lowPos);
00429         cursor.setPosition(lowPos + _linkText.length(), QTextCursor::KeepAnchor);
00430 
00431         if (!cursor.currentList()) {
00432             cursor.insertHtml(cursor.selection().toHtml());
00433         } else {
00434             // Workaround for qt bug 215576:
00435             // If the cursor is currently on a list, inserting html will create a new block.
00436             // This seems to be because toHtml() does not create a <!-- StartFragment --> tag in
00437             // this case and text style information is stored in the list item rather than a span tag.
00438             // -- Stephen Kelly, 8th June 2008
00439 
00440             QString selectionHtml = cursor.selection().toHtml();
00441             QString style = selectionHtml.split("<li style=\"").takeAt(1).split('\"').first();
00442             QString linkTag = "<a" + selectionHtml.split("<a").takeAt(1).split('>').first() + '>'
00443                 + "<span style=\"" + style + "\">" + _linkText + "</span></a>";
00444             cursor.insertHtml(linkTag);
00445         }
00446 
00447         // Insert a space after the link if at the end of the block so that
00448         // typing some text after the link does not carry link formatting
00449         if (cursor.position() == cursor.block().position() + cursor.block().length() - 1) {
00450             cursor.setCharFormat(cursor.block().charFormat());
00451             cursor.insertText(QString(' '));
00452         }
00453 
00454         d->activateRichText();
00455     } else {
00456         // Remove link formatting. This is a workaround for the same qt bug (203510).
00457         // Just remove all formatting from the link text.
00458         QTextCharFormat charFormat;
00459         cursor.setCharFormat(charFormat);
00460     }
00461 
00462     cursor.endEditBlock();
00463 }
00464 
00465 void KRichTextEdit::keyPressEvent(QKeyEvent *event)
00466 {
00467     bool handled = false;
00468     if (textCursor().currentList()) {
00469         // handled is False if the key press event was not handled or not completely
00470         // handled by the Helper class.
00471         handled = d->nestedListHelper->handleBeforeKeyPressEvent(event);
00472     }
00473 
00474     if (!handled) {
00475         KTextEdit::keyPressEvent(event);
00476     }
00477 
00478     if (textCursor().currentList()) {
00479         d->nestedListHelper->handleAfterKeyPressEvent(event);
00480     }
00481     emit cursorPositionChanged();
00482 }
00483 
00484 // void KRichTextEdit::dropEvent(QDropEvent *event)
00485 // {
00486 //     int dropSize = event->mimeData()->text().size();
00487 //
00488 //     dropEvent( event );
00489 //     QTextCursor cursor = textCursor();
00490 //     int cursorPosition = cursor.position();
00491 //     cursor.setPosition( cursorPosition - dropSize );
00492 //     cursor.setPosition( cursorPosition, QTextCursor::KeepAnchor );
00493 //     setTextCursor( cursor );
00494 //     d->nestedListHelper->handleAfterDropEvent( event );
00495 // }
00496 
00497 
00498 bool KRichTextEdit::canIndentList() const
00499 {
00500     return d->nestedListHelper->canIndent();
00501 }
00502 
00503 bool KRichTextEdit::canDedentList() const
00504 {
00505     return d->nestedListHelper->canDedent();
00506 }
00507 
00508 QString KRichTextEdit::toCleanHtml() const
00509 {
00510     static QString evilline = "<p style=\" margin-top:0px; margin-bottom:0px; "
00511                       "margin-left:0px; margin-right:0px; -qt-block-indent:0; "
00512                       "text-indent:0px; -qt-user-state:0;\">";
00513 
00514     QString result;
00515     const QStringList lines = toHtml().split('\n');
00516     Q_FOREACH (const QString &tempLine, lines) {
00517         if (tempLine.startsWith(evilline)) { 
00518             QString tempLineCopy = tempLine;
00519             tempLineCopy.remove(evilline);
00520             if (tempLineCopy.endsWith("</p>")) {
00521                 tempLineCopy.remove(QRegExp("</p>$"));
00522                 tempLineCopy.append("<br>\n");
00523             }
00524             result += tempLineCopy;
00525         } else {
00526             result += tempLine;
00527         }
00528     }
00529 
00530     // ### HACK to fix bug 86925: A completely empty line is ignored in HTML-mode
00531     int offset = 0;
00532     QRegExp paragraphFinder("<p.*>(.*)</p>");
00533     QRegExp paragraphEnd("</p>");
00534     paragraphFinder.setMinimal(true);
00535 
00536     while (offset != -1) {
00537 
00538         // Find the next paragraph
00539         offset = paragraphFinder.indexIn(result, offset);
00540 
00541         if (offset != -1) {
00542 
00543             // If the text in the paragraph is empty, add a &nbsp there.
00544             if (paragraphFinder.capturedTexts().size() == 2 &&
00545                 paragraphFinder.capturedTexts()[1].isEmpty()) {
00546                 int end = paragraphEnd.indexIn(result, offset);
00547                 Q_ASSERT(end != -1 && end > offset);
00548                 result.replace(end, paragraphEnd.pattern().length(), "<br></p>");
00549             }
00550 
00551             // Avoid finding the same match again
00552             offset++;
00553         }
00554     }
00555 
00556     return result;
00557 }
00558 
00559 #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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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