int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex) { QTextDocumentPrivate::FragmentIterator fragIt = src->find(pos); const QTextFragmentData * const frag = fragIt.value(); Q_ASSERT(objectIndex == -1 || (frag->size_array[0] == 1 && src->formatCollection()->format(frag->format).objectIndex() != -1)); int charFormatIndex; if (forceCharFormat) charFormatIndex = primaryCharFormatIndex; else charFormatIndex = convertFormatIndex(frag->format, objectIndex); const int inFragmentOffset = qMax(0, pos - fragIt.position()); int charsToCopy = qMin(int(frag->size_array[0] - inFragmentOffset), endPos - pos); QTextBlock nextBlock = src->blocksFind(pos + 1); int blockIdx = -2; if (nextBlock.position() == pos + 1) { blockIdx = convertFormatIndex(nextBlock.blockFormat()); } else if (pos == 0 && insertPos == 0) { dst->setBlockFormat(dst->blocksBegin(), dst->blocksBegin(), convertFormat(src->blocksBegin().blockFormat()).toBlockFormat()); dst->setCharFormat(-1, 1, convertFormat(src->blocksBegin().charFormat()).toCharFormat()); } QString txtToInsert(originalText.constData() + frag->stringPosition + inFragmentOffset, charsToCopy); if (txtToInsert.length() == 1 && (txtToInsert.at(0) == QChar::ParagraphSeparator || txtToInsert.at(0) == QTextBeginningOfFrame || txtToInsert.at(0) == QTextEndOfFrame ) ) { dst->insertBlock(txtToInsert.at(0), insertPos, blockIdx, charFormatIndex); ++insertPos; } else { if (nextBlock.textList()) { QTextBlock dstBlock = dst->blocksFind(insertPos); if (!dstBlock.textList()) { // insert a new text block with the block and char format from the // source block to make sure that the following text fragments // end up in a list as they should int listBlockFormatIndex = convertFormatIndex(nextBlock.blockFormat()); int listCharFormatIndex = convertFormatIndex(nextBlock.charFormat()); dst->insertBlock(insertPos, listBlockFormatIndex, listCharFormatIndex); ++insertPos; } } dst->insert(insertPos, txtToInsert, charFormatIndex); const int userState = nextBlock.userState(); if (userState != -1) dst->blocksFind(insertPos).setUserState(userState); insertPos += txtToInsert.length(); } return charsToCopy; }
int KoList::level(const QTextBlock &block) { if (!block.textList()) return 0; int l = block.blockFormat().intProperty(KParagraphStyle::ListLevel); if (!l) { // not a numbered-paragraph QTextListFormat format = block.textList()->format(); l = format.intProperty(KListStyle::Level); } return l; }
void KoList::updateStoredList(const QTextBlock &block) { if (block.textList()) { int level = block.textList()->format().property(KListStyle::Level).toInt(); QTextList *textList = block.textList(); QTextListFormat format = textList->format(); format.setProperty(KListStyle::ListId, (KListStyle::ListIdType)(textList)); textList->setFormat(format); d->textLists[level-1] = textList; d->textListIds[level-1] = (KListStyle::ListIdType)textList; } }
void visit(QTextBlock &block) const { QTextBlockFormat format = block.blockFormat(); // TODO make the 10 configurable. format.setLeftMargin(qMax(qreal(0.0), format.leftMargin() - 10)); if (block.textList()) { const QTextListFormat listFormat = block.textList()->format(); if (format.leftMargin() < listFormat.doubleProperty(KoListStyle::Margin)) { format.setLeftMargin(listFormat.doubleProperty(KoListStyle::Margin)); } } QTextCursor cursor(block); cursor.setBlockFormat(format); }
ChangeListLevelCommand::ChangeListLevelCommand(const QTextCursor &cursor, ChangeListLevelCommand::CommandType type, int coef, KUndo2Command *parent) : KoTextCommandBase(parent), m_type(type), m_coefficient(coef), m_first(true) { setText(kundo2_i18n("Change List Level")); int selectionStart = qMin(cursor.anchor(), cursor.position()); int selectionEnd = qMax(cursor.anchor(), cursor.position()); QTextBlock block = cursor.block().document()->findBlock(selectionStart); bool oneOf = (selectionStart == selectionEnd); //ensures the block containing the cursor is selected in that case while (block.isValid() && ((block.position() < selectionEnd) || oneOf)) { m_blocks.append(block); if (block.textList()) { m_lists.insert(m_blocks.size() - 1, KoTextDocument(block.document()).list(block.textList())); Q_ASSERT(m_lists.value(m_blocks.size() - 1)); m_levels.insert(m_blocks.size() - 1, effectiveLevel(m_lists.value(m_blocks.size() - 1)->level(block))); } oneOf = false; block = block.next(); } }
void TestDocumentLayout::testInvalidateLists() { initForNewTest("Base\nListItem1\nListItem2"); //KoParagraphStyle style; KoListStyle listStyle; KoListLevelProperties llp; llp.setStyle(KoListStyle::DecimalItem); listStyle.setLevelProperties(llp); //style.setListStyle(&listStyle); QTextBlock block = m_doc->begin().next(); QVERIFY(block.isValid()); listStyle.applyStyle(block); block = block.next(); QVERIFY(block.isValid()); listStyle.applyStyle(block); m_layout->layout(); // check the list items were done (semi) properly block = m_doc->begin().next(); QVERIFY(block.textList()); KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); QVERIFY(data->hasCounterData()); QTextCursor cursor(m_doc); cursor.setPosition(10); // list item1 cursor.insertText("x"); QCOMPARE(data->hasCounterData(), true); // nothing changed cursor.setPosition(22); // list item2 cursor.insertText("x"); QCOMPARE(data->hasCounterData(), true); // nothing changed cursor.deleteChar(); QCOMPARE(data->hasCounterData(), true); // nothing changed cursor.setPosition(25); // end of doc cursor.insertBlock(); block = cursor.block(); QVERIFY(block.textList()); QVERIFY(block.userData() == 0); QCOMPARE(data->hasCounterData(), false); // inserting a new block on this list made the list be invalidated }
void TestDocumentLayout::testBasicList() { initForNewTest("Base\nListItem\nListItem2: The quick brown fox jums over the lazy dog.\nNormal\nNormal"); KoParagraphStyle style; QTextBlock block = m_doc->begin(); style.applyStyle(block); block = block.next(); QVERIFY(block.isValid()); KoListStyle listStyle; KoListLevelProperties level1; level1.setStyle(KoListStyle::Bullet); listStyle.setLevelProperties(level1); style.setListStyle(&listStyle); style.applyStyle(block); // make this a listStyle QVERIFY(block.textList()); QCOMPARE(block.textList()->format().intProperty(QTextListFormat::ListStyle), (int) KoListStyle::Bullet); block = block.next(); QVERIFY(block.isValid()); style.applyStyle(block); // make this a listStyle m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0); block = m_doc->begin().next(); QVERIFY(block.isValid()); blockLayout = block.layout(); // parag 2 KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); qreal counterSpacing = data->counterSpacing(); QVERIFY(counterSpacing > 0.); // 12 is hardcoded to be the width of a discitem (taken from the default font): QCOMPARE(blockLayout->lineAt(0).x(), 12.0 + counterSpacing); block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); // parag 3 QCOMPARE(blockLayout->lineAt(0).x(), 12.0 + counterSpacing); QVERIFY(blockLayout->lineCount() > 1); QCOMPARE(blockLayout->lineAt(1).x(), 12.0 + counterSpacing); // make sure not only the first line is indented block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); // parag 4 QCOMPARE(blockLayout->lineAt(0).x(), 0.0); }
QString TextDocumentSerializer::processBlock(QTextBlock block, QTextFrame::iterator &it) { if(block.textList()) return processList(it); else return processBlockContent(block); }
void visit(QTextBlock &block) const { QTextBlockFormat format = block.blockFormat(); // TODO make the 10 configurable. if (!block.textList()) { format.setLeftMargin(format.leftMargin() + 10); } else { const QTextListFormat listFormat = block.textList()->format(); if (format.leftMargin() == 0) { format.setLeftMargin(listFormat.doubleProperty(KoListStyle::Margin) + 10); } else { format.setLeftMargin(format.leftMargin() + 10); } } QTextCursor cursor(block); cursor.setBlockFormat(format); }
void KoList::remove(const QTextBlock &block) { if (QTextList *textList = block.textList()) { // invalidate the list before we remove the item // (since the list might disappear if the block is the only item) KoListPrivate::invalidateList(block); textList->remove(block); } KoListPrivate::invalidate(block); }
void KTextDocumentLayout::documentChanged(int position, int charsRemoved, int charsAdded) { Q_UNUSED(charsRemoved); if (shapes().count() == 0) // nothing to do. return; /* switch (document()->documentLayout()->property("KoTextRelayoutForPage").toInt()) { case KTextShapeData::NormalState: kDebug() << "KoTextRelayoutForPage in NormalState"; break; case KTextShapeData::LayoutCopyShape: kDebug() << "KoTextRelayoutForPage in LayoutCopyShape"; break; case KTextShapeData::LayoutOrig: kDebug() << "KoTextRelayoutForPage in LayoutOrig, skipping relayout"; break; } */ if (document()->documentLayout()->property("KoTextRelayoutForPage").toInt() == KTextShapeData::LayoutOrig) { // don't refresh if we relayout after a relayout-for-page return; } int from = position; const int to = from + charsAdded; while (from < to) { // find blocks that have been added QTextBlock block = document()->findBlock(from); if (! block.isValid()) break; if (from == block.position() && block.textList()) { KTextBlockData *data = dynamic_cast<KTextBlockData*>(block.userData()); if (data) data->setCounterWidth(-1); // invalidate whole list. } from = block.position() + block.length(); } foreach (KShape *shape, shapes()) { KTextShapeData *data = qobject_cast<KTextShapeData*>(shape->userData()); Q_ASSERT(data); if (data && data->position() <= position && data->endPosition() >= position) { // found our (first) shape to re-layout data->foul(); m_state->interrupted = true; scheduleLayout(); return; } }
QString TextDocumentSerializer::processList(QTextFrame::iterator &it) { QString text; QTextBlock block = it.currentBlock(); while(block.textList()){ text += processBlockContent(block); ++it; block = it.currentBlock(); } --it; return QString("<ul>%1</ul>").arg(text); }
QString TextDocumentSerializer::processBlockContent(QTextBlock block) { QTextBlock::iterator it = block.begin(); QString text; while(!it.atEnd()){ QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) text += processFragment(currentFragment); ++it; } if(block.textList()) return QString("<li>%1</li>").arg(text); else return QString("%1<br>").arg(text); }
void TestDocumentLayout::testMultiLevel() { initForNewTest("ListItem1\n"); KoListStyle listStyle; KoListLevelProperties llp; llp.setStyle(KoListStyle::DecimalItem); llp.setLevel(3); llp.setDisplayLevel(4); // we won't show a .0 at the end so this is truncated to 3 listStyle.setLevelProperties(llp); QTextBlock block = m_doc->begin(); QVERIFY(block.isValid()); listStyle.applyStyle(block); QVERIFY(block.textList()); m_layout->layout(); KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); QVERIFY(data->hasCounterData()); QCOMPARE(data->counterText(), QString("1.1.1")); }
QHash<QTextList *, QString> KoTextWriter::saveListStyles(QTextBlock block, int to) { QHash<KoList *, QString> generatedLists; QHash<QTextList *, QString> listStyles; KoTextDocument document(block.document()); for (;block.isValid() && ((to == -1) || (block.position() < to)); block = block.next()) { QTextList *textList = block.textList(); if (!textList) continue; if (KoList *list = document.list(block)) { if (generatedLists.contains(list)) { if (!listStyles.contains(textList)) listStyles.insert(textList, generatedLists.value(list)); continue; } KoListStyle *listStyle = list->style(); bool automatic = listStyle->styleId() == 0; KoGenStyle style(automatic ? KoGenStyle::StyleListAuto : KoGenStyle::StyleList); listStyle->saveOdf(style); QString generatedName = d->context.mainStyles().lookup(style, listStyle->name(), KoGenStyles::AllowDuplicates); listStyles[textList] = generatedName; generatedLists.insert(list, generatedName); } else { if (listStyles.contains(textList)) continue; KoListLevelProperties llp = KoListLevelProperties::fromTextList(textList); KoGenStyle style(KoGenStyle::StyleListAuto); KoListStyle listStyle; listStyle.setLevelProperties(llp); listStyle.saveOdf(style); QString generatedName = d->context.mainStyles().lookup(style, listStyle.name()); listStyles[textList] = generatedName; } } return listStyles; }
QString TextDocumentSerializer::processFrame(QTextFrame *frame) { QString text; QTextFrame::iterator it = frame->begin(); while(!it.atEnd()){ QTextFrame *childFrame = it.currentFrame(); QTextBlock childBlock = it.currentBlock(); if(childFrame) text += processFrame(childFrame); else if(childBlock.isValid()){ if(childBlock.textList()) text = removeTrailingLineBreaks(text); text += processBlock(childBlock, it); } ++it; } text = removeTrailingLineBreaks(text); return text; }
QString Format::frameToString( QTextFrame *frame ) { QString out; QTextFrame::iterator it; for( it = frame->begin(); it != frame->end(); ++it ) { QTextBlock block = it.currentBlock(); if ( block.isValid() ) { out += "<block"; QTextCursor c( block ); QDateTime dt = TextFormats::lastModified( c ); if ( dt.isValid() ) { out += " lastmodified=\"" + dt.toString( Qt::ISODate ) + "\""; } if ( TextFormats::isTitle( c ) ) { out += " titlestyle=\"title\""; } else if ( TextFormats::isSubTitle( c ) ) { out += " titlestyle=\"subtitle\""; } QTextBlockFormat blockFormat = block.blockFormat(); if ( blockFormat.isValid() ) { QTextList *list = block.textList(); if ( list ) { QTextListFormat f = list->format(); out += " liststyle=\""; switch( f.style() ) { default: case QTextListFormat::ListDisc: out += "disc"; break; case QTextListFormat::ListDecimal: out += "decimal"; break; } out += "\""; out += " listindent=\"" + QString::number( f.indent() ) + "\""; } else { if ( blockFormat.indent() != 0 ) { out += " blockindent=\"" + QString::number( blockFormat.indent() ) + "\""; } } } out += ">\n"; QTextBlock::iterator it2; for( it2 = block.begin(); it2 != block.end(); ++it2 ) { QTextFragment fragment = it2.fragment(); if ( !fragment.isValid() ) continue; QString text = fragment.text(); QString outText; for( int i = 0; i < text.size(); ++i ) { if ( text.at( i ) == 0xfffc ) { outText += "<todo status=\""; QTextImageFormat imageFormat = fragment.charFormat().toImageFormat(); if ( imageFormat.isValid() ) { if ( imageFormat.name().contains( "done" ) ) outText += "done"; else outText += "todo"; } else { dbg() << "NO IMAGE FORMAT" << endl; } outText += "\"/>"; } else { outText += escape( QString( text.at( i ) ) ); } } out += " <fragment"; QTextCharFormat format = fragment.charFormat(); if ( !format.anchorHref().isEmpty() ) { out += " link=\"" + escape( format.anchorHref() ) + "\""; } if ( format.fontWeight() == QFont::Bold ) { out += " bold=\"true\""; } if ( format.fontItalic() ) { out += " italic=\"true\""; } if ( format.hasProperty( QTextFormat::FontPointSize ) && format.fontPointSize() != 10 ) { out += " fontsize=\"" + QString::number( format.fontPointSize() ) + "\""; } if ( outText.trimmed().isEmpty() ) outText.replace( " ", "[FIXME:space]" ); out += ">" + outText + "</fragment>\n"; } out += "</block>"; out += "\n"; } QTextFrame *f = it.currentFrame(); if ( f ) { QTextFrameFormat format = f->frameFormat(); out += "<frame"; if ( format.hasProperty( TextFormats::FrameType ) ) { out += " type="; if ( format.property( TextFormats::FrameType ) == TextFormats::CodeFrame ) { out += "\"code\""; } else { out += "\"undefined\""; } } out += ">\n"; out += frameToString( f ); out += "</frame>\n"; } } return out; }
void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &block) { if (block.textList()) { // its a list-item const int listLevel = block.textList()->format().indent(); if (m_listStack.isEmpty() || m_listStack.top() != block.textList()) { // not the same list we were in. while (m_listStack.count() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) { // we need to close tags m_listStack.pop(); writer.writeEndElement(); // list if (m_listStack.count()) writer.writeEndElement(); // list-item } while (m_listStack.count() < listLevel) { if (m_listStack.count()) writer.writeStartElement(textNS, QString::fromLatin1("list-item")); writer.writeStartElement(textNS, QString::fromLatin1("list")); if (m_listStack.count() == listLevel - 1) { m_listStack.push(block.textList()); writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("L%1") .arg(block.textList()->formatIndex())); } else { m_listStack.push(0); } } } writer.writeStartElement(textNS, QString::fromLatin1("list-item")); } else { while (! m_listStack.isEmpty()) { m_listStack.pop(); writer.writeEndElement(); // list if (m_listStack.count()) writer.writeEndElement(); // list-item } } if (block.length() == 1) { // only a linefeed writer.writeEmptyElement(textNS, QString::fromLatin1("p")); writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("p%1") .arg(block.blockFormatIndex())); if (block.textList()) writer.writeEndElement(); // numbered-paragraph return; } writer.writeStartElement(textNS, QString::fromLatin1("p")); writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("p%1") .arg(block.blockFormatIndex())); for (QTextBlock::Iterator frag= block.begin(); !frag.atEnd(); frag++) { writer.writeCharacters(QString()); // Trick to make sure that the span gets no linefeed in front of it. writer.writeStartElement(textNS, QString::fromLatin1("span")); QString fragmentText = frag.fragment().text(); if (fragmentText.length() == 1 && fragmentText[0] == 0xFFFC) { // its an inline character. writeInlineCharacter(writer, frag.fragment()); writer.writeEndElement(); // span continue; } writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("c%1") .arg(frag.fragment().charFormatIndex())); bool escapeNextSpace = true; int precedingSpaces = 0; int exportedIndex = 0; for (int i=0; i <= fragmentText.count(); ++i) { QChar character = fragmentText[i]; bool isSpace = character.unicode() == ' '; // find more than one space. -> <text:s text:c="2" /> if (!isSpace && escapeNextSpace && precedingSpaces > 1) { const bool startParag = exportedIndex == 0 && i == precedingSpaces; if (!startParag) writer.writeCharacters(fragmentText.mid(exportedIndex, i - precedingSpaces + 1 - exportedIndex)); writer.writeEmptyElement(textNS, QString::fromLatin1("s")); const int count = precedingSpaces - (startParag?0:1); if (count > 1) writer.writeAttribute(textNS, QString::fromLatin1("c"), QString::number(count)); precedingSpaces = 0; exportedIndex = i; } if (i < fragmentText.count()) { if (character.unicode() == 0x2028) { // soft-return //if (exportedIndex < i) writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex)); writer.writeEmptyElement(textNS, QString::fromLatin1("line-break")); exportedIndex = i+1; continue; } else if (character.unicode() == '\t') { // Tab //if (exportedIndex < i) writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex)); writer.writeEmptyElement(textNS, QString::fromLatin1("tab")); exportedIndex = i+1; precedingSpaces = 0; } else if (isSpace) { ++precedingSpaces; escapeNextSpace = true; } else if (!isSpace) { precedingSpaces = 0; } } } writer.writeCharacters(fragmentText.mid(exportedIndex)); writer.writeEndElement(); // span } writer.writeCharacters(QString()); // Trick to make sure that the span gets no linefeed behind it. writer.writeEndElement(); // p if (block.textList()) writer.writeEndElement(); // list-item }
void MainWindow::showList() { QTextCursor cursor = editor->textCursor(); QTextFrame *frame = cursor.currentFrame(); if (!frame) return; QTreeWidget *treeWidget = new QTreeWidget; treeWidget->setColumnCount(1); QStringList headerLabels; headerLabels << tr("Lists"); treeWidget->setHeaderLabels(headerLabels); QTreeWidgetItem *parentItem = 0; QTreeWidgetItem *item; QTreeWidgetItem *lastItem = 0; parentItems.clear(); previousItems.clear(); //! [3] QTextFrame::iterator it; for (it = frame->begin(); !(it.atEnd()); ++it) { QTextBlock block = it.currentBlock(); if (block.isValid()) { QTextList *list = block.textList(); if (list) { int index = list->itemNumber(block); //! [3] if (index == 0) { parentItems.append(parentItem); previousItems.append(lastItem); listStructures.append(list); parentItem = lastItem; lastItem = 0; if (parentItem != 0) item = new QTreeWidgetItem(parentItem, lastItem); else item = new QTreeWidgetItem(treeWidget, lastItem); } else { while (parentItem != 0 && listStructures.last() != list) { listStructures.pop_back(); parentItem = parentItems.takeLast(); lastItem = previousItems.takeLast(); } if (parentItem != 0) item = new QTreeWidgetItem(parentItem, lastItem); else item = new QTreeWidgetItem(treeWidget, lastItem); } item->setText(0, block.text()); lastItem = item; /* //! [4] processListItem(list, index); //! [4] */ //! [5] } //! [5] //! [6] } //! [6] //! [7] } //! [7] treeWidget->setWindowTitle(tr("List Contents")); treeWidget->show(); }
QByteArray QGithubMarkdown::write(QTextDocument *source) { QStringList output; bool wasInList = false; bool inCodeBlock = false; auto endCodeBlock = [&]() { if (inCodeBlock) { output.append("```\n"); } inCodeBlock = false; }; auto formatForPos = [&](const QTextBlock &block, const int pos) -> QTextCharFormat { for (const auto fmtRange : block.textFormats()) { if (fmtRange.start <= pos && pos <= (fmtRange.start + fmtRange.length)) { return fmtRange.format; } } Q_ASSERT(false); return QTextCharFormat(); }; auto blockToMarkdown = [&](const QTextBlock &block, const int offset = 0) -> QString { QString out; bool inBold = false; bool inItalic = false; QString currentLink; for (int i = offset; i < block.text().size(); ++i) { const QChar c = block.text().at(i); const QTextCharFormat fmt = formatForPos(block, i); if (fmt.fontItalic() != inItalic) { out.insert(out.size() - 1, '_'); inItalic = !inItalic; } if ((fmt.fontWeight() == QFont::Bold) != inBold) { out.insert(out.size() - 1, "**"); inBold = !inBold; } if (fmt.anchorHref().isEmpty() && !currentLink.isNull()) { out.insert(out.size() - 1, "](" + currentLink + ")"); } else if (!fmt.anchorHref().isEmpty() && currentLink.isNull()) { out.insert(out.size() - 1, "["); currentLink = fmt.anchorHref(); } // FIXME images out.append(c); } return out; }; for (QTextBlock block = source->begin(); block != source->end(); block = block.next()) { // heading if (block.charFormat().toolTip() == block.text()) { endCodeBlock(); output.append(QString(sizeMap.key(block.charFormat().fontPointSize()), '#') + " " + block.text() + "\n"); } else { // list if (QTextList *list = block.textList()) { endCodeBlock(); const QString indent = QString((list->format().indent()-1) * 2, ' '); if (list->format().style() == QTextListFormat::ListDisc) { output.append(indent + "* " + blockToMarkdown(block)); } else { output.append(indent + QString::number(list->itemNumber(block) + 1) + ". " + blockToMarkdown(block)); } wasInList = true; } else { if (wasInList) { output.append(""); wasInList = false; } if (block.charFormat().fontFamily() == "Monospace") { if (!inCodeBlock) { inCodeBlock = true; output.insert(output.size() - 1, "```"); } output.append(block.text().remove("\n")); } else { endCodeBlock(); output.append(blockToMarkdown(block) + "\n"); } } } } QString string = output.join("\n"); return string.trimmed().toUtf8(); }
void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QTextBlock &block, const QPointF &position, const QColor &textColor, const QColor &anchorColor, int selectionStart, int selectionEnd) { Q_ASSERT(textDocument); #ifndef QT_NO_IM int preeditLength = block.isValid() ? block.layout()->preeditAreaText().length() : 0; int preeditPosition = block.isValid() ? block.layout()->preeditAreaPosition() : -1; #endif QVarLengthArray<QTextLayout::FormatRange> colorChanges; mergeFormats(block.layout(), &colorChanges); QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft() + position; if (QTextList *textList = block.textList()) { QPointF pos = blockPosition; QTextLayout *layout = block.layout(); if (layout->lineCount() > 0) { QTextLine firstLine = layout->lineAt(0); Q_ASSERT(firstLine.isValid()); setCurrentLine(firstLine); QRectF textRect = firstLine.naturalTextRect(); pos += textRect.topLeft(); if (block.textDirection() == Qt::RightToLeft) pos.rx() += textRect.width(); const QTextCharFormat charFormat = block.charFormat(); QFont font(charFormat.font()); QFontMetricsF fontMetrics(font); QTextListFormat listFormat = textList->format(); QString listItemBullet; switch (listFormat.style()) { case QTextListFormat::ListCircle: listItemBullet = QChar(0x25E6); // White bullet break; case QTextListFormat::ListSquare: listItemBullet = QChar(0x25AA); // Black small square break; case QTextListFormat::ListDecimal: case QTextListFormat::ListLowerAlpha: case QTextListFormat::ListUpperAlpha: case QTextListFormat::ListLowerRoman: case QTextListFormat::ListUpperRoman: listItemBullet = textList->itemText(block); break; default: listItemBullet = QChar(0x2022); // Black bullet break; }; QSizeF size(fontMetrics.width(listItemBullet), fontMetrics.height()); qreal xoff = fontMetrics.width(QLatin1Char(' ')); if (block.textDirection() == Qt::LeftToRight) xoff = -xoff - size.width(); setPosition(pos + QPointF(xoff, 0)); QTextLayout layout; layout.setFont(font); layout.setText(listItemBullet); // Bullet layout.beginLayout(); QTextLine line = layout.createLine(); line.setPosition(QPointF(0, 0)); layout.endLayout(); QList<QGlyphRun> glyphRuns = layout.glyphRuns(); for (int i=0; i<glyphRuns.size(); ++i) addUnselectedGlyphs(glyphRuns.at(i)); } } int textPos = block.position(); QTextBlock::iterator blockIterator = block.begin(); while (!blockIterator.atEnd()) { QTextFragment fragment = blockIterator.fragment(); QString text = fragment.text(); if (text.isEmpty()) continue; QTextCharFormat charFormat = fragment.charFormat(); QFont font(charFormat.font()); QFontMetricsF fontMetrics(font); int fontHeight = fontMetrics.descent() + fontMetrics.ascent(); int valign = charFormat.verticalAlignment(); if (valign == QTextCharFormat::AlignSuperScript) setPosition(QPointF(blockPosition.x(), blockPosition.y() - fontHeight / 2)); else if (valign == QTextCharFormat::AlignSubScript) setPosition(QPointF(blockPosition.x(), blockPosition.y() + fontHeight / 6)); else setPosition(blockPosition); if (text.contains(QChar::ObjectReplacementCharacter)) { QTextFrame *frame = qobject_cast<QTextFrame *>(textDocument->objectForFormat(charFormat)); if (frame && frame->frameFormat().position() == QTextFrameFormat::InFlow) { int blockRelativePosition = textPos - block.position(); QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); if (!currentLine().isValid() || line.lineNumber() != currentLine().lineNumber()) { setCurrentLine(line); } QQuickTextNodeEngine::SelectionState selectionState = (selectionStart < textPos + text.length() && selectionEnd >= textPos) ? QQuickTextNodeEngine::Selected : QQuickTextNodeEngine::Unselected; addTextObject(QPointF(), charFormat, selectionState, textDocument, textPos); } textPos += text.length(); } else { if (charFormat.foreground().style() != Qt::NoBrush) setTextColor(charFormat.foreground().color()); else if (charFormat.isAnchor()) setTextColor(anchorColor); else setTextColor(textColor); int fragmentEnd = textPos + fragment.length(); #ifndef QT_NO_IM if (preeditPosition >= 0 && (preeditPosition + block.position()) >= textPos && (preeditPosition + block.position()) <= fragmentEnd) { fragmentEnd += preeditLength; } #endif if (charFormat.background().style() != Qt::NoBrush) { QTextLayout::FormatRange additionalFormat; additionalFormat.start = textPos - block.position(); additionalFormat.length = fragmentEnd - textPos; additionalFormat.format = charFormat; colorChanges << additionalFormat; } textPos = addText(block, charFormat, textColor, colorChanges, textPos, fragmentEnd, selectionStart, selectionEnd); } ++blockIterator; } #ifndef QT_NO_IM if (preeditLength >= 0 && textPos <= block.position() + preeditPosition) { setPosition(blockPosition); textPos = block.position() + preeditPosition; QTextLine line = block.layout()->lineForTextPosition(preeditPosition); if (!currentLine().isValid() || line.lineNumber() != currentLine().lineNumber()) { setCurrentLine(line); } textPos = addText(block, block.charFormat(), textColor, colorChanges, textPos, textPos + preeditLength, selectionStart, selectionEnd); } #endif setCurrentLine(QTextLine()); // Reset current line because the text layout changed m_hasContents = true; }
void KoTextWriter::write(QTextDocument *document, int from, int to) { d->styleManager = KoTextDocument(document).styleManager(); d->layout = dynamic_cast<KoTextDocumentLayout*>(document->documentLayout()); d->changeTracker = KoTextDocument(document).changeTracker(); // Q_ASSERT(d->changeTracker); Q_ASSERT(d->layout); Q_ASSERT(d->layout->inlineTextObjectManager()); QTextBlock block = document->findBlock(from); KoTextDocument textDocument(document); QHash<QTextList *, QString> listStyles = saveListStyles(block, to); QList<QTextList*> textLists; // Store the current lists being stored. KoList *currentList = 0; while (block.isValid() && ((to == -1) || (block.position() <= to))) { QTextBlockFormat blockFormat = block.blockFormat(); QTextList *textList = block.textList(); int headingLevel = 0, numberedParagraphLevel = 0; if (textList) { headingLevel = blockFormat.intProperty(KoParagraphStyle::OutlineLevel); numberedParagraphLevel = blockFormat.intProperty(KoParagraphStyle::ListLevel); } if (textList && !headingLevel && !numberedParagraphLevel) { if (!textLists.contains(textList)) { KoList *list = textDocument.list(block); if (currentList != list) { while (!textLists.isEmpty()) { textLists.removeLast(); d->writer->endElement(); // </text:list> if (!textLists.isEmpty()) { d->writer->endElement(); // </text:list-element> } } currentList = list; } else if (!textLists.isEmpty()) // sublists should be written within a list-item d->writer->startElement("text:list-item", false); d->writer->startElement("text:list", false); d->writer->addAttribute("text:style-name", listStyles[textList]); if (textList->format().hasProperty(KoListStyle::ContinueNumbering)) d->writer->addAttribute("text:continue-numbering", textList->format().boolProperty(KoListStyle::ContinueNumbering) ? "true" : "false"); textLists.append(textList); } else if (textList != textLists.last()) { while ((!textLists.isEmpty()) && (textList != textLists.last())) { textLists.removeLast(); d->writer->endElement(); // </text:list> d->writer->endElement(); // </text:list-element> } } const bool listHeader = blockFormat.boolProperty(KoParagraphStyle::IsListHeader) || blockFormat.boolProperty(KoParagraphStyle::UnnumberedListItem); d->writer->startElement(listHeader ? "text:list-header" : "text:list-item", false); if (KoListStyle::isNumberingStyle(textList->format().style())) { if (KoTextBlockData *blockData = dynamic_cast<KoTextBlockData *>(block.userData())) { d->writer->startElement("text:number", false); d->writer->addTextSpan(blockData->counterText()); d->writer->endElement(); } } } else { // Close any remaining list... while (!textLists.isEmpty()) { textLists.removeLast(); d->writer->endElement(); // </text:list> if (!textLists.isEmpty()) { d->writer->endElement(); // </text:list-element> } } if (textList && numberedParagraphLevel) { d->writer->startElement("text:numbered-paragraph", false); d->writer->addAttribute("text:level", numberedParagraphLevel); d->writer->addAttribute("text:style-name", listStyles.value(textList)); } } saveParagraph(block, from, to); if (!textLists.isEmpty() || numberedParagraphLevel) { // we are generating a text:list-item. Look forward and generate unnumbered list items. while (true) { QTextBlock nextBlock = block.next(); if (!nextBlock.isValid() || !((to == -1) || (nextBlock.position() < to))) break; if (!nextBlock.textList() || !nextBlock.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) break; block = nextBlock; saveParagraph(block, from, to); } } // We must check if we need to close a previously-opened text:list node. if ((block.textList() && !headingLevel) || numberedParagraphLevel) d->writer->endElement(); block = block.next(); } // while // Close any remaining lists while (!textLists.isEmpty()) { textLists.removeLast(); d->writer->endElement(); // </text:list> if (!textLists.isEmpty()) { d->writer->endElement(); // </text:list-element> } } }
void TestListStyle::testListStyle() { KListStyle ls; KListLevelProperties llp = ls.levelProperties(2); QCOMPARE(llp.level(), 2); llp.setStyle(KListStyle::AlphaLowerItem); KListLevelProperties llp2 = ls.levelProperties(2); QVERIFY(llp2.style() != llp.style()); ls.setLevelProperties(llp); QCOMPARE(llp.level(), 2); QCOMPARE(llp.style(), KListStyle::AlphaLowerItem); llp = ls.levelProperties(2); QCOMPARE(llp.level(), 2); QCOMPARE(llp.style(), KListStyle::AlphaLowerItem); QTextDocument doc; KTextDocument kodoc(&doc); kodoc.setStyleManager(new KStyleManager); QTextCursor cursor(&doc); cursor.insertText("foo\nbar\nBaz\n"); QTextBlock block = doc.begin(); ls.applyStyle(block, 2); QVERIFY(block.textList()); QTextList *textList = block.textList(); QTextListFormat format = textList->format(); QCOMPARE(format.intProperty(QTextListFormat::ListStyle), (int)(KListStyle::AlphaLowerItem)); block = block.next(); QVERIFY(block.isValid()); ls.applyStyle(block, 2); QVERIFY(block.textList()); QCOMPARE(block.textList(), textList); ls.applyStyle(block, 10); // should set the properties of the only one that is set, level 1 QVERIFY(block.textList()); textList = block.textList(); format = textList->format(); QCOMPARE(format.intProperty(QTextListFormat::ListStyle), (int)(KListStyle::AlphaLowerItem)); // getting a properties without setting it doesn't change the list. KListLevelProperties l4 = ls.levelProperties(4); QCOMPARE(l4.level(), 4); QCOMPARE(l4.displayLevel(), 0); // default l4.setDisplayLevel(3); QCOMPARE(l4.displayLevel(), 3); QCOMPARE(ls.hasLevelProperties(4), false); KListLevelProperties anotherL4 = ls.levelProperties(4); QCOMPARE(anotherL4.level(), 4); QCOMPARE(anotherL4.displayLevel(), 0); // default QCOMPARE(ls.hasLevelProperties(4), false); QCOMPARE(ls.hasLevelProperties(5), false); // new levels are a copy of the existing level. KListLevelProperties l5 = ls.levelProperties(5); QCOMPARE(l5.displayLevel(), 0); QCOMPARE(l5.style(), KListStyle::AlphaLowerItem); QCOMPARE(l5.indent(), 0.); }
void TestDocumentLayout::testNumberedList() { initForNewTest("Base\nListItem1\nListItem2\nListItem3\nListItem4\nListItem5\nListItem6\nListItem6\nListItem7\nListItem8\nListItem9\nListItem10\nListItem11\nListItem12\n"); KoParagraphStyle style; m_styleManager->add(&style); QTextBlock block = m_doc->begin(); style.applyStyle(block); block = block.next(); KoListStyle listStyle; KoListLevelProperties llp; llp.setStyle(KoListStyle::DecimalItem); listStyle.setLevelProperties(llp); style.setListStyle(&listStyle); QTextList *previous = 0; int i; for (i = 1; i <= 9; i++) { QVERIFY(block.isValid()); // qDebug() << "->" << block.text(); style.applyStyle(block); QTextList *textList = block.textList(); QVERIFY(textList); if (previous == 0) { previous = textList; } else { QCOMPARE(textList, previous); } QCOMPARE(textList->format().intProperty(QTextListFormat::ListStyle), (int)(KoListStyle::DecimalItem)); block = block.next(); } m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0); QTextBlock blok = m_doc->begin().next(); qreal indent = blok.layout()->lineAt(0).x(); QVERIFY(indent > 0.0); for (i = 1; i <= 9; ++i) { // qDebug() << "=>" << blok.text(); QTextList *textList = blok.textList(); QVERIFY(textList); QCOMPARE(blok.layout()->lineAt(0).x(), indent); // all the same indent. blok = blok.next(); } // now make number of listitems be more than 10, so we use 2 digits. for (i = 9; i <= 12; ++i) { QVERIFY(block.isValid()); style.applyStyle(block); // qDebug() << "->" << block.text(); block = block.next(); } m_layout->layout(); blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0); blok = m_doc->begin().next(); qreal indent2 = blok.layout()->lineAt(0).x(); QVERIFY(indent2 > indent); // since it takes an extra digit for (i = 2; i <= 12; ++i) { // qDebug() << "=>" << blok.text(); QCOMPARE(blok.layout()->lineAt(0).x(), indent2); // all the same indent. blok = blok.next(); } // now to make sure the text is actually properly set. block = m_doc->begin().next(); i = 1; while (block.isValid() && i < 13) { KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); QCOMPARE(data->counterText(), QString::number(i++)); block = block.next(); } llp.setListItemSuffix("."); llp.setStartValue(4); listStyle.setLevelProperties(llp); QTextCursor cursor(m_doc); cursor.setPosition(10); // listItem1 QTextBlockFormat format = cursor.blockFormat(); format.setProperty(KoParagraphStyle::ListStartValue, 4); cursor.setBlockFormat(format); cursor.setPosition(40); // listItem4 format = cursor.blockFormat(); format.setProperty(KoParagraphStyle::ListStartValue, 12); cursor.setBlockFormat(format); // at this point we start numbering at 4. Have 4, 5, 6, 12, 13, 14, 15 etc m_layout->layout(); // now to make sur the text is actually properly set. block = m_doc->begin().next(); i = 4; while (block.isValid() && i < 22) { if (i == 7) { i = 12; } KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); QCOMPARE(data->counterText(), QString::number(i++)); block = block.next(); } }
void HtmlExporter::emitBlock( const QTextBlock &block ) { // save and later restore, in case we 'change' the default format by // emitting block char format information // NOTE the bottom line is commented, to use default charFormat, which can be set from outside. //QTextCharFormat oldDefaultCharFormat = defaultCharFormat; QString blockTag; bool isBlockQuote = false; const QTextBlockFormat blockFormat = block.blockFormat(); if ( blockFormat.hasProperty( BilboTextFormat::IsBlockQuote ) && blockFormat.boolProperty( BilboTextFormat::IsBlockQuote ) ) { isBlockQuote = true; } QTextList *list = block.textList(); if ( list ) { if ( list->itemNumber( block ) == 0 ) { // first item? emit <ul> or appropriate // qDebug() << "first item" << endl; if ( isBlockQuote ) { html += QLatin1String( "<blockquote>" ); } const QTextListFormat format = list->format(); const int style = format.style(); switch ( style ) { case QTextListFormat::ListDecimal: html += QLatin1String( "<ol" ); break; case QTextListFormat::ListDisc: html += QLatin1String( "<ul" ); break; case QTextListFormat::ListCircle: html += QLatin1String( "<ul type=\"circle\"" ); break; case QTextListFormat::ListSquare: html += QLatin1String( "<ul type=\"square\"" ); break; case QTextListFormat::ListLowerAlpha: html += QLatin1String( "<ol type=\"a\"" ); break; case QTextListFormat::ListUpperAlpha: html += QLatin1String( "<ol type=\"A\"" ); break; default: html += QLatin1String( "<ul" ); // ### should not happen //qDebug() << html; } /* if (format.hasProperty(QTextFormat::ListIndent)) { html += QLatin1String(" style=\"-qt-list-indent: "); html += QString::number(format.indent()); html += QLatin1String(";\""); }*/ html += QLatin1Char( '>' ); } blockTag = QLatin1String( "li" ); // html += QLatin1String( "<li " ); } // const QTextBlockFormat blockFormat = block.blockFormat(); if ( blockFormat.hasProperty( QTextFormat::BlockTrailingHorizontalRulerWidth ) ) { if ( ( blockFormat.hasProperty( BilboTextFormat::IsHtmlTagSign ) ) && ( blockFormat.boolProperty( BilboTextFormat::IsHtmlTagSign ) ) ) { html += QLatin1String( "<!--split-->" ); return; } else { html += QLatin1String( "<hr" ); QTextLength width = blockFormat.lengthProperty( QTextFormat::BlockTrailingHorizontalRulerWidth ); if ( width.type() != QTextLength::VariableLength ) { emitTextLength( "width", width ); } else { html += QLatin1Char( ' ' ); } html += QLatin1String( "/>" ); return; } } const bool pre = blockFormat.nonBreakableLines(); if ( pre ) { // qDebug() << "NonBreakable lines" << endl; // if (list) { // html += QLatin1Char('>'); // } // html += QLatin1String( "<pre" ); // emitBlockAttributes( block ); // html += QLatin1Char( '>' ); blockTag = QLatin1String( "pre" ); } else { if (!list) { if ( isBlockQuote ) { html += QLatin1String( "<blockquote>" ); } if ( ( blockFormat.hasProperty( BilboTextFormat::HtmlHeading ) ) && ( blockFormat.intProperty( BilboTextFormat::HtmlHeading ) ) ) { const int index = blockFormat.intProperty( BilboTextFormat::HtmlHeading ); blockTag = QLatin1Char( 'h' ) + QString::number( index ); } else { //html += QLatin1String("<div"); // html += QLatin1String( "<p" ); blockTag = QLatin1String( "p" ); } } } if ( !blockTag.isEmpty() ) { html += QLatin1Char( '<' ) + blockTag; emitBlockAttributes( block ); html += QLatin1Char( '>' ); } QTextBlock::Iterator it = block.begin(); for ( ; !it.atEnd(); ++it ) { emitFragment( it.fragment(), blockFormat ); } if ( !blockTag.isEmpty() ) { html += QLatin1String( "</" ) + blockTag + QLatin1String( ">\n" ); } // if ( pre ) { // html += QLatin1String( "</pre>\n" ); // } else { // if ( list ) { // html += QLatin1String( "</li>\n" ); // } else { // if ( blockFormat::boolProperty( BilboTextFormat::IsHtmlHeading ) ) { // const int index = format.intProperty( QTextFormat::FontSizeAdjustment ); // switch ( index ) { // case -2: // html += QLatin1String( "</h6>" ); // break; // case -1: // html += QLatin1String( "</h5>" ); // break; // case 0: // html += QLatin1String( "</h4>" ); // break; // case 1: // html += QLatin1String( "</h3>" ); // break; // case 2: // html += QLatin1String( "<h2" ); // break; // case 3: // html += QLatin1String( "<h1" ); // break; // } // } else { // html += QLatin1String( "</p>\n" ); // } // } // } // HACK html.replace( QRegExp("<br[\\s]*/>[\\n]*<br[\\s]*/>[\\n]*"),"<br /> <br />" ); if ( list ) { if ( list->itemNumber( block ) == list->count() - 1 ) { // last item? close list if ( isOrderedList( list->format().style() ) ) { html += QLatin1String( "</ol>\n" ); } else { html += QLatin1String( "</ul>\n" ); } if ( isBlockQuote ) { html += QLatin1String( "</blockquote>\n" ); } } } else { if ( isBlockQuote ) { html += QLatin1String( "</blockquote>\n" ); } } // NOTE the bottom line is commented, to use default charFormat, which can be set from outside. //defaultCharFormat = oldDefaultCharFormat; }
void ListItemsHelper::recalculate() { //kDebug(32500); const QTextListFormat format = m_textList->format(); const KoListStyle::Style listStyle = static_cast<KoListStyle::Style>(m_textList->format().style()); const QString prefix = format.stringProperty(KoListStyle::ListItemPrefix); const QString suffix = format.stringProperty(KoListStyle::ListItemSuffix); const int level = format.intProperty(KoListStyle::Level); int dp = format.intProperty(KoListStyle::DisplayLevel); if (dp > level) dp = level; const int displayLevel = dp ? dp : 1; int startValue = 1; if (format.hasProperty(KoListStyle::StartValue)) startValue = format.intProperty(KoListStyle::StartValue); if (format.boolProperty(KoListStyle::ContinueNumbering)) { // Look for the index of a previous list of the same numbering style and level for (QTextBlock tb = m_textList->item(0).previous(); tb.isValid(); tb = tb.previous()) { if (!tb.textList() || tb.textList() == m_textList) continue; // no list here or it's the same list; keep looking QTextListFormat otherFormat = tb.textList()->format(); if (otherFormat.intProperty(KoListStyle::Level) != level) break; // found a different list but of a different level if (otherFormat.style() == format.style()) { if (KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(tb.userData())) startValue = data->counterIndex() + 1; // Start from previous list value + 1 } break; } } int index = startValue; QList<QTextList*> sublistsToRecalculate; qreal width = format.doubleProperty(KoListStyle::MinimumWidth); for (int i = 0; i < m_textList->count(); i++) { QTextBlock tb = m_textList->item(i); //kDebug(32500) <<" *" << tb.text(); KoTextBlockData *data = dynamic_cast<KoTextBlockData*>(tb.userData()); if (!data) { data = new KoTextBlockData(); tb.setUserData(data); } QTextBlockFormat blockFormat = tb.blockFormat(); if (blockFormat.boolProperty(KoParagraphStyle::UnnumberedListItem) || blockFormat.boolProperty(KoParagraphStyle::IsListHeader)) { data->setCounterText(QString()); data->setPartialCounterText(QString()); continue; } if (blockFormat.boolProperty(KoParagraphStyle::RestartListNumbering)) index = format.intProperty(KoListStyle::StartValue); const int paragIndex = blockFormat.intProperty(KoParagraphStyle::ListStartValue); if (paragIndex > 0) index = paragIndex; //check if this is the first of this level meaning we should start from startvalue QTextBlock b = tb.previous(); for (;b.isValid(); b = b.previous()) { if (b.textList() == m_textList) break; // all fine if (b.textList() == 0) continue; QTextListFormat otherFormat = b.textList()->format(); if (otherFormat.style() != format.style()) continue; // uninteresting for us if (b.textList()->format().intProperty(KoListStyle::Level) < level) { index = startValue; break; } } QString item; if (displayLevel > 1) { int checkLevel = level; int tmpDisplayLevel = displayLevel; for (QTextBlock b = tb.previous(); tmpDisplayLevel > 1 && b.isValid(); b = b.previous()) { if (b.textList() == 0) continue; QTextListFormat lf = b.textList()->format(); if (lf.style() != format.style()) continue; // uninteresting for us const int otherLevel = lf.intProperty(KoListStyle::Level); if (checkLevel <= otherLevel) continue; /*if(needsRecalc(b->textList())) { TODO } */ KoTextBlockData *otherData = dynamic_cast<KoTextBlockData*>(b.userData()); if (! otherData) { kWarning(32500) << "Missing KoTextBlockData, Skipping textblock"; continue; } if (tmpDisplayLevel - 1 < otherLevel) { // can't just copy it fully since we are // displaying less then the full counter item += otherData->partialCounterText(); tmpDisplayLevel--; checkLevel--; for (int i = otherLevel + 1; i < level; i++) { tmpDisplayLevel--; item += ".1"; // add missing counters. } } else { // just copy previous counter as prefix QString otherPrefix = lf.stringProperty(KoListStyle::ListItemPrefix); QString otherSuffix = lf.stringProperty(KoListStyle::ListItemSuffix); QString pureCounter = otherData->counterText().mid(otherPrefix.size()); pureCounter = pureCounter.left(pureCounter.size() - otherSuffix.size()); item += pureCounter; for (int i = otherLevel + 1; i < level; i++) item += ".1"; // add missing counters. tmpDisplayLevel = 0; break; } } for (int i = 1; i < tmpDisplayLevel; i++) item = "1." + item; // add missing counters. } if ((listStyle == KoListStyle::DecimalItem || listStyle == KoListStyle::AlphaLowerItem || listStyle == KoListStyle::UpperAlphaItem || listStyle == KoListStyle::RomanLowerItem || listStyle == KoListStyle::UpperRomanItem) && !(item.isEmpty() || item.endsWith('.') || item.endsWith(' '))) { item += '.'; } bool calcWidth = true; QString partialCounterText; switch (listStyle) { case KoListStyle::DecimalItem: partialCounterText = QString::number(index); break; case KoListStyle::AlphaLowerItem: partialCounterText = intToAlpha(index, Lowercase, m_textList->format().boolProperty(KoListStyle::LetterSynchronization)); break; case KoListStyle::UpperAlphaItem: partialCounterText = intToAlpha(index, Uppercase, m_textList->format().boolProperty(KoListStyle::LetterSynchronization)); break; case KoListStyle::RomanLowerItem: partialCounterText = intToRoman(index); break; case KoListStyle::UpperRomanItem: partialCounterText = intToRoman(index).toUpper(); break; case KoListStyle::SquareItem: case KoListStyle::DiscItem: case KoListStyle::CircleItem: case KoListStyle::HeavyCheckMarkItem: case KoListStyle::BallotXItem: case KoListStyle::RightArrowItem: case KoListStyle::RightArrowHeadItem: case KoListStyle::RhombusItem: case KoListStyle::BoxItem: { calcWidth = false; item = ' '; width = m_displayFont.pointSizeF(); int percent = format.intProperty(KoListStyle::BulletSize); if (percent > 0) width = width * (percent / 100.0); break; } case KoListStyle::CustomCharItem: calcWidth = false; if (format.intProperty(KoListStyle::BulletCharacter)) item = QString(QChar(format.intProperty(KoListStyle::BulletCharacter))); width = m_fm.width(item); break; case KoListStyle::None: calcWidth = false; width = 10.0; // simple indenting break; case KoListStyle::Bengali: case KoListStyle::Gujarati: case KoListStyle::Gurumukhi: case KoListStyle::Kannada: case KoListStyle::Malayalam: case KoListStyle::Oriya: case KoListStyle::Tamil: case KoListStyle::Telugu: case KoListStyle::Tibetan: case KoListStyle::Thai: partialCounterText = intToScript(index, listStyle); break; case KoListStyle::Abjad: case KoListStyle::ArabicAlphabet: case KoListStyle::AbjadMinor: partialCounterText = intToScriptList(index, listStyle); break; case KoListStyle::ImageItem: calcWidth = false; width = qMax(format.doubleProperty(KoListStyle::Width), (qreal)1.0); break; default: // others we ignore. calcWidth = false; } data->setCounterIsImage(listStyle == KoListStyle::ImageItem); data->setPartialCounterText(partialCounterText); data->setCounterIndex(index); item += partialCounterText; if (calcWidth) width = qMax(width, m_fm.width(item)); data->setCounterText(prefix + item + suffix); index++; // have to recalculate any sublists under this element too QTextBlock nb = tb.next(); while (nb.isValid() && nb.textList() == 0) nb = nb.next(); if (nb.isValid()) { QTextListFormat lf = nb.textList()->format(); if ((lf.style() == format.style()) && nb.textList()->format().intProperty(KoListStyle::Level) > level) { // this is a sublist // have to remember to recalculate this list after the current level is done // cant do it right away since the sublist's prefix text is dependant on this level sublistsToRecalculate.append(nb.textList()); } } } for (int i = 0; i < sublistsToRecalculate.count(); i++) { ListItemsHelper lih(sublistsToRecalculate.at(i), m_displayFont); lih.recalculate(); } qreal counterSpacing = m_fm.width(' '); counterSpacing = qMax(format.doubleProperty(KoListStyle::MinimumDistance), counterSpacing); width += m_fm.width(prefix + suffix); // same for all width = qMax(format.doubleProperty(KoListStyle::MinimumWidth), width); for (int i = 0; i < m_textList->count(); i++) { QTextBlock tb = m_textList->item(i); KoTextBlockData *data = dynamic_cast<KoTextBlockData*>(tb.userData()); Q_ASSERT(data); data->setCounterWidth(width); data->setCounterSpacing(counterSpacing); //kDebug(32500) << data->counterText() <<"" << tb.text(); //kDebug(32500) <<" setCounterWidth:" << width; } //kDebug(32500); }