Newer
Older
if (event->type() == QEvent::ContextMenu) {
const QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event);
if (ce->reason() == QContextMenuEvent::Mouse && !textCursor().hasSelection())
setTextCursor(cursorForPosition(ce->pos()));
} else if (event->type() == QEvent::ToolTip) {
const QHelpEvent *he = static_cast<QHelpEvent*>(event);
if (QApplication::keyboardModifiers() & Qt::ControlModifier)
return true; // eat tooltip event when control is pressed
const QPoint &pos = he->pos();
// Allow plugins to show tooltips
const QTextCursor &c = cursorForPosition(pos);
QPoint cursorPos = mapToGlobal(cursorRect(c).bottomRight() + QPoint(1,1));
cursorPos.setX(cursorPos.x() + d->m_extraArea->width());
editableInterface(); // create if necessary
emit d->m_editable->tooltipRequested(editableInterface(), cursorPos, c.position());
return true;
}
return QPlainTextEdit::viewportEvent(event);
}
void BaseTextEditor::resizeEvent(QResizeEvent *e)
{
QPlainTextEdit::resizeEvent(e);
d->m_extraArea->setGeometry(
QStyle::visualRect(layoutDirection(), cr,
QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height())));
}
QRect BaseTextEditor::collapseBox()
if (d->m_highlightBlocksInfo.isEmpty() || d->extraAreaHighlightCollapseBlockNumber < 0)
return QRect();
QTextBlock begin = document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last());
if (TextBlockUserData::hasCollapseAfter(begin.previous()))
begin = begin.previous();
QTextBlock end = document()->findBlockByNumber(d->m_highlightBlocksInfo.close.first());
if (!begin.isValid() || !end.isValid())
return QRect();
QRectF br = blockBoundingGeometry(begin).translated(contentOffset());
QRectF er = blockBoundingGeometry(end).translated(contentOffset());
return QRect(d->m_extraArea->width() - collapseBoxWidth(fontMetrics()),
collapseBoxWidth(fontMetrics()),
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
}
QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const {
QPointF offset(contentOffset());
QTextBlock block = firstVisibleBlock();
int top = (int)blockBoundingGeometry(block).translated(offset).top();
int bottom = top + (int)blockBoundingRect(block).height();
int viewportHeight = viewport()->height();
while (block.isValid() && top <= viewportHeight) {
QTextBlock nextBlock = block.next();
if (block.isVisible() && bottom >= 0) {
if (nextBlock.isValid() && !nextBlock.isVisible()) {
QTextLayout *layout = block.layout();
QTextLine line = layout->lineAt(layout->lineCount()-1);
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
lineRect.adjust(0, 0, -1, -1);
QRectF collapseRect(lineRect.right() + 12,
lineRect.top(),
fontMetrics().width(QLatin1String(" {...}; ")),
lineRect.height());
if (collapseRect.contains(pos)) {
QTextBlock result = block;
if (box)
*box = collapseRect.toAlignedRect();
return result;
} else {
block = nextBlock;
while (nextBlock.isValid() && !nextBlock.isVisible()) {
block = nextBlock;
nextBlock = block.next();
}
}
}
}
block = nextBlock;
top = bottom;
bottom = top + (int)blockBoundingRect(block).height();
}
return QTextBlock();
}
void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
int blockPosition = block.position();
QTextCursor cursor = q->textCursor();
QString text = block.text();
text.replace(QChar::Nbsp, QLatin1Char(' '));
int idx = -1;
idx = m_searchExpr.indexIn(text, idx + l);
l = m_searchExpr.matchedLength();
if ((m_findFlags & Find::IFindSupport::FindWholeWords)
&& ((idx && text.at(idx-1).isLetterOrNumber())
|| (idx + l < text.length() && text.at(idx + l).isLetterOrNumber())))
continue;
if (m_findScope.isNull()
|| (blockPosition + idx >= m_findScope.selectionStart()
&& blockPosition + idx + l <= m_findScope.selectionEnd())) {
overlay->addOverlaySelection(blockPosition + idx,
blockPosition + idx + l,
m_searchResultFormat.background().color().darker(120),
(idx == cursor.selectionStart() - blockPosition
&& idx + l == cursor.selectionEnd() - blockPosition)?
TextEditorOverlay::DropShadow : 0);
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
}
}
}
namespace TextEditor {
namespace Internal {
struct BlockSelectionData {
int selectionIndex;
int selectionStart;
int selectionEnd;
int firstColumn;
int lastColumn;
};
}
}
void BaseTextEditorPrivate::clearBlockSelection()
{
if (m_inBlockSelectionMode) {
m_inBlockSelectionMode = false;
QTextCursor cursor = q->textCursor();
cursor.clearSelection();
q->setTextCursor(cursor);
}
}
QString BaseTextEditorPrivate::copyBlockSelection()
{
QString text;
QTextCursor cursor = q->textCursor();
if (!cursor.hasSelection())
return text;
QTextDocument *doc = q->document();
int start = cursor.selectionStart();
int end = cursor.selectionEnd();
QTextBlock startBlock = doc->findBlock(start);
int columnA = start - startBlock.position();
QTextBlock endBlock = doc->findBlock(end);
int columnB = end - endBlock.position();
int firstColumn = qMin(columnA, columnB);
int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
QTextBlock block = startBlock;
for (;;) {
cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
text += cursor.selectedText();
if (block == endBlock)
break;
text += QLatin1Char('\n');
block = block.next();
}
return text;
}
void BaseTextEditorPrivate::removeBlockSelection(const QString &text)
{
QTextCursor cursor = q->textCursor();
if (!cursor.hasSelection())
return;
QTextDocument *doc = q->document();
int start = cursor.selectionStart();
int end = cursor.selectionEnd();
QTextBlock startBlock = doc->findBlock(start);
int columnA = start - startBlock.position();
QTextBlock endBlock = doc->findBlock(end);
int columnB = end - endBlock.position();
int firstColumn = qMin(columnA, columnB);
int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
cursor.clearSelection();
cursor.beginEditBlock();
QTextBlock block = startBlock;
for (;;) {
cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
cursor.removeSelectedText();
if (block == endBlock)
break;
block = block.next();
}
cursor.setPosition(start);
if (!text.isEmpty())
cursor.insertText(text);
cursor.endEditBlock();
q->setTextCursor(cursor);
}
void BaseTextEditorPrivate::moveCursorVisible(bool ensureVisible)
{
QTextCursor cursor = q->textCursor();
if (!cursor.block().isVisible()) {
cursor.setVisualNavigation(true);
cursor.movePosition(QTextCursor::Up);
q->setTextCursor(cursor);
}
if (ensureVisible)
q->ensureCursorVisible();
}
static QColor blendColors(const QColor &a, const QColor &b, int alpha)
{
return QColor((a.red() * (256 - alpha) + b.red() * alpha) / 256,
(a.green() * (256 - alpha) + b.green() * alpha) / 256,
(a.blue() * (256 - alpha) + b.blue() * alpha) / 256);
}
static QColor calcBlendColor(const QColor &baseColor, int level, int count)
{
QColor color80;
QColor color90;
if (baseColor.value() > 128) {
const int f90 = 15;
const int f80 = 30;
color80.setRgb(qMax(0, baseColor.red() - f80),
qMax(0, baseColor.green() - f80),
qMax(0, baseColor.blue() - f80));
color90.setRgb(qMax(0, baseColor.red() - f90),
qMax(0, baseColor.green() - f90),
qMax(0, baseColor.blue() - f90));
const int f90 = 20;
const int f80 = 40;
color80.setRgb(qMin(255, baseColor.red() + f80),
qMin(255, baseColor.green() + f80),
qMin(255, baseColor.blue() + f80));
color90.setRgb(qMin(255, baseColor.red() + f90),
qMin(255, baseColor.green() + f90),
qMin(255, baseColor.blue() + f90));
}
if (level == count)
return baseColor;
if (level == 0)
return color80;
if (level == count - 1)
return color90;
const int blendFactor = level * (256 / (count - 2));
return blendColors(color80, color90, blendFactor);
void BaseTextEditor::paintEvent(QPaintEvent *e)
{
/*
Here comes an almost verbatim copy of
QPlainTextEdit::paintEvent() so we can adjust the extra
selections dynamically to indicate all search results.
*/
//begin QPlainTextEdit::paintEvent()
QPainter painter(viewport());
QTextDocument *doc = document();
TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout());
bool hasMainSelection = textCursor().hasSelection();
QRect er = e->rect();
QRect viewportRect = viewport()->rect();
const QColor baseColor = palette().base().color();
qreal lineX = 0;
if (d->m_visibleWrapColumn > 0) {
lineX = fontMetrics().averageCharWidth() * d->m_visibleWrapColumn + offset.x() + 4;
if (lineX < viewportRect.width()) {
const QColor backgroundColor = d->m_ifdefedOutFormat.background().color();
painter.fillRect(QRectF(lineX, er.top(), viewportRect.width() - lineX, er.height()),
backgroundColor);
const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white;
const QPen pen = painter.pen();
painter.setPen(blendColors(backgroundColor, 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(), documentLayout->documentSize().width())
// - doc->documentMargin();
// er.setRight(qMin(er.right(), maxX));
// painter.setClipRect(er);
bool editable = !isReadOnly();
QTextBlock block = firstVisibleBlock();
QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
if (!d->m_findScope.isNull()) {
TextEditorOverlay *overlay = new TextEditorOverlay(this);
overlay->addOverlaySelection(d->m_findScope, d->m_searchScopeFormat.background().color().darker(120),
d->m_searchScopeFormat.background().color());
overlay->setAlpha(false);
overlay->paint(&painter, e->rect());
delete overlay;
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
}
BlockSelectionData *blockSelection = 0;
if (d->m_inBlockSelectionMode
&& context.selections.count() && context.selections.last().cursor == textCursor()) {
blockSelection = new BlockSelectionData;
blockSelection->selectionIndex = context.selections.size()-1;
const QAbstractTextDocumentLayout::Selection &selection = context.selections[blockSelection->selectionIndex];
int start = blockSelection->selectionStart = selection.cursor.selectionStart();
int end = blockSelection->selectionEnd = selection.cursor.selectionEnd();
QTextBlock block = doc->findBlock(start);
int columnA = start - block.position();
block = doc->findBlock(end);
int columnB = end - block.position();
blockSelection->firstColumn = qMin(columnA, columnB);
blockSelection->lastColumn = qMax(columnA, columnB) + d->m_blockSelectionExtraX;
}
QTextBlock visibleCollapsedBlock;
QPointF visibleCollapsedBlockOffset;
QTextLayout *cursor_layout = 0;
QPointF cursor_offset;
int cursor_cpos = 0;
QPen cursor_pen;
if (!d->m_searchExpr.isEmpty()) { // first pass for the search result overlays
const int margin = 5;
QTextBlock blockFP = block;
QPointF offsetFP = offset;
while (blockFP.isValid()) {
QRectF r = blockBoundingRect(blockFP).translated(offsetFP);
if (r.bottom() >= er.top() - margin && r.top() <= er.bottom() + margin) {
d->highlightSearchResults(blockFP,
d->m_searchResultOverlay);
}
offsetFP.ry() += r.height();
if (offsetFP.y() > viewportRect.height() + margin)
break;
d->m_searchResultOverlay->fill(&painter,
d->m_searchResultFormat.background().color(),
e->rect());
while (block.isValid()) {
QRectF r = blockBoundingRect(block).translated(offset);
if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
if (TextEditDocumentLayout::ifdefedOut(block)) {
QRectF rr = r;
rr.setWidth(viewport()->width());
if (lineX > 0)
rr.setRight(qMin(lineX, rr.right()));
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
painter.fillRect(rr, d->m_ifdefedOutFormat.background());
}
if (!d->m_highlightBlocksInfo.isEmpty()) {
int n = block.blockNumber();
int depth = 0;
foreach (int i, d->m_highlightBlocksInfo.open)
if (n >= i)
++depth;
foreach (int i, d->m_highlightBlocksInfo.close)
if (n > i)
--depth;
int count = d->m_highlightBlocksInfo.count();
if (count) {
QRectF rr = r;
rr.setWidth(viewport()->width());
if (lineX > 0)
rr.setRight(qMin(lineX, rr.right()));
for (int i = 0; i <= depth; ++i) {
int vi = i > 0 ? d->m_highlightBlocksInfo.visualIndent.at(i-1) : 0;
painter.fillRect(rr.adjusted(vi, 0, -8*i, 0), calcBlendColor(baseColor, i, count));
}
QTextOption option = layout->textOption();
if (TextEditDocumentLayout::ifdefedOut(block)) {
option.setFlags(option.flags() /*| QTextOption::SuppressColors*/);
painter.setPen(d->m_ifdefedOutFormat.foreground().color());
} else {
option.setFlags(option.flags() & ~QTextOption::SuppressColors);
painter.setPen(context.palette.text().color());
}
layout->setTextOption(option);
int blpos = block.position();
int bllen = block.length();
QVector<QTextLayout::FormatRange> selections;
QVector<QTextLayout::FormatRange> prioritySelections;
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;
if (blockSelection && blockSelection->selectionIndex == i) {
o.start = qMin(blockSelection->firstColumn, bllen-1);
o.length = qMin(blockSelection->lastColumn, bllen-1) - o.start;
}
if ((hasMainSelection && i == context.selections.size()-1)
|| (o.format.foreground().style() == Qt::NoBrush
&& o.format.underlineStyle() != QTextCharFormat::NoUnderline
&& o.format.background() == Qt::NoBrush))
prioritySelections.append(o);
else
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);
selections += prioritySelections;
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
bool drawCursor = ((editable || true) // we want the cursor in read-only mode
&& 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;
cursor_layout = layout;
cursor_offset = offset;
cursor_cpos = cpos;
cursor_pen = painter.pen();
}
}
offset.ry() += r.height();
if (offset.y() > viewportRect.height())
break;
if (!block.isVisible()) {
if (block.blockNumber() == d->visibleCollapsedBlockNumber) {
visibleCollapsedBlock = block;
}
// invisible blocks do have zero line count
block = doc->findBlockByLineNumber(block.firstLineNumber());
}
}
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
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());
}
//end QPlainTextEdit::paintEvent()
delete blockSelection;
offset = contentOffset();
block = firstVisibleBlock();
int top = (int)blockBoundingGeometry(block).translated(offset).top();
int bottom = top + (int)blockBoundingRect(block).height();
QTextCursor cursor = textCursor();
bool hasSelection = cursor.hasSelection();
int selectionStart = cursor.selectionStart();
int selectionEnd = cursor.selectionEnd();
while (block.isValid() && top <= e->rect().bottom()) {
QTextBlock nextBlock = block.next();
QTextBlock nextVisibleBlock = nextBlock;
// invisible blocks do have zero line count
nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber());
// paranoia in case our code somewhere did not set the line count
// of the invisible block to 0
while (nextVisibleBlock.isValid() && !nextVisibleBlock.isVisible())
nextVisibleBlock = nextVisibleBlock.next();
}
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
if (block.isVisible() && bottom >= e->rect().top()) {
if (d->m_displaySettings.m_visualizeWhitespace) {
QTextLayout *layout = block.layout();
int lineCount = layout->lineCount();
if (lineCount >= 2 || !nextBlock.isValid()) {
painter.save();
painter.setPen(Qt::lightGray);
for (int i = 0; i < lineCount-1; ++i) { // paint line wrap indicator
QTextLine line = layout->lineAt(i);
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
QChar visualArrow((ushort)0x21b5);
painter.drawText(static_cast<int>(lineRect.right()),
static_cast<int>(lineRect.top() + line.ascent()), visualArrow);
}
if (!nextBlock.isValid()) { // paint EOF symbol
QTextLine line = layout->lineAt(lineCount-1);
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
int h = 4;
lineRect.adjust(0, 0, -1, -1);
QPainterPath path;
QPointF pos(lineRect.topRight() + QPointF(h+4, line.ascent()));
path.moveTo(pos);
path.lineTo(pos + QPointF(-h, -h));
path.lineTo(pos + QPointF(0, -2*h));
path.lineTo(pos + QPointF(h, -h));
path.closeSubpath();
painter.setBrush(painter.pen().color());
painter.drawPath(path);
}
painter.restore();
}
}
if (nextBlock.isValid() && !nextBlock.isVisible()) {
bool selectThis = (hasSelection
&& nextBlock.position() >= selectionStart
&& nextBlock.position() < selectionEnd);
if (selectThis) {
painter.save();
painter.setBrush(palette().highlight());
}
QTextLayout *layout = block.layout();
QTextLine line = layout->lineAt(layout->lineCount()-1);
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
lineRect.adjust(0, 0, -1, -1);
QRectF collapseRect(lineRect.right() + 12,
lineRect.top(),
fontMetrics().width(QLatin1String(" {...}; ")),
lineRect.height());
painter.setRenderHint(QPainter::Antialiasing, true);
painter.translate(.5, .5);
painter.drawRoundedRect(collapseRect.adjusted(0, 0, 0, -1), 3, 3);
painter.setRenderHint(QPainter::Antialiasing, false);
painter.translate(-.5, -.5);
QString replacement = QLatin1String("...");
QTextBlock info = block;
if (block.userData()
&& static_cast<TextBlockUserData*>(block.userData())->collapseMode() == TextBlockUserData::CollapseAfter)
;
else if (block.next().userData()
&& static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
== TextBlockUserData::CollapseThis) {
replacement.prepend(nextBlock.text().trimmed().left(1));
info = nextBlock;
}
block = nextVisibleBlock.previous();
if (!block.isValid())
block = doc->lastBlock();
if (info.userData()
&& static_cast<TextBlockUserData*>(info.userData())->collapseIncludesClosure()) {
QString right = block.text().trimmed();
if (right.endsWith(QLatin1Char(';'))) {
right.chop(1);
right = right.trimmed();
replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
replacement.append(QLatin1Char(';'));
} else {
replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
}
}
if (selectThis)
painter.setPen(palette().highlightedText().color());
painter.drawText(collapseRect, Qt::AlignCenter, replacement);
if (selectThis)
painter.restore();
}
}
block = nextVisibleBlock;
top = bottom;
bottom = top + (int)blockBoundingRect(block).height();
}
if (visibleCollapsedBlock.isValid() ) {
int margin = doc->documentMargin();
qreal maxWidth = 0;
qreal blockHeight = 0;
QTextBlock b = visibleCollapsedBlock;
b.setVisible(true); // make sure block bounding rect works
QRectF r = blockBoundingRect(b).translated(visibleCollapsedBlockOffset);
QTextLayout *layout = b.layout();
for (int i = layout->lineCount()-1; i >= 0; --i)
maxWidth = qMax(maxWidth, layout->lineAt(i).naturalTextWidth() + 2*margin);
blockHeight += r.height();
b.setVisible(false); // restore previous state
b.setLineCount(0); // restore 0 line count for invisible block
b = b.next();
}
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
painter.translate(.5, .5);
painter.setBrush(d->m_ifdefedOutFormat.background());
painter.drawRoundedRect(QRectF(visibleCollapsedBlockOffset.x(),
visibleCollapsedBlockOffset.y(),
painter.restore();
QTextBlock end = b;
b = visibleCollapsedBlock;
while (b != end) {
b.setVisible(true); // make sure block bounding rect works
QRectF r = blockBoundingRect(b).translated(visibleCollapsedBlockOffset);
QTextLayout *layout = b.layout();
QVector<QTextLayout::FormatRange> selections;
layout->draw(&painter, visibleCollapsedBlockOffset, selections, er);
b.setVisible(false); // restore previous state
visibleCollapsedBlockOffset.ry() += r.height();
b = b.next();
}
}
if (d->m_animator && d->m_animator->isRunning()) {

mae
committed
QTextCursor cursor = textCursor();
cursor.setPosition(d->m_animator->position());
d->m_animator->draw(&painter, cursorRect(cursor).topLeft());
}
if (lineX > 0) {
const QColor bg = palette().base().color();
QColor col = (bg.value() > 128) ? Qt::black : Qt::white;
col.setAlpha(32);
painter.setPen(QPen(col, 0));
painter.drawLine(QPointF(lineX, 0), QPointF(lineX, viewport()->height()));
}
if (d->m_overlay && d->m_overlay->isVisible())
d->m_overlay->paint(&painter, e->rect());
if (d->m_snippetOverlay && d->m_snippetOverlay->isVisible())
d->m_snippetOverlay->paint(&painter, e->rect());
if (!d->m_searchResultOverlay->isEmpty()) {
d->m_searchResultOverlay->paint(&painter, e->rect());
d->m_searchResultOverlay->clear();
}
// draw the cursor last, on top of everything
if (cursor_layout) {
painter.setPen(cursor_pen);
cursor_layout->drawCursor(&painter, cursor_offset, cursor_cpos, cursorWidth());
}
}
QWidget *BaseTextEditor::extraArea() const
{
return d->m_extraArea;
}
int BaseTextEditor::extraAreaWidth(int *markWidthPtr) const
{
TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document()->documentLayout());
if (!documentLayout)
return 0;
if (!d->m_marksVisible && documentLayout->hasMarks)
d->m_marksVisible = true;
int space = 0;
const QFontMetrics fm(d->m_extraArea->fontMetrics());
if (d->m_lineNumbersVisible) {
QFont fnt = d->m_extraArea->font();
// this works under the assumption that bold or italic can only make a font wider
fnt.setBold(d->m_currentLineNumberFormat.font().bold());
fnt.setItalic(d->m_currentLineNumberFormat.font().italic());
const QFontMetrics linefm(fnt);
int digits = 2;
int max = qMax(1, blockCount());
while (max >= 100) {
max /= 10;
++digits;
}
space += linefm.width(QLatin1Char('9')) * digits;
}
int markWidth = 0;
if (d->m_marksVisible) {
markWidth += fm.lineSpacing();
// if (documentLayout->doubleMarkCount)
// markWidth += fm.lineSpacing() / 3;
space += markWidth;
} else {
space += 2;
}
if (markWidthPtr)
*markWidthPtr = markWidth;
space += 4;
if (d->m_codeFoldingVisible)
space += collapseBoxWidth(fm);
void BaseTextEditor::slotUpdateExtraAreaWidth()
if (isLeftToRight())
setViewportMargins(extraAreaWidth(), 0, 0, 0);
else
setViewportMargins(0, 0, extraAreaWidth(), 0);
static void drawRectBox(QPainter *painter, const QRect &rect, bool start, bool end,
const QPalette &pal)
{
painter->setRenderHint(QPainter::Antialiasing, false);
QRgb b = pal.base().color().rgb();
QRgb h = pal.highlight().color().rgb();
QColor c = Utils::StyleHelper::mergedColors(b,h, 50);
QLinearGradient grad(rect.topLeft(), rect.topRight());
grad.setColorAt(0, c.lighter(110));
grad.setColorAt(1, c.lighter(130));
QColor outline = c;
QRect r = rect;
painter->fillRect(rect, grad);
if (start)
painter->drawLine(rect.topLeft() + QPoint(1, 0), rect.topRight() - QPoint(1, 0));
if (end)
painter->drawLine(rect.bottomLeft() + QPoint(1, 0), rect.bottomRight() - QPoint(1, 0));
painter->drawLine(rect.topRight() + QPoint(0, start ? 1 : 0), rect.bottomRight() - QPoint(0, end ? 1 : 0));
painter->drawLine(rect.topLeft() + QPoint(0, start ? 1 : 0), rect.bottomLeft() - QPoint(0, end ? 1 : 0));
void BaseTextEditor::extraAreaPaintEvent(QPaintEvent *e)
{
QTextDocument *doc = document();
TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout());
int selStart = textCursor().selectionStart();
int selEnd = textCursor().selectionEnd();
const QColor baseColor = palette().base().color();
QPalette pal = d->m_extraArea->palette();
pal.setCurrentColorGroup(QPalette::Active);
QPainter painter(d->m_extraArea);
const QFontMetrics fm(d->m_extraArea->font());
int fmLineSpacing = fm.lineSpacing();
int markWidth = 0;
if (d->m_marksVisible)
markWidth += fm.lineSpacing();
const int collapseColumnWidth = d->m_codeFoldingVisible ? collapseBoxWidth(fm): 0;
const int extraAreaWidth = d->m_extraArea->width() - collapseColumnWidth;
painter.fillRect(e->rect(), pal.color(QPalette::Base));
painter.fillRect(e->rect().intersected(QRect(0, 0, extraAreaWidth, INT_MAX)),
pal.color(QPalette::Background));
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top;
while (block.isValid() && top <= e->rect().bottom()) {
top = bottom;
bottom = top + (int)blockBoundingRect(block).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;
}
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
painter.setPen(pal.color(QPalette::Dark));
if (d->m_codeFoldingVisible || d->m_marksVisible) {
painter.save();
painter.setRenderHint(QPainter::Antialiasing, false);
int previousBraceDepth = block.previous().userState();
if (previousBraceDepth >= 0)
previousBraceDepth >>= 8;
else
previousBraceDepth = 0;
int braceDepth = block.userState();
if (!nextBlock.isVisible()) {
QTextBlock lastInvisibleBlock = nextVisibleBlock.previous();
if (!lastInvisibleBlock.isValid())
lastInvisibleBlock = doc->lastBlock();
braceDepth = lastInvisibleBlock.userState();
}
if (braceDepth >= 0)
braceDepth >>= 8;
else
braceDepth = 0;
if (TextBlockUserData *userData = static_cast<TextBlockUserData*>(block.userData())) {
if (d->m_marksVisible) {
int xoffset = 0;
foreach (ITextMark *mrk, userData->marks()) {
int x = 0;
int radius = fmLineSpacing - 1;
QRect r(x + xoffset, top, radius, radius);
mrk->icon().paint(&painter, r, Qt::AlignCenter);
xoffset += 2;
}
}
}
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
bool collapseThis = false;
bool collapseAfter = false;
bool hasClosingCollapse = false;
if (TextBlockUserData *userData = static_cast<TextBlockUserData*>(block.userData())) {
if (!userData->ifdefedOut()) {
collapseAfter = (userData->collapseMode() == TextBlockUserData::CollapseAfter);
collapseThis = (userData->collapseMode() == TextBlockUserData::CollapseThis);
hasClosingCollapse = userData->hasClosingCollapse() && (previousBraceDepth > 0);
}
}
int extraAreaHighlightCollapseBlockNumber = -1;
int extraAreaHighlightCollapseEndBlockNumber = -1;
bool endIsVisible = false;
if (!d->m_highlightBlocksInfo.isEmpty()) {
extraAreaHighlightCollapseBlockNumber = d->m_highlightBlocksInfo.open.last();
extraAreaHighlightCollapseEndBlockNumber = d->m_highlightBlocksInfo.close.first();
endIsVisible = doc->findBlockByNumber(extraAreaHighlightCollapseEndBlockNumber).isVisible();
QTextBlock before = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber-1);
if (TextBlockUserData::hasCollapseAfter(before)) {
extraAreaHighlightCollapseBlockNumber--;
}
}
TextBlockUserData *nextBlockUserData = TextEditDocumentLayout::testUserData(nextBlock);
bool collapseNext = nextBlockUserData
&& nextBlockUserData->collapseMode() == TextBlockUserData::CollapseThis
&& !nextBlockUserData->ifdefedOut();
bool nextHasClosingCollapse = nextBlockUserData
&& 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);