Newer
Older
&& nextBlockUserData->hasClosingCollapseInside()
&& nextBlockUserData->ifdefedOut();
bool drawBox = ((collapseAfter || collapseNext) && !nextHasClosingCollapse);
bool active = blockNumber == extraAreaHighlightCollapseBlockNumber;
bool drawStart = drawBox && active;
bool drawEnd = blockNumber == extraAreaHighlightCollapseEndBlockNumber || (drawStart && !endIsVisible);
bool hovered = blockNumber >= extraAreaHighlightCollapseBlockNumber
&& blockNumber <= extraAreaHighlightCollapseEndBlockNumber;
int boxWidth = collapseBoxWidth(fm);
if (hovered) {
QRect box = QRect(extraAreaWidth + 1, top, boxWidth - 2, bottom - top);
drawRectBox(&painter, box, drawStart, drawEnd, pal);
bool expanded = nextBlock.isVisible();
int size = boxWidth/4;
QRect box(extraAreaWidth + size, top + size,
2 * (size) + 1, 2 * (size) + 1);
drawFoldingMarker(&painter, pal, box, expanded, active, hovered);
}
painter.restore();
}
if (d->m_revisionsVisible && block.revision() != documentLayout->lastSaveRevision) {
painter.save();
painter.setRenderHint(QPainter::Antialiasing, false);
if (block.revision() < 0)
painter.setPen(QPen(Qt::darkGreen, 2));
else
painter.setPen(QPen(Qt::red, 2));
painter.drawLine(extraAreaWidth - 1, top, extraAreaWidth - 1, bottom - 1);
painter.restore();
}
if (d->m_lineNumbersVisible) {
const QString &number = QString::number(blockNumber + 1);

mae
committed
(selStart < block.position() + block.length()
&& selEnd > block.position())
|| (selStart == selEnd && selStart == block.position())
QFont f = painter.font();
f.setBold(d->m_currentLineNumberFormat.font().bold());
f.setItalic(d->m_currentLineNumberFormat.font().italic());
painter.setFont(f);
painter.setPen(d->m_currentLineNumberFormat.foreground().color());
painter.drawText(markWidth, top, extraAreaWidth - markWidth - 4, fm.height(), Qt::AlignRight, number);
}
block = nextVisibleBlock;
blockNumber = nextVisibleBlockNumber;
}
}
void BaseTextEditor::drawFoldingMarker(QPainter *painter, const QPalette &pal,
const QRect &rect,
bool expanded,
bool active,
bool hovered) const
Q_UNUSED(active)
Q_UNUSED(hovered)
QStyle *s = style();
if (ManhattanStyle *ms = qobject_cast<ManhattanStyle*>(s))
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
if (!qstrcmp(s->metaObject()->className(), "OxygenStyle")) {
painter->save();
painter->setPen(Qt::NoPen);
int size = rect.size().width();
int sqsize = 2*(size/2);
QColor textColor = pal.buttonText().color();
QColor brushColor = textColor;
textColor.setAlpha(100);
brushColor.setAlpha(100);
QPolygon a;
if (expanded) {
// down arrow
a.setPoints(3, 0, sqsize/3, sqsize/2, sqsize - sqsize/3, sqsize, sqsize/3);
} else {
// right arrow
a.setPoints(3, sqsize - sqsize/3, sqsize/2, sqsize/2 - sqsize/3, 0, sqsize/2 - sqsize/3, sqsize);
painter->setBrush(brushColor);
}
painter->translate(0.5, 0.5);
painter->setRenderHint(QPainter::Antialiasing);
painter->translate(rect.topLeft());
painter->setPen(textColor);
painter->setBrush(textColor);
painter->drawPolygon(a);
painter->restore();
QStyleOptionViewItemV2 opt;
opt.rect = rect;
opt.state = QStyle::State_Active | QStyle::State_Item | QStyle::State_Children;
if (expanded)
opt.state |= QStyle::State_Open;
if (active)
opt.state |= QStyle::State_MouseOver | QStyle::State_Enabled | QStyle::State_Selected;
if (hovered)
opt.palette.setBrush(QPalette::Window, pal.highlight());
// QGtkStyle needs a small correction to draw the marker in the right place
if (!qstrcmp(s->metaObject()->className(), "QGtkStyle"))
opt.rect.translate(-2, 0);
else if (!qstrcmp(s->metaObject()->className(), "QMacStyle"))
opt.rect.translate(-1, 0);
s->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
void BaseTextEditor::slotModificationChanged(bool m)
{
if (m)
return;
QTextDocument *doc = document();
TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout());
QTC_ASSERT(documentLayout, return);
int oldLastSaveRevision = documentLayout->lastSaveRevision;
documentLayout->lastSaveRevision = doc->revision();
if (oldLastSaveRevision != documentLayout->lastSaveRevision) {
QTextBlock block = doc->begin();
while (block.isValid()) {
if (block.revision() < 0 || block.revision() != oldLastSaveRevision) {
block.setRevision(-documentLayout->lastSaveRevision - 1);
} else {
block.setRevision(documentLayout->lastSaveRevision);
}
block = block.next();
}
}
d->m_extraArea->update();
}
void BaseTextEditor::slotUpdateRequest(const QRect &r, int dy)
{
if (dy)
d->m_extraArea->scroll(0, dy);
else if (r.width() > 4) { // wider than cursor width, not just cursor blinking
d->m_extraArea->update(0, r.y(), d->m_extraArea->width(), r.height());
if (!d->m_searchExpr.isEmpty()) {
const int m = d->m_searchResultOverlay->dropShadowWidth();
viewport()->update(r.adjusted(-m, -m, m, m));
}
}
if (r.contains(viewport()->rect()))
slotUpdateExtraAreaWidth();
}
void BaseTextEditor::saveCurrentCursorPositionForNavigation()
{
d->m_lastCursorChangeWasInteresting = true;
d->m_tempNavigationState = saveState();
void BaseTextEditor::updateCurrentLineHighlight()
{
QList<QTextEdit::ExtraSelection> extraSelections;
if (d->m_highlightCurrentLine) {
QTextEdit::ExtraSelection sel;
sel.format.setBackground(d->m_currentLineFormat.background());
sel.format.setProperty(QTextFormat::FullWidthSelection, true);
sel.cursor = textCursor();
sel.cursor.clearSelection();
extraSelections.append(sel);
}
setExtraSelections(CurrentLineSelection, extraSelections);
// the extra area shows information for the entire current block, not just the currentline.
// This is why we must force a bigger update region.
int cursorBlockNumber = textCursor().blockNumber();
if (cursorBlockNumber != d->m_cursorBlockNumber) {
QPointF offset = contentOffset();
QTextBlock block = document()->findBlockByNumber(d->m_cursorBlockNumber);
if (block.isValid())
d->m_extraArea->update(blockBoundingGeometry(block).translated(offset).toAlignedRect());
block = document()->findBlockByNumber(cursorBlockNumber);
if (block.isValid())
d->m_extraArea->update(blockBoundingGeometry(block).translated(offset).toAlignedRect());
d->m_cursorBlockNumber = cursorBlockNumber;
}
}
void BaseTextEditor::slotCursorPositionChanged()
{
#if 0
qDebug() << "block" << textCursor().blockNumber()+1
<< "depth:" << TextEditDocumentLayout::braceDepth(textCursor().block())
<< '/' << TextEditDocumentLayout::braceDepth(document()->lastBlock());
if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) {
Core::EditorManager::instance()->addCurrentPositionToNavigationHistory(editableInterface(), d->m_tempNavigationState);
d->m_lastCursorChangeWasInteresting = false;
} else if (d->m_contentsChanged) {
saveCurrentCursorPositionForNavigation();
}
if (d->m_parenthesesMatchingEnabled && hasFocus()) {
// Delay update when no matching is displayed yet, to avoid flicker
if (extraSelections(ParenthesesMatchingSelection).isEmpty()
&& d->m_animator == 0) {
d->m_parenthesesMatchingTimer->start(50);
} else {
// use 0-timer, not direct call, to give the syntax highlighter a chance
// to update the parantheses information
d->m_parenthesesMatchingTimer->start(0);
}
}
updateCurrentLineHighlight();
if (d->m_displaySettings.m_highlightBlocks) {
QTextCursor cursor = textCursor();
d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber();
d->extraAreaHighlightCollapseColumn = cursor.position() - cursor.block().position();
d->m_highlightBlocksTimer->start(100);
}
}
void BaseTextEditor::slotUpdateBlockNotify(const QTextBlock &block)
{
static bool blockRecursion = false;
if (blockRecursion)
return;
blockRecursion = true;
if (d->m_overlay->isVisible()) {
/* an overlay might draw outside the block bounderies, force
complete viewport update */
viewport()->update();
} else {
if (block.previous().isValid() && block.userState() != block.previous().userState()) {
/* The syntax highlighting state changes. This opens up for
the possibility that the paragraph has braces that support
code folding. In this case, do the save thing and also
update the previous block, which might contain a collapse
box which now is invalid.*/
emit requestBlockUpdate(block.previous());
}
if (!d->m_findScope.isNull()) {
if (block.position() < d->m_findScope.selectionEnd()
&& block.position()+block.length() >= d->m_findScope.selectionStart() ) {
QTextBlock b = block.document()->findBlock(d->m_findScope.selectionStart());
do {
emit requestBlockUpdate(b);
b = b.next();
} while (b.isValid() && b.position() < d->m_findScope.selectionEnd());
}
}
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
void BaseTextEditor::timerEvent(QTimerEvent *e)
{
if (e->timerId() == d->autoScrollTimer.timerId()) {
const QPoint globalPos = QCursor::pos();
const QPoint pos = d->m_extraArea->mapFromGlobal(globalPos);
QRect visible = d->m_extraArea->rect();
verticalScrollBar()->triggerAction( pos.y() < visible.center().y() ?
QAbstractSlider::SliderSingleStepSub
: QAbstractSlider::SliderSingleStepAdd);
QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
extraAreaMouseEvent(&ev);
int delta = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
if (delta < 7)
delta = 7;
int timeout = 4900 / (delta * delta);
d->autoScrollTimer.start(timeout, this);
} else if (e->timerId() == d->collapsedBlockTimer.timerId()) {
d->visibleCollapsedBlockNumber = d->suggestedVisibleCollapsedBlockNumber;
d->suggestedVisibleCollapsedBlockNumber = -1;
d->collapsedBlockTimer.stop();
viewport()->update();
}
QPlainTextEdit::timerEvent(e);
}
void BaseTextEditorPrivate::clearVisibleCollapsedBlock()
{
if (suggestedVisibleCollapsedBlockNumber) {
suggestedVisibleCollapsedBlockNumber = -1;
collapsedBlockTimer.stop();
}
if (visibleCollapsedBlockNumber >= 0) {
visibleCollapsedBlockNumber = -1;
q->viewport()->update();
}
}
void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
{
d->m_lastEventWasBlockSelectionEvent = (e->modifiers() & Qt::AltModifier);
updateLink(e);
if (e->buttons() == Qt::NoButton) {
const QTextBlock collapsedBlock = collapsedBlockAt(e->pos());
const int blockNumber = collapsedBlock.next().blockNumber();
if (blockNumber < 0) {
d->clearVisibleCollapsedBlock();
} else if (blockNumber != d->visibleCollapsedBlockNumber) {
d->suggestedVisibleCollapsedBlockNumber = blockNumber;
d->collapsedBlockTimer.start(40, this);
}
// Update the mouse cursor
if (collapsedBlock.isValid() && !d->m_mouseOnCollapsedMarker) {
d->m_mouseOnCollapsedMarker = true;
viewport()->setCursor(Qt::PointingHandCursor);
} else if (!collapsedBlock.isValid() && d->m_mouseOnCollapsedMarker) {
d->m_mouseOnCollapsedMarker = false;
viewport()->setCursor(Qt::IBeamCursor);
}
} else {
QPlainTextEdit::mouseMoveEvent(e);
}
if (d->m_lastEventWasBlockSelectionEvent && d->m_inBlockSelectionMode) {
if (textCursor().atBlockEnd()) {
d->m_blockSelectionExtraX = qMax(0, e->pos().x() - cursorRect().center().x()) / fontMetrics().averageCharWidth();
if (viewport()->cursor().shape() == Qt::BlankCursor)
viewport()->setCursor(Qt::IBeamCursor);
}
void BaseTextEditor::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) {
d->clearBlockSelection(); // just in case, otherwise we might get strange drag and drop
QTextBlock collapsedBlock = collapsedBlockAt(e->pos());
if (collapsedBlock.isValid()) {
toggleBlockVisible(collapsedBlock);
viewport()->setCursor(Qt::IBeamCursor);
}
updateLink(e);
if (d->m_currentLink.isValid())
d->m_linkPressed = true;
void BaseTextEditor::mouseReleaseEvent(QMouseEvent *e)
{
if (mouseNavigationEnabled()
&& d->m_linkPressed
&& e->modifiers() & Qt::ControlModifier
&& !(e->modifiers() & Qt::ShiftModifier)
&& e->button() == Qt::LeftButton
) {
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
const QTextCursor cursor = cursorForPosition(e->pos());
if (openLink(findLinkAt(cursor))) {
clearLink();
return;
}
}
QPlainTextEdit::mouseReleaseEvent(e);
}
void BaseTextEditor::leaveEvent(QEvent *e)
{
// Clear link emulation when the mouse leaves the editor
clearLink();
QPlainTextEdit::leaveEvent(e);
}
void BaseTextEditor::keyReleaseEvent(QKeyEvent *e)
{
// Clear link emulation when Ctrl is released
if (e->key() == Qt::Key_Control)
clearLink();
QPlainTextEdit::keyReleaseEvent(e);
}
void BaseTextEditor::dragEnterEvent(QDragEnterEvent *e)
{
// If the drag event contains URLs, we don't want to insert them as text
if (e->mimeData()->hasUrls()) {
e->ignore();
return;
}
QPlainTextEdit::dragEnterEvent(e);
}
// fake missing mouse move event from Qt
QMouseEvent me(QEvent::MouseMove, QPoint(-1, -1), Qt::NoButton, 0, 0);
extraAreaMouseEvent(&me);
}
void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e)
{
QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
cursor.setPosition(cursor.block().position());
int markWidth;
extraAreaWidth(&markWidth);
if (d->m_codeFoldingVisible
&& e->type() == QEvent::MouseMove && e->buttons() == 0) { // mouse tracking
// Update which folder marker is highlighted
const int highlightBlockNumber = d->extraAreaHighlightCollapseBlockNumber;
const int highlightColumn = d->extraAreaHighlightCollapseColumn;
d->extraAreaHighlightCollapseColumn = -1;
if (e->pos().x() > extraArea()->width() - collapseBoxWidth(fontMetrics())) {
d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber();
if (TextBlockUserData::canCollapse(cursor.block())
|| !TextBlockUserData::hasClosingCollapse(cursor.block()))
d->extraAreaHighlightCollapseColumn = cursor.block().length()-1;
if (TextBlockUserData::hasCollapseAfter(cursor.block())) {
d->extraAreaHighlightCollapseBlockNumber++;
d->extraAreaHighlightCollapseColumn = -1;
if (TextBlockUserData::canCollapse(cursor.block().next())
|| !TextBlockUserData::hasClosingCollapse(cursor.block().next()))
d->extraAreaHighlightCollapseColumn = cursor.block().next().length()-1;
}
} else if (d->m_displaySettings.m_highlightBlocks) {
QTextCursor cursor = textCursor();
d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber();
d->extraAreaHighlightCollapseColumn = cursor.position() - cursor.block().position();
if (highlightBlockNumber != d->extraAreaHighlightCollapseBlockNumber
|| highlightColumn != d->extraAreaHighlightCollapseColumn) {
d->m_highlightBlocksTimer->start(d->m_highlightBlocksInfo.isEmpty() ? 120 : 0);
}

Thorbjørn Lindeijer
committed
// Set whether the mouse cursor is a hand or normal arrow
if (e->type() == QEvent::MouseMove) {
bool hand = (e->pos().x() <= markWidth);
if (hand != (d->m_extraArea->cursor().shape() == Qt::PointingHandCursor))
d->m_extraArea->setCursor(hand ? Qt::PointingHandCursor : Qt::ArrowCursor);
}
if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) {
if (e->button() == Qt::LeftButton) {
int boxWidth = collapseBoxWidth(fontMetrics());
if (d->m_codeFoldingVisible && e->pos().x() > extraArea()->width() - boxWidth) {
if (!cursor.block().next().isVisible()) {
toggleBlockVisible(cursor.block());
d->moveCursorVisible(false);
} else if (collapseBox().contains(e->pos())) {
cursor.setPosition(
document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last()).position()
);
QTextBlock c = cursor.block();
if (TextBlockUserData::hasCollapseAfter(c.previous()))
c = c.previous();
toggleBlockVisible(c);
d->moveCursorVisible(false);
}
} else if (d->m_marksVisible && e->pos().x() > markWidth) {
QTextCursor selection = cursor;
selection.setVisualNavigation(true);
d->extraAreaSelectionAnchorBlockNumber = selection.blockNumber();
selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
setTextCursor(selection);
} else {
d->extraAreaToggleMarkBlockNumber = cursor.blockNumber();
}
} else if (d->m_marksVisible && e->button() == Qt::RightButton) {
QMenu * contextMenu = new QMenu(this);
emit d->m_editable->markContextMenuRequested(editableInterface(), cursor.blockNumber() + 1, contextMenu);
if (!contextMenu->isEmpty())
contextMenu->exec(e->globalPos());
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
}
} else if (d->extraAreaSelectionAnchorBlockNumber >= 0) {
QTextCursor selection = cursor;
selection.setVisualNavigation(true);
if (e->type() == QEvent::MouseMove) {
QTextBlock anchorBlock = document()->findBlockByNumber(d->extraAreaSelectionAnchorBlockNumber);
selection.setPosition(anchorBlock.position());
if (cursor.blockNumber() < d->extraAreaSelectionAnchorBlockNumber) {
selection.movePosition(QTextCursor::EndOfBlock);
selection.movePosition(QTextCursor::Right);
}
selection.setPosition(cursor.block().position(), QTextCursor::KeepAnchor);
if (cursor.blockNumber() >= d->extraAreaSelectionAnchorBlockNumber) {
selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
}
if (e->pos().y() >= 0 && e->pos().y() <= d->m_extraArea->height())
d->autoScrollTimer.stop();
else if (!d->autoScrollTimer.isActive())
d->autoScrollTimer.start(100, this);
} else {
d->autoScrollTimer.stop();
d->extraAreaSelectionAnchorBlockNumber = -1;
return;
}
setTextCursor(selection);
} else if (d->extraAreaToggleMarkBlockNumber >= 0 && d->m_marksVisible && d->m_requestMarkEnabled) {
if (e->type() == QEvent::MouseButtonRelease && e->button() == Qt::LeftButton) {
int n = d->extraAreaToggleMarkBlockNumber;
d->extraAreaToggleMarkBlockNumber = -1;
if (cursor.blockNumber() == n) {
int line = n + 1;
emit d->m_editable->markRequested(editableInterface(), line);
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
}
}
}
}
QTextBlock TextBlockUserData::testCollapse(const QTextBlock& block)
{
QTextBlock info = block;
if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter)
;
else if (block.next().userData()
&& static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
== TextBlockUserData::CollapseThis)
info = block.next();
else
return QTextBlock();
int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos();
if (pos < 0)
return QTextBlock();
QTextCursor cursor(info);
cursor.setPosition(cursor.position() + pos);
matchCursorForward(&cursor);
return cursor.block();
}
void TextBlockUserData::doCollapse(const QTextBlock& block, bool visible)
{
QTextBlock info = block;
if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter)
;
else if (block.next().userData()
&& static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
== TextBlockUserData::CollapseThis)
info = block.next();
else {
if (visible && !block.next().isVisible()) {
// no match, at least unfold!
QTextBlock b = block.next();
while (b.isValid() && !b.isVisible()) {
b.setVisible(true);
b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
b = b.next();
}
}
return;
}
int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos();
if (pos < 0)
return;
QTextCursor cursor(info);
cursor.setPosition(cursor.position() + pos);
if (matchCursorForward(&cursor) != Match) {
if (visible) {
// no match, at least unfold!
QTextBlock b = block.next();
while (b.isValid() && !b.isVisible()) {
b.setVisible(true);
b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
b = b.next();
}
}
return;
}
QTextBlock b = block.next();
while (b < cursor.block()) {
b.setVisible(visible);
b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
if (visible) {
TextBlockUserData *data = canCollapse(b);
if (data && data->collapsed()) {
QTextBlock end = testCollapse(b);
if (data->collapseIncludesClosure())
end = end.next();
if (end.isValid()) {
b = end;
continue;
}
}
}
b = b.next();
}
bool collapseIncludesClosure = hasClosingCollapseAtEnd(b);
if (collapseIncludesClosure) {
b.setVisible(visible);
b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
}
static_cast<TextBlockUserData*>(info.userData())->setCollapseIncludesClosure(collapseIncludesClosure);
static_cast<TextBlockUserData*>(info.userData())->setCollapsed(!block.next().isVisible());
}
void BaseTextEditor::ensureCursorVisible()
{
QTextBlock block = textCursor().block();
if (!block.isVisible()) {
while (!block.isVisible() && block.previous().isValid())
block = block.previous();
toggleBlockVisible(block);
}
QPlainTextEdit::ensureCursorVisible();
}
void BaseTextEditor::toggleBlockVisible(const QTextBlock &block)
{
TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document()->documentLayout());
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
bool visible = block.next().isVisible();
TextBlockUserData::doCollapse(block, !visible);
documentLayout->requestUpdate();
documentLayout->emitDocumentSizeChanged();
}
const TabSettings &BaseTextEditor::tabSettings() const
{
return d->m_document->tabSettings();
}
const DisplaySettings &BaseTextEditor::displaySettings() const
{
return d->m_displaySettings;
}
void BaseTextEditor::indentOrUnindent(bool doIndent)
{
QTextCursor cursor = textCursor();
cursor.beginEditBlock();
int pos = cursor.position();
const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings();
QTextDocument *doc = document();
if (!cursor.hasSelection() && doIndent) {
// Insert tab if there is no selection and indent is requested
QTextBlock block = cursor.block();
QString text = block.text();
int indentPosition = (cursor.position() - block.position());;
int spaces = tabSettings.spacesLeftFromPosition(text, indentPosition);
int startColumn = tabSettings.columnAt(text, indentPosition - spaces);
int targetColumn = tabSettings.indentedColumn(tabSettings.columnAt(text, indentPosition), doIndent);
cursor.setPosition(block.position() + indentPosition);
cursor.setPosition(block.position() + indentPosition - spaces, QTextCursor::KeepAnchor);
cursor.removeSelectedText();

Roopesh Chander
committed
cursor.insertText(tabSettings.indentationString(startColumn, targetColumn, block));
// Indent or unindent the selected lines
int anchor = cursor.anchor();
int start = qMin(anchor, pos);
int end = qMax(anchor, pos);
QTextBlock startBlock = doc->findBlock(start);
QTextBlock endBlock = doc->findBlock(end-1).next();
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
QString text = block.text();
int indentPosition = tabSettings.lineIndentPosition(text);
if (!doIndent && !indentPosition)
indentPosition = tabSettings.firstNonSpace(text);
int targetColumn = tabSettings.indentedColumn(tabSettings.columnAt(text, indentPosition), doIndent);
cursor.setPosition(block.position() + indentPosition);

Roopesh Chander
committed
cursor.insertText(tabSettings.indentationString(0, targetColumn, block));
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
cursor.setPosition(block.position());
cursor.setPosition(block.position() + indentPosition, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
}
}
cursor.endEditBlock();
}
void BaseTextEditor::handleHomeKey(bool anchor)
{
QTextCursor cursor = textCursor();
QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
if (anchor)
mode = QTextCursor::KeepAnchor;
const int initpos = cursor.position();
int pos = cursor.block().position();
QChar character = characterAt(pos);
const QLatin1Char tab = QLatin1Char('\t');
while (character == tab || character.category() == QChar::Separator_Space) {
++pos;
if (pos == initpos)
break;
character = characterAt(pos);
}
// Go to the start of the block when we're already at the start of the text
if (pos == initpos)
pos = cursor.block().position();
cursor.setPosition(pos, mode);
setTextCursor(cursor);
}
void BaseTextEditor::handleBackspaceKey()
{
QTextCursor cursor = textCursor();
if (d->m_snippetOverlay->isVisible()) {
QTextCursor snippetCursor = cursor;
snippetCursor.movePosition(QTextCursor::Left);
d->snippetCheckCursor(snippetCursor);
}
const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings();
if (tabSettings.m_autoIndent && autoBackspace(cursor))
if (!tabSettings.m_smartBackspace) {
cursor.deletePreviousChar();
return;
}
int positionInBlock = pos - currentBlock.position();
const QString blockText = currentBlock.text();
if (cursor.atBlockStart() || tabSettings.firstNonSpace(blockText) < positionInBlock) {
cursor.deletePreviousChar();
return;
}
int previousIndent = 0;
const int indent = tabSettings.columnAt(blockText, positionInBlock);
for (QTextBlock previousNonEmptyBlock = currentBlock.previous();
previousNonEmptyBlock.isValid();
previousNonEmptyBlock = previousNonEmptyBlock.previous()) {
QString previousNonEmptyBlockText = previousNonEmptyBlock.text();
if (previousNonEmptyBlockText.trimmed().isEmpty())
continue;
previousIndent = tabSettings.columnAt(previousNonEmptyBlockText,
tabSettings.firstNonSpace(previousNonEmptyBlockText));
if (previousIndent < indent) {
cursor.beginEditBlock();
cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor);
cursor.insertText(tabSettings.indentationString(previousNonEmptyBlockText));
cursor.endEditBlock();
}
void BaseTextEditor::wheelEvent(QWheelEvent *e)
{
d->clearVisibleCollapsedBlock();
if (scrollWheelZoomingEnabled() && e->modifiers() & Qt::ControlModifier) {
const int delta = e->delta();
if (delta < 0)
zoomOut();
else if (delta > 0)
zoomIn();
return;
}
QPlainTextEdit::wheelEvent(e);
}
void BaseTextEditor::zoomIn(int range)
{
d->clearVisibleCollapsedBlock();
emit requestFontZoom(range*10);
}
void BaseTextEditor::zoomOut(int range)
{
zoomIn(-range);
}
void BaseTextEditor::zoomReset()
{
emit requestZoomReset();
}
bool BaseTextEditor::isElectricCharacter(const QChar &) const
{
return false;
}
void BaseTextEditor::indentInsertedText(const QTextCursor &tc)
{
indent(tc.document(), tc, QChar::Null);
}
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
void BaseTextEditor::countBracket(QChar open, QChar close, QChar c, int *errors, int *stillopen)
{
if (c == open)
++*stillopen;
else if (c == close)
--*stillopen;
if (*stillopen < 0) {
*errors += -1 * (*stillopen);
*stillopen = 0;
}
}
void BaseTextEditor::countBrackets(QTextCursor cursor, int from, int end, QChar open, QChar close, int *errors, int *stillopen)
{
cursor.setPosition(from);
QTextBlock block = cursor.block();
while (block.isValid() && block.position() < end) {
TextEditor::Parentheses parenList = TextEditor::TextEditDocumentLayout::parentheses(block);
if (!parenList.isEmpty() && !TextEditor::TextEditDocumentLayout::ifdefedOut(block)) {
for (int i = 0; i < parenList.count(); ++i) {
TextEditor::Parenthesis paren = parenList.at(i);
int position = block.position() + paren.pos;
if (position < from || position >= end)
continue;
countBracket(open, close, paren.chr, errors, stillopen);
}
}
block = block.next();
}
}
bool BaseTextEditor::contextAllowsAutoParentheses(const QTextCursor &cursor,
const QString &textToInsert) const
{
Q_UNUSED(cursor);
Q_UNUSED(textToInsert);
return false;
}
bool BaseTextEditor::isInComment(const QTextCursor &cursor) const
return false;
}
QString BaseTextEditor::insertMatchingBrace(const QTextCursor &tc, const QString &text,
const QChar &la, int *skippedChars) const
{
Q_UNUSED(tc);
Q_UNUSED(la);
Q_UNUSED(skippedChars);
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
QString BaseTextEditor::insertParagraphSeparator(const QTextCursor &tc) const
{
Q_UNUSED(tc);
return QString();
}
QString BaseTextEditor::autoComplete(QTextCursor &cursor, const QString &textToInsert) const
{
const bool checkBlockEnd = d->m_allowSkippingOfBlockEnd;
d->m_allowSkippingOfBlockEnd = false; // consume blockEnd.
if (!contextAllowsAutoParentheses(cursor, textToInsert))
return QString();
const QString text = textToInsert;
const QChar lookAhead = characterAt(cursor.selectionEnd());
QChar character = textToInsert.at(0);
const QString parentheses = QLatin1String("()");
const QString brackets = QLatin1String("[]");
if (parentheses.contains(character) || brackets.contains(character)) {
QTextCursor tmp= cursor;
bool foundBlockStart = TextEditor::TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
int blockStart = foundBlockStart ? tmp.position() : 0;
bool foundBlockEnd = TextEditor::TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
int blockEnd = foundBlockEnd ? tmp.position() : (cursor.document()->characterCount() - 1);
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
const QChar openChar = parentheses.contains(character) ? QLatin1Char('(') : QLatin1Char('[');
const QChar closeChar = parentheses.contains(character) ? QLatin1Char(')') : QLatin1Char(']');
int errors = 0;
int stillopen = 0;
countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsBeforeInsertion = errors + stillopen;
errors = 0;
stillopen = 0;
countBrackets(cursor, blockStart, cursor.position(), openChar, closeChar, &errors, &stillopen);
countBracket(openChar, closeChar, character, &errors, &stillopen);
countBrackets(cursor, cursor.position(), blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsAfterInsertion = errors + stillopen;
if (errorsAfterInsertion < errorsBeforeInsertion)
return QString(); // insertion fixes parentheses or bracket errors, do not auto complete
}
int skippedChars = 0;
const QString autoText = insertMatchingBrace(cursor, text, lookAhead, &skippedChars);
if (checkBlockEnd && textToInsert.at(0) == QLatin1Char('}')) {
if (textToInsert.length() > 1)
qWarning() << "*** handle event compression";
int startPos = cursor.selectionEnd(), pos = startPos;
while (characterAt(pos).isSpace())
++pos;
if (characterAt(pos) == QLatin1Char('}'))
skippedChars += (pos - startPos) + 1;
}
if (skippedChars) {
const int pos = cursor.position();
cursor.setPosition(pos + skippedChars);
cursor.setPosition(pos, QTextCursor::KeepAnchor);
}
return autoText;
}
bool BaseTextEditor::autoBackspace(QTextCursor &cursor)
{
d->m_allowSkippingOfBlockEnd = false;
int pos = cursor.position();
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
QTextCursor c = cursor;
c.setPosition(pos - 1);
QChar lookAhead = characterAt(pos);
QChar lookBehind = characterAt(pos-1);
QChar lookFurtherBehind = characterAt(pos-2);
QChar character = lookBehind;
if (character == QLatin1Char('(') || character == QLatin1Char('[')) {
QTextCursor tmp = cursor;
TextEditor::TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
int blockStart = tmp.isNull() ? 0 : tmp.position();
tmp = cursor;
TextEditor::TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
int blockEnd = tmp.isNull() ? (cursor.document()->characterCount()-1) : tmp.position();
QChar openChar = character;
QChar closeChar = (character == QLatin1Char('(')) ? QLatin1Char(')') : QLatin1Char(']');
int errors = 0;
int stillopen = 0;
countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsBeforeDeletion = errors + stillopen;
errors = 0;
stillopen = 0;
countBrackets(cursor, blockStart, pos - 1, openChar, closeChar, &errors, &stillopen);
countBrackets(cursor, pos, blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsAfterDeletion = errors + stillopen;
if (errorsAfterDeletion < errorsBeforeDeletion)
return false; // insertion fixes parentheses or bracket errors, do not auto complete
}