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()); }
void ClangFormat::formatAtCursor() { const TextEditor::TextEditorWidget *widget = TextEditor::TextEditorWidget::currentTextEditorWidget(); if (!widget) return; const QTextCursor tc = widget->textCursor(); if (tc.hasSelection()) { const int offset = tc.selectionStart(); const int length = tc.selectionEnd() - offset; BeautifierPlugin::formatCurrentFile(command(offset, length)); } else { // Pretend that the current line was selected. // Note that clang-format will extend the range to the next bigger // syntactic construct if needed. const QTextBlock block = tc.block(); const int offset = block.position(); const int length = block.length(); BeautifierPlugin::formatCurrentFile(command(offset, length)); } }
void XmlWriter::processBlock(QDomElement &parent, const QTextBlock &block) { QDomElement blockElement = document->createElement("block"); blockElement.setAttribute("position", block.position()); blockElement.setAttribute("length", block.length()); parent.appendChild(blockElement); QTextBlock::iterator it; for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment fragment = it.fragment(); if (fragment.isValid()) { QDomElement fragmentElement = document->createElement("fragment"); blockElement.appendChild(fragmentElement); fragmentElement.setAttribute("length", fragment.length()); QDomText fragmentText = document->createTextNode(fragment.text()); fragmentElement.appendChild(fragmentText); } } }
void Changecase::upperCase() { QTextBlock block = m_document->findBlock(m_startPosition); int pos = block.position(); bool finished = false; bool foundToBeChanged = false; while (true) { QString text = block.text(); QString result; QString::ConstIterator constIter = text.constBegin(); while (pos < m_endPosition && constIter != text.constEnd()) { if (pos >= m_startPosition) { if (!foundToBeChanged && constIter->isLower()) foundToBeChanged = true; result.append(constIter->toUpper()); } pos++; constIter++; } if (!(block.isValid() && block.position() + block.length() < m_endPosition)) finished = true; if (foundToBeChanged) { m_cursor.setPosition(qMax(m_startPosition, block.position())); m_cursor.setPosition(qMin(pos, m_endPosition), QTextCursor::KeepAnchor); m_cursor.insertText(result); } if (finished) break; block = block.next(); pos = block.position(); } }
bool QssCompletionAssistProcessor::isInComment() const { QTextBlock block = m_interface->textDocument()->findBlock(m_interface->position()); QTextBlock prevBlock = block.previous(); QList<Token> tokenList; Lexer lexer; lexer.setScanComments(true); if (prevBlock.isValid()) lexer.setState(qssLexerState(prevBlock.userState())); tokenList = lexer.scanMore(block.text()); int index = m_interface->position() - block.position(); Q_FOREACH (const Token &t, tokenList) { if (t.is(Token::Comment) && index >= t.begin() && index < t.end()) { qDebug() << "InComment"; return true; } } if (index == block.length() && (lexer.state() & Lexer::MultiLineComment)) { qDebug() << "InComment"; return true; } return false; }
void QmlJSOutlineWidget::updateTextCursor(const QModelIndex &index) { QModelIndex sourceIndex = m_filterModel->mapToSource(index); AST::SourceLocation location = m_editor->qmlJsEditorDocument()->outlineModel()->sourceLocation(sourceIndex); if (!location.isValid()) return; const QTextBlock lastBlock = m_editor->document()->lastBlock(); const uint textLength = lastBlock.position() + lastBlock.length(); if (location.offset >= textLength) return; Core::EditorManager::cutForwardNavigationHistory(); Core::EditorManager::addCurrentPositionToNavigationHistory(); QTextCursor textCursor = m_editor->textCursor(); m_blockCursorSync = true; textCursor.setPosition(location.offset); m_editor->setTextCursor(textCursor); m_editor->centerCursor(); m_blockCursorSync = false; }
QPair<int, int> getFirstAndLastVisiblePosition(Editor *editor) { QTextCursor cursor = editor->textCursor(); QTextDocument *doc = editor->document(); int currentLine = doc->findBlock(cursor.position()).blockNumber(); int cursorHeight = editor->cursorRect().height(); int lineCountToFirstVisibleLine = editor->cursorRect().top() / cursorHeight; int firstVisibleLineNum = currentLine - lineCountToFirstVisibleLine; if (firstVisibleLineNum < 0) { firstVisibleLineNum = 0; } int maxLineNumOnScreen = (editor->viewport()->height() / cursorHeight); if (maxLineNumOnScreen < 1) { maxLineNumOnScreen = 1; } int firstPos = doc->findBlockByNumber(firstVisibleLineNum).position(); int lastVisibleLineNum = firstVisibleLineNum + maxLineNumOnScreen - 1; QTextBlock lastVisibleTextBlock = doc->findBlockByNumber(lastVisibleLineNum); if (!lastVisibleTextBlock.isValid()) { lastVisibleTextBlock = doc->lastBlock(); } int lastPos = lastVisibleTextBlock.position() + lastVisibleTextBlock.length() - 1; return QPair<int, int>(firstPos, lastPos); }
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; }
void Changecase::sentenceCase() { QTextBlock block = m_document->findBlock(m_startPosition); QTextCursor backCursor(m_cursor); int pos = block.position() + block.length() - 1; // TODO // * Exception? while (true) { QString text = block.text(); int prevLetterIndex = -1; QChar currentWord; pos = block.position() + block.length() - 1; QString::Iterator iter = text.end(); if (text.isEmpty()) { // empty block, go to next block if (!(block.isValid() && block.position() + block.length() < m_endPosition)) break; block = block.next(); continue; } iter--; while (iter != text.begin()) { while (iter != text.begin() && !iter->isSpace()) { iter--; pos--; } prevLetterIndex = pos; currentWord = QChar(*(iter + 1)); while (iter != text.begin() && iter->isSpace()) { iter--; pos--; } // found end of sentence, go back to last found letter (indicating start of a word) if (iter != text.begin() && (*iter == QChar('.') || *iter == QChar('!') || *iter == QChar('?'))) { if (prevLetterIndex >= m_startPosition && prevLetterIndex <= m_endPosition && currentWord.isLower()) { // kDebug() <<"Found end of sentence" << *iter <<" :" << currentWord; m_cursor.setPosition(prevLetterIndex); m_cursor.deleteChar(); m_cursor.insertText(currentWord.toUpper()); iter--; pos--; } else if (prevLetterIndex < m_startPosition) break; } } if (iter == text.begin() && --pos >= m_startPosition) { // start of paragraph, must be start of a sentence also if (pos + 1 == prevLetterIndex && (*iter).isLower()) { m_cursor.setPosition(pos); m_cursor.deleteChar(); m_cursor.insertText((*iter).toUpper()); } else if (!(*iter).isLetter() && currentWord.isLower()) { m_cursor.setPosition(prevLetterIndex); m_cursor.deleteChar(); m_cursor.insertText(currentWord.toUpper()); } } if (!(block.isValid() && block.position() + block.length() < m_endPosition)) break; block = block.next(); } }
int QTextDocumentPrivate::undoRedo(bool undo) { PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size()); if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size())) return -1; undoEnabled = false; beginEditBlock(); while (1) { if (undo) --undoState; QTextUndoCommand &c = undoStack[undoState]; int resetBlockRevision = c.pos; switch(c.command) { case QTextUndoCommand::Inserted: remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation); PMDEBUG(" erase: from %d, length %d", c.pos, c.length); c.command = QTextUndoCommand::Removed; break; case QTextUndoCommand::Removed: PMDEBUG(" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos); insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation); c.command = QTextUndoCommand::Inserted; break; case QTextUndoCommand::BlockInserted: case QTextUndoCommand::BlockAdded: remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation); PMDEBUG(" blockremove: from %d", c.pos); if (c.command == QTextUndoCommand::BlockInserted) c.command = QTextUndoCommand::BlockRemoved; else c.command = QTextUndoCommand::BlockDeleted; break; case QTextUndoCommand::BlockRemoved: case QTextUndoCommand::BlockDeleted: PMDEBUG(" blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos); insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command); resetBlockRevision += 1; if (c.command == QTextUndoCommand::BlockRemoved) c.command = QTextUndoCommand::BlockInserted; else c.command = QTextUndoCommand::BlockAdded; break; case QTextUndoCommand::CharFormatChanged: { resetBlockRevision = -1; // ## TODO PMDEBUG(" charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length); FragmentIterator it = find(c.pos); Q_ASSERT(!it.atEnd()); int oldFormat = it.value()->format; setCharFormat(c.pos, c.length, formats.charFormat(c.format)); c.format = oldFormat; break; } case QTextUndoCommand::BlockFormatChanged: { resetBlockRevision = -1; // ## TODO PMDEBUG(" blockformat: format %d pos %d", c.format, c.pos); QTextBlock it = blocksFind(c.pos); Q_ASSERT(it.isValid()); int oldFormat = block(it)->format; block(it)->format = c.format; QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat))); QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format))); c.format = oldFormat; if (group != oldGroup) { if (oldGroup) oldGroup->blockRemoved(it); if (group) group->blockInserted(it); } else if (group) { group->blockFormatChanged(it); } documentChange(it.position(), it.length()); break; } case QTextUndoCommand::GroupFormatChange: { resetBlockRevision = -1; // ## TODO PMDEBUG(" group format change"); QTextObject *object = objectForIndex(c.objectIndex); int oldFormat = formats.objectFormatIndex(c.objectIndex); changeObjectFormat(object, c.format); c.format = oldFormat; break; } case QTextUndoCommand::Custom: resetBlockRevision = -1; // ## TODO if (undo) c.custom->undo(); else c.custom->redo(); break; default: Q_ASSERT(false); } if (resetBlockRevision >= 0) { int b = blocks.findNode(resetBlockRevision); QTextBlockData *B = blocks.fragment(b); B->revision = c.revision; } if (undo) { if (undoState == 0 || !undoStack[undoState-1].block) break; } else { ++undoState; if (undoState == undoStack.size() || !undoStack[undoState-1].block) break; } } undoEnabled = true; int editPos = -1; if (docChangeFrom >= 0) { editPos = qMin(docChangeFrom + docChangeLength, length() - 1); } endEditBlock(); emitUndoAvailable(isUndoAvailable()); emitRedoAvailable(isRedoAvailable()); return editPos; }
void MarkdownHighlighter::highlight() { if (cached_elements == nullptr) { // Shouldn't happen return; } QHash<int, QList<QTextLayout::FormatRange> > lists; QHash<int, QList<QTextLayout::FormatRange> >::iterator hashIter; QList<QTextBlock> blocks; 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); hashIter = lists.find(block.blockNumber()); if (hashIter == lists.end()) { QList<QTextLayout::FormatRange> emptyList; hashIter = lists.insert(block.blockNumber(), emptyList); blocks.append(block); } 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(); } hashIter.value().append(r); } elem_cursor = elem_cursor->next; } } std::sort(blocks.begin(), blocks.end()); for (auto &block : blocks) { hashIter = lists.find(block.blockNumber()); std::sort(hashIter.value().begin(), hashIter.value().end(), &MarkdownHighlighter::formatLessThan); block.layout()->setAdditionalFormats(hashIter.value()); } document->markContentsDirty(0, document->characterCount()); }
void BaseEditor::paintEvent(QPaintEvent *e) { //copy from QPlainTextEditor QPainter painter(viewport()); Q_ASSERT(qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout())); QPointF offset(contentOffset()); QRect er = e->rect(); QRect viewportRect = viewport()->rect(); bool editable = !isReadOnly(); QTextBlock block = firstVisibleBlock(); qreal maximumWidth = document()->documentLayout()->documentSize().width(); //margin qreal lineX = 0; if (conf->isDisplayRightColumnMargin()) { // Don't use QFontMetricsF::averageCharWidth here, due to it returning // a fractional size even when this is not supported by the platform. lineX = QFontMetricsF(document()->defaultFont()).width(QLatin1Char('X')) * conf->getRightMarginColumn() + offset.x() + 4; if (lineX < viewportRect.width()) { const QBrush background = QBrush(QColor(239, 239, 239)); painter.fillRect(QRectF(lineX, er.top(), viewportRect.width() - lineX, er.height()), background); const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white; const QPen pen = painter.pen(); painter.setPen(blendColors(background.isOpaque() ? background.color() : palette().base().color(), col, 32)); painter.drawLine(QPointF(lineX, er.top()), QPointF(lineX, er.bottom())); painter.setPen(pen); } } // Set a brush origin so that the WaveUnderline knows where the wave started painter.setBrushOrigin(offset); // keep right margin clean from full-width selection int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth) - document()->documentMargin(); er.setRight(qMin(er.right(), maxX)); painter.setClipRect(er); QAbstractTextDocumentLayout::PaintContext context = getPaintContext(); while (block.isValid()) { QRectF r = blockBoundingRect(block).translated(offset); QTextLayout *layout = block.layout(); if (!block.isVisible()) { offset.ry() += r.height(); block = block.next(); continue; } if (r.bottom() >= er.top() && r.top() <= er.bottom()) { QTextBlockFormat blockFormat = block.blockFormat(); QBrush bg = blockFormat.background(); if (bg != Qt::NoBrush) { QRectF contentsRect = r; contentsRect.setWidth(qMax(r.width(), maximumWidth)); fillBackground(&painter, contentsRect, bg); } QVector<QTextLayout::FormatRange> selections; int blpos = block.position(); int bllen = block.length(); for (int i = 0; i < context.selections.size(); ++i) { const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i); const int selStart = range.cursor.selectionStart() - blpos; const int selEnd = range.cursor.selectionEnd() - blpos; if (selStart < bllen && selEnd > 0 && selEnd > selStart) { QTextLayout::FormatRange o; o.start = selStart; o.length = selEnd - selStart; o.format = range.format; selections.append(o); } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection) && block.contains(range.cursor.position())) { // for full width selections we don't require an actual selection, just // a position to specify the line. that's more convenience in usage. QTextLayout::FormatRange o; QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos); o.start = l.textStart(); o.length = l.textLength(); if (o.start + o.length == bllen - 1) ++o.length; // include newline o.format = range.format; selections.append(o); } } bool drawCursor = ((editable || (textInteractionFlags() & Qt::TextSelectableByKeyboard)) && context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen); bool drawCursorAsBlock = drawCursor && overwriteMode() ; if (drawCursorAsBlock) { if (context.cursorPosition == blpos + bllen - 1) { drawCursorAsBlock = false; } else { QTextLayout::FormatRange o; o.start = context.cursorPosition - blpos; o.length = 1; o.format.setForeground(palette().base()); o.format.setBackground(palette().text()); selections.append(o); } } layout->draw(&painter, offset, selections, er); if ((drawCursor && !drawCursorAsBlock) || (editable && context.cursorPosition < -1 && !layout->preeditAreaText().isEmpty())) { int cpos = context.cursorPosition; if (cpos < -1) cpos = layout->preeditAreaPosition() - (cpos + 2); else cpos -= blpos; layout->drawCursor(&painter, offset, cpos, cursorWidth()); } } offset.ry() += r.height(); if (offset.y() > viewportRect.height()) break; block = block.next(); } if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom() && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) { painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background()); } }
void ScCodeEditor::blinkCode( const QTextCursor & c ) { if( !c.document() || !c.hasSelection() ) return; Settings::Manager *settings = Main::settings(); QTextCharFormat evalCodeTextFormat = settings->getThemeVal("evaluatedCode"); QTextDocument *doc = c.document(); int startPos = c.selectionStart(); int endPos = c.selectionEnd(); QTextBlock startBlock = doc->findBlock(startPos); QTextBlock endBlock = doc->findBlock(endPos); startPos -= startBlock.position(); endPos -= endBlock.position(); // Get the bounds of visible blocks within the cursor's selection: QTextBlock block = firstVisibleBlock(); int idx = block.blockNumber(); int sidx = startBlock.blockNumber(); QTextBlock firstBlock, lastBlock; firstBlock = lastBlock = block; QRectF geom = blockBoundingGeometry(block).translated(contentOffset()); qreal top = geom.top(); qreal bottom = top; qreal width=0; while(block.isValid() && bottom < viewport()->rect().height()) { if(block.isVisible()) { QTextLayout *l = block.layout(); QRectF r = l->boundingRect(); bottom += r.height(); if(idx < sidx) { // Block not within the selection. Will skip it. top = bottom; } else { // Block within the selection. width = qMax(width, l->maximumWidth() + r.left()); } } if(block == endBlock) break; block = block.next(); ++idx; if(top == bottom) firstBlock = block; } lastBlock = block; if(bottom == top) { //qDebug("no visible block."); return; } // Construct a pixmap to render the code on: QPixmap pix( QSize(qCeil(width), qCeil(bottom - top)) ); pix.fill(QColor(0,0,0,0)); // Render the visible blocks: QPainter painter(&pix); QVector<QTextLayout::FormatRange> selections; block = firstBlock; int y=0; while( block.isValid() ) { if (block.isVisible()) { QRectF blockRect = block.layout()->boundingRect(); // Use extra char formatting to hide code outside of selection // and modify the appearance of selected code: QTextLayout::FormatRange range; selections.clear(); int start = 0; if(block == startBlock) { range.start = 0; range.length = startPos; range.format.setForeground(QColor(0,0,0,0)); range.format.setBackground(Qt::NoBrush); selections.append(range); start = startPos; } range.start = start; range.length = (block == endBlock ? endPos : block.length() - 1) - range.start; range.format = evalCodeTextFormat; selections.append(range); if(block == endBlock) { range.start = range.start + range.length; range.length = block.length() - 1 - range.start; range.format.setForeground(QColor(0,0,0,0)); range.format.setBackground(Qt::NoBrush); selections.append(range); } block.layout()->draw(&painter, QPointF(0,y), selections); y += blockRect.height(); } if(block == lastBlock) break; block = block.next(); } // Create an overlay item to display the pixmap, and animate it: CodeFragmentOverlay *item = new CodeFragmentOverlay(); item->setPixmap(pix); item->setPos(geom.left(), top); mOverlay->addItem(item); QPropertyAnimation *anim = new QPropertyAnimation(item, "opacity", item); anim->setDuration(mBlinkDuration); anim->setStartValue(1.0); anim->setEndValue(0.0); anim->setEasingCurve( QEasingCurve::InCubic ); anim->start(); connect(anim, SIGNAL(finished()), item, SLOT(deleteLater())); }
void MarkdownHighlighter::resultReady(pmh_element **elements) { QTextBlock block = document()->firstBlock(); while (block.isValid()) { block.layout()->clearAdditionalFormats(); block = block.next(); } if (!elements) { qDebug() << "elements is null"; return; } // QTextDocument::characterCount returns a value one higher than the // actual character count. // See: https://bugreports.qt.nokia.com//browse/QTBUG-4841 // document->toPlainText().length() would give us the correct value // but it's probably too slow. unsigned long max_offset = document()->characterCount() - 1; for (int i = 0; i < highlightingStyles.size(); i++) { HighlightingStyle style = highlightingStyles.at(i); pmh_element *elem_cursor = elements[style.type]; while (elem_cursor != NULL) { unsigned long pos = elem_cursor->pos; unsigned long end = elem_cursor->end; if (end <= pos || max_offset < pos) { elem_cursor = elem_cursor->next; continue; } if (max_offset < end) end = max_offset; // "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(pos).blockNumber(); int endBlockNum = document()->findBlock(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 (/*_makeLinksClickable &&*/ (elem_cursor->type == pmh_LINK || elem_cursor->type == pmh_AUTO_LINK_URL || elem_cursor->type == pmh_AUTO_LINK_EMAIL || elem_cursor->type == pmh_REFERENCE) && elem_cursor->address != NULL) { QString address(elem_cursor->address); if (elem_cursor->type == pmh_AUTO_LINK_EMAIL && !address.startsWith("mailto:")) address = "mailto:" + address; QTextCharFormat linkFormat(r.format); linkFormat.setAnchor(true); linkFormat.setAnchorHref(address); linkFormat.setToolTip(address); r.format = linkFormat; } if (j == startBlockNum) { r.start = pos - blockpos; r.length = (startBlockNum == endBlockNum) ? end - pos : block.length() - r.start; } else if (j == endBlockNum) { r.start = 0; r.length = 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()); pmh_free_elements(elements); }
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(); }
QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect &clip) { if (begin.isNull() || end.isNull() || begin.position() > end.position()) return QPainterPath(); QPointF offset = m_editor->contentOffset(); QRect viewportRect = rect(); QTextDocument *document = m_editor->document(); if (m_editor->blockBoundingGeometry(begin.block()).translated(offset).top() > clip.bottom() + 10 || m_editor->blockBoundingGeometry(end.block()).translated(offset).bottom() < clip.top() - 10 ) return QPainterPath(); // nothing of the selection is visible QTextBlock block = begin.block(); if (block.blockNumber() < m_editor->firstVisibleBlock().blockNumber() - 4) block = m_editor->document()->findBlockByNumber(m_editor->firstVisibleBlock().blockNumber() - 4); bool inSelection = false; QVector<QRectF> selection; if (begin.position() == end.position()) { // special case empty selections const QRectF blockGeometry = m_editor->blockBoundingGeometry(block); QTextLayout *blockLayout = block.layout(); int pos = begin.position() - begin.block().position(); QTextLine line = blockLayout->lineForTextPosition(pos); QRectF lineRect = line.naturalTextRect(); int x = line.cursorToX(pos); lineRect.setLeft(x - m_borderWidth); lineRect.setRight(x + m_borderWidth); selection += lineRect.translated(blockGeometry.topLeft()); } else { for (; block.isValid() && block.blockNumber() <= end.blockNumber(); block = block.next()) { if (! block.isVisible()) continue; const QRectF blockGeometry = m_editor->blockBoundingGeometry(block); QTextLayout *blockLayout = block.layout(); QTextLine line = blockLayout->lineAt(0); bool firstOrLastBlock = false; int beginChar = 0; if (!inSelection) { if (block == begin.block()) { beginChar = begin.positionInBlock(); line = blockLayout->lineForTextPosition(beginChar); firstOrLastBlock = true; } inSelection = true; } else { // while (beginChar < block.length() && document->characterAt(block.position() + beginChar).isSpace()) // ++beginChar; // if (beginChar == block.length()) // beginChar = 0; } int lastLine = blockLayout->lineCount()-1; int endChar = -1; if (block == end.block()) { endChar = end.positionInBlock(); lastLine = blockLayout->lineForTextPosition(endChar).lineNumber(); inSelection = false; firstOrLastBlock = true; } else { endChar = block.length(); while (endChar > beginChar && document->characterAt(block.position() + endChar - 1).isSpace()) --endChar; } QRectF lineRect = line.naturalTextRect(); if (beginChar < endChar) { lineRect.setLeft(line.cursorToX(beginChar)); if (line.lineNumber() == lastLine) lineRect.setRight(line.cursorToX(endChar)); selection += lineRect.translated(blockGeometry.topLeft()); for (int lineIndex = line.lineNumber()+1; lineIndex <= lastLine; ++lineIndex) { line = blockLayout->lineAt(lineIndex); lineRect = line.naturalTextRect(); if (lineIndex == lastLine) lineRect.setRight(line.cursorToX(endChar)); selection += lineRect.translated(blockGeometry.topLeft()); } } else { // empty lines const int emptyLineSelectionSize = 16; if (!firstOrLastBlock && !selection.isEmpty()) { // middle lineRect.setLeft(selection.last().left()); } else if (inSelection) { // first line lineRect.setLeft(line.cursorToX(beginChar)); } else { // last line if (endChar == 0) break; lineRect.setLeft(line.cursorToX(endChar) - emptyLineSelectionSize); } lineRect.setRight(lineRect.left() + emptyLineSelectionSize); selection += lineRect.translated(blockGeometry.topLeft()); } if (!inSelection) break; if (blockGeometry.translated(offset).y() > 2*viewportRect.height()) break; } } if (selection.isEmpty()) return QPainterPath(); QVector<QPointF> points; const int margin = m_borderWidth/2; const int extra = 0; const QRectF &firstSelection = selection.at(0); points += (firstSelection.topLeft() + firstSelection.topRight()) / 2 + QPointF(0, -margin); points += firstSelection.topRight() + QPointF(margin+1, -margin); points += firstSelection.bottomRight() + QPointF(margin+1, 0); const int count = selection.count(); for (int i = 1; i < count-1; ++i) { #define MAX3(a,b,c) qMax(a, qMax(b,c)) qreal x = MAX3(selection.at(i-1).right(), selection.at(i).right(), selection.at(i+1).right()) + margin; points += QPointF(x+1, selection.at(i).top()); points += QPointF(x+1, selection.at(i).bottom()); } const QRectF &lastSelection = selection.at(count-1); points += lastSelection.topRight() + QPointF(margin+1, 0); points += lastSelection.bottomRight() + QPointF(margin+1, margin+extra); points += lastSelection.bottomLeft() + QPointF(-margin, margin+extra); points += lastSelection.topLeft() + QPointF(-margin, 0); for (int i = count-2; i > 0; --i) { #define MIN3(a,b,c) qMin(a, qMin(b,c)) qreal x = MIN3(selection.at(i-1).left(), selection.at(i).left(), selection.at(i+1).left()) - margin; points += QPointF(x, selection.at(i).bottom()+extra); points += QPointF(x, selection.at(i).top()); } points += firstSelection.bottomLeft() + QPointF(-margin, extra); points += firstSelection.topLeft() + QPointF(-margin, -margin); QPainterPath path; const int corner = 4; path.moveTo(points.at(0)); points += points.at(0); QPointF previous = points.at(0); for (int i = 1; i < points.size(); ++i) { QPointF point = points.at(i); if (point.y() == previous.y() && qAbs(point.x() - previous.x()) > 2*corner) { QPointF tmp = QPointF(previous.x() + corner * ((point.x() > previous.x())?1:-1), previous.y()); path.quadTo(previous, tmp); previous = tmp; i--; continue; } else if (point.x() == previous.x() && qAbs(point.y() - previous.y()) > 2*corner) { QPointF tmp = QPointF(previous.x(), previous.y() + corner * ((point.y() > previous.y())?1:-1)); path.quadTo(previous, tmp); previous = tmp; i--; continue; } QPointF target = (previous + point) / 2; path.quadTo(previous, target); previous = points.at(i); } path.closeSubpath(); path.translate(offset); return path; }
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; }
QString ToCGenerator::fetchBookmarkRef(const QTextBlock &block, KoTextRangeManager *textRangeManager) { QHash<int, KoTextRange *> ranges = textRangeManager->textRangesChangingWithin(block.document(), block.position(), block.position() + block.length(), block.position(), block.position() + block.length()); foreach (KoTextRange *range, ranges) { KoBookmark *bookmark = dynamic_cast<KoBookmark *>(range); if (bookmark) { return bookmark->name(); } }
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 }
bool GolangTextLexer::isInImportHelper(const QTextCursor &cursor) const { const int blockNumber = cursor.block().blockNumber(); QTextBlock block = cursor.document()->firstBlock(); int pos1 = -1; while (block.isValid()) { QString text = block.text().trimmed(); if (text.startsWith("/*")) { block = block.next(); while(block.isValid()) { if (block.text().endsWith("*/")) { break; } block = block.next(); } if (!block.isValid()) { break; } } else if (text.startsWith("var")) { break; } else if (text.startsWith("func")) { break; } else if (text.startsWith("package ")) { pos1 = block.position()+block.length(); } else if (pos1 != -1 && text.startsWith("import (")) { block = block.next(); while(block.isValid()) { QString text = block.text().trimmed(); if (text.startsWith(")")) { break; } //skip if (text.startsWith("/*")) { block = block.next(); while(block.isValid()) { if (block.text().endsWith("*/")) { break; } block = block.next(); } if (!block.isValid()) { break; } } if (text.startsWith("//")) { block = block.next(); continue; } if (block.blockNumber() == blockNumber) { return true; } block = block.next(); } } else if (pos1 != -1 && text.startsWith("import ")) { if (block.blockNumber() == blockNumber) { return true; } } block = block.next(); } return false; }