src/gui/text/qtextdocumentfragment.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qtextdocumentfragment.h"
       
    43 #include "qtextdocumentfragment_p.h"
       
    44 #include "qtextcursor_p.h"
       
    45 #include "qtextlist.h"
       
    46 #include "private/qunicodetables_p.h"
       
    47 
       
    48 #include <qdebug.h>
       
    49 #include <qtextcodec.h>
       
    50 #include <qbytearray.h>
       
    51 #include <qdatastream.h>
       
    52 #include <qdatetime.h>
       
    53 
       
    54 QT_BEGIN_NAMESPACE
       
    55 
       
    56 QTextCopyHelper::QTextCopyHelper(const QTextCursor &_source, const QTextCursor &_destination, bool forceCharFormat, const QTextCharFormat &fmt)
       
    57 #if defined(Q_CC_DIAB) // compiler bug
       
    58     : formatCollection(*_destination.d->priv->formatCollection()), originalText((const QString)_source.d->priv->buffer())
       
    59 #else
       
    60     : formatCollection(*_destination.d->priv->formatCollection()), originalText(_source.d->priv->buffer())
       
    61 #endif
       
    62 {
       
    63     src = _source.d->priv;
       
    64     dst = _destination.d->priv;
       
    65     insertPos = _destination.position();
       
    66     this->forceCharFormat = forceCharFormat;
       
    67     primaryCharFormatIndex = convertFormatIndex(fmt);
       
    68     cursor = _source;
       
    69 }
       
    70 
       
    71 int QTextCopyHelper::convertFormatIndex(const QTextFormat &oldFormat, int objectIndexToSet)
       
    72 {
       
    73     QTextFormat fmt = oldFormat;
       
    74     if (objectIndexToSet != -1) {
       
    75         fmt.setObjectIndex(objectIndexToSet);
       
    76     } else if (fmt.objectIndex() != -1) {
       
    77         int newObjectIndex = objectIndexMap.value(fmt.objectIndex(), -1);
       
    78         if (newObjectIndex == -1) {
       
    79             QTextFormat objFormat = src->formatCollection()->objectFormat(fmt.objectIndex());
       
    80             Q_ASSERT(objFormat.objectIndex() == -1);
       
    81             newObjectIndex = formatCollection.createObjectIndex(objFormat);
       
    82             objectIndexMap.insert(fmt.objectIndex(), newObjectIndex);
       
    83         }
       
    84         fmt.setObjectIndex(newObjectIndex);
       
    85     }
       
    86     int idx = formatCollection.indexForFormat(fmt);
       
    87     Q_ASSERT(formatCollection.format(idx).type() == oldFormat.type());
       
    88     return idx;
       
    89 }
       
    90 
       
    91 int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex)
       
    92 {
       
    93     QTextDocumentPrivate::FragmentIterator fragIt = src->find(pos);
       
    94     const QTextFragmentData * const frag = fragIt.value();
       
    95 
       
    96     Q_ASSERT(objectIndex == -1
       
    97              || (frag->size_array[0] == 1 && src->formatCollection()->format(frag->format).objectIndex() != -1));
       
    98 
       
    99     int charFormatIndex;
       
   100     if (forceCharFormat)
       
   101        charFormatIndex = primaryCharFormatIndex;
       
   102     else
       
   103        charFormatIndex = convertFormatIndex(frag->format, objectIndex);
       
   104 
       
   105     const int inFragmentOffset = qMax(0, pos - fragIt.position());
       
   106     int charsToCopy = qMin(int(frag->size_array[0] - inFragmentOffset), endPos - pos);
       
   107 
       
   108     QTextBlock nextBlock = src->blocksFind(pos + 1);
       
   109 
       
   110     int blockIdx = -2;
       
   111     if (nextBlock.position() == pos + 1) {
       
   112         blockIdx = convertFormatIndex(nextBlock.blockFormat());
       
   113     } else if (pos == 0 && insertPos == 0) {
       
   114         dst->setBlockFormat(dst->blocksBegin(), dst->blocksBegin(), convertFormat(src->blocksBegin().blockFormat()).toBlockFormat());
       
   115         dst->setCharFormat(-1, 1, convertFormat(src->blocksBegin().charFormat()).toCharFormat());
       
   116     }
       
   117 
       
   118     QString txtToInsert(originalText.constData() + frag->stringPosition + inFragmentOffset, charsToCopy);
       
   119     if (txtToInsert.length() == 1
       
   120         && (txtToInsert.at(0) == QChar::ParagraphSeparator
       
   121             || txtToInsert.at(0) == QTextBeginningOfFrame
       
   122             || txtToInsert.at(0) == QTextEndOfFrame
       
   123            )
       
   124        ) {
       
   125         dst->insertBlock(txtToInsert.at(0), insertPos, blockIdx, charFormatIndex);
       
   126         ++insertPos;
       
   127     } else {
       
   128         if (nextBlock.textList()) {
       
   129             QTextBlock dstBlock = dst->blocksFind(insertPos);
       
   130             if (!dstBlock.textList()) {
       
   131                 // insert a new text block with the block and char format from the
       
   132                 // source block to make sure that the following text fragments
       
   133                 // end up in a list as they should
       
   134                 int listBlockFormatIndex = convertFormatIndex(nextBlock.blockFormat());
       
   135                 int listCharFormatIndex = convertFormatIndex(nextBlock.charFormat());
       
   136                 dst->insertBlock(insertPos, listBlockFormatIndex, listCharFormatIndex);
       
   137                 ++insertPos;
       
   138             }
       
   139         }
       
   140         dst->insert(insertPos, txtToInsert, charFormatIndex);
       
   141         const int userState = nextBlock.userState();
       
   142         if (userState != -1)
       
   143             dst->blocksFind(insertPos).setUserState(userState);
       
   144         insertPos += txtToInsert.length();
       
   145     }
       
   146 
       
   147     return charsToCopy;
       
   148 }
       
   149 
       
   150 void QTextCopyHelper::appendFragments(int pos, int endPos)
       
   151 {
       
   152     Q_ASSERT(pos < endPos);
       
   153 
       
   154     while (pos < endPos)
       
   155         pos += appendFragment(pos, endPos);
       
   156 }
       
   157 
       
   158 void QTextCopyHelper::copy()
       
   159 {
       
   160     if (cursor.hasComplexSelection()) {
       
   161         QTextTable *table = cursor.currentTable();
       
   162         int row_start, col_start, num_rows, num_cols;
       
   163         cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
       
   164 
       
   165         QTextTableFormat tableFormat = table->format();
       
   166         tableFormat.setColumns(num_cols);
       
   167         tableFormat.clearColumnWidthConstraints();
       
   168         const int objectIndex = dst->formatCollection()->createObjectIndex(tableFormat);
       
   169 
       
   170         Q_ASSERT(row_start != -1);
       
   171         for (int r = row_start; r < row_start + num_rows; ++r) {
       
   172             for (int c = col_start; c < col_start + num_cols; ++c) {
       
   173                 QTextTableCell cell = table->cellAt(r, c);
       
   174                 const int rspan = cell.rowSpan();
       
   175                 const int cspan = cell.columnSpan();
       
   176                 if (rspan != 1) {
       
   177                     int cr = cell.row();
       
   178                     if (cr != r)
       
   179                         continue;
       
   180                 }
       
   181                 if (cspan != 1) {
       
   182                     int cc = cell.column();
       
   183                     if (cc != c)
       
   184                         continue;
       
   185                 }
       
   186 
       
   187                 // add the QTextBeginningOfFrame
       
   188                 QTextCharFormat cellFormat = cell.format();
       
   189                 if (r + rspan >= row_start + num_rows) {
       
   190                     cellFormat.setTableCellRowSpan(row_start + num_rows - r);
       
   191                 }
       
   192                 if (c + cspan >= col_start + num_cols) {
       
   193                     cellFormat.setTableCellColumnSpan(col_start + num_cols - c);
       
   194                 }
       
   195                 const int charFormatIndex = convertFormatIndex(cellFormat, objectIndex);
       
   196 
       
   197                 int blockIdx = -2;
       
   198                 const int cellPos = cell.firstPosition();
       
   199                 QTextBlock block = src->blocksFind(cellPos);
       
   200                 if (block.position() == cellPos) {
       
   201                     blockIdx = convertFormatIndex(block.blockFormat());
       
   202                 }
       
   203 
       
   204                 dst->insertBlock(QTextBeginningOfFrame, insertPos, blockIdx, charFormatIndex);
       
   205                 ++insertPos;
       
   206 
       
   207                 // nothing to add for empty cells
       
   208                 if (cell.lastPosition() > cellPos) {
       
   209                     // add the contents
       
   210                     appendFragments(cellPos, cell.lastPosition());
       
   211                 }
       
   212             }
       
   213         }
       
   214 
       
   215         // add end of table
       
   216         int end = table->lastPosition();
       
   217         appendFragment(end, end+1, objectIndex);
       
   218     } else {
       
   219         appendFragments(cursor.selectionStart(), cursor.selectionEnd());
       
   220     }
       
   221 }
       
   222 
       
   223 QTextDocumentFragmentPrivate::QTextDocumentFragmentPrivate(const QTextCursor &_cursor)
       
   224     : ref(1), doc(new QTextDocument), importedFromPlainText(false)
       
   225 {
       
   226     doc->setUndoRedoEnabled(false);
       
   227 
       
   228     if (!_cursor.hasSelection())
       
   229         return;
       
   230 
       
   231     doc->docHandle()->beginEditBlock();
       
   232     QTextCursor destCursor(doc);
       
   233     QTextCopyHelper(_cursor, destCursor).copy();
       
   234     doc->docHandle()->endEditBlock();
       
   235 
       
   236     if (_cursor.d)
       
   237         doc->docHandle()->mergeCachedResources(_cursor.d->priv);
       
   238 }
       
   239 
       
   240 void QTextDocumentFragmentPrivate::insert(QTextCursor &_cursor) const
       
   241 {
       
   242     if (_cursor.isNull())
       
   243         return;
       
   244 
       
   245     QTextDocumentPrivate *destPieceTable = _cursor.d->priv;
       
   246     destPieceTable->beginEditBlock();
       
   247 
       
   248     QTextCursor sourceCursor(doc);
       
   249     sourceCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
       
   250     QTextCopyHelper(sourceCursor, _cursor, importedFromPlainText, _cursor.charFormat()).copy();
       
   251 
       
   252     destPieceTable->endEditBlock();
       
   253 }
       
   254 
       
   255 /*!
       
   256     \class QTextDocumentFragment
       
   257     \reentrant
       
   258 
       
   259     \brief The QTextDocumentFragment class represents a piece of formatted text
       
   260     from a QTextDocument.
       
   261 
       
   262     \ingroup richtext-processing
       
   263     \ingroup shared
       
   264 
       
   265     A QTextDocumentFragment is a fragment of rich text, that can be inserted into
       
   266     a QTextDocument. A document fragment can be created from a
       
   267     QTextDocument, from a QTextCursor's selection, or from another
       
   268     document fragment. Document fragments can also be created by the
       
   269     static functions, fromPlainText() and fromHtml().
       
   270 
       
   271     The contents of a document fragment can be obtained as plain text
       
   272     by using the toPlainText() function, or it can be obtained as HTML
       
   273     with toHtml().
       
   274 */
       
   275 
       
   276 
       
   277 /*!
       
   278     Constructs an empty QTextDocumentFragment.
       
   279 
       
   280     \sa isEmpty()
       
   281 */
       
   282 QTextDocumentFragment::QTextDocumentFragment()
       
   283     : d(0)
       
   284 {
       
   285 }
       
   286 
       
   287 /*!
       
   288     Converts the given \a document into a QTextDocumentFragment.
       
   289     Note that the QTextDocumentFragment only stores the document contents, not meta information
       
   290     like the document's title.
       
   291 */
       
   292 QTextDocumentFragment::QTextDocumentFragment(const QTextDocument *document)
       
   293     : d(0)
       
   294 {
       
   295     if (!document)
       
   296         return;
       
   297 
       
   298     QTextCursor cursor(const_cast<QTextDocument *>(document));
       
   299     cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
       
   300     d = new QTextDocumentFragmentPrivate(cursor);
       
   301 }
       
   302 
       
   303 /*!
       
   304     Creates a QTextDocumentFragment from the \a{cursor}'s selection.
       
   305     If the cursor doesn't have a selection, the created fragment is empty.
       
   306 
       
   307     \sa isEmpty() QTextCursor::selection()
       
   308 */
       
   309 QTextDocumentFragment::QTextDocumentFragment(const QTextCursor &cursor)
       
   310     : d(0)
       
   311 {
       
   312     if (!cursor.hasSelection())
       
   313         return;
       
   314 
       
   315     d = new QTextDocumentFragmentPrivate(cursor);
       
   316 }
       
   317 
       
   318 /*!
       
   319     \fn QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &other)
       
   320 
       
   321     Copy constructor. Creates a copy of the \a other fragment.
       
   322 */
       
   323 QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &rhs)
       
   324     : d(rhs.d)
       
   325 {
       
   326     if (d)
       
   327         d->ref.ref();
       
   328 }
       
   329 
       
   330 /*!
       
   331     \fn QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &other)
       
   332 
       
   333     Assigns the \a other fragment to this fragment.
       
   334 */
       
   335 QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &rhs)
       
   336 {
       
   337     if (rhs.d)
       
   338         rhs.d->ref.ref();
       
   339     if (d && !d->ref.deref())
       
   340         delete d;
       
   341     d = rhs.d;
       
   342     return *this;
       
   343 }
       
   344 
       
   345 /*!
       
   346     Destroys the document fragment.
       
   347 */
       
   348 QTextDocumentFragment::~QTextDocumentFragment()
       
   349 {
       
   350     if (d && !d->ref.deref())
       
   351         delete d;
       
   352 }
       
   353 
       
   354 /*!
       
   355     Returns true if the fragment is empty; otherwise returns false.
       
   356 */
       
   357 bool QTextDocumentFragment::isEmpty() const
       
   358 {
       
   359     return !d || !d->doc || d->doc->docHandle()->length() <= 1;
       
   360 }
       
   361 
       
   362 /*!
       
   363     Returns the document fragment's text as plain text (i.e. with no
       
   364     formatting information).
       
   365 
       
   366     \sa toHtml()
       
   367 */
       
   368 QString QTextDocumentFragment::toPlainText() const
       
   369 {
       
   370     if (!d)
       
   371         return QString();
       
   372 
       
   373     return d->doc->toPlainText();
       
   374 }
       
   375 
       
   376 // #### Qt 5: merge with other overload
       
   377 /*!
       
   378     \overload
       
   379 */
       
   380 
       
   381 #ifndef QT_NO_TEXTHTMLPARSER
       
   382 
       
   383 QString QTextDocumentFragment::toHtml() const
       
   384 {
       
   385     return toHtml(QByteArray());
       
   386 }
       
   387 
       
   388 /*!
       
   389     \since 4.2
       
   390 
       
   391     Returns the contents of the document fragment as HTML,
       
   392     using the specified \a encoding (e.g., "UTF-8", "ISO 8859-1").
       
   393 
       
   394     \sa toPlainText(), QTextDocument::toHtml(), QTextCodec
       
   395 */
       
   396 QString QTextDocumentFragment::toHtml(const QByteArray &encoding) const
       
   397 {
       
   398     if (!d)
       
   399         return QString();
       
   400 
       
   401     return QTextHtmlExporter(d->doc).toHtml(encoding, QTextHtmlExporter::ExportFragment);
       
   402 }
       
   403 
       
   404 #endif // QT_NO_TEXTHTMLPARSER
       
   405 
       
   406 /*!
       
   407     Returns a document fragment that contains the given \a plainText.
       
   408 
       
   409     When inserting such a fragment into a QTextDocument the current char format of
       
   410     the QTextCursor used for insertion is used as format for the text.
       
   411 */
       
   412 QTextDocumentFragment QTextDocumentFragment::fromPlainText(const QString &plainText)
       
   413 {
       
   414     QTextDocumentFragment res;
       
   415 
       
   416     res.d = new QTextDocumentFragmentPrivate;
       
   417     res.d->importedFromPlainText = true;
       
   418     QTextCursor cursor(res.d->doc);
       
   419     cursor.insertText(plainText);
       
   420     return res;
       
   421 }
       
   422 
       
   423 static QTextListFormat::Style nextListStyle(QTextListFormat::Style style)
       
   424 {
       
   425     if (style == QTextListFormat::ListDisc)
       
   426         return QTextListFormat::ListCircle;
       
   427     else if (style == QTextListFormat::ListCircle)
       
   428         return QTextListFormat::ListSquare;
       
   429     return style;
       
   430 }
       
   431 
       
   432 #ifndef QT_NO_TEXTHTMLPARSER
       
   433 
       
   434 QTextHtmlImporter::QTextHtmlImporter(QTextDocument *_doc, const QString &_html, ImportMode mode, const QTextDocument *resourceProvider)
       
   435     : indent(0), compressNextWhitespace(PreserveWhiteSpace), doc(_doc), importMode(mode)
       
   436 {
       
   437     cursor = QTextCursor(doc);
       
   438     wsm = QTextHtmlParserNode::WhiteSpaceNormal;
       
   439 
       
   440     QString html = _html;
       
   441     const int startFragmentPos = html.indexOf(QLatin1String("<!--StartFragment-->"));
       
   442     if (startFragmentPos != -1) {
       
   443         QString qt3RichTextHeader(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
       
   444 
       
   445         // Hack for Qt3
       
   446         const bool hasQtRichtextMetaTag = html.contains(qt3RichTextHeader);
       
   447 
       
   448         const int endFragmentPos = html.indexOf(QLatin1String("<!--EndFragment-->"));
       
   449         if (startFragmentPos < endFragmentPos)
       
   450             html = html.mid(startFragmentPos, endFragmentPos - startFragmentPos);
       
   451         else
       
   452             html = html.mid(startFragmentPos);
       
   453 
       
   454         if (hasQtRichtextMetaTag)
       
   455             html.prepend(qt3RichTextHeader);
       
   456     }
       
   457 
       
   458     parse(html, resourceProvider ? resourceProvider : doc);
       
   459 //    dumpHtml();
       
   460 }
       
   461 
       
   462 void QTextHtmlImporter::import()
       
   463 {
       
   464     cursor.beginEditBlock();
       
   465     hasBlock = true;
       
   466     forceBlockMerging = false;
       
   467     compressNextWhitespace = RemoveWhiteSpace;
       
   468     blockTagClosed = false;
       
   469     for (currentNodeIdx = 0; currentNodeIdx < count(); ++currentNodeIdx) {
       
   470         currentNode = &at(currentNodeIdx);
       
   471         wsm = textEditMode ? QTextHtmlParserNode::WhiteSpacePreWrap : currentNode->wsm;
       
   472 
       
   473         /*
       
   474          * process each node in three stages:
       
   475          * 1) check if the hierarchy changed and we therefore passed the
       
   476          *    equivalent of a closing tag -> we may need to finish off
       
   477          *    some structures like tables
       
   478          *
       
   479          * 2) check if the current node is a special node like a
       
   480          *    <table>, <ul> or <img> tag that requires special processing
       
   481          *
       
   482          * 3) if the node should result in a QTextBlock create one and
       
   483          *    finally insert text that may be attached to the node
       
   484          */
       
   485 
       
   486         /* emit 'closing' table blocks or adjust current indent level
       
   487          * if we
       
   488          *  1) are beyond the first node
       
   489          *  2) the current node not being a child of the previous node
       
   490          *      means there was a tag closing in the input html
       
   491          */
       
   492         if (currentNodeIdx > 0 && (currentNode->parent != currentNodeIdx - 1)) {
       
   493             blockTagClosed = closeTag();
       
   494             // visually collapse subsequent block tags, but if the element after the closed block tag
       
   495             // is for example an inline element (!isBlock) we have to make sure we start a new paragraph by setting
       
   496             // hasBlock to false.
       
   497             if (blockTagClosed
       
   498                 && !currentNode->isBlock()
       
   499                 && currentNode->id != Html_unknown)
       
   500             {
       
   501                 hasBlock = false;
       
   502             } else if (hasBlock) {
       
   503                 // when collapsing subsequent block tags we need to clear the block format
       
   504                 QTextBlockFormat blockFormat = currentNode->blockFormat;
       
   505                 blockFormat.setIndent(indent);
       
   506 
       
   507                 QTextBlockFormat oldFormat = cursor.blockFormat();
       
   508                 if (oldFormat.hasProperty(QTextFormat::PageBreakPolicy)) {
       
   509                     QTextFormat::PageBreakFlags pageBreak = oldFormat.pageBreakPolicy();
       
   510                     if (pageBreak == QTextFormat::PageBreak_AlwaysAfter)
       
   511                         /* We remove an empty paragrah that requested a page break after.
       
   512                            moving that request to the next paragraph means we also need to make
       
   513                             that a pagebreak before to keep the same visual appearance.
       
   514                         */
       
   515                         pageBreak = QTextFormat::PageBreak_AlwaysBefore;
       
   516                     blockFormat.setPageBreakPolicy(pageBreak);
       
   517                 }
       
   518 
       
   519                 cursor.setBlockFormat(blockFormat);
       
   520             }
       
   521         }
       
   522 
       
   523         if (currentNode->displayMode == QTextHtmlElement::DisplayNone) {
       
   524             if (currentNode->id == Html_title)
       
   525                 doc->setMetaInformation(QTextDocument::DocumentTitle, currentNode->text);
       
   526             // ignore explicitly 'invisible' elements
       
   527             continue;
       
   528         }
       
   529 
       
   530         if (processSpecialNodes() == ContinueWithNextNode)
       
   531             continue;
       
   532 
       
   533         // make sure there's a block for 'Blah' after <ul><li>foo</ul>Blah
       
   534         if (blockTagClosed
       
   535             && !hasBlock
       
   536             && !currentNode->isBlock()
       
   537             && !currentNode->text.isEmpty() && !currentNode->hasOnlyWhitespace()
       
   538             && currentNode->displayMode == QTextHtmlElement::DisplayInline) {
       
   539 
       
   540             QTextBlockFormat block = currentNode->blockFormat;
       
   541             block.setIndent(indent);
       
   542 
       
   543             appendBlock(block, currentNode->charFormat);
       
   544 
       
   545             hasBlock = true;
       
   546         }
       
   547 
       
   548         if (currentNode->isBlock()) {
       
   549             if (processBlockNode() == ContinueWithNextNode)
       
   550                 continue;
       
   551         }
       
   552 
       
   553         if (currentNode->charFormat.isAnchor() && !currentNode->charFormat.anchorName().isEmpty()) {
       
   554             namedAnchors.append(currentNode->charFormat.anchorName());
       
   555         }
       
   556 
       
   557         if (appendNodeText())
       
   558             hasBlock = false; // if we actually appended text then we don't
       
   559                               // have an empty block anymore
       
   560     }
       
   561 
       
   562     cursor.endEditBlock();
       
   563 }
       
   564 
       
   565 bool QTextHtmlImporter::appendNodeText()
       
   566 {
       
   567     const int initialCursorPosition = cursor.position();
       
   568     QTextCharFormat format = currentNode->charFormat;
       
   569 
       
   570     if(wsm == QTextHtmlParserNode::WhiteSpacePre || wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
       
   571         compressNextWhitespace = PreserveWhiteSpace;
       
   572 
       
   573     QString text = currentNode->text;
       
   574 
       
   575     QString textToInsert;
       
   576     textToInsert.reserve(text.size());
       
   577 
       
   578     for (int i = 0; i < text.length(); ++i) {
       
   579         QChar ch = text.at(i);
       
   580 
       
   581         if (ch.isSpace()
       
   582             && ch != QChar::Nbsp
       
   583             && ch != QChar::ParagraphSeparator) {
       
   584 
       
   585             if (compressNextWhitespace == CollapseWhiteSpace)
       
   586                 compressNextWhitespace = RemoveWhiteSpace; // allow this one, and remove the ones coming next.
       
   587             else if(compressNextWhitespace == RemoveWhiteSpace)
       
   588                 continue;
       
   589 
       
   590             if (wsm == QTextHtmlParserNode::WhiteSpacePre
       
   591                 || textEditMode
       
   592                ) {
       
   593                 if (ch == QLatin1Char('\n')) {
       
   594                     if (textEditMode)
       
   595                         continue;
       
   596                 } else if (ch == QLatin1Char('\r')) {
       
   597                     continue;
       
   598                 }
       
   599             } else if (wsm != QTextHtmlParserNode::WhiteSpacePreWrap) {
       
   600                 compressNextWhitespace = RemoveWhiteSpace;
       
   601                 if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap)
       
   602                     ch = QChar::Nbsp;
       
   603                 else
       
   604                     ch = QLatin1Char(' ');
       
   605             }
       
   606         } else {
       
   607             compressNextWhitespace = PreserveWhiteSpace;
       
   608         }
       
   609 
       
   610         if (ch == QLatin1Char('\n')
       
   611             || ch == QChar::ParagraphSeparator) {
       
   612 
       
   613             if (!textToInsert.isEmpty()) {
       
   614                 cursor.insertText(textToInsert, format);
       
   615                 textToInsert.clear();
       
   616             }
       
   617 
       
   618             QTextBlockFormat fmt = cursor.blockFormat();
       
   619 
       
   620             if (fmt.hasProperty(QTextFormat::BlockBottomMargin)) {
       
   621                 QTextBlockFormat tmp = fmt;
       
   622                 tmp.clearProperty(QTextFormat::BlockBottomMargin);
       
   623                 cursor.setBlockFormat(tmp);
       
   624             }
       
   625 
       
   626             fmt.clearProperty(QTextFormat::BlockTopMargin);
       
   627             appendBlock(fmt, cursor.charFormat());
       
   628         } else {
       
   629             if (!namedAnchors.isEmpty()) {
       
   630                 if (!textToInsert.isEmpty()) {
       
   631                     cursor.insertText(textToInsert, format);
       
   632                     textToInsert.clear();
       
   633                 }
       
   634 
       
   635                 format.setAnchor(true);
       
   636                 format.setAnchorNames(namedAnchors);
       
   637                 cursor.insertText(ch, format);
       
   638                 namedAnchors.clear();
       
   639                 format.clearProperty(QTextFormat::IsAnchor);
       
   640                 format.clearProperty(QTextFormat::AnchorName);
       
   641             } else {
       
   642                 textToInsert += ch;
       
   643             }
       
   644         }
       
   645     }
       
   646 
       
   647     if (!textToInsert.isEmpty()) {
       
   648         cursor.insertText(textToInsert, format);
       
   649     }
       
   650 
       
   651     return cursor.position() != initialCursorPosition;
       
   652 }
       
   653 
       
   654 QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
       
   655 {
       
   656     switch (currentNode->id) {
       
   657         case Html_body:
       
   658             if (currentNode->charFormat.background().style() != Qt::NoBrush) {
       
   659                 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
       
   660                 fmt.setBackground(currentNode->charFormat.background());
       
   661                 doc->rootFrame()->setFrameFormat(fmt);
       
   662                 const_cast<QTextHtmlParserNode *>(currentNode)->charFormat.clearProperty(QTextFormat::BackgroundBrush);
       
   663             }
       
   664             compressNextWhitespace = RemoveWhiteSpace;
       
   665             break;
       
   666 
       
   667         case Html_ol:
       
   668         case Html_ul: {
       
   669             QTextListFormat::Style style = currentNode->listStyle;
       
   670 
       
   671             if (currentNode->id == Html_ul && !currentNode->hasOwnListStyle && currentNode->parent) {
       
   672                 const QTextHtmlParserNode *n = &at(currentNode->parent);
       
   673                 while (n) {
       
   674                     if (n->id == Html_ul) {
       
   675                         style = nextListStyle(currentNode->listStyle);
       
   676                     }
       
   677                     if (n->parent)
       
   678                         n = &at(n->parent);
       
   679                     else
       
   680                         n = 0;
       
   681                 }
       
   682             }
       
   683 
       
   684             QTextListFormat listFmt;
       
   685             listFmt.setStyle(style);
       
   686 
       
   687             ++indent;
       
   688             if (currentNode->hasCssListIndent)
       
   689                 listFmt.setIndent(currentNode->cssListIndent);
       
   690             else
       
   691                 listFmt.setIndent(indent);
       
   692 
       
   693             List l;
       
   694             l.format = listFmt;
       
   695             l.listNode = currentNodeIdx;
       
   696             lists.append(l);
       
   697             compressNextWhitespace = RemoveWhiteSpace;
       
   698 
       
   699             // broken html: <ul>Text here<li>Foo
       
   700             const QString simpl = currentNode->text.simplified();
       
   701             if (simpl.isEmpty() || simpl.at(0).isSpace())
       
   702                 return ContinueWithNextNode;
       
   703             break;
       
   704         }
       
   705 
       
   706         case Html_table: {
       
   707             Table t = scanTable(currentNodeIdx);
       
   708             tables.append(t);
       
   709             hasBlock = false;
       
   710             compressNextWhitespace = RemoveWhiteSpace;
       
   711             return ContinueWithNextNode;
       
   712         }
       
   713 
       
   714         case Html_tr:
       
   715             return ContinueWithNextNode;
       
   716 
       
   717         case Html_img: {
       
   718             QTextImageFormat fmt;
       
   719             fmt.setName(currentNode->imageName);
       
   720 
       
   721             fmt.merge(currentNode->charFormat);
       
   722 
       
   723             if (currentNode->imageWidth != -1)
       
   724                 fmt.setWidth(currentNode->imageWidth);
       
   725             if (currentNode->imageHeight != -1)
       
   726                 fmt.setHeight(currentNode->imageHeight);
       
   727 
       
   728             cursor.insertImage(fmt, QTextFrameFormat::Position(currentNode->cssFloat));
       
   729 
       
   730             cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
       
   731             cursor.mergeCharFormat(currentNode->charFormat);
       
   732             cursor.movePosition(QTextCursor::Right);
       
   733             compressNextWhitespace = CollapseWhiteSpace;
       
   734 
       
   735             hasBlock = false;
       
   736             return ContinueWithNextNode;
       
   737         }
       
   738 
       
   739         case Html_hr: {
       
   740             QTextBlockFormat blockFormat = currentNode->blockFormat;
       
   741             blockFormat.setTopMargin(topMargin(currentNodeIdx));
       
   742             blockFormat.setBottomMargin(bottomMargin(currentNodeIdx));
       
   743             blockFormat.setProperty(QTextFormat::BlockTrailingHorizontalRulerWidth, currentNode->width);
       
   744             if (hasBlock && importMode == ImportToDocument)
       
   745                 cursor.mergeBlockFormat(blockFormat);
       
   746             else
       
   747                 appendBlock(blockFormat);
       
   748             hasBlock = false;
       
   749             compressNextWhitespace = RemoveWhiteSpace;
       
   750             return ContinueWithNextNode;
       
   751         }
       
   752 
       
   753         default: break;
       
   754     }
       
   755     return ContinueWithCurrentNode;
       
   756 }
       
   757 
       
   758 // returns true if a block tag was closed
       
   759 bool QTextHtmlImporter::closeTag()
       
   760 {
       
   761     const QTextHtmlParserNode *closedNode = &at(currentNodeIdx - 1);
       
   762     const int endDepth = depth(currentNodeIdx) - 1;
       
   763     int depth = this->depth(currentNodeIdx - 1);
       
   764     bool blockTagClosed = false;
       
   765 
       
   766     while (depth > endDepth) {
       
   767         Table *t = 0;
       
   768         if (!tables.isEmpty())
       
   769             t = &tables.last();
       
   770 
       
   771         switch (closedNode->id) {
       
   772             case Html_tr:
       
   773                 if (t && !t->isTextFrame) {
       
   774                     ++t->currentRow;
       
   775 
       
   776                     // for broken html with rowspans but missing tr tags
       
   777                     while (!t->currentCell.atEnd() && t->currentCell.row < t->currentRow)
       
   778                         ++t->currentCell;
       
   779                 }
       
   780 
       
   781                 blockTagClosed = true;
       
   782                 break;
       
   783 
       
   784             case Html_table:
       
   785                 if (!t)
       
   786                     break;
       
   787                 indent = t->lastIndent;
       
   788 
       
   789                 tables.resize(tables.size() - 1);
       
   790                 t = 0;
       
   791 
       
   792                 if (tables.isEmpty()) {
       
   793                     cursor = doc->rootFrame()->lastCursorPosition();
       
   794                 } else {
       
   795                     t = &tables.last();
       
   796                     if (t->isTextFrame)
       
   797                         cursor = t->frame->lastCursorPosition();
       
   798                     else if (!t->currentCell.atEnd())
       
   799                         cursor = t->currentCell.cell().lastCursorPosition();
       
   800                 }
       
   801 
       
   802                 // we don't need an extra block after tables, so we don't
       
   803                 // claim to have closed one for the creation of a new one
       
   804                 // in import()
       
   805                 blockTagClosed = false;
       
   806                 compressNextWhitespace = RemoveWhiteSpace;
       
   807                 break;
       
   808 
       
   809             case Html_th:
       
   810             case Html_td:
       
   811                 if (t && !t->isTextFrame)
       
   812                     ++t->currentCell;
       
   813                 blockTagClosed = true;
       
   814                 compressNextWhitespace = RemoveWhiteSpace;
       
   815                 break;
       
   816 
       
   817             case Html_ol:
       
   818             case Html_ul:
       
   819                 if (lists.isEmpty())
       
   820                     break;
       
   821                 lists.resize(lists.size() - 1);
       
   822                 --indent;
       
   823                 blockTagClosed = true;
       
   824                 break;
       
   825 
       
   826             case Html_br:
       
   827                 compressNextWhitespace = RemoveWhiteSpace;
       
   828                 break;
       
   829 
       
   830             case Html_div:
       
   831                 if (closedNode->children.isEmpty())
       
   832                     break;
       
   833                 // fall through
       
   834             default:
       
   835                 if (closedNode->isBlock())
       
   836                     blockTagClosed = true;
       
   837                 break;
       
   838         }
       
   839 
       
   840         closedNode = &at(closedNode->parent);
       
   841         --depth;
       
   842     }
       
   843 
       
   844     return blockTagClosed;
       
   845 }
       
   846 
       
   847 QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
       
   848 {
       
   849     Table table;
       
   850     table.columns = 0;
       
   851 
       
   852     QVector<QTextLength> columnWidths;
       
   853 
       
   854     int tableHeaderRowCount = 0;
       
   855     QVector<int> rowNodes;
       
   856     rowNodes.reserve(at(tableNodeIdx).children.count());
       
   857     foreach (int row, at(tableNodeIdx).children)
       
   858         switch (at(row).id) {
       
   859             case Html_tr:
       
   860                 rowNodes += row;
       
   861                 break;
       
   862             case Html_thead:
       
   863             case Html_tbody:
       
   864             case Html_tfoot:
       
   865                 foreach (int potentialRow, at(row).children)
       
   866                     if (at(potentialRow).id == Html_tr) {
       
   867                         rowNodes += potentialRow;
       
   868                         if (at(row).id == Html_thead)
       
   869                             ++tableHeaderRowCount;
       
   870                     }
       
   871                 break;
       
   872             default: break;
       
   873         }
       
   874 
       
   875     QVector<RowColSpanInfo> rowColSpans;
       
   876     QVector<RowColSpanInfo> rowColSpanForColumn;
       
   877 
       
   878     int effectiveRow = 0;
       
   879     foreach (int row, rowNodes) {
       
   880         int colsInRow = 0;
       
   881 
       
   882         foreach (int cell, at(row).children)
       
   883             if (at(cell).isTableCell()) {
       
   884                 // skip all columns with spans from previous rows
       
   885                 while (colsInRow < rowColSpanForColumn.size()) {
       
   886                     const RowColSpanInfo &spanInfo = rowColSpanForColumn[colsInRow];
       
   887 
       
   888                     if (spanInfo.row + spanInfo.rowSpan > effectiveRow) {
       
   889                         Q_ASSERT(spanInfo.col == colsInRow);
       
   890                         colsInRow += spanInfo.colSpan;
       
   891                     } else
       
   892                         break;
       
   893                 }
       
   894 
       
   895                 const QTextHtmlParserNode &c = at(cell);
       
   896                 const int currentColumn = colsInRow;
       
   897                 colsInRow += c.tableCellColSpan;
       
   898 
       
   899                 RowColSpanInfo spanInfo;
       
   900                 spanInfo.row = effectiveRow;
       
   901                 spanInfo.col = currentColumn;
       
   902                 spanInfo.colSpan = c.tableCellColSpan;
       
   903                 spanInfo.rowSpan = c.tableCellRowSpan;
       
   904                 if (spanInfo.colSpan > 1 || spanInfo.rowSpan > 1)
       
   905                     rowColSpans.append(spanInfo);
       
   906 
       
   907                 columnWidths.resize(qMax(columnWidths.count(), colsInRow));
       
   908                 rowColSpanForColumn.resize(columnWidths.size());
       
   909                 for (int i = currentColumn; i < currentColumn + c.tableCellColSpan; ++i) {
       
   910                     if (columnWidths.at(i).type() == QTextLength::VariableLength) {
       
   911                         QTextLength w = c.width;
       
   912                         if (c.tableCellColSpan > 1 && w.type() != QTextLength::VariableLength)
       
   913                             w = QTextLength(w.type(), w.value(100.) / c.tableCellColSpan);
       
   914                         columnWidths[i] = w;
       
   915                     }
       
   916                     rowColSpanForColumn[i] = spanInfo;
       
   917                 }
       
   918             }
       
   919 
       
   920         table.columns = qMax(table.columns, colsInRow);
       
   921 
       
   922         ++effectiveRow;
       
   923     }
       
   924     table.rows = effectiveRow;
       
   925 
       
   926     table.lastIndent = indent;
       
   927     indent = 0;
       
   928 
       
   929     if (table.rows == 0 || table.columns == 0)
       
   930         return table;
       
   931 
       
   932     QTextFrameFormat fmt;
       
   933     const QTextHtmlParserNode &node = at(tableNodeIdx);
       
   934 
       
   935     if (!node.isTextFrame) {
       
   936         QTextTableFormat tableFmt;
       
   937         tableFmt.setCellSpacing(node.tableCellSpacing);
       
   938         tableFmt.setCellPadding(node.tableCellPadding);
       
   939         if (node.blockFormat.hasProperty(QTextFormat::BlockAlignment))
       
   940             tableFmt.setAlignment(node.blockFormat.alignment());
       
   941         tableFmt.setColumns(table.columns);
       
   942         tableFmt.setColumnWidthConstraints(columnWidths);
       
   943         tableFmt.setHeaderRowCount(tableHeaderRowCount);
       
   944         fmt = tableFmt;
       
   945     }
       
   946 
       
   947     fmt.setTopMargin(topMargin(tableNodeIdx));
       
   948     fmt.setBottomMargin(bottomMargin(tableNodeIdx));
       
   949     fmt.setLeftMargin(leftMargin(tableNodeIdx)
       
   950                       + table.lastIndent * 40 // ##### not a good emulation
       
   951                       );
       
   952     fmt.setRightMargin(rightMargin(tableNodeIdx));
       
   953 
       
   954     // compatibility
       
   955     if (qFuzzyCompare(fmt.leftMargin(), fmt.rightMargin())
       
   956         && qFuzzyCompare(fmt.leftMargin(), fmt.topMargin())
       
   957         && qFuzzyCompare(fmt.leftMargin(), fmt.bottomMargin()))
       
   958         fmt.setProperty(QTextFormat::FrameMargin, fmt.leftMargin());
       
   959 
       
   960     fmt.setBorderStyle(node.borderStyle);
       
   961     fmt.setBorderBrush(node.borderBrush);
       
   962     fmt.setBorder(node.tableBorder);
       
   963     fmt.setWidth(node.width);
       
   964     fmt.setHeight(node.height);
       
   965     if (node.blockFormat.hasProperty(QTextFormat::PageBreakPolicy))
       
   966         fmt.setPageBreakPolicy(node.blockFormat.pageBreakPolicy());
       
   967 
       
   968     if (node.blockFormat.hasProperty(QTextFormat::LayoutDirection))
       
   969         fmt.setLayoutDirection(node.blockFormat.layoutDirection());
       
   970     if (node.charFormat.background().style() != Qt::NoBrush)
       
   971         fmt.setBackground(node.charFormat.background());
       
   972     fmt.setPosition(QTextFrameFormat::Position(node.cssFloat));
       
   973 
       
   974     if (node.isTextFrame) {
       
   975         if (node.isRootFrame) {
       
   976             table.frame = cursor.currentFrame();
       
   977             table.frame->setFrameFormat(fmt);
       
   978         } else
       
   979             table.frame = cursor.insertFrame(fmt);
       
   980 
       
   981         table.isTextFrame = true;
       
   982     } else {
       
   983         const int oldPos = cursor.position();
       
   984         QTextTable *textTable = cursor.insertTable(table.rows, table.columns, fmt.toTableFormat());
       
   985         table.frame = textTable;
       
   986 
       
   987         for (int i = 0; i < rowColSpans.count(); ++i) {
       
   988             const RowColSpanInfo &nfo = rowColSpans.at(i);
       
   989             textTable->mergeCells(nfo.row, nfo.col, nfo.rowSpan, nfo.colSpan);
       
   990         }
       
   991 
       
   992         table.currentCell = TableCellIterator(textTable);
       
   993         cursor.setPosition(oldPos); // restore for caption support which needs to be inserted right before the table
       
   994     }
       
   995     return table;
       
   996 }
       
   997 
       
   998 QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode()
       
   999 {
       
  1000     QTextBlockFormat block;
       
  1001     QTextCharFormat charFmt;
       
  1002     bool modifiedBlockFormat = true;
       
  1003     bool modifiedCharFormat = true;
       
  1004 
       
  1005     if (currentNode->isTableCell() && !tables.isEmpty()) {
       
  1006         Table &t = tables.last();
       
  1007         if (!t.isTextFrame && !t.currentCell.atEnd()) {
       
  1008             QTextTableCell cell = t.currentCell.cell();
       
  1009             if (cell.isValid()) {
       
  1010                 QTextTableCellFormat fmt = cell.format().toTableCellFormat();
       
  1011                 if (topPadding(currentNodeIdx) >= 0)
       
  1012                     fmt.setTopPadding(topPadding(currentNodeIdx));
       
  1013                 if (bottomPadding(currentNodeIdx) >= 0)
       
  1014                     fmt.setBottomPadding(bottomPadding(currentNodeIdx));
       
  1015                 if (leftPadding(currentNodeIdx) >= 0)
       
  1016                     fmt.setLeftPadding(leftPadding(currentNodeIdx));
       
  1017                 if (rightPadding(currentNodeIdx) >= 0)
       
  1018                     fmt.setRightPadding(rightPadding(currentNodeIdx));
       
  1019                 cell.setFormat(fmt);
       
  1020 
       
  1021                 cursor.setPosition(cell.firstPosition());
       
  1022             }
       
  1023         }
       
  1024         hasBlock = true;
       
  1025         compressNextWhitespace = RemoveWhiteSpace;
       
  1026 
       
  1027         if (currentNode->charFormat.background().style() != Qt::NoBrush) {
       
  1028             charFmt.setBackground(currentNode->charFormat.background());
       
  1029             cursor.mergeBlockCharFormat(charFmt);
       
  1030         }
       
  1031     }
       
  1032 
       
  1033     if (hasBlock) {
       
  1034         block = cursor.blockFormat();
       
  1035         charFmt = cursor.blockCharFormat();
       
  1036         modifiedBlockFormat = false;
       
  1037         modifiedCharFormat = false;
       
  1038     }
       
  1039 
       
  1040     // collapse
       
  1041     {
       
  1042         qreal tm = qreal(topMargin(currentNodeIdx));
       
  1043         if (tm > block.topMargin()) {
       
  1044             block.setTopMargin(tm);
       
  1045             modifiedBlockFormat = true;
       
  1046         }
       
  1047     }
       
  1048 
       
  1049     int bottomMargin = this->bottomMargin(currentNodeIdx);
       
  1050 
       
  1051     // for list items we may want to collapse with the bottom margin of the
       
  1052     // list.
       
  1053     const QTextHtmlParserNode *parentNode = currentNode->parent ? &at(currentNode->parent) : 0;
       
  1054     if ((currentNode->id == Html_li || currentNode->id == Html_dt || currentNode->id == Html_dd)
       
  1055         && parentNode
       
  1056         && (parentNode->isListStart() || parentNode->id == Html_dl)
       
  1057         && (parentNode->children.last() == currentNodeIdx)) {
       
  1058         bottomMargin = qMax(bottomMargin, this->bottomMargin(currentNode->parent));
       
  1059     }
       
  1060 
       
  1061     if (block.bottomMargin() != bottomMargin) {
       
  1062         block.setBottomMargin(bottomMargin);
       
  1063         modifiedBlockFormat = true;
       
  1064     }
       
  1065 
       
  1066     {
       
  1067         const qreal lm = leftMargin(currentNodeIdx);
       
  1068         const qreal rm = rightMargin(currentNodeIdx);
       
  1069 
       
  1070         if (block.leftMargin() != lm) {
       
  1071             block.setLeftMargin(lm);
       
  1072             modifiedBlockFormat = true;
       
  1073         }
       
  1074         if (block.rightMargin() != rm) {
       
  1075             block.setRightMargin(rm);
       
  1076             modifiedBlockFormat = true;
       
  1077         }
       
  1078     }
       
  1079 
       
  1080     if (currentNode->id != Html_li
       
  1081         && indent != 0
       
  1082         && (lists.isEmpty()
       
  1083             || !hasBlock
       
  1084             || !lists.last().list
       
  1085             || lists.last().list->itemNumber(cursor.block()) == -1
       
  1086            )
       
  1087        ) {
       
  1088         block.setIndent(indent);
       
  1089         modifiedBlockFormat = true;
       
  1090     }
       
  1091 
       
  1092     if (currentNode->blockFormat.propertyCount() > 0) {
       
  1093         modifiedBlockFormat = true;
       
  1094         block.merge(currentNode->blockFormat);
       
  1095     }
       
  1096 
       
  1097     if (currentNode->charFormat.propertyCount() > 0) {
       
  1098         modifiedCharFormat = true;
       
  1099         charFmt.merge(currentNode->charFormat);
       
  1100     }
       
  1101 
       
  1102     // ####################
       
  1103     //                block.setFloatPosition(node->cssFloat);
       
  1104 
       
  1105     if (wsm == QTextHtmlParserNode::WhiteSpacePre) {
       
  1106         block.setNonBreakableLines(true);
       
  1107         modifiedBlockFormat = true;
       
  1108     }
       
  1109 
       
  1110     if (currentNode->charFormat.background().style() != Qt::NoBrush && !currentNode->isTableCell()) {
       
  1111         block.setBackground(currentNode->charFormat.background());
       
  1112         modifiedBlockFormat = true;
       
  1113     }
       
  1114 
       
  1115     if (hasBlock && (!currentNode->isEmptyParagraph || forceBlockMerging)) {
       
  1116         if (modifiedBlockFormat)
       
  1117             cursor.setBlockFormat(block);
       
  1118         if (modifiedCharFormat)
       
  1119             cursor.setBlockCharFormat(charFmt);
       
  1120     } else {
       
  1121         if (currentNodeIdx == 1 && cursor.position() == 0 && currentNode->isEmptyParagraph) {
       
  1122             cursor.setBlockFormat(block);
       
  1123             cursor.setBlockCharFormat(charFmt);
       
  1124         } else {
       
  1125             appendBlock(block, charFmt);
       
  1126         }
       
  1127     }
       
  1128 
       
  1129     if (currentNode->userState != -1)
       
  1130         cursor.block().setUserState(currentNode->userState);
       
  1131 
       
  1132     if (currentNode->id == Html_li && !lists.isEmpty()) {
       
  1133         List &l = lists.last();
       
  1134         if (l.list) {
       
  1135             l.list->add(cursor.block());
       
  1136         } else {
       
  1137             l.list = cursor.createList(l.format);
       
  1138             const qreal listTopMargin = topMargin(l.listNode);
       
  1139             if (listTopMargin > block.topMargin()) {
       
  1140                 block.setTopMargin(listTopMargin);
       
  1141                 cursor.mergeBlockFormat(block);
       
  1142             }
       
  1143         }
       
  1144         if (hasBlock) {
       
  1145             QTextBlockFormat fmt;
       
  1146             fmt.setIndent(0);
       
  1147             cursor.mergeBlockFormat(fmt);
       
  1148         }
       
  1149     }
       
  1150 
       
  1151     forceBlockMerging = false;
       
  1152     if (currentNode->id == Html_body || currentNode->id == Html_html)
       
  1153         forceBlockMerging = true;
       
  1154 
       
  1155     if (currentNode->isEmptyParagraph) {
       
  1156         hasBlock = false;
       
  1157         return ContinueWithNextNode;
       
  1158     }
       
  1159 
       
  1160     hasBlock = true;
       
  1161     blockTagClosed = false;
       
  1162     return ContinueWithCurrentNode;
       
  1163 }
       
  1164 
       
  1165 void QTextHtmlImporter::appendBlock(const QTextBlockFormat &format, QTextCharFormat charFmt)
       
  1166 {
       
  1167     if (!namedAnchors.isEmpty()) {
       
  1168         charFmt.setAnchor(true);
       
  1169         charFmt.setAnchorNames(namedAnchors);
       
  1170         namedAnchors.clear();
       
  1171     }
       
  1172 
       
  1173     cursor.insertBlock(format, charFmt);
       
  1174 
       
  1175     if (wsm != QTextHtmlParserNode::WhiteSpacePre && wsm != QTextHtmlParserNode::WhiteSpacePreWrap)
       
  1176         compressNextWhitespace = RemoveWhiteSpace;
       
  1177 }
       
  1178 
       
  1179 #endif // QT_NO_TEXTHTMLPARSER
       
  1180 
       
  1181 /*!
       
  1182     \fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text)
       
  1183 
       
  1184     Returns a QTextDocumentFragment based on the arbitrary piece of
       
  1185     HTML in the given \a text. The formatting is preserved as much as
       
  1186     possible; for example, "<b>bold</b>" will become a document
       
  1187     fragment with the text "bold" with a bold character format.
       
  1188 */
       
  1189 
       
  1190 #ifndef QT_NO_TEXTHTMLPARSER
       
  1191 
       
  1192 QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html)
       
  1193 {
       
  1194     return fromHtml(html, 0);
       
  1195 }
       
  1196 
       
  1197 /*!
       
  1198     \fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text, const QTextDocument *resourceProvider)
       
  1199     \since 4.2
       
  1200 
       
  1201     Returns a QTextDocumentFragment based on the arbitrary piece of
       
  1202     HTML in the given \a text. The formatting is preserved as much as
       
  1203     possible; for example, "<b>bold</b>" will become a document
       
  1204     fragment with the text "bold" with a bold character format.
       
  1205 
       
  1206     If the provided HTML contains references to external resources such as imported style sheets, then
       
  1207     they will be loaded through the \a resourceProvider.
       
  1208 */
       
  1209 
       
  1210 QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html, const QTextDocument *resourceProvider)
       
  1211 {
       
  1212     QTextDocumentFragment res;
       
  1213     res.d = new QTextDocumentFragmentPrivate;
       
  1214 
       
  1215     QTextHtmlImporter importer(res.d->doc, html, QTextHtmlImporter::ImportToFragment, resourceProvider);
       
  1216     importer.import();
       
  1217     return res;
       
  1218 }
       
  1219 
       
  1220 QT_END_NAMESPACE
       
  1221 #endif // QT_NO_TEXTHTMLPARSER