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(); } }
/*! Makes the given \a block part of the list. \sa remove(), removeItem() */ void QTextList::add(const QTextBlock &block) { QTextBlockFormat fmt = block.blockFormat(); fmt.setObjectIndex(objectIndex()); block.docHandle()->setBlockFormat(block, block, fmt, QTextDocumentPrivate::SetFormat); }
void HGMarkdownHighlighter::highlight() { if (cached_elements == NULL) { qDebug() << "cached_elements is NULL"; return; } if (highlightingStyles == NULL) this->setDefaultStyles(); this->clearFormatting(); for (int i = 0; i < highlightingStyles->size(); i++) { HighlightingStyle style = highlightingStyles->at(i); pmh_element *elem_cursor = cached_elements[style.type]; while (elem_cursor != NULL) { if (elem_cursor->end <= elem_cursor->pos) { elem_cursor = elem_cursor->next; continue; } // "The QTextLayout object can only be modified from the // documentChanged implementation of a QAbstractTextDocumentLayout // subclass. Any changes applied from the outside cause undefined // behavior." -- we are breaking this rule here. There might be // a better (more correct) way to do this. int startBlockNum = document->findBlock(elem_cursor->pos).blockNumber(); int endBlockNum = document->findBlock(elem_cursor->end).blockNumber(); for (int j = startBlockNum; j <= endBlockNum; j++) { QTextBlock block = document->findBlockByNumber(j); QTextLayout *layout = block.layout(); QList<QTextLayout::FormatRange> list = layout->additionalFormats(); int blockpos = block.position(); QTextLayout::FormatRange r; r.format = style.format; if (j == startBlockNum) { r.start = elem_cursor->pos - blockpos; r.length = (startBlockNum == endBlockNum) ? elem_cursor->end - elem_cursor->pos : block.length() - r.start; } else if (j == endBlockNum) { r.start = 0; r.length = elem_cursor->end - blockpos; } else { r.start = 0; r.length = block.length(); } list.append(r); layout->setAdditionalFormats(list); } elem_cursor = elem_cursor->next; } } document->markContentsDirty(0, document->characterCount()); }
QString FlatTextarea::getText(int32 start, int32 end) const { if (end >= 0 && end <= start) return QString(); if (start < 0) start = 0; bool full = (start == 0) && (end < 0); QTextDocument *doc(document()); QTextBlock from = full ? doc->begin() : doc->findBlock(start), till = (end < 0) ? doc->end() : doc->findBlock(end); if (till.isValid()) till = till.next(); int32 possibleLen = 0; for (QTextBlock b = from; b != till; b = b.next()) { possibleLen += b.length(); } QString result; result.reserve(possibleLen + 1); if (!full && end < 0) { end = possibleLen; } for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 p = full ? 0 : fragment.position(), e = full ? 0 : (p + fragment.length()); if (!full) { if (p >= end || e <= start) { continue; } } QTextCharFormat f = fragment.charFormat(); QString emojiText; QString t(fragment.text()); if (!full) { if (p < start) { t = t.mid(start - p, end - start); } else if (e > end) { t = t.mid(0, end - p); } } QChar *ub = t.data(), *uc = ub, *ue = uc + t.size(); for (; uc != ue; ++uc) { switch (uc->unicode()) { case 0xfdd0: // QTextBeginningOfFrame case 0xfdd1: // QTextEndOfFrame case QChar::ParagraphSeparator: case QChar::LineSeparator: *uc = QLatin1Char('\n'); break; case QChar::Nbsp: *uc = QLatin1Char(' '); break; case QChar::ObjectReplacementCharacter: if (emojiText.isEmpty() && f.isImageFormat()) { QString imageName = static_cast<QTextImageFormat*>(&f)->name(); if (imageName.startsWith(QLatin1String("emoji://e."))) { if (EmojiPtr emoji = emojiFromUrl(imageName)) { emojiText = textEmojiString(emoji); } } } if (uc > ub) result.append(ub, uc - ub); if (!emojiText.isEmpty()) result.append(emojiText); ub = uc + 1; break; } } if (uc > ub) result.append(ub, uc - ub); } result.append('\n'); } result.chop(1); return result; }
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 CodeArea::keyPressEvent(QKeyEvent *e) { if (mCompleter && mCompleter->popup()->isVisible()) { // The following keys are forwarded by the completer to the widget switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Escape: case Qt::Key_Tab: case Qt::Key_Backtab: e->ignore(); return; // let the completer do default behavior default: break; } } { switch (e->key()) { case Qt::Key_Tab: { QTextCursor tc = textCursor(); if(tc.hasSelection()) { } if(!(e->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier))) { this->insertPlainText(" "); QTextCursor tc = textCursor(); tc.setPosition(tc.position()); setTextCursor(tc); return; } } case Qt::Key_Backtab: { QTextCursor tc = textCursor(); QTextBlock tb = tc.block(); QString str = tb.text(); int space = 4; tc.movePosition(QTextCursor::StartOfLine); foreach(QString s, str) { if(s == " "&& space!=0) { space--; tc.movePosition(QTextCursor::Right); tc.deletePreviousChar(); } else break; } return; } case Qt::Key_Return: { QTextCursor tc = textCursor(); QTextBlock tb = tc.block(); QString str = tb.text(); int space = 0; foreach(QString s, str) { if(s == " ") space++; else break; } insertPlainText("\n"); for(int x= 0; x <space;++x) insertPlainText(" "); tc.movePosition(QTextCursor::EndOfLine); setTextCursor(tc); e->accept(); return; } default: break; } } bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space); // CTRL+E if (!mCompleter || !isShortcut) // do not process the shortcut when we have a completer QPlainTextEdit::keyPressEvent(e); const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier); if (!mCompleter || (ctrlOrShift && e->text().isEmpty())) return; static QString eow("~!@#$%^&*_+(){}|\"<>?,:./;'[]\\-="); // end of word bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; QString completionPrefix = textUnderCursor(); /*if(completionPrefix.endsWith(")")) { completionPrefix.remove(completionPrefix.size()-1); }*/ if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3 || eow.contains(e->text().right(1)))) { mCompleter->popup()->hide(); return; } if (completionPrefix != mCompleter->completionPrefix()) { mCompleter->setCompletionPrefix(completionPrefix); mCompleter->popup()->setCurrentIndex(mCompleter->completionModel()->index(0, 0)); } QRect cr = cursorRect(); cr.setWidth(mCompleter->popup()->sizeHintForColumn(0) + mCompleter->popup()->verticalScrollBar()->sizeHint().width()); mCompleter->complete(cr); // popup it up! }
void QFCompleterTextEditNumberBar::paintEvent(QPaintEvent */*event*/) { QTextDocument* doc=editor->document(); QAbstractTextDocumentLayout *layout = doc->documentLayout(); qreal yPosition=editor->verticalScrollBar()->value(); qreal vpHeight=editor->viewport()->height(); QPainter p(this); p.setFont(linenumberFont); int linenumberWidth=QString::number(doc->blockCount()).size()*p.fontMetrics().width("0")+4; int markerheight=p.fontMetrics().ascent()+2; // set the width of the widget setFixedWidth(linenumberWidth+markerWidth+2); // first we draw the background p.setBrush(QBrush(linenumberColumnColor)); p.setPen(QPen(linenumberColumnColor)); p.drawRect(0,0,width(),vpHeight); p.setPen(QPen(markerColumnColor)); p.setBrush(QBrush(markerColumnColor)); p.drawRect(linenumberWidth,0,width()-linenumberWidth,vpHeight); // reset the rect of all markers QMutableMapIterator<int, itemData> i(markers); while (i.hasNext()) { i.next(); itemData d=i.value(); d.rect=QRect(0,0,0,0); i.setValue(d); } // now we draw the line numbers p.setPen(QPen(linenumberColor)); for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) { QRectF brect=layout->blockBoundingRect(it); qreal bottompos=brect.y()+brect.height(); markerheight=brect.height()-8; // we end this loop if the current block lies below the viewport if (brect.y() > yPosition+ vpHeight) break; // if we are inside the viewport, we have to paint a line number for this line if (bottompos >= yPosition) { QString txt = QString::number(it.blockNumber()+1); p.drawText(1, brect.y()-yPosition, linenumberWidth-2, brect.height(), Qt::AlignRight|Qt::AlignVCenter, txt); if (markers.contains(it.blockNumber()+1)) { itemData d=markers[it.blockNumber()+1]; QRect markerrect=QRect(linenumberWidth+2, brect.y()-yPosition+4, width()-linenumberWidth-4, markerheight); markers[it.blockNumber()+1].rect=markerrect; if (d.type==mtInfo) { //p.drawImage(linenumberWidth+2, brect.y()-yPosition, QIcon(":/event_info.png")); p.setBrush(infoMarkerColor); QPen pe=p.pen(); pe.setColor(QColor("black")); pe.setCosmetic(true); pe.setWidth(1); p.setPen(pe); p.drawRect(markerrect); } else if (d.type==mtError) { //p.drawImage(linenumberWidth+2, brect.y()-yPosition, QIcon(":/event_error.png")); p.setBrush(errorMarkerColor); QPen pe=p.pen(); pe.setColor(QColor("black")); pe.setCosmetic(true); pe.setWidth(1); p.setPen(pe); p.drawRect(markerrect); } else { //p.drawImage(linenumberWidth+2, brect.y()-yPosition, QIcon(":/event_warning.png")); p.setBrush(warningMarkerColor); QPen pe=p.pen(); pe.setColor(QColor("black")); pe.setCosmetic(true); pe.setWidth(1); p.setPen(pe); p.drawRect(markerrect); } } } } }
void GenericCodeEditor::paintLineIndicator( QPaintEvent *e ) { QPalette plt( mLineIndicator->palette() ); QRect r( e->rect() ); QPainter p( mLineIndicator ); p.fillRect( r, plt.color( QPalette::Mid ) ); p.setPen( plt.color(QPalette::Dark) ); p.drawLine( r.topRight(), r.bottomRight() ); p.setPen( plt.color(QPalette::ButtonText) ); QTextDocument *doc = QPlainTextEdit::document(); QTextCursor cursor(textCursor()); int selStartBlock, selEndBlock; if (cursor.hasSelection()) { selStartBlock = doc->findBlock(cursor.selectionStart()).blockNumber(); selEndBlock = doc->findBlock(cursor.selectionEnd()).blockNumber(); } else selStartBlock = selEndBlock = -1; QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); qreal top = blockBoundingGeometry(block).translated(contentOffset()).top(); qreal bottom = top + blockBoundingRect(block).height(); while (block.isValid() && top <= e->rect().bottom()) { if (block.isVisible() && bottom >= e->rect().top()) { p.save(); QRectF numRect( 0, top, mLineIndicator->width() - 1, bottom - top ); int num = blockNumber; if (num >= selStartBlock && num <= selEndBlock) { num -= selStartBlock; p.setPen(Qt::NoPen); p.setBrush(plt.color(QPalette::Highlight)); p.drawRect(numRect); p.setPen(plt.color(QPalette::HighlightedText)); } QString number = QString::number(num + 1); p.drawText(0, top, mLineIndicator->width() - 4, bottom - top, Qt::AlignRight, number); p.restore(); } block = block.next(); top = bottom; bottom = top + blockBoundingRect(block).height(); ++blockNumber; } if(!mEditorBoxIsActive) { QColor color = plt.color(QPalette::Mid); if(color.lightness() >= 128) color = color.darker(60); else color = color.lighter(50); color.setAlpha(inactiveFadeAlpha()); p.fillRect( r, color ); } }
QString KTextEditingPlugin::paragraph(QTextDocument *document, int cursorPosition) const { QTextBlock block = document->findBlock(cursorPosition); return block.text(); }
void InputField::processDocumentContentsChange(int position, int charsAdded) { int32 emojiPosition = 0, emojiLen = 0; const EmojiData *emoji = 0; static QString space(' '); QTextDocument *doc(_inner.document()); QTextCursor c(_inner.textCursor()); c.joinPreviousEditBlock(); while (true) { int32 start = position, end = position + charsAdded; QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); if (till.isValid()) till = till.next(); for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 fp = fragment.position(), fe = fp + fragment.length(); if (fp >= end || fe <= start) { continue; } QString t(fragment.text()); const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch) { // QTextBeginningOfFrame // QTextEndOfFrame if (ch->unicode() == 0xfdd0 || ch->unicode() == 0xfdd1 || ch->unicode() == QChar::ParagraphSeparator || ch->unicode() == QChar::LineSeparator || ch->unicode() == '\n' || ch->unicode() == '\r') { if (!_inner.document()->pageSize().isNull()) { _inner.document()->setPageSize(QSizeF(0, 0)); } int32 nlPosition = fp + (ch - t.constData()); QTextCursor c(doc->docHandle(), nlPosition); c.setPosition(nlPosition + 1, QTextCursor::KeepAnchor); c.insertText(space); position = nlPosition + 1; emoji = TwoSymbolEmoji; // just a flag break; } emoji = emojiFromText(ch, e, emojiLen); if (emoji) { emojiPosition = fp + (ch - t.constData()); break; } if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch; } if (emoji) break; } if (emoji) break; if (b.next() != doc->end()) { int32 nlPosition = b.next().position() - 1; QTextCursor c(doc->docHandle(), nlPosition); c.setPosition(nlPosition + 1, QTextCursor::KeepAnchor); c.insertText(space); position = nlPosition + 1; emoji = TwoSymbolEmoji; // just a flag break; } } if (emoji == TwoSymbolEmoji) { // just skip emoji = 0; emojiPosition = 0; } else if (emoji) { if (!_inner.document()->pageSize().isNull()) { _inner.document()->setPageSize(QSizeF(0, 0)); } QTextCursor c(doc->docHandle(), emojiPosition); c.setPosition(emojiPosition + emojiLen, QTextCursor::KeepAnchor); int32 removedUpto = c.position(); insertEmoji(emoji, c); for (Insertions::iterator i = _insertions.begin(), e = _insertions.end(); i != e; ++i) { if (i->first >= removedUpto) { i->first -= removedUpto - emojiPosition - 1; } else if (i->first >= emojiPosition) { i->second -= removedUpto - emojiPosition; i->first = emojiPosition + 1; } else if (i->first + i->second > emojiPosition + 1) { i->second -= qMin(removedUpto, i->first + i->second) - emojiPosition; } } charsAdded -= removedUpto - position; position = emojiPosition + 1; emoji = 0; emojiPosition = 0; } else { break; } } c.endEditBlock(); }
void QSyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded) { Q_UNUSED(charsRemoved); rehighlightPending = false; QTextBlock block = doc->findBlock(from); if (!block.isValid()) return; int endPosition; QTextBlock lastBlock = doc->findBlock(from + charsAdded); if (lastBlock.isValid()) endPosition = lastBlock.position() + lastBlock.length(); else endPosition = doc->docHandle()->length(); bool forceHighlightOfNextBlock = false; while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) { const int stateBeforeHighlight = block.userState(); reformatBlock(block); forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight); block = block.next(); } formatChanges.clear(); }
void QTextCopyHelper::copy() { if (cursor.hasComplexSelection()) { QTextTable *table = cursor.currentTable(); int row_start, col_start, num_rows, num_cols; cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols); QTextTableFormat tableFormat = table->format(); tableFormat.setColumns(num_cols); tableFormat.clearColumnWidthConstraints(); const int objectIndex = dst->formatCollection()->createObjectIndex(tableFormat); Q_ASSERT(row_start != -1); for (int r = row_start; r < row_start + num_rows; ++r) { for (int c = col_start; c < col_start + num_cols; ++c) { QTextTableCell cell = table->cellAt(r, c); const int rspan = cell.rowSpan(); const int cspan = cell.columnSpan(); if (rspan != 1) { int cr = cell.row(); if (cr != r) continue; } if (cspan != 1) { int cc = cell.column(); if (cc != c) continue; } // add the QTextBeginningOfFrame QTextCharFormat cellFormat = cell.format(); if (r + rspan >= row_start + num_rows) { cellFormat.setTableCellRowSpan(row_start + num_rows - r); } if (c + cspan >= col_start + num_cols) { cellFormat.setTableCellColumnSpan(col_start + num_cols - c); } const int charFormatIndex = convertFormatIndex(cellFormat, objectIndex); int blockIdx = -2; const int cellPos = cell.firstPosition(); QTextBlock block = src->blocksFind(cellPos); if (block.position() == cellPos) { blockIdx = convertFormatIndex(block.blockFormat()); } dst->insertBlock(QTextBeginningOfFrame, insertPos, blockIdx, charFormatIndex); ++insertPos; // nothing to add for empty cells if (cell.lastPosition() > cellPos) { // add the contents appendFragments(cellPos, cell.lastPosition()); } } } // add end of table int end = table->lastPosition(); appendFragment(end, end+1, objectIndex); } else { appendFragments(cursor.selectionStart(), cursor.selectionEnd()); } }
void CodeFoldingPanel::paintEvent(QPaintEvent *e) { QTextDocument *doc = editorWidget()->document(); TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout()); if(!documentLayout) return; QPalette pal = areaWidget()->palette(); pal.setCurrentColorGroup(QPalette::Active); QPainter painter(this); const QFontMetrics fm(areaWidget()->font()); const int collapseColumnWidth = d->m_codeFoldingVisible ? foldBoxWidth(fm): 0; const int extraAreaWidth = d->m_extraArea->width() - collapseColumnWidth; painter.fillRect(e->rect(), pal.color(QPalette::Background)); QTextBlock block = editorWidget()->firstVisibleBlock(); int blockNumber = block.blockNumber(); qreal top = editorWidget()->blockBoundingGeometry(block).translated(editorWidget()->contentOffset()).top(); qreal bottom = top; while (block.isValid() && top <= e->rect().bottom()) { top = bottom; const qreal height = editorWidget()->blockBoundingRect(block).height(); bottom = top + height; QTextBlock nextBlock = block.next(); QTextBlock nextVisibleBlock = nextBlock; int nextVisibleBlockNumber = blockNumber + 1; if (!nextVisibleBlock.isVisible()) { // invisible blocks do have zero line count nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber()); nextVisibleBlockNumber = nextVisibleBlock.blockNumber(); } if (bottom < e->rect().top()) { block = nextVisibleBlock; blockNumber = nextVisibleBlockNumber; continue; } painter.setPen(pal.color(QPalette::Dark)); painter.save(); painter.setRenderHint(QPainter::Antialiasing, false); int extraAreaHighlightFoldBlockNumber = -1; int extraAreaHighlightFoldEndBlockNumber = -1; bool endIsVisible = false; if (!d->m_highlightBlocksInfo.isEmpty()) { extraAreaHighlightFoldBlockNumber = d->m_highlightBlocksInfo.open.last(); extraAreaHighlightFoldEndBlockNumber = d->m_highlightBlocksInfo.close.first(); endIsVisible = doc->findBlockByNumber(extraAreaHighlightFoldEndBlockNumber).isVisible(); // QTextBlock before = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber-1); // if (TextBlockUserData::hasCollapseAfter(before)) { // extraAreaHighlightCollapseBlockNumber--; // } } TextBlockUserData *nextBlockUserData = TextDocumentLayout::testUserData(nextBlock); bool drawBox = nextBlockUserData && TextDocumentLayout::foldingIndent(block) < nextBlockUserData->foldingIndent(); bool active = blockNumber == extraAreaHighlightFoldBlockNumber; bool drawStart = active; bool drawEnd = blockNumber == extraAreaHighlightFoldEndBlockNumber || (drawStart && !endIsVisible); bool hovered = blockNumber >= extraAreaHighlightFoldBlockNumber && blockNumber <= extraAreaHighlightFoldEndBlockNumber; int boxWidth = foldBoxWidth(fm); if (hovered) { int itop = qRound(top); int ibottom = qRound(bottom); QRect box = QRect(extraAreaWidth + 1, itop, boxWidth - 2, ibottom - itop); drawRectBox(&painter, box, drawStart, drawEnd, pal); } if (drawBox) { bool expanded = nextBlock.isVisible(); int size = boxWidth/4; QRect box(extraAreaWidth + size, top + size, 2 * (size) + 1, 2 * (size) + 1); d->drawFoldingMarker(&painter, pal, box, expanded, active, hovered); } painter.restore(); block = nextVisibleBlock; blockNumber = nextVisibleBlockNumber; } }
void TestDocumentLayout::testCenteredItems() { initForNewTest("ListItem\nListItem\nListItem"); KoListStyle listStyle; KoListLevelProperties llp; llp.setStyle(KoListStyle::DecimalItem); listStyle.setLevelProperties(llp); QTextBlock block = m_doc->begin(); // normal block QVERIFY(block.isValid()); listStyle.applyStyle(block); block = block.next(); // centered block QVERIFY(block.isValid()); listStyle.applyStyle(block); QTextBlockFormat fmt; fmt.setAlignment(Qt::AlignHCenter); QTextCursor cursor(block); cursor.mergeBlockFormat(fmt); block = block.next(); // centered RTL text. listStyle.applyStyle(block); cursor = QTextCursor(block); fmt.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::RightLeftTopBottom); cursor.mergeBlockFormat(fmt); m_layout->layout(); block = m_doc->begin(); QTextLayout *layout = block.layout(); QTextLine line1 = layout->lineAt(0); KoTextBlockData *data1 = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(line1.isValid()); QVERIFY(line1.width() < 200); // the counter takes some space. block = block.next(); layout = block.layout(); QTextLine line2 = layout->lineAt(0); KoTextBlockData *data2 = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(line2.isValid()); QVERIFY(line2.width() < 200); // the counter takes some space. QCOMPARE(line1.width(), line2.width()); const qreal width1 = line1.naturalTextWidth() + data1->counterWidth() + data1->counterSpacing(); const qreal width2 = line2.naturalTextWidth() + data2->counterWidth() + data2->counterSpacing(); QCOMPARE(width1, width2); QVERIFY(data1->counterPosition().x() < data2->counterPosition().x()); const qreal padding = (200 - width2) / 2; QVERIFY(padding > 0);// not really a layout test, but the rest will be bogus otherwise. QCOMPARE(data2->counterPosition().x(), padding); // close to the centered text. // right to left parag places the counter on the right. Its centered, so not the far right. block = block.next(); layout = block.layout(); QTextLine line = layout->lineAt(0); KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QCOMPARE(data->counterPosition().x(), 200 - padding - data->counterWidth()); }
IAssistProposal *QssCompletionAssistProcessor::perform(const IAssistInterface *interface) { m_interface.reset(interface); if (isInComment()) return 0; if (interface->reason() == IdleEditor && !acceptsIdleEditor()) return 0; if (m_startPosition == -1) m_startPosition = findStartOfName(); QTextBlock block = interface->textDocument()->findBlock(interface->position()); QTextBlock prevBlock = block.previous(); Lexer lexer; Parser parser; if (prevBlock.isValid()) { lexer.setState(qssLexerState(prevBlock.userState())); parser.setState(qssParserState(prevBlock.userState())); } QList<Token> tokenList = lexer.scanMore(block.text()); int index = m_startPosition - block.position(); Q_FOREACH (const Token &t, tokenList) { if (index <= t.begin()) break; parser.parseMore(t); } QStringList keywords; QIcon icon; switch (parser.state()) { case Parser::ObjectState: keywords << Lexer::objects(); icon = QIcon(":/qsseditor/images/class.png"); break; case Parser::PseudoStatesState: keywords << Lexer::pseudoStates(); icon = QIcon(":/qsseditor/images/func.png"); break; case Parser::SubControlState: keywords << Lexer::subControls(); icon = QIcon(":/qsseditor/images/func.png"); break; case Parser::AttributeNameState: keywords << Lexer::attributeNames(); icon = QIcon(":/qsseditor/images/var.png"); break; case Parser::AttributeBodyState: keywords << Lexer::attributeCtors() << Lexer::attributeKeywords(); icon = QIcon(":/qsseditor/images/keyword.png"); break; default: ; } keywords.removeDuplicates(); QList<TextEditor::BasicProposalItem *> items; for (int i = 0; i < keywords.count(); i++) { BasicProposalItem *item = new QssAssistProposalItem; item->setText(keywords[i]); item->setIcon(icon); items.append(item); } return new GenericProposal(m_startPosition, new QssAssistProposalModel(items)); }
/* Returns the recommended indent for the bottom line of program. Unless null, typedIn stores the character of yyProgram that triggered reindentation. This function works better if typedIn is set properly; it is slightly more conservative if typedIn is completely wild, and slighly more liberal if typedIn is always null. The user might be annoyed by the liberal behavior. */ int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn) { if (begin == end) return 0; const QTextBlock last = end.previous(); initialize(begin, last); QString bottomLine = last.text(); QChar firstCh = firstNonWhiteSpace(bottomLine); int indent = 0; if (bottomLineStartsInMultilineComment()) { /* The bottom line starts in a C-style comment. Indent it smartly, unless the user has already played around with it, in which case it's better to leave her stuff alone. */ if (isOnlyWhiteSpace(bottomLine)) indent = indentWhenBottomLineStartsInMultiLineComment(); else indent = indentOfLine(bottomLine); } else { if (isUnfinishedLine()) indent = indentForContinuationLine(); else indent = indentForStandaloneLine(); if ((okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}')) || (okay(typedIn, QLatin1Char(']')) && firstCh == QLatin1Char(']'))) { /* A closing brace is one level more to the left than the code it follows. */ indent -= ppIndentSize; } else if (okay(typedIn, QLatin1Char(':'))) { if (caseOrDefault.exactMatch(bottomLine)) { /* Move a case label (or the ':' in front of a constructor initialization list) one level to the left, but only if the user did not play around with it yet. Some users have exotic tastes in the matter, and most users probably are not patient enough to wait for the final ':' to format their code properly. We don't attempt the same for goto labels, as the user is probably the middle of "foo::bar". (Who uses goto, anyway?) */ if (indentOfLine(bottomLine) <= indent) indent -= ppIndentSize; else indent = indentOfLine(bottomLine); } } } return qMax(0, indent); }
void CodeFormatter::recalculateStateAfter(const QTextBlock &block) { restoreCurrentState(block.previous()); const int lexerState = tokenizeBlock(block); m_tokenIndex = 0; m_newStates.clear(); //qDebug() << "Starting to look at " << block.text() << block.blockNumber() + 1; for (; m_tokenIndex < m_tokens.size(); ) { m_currentToken = tokenAt(m_tokenIndex); const int kind = extendedTokenKind(m_currentToken); //qDebug() << "Token" << m_currentLine.mid(m_currentToken.begin(), m_currentToken.length) << m_tokenIndex << "in line" << block.blockNumber() + 1; //dump(); if (kind == Comment && state().type != multiline_comment_cont && state().type != multiline_comment_start) { m_tokenIndex += 1; continue; } switch (m_currentState.top().type) { case topmost_intro: switch (kind) { case Identifier: enter(objectdefinition_or_js); continue; case Import: enter(top_qml); continue; case LeftBrace: enter(top_js); enter(expression); continue; // if a file starts with {, it's likely json default: enter(top_js); continue; } break; case top_qml: switch (kind) { case Import: enter(import_start); break; case Identifier: enter(binding_or_objectdefinition); break; } break; case top_js: tryStatement(); break; case objectdefinition_or_js: switch (kind) { case Dot: break; case Identifier: if (!m_currentLine.at(m_currentToken.begin()).isUpper()) { turnInto(top_js); continue; } break; case LeftBrace: turnInto(binding_or_objectdefinition); continue; default: turnInto(top_js); continue; } break; case import_start: enter(import_maybe_dot_or_version_or_as); break; case import_maybe_dot_or_version_or_as: switch (kind) { case Dot: turnInto(import_dot); break; case As: turnInto(import_as); break; case Number: turnInto(import_maybe_as); break; default: leave(); leave(); continue; } break; case import_maybe_as: switch (kind) { case As: turnInto(import_as); break; default: leave(); leave(); continue; } break; case import_dot: switch (kind) { case Identifier: turnInto(import_maybe_dot_or_version_or_as); break; default: leave(); leave(); continue; } break; case import_as: switch (kind) { case Identifier: leave(); leave(); break; } break; case binding_or_objectdefinition: switch (kind) { case Colon: enter(binding_assignment); break; case LeftBrace: enter(objectdefinition_open); break; } break; case binding_assignment: switch (kind) { case Semicolon: leave(true); break; case If: enter(if_statement); break; case With: enter(statement_with_condition); break; case Try: enter(try_statement); break; case Switch: enter(switch_statement); break; case LeftBrace: enter(jsblock_open); break; case On: case As: case List: case Import: case Signal: case Property: case Identifier: enter(expression_or_objectdefinition); break; // error recovery case RightBracket: case RightParenthesis: leave(true); break; default: enter(expression); continue; } break; case objectdefinition_open: switch (kind) { case RightBrace: leave(true); break; case Default: enter(default_property_start); break; case Property: enter(property_start); break; case Function: enter(function_start); break; case Signal: enter(signal_start); break; case On: case As: case List: case Import: case Identifier: enter(binding_or_objectdefinition); break; } break; case default_property_start: if (kind != Property) leave(true); else turnInto(property_start); break; case property_start: switch (kind) { case Colon: enter(binding_assignment); break; // oops, was a binding case Var: case Identifier: enter(property_name); break; case List: enter(property_list_open); break; default: leave(true); continue; } break; case property_name: turnInto(property_maybe_initializer); break; case property_list_open: if (m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length) == QLatin1String(">")) turnInto(property_name); break; case property_maybe_initializer: switch (kind) { case Colon: turnInto(binding_assignment); break; default: leave(true); continue; } break; case signal_start: switch (kind) { case Colon: enter(binding_assignment); break; // oops, was a binding default: enter(signal_maybe_arglist); break; } break; case signal_maybe_arglist: switch (kind) { case LeftParenthesis: turnInto(signal_arglist_open); break; default: leave(true); continue; } break; case signal_arglist_open: switch (kind) { case RightParenthesis: leave(true); break; } break; case function_start: switch (kind) { case LeftParenthesis: enter(function_arglist_open); break; } break; case function_arglist_open: switch (kind) { case RightParenthesis: turnInto(function_arglist_closed); break; } break; case function_arglist_closed: switch (kind) { case LeftBrace: turnInto(jsblock_open); break; default: leave(true); continue; // error recovery } break; case expression_or_objectdefinition: switch (kind) { case Dot: case Identifier: break; // need to become an objectdefinition_open in cases like "width: Qt.Foo {" case LeftBrace: turnInto(objectdefinition_open); break; // propagate 'leave' from expression state case RightBracket: case RightParenthesis: leave(); continue; default: enter(expression); continue; // really? identifier and more tokens might already be gone } break; case expression_or_label: switch (kind) { case Colon: turnInto(labelled_statement); break; // propagate 'leave' from expression state case RightBracket: case RightParenthesis: leave(); continue; default: enter(expression); continue; } break; case ternary_op: if (kind == Colon) { enter(ternary_op_after_colon); enter(expression_continuation); break; } // fallthrough case ternary_op_after_colon: case expression: if (tryInsideExpression()) break; switch (kind) { case Comma: case Delimiter: enter(expression_continuation); break; case RightBracket: case RightParenthesis: leave(); continue; case RightBrace: leave(true); continue; case Semicolon: leave(true); break; } break; case expression_continuation: leave(); continue; case expression_maybe_continuation: switch (kind) { case Question: case Delimiter: case LeftBracket: case LeftParenthesis: leave(); continue; default: leave(true); continue; } break; case paren_open: if (tryInsideExpression()) break; switch (kind) { case RightParenthesis: leave(); break; } break; case bracket_open: if (tryInsideExpression()) break; switch (kind) { case Comma: enter(bracket_element_start); break; case RightBracket: leave(); break; } break; case objectliteral_open: if (tryInsideExpression()) break; switch (kind) { case Colon: enter(objectliteral_assignment); break; case RightBracket: case RightParenthesis: leave(); continue; // error recovery case RightBrace: leave(true); break; } break; // pretty much like expression, but ends with , or } case objectliteral_assignment: if (tryInsideExpression()) break; switch (kind) { case Delimiter: enter(expression_continuation); break; case RightBracket: case RightParenthesis: leave(); continue; // error recovery case RightBrace: leave(); continue; // so we also leave objectliteral_open case Comma: leave(); break; } break; case bracket_element_start: switch (kind) { case Identifier: turnInto(bracket_element_maybe_objectdefinition); break; default: leave(); continue; } break; case bracket_element_maybe_objectdefinition: switch (kind) { case LeftBrace: turnInto(objectdefinition_open); break; default: leave(); continue; } break; case jsblock_open: case substatement_open: if (tryStatement()) break; switch (kind) { case RightBrace: leave(true); break; } break; case labelled_statement: if (tryStatement()) break; leave(true); // error recovery break; case substatement: // prefer substatement_open over block_open if (kind != LeftBrace) { if (tryStatement()) break; } switch (kind) { case LeftBrace: turnInto(substatement_open); break; } break; case if_statement: switch (kind) { case LeftParenthesis: enter(condition_open); break; default: leave(true); break; // error recovery } break; case maybe_else: switch (kind) { case Else: turnInto(else_clause); enter(substatement); break; default: leave(true); continue; } break; case maybe_catch_or_finally: switch (kind) { case Catch: turnInto(catch_statement); break; case Finally: turnInto(finally_statement); break; default: leave(true); continue; } break; case else_clause: // ### shouldn't happen dump(); Q_ASSERT(false); leave(true); break; case condition_open: if (tryInsideExpression()) break; switch (kind) { case RightParenthesis: turnInto(substatement); break; } break; case switch_statement: case catch_statement: case statement_with_condition: switch (kind) { case LeftParenthesis: enter(statement_with_condition_paren_open); break; default: leave(true); } break; case statement_with_condition_paren_open: if (tryInsideExpression()) break; switch (kind) { case RightParenthesis: turnInto(substatement); break; } break; case try_statement: case finally_statement: switch (kind) { case LeftBrace: enter(jsblock_open); break; default: leave(true); break; } break; case do_statement: switch (kind) { case While: break; case LeftParenthesis: enter(do_statement_while_paren_open); break; default: leave(true); continue; // error recovery } break; case do_statement_while_paren_open: if (tryInsideExpression()) break; switch (kind) { case RightParenthesis: leave(); leave(true); break; } break; case breakcontinue_statement: switch (kind) { case Identifier: leave(true); break; default: leave(true); continue; // try again } break; case case_start: switch (kind) { case Colon: turnInto(case_cont); break; } break; case case_cont: if (kind != Case && kind != Default && tryStatement()) break; switch (kind) { case RightBrace: leave(); continue; case Default: case Case: leave(); continue; } break; case multiline_comment_start: case multiline_comment_cont: if (kind != Comment) { leave(); continue; } else if (m_tokenIndex == m_tokens.size() - 1 && (lexerState & Scanner::MultiLineMask) == Scanner::Normal) { leave(); } else if (m_tokenIndex == 0) { // to allow enter/leave to update the indentDepth turnInto(multiline_comment_cont); } break; default: qWarning() << "Unhandled state" << m_currentState.top().type; break; } // end of state switch ++m_tokenIndex; } int topState = m_currentState.top().type; // if there's no colon on the same line, it's not a label if (topState == expression_or_label) enter(expression); // if not followed by an identifier on the same line, it's done else if (topState == breakcontinue_statement) leave(true); topState = m_currentState.top().type; // some states might be continued on the next line if (topState == expression || topState == expression_or_objectdefinition || topState == objectliteral_assignment || topState == ternary_op_after_colon) { enter(expression_maybe_continuation); } // multi-line comment start? if (topState != multiline_comment_start && topState != multiline_comment_cont && (lexerState & Scanner::MultiLineMask) == Scanner::MultiLineComment) { enter(multiline_comment_start); } saveCurrentState(block); }
void SourceWindow::drawLineInfoArea(QPainter* p, QPaintEvent* event) { QTextBlock block = firstVisibleBlock(); p->setFont(m_lineNoFont); for (; block.isValid(); block = block.next()) { if (!block.isVisible()) continue; QRect r = blockBoundingGeometry(block).translated(contentOffset()).toRect(); if (r.bottom() < event->rect().top()) continue; // skip blocks that are higher than the region being updated else if (r.top() > event->rect().bottom()) break; // all the following blocks are lower then the region being updated int row = block.blockNumber(); uchar item = m_lineItems[row]; int h = r.height(); p->save(); p->translate(0, r.top()); if (item & liBP) { // enabled breakpoint int y = (h - m_brkena.height())/2; if (y < 0) y = 0; p->drawPixmap(0,y,m_brkena); } if (item & liBPdisabled) { // disabled breakpoint int y = (h - m_brkdis.height())/2; if (y < 0) y = 0; p->drawPixmap(0,y,m_brkdis); } if (item & liBPtemporary) { // temporary breakpoint marker int y = (h - m_brktmp.height())/2; if (y < 0) y = 0; p->drawPixmap(0,y,m_brktmp); } if (item & liBPconditional) { // conditional breakpoint marker int y = (h - m_brkcond.height())/2; if (y < 0) y = 0; p->drawPixmap(0,y,m_brkcond); } if (item & liBPorphan) { // orphaned breakpoint marker int y = (h - m_brkcond.height())/2; if (y < 0) y = 0; p->drawPixmap(0,y,m_brkorph); } if (item & liPC) { // program counter in innermost frame int y = (h - m_pcinner.height())/2; if (y < 0) y = 0; p->drawPixmap(0,y,m_pcinner); } if (item & liPCup) { // program counter somewhere up the stack int y = (h - m_pcup.height())/2; if (y < 0) y = 0; p->drawPixmap(0,y,m_pcup); } p->translate(m_widthItems, 0); if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) { int w = m_widthPlus; int x = w/2; int y = h/2; p->drawLine(x-2, y, x+2, y); if (!isRowExpanded(row)) { p->drawLine(x, y-2, x, y+2); } } p->translate(m_widthPlus, 0); if (!isRowDisassCode(row)) { p->drawText(0, 0, m_widthLineNo, h, Qt::AlignRight|Qt::AlignVCenter, QString().setNum(rowToLine(row)+1)); } p->restore(); } }
bool GenericCodeEditor::find( const QRegExp &expr, QTextDocument::FindFlags options ) { // Although QTextDocument provides a find() method, we implement // our own, because the former one is not adequate. if(expr.isEmpty()) return true; bool backwards = options & QTextDocument::FindBackward; QTextCursor c( textCursor() ); int pos; if (c.hasSelection()) { bool matching = expr.exactMatch(c.selectedText()); if( backwards == matching ) pos = c.selectionStart(); else pos = c.selectionEnd(); } else pos = c.position(); QTextDocument *doc = QPlainTextEdit::document(); QTextBlock startBlock = doc->findBlock(pos); int startBlockOffset = pos - startBlock.position(); QTextCursor cursor; if (!backwards) { int blockOffset = startBlockOffset; QTextBlock block = startBlock; while (block.isValid()) { if (findInBlock(doc, block, expr, blockOffset, options, cursor)) break; blockOffset = 0; block = block.next(); } if(cursor.isNull()) { blockOffset = 0; block = doc->begin(); while(true) { if (findInBlock(doc, block, expr, blockOffset, options, cursor) || block == startBlock) break; block = block.next(); } } } else { int blockOffset = startBlockOffset; QTextBlock block = startBlock; while (block.isValid()) { if (findInBlock(doc, block, expr, blockOffset, options, cursor)) break; block = block.previous(); blockOffset = block.length() - 1; } if(cursor.isNull()) { block = doc->end(); while(true) { blockOffset = block.length() - 1; if (findInBlock(doc, block, expr, blockOffset, options, cursor) || block == startBlock) break; block = block.previous(); } } } if(!cursor.isNull()) { setTextCursor(cursor); return true; } else return false; }
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 MainWindow::interpolateButtonClicked() { QString tString = ui->tLineEdit->text(); bool ok; double tValue = tString.toDouble(&ok); if(!ok) ; // if you're too stupid to enter a valid number, you're too stupid // to read a proper error message, so no error message here. // get the input by filtering non-valid input data QTextDocument *doc = ui->dataInput->document(); QStringList inputList; for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) { QString line = QString::fromStdString(it.text().toStdString()); // empty lines and malformed input are skipped if(line != "" && line.contains(QRegExp("-?\\d+(\\.\\d+)?\\s+-?\\d+(\\.\\d+)?"))) inputList << line.trimmed(); else qDebug() << "Skipping line: " << line; } int number_points = inputList.size(); double data[number_points][2]; // split the lines in x and y for(int i=0; i<inputList.size(); i++) { QString point = inputList.at(i); QStringList l = point.split(QRegExp("\\s+")); // this should never happen if(l.size()<2){ qDebug() << "String couldn't be split."; qDebug() << "Can't interpolate."; return; } data[i][0] = l.at(0).toDouble(); data[i][1] = l.at(1).toDouble(); } // the output file QFile output; output.setFileName("results.dat"); output.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream fout(&output); // basic matrix double Mcr[4][4] = { {-0.5, 1.5, -1.5, 0.5}, {1, -2.5, 2, -0.5}, {-0.5, 0, 0.5, 0}, {0, 1, 0, 0} }; double Gbi[4][2]; for(int i=0; i<number_points-1; i++) { // Select the four points required for the calculation. // With the min/max functions the border points are implicitely // duplicated as they wouldn't be interpolated by the algorithm // otherwise. for(int j=0; j<2; j++) { Gbi[0][j] = data[std::max(0, i-1)][j]; Gbi[1][j] = data[i][j]; Gbi[2][j] = data[std::min(number_points-1, i+1)][j]; Gbi[3][j] = data[std::min(number_points-1, i+2)][j]; } // interpolate points between two given points for(double u=0; u<1; u=u+0.001) { double T[4] = {pow(u,3), pow(u,2), u, 1}; double results[2] = {0, 0}; // interpolate one point by calculating T * Mcr * Gbi for(int bi_ctr=0; bi_ctr<2; bi_ctr++) { double sum = 0; for(int point_row=0; point_row<4; point_row++) { for(int mcr_row=0; mcr_row<4; mcr_row++) { sum += T[mcr_row] * Mcr[mcr_row][point_row] * Gbi[point_row][bi_ctr]; } } results[bi_ctr] = sum; } fout << results[0] << " " << results[1] << "\n"; // if the user entered a valid value for x then display y if(ok) { //qDebug() << "ok"; double diff = results[0] - tValue; //qDebug() << tValue << " " << results[0]; qDebug() << diff; if( diff <= 0.001 ) ui->resultLineEdit->setText(QString("%1").arg(results[1])); } } } output.close(); }
void ScCodeEditor::keyPressEvent( QKeyEvent *e ) { hideMouseCursor(e); QTextCursor cursor( textCursor() ); bool cursorMoved = true; if (e == QKeySequence::MoveToNextWord) moveToNextToken( cursor, QTextCursor::MoveAnchor ); else if (e == QKeySequence::MoveToPreviousWord) moveToPreviousToken( cursor, QTextCursor::MoveAnchor ); else if (e == QKeySequence::SelectNextWord) moveToNextToken( cursor, QTextCursor::KeepAnchor ); else if (e == QKeySequence::SelectPreviousWord) moveToPreviousToken( cursor, QTextCursor::KeepAnchor ); else cursorMoved = false; if (cursorMoved) { setTextCursor( cursor ); return; } switch (e->key()) { case Qt::Key_Home: { Qt::KeyboardModifiers mods(e->modifiers()); if (mods && mods != Qt::ShiftModifier) { GenericCodeEditor::keyPressEvent(e); return; } QTextCursor::MoveMode mode = mods & Qt::ShiftModifier ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor; QTextCursor c(textCursor()); QTextBlock b(c.block()); int pos = indentedStartOfLine(b); pos += b.position(); if (c.position() == pos) c.movePosition(QTextCursor::StartOfLine, mode); else c.setPosition(pos, mode); setTextCursor(c); return; } case Qt::Key_Backtab: { QTextCursor cursor = textCursor(); insertSpaceToNextTabStop( cursor ); ensureCursorVisible(); return; } case Qt::Key_Backspace: if (mInsertMatchingTokens && !overwriteMode() && e->modifiers() == 0) if (removeMatchingTokens()) break; GenericCodeEditor::keyPressEvent(e); break; case Qt::Key_Enter: case Qt::Key_Return: { QTextBlock cursorBlock = cursor.block(); int cursorPosInBlock = cursor.position() - cursorBlock.position(); TokenIterator nextToken = TokenIterator::rightOf(cursorBlock, cursorPosInBlock); if ( nextToken.block() == cursorBlock && nextToken.type() == Token::ClosingBracket ) { cursor.beginEditBlock(); cursor.insertBlock(); cursor.insertBlock(); cursor.endEditBlock(); cursor.movePosition( QTextCursor::PreviousBlock, QTextCursor::KeepAnchor ); indent(cursor, JoinEditBlock); cursor.movePosition( QTextCursor::EndOfBlock ); } else { cursor.beginEditBlock(); cursor.insertBlock(); cursor.endEditBlock(); indent(cursor, JoinEditBlock); } cursor.setVerticalMovementX(-1); setTextCursor(cursor); break; } default: if (!overwriteMode() && insertMatchingTokens(e->text())) break; GenericCodeEditor::keyPressEvent(e); } mAutoCompleter->keyPress(e); }
void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { int32 emojiPosition = 0, emojiLen = 0; const EmojiData *emoji = 0; QTextDocument *doc(document()); while (true) { int32 start = position, end = position + charsAdded; QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); if (till.isValid()) till = till.next(); for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 fp = fragment.position(), fe = fp + fragment.length(); if (fp >= end || fe <= start) { continue; } QString t(fragment.text()); const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch) { emoji = emojiFromText(ch, e, emojiLen); if (emoji) { emojiPosition = fp + (ch - t.constData()); break; } if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch; } if (emoji) break; } if (emoji) break; } if (emoji) { QTextCursor c(doc->docHandle(), emojiPosition); c.setPosition(emojiPosition + emojiLen, QTextCursor::KeepAnchor); int32 removedUpto = c.position(); insertEmoji(emoji, c); for (Insertions::iterator i = _insertions.begin(), e = _insertions.end(); i != e; ++i) { if (i->first >= removedUpto) { i->first -= removedUpto - emojiPosition - 1; } else if (i->first >= emojiPosition) { i->second -= removedUpto - emojiPosition; i->first = emojiPosition + 1; } else if (i->first + i->second > emojiPosition + 1) { i->second -= qMin(removedUpto, i->first + i->second) - emojiPosition; } } charsAdded -= removedUpto - position; position = emojiPosition + 1; emoji = 0; emojiPosition = 0; } else { break; } } }
void ScCodeEditor::indent( const QTextCursor & selection, EditBlockMode editBlockMode ) { if (selection.isNull()) return; QTextCursor cursor(selection); if (editBlockMode == NewEditBlock) cursor.beginEditBlock(); else cursor.joinPreviousEditBlock(); QTextDocument *doc = QPlainTextEdit::document(); int startBlockNum = doc->findBlock(cursor.selectionStart()).blockNumber(); int endBlockNum = cursor.hasSelection() ? doc->findBlock(cursor.selectionEnd()).blockNumber() : startBlockNum; QStack<int> stack; int global_level = 0; int blockNum = 0; bool in_string = false; QTextBlock block = QPlainTextEdit::document()->begin(); while (block.isValid()) { int initialStackSize = stack.size(); int level = 0; bool block_start_in_string = in_string; TextBlockData *data = static_cast<TextBlockData*>(block.userData()); if (data) { int count = data->tokens.size(); for (int idx = 0; idx < count; ++idx) { const Token & token = data->tokens[idx]; switch (token.type) { case Token::OpeningBracket: if (token.character != '(' || stack.size() || token.positionInBlock) ++level; break; case Token::ClosingBracket: if (level) --level; else if (global_level) { --global_level; if (!stack.isEmpty() && global_level < stack.top()) stack.pop(); } break; case Token::StringMark: in_string = !in_string; break; default: break; } } } if(blockNum >= startBlockNum) { int indentLevel; if (data && data->tokens.size() && data->tokens[0].type == Token::ClosingBracket) indentLevel = stack.size(); else if (!block_start_in_string) indentLevel = initialStackSize; else indentLevel = 0; block = indent(block, indentLevel); } if(blockNum == endBlockNum) break; block = block.next(); ++blockNum; if (level) { global_level += level; stack.push(global_level); } } cursor.endEditBlock(); }
/*! \fn QString QTextList::itemText(const QTextBlock &block) const Returns the text of the list item that corresponds to the given \a block. */ QString QTextList::itemText(const QTextBlock &blockIt) const { Q_D(const QTextList); int item = d->blocks.indexOf(blockIt) + 1; if (item <= 0) return QString(); QTextBlock block = d->blocks.at(item-1); QTextBlockFormat blockFormat = block.blockFormat(); QString result; const int style = format().style(); QString numberPrefix; QString numberSuffix = QLatin1String("."); if (format().hasProperty(QTextFormat::ListNumberPrefix)) numberPrefix = format().numberPrefix(); if (format().hasProperty(QTextFormat::ListNumberSuffix)) numberSuffix = format().numberSuffix(); switch (style) { case QTextListFormat::ListDecimal: result = QString::number(item); break; // from the old richtext case QTextListFormat::ListLowerAlpha: case QTextListFormat::ListUpperAlpha: { const char baseChar = style == QTextListFormat::ListUpperAlpha ? 'A' : 'a'; int c = item; while (c > 0) { c--; result.prepend(QChar(baseChar + (c % 26))); c /= 26; } } break; case QTextListFormat::ListLowerRoman: case QTextListFormat::ListUpperRoman: { if (item < 5000) { QByteArray romanNumeral; // works for up to 4999 items static const char romanSymbolsLower[] = "iiivixxxlxcccdcmmmm"; static const char romanSymbolsUpper[] = "IIIVIXXXLXCCCDCMMMM"; QByteArray romanSymbols; // wrap to have "mid" if (style == QTextListFormat::ListLowerRoman) romanSymbols = QByteArray::fromRawData(romanSymbolsLower, sizeof(romanSymbolsLower)); else romanSymbols = QByteArray::fromRawData(romanSymbolsUpper, sizeof(romanSymbolsUpper)); int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 }; int n = item; for (int i = 12; i >= 0; n %= c[i], i--) { int q = n / c[i]; if (q > 0) { int startDigit = i + (i+3)/4; int numDigits; if (i % 4) { // c[i] == 4|5|9|40|50|90|400|500|900 if ((i-2) % 4) { // c[i] == 4|9|40|90|400|900 => with subtraction (IV, IX, XL, XC, ...) numDigits = 2; } else { // c[i] == 5|50|500 (V, L, D) numDigits = 1; } } else { // c[i] == 1|10|100|1000 (I, II, III, X, XX, ...) numDigits = q; } romanNumeral.append(romanSymbols.mid(startDigit, numDigits)); } } result = QString::fromLatin1(romanNumeral); } else { result = QLatin1String("?"); } } break; default: Q_ASSERT(false); } if (blockIt.textDirection() == Qt::RightToLeft) return numberSuffix + result + numberPrefix; else return numberPrefix + result + numberSuffix; }
static bool isSingleLineComment(QTextBlock const & block) { static QRegExp commentRegex("^\\s*//.*"); return commentRegex.exactMatch(block.text()); }
/* Removes some nefast constructs from a code line and returns the resulting line. */ QString LineInfo::trimmedCodeLine(const QString &t) { Scanner scanner; QTextBlock currentLine = yyLinizerState.iter; int startState = qMax(0, currentLine.previous().userState()) & 0xff; yyLinizerState.tokens = scanner(t, startState); QString trimmed; int previousTokenEnd = 0; foreach (const Token &token, yyLinizerState.tokens) { trimmed.append(t.midRef(previousTokenEnd, token.begin() - previousTokenEnd)); if (token.is(Token::String)) { for (int i = 0; i < token.length; ++i) trimmed.append(QLatin1Char('X')); } else if (token.is(Token::Comment)) { for (int i = 0; i < token.length; ++i) trimmed.append(QLatin1Char(' ')); } else { trimmed.append(tokenText(token)); } previousTokenEnd = token.end(); } int index = yyLinizerState.tokens.size() - 1; for (; index != -1; --index) { const Token &token = yyLinizerState.tokens.at(index); if (token.isNot(Token::Comment)) break; } bool isBinding = false; foreach (const Token &token, yyLinizerState.tokens) { if (token.is(Token::Colon)) { isBinding = true; break; } } if (index != -1) { const Token &last = yyLinizerState.tokens.at(index); bool needSemicolon = false; switch (last.kind) { case Token::LeftParenthesis: case Token::LeftBrace: case Token::LeftBracket: case Token::Semicolon: case Token::Delimiter: break; case Token::RightParenthesis: case Token::RightBrace: case Token::RightBracket: if (isBinding) needSemicolon = true; break; case Token::String: case Token::Number: case Token::Comma: needSemicolon = true; break; case Token::Identifier: { // need to disambiguate // "Rectangle\n{" in a QML context from // "a = Somevar\n{" in a JS context // What's done here does not cover all cases, but goes as far as possible // with the limited information that's available. const QStringRef text = tokenText(last); if (yyLinizerState.leftBraceFollows && !text.isEmpty() && text.at(0).isUpper()) { int i = index; // skip any preceding 'identifier.'; these could appear in both cases while (i >= 2) { const Token &prev = yyLinizerState.tokens.at(i-1); const Token &prevPrev = yyLinizerState.tokens.at(i-2); if (prev.kind == Token::Dot && prevPrev.kind == Token::Identifier) i -= 2; else break; } // it could also be 'a = \n Foo \n {', but that sounds unlikely if (i == 0) break; // these indicate a QML context const Token &prev = yyLinizerState.tokens.at(i-1); if (prev.kind == Token::Semicolon || prev.kind == Token::Identifier || prev.kind == Token::RightBrace || prev.kind == Token::RightBracket) { break; } } needSemicolon = true; break; } case Token::Keyword: if (tokenText(last) != QLatin1String("else")) needSemicolon = true; break; default: break; } // end of switch if (needSemicolon) { const Token sc(trimmed.size(), 1, Token::Semicolon); yyLinizerState.tokens.append(sc); trimmed.append(QLatin1Char(';')); yyLinizerState.insertedSemicolon = true; } } return trimmed; }
void ScCodeEditor::toggleCommentSelection() { QTextCursor cursor = textCursor(); cursor.beginEditBlock(); if (isBlockOnlySelection(cursor)) { const bool isComment = isSingleLineComment(cursor); QTextCursor selectionCursor(cursor); selectionCursor.setPosition(cursor.selectionStart()); QTextBlock currentBlock = selectionCursor.block(); int firstBlockIndentation = isComment ? 0 : indentationLevel(selectionCursor); do { QTextCursor blockCursor(currentBlock); if (!isComment) addSingleLineComment(blockCursor, firstBlockIndentation); else removeSingleLineComment(blockCursor); currentBlock = currentBlock.next(); } while (currentBlock.isValid() && currentBlock.position() < cursor.selectionEnd()); if (!isComment) { // fix up selection QTextCursor newSelection(cursor); if (cursor.anchor() < cursor.position()) { newSelection.setPosition(newSelection.selectionStart()); newSelection.movePosition(QTextCursor::StartOfBlock); newSelection.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor); } else { newSelection.setPosition(newSelection.selectionEnd()); newSelection.setPosition(cursor.selectionStart(), QTextCursor::KeepAnchor); newSelection.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); } setTextCursor(newSelection); } } else { QString selectionText = cursor.selectedText(); QTextCursor selectionCursor(cursor); if (isSelectionComment(selectionText)) { selectionText = selectionText.trimmed().remove(0, 2); selectionText.chop(2); selectionCursor.insertText(selectionText); } else { selectionText = QString("/*") + selectionText + QString("*/"); selectionCursor.insertText(selectionText); } // fix up selection const int position = selectionCursor.position(); const int anchor = selectionCursor.anchor(); if (position > anchor) { cursor.setPosition(position - selectionText.size()); cursor.setPosition(position, QTextCursor::KeepAnchor); } else { cursor.setPosition(position); cursor.setPosition(position - selectionText.size(), QTextCursor::KeepAnchor); } setTextCursor(cursor); } cursor.endEditBlock(); }
void Utils::unCommentSelection(QPlainTextEdit *edit, CommentFlag flag, const CommentDefinition &definition) { if (!definition.hasSingleLineStyle() && !definition.hasMultiLineStyle()) return; QTextCursor cursor = edit->textCursor(); QTextDocument *doc = cursor.document(); if (!cursor.hasSelection() && (flag == BlockComment) ) { if (definition.hasMultiLineStyle()) { cursor.beginEditBlock(); cursor.insertText(definition.multiLineStart()); cursor.insertText(definition.multiLineEnd()); cursor.movePosition(QTextCursor::Left,QTextCursor::MoveAnchor,definition.multiLineEnd().length()); cursor.endEditBlock(); edit->setTextCursor(cursor); return; } } cursor.beginEditBlock(); int pos = cursor.position(); int anchor = cursor.anchor(); int start = qMin(anchor, pos); int end = qMax(anchor, pos); bool anchorIsStart = (anchor == start); QTextBlock startBlock = doc->findBlock(start); QTextBlock endBlock = doc->findBlock(end); if (end > start && endBlock.position() == end) { --end; endBlock = endBlock.previous(); } bool doMultiLineStyleUncomment = false; bool doMultiLineStyleComment = false; bool doSingleLineStyleUncomment = false; bool hasSelection = cursor.hasSelection(); int firstSpacesOffset = -1; if (hasSelection && definition.hasMultiLineStyle()) { QString startText = startBlock.text(); int startPos = start - startBlock.position(); const int multiLineStartLength = definition.multiLineStart().length(); bool hasLeadingCharacters = !startText.left(startPos).trimmed().isEmpty(); if (startPos >= multiLineStartLength && isComment(startText, startPos - multiLineStartLength, definition, &CommentDefinition::multiLineStart)) { startPos -= multiLineStartLength; start -= multiLineStartLength; } bool hasSelStart = (startPos <= startText.length() - multiLineStartLength && isComment(startText, startPos, definition, &CommentDefinition::multiLineStart)); QString endText = endBlock.text(); int endPos = end - endBlock.position(); const int multiLineEndLength = definition.multiLineEnd().length(); bool hasTrailingCharacters = !endText.left(endPos).remove(definition.singleLine()).trimmed().isEmpty() && !endText.mid(endPos).trimmed().isEmpty(); if (endPos <= endText.length() - multiLineEndLength && isComment(endText, endPos, definition, &CommentDefinition::multiLineEnd)) { endPos += multiLineEndLength; end += multiLineEndLength; } bool hasSelEnd = (endPos >= multiLineEndLength && isComment(endText, endPos - multiLineEndLength, definition, &CommentDefinition::multiLineEnd)); doMultiLineStyleUncomment = hasSelStart && hasSelEnd; doMultiLineStyleComment = !doMultiLineStyleUncomment && (hasLeadingCharacters || hasTrailingCharacters || !definition.hasSingleLineStyle() || (flag == BlockComment)); } else if (!hasSelection && !definition.hasSingleLineStyle()) { QString text = startBlock.text().trimmed(); doMultiLineStyleUncomment = text.startsWith(definition.multiLineStart()) && text.endsWith(definition.multiLineEnd()); doMultiLineStyleComment = !doMultiLineStyleUncomment && !text.isEmpty(); start = startBlock.position(); end = endBlock.position() + endBlock.length() - 1; if (doMultiLineStyleUncomment) { int offset = 0; text = startBlock.text(); const int length = text.length(); while (offset < length && text.at(offset).isSpace()) ++offset; start += offset; } } if (flag == SingleComment) { if (doMultiLineStyleComment) { doMultiLineStyleComment = false; } } if (doMultiLineStyleUncomment) { cursor.setPosition(end); cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, definition.multiLineEnd().length()); cursor.removeSelectedText(); cursor.setPosition(start); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, definition.multiLineStart().length()); cursor.removeSelectedText(); } else if (doMultiLineStyleComment) { cursor.setPosition(end); cursor.insertText(definition.multiLineEnd()); cursor.setPosition(start); cursor.insertText(definition.multiLineStart()); } else { endBlock = endBlock.next(); doSingleLineStyleUncomment = true; for (QTextBlock block = startBlock; block != endBlock; block = block.next()) { QString text = block.text().trimmed(); if (!text.isEmpty() && !text.startsWith(definition.singleLine())) { doSingleLineStyleUncomment = false; break; } } if (!hasSelection && cursor.block().text().isEmpty()) { doSingleLineStyleUncomment = false; } const int singleLineLength = definition.singleLine().length(); for (QTextBlock block = startBlock; block != endBlock; block = block.next()) { if (doSingleLineStyleUncomment) { QString text = block.text(); int i = 0; while (i <= text.size() - singleLineLength) { if (isComment(text, i, definition, &CommentDefinition::singleLine)) { cursor.setPosition(block.position() + i); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, singleLineLength); if (definition.isAfterWhiteSpacesAddSpace()) { if (i < text.size()-singleLineLength) { if (text.at(i+singleLineLength) == 0x0020) { cursor.movePosition(QTextCursor::NextCharacter,QTextCursor::KeepAnchor,1); } } } cursor.removeSelectedText(); break; } if (!text.at(i).isSpace()) break; ++i; } } else { QString text = block.text(); foreach(QChar c, text) { if (!c.isSpace()) { if (definition.isAfterWhiteSpaces()) { int offset = text.indexOf(c); if (firstSpacesOffset != -1 && offset > firstSpacesOffset) { offset = firstSpacesOffset; } cursor.setPosition(block.position() + offset); } else { cursor.setPosition(block.position()); } if (firstSpacesOffset == -1) { firstSpacesOffset = cursor.position()-cursor.block().position(); } if (definition.isAfterWhiteSpaces() && definition.isAfterWhiteSpacesAddSpace()) { cursor.insertText(definition.singleLine()+" "); } else { cursor.insertText(definition.singleLine()); } break; } } } } } // adjust selection when commenting out if (hasSelection && !doMultiLineStyleUncomment && !doSingleLineStyleUncomment) { cursor = edit->textCursor(); if (!doMultiLineStyleComment) start = startBlock.position(); // move the comment into the selection int lastSelPos = anchorIsStart ? cursor.position() : cursor.anchor(); if (anchorIsStart) { cursor.setPosition(start); cursor.setPosition(lastSelPos, QTextCursor::KeepAnchor); } else { cursor.setPosition(lastSelPos); cursor.setPosition(start, QTextCursor::KeepAnchor); } edit->setTextCursor(cursor); } cursor.endEditBlock(); }
void TestDocumentLayout::testNestedPrefixedLists() { /* A list with different prefix for each level should show only the prefix of that level * Specifically we should not concatenate the prefixes of the higher levels * Specifically we should not concatenate the suffixes of the higher levels * That is only the prefix and the suffix of the current level should be applied */ initForNewTest("MMMM\nSSSS\n"); KoParagraphStyle h1; m_styleManager->add(&h1); KoParagraphStyle h2; m_styleManager->add(&h2); KoListStyle listStyle; KoListLevelProperties llp1; llp1.setStartValue(1); llp1.setStyle(KoListStyle::DecimalItem); llp1.setListItemPrefix("Main"); llp1.setListItemSuffix(":"); listStyle.setLevelProperties(llp1); KoListLevelProperties llp2; llp2.setStartValue(1); llp2.setStyle(KoListStyle::DecimalItem); llp2.setLevel(2); llp2.setListItemPrefix("Sub"); llp2.setListItemSuffix("*"); llp2.setDisplayLevel(2); listStyle.setLevelProperties(llp2); h1.setListStyle(&listStyle); h2.setListLevel(2); h2.setListStyle(&listStyle); QVERIFY(listStyle.hasLevelProperties(1)); QVERIFY(listStyle.hasLevelProperties(2)); QVERIFY(!listStyle.hasLevelProperties(3)); QTextBlock block = m_doc->begin().next(); h1.applyStyle(block); block = block.next(); QVERIFY(block.isValid()); h2.applyStyle(block); m_layout->layout(); block = m_doc->begin(); QVERIFY(block.userData() == 0); block = block.next(); static const char *const texts[] = { "Main1:", "Sub1.1*"}; int i = 0; while (block.isValid()) { KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); //qDebug() << "text: " << block.text(); //qDebug() << "expected: " << texts[i]; QVERIFY(data); //qDebug() << data->counterText(); QCOMPARE(data->counterText(), QString(texts[i++])); block = block.next(); } }