2021-05-14 23:56:43 +08:00
|
|
|
#include "TextPainter.h"
|
|
|
|
#include "SynEdit.h"
|
|
|
|
#include "Constants.h"
|
|
|
|
#include <cmath>
|
2021-05-24 00:41:00 +08:00
|
|
|
#include <QDebug>
|
2021-05-14 23:56:43 +08:00
|
|
|
|
2021-05-24 00:41:00 +08:00
|
|
|
SynEditTextPainter::SynEditTextPainter(SynEdit *edit, QPainter *painter, int FirstRow, int LastRow, int FirstCol, int LastCol)
|
2021-05-14 23:56:43 +08:00
|
|
|
{
|
|
|
|
this->edit = edit;
|
2021-05-24 00:41:00 +08:00
|
|
|
this->painter = painter;
|
2021-05-21 23:33:53 +08:00
|
|
|
this->aFirstRow = FirstRow;
|
|
|
|
this->aLastRow = LastRow;
|
|
|
|
this->FirstCol = FirstCol;
|
|
|
|
this->LastCol = LastCol;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::paintTextLines(const QRect& clip)
|
|
|
|
{
|
|
|
|
AClip = clip;
|
|
|
|
vFirstLine = edit->rowToLine(aFirstRow);
|
|
|
|
vLastLine = edit->rowToLine(aLastRow);
|
|
|
|
bCurrentLine = false;
|
|
|
|
// If the right edge is visible and in the invalid area, prepare to paint it.
|
|
|
|
// Do this first to realize the pen when getting the dc variable.
|
|
|
|
bDoRightEdge = false;
|
|
|
|
if (edit->mRightEdge > 0) { // column value
|
2021-05-24 21:48:03 +08:00
|
|
|
nRightEdge = edit->textOffset()+ edit->mRightEdge * edit->mCharWidth; // pixel value
|
2021-05-21 23:33:53 +08:00
|
|
|
if (nRightEdge >= AClip.left() &&nRightEdge <= AClip.right()) {
|
|
|
|
bDoRightEdge = true;
|
|
|
|
QPen pen(edit->mRightEdgeColor,1);
|
|
|
|
painter->setPen(pen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Paint the visible text lines. To make this easier, compute first the
|
|
|
|
// necessary information about the selected area: is there any visible
|
|
|
|
// selected area, and what are its lines / columns?
|
|
|
|
if (vLastLine >= vFirstLine) {
|
|
|
|
ComputeSelectionInfo();
|
|
|
|
PaintLines();
|
|
|
|
}
|
2021-05-26 18:32:17 +08:00
|
|
|
//painter->setClipping(false);
|
2021-05-21 23:33:53 +08:00
|
|
|
|
|
|
|
// If anything of the two pixel space before the text area is visible, then
|
|
|
|
// fill it with the component background color.
|
2021-05-26 18:32:17 +08:00
|
|
|
if (AClip.left() <edit->mGutterWidth + 2) {
|
2021-05-21 23:33:53 +08:00
|
|
|
rcToken = AClip;
|
|
|
|
rcToken.setLeft( std::max(AClip.left(), edit->mGutterWidth));
|
|
|
|
rcToken.setRight(edit->mGutterWidth + 2);
|
|
|
|
// Paint whole left edge of the text with same color.
|
|
|
|
// (value of WhiteAttribute can vary in e.g. MultiSyn)
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(rcToken,colEditorBG());
|
2021-05-21 23:33:53 +08:00
|
|
|
// Adjust the invalid area to not include this area.
|
|
|
|
AClip.setLeft(rcToken.right());
|
|
|
|
}
|
|
|
|
// If there is anything visible below the last line, then fill this as well.
|
|
|
|
rcToken = AClip;
|
|
|
|
rcToken.setTop((aLastRow - edit->mTopLine + 1) * edit->mTextHeight);
|
|
|
|
if (rcToken.top() < rcToken.bottom()) {
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(rcToken,colEditorBG());
|
2021-05-21 23:33:53 +08:00
|
|
|
// Draw the right edge if necessary.
|
|
|
|
if (bDoRightEdge) {
|
|
|
|
QPen pen(edit->mRightEdgeColor,1);
|
|
|
|
painter->setPen(pen);
|
|
|
|
painter->drawLine(nRightEdge, rcToken.top(),nRightEdge, rcToken.bottom() + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This messes with pen colors, so draw after right margin has been drawn
|
|
|
|
PaintFoldAttributes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::paintGutter(const QRect& clip)
|
|
|
|
{
|
|
|
|
int cRow;
|
|
|
|
QRect rcLine, rcFold;
|
|
|
|
QString s;
|
|
|
|
int vLine;
|
|
|
|
int vLineTop;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
AClip = clip;
|
|
|
|
|
|
|
|
//todo: Does the following comment still apply?
|
|
|
|
// Changed to use fTextDrawer.BeginDrawing and fTextDrawer.EndDrawing only
|
|
|
|
// when absolutely necessary. Note: Never change brush / pen / font of the
|
|
|
|
// canvas inside of this block (only through methods of fTextDrawer)!
|
|
|
|
// If we have to draw the line numbers then we don't want to erase
|
|
|
|
// the background first. Do it line by line with TextRect instead
|
|
|
|
// and fill only the area after the last visible line.
|
2021-05-26 18:32:17 +08:00
|
|
|
//painter->setClipRect(AClip);
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(AClip,edit->mGutter.color());
|
2021-05-21 23:33:53 +08:00
|
|
|
|
2021-05-27 20:33:25 +08:00
|
|
|
rcLine=AClip;
|
2021-05-21 23:33:53 +08:00
|
|
|
if (edit->mGutter.showLineNumbers()) {
|
2021-05-26 18:32:17 +08:00
|
|
|
// prepare the rect initially
|
|
|
|
rcLine = AClip;
|
|
|
|
rcLine.setRight( std::max(rcLine.right(), edit->mGutterWidth - 2));
|
|
|
|
rcLine.setBottom(rcLine.top());
|
|
|
|
|
2021-05-21 23:33:53 +08:00
|
|
|
if (edit->mGutter.useFontStyle()) {
|
|
|
|
painter->setFont(edit->mGutter.font());
|
2021-10-10 21:46:11 +08:00
|
|
|
} else {
|
|
|
|
QFont newFont = painter->font();
|
|
|
|
newFont.setBold(false);
|
|
|
|
newFont.setItalic(false);
|
|
|
|
newFont.setStrikeOut(false);
|
|
|
|
newFont.setUnderline(false);
|
|
|
|
painter->setFont(newFont);
|
2021-06-19 22:58:35 +08:00
|
|
|
}
|
2021-08-22 23:48:00 +08:00
|
|
|
if (edit->mGutter.textColor().isValid()) {
|
|
|
|
painter->setPen(edit->mGutter.textColor());
|
2021-05-21 23:33:53 +08:00
|
|
|
} else {
|
|
|
|
painter->setPen(edit->palette().color(QPalette::Text));
|
|
|
|
}
|
|
|
|
// draw each line if it is not hidden by a fold
|
|
|
|
for (int cRow = aFirstRow; cRow <= aLastRow; cRow++) {
|
|
|
|
vLine = edit->rowToLine(cRow);
|
|
|
|
if ((vLine > edit->mLines->count()) && (edit->mLines->count() > 0 ))
|
|
|
|
break;
|
|
|
|
vLineTop = (cRow - edit->mTopLine) * edit->mTextHeight;
|
|
|
|
|
|
|
|
// next line rect
|
|
|
|
rcLine.setTop(vLineTop);
|
|
|
|
rcLine.setBottom(rcLine.top() + edit->mTextHeight);
|
|
|
|
|
|
|
|
s = edit->mGutter.formatLineNumber(vLine);
|
|
|
|
|
2021-05-24 00:41:00 +08:00
|
|
|
edit->onGutterGetText(vLine,s);
|
2021-05-21 23:33:53 +08:00
|
|
|
QRectF textRect;
|
|
|
|
textRect = painter->boundingRect(textRect, Qt::AlignLeft,s);
|
|
|
|
painter->drawText(
|
2021-05-27 01:05:49 +08:00
|
|
|
(edit->mGutterWidth - edit->mGutter.rightOffset() - 2) - textRect.width(),
|
2021-05-24 18:11:07 +08:00
|
|
|
rcLine.bottom() + ((edit->mTextHeight - int(textRect.height())) / 2 - painter->fontMetrics().descent()),
|
2021-05-21 23:33:53 +08:00
|
|
|
s
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the folding lines and squares
|
2021-05-27 01:05:49 +08:00
|
|
|
if (edit->mUseCodeFolding) {
|
|
|
|
for (cRow = aFirstRow; cRow<=aLastRow; cRow++) {
|
|
|
|
vLine = edit->rowToLine(cRow);
|
|
|
|
if ((vLine > edit->mLines->count()) && (edit->mLines->count() != 0))
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Form a rectangle for the square the user can click on
|
|
|
|
//rcFold.Left := Gutter.RealGutterWidth(CharWidth) - Gutter.RightOffset;
|
|
|
|
rcFold.setLeft(edit->mGutterWidth - edit->mGutter.rightOffset());
|
|
|
|
rcFold.setRight(rcFold.left() + edit->mGutter.rightOffset() - 4);
|
|
|
|
rcFold.setTop((cRow - edit->mTopLine) * edit->mTextHeight);
|
|
|
|
rcFold.setBottom(rcFold.top() + edit->mTextHeight);
|
|
|
|
|
|
|
|
painter->setPen(edit->mCodeFolding.folderBarLinesColor);
|
|
|
|
|
|
|
|
|
|
|
|
// Need to paint a line?
|
|
|
|
if (edit->foldAroundLine(vLine)) {
|
|
|
|
x = rcFold.left() + (rcFold.width() / 2);
|
|
|
|
painter->drawLine(x,rcFold.top(), x, rcFold.bottom());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need to paint a line end?
|
|
|
|
if (edit->foldEndAtLine(vLine)) {
|
|
|
|
x = rcFold.left() + (rcFold.width() / 2);
|
|
|
|
painter->drawLine(x,rcFold.top(), x, rcFold.top() + rcFold.height() / 2);
|
|
|
|
painter->drawLine(x,
|
|
|
|
rcFold.top() + rcFold.height() / 2,
|
|
|
|
rcFold.right() - 2 ,
|
|
|
|
rcFold.top() + rcFold.height() / 2);
|
|
|
|
}
|
|
|
|
// Any fold ranges beginning on this line?
|
|
|
|
PSynEditFoldRange FoldRange = edit->foldStartAtLine(vLine);
|
|
|
|
if (FoldRange) {
|
|
|
|
// Draw the bottom part of a line
|
|
|
|
if (!FoldRange->collapsed) {
|
|
|
|
x = rcFold.left() + (rcFold.width() / 2);
|
|
|
|
painter->drawLine(x, rcFold.top() + rcFold.height() / 2,
|
|
|
|
x, rcFold.bottom());
|
|
|
|
}
|
|
|
|
|
|
|
|
// make a square rect
|
|
|
|
inflateRect(rcFold,-2, 0);
|
|
|
|
rcFold.setTop(
|
|
|
|
rcFold.top() + ((edit->mTextHeight - rcFold.width()) / 2));
|
|
|
|
rcFold.setBottom(rcFold.top() + rcFold.width());
|
|
|
|
|
|
|
|
// Paint the square the user can click on
|
|
|
|
painter->setBrush(edit->mGutter.color());
|
|
|
|
painter->setPen(edit->mCodeFolding.folderBarLinesColor);
|
|
|
|
painter->drawRect(rcFold);
|
|
|
|
|
|
|
|
// Paint minus sign
|
|
|
|
painter->drawLine(
|
|
|
|
rcFold.left() + 2, rcFold.top() + (rcFold.height() / 2 ),
|
|
|
|
rcFold.right() - 2, rcFold.top() + (rcFold.height() / 2 ));
|
|
|
|
// Paint vertical line of plus sign
|
|
|
|
if (FoldRange->collapsed) {
|
|
|
|
x = rcFold.left() + (rcFold.width() / 2);
|
|
|
|
painter->drawLine(x, rcFold.top() + 2,
|
|
|
|
x, rcFold.bottom() + 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-21 23:33:53 +08:00
|
|
|
|
|
|
|
// // the gutter separator if visible
|
|
|
|
// if (edit->mGutter.borderStyle <> gbsNone) and (AClip.Right >= fGutterWidth - 2) then
|
|
|
|
// with Canvas do begin
|
|
|
|
// Pen.Color := fGutter.BorderColor;
|
|
|
|
// Pen.Width := 1;
|
|
|
|
// with AClip do begin
|
|
|
|
// if fGutter.BorderStyle = gbsMiddle then begin
|
|
|
|
// MoveTo(fGutterWidth - 2, Top);
|
|
|
|
// LineTo(fGutterWidth - 2, Bottom);
|
|
|
|
// Pen.Color := fGutter.Color;
|
|
|
|
// end;
|
|
|
|
// MoveTo(fGutterWidth - 1, Top);
|
|
|
|
// LineTo(fGutterWidth - 1, Bottom);
|
|
|
|
// end;
|
|
|
|
// end;
|
|
|
|
|
|
|
|
// // now the gutter marks
|
|
|
|
// if BookMarkOptions.GlyphsVisible and (Marks.Count > 0) and (aLastRow >= aFirstRow) then begin
|
|
|
|
// aGutterOffs := AllocMem((aLastRow - aFirstRow + 1) * SizeOf(integer));
|
|
|
|
// vFirstLine := RowToLine(aFirstRow);
|
|
|
|
// vLastLine := RowToLine(aLastRow);
|
|
|
|
// try
|
|
|
|
// // Instead of making a two pass loop we look while drawing the bookmarks
|
|
|
|
// // whether there is any other mark to be drawn
|
|
|
|
// bHasOtherMarks := FALSE;
|
|
|
|
// for cMark := 0 to Marks.Count - 1 do
|
|
|
|
// with Marks[cMark] do
|
|
|
|
// if Visible and (Line >= vFirstLine) and (Line <= vLastLine) then begin
|
|
|
|
// if IsBookmark <> BookMarkOptions.DrawBookmarksFirst then
|
|
|
|
// bHasOtherMarks := TRUE
|
|
|
|
// else begin
|
|
|
|
// vMarkRow := LineToRow(Line);
|
|
|
|
// if vMarkRow >= aFirstRow then
|
|
|
|
// DrawMark(Marks[cMark], aGutterOffs[vMarkRow - aFirstRow], vMarkRow);
|
|
|
|
// end
|
|
|
|
// end;
|
|
|
|
// if bHasOtherMarks then
|
|
|
|
// for cMark := 0 to Marks.Count - 1 do
|
|
|
|
// with Marks[cMark] do begin
|
|
|
|
// if Visible and (IsBookmark <> BookMarkOptions.DrawBookmarksFirst)
|
|
|
|
// and (Line >= vFirstLine) and (Line <= vLastLine) then begin
|
|
|
|
// vMarkRow := LineToRow(Line);
|
|
|
|
// if vMarkRow >= aFirstRow then
|
|
|
|
// DrawMark(Marks[cMark], aGutterOffs[vMarkRow - aFirstRow], vMarkRow);
|
|
|
|
// end;
|
|
|
|
// end;
|
|
|
|
// if Assigned(OnGutterPaint) then
|
|
|
|
// for cRow := aFirstRow to aLastRow do begin
|
|
|
|
// OnGutterPaint(Self, cRow, aGutterOffs[cRow - aFirstRow],
|
|
|
|
// (vGutterRow - TopLine) * LineHeight);
|
|
|
|
// end;
|
|
|
|
// finally
|
|
|
|
// FreeMem(aGutterOffs);
|
|
|
|
// end;
|
|
|
|
// end;
|
|
|
|
for (cRow = aFirstRow; cRow <=aLastRow; cRow++) {
|
|
|
|
vLine = edit->rowToLine(cRow);
|
2021-05-24 00:41:00 +08:00
|
|
|
edit->onGutterPaint(*painter,vLine, 0, (cRow - edit->mTopLine) * edit->mTextHeight);
|
2021-05-21 23:33:53 +08:00
|
|
|
}
|
2021-05-14 23:56:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
QColor SynEditTextPainter::colEditorBG()
|
|
|
|
{
|
|
|
|
if (edit->mActiveLineColor.isValid() && bCurrentLine) {
|
|
|
|
return edit->mActiveLineColor;
|
|
|
|
} else {
|
|
|
|
if (edit->mHighlighter) {
|
|
|
|
PSynHighlighterAttribute attr = edit->mHighlighter->whitespaceAttribute();
|
|
|
|
if (attr && attr->background().isValid()) {
|
|
|
|
return attr->background();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-16 20:36:00 +08:00
|
|
|
return edit->palette().color(QPalette::Base);
|
2021-05-14 23:56:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::ComputeSelectionInfo()
|
|
|
|
{
|
|
|
|
BufferCoord vStart;
|
|
|
|
BufferCoord vEnd;
|
|
|
|
bAnySelection = false;
|
|
|
|
// Only if selection is visible anyway.
|
|
|
|
if (!edit->mHideSelection || edit->hasFocus()) {
|
|
|
|
bAnySelection = true;
|
|
|
|
// Get the *real* start of the selected area.
|
|
|
|
if (edit->mBlockBegin.Line < edit->mBlockEnd.Line) {
|
|
|
|
vStart = edit->mBlockBegin;
|
|
|
|
vEnd = edit->mBlockEnd;
|
|
|
|
} else if (edit->mBlockBegin.Line > edit->mBlockEnd.Line) {
|
|
|
|
vEnd = edit->mBlockBegin;
|
|
|
|
vStart = edit->mBlockEnd;
|
|
|
|
} else if (edit->mBlockBegin.Char != edit->mBlockEnd.Char) {
|
|
|
|
// No selection at all, or it is only on this line.
|
|
|
|
vStart.Line = edit->mBlockBegin.Line;
|
|
|
|
vEnd.Line = vStart.Line;
|
|
|
|
if (edit->mBlockBegin.Char < edit->mBlockEnd.Char) {
|
|
|
|
vStart.Char = edit->mBlockBegin.Char;
|
|
|
|
vEnd.Char = edit->mBlockEnd.Char;
|
|
|
|
} else {
|
|
|
|
vStart.Char = edit->mBlockEnd.Char;
|
|
|
|
vEnd.Char = edit->mBlockBegin.Char;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
bAnySelection = false;
|
2021-10-05 21:25:23 +08:00
|
|
|
if (edit->mInputPreeditString.length()>0) {
|
|
|
|
if (vStart.Line == edit->mCaretY && vStart.Char >=edit->mCaretX) {
|
|
|
|
vStart.Char+=edit->mInputPreeditString.length();
|
|
|
|
}
|
|
|
|
if (vEnd.Line == edit->mCaretY && vEnd.Char >edit->mCaretX) {
|
|
|
|
vEnd.Char+=edit->mInputPreeditString.length();
|
|
|
|
}
|
|
|
|
}
|
2021-05-14 23:56:43 +08:00
|
|
|
// If there is any visible selection so far, then test if there is an
|
|
|
|
// intersection with the area to be painted.
|
|
|
|
if (bAnySelection) {
|
|
|
|
// Don't care if the selection is not visible.
|
2021-08-16 23:17:48 +08:00
|
|
|
bAnySelection = (vEnd.Line >= vFirstLine) && (vStart.Line <= vLastLine);
|
2021-05-14 23:56:43 +08:00
|
|
|
if (bAnySelection) {
|
|
|
|
// Transform the selection from text space into screen space
|
|
|
|
vSelStart = edit->bufferToDisplayPos(vStart);
|
|
|
|
vSelEnd = edit->bufferToDisplayPos(vEnd);
|
2021-10-05 21:25:23 +08:00
|
|
|
if (edit->mInputPreeditString.length()
|
|
|
|
&& vStart.Line == edit->mCaretY) {
|
|
|
|
QString sLine = edit->lineText().left(edit->mCaretX-1)
|
|
|
|
+ edit->mInputPreeditString
|
|
|
|
+ edit->lineText().mid(edit->mCaretX-1);
|
|
|
|
vSelStart.Column = edit->charToColumn(sLine,vStart.Char);
|
|
|
|
}
|
|
|
|
if (edit->mInputPreeditString.length()
|
|
|
|
&& vEnd.Line == edit->mCaretY) {
|
|
|
|
QString sLine = edit->lineText().left(edit->mCaretX-1)
|
|
|
|
+ edit->mInputPreeditString
|
|
|
|
+ edit->lineText().mid(edit->mCaretX-1);
|
|
|
|
vSelEnd.Column = edit->charToColumn(sLine,vEnd.Char);
|
|
|
|
}
|
2021-05-14 23:56:43 +08:00
|
|
|
// In the column selection mode sort the begin and end of the selection,
|
|
|
|
// this makes the painting code simpler.
|
|
|
|
if (edit->mActiveSelectionMode == SynSelectionMode::smColumn && vSelStart.Column > vSelEnd.Column)
|
|
|
|
std::swap(vSelStart.Column, vSelEnd.Column);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::setDrawingColors(bool Selected)
|
|
|
|
{
|
|
|
|
if (Selected) {
|
|
|
|
painter->setPen(colSelFG);
|
|
|
|
painter->setBrush(colSelBG);
|
|
|
|
painter->setBackground(colSelBG);
|
|
|
|
} else {
|
|
|
|
painter->setPen(colFG);
|
|
|
|
painter->setBrush(colBG);
|
|
|
|
painter->setBackground(colBG);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int SynEditTextPainter::ColumnToXValue(int Col)
|
|
|
|
{
|
2021-05-24 21:48:03 +08:00
|
|
|
return edit->textOffset() + (Col - 1) * edit->mCharWidth;
|
2021-05-14 23:56:43 +08:00
|
|
|
}
|
|
|
|
|
2021-05-16 20:36:00 +08:00
|
|
|
void SynEditTextPainter::PaintToken(const QString &Token, int TokenCols, int ColumnsBefore, int First, int Last, bool)
|
2021-05-14 23:56:43 +08:00
|
|
|
{
|
|
|
|
bool startPaint;
|
|
|
|
int nX;
|
|
|
|
|
|
|
|
if (Last >= First && rcToken.right() > rcToken.left()) {
|
2021-05-27 01:05:49 +08:00
|
|
|
// qDebug()<<"Paint Token"<<Token<<ColumnsBefore<<TokenCols<<First<<Last;
|
2021-05-14 23:56:43 +08:00
|
|
|
nX = ColumnToXValue(First);
|
2021-05-16 20:36:00 +08:00
|
|
|
First -= ColumnsBefore;
|
|
|
|
Last -= ColumnsBefore;
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(rcToken,painter->brush());
|
2021-05-16 20:36:00 +08:00
|
|
|
if (First > TokenCols) {
|
2021-05-14 23:56:43 +08:00
|
|
|
} else {
|
2021-05-24 00:41:00 +08:00
|
|
|
int tokenColLen=0;
|
|
|
|
startPaint = false;
|
|
|
|
for (int i=0;i<Token.length();i++) {
|
|
|
|
int charCols;
|
|
|
|
if (Token[i] == SynTabChar) {
|
|
|
|
charCols = edit->mTabWidth - ((ColumnsBefore+tokenColLen) % edit->mTabWidth);
|
|
|
|
} else {
|
|
|
|
charCols = edit->charColumns(Token[i]);
|
|
|
|
}
|
|
|
|
if (tokenColLen+charCols>=First) {
|
|
|
|
if (!startPaint && (tokenColLen+1!=First)) {
|
|
|
|
nX-= (First - tokenColLen - 1) * edit->mCharWidth;
|
|
|
|
}
|
|
|
|
startPaint = true;
|
|
|
|
}
|
2021-10-05 21:25:23 +08:00
|
|
|
if (tokenColLen+charCols > Last)
|
|
|
|
break;
|
2021-05-24 18:11:07 +08:00
|
|
|
//painter->drawText(nX,rcToken.bottom()-painter->fontMetrics().descent()*edit->dpiFactor() , Token[i]);
|
2021-05-24 21:48:03 +08:00
|
|
|
if (startPaint) {
|
|
|
|
painter->drawText(nX,rcToken.bottom()-painter->fontMetrics().descent() , Token[i]);
|
|
|
|
nX += charCols * edit->mCharWidth;
|
|
|
|
}
|
2021-05-24 00:41:00 +08:00
|
|
|
|
|
|
|
tokenColLen += charCols;
|
|
|
|
}
|
2021-05-14 23:56:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
rcToken.setLeft(rcToken.right());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-21 23:33:53 +08:00
|
|
|
void SynEditTextPainter::PaintEditAreas(const SynEditingAreaList &areaList)
|
2021-05-16 20:36:00 +08:00
|
|
|
{
|
|
|
|
QRect rc;
|
|
|
|
int x1,x2;
|
|
|
|
int offset;
|
2021-05-26 18:32:17 +08:00
|
|
|
//painter->setClipRect(rcLine);
|
2021-05-16 20:36:00 +08:00
|
|
|
rc=rcLine;
|
|
|
|
rc.setBottom(rc.bottom()-1);
|
|
|
|
setDrawingColors(false);
|
2021-08-29 10:14:07 +08:00
|
|
|
for (const PSynEditingArea& p:areaList) {
|
2021-05-16 20:36:00 +08:00
|
|
|
if (p->beginX > LastCol)
|
|
|
|
continue;
|
|
|
|
if (p->endX < FirstCol)
|
|
|
|
continue;
|
|
|
|
if (p->beginX < FirstCol)
|
|
|
|
x1 = FirstCol;
|
|
|
|
else
|
|
|
|
x1 = p->beginX;
|
|
|
|
if (p->endX > LastCol)
|
|
|
|
x2 = LastCol;
|
|
|
|
else
|
|
|
|
x2 = p->endX;
|
|
|
|
rc.setLeft(ColumnToXValue(x1));
|
|
|
|
rc.setRight(ColumnToXValue(x2));
|
|
|
|
painter->setPen(p->color);
|
|
|
|
painter->setBrush(Qt::NoBrush);
|
|
|
|
switch(p->type) {
|
|
|
|
case SynEditingAreaType::eatRectangleBorder:
|
|
|
|
painter->drawRect(rc);
|
|
|
|
break;
|
|
|
|
case SynEditingAreaType::eatUnderLine:
|
|
|
|
painter->drawLine(rc.left(),rc.bottom(),rc.right(),rc.bottom());
|
|
|
|
break;
|
|
|
|
case SynEditingAreaType::eatWaveUnderLine:
|
|
|
|
offset=3;
|
|
|
|
int lastX=rc.left();
|
|
|
|
int lastY=rc.bottom()-offset;
|
|
|
|
int t = rc.left();
|
2021-06-24 16:05:19 +08:00
|
|
|
while (t<rc.right()) {
|
2021-05-16 20:36:00 +08:00
|
|
|
t+=3;
|
|
|
|
if (t>rc.right())
|
|
|
|
t = rc.right();
|
|
|
|
offset = 3 - offset;
|
|
|
|
painter->drawLine(lastX,lastY,t,rc.bottom()-offset);
|
|
|
|
lastX = t;
|
|
|
|
lastY = rc.bottom()-offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::PaintHighlightToken(bool bFillToEOL)
|
|
|
|
{
|
|
|
|
bool bComplexToken;
|
|
|
|
int nC1, nC2, nC1Sel, nC2Sel;
|
|
|
|
bool bU1, bSel, bU2;
|
|
|
|
int nX1, nX2;
|
|
|
|
// Compute some helper variables.
|
|
|
|
nC1 = std::max(FirstCol, TokenAccu.ColumnsBefore + 1);
|
|
|
|
nC2 = std::min(LastCol, TokenAccu.ColumnsBefore + TokenAccu.Columns + 1);
|
|
|
|
if (bComplexLine) {
|
|
|
|
bU1 = (nC1 < nLineSelStart);
|
|
|
|
bSel = (nC1 < nLineSelEnd) && (nC2 >= nLineSelStart);
|
|
|
|
bU2 = (nC2 >= nLineSelEnd);
|
2021-08-16 23:17:48 +08:00
|
|
|
bComplexToken = bSel && (bU1 || bU2);
|
2021-05-16 20:36:00 +08:00
|
|
|
} else {
|
|
|
|
bSel = bLineSelected;
|
|
|
|
bComplexToken = false;
|
2021-10-24 15:17:31 +08:00
|
|
|
bU1 = false; // to shut up compiler warning.
|
|
|
|
bU2 = false; // to shut up compiler warning.
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
|
|
|
// Any token chars accumulated?
|
|
|
|
if (TokenAccu.Columns > 0) {
|
|
|
|
// Initialize the colors and the font style.
|
|
|
|
if (!bSpecialLine) {
|
|
|
|
colBG = TokenAccu.BG;
|
|
|
|
colFG = TokenAccu.FG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bSpecialLine && edit->mOptions.testFlag(eoSpecialLineDefaultFg))
|
|
|
|
colFG = TokenAccu.FG;
|
|
|
|
QFont font = edit->font();
|
2021-05-26 18:32:17 +08:00
|
|
|
font.setBold(TokenAccu.Style & SynFontStyle::fsBold);
|
|
|
|
font.setItalic(TokenAccu.Style & SynFontStyle::fsItalic);
|
|
|
|
font.setStrikeOut(TokenAccu.Style & SynFontStyle::fsStrikeOut);
|
|
|
|
font.setUnderline(TokenAccu.Style & SynFontStyle::fsUnderline);
|
2021-05-16 20:36:00 +08:00
|
|
|
painter->setFont(font);
|
|
|
|
// Paint the chars
|
|
|
|
if (bComplexToken) {
|
|
|
|
// first unselected part of the token
|
|
|
|
if (bU1) {
|
|
|
|
setDrawingColors(false);
|
|
|
|
rcToken.setRight(ColumnToXValue(nLineSelStart));
|
|
|
|
PaintToken(TokenAccu.s,TokenAccu.Columns,TokenAccu.ColumnsBefore,nC1,nLineSelStart,false);
|
|
|
|
}
|
|
|
|
// selected part of the token
|
|
|
|
setDrawingColors(true);
|
|
|
|
nC1Sel = std::max(nLineSelStart, nC1);
|
|
|
|
nC2Sel = std::min(nLineSelEnd, nC2);
|
|
|
|
rcToken.setRight(ColumnToXValue(nC2Sel));
|
|
|
|
PaintToken(TokenAccu.s, TokenAccu.Columns, TokenAccu.ColumnsBefore, nC1Sel, nC2Sel,true);
|
|
|
|
// second unselected part of the token
|
|
|
|
if (bU2) {
|
|
|
|
setDrawingColors(false);
|
|
|
|
rcToken.setRight(ColumnToXValue(nC2));
|
|
|
|
PaintToken(TokenAccu.s, TokenAccu.Columns, TokenAccu.ColumnsBefore,nLineSelEnd, nC2,false);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setDrawingColors(bSel);
|
|
|
|
rcToken.setRight(ColumnToXValue(nC2));
|
|
|
|
PaintToken(TokenAccu.s, TokenAccu.Columns, TokenAccu.ColumnsBefore, nC1, nC2,bSel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill the background to the end of this line if necessary.
|
|
|
|
if (bFillToEOL && rcToken.left() < rcLine.right()) {
|
|
|
|
if (!bSpecialLine)
|
|
|
|
colBG = colEditorBG();
|
|
|
|
if (bComplexLine) {
|
|
|
|
nX1 = ColumnToXValue(nLineSelStart);
|
|
|
|
nX2 = ColumnToXValue(nLineSelEnd);
|
|
|
|
if (rcToken.left() < nX1) {
|
|
|
|
setDrawingColors(false);
|
|
|
|
rcToken.setRight(nX1);
|
|
|
|
// if (TokenAccu.Len != 0 && TokenAccu.Style != SynFontStyle::fsNone)
|
|
|
|
// AdjustEndRect();
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(rcToken,painter->brush());
|
2021-05-16 20:36:00 +08:00
|
|
|
rcToken.setLeft(nX1);
|
|
|
|
}
|
|
|
|
if (rcToken.left() < nX2) {
|
|
|
|
setDrawingColors(true);
|
|
|
|
rcToken.setRight(nX2);
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(rcToken,painter->brush());
|
2021-05-16 20:36:00 +08:00
|
|
|
rcToken.setLeft(nX2);
|
|
|
|
}
|
|
|
|
if (rcToken.left() < rcLine.right()) {
|
|
|
|
setDrawingColors(false);
|
|
|
|
rcToken.setRight(rcLine.right());
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(rcToken,painter->brush());
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setDrawingColors(bLineSelected);
|
|
|
|
rcToken.setRight(rcLine.right());
|
|
|
|
// if (TokenAccu.Len != 0 && TokenAccu.Style != SynFontStyle::fsNone)
|
|
|
|
// AdjustEndRect();
|
2021-05-27 20:33:25 +08:00
|
|
|
painter->fillRect(rcToken,painter->brush());
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SynEditTextPainter::TokenIsSpaces(bool &bSpacesTest, const QString& Token, bool& bIsSpaces)
|
|
|
|
{
|
|
|
|
if (!bSpacesTest) {
|
|
|
|
bSpacesTest = true;
|
|
|
|
for (QChar ch:Token) {
|
|
|
|
//todo: should include tabs?
|
|
|
|
if (ch!= ' ') {
|
|
|
|
bIsSpaces = false;
|
|
|
|
return bIsSpaces;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bIsSpaces = true;
|
|
|
|
}
|
|
|
|
return bIsSpaces;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the token chars with the attributes in the TokenAccu
|
|
|
|
// record. This will paint any chars already stored if there is
|
|
|
|
// a (visible) change in the attributes.
|
2021-05-18 15:49:58 +08:00
|
|
|
void SynEditTextPainter::AddHighlightToken(const QString &Token, int ColumnsBefore,
|
|
|
|
int TokenColumns, int cLine, PSynHighlighterAttribute p_Attri)
|
2021-05-16 20:36:00 +08:00
|
|
|
{
|
|
|
|
bool bCanAppend;
|
|
|
|
QColor Foreground, Background;
|
|
|
|
SynFontStyles Style;
|
|
|
|
bool bSpacesTest,bIsSpaces;
|
|
|
|
|
2021-05-27 01:05:49 +08:00
|
|
|
// qDebug()<<"Add highlight token"<<Token<<ColumnsBefore<<TokenColumns<<cLine;
|
|
|
|
|
2021-05-16 20:36:00 +08:00
|
|
|
if (p_Attri) {
|
|
|
|
Foreground = p_Attri->foreground();
|
|
|
|
Background = p_Attri->background();
|
|
|
|
Style = p_Attri->styles();
|
|
|
|
} else {
|
|
|
|
Foreground = colFG;
|
|
|
|
Background = colBG;
|
|
|
|
Style = getFontStyles(edit->font());
|
|
|
|
}
|
|
|
|
|
2021-05-26 18:32:17 +08:00
|
|
|
if (!Background.isValid() || (edit->mActiveLineColor.isValid() && bCurrentLine)) {
|
2021-05-16 20:36:00 +08:00
|
|
|
Background = colEditorBG();
|
|
|
|
}
|
2021-05-26 18:32:17 +08:00
|
|
|
if (!Foreground.isValid()) {
|
2021-05-16 20:36:00 +08:00
|
|
|
Foreground = edit->palette().color(QPalette::Text);
|
|
|
|
}
|
|
|
|
|
2021-08-29 22:51:23 +08:00
|
|
|
edit->onPreparePaintHighlightToken(cLine,edit->mHighlighter->getTokenPos()+1,
|
2021-05-16 20:36:00 +08:00
|
|
|
Token,p_Attri,Style,Foreground,Background);
|
|
|
|
|
|
|
|
// Do we have to paint the old chars first, or can we just append?
|
|
|
|
bCanAppend = false;
|
|
|
|
bSpacesTest = false;
|
|
|
|
if (TokenAccu.Columns > 0) {
|
|
|
|
// font style must be the same or token is only spaces
|
|
|
|
if (TokenAccu.Style == Style || ( (Style & SynFontStyle::fsUnderline) == (TokenAccu.Style & fsUnderline)
|
|
|
|
&& TokenIsSpaces(bSpacesTest,Token,bIsSpaces)) ) {
|
|
|
|
// either special colors or same colors
|
|
|
|
if ((bSpecialLine && !(edit->mOptions.testFlag(SynEditorOption::eoSpecialLineDefaultFg))) || bLineSelected ||
|
|
|
|
// background color must be the same and
|
|
|
|
((TokenAccu.BG == Background) &&
|
|
|
|
// foreground color must be the same or token is only spaces
|
2021-05-24 18:11:07 +08:00
|
|
|
((TokenAccu.FG == Foreground) || (TokenIsSpaces(bSpacesTest,Token,bIsSpaces) && !edit->mOptions.testFlag(eoShowSpecialChars))))) {
|
2021-05-16 20:36:00 +08:00
|
|
|
bCanAppend = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we can't append it, then we have to paint the old token chars first.
|
|
|
|
if (!bCanAppend)
|
|
|
|
PaintHighlightToken(false);
|
|
|
|
}
|
|
|
|
// Don't use AppendStr because it's more expensive.
|
|
|
|
if (bCanAppend) {
|
|
|
|
TokenAccu.s.append(Token);
|
|
|
|
TokenAccu.Columns+=TokenColumns;
|
|
|
|
} else {
|
2021-05-26 18:32:17 +08:00
|
|
|
TokenAccu.Columns = TokenColumns;
|
|
|
|
TokenAccu.s = Token;
|
|
|
|
TokenAccu.ColumnsBefore = ColumnsBefore;
|
|
|
|
TokenAccu.FG = Foreground;
|
|
|
|
TokenAccu.BG = Background;
|
|
|
|
TokenAccu.Style = Style;
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::PaintFoldAttributes()
|
|
|
|
{
|
2021-05-26 18:32:17 +08:00
|
|
|
int TabSteps, LineIndent, LastNonBlank, X, Y, cRow, vLine;
|
2021-05-16 20:36:00 +08:00
|
|
|
// Paint indent guides. Use folds to determine indent value of these
|
|
|
|
// Use a separate loop so we can use a custom pen
|
|
|
|
// Paint indent guides using custom pen
|
2021-10-20 11:14:49 +08:00
|
|
|
if (edit->mCodeFolding.indentGuides || edit->mCodeFolding.fillIndents) {
|
|
|
|
QColor paintColor;
|
|
|
|
if (edit->mCodeFolding.indentGuidesColor.isValid()) {
|
|
|
|
paintColor = edit->mCodeFolding.indentGuidesColor;
|
|
|
|
} else {
|
|
|
|
paintColor = edit->palette().color(QPalette::Text);
|
|
|
|
}
|
|
|
|
QColor gradientStart = paintColor;
|
|
|
|
QColor gradientEnd = paintColor;
|
2021-05-16 20:36:00 +08:00
|
|
|
QPen oldPen = painter->pen();
|
2021-10-20 11:14:49 +08:00
|
|
|
|
2021-05-16 20:36:00 +08:00
|
|
|
// Now loop through all the lines. The indices are valid for Lines.
|
|
|
|
for (cRow = aFirstRow; cRow<=aLastRow;cRow++) {
|
|
|
|
vLine = edit->rowToLine(cRow);
|
|
|
|
if (vLine > edit->mLines->count() && edit->mLines->count() > 0)
|
|
|
|
break;
|
|
|
|
// Set vertical coord
|
|
|
|
Y = (vLine - edit->mTopLine) * edit->mTextHeight; // limit inside clip rect
|
|
|
|
if (edit->mTextHeight % 2 == 1 && vLine % 2 == 0) {
|
|
|
|
Y++;
|
|
|
|
}
|
|
|
|
// Get next nonblank line
|
|
|
|
LastNonBlank = vLine - 1;
|
2021-10-12 20:45:26 +08:00
|
|
|
while (LastNonBlank + 1 < edit->mLines->count() && edit->mLines->getString(LastNonBlank).isEmpty())
|
2021-05-16 20:36:00 +08:00
|
|
|
LastNonBlank++;
|
2021-10-13 11:32:59 +08:00
|
|
|
if (LastNonBlank>=edit->lines()->count())
|
|
|
|
continue;
|
2021-05-21 23:33:53 +08:00
|
|
|
LineIndent = edit->getLineIndent(edit->mLines->getString(LastNonBlank));
|
2021-10-12 20:38:39 +08:00
|
|
|
int braceLevel = edit->mLines->ranges(LastNonBlank).braceLevel;
|
|
|
|
int indentLevel = braceLevel ;
|
|
|
|
if (edit->mTabWidth>0)
|
|
|
|
indentLevel = LineIndent / edit->mTabWidth;
|
|
|
|
int levelDiff = std::max(0,braceLevel - indentLevel);
|
2021-05-16 20:36:00 +08:00
|
|
|
// Step horizontal coord
|
2021-10-12 20:38:39 +08:00
|
|
|
//TabSteps = edit->mTabWidth;
|
|
|
|
TabSteps = 0;
|
|
|
|
indentLevel = 0;
|
2021-10-20 11:14:49 +08:00
|
|
|
|
2021-05-16 20:36:00 +08:00
|
|
|
while (TabSteps < LineIndent) {
|
2021-05-24 21:48:03 +08:00
|
|
|
X = TabSteps * edit->mCharWidth + edit->textOffset() - 2;
|
2021-05-16 20:36:00 +08:00
|
|
|
TabSteps+=edit->mTabWidth;
|
2021-10-12 20:38:39 +08:00
|
|
|
indentLevel++ ;
|
|
|
|
if (edit->mHighlighter) {
|
2021-10-20 11:14:49 +08:00
|
|
|
if (edit->mCodeFolding.indentGuides) {
|
|
|
|
PSynHighlighterAttribute attr = edit->mHighlighter->symbolAttribute();
|
|
|
|
GetBraceColorAttr(indentLevel,attr);
|
|
|
|
paintColor = attr->foreground();
|
2021-10-12 20:38:39 +08:00
|
|
|
}
|
2021-10-20 11:14:49 +08:00
|
|
|
if (edit->mCodeFolding.fillIndents) {
|
|
|
|
PSynHighlighterAttribute attr = edit->mHighlighter->symbolAttribute();
|
|
|
|
GetBraceColorAttr(indentLevel,attr);
|
|
|
|
gradientStart=attr->foreground();
|
|
|
|
attr = edit->mHighlighter->symbolAttribute();
|
|
|
|
GetBraceColorAttr(indentLevel+1,attr);
|
|
|
|
gradientStart=attr->foreground();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (edit->mCodeFolding.fillIndents) {
|
2021-10-20 20:15:16 +08:00
|
|
|
int X1;
|
|
|
|
if (TabSteps>LineIndent)
|
|
|
|
X1 = LineIndent * edit->mCharWidth + edit->textOffset() - 2;
|
|
|
|
else
|
|
|
|
X1 = TabSteps * edit->mCharWidth + edit->textOffset() - 2;
|
2021-10-20 11:14:49 +08:00
|
|
|
gradientStart.setAlpha(20);
|
|
|
|
gradientEnd.setAlpha(10);
|
|
|
|
QLinearGradient gradient(X,Y,X1,Y);
|
2021-10-20 20:15:16 +08:00
|
|
|
gradient.setColorAt(1,gradientStart);
|
|
|
|
gradient.setColorAt(0,gradientEnd);
|
|
|
|
painter->fillRect(X,Y,(X1-X),edit->mTextHeight,gradient);
|
2021-10-12 20:38:39 +08:00
|
|
|
}
|
2021-05-16 20:36:00 +08:00
|
|
|
|
|
|
|
// Move to top of vertical line
|
2021-10-20 11:14:49 +08:00
|
|
|
if (edit->mCodeFolding.indentGuides) {
|
|
|
|
QPen dottedPen(Qt::PenStyle::DashLine);
|
|
|
|
dottedPen.setColor(paintColor);
|
|
|
|
painter->setPen(dottedPen);
|
|
|
|
painter->drawLine(X,Y,X,Y+edit->mTextHeight);
|
|
|
|
}
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
painter->setPen(oldPen);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!edit->mUseCodeFolding)
|
2021-05-21 23:33:53 +08:00
|
|
|
return;
|
2021-05-16 20:36:00 +08:00
|
|
|
|
|
|
|
// Paint collapsed lines using changed pen
|
|
|
|
if (edit->mCodeFolding.showCollapsedLine) {
|
|
|
|
painter->setPen(edit->mCodeFolding.collapsedLineColor);
|
|
|
|
for (int i=0; i< edit->mAllFoldRanges.count();i++) {
|
|
|
|
PSynEditFoldRange range = edit->mAllFoldRanges[i];
|
|
|
|
if (range->collapsed && !range->parentCollapsed() &&
|
|
|
|
(range->fromLine <= vLastLine) && (range->fromLine >= vFirstLine) ) {
|
|
|
|
// Get starting and end points
|
|
|
|
Y = (edit->lineToRow(range->fromLine) - edit->mTopLine + 1) * edit->mTextHeight - 1;
|
|
|
|
painter->drawLine(AClip.left(),Y, AClip.right(),Y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::GetBraceColorAttr(int level, PSynHighlighterAttribute &attr)
|
|
|
|
{
|
|
|
|
if (!edit->mOptions.testFlag(SynEditorOption::eoShowRainbowColor))
|
|
|
|
return;
|
|
|
|
if (attr != edit->mHighlighter->symbolAttribute())
|
|
|
|
return;
|
2021-09-19 17:59:03 +08:00
|
|
|
PSynHighlighterAttribute oldAttr = attr;
|
2021-05-16 20:36:00 +08:00
|
|
|
switch(level % 4) {
|
|
|
|
case 0:
|
2021-09-19 17:59:03 +08:00
|
|
|
attr = edit->mRainbowAttr0;
|
2021-05-26 18:32:17 +08:00
|
|
|
break;
|
2021-05-16 20:36:00 +08:00
|
|
|
case 1:
|
2021-09-19 17:59:03 +08:00
|
|
|
attr = edit->mRainbowAttr1;
|
2021-05-26 18:32:17 +08:00
|
|
|
break;
|
2021-05-16 20:36:00 +08:00
|
|
|
case 2:
|
2021-09-19 17:59:03 +08:00
|
|
|
attr = edit->mRainbowAttr2;
|
2021-05-26 18:32:17 +08:00
|
|
|
break;
|
2021-05-16 20:36:00 +08:00
|
|
|
case 3:
|
2021-09-19 17:59:03 +08:00
|
|
|
attr = edit->mRainbowAttr3;
|
2021-05-26 18:32:17 +08:00
|
|
|
break;
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
2021-09-19 17:59:03 +08:00
|
|
|
if (!attr)
|
|
|
|
attr = oldAttr;
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void SynEditTextPainter::PaintLines()
|
|
|
|
{
|
|
|
|
int cRow; // row index for the loop
|
|
|
|
int vLine;
|
|
|
|
QString sLine; // the current line
|
|
|
|
QString sToken; // highlighter token info
|
2021-05-26 18:32:17 +08:00
|
|
|
int nTokenColumnsBefore, nTokenColumnLen;
|
2021-05-16 20:36:00 +08:00
|
|
|
PSynHighlighterAttribute attr;
|
|
|
|
int vFirstChar;
|
|
|
|
int vLastChar;
|
2021-05-21 23:33:53 +08:00
|
|
|
SynEditingAreaList areaList;
|
2021-05-16 20:36:00 +08:00
|
|
|
PSynEditFoldRange foldRange;
|
2021-10-05 21:25:23 +08:00
|
|
|
PSynHighlighterAttribute preeditAttr;
|
2021-05-24 00:41:00 +08:00
|
|
|
int nFold;
|
2021-05-16 20:36:00 +08:00
|
|
|
QString sFold;
|
|
|
|
|
|
|
|
// Initialize rcLine for drawing. Note that Top and Bottom are updated
|
|
|
|
// inside the loop. Get only the starting point for this.
|
|
|
|
rcLine = AClip;
|
|
|
|
rcLine.setBottom((aFirstRow - edit->mTopLine) * edit->mTextHeight);
|
|
|
|
TokenAccu.Columns = 0;
|
|
|
|
TokenAccu.ColumnsBefore = 0;
|
|
|
|
// Now loop through all the lines. The indices are valid for Lines.
|
2021-05-18 15:49:58 +08:00
|
|
|
for (cRow = aFirstRow; cRow<=aLastRow; cRow++) {
|
|
|
|
vLine = edit->rowToLine(cRow);
|
|
|
|
if (vLine > edit->mLines->count() && edit->mLines->count() != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Get the line.
|
|
|
|
sLine = edit->mLines->getString(vLine - 1);
|
|
|
|
// determine whether will be painted with ActiveLineColor
|
|
|
|
bCurrentLine = (edit->mCaretY == vLine);
|
2021-10-05 00:42:35 +08:00
|
|
|
if (bCurrentLine && !edit->mInputPreeditString.isEmpty()) {
|
|
|
|
sLine = sLine.left(edit->mCaretX-1) + edit->mInputPreeditString
|
|
|
|
+ sLine.mid(edit->mCaretX-1);
|
|
|
|
}
|
2021-05-18 15:49:58 +08:00
|
|
|
// Initialize the text and background colors, maybe the line should
|
|
|
|
// use special values for them.
|
|
|
|
colFG = edit->palette().color(QPalette::Text);
|
|
|
|
colBG = colEditorBG();
|
2021-05-24 00:41:00 +08:00
|
|
|
bSpecialLine = edit->onGetSpecialLineColors(vLine, colFG, colBG);
|
2021-05-24 18:11:07 +08:00
|
|
|
|
2021-05-18 15:49:58 +08:00
|
|
|
if (bSpecialLine) {
|
|
|
|
// The selection colors are just swapped, like seen in Delphi.
|
|
|
|
colSelFG = colBG;
|
|
|
|
colSelBG = colFG;
|
|
|
|
} else {
|
|
|
|
colSelFG = edit->mSelectedForeground;
|
|
|
|
colSelBG = edit->mSelectedBackground;
|
|
|
|
}
|
2021-05-24 00:41:00 +08:00
|
|
|
edit->onGetEditingAreas(vLine, areaList);
|
2021-05-18 15:49:58 +08:00
|
|
|
// Removed word wrap support
|
|
|
|
vFirstChar = FirstCol;
|
|
|
|
vLastChar = LastCol;
|
|
|
|
// Get the information about the line selection. Three different parts
|
|
|
|
// are possible (unselected before, selected, unselected after), only
|
|
|
|
// unselected or only selected means bComplexLine will be FALSE. Start
|
|
|
|
// with no selection, compute based on the visible columns.
|
|
|
|
bComplexLine = false;
|
|
|
|
nLineSelStart = 0;
|
|
|
|
nLineSelEnd = 0;
|
|
|
|
// Does the selection intersect the visible area?
|
|
|
|
if (bAnySelection && (cRow >= vSelStart.Row) && (cRow <= vSelEnd.Row)) {
|
|
|
|
// Default to a fully selected line. This is correct for the smLine
|
|
|
|
// selection mode and a good start for the smNormal mode.
|
|
|
|
nLineSelStart = FirstCol;
|
|
|
|
nLineSelEnd = LastCol + 1;
|
|
|
|
if ((edit->mActiveSelectionMode == SynSelectionMode::smColumn) ||
|
|
|
|
((edit->mActiveSelectionMode == SynSelectionMode::smNormal) && (cRow == vSelStart.Row)) ) {
|
|
|
|
if (vSelStart.Column > LastCol) {
|
|
|
|
nLineSelStart = 0;
|
|
|
|
nLineSelEnd = 0;
|
|
|
|
} else if (vSelStart.Column > FirstCol) {
|
|
|
|
nLineSelStart = vSelStart.Column;
|
|
|
|
bComplexLine = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( (edit->mActiveSelectionMode == SynSelectionMode::smColumn) ||
|
|
|
|
((edit->mActiveSelectionMode == SynSelectionMode::smNormal) && (cRow == vSelEnd.Row)) ) {
|
|
|
|
if (vSelEnd.Column < FirstCol) {
|
|
|
|
nLineSelStart = 0;
|
|
|
|
nLineSelEnd = 0;
|
|
|
|
} else if (vSelEnd.Column < LastCol) {
|
|
|
|
nLineSelEnd = vSelEnd.Column;
|
|
|
|
bComplexLine = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} //endif bAnySelection
|
|
|
|
|
|
|
|
// Update the rcLine rect to this line.
|
|
|
|
rcLine.setTop(rcLine.bottom());
|
|
|
|
rcLine.setBottom(rcLine.bottom()+edit->mTextHeight);
|
|
|
|
|
|
|
|
bLineSelected = (!bComplexLine) && (nLineSelStart > 0);
|
|
|
|
rcToken = rcLine;
|
|
|
|
if (!edit->mHighlighter || !edit->mHighlighter->enabled()) {
|
|
|
|
sToken = sLine;
|
2021-10-05 00:42:35 +08:00
|
|
|
if (bCurrentLine) {
|
|
|
|
nTokenColumnLen = edit->stringColumns(sLine,0);
|
|
|
|
} else {
|
|
|
|
nTokenColumnLen = edit->mLines->lineColumns(vLine-1);
|
|
|
|
}
|
2021-05-24 18:11:07 +08:00
|
|
|
if (edit->mOptions.testFlag(eoShowSpecialChars) && (!bLineSelected) && (!bSpecialLine) && (nTokenColumnLen < vLastChar)) {
|
2021-05-18 15:49:58 +08:00
|
|
|
sToken = sToken + SynLineBreakGlyph;
|
2021-05-21 23:33:53 +08:00
|
|
|
nTokenColumnLen += edit->charColumns(SynLineBreakGlyph);
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
|
|
|
if (bComplexLine) {
|
2021-10-05 21:25:23 +08:00
|
|
|
setDrawingColors(true);
|
|
|
|
rcToken.setLeft(std::max(rcLine.left(), ColumnToXValue(nLineSelStart)));
|
|
|
|
rcToken.setRight(std::min(rcLine.right(), ColumnToXValue(nLineSelEnd)));
|
|
|
|
PaintToken(sToken, nTokenColumnLen, 0, nLineSelStart, nLineSelEnd,false);
|
2021-05-18 15:49:58 +08:00
|
|
|
setDrawingColors(false);
|
|
|
|
rcToken.setLeft(std::max(rcLine.left(), ColumnToXValue(FirstCol)));
|
|
|
|
rcToken.setRight(std::min(rcLine.right(), ColumnToXValue(nLineSelStart)));
|
2021-05-21 23:33:53 +08:00
|
|
|
PaintToken(sToken, nTokenColumnLen, 0, FirstCol, nLineSelStart,false);
|
2021-05-18 15:49:58 +08:00
|
|
|
rcToken.setLeft(std::max(rcLine.left(), ColumnToXValue(nLineSelEnd)));
|
|
|
|
rcToken.setRight(std::min(rcLine.right(), ColumnToXValue(LastCol)));
|
2021-05-21 23:33:53 +08:00
|
|
|
PaintToken(sToken, nTokenColumnLen, 0, nLineSelEnd, LastCol,true);
|
2021-05-18 15:49:58 +08:00
|
|
|
} else {
|
|
|
|
setDrawingColors(bLineSelected);
|
2021-05-21 23:33:53 +08:00
|
|
|
PaintToken(sToken, nTokenColumnLen, 0, FirstCol, LastCol,bLineSelected);
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
2021-10-05 21:25:23 +08:00
|
|
|
//Paint editingAreaBorders
|
|
|
|
if (bCurrentLine && edit->mInputPreeditString.length()>0) {
|
|
|
|
PSynEditingArea area = std::make_shared<SynEditingArea>();
|
|
|
|
area->beginX = edit->charToColumn(sLine,edit->mCaretX);
|
|
|
|
area->endX = edit->charToColumn(sLine,edit->mCaretX + edit->mInputPreeditString.length());
|
|
|
|
area->type = SynEditingAreaType::eatUnderLine;
|
|
|
|
area->color = colFG;
|
|
|
|
areaList.append(area);
|
|
|
|
PaintEditAreas(areaList);
|
|
|
|
}
|
2021-05-18 15:49:58 +08:00
|
|
|
} else {
|
|
|
|
// Initialize highlighter with line text and range info. It is
|
|
|
|
// necessary because we probably did not scan to the end of the last
|
|
|
|
// line - the internal highlighter range might be wrong.
|
|
|
|
if (vLine == 1) {
|
|
|
|
edit->mHighlighter->resetState();
|
|
|
|
} else {
|
|
|
|
edit->mHighlighter->setState(
|
2021-09-23 12:06:26 +08:00
|
|
|
edit->mLines->ranges(vLine-2));
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
2021-05-26 18:32:17 +08:00
|
|
|
edit->mHighlighter->setLine(sLine, vLine - 1);
|
2021-05-18 15:49:58 +08:00
|
|
|
// Try to concatenate as many tokens as possible to minimize the count
|
|
|
|
// of ExtTextOut calls necessary. This depends on the selection state
|
|
|
|
// or the line having special colors. For spaces the foreground color
|
|
|
|
// is ignored as well.
|
|
|
|
TokenAccu.Columns = 0;
|
2021-05-26 18:32:17 +08:00
|
|
|
nTokenColumnsBefore = 0;
|
2021-05-18 15:49:58 +08:00
|
|
|
// Test first whether anything of this token is visible.
|
2021-05-26 18:32:17 +08:00
|
|
|
while (!edit->mHighlighter->eol()) {
|
2021-05-18 15:49:58 +08:00
|
|
|
sToken = edit->mHighlighter->getToken();
|
|
|
|
// Work-around buggy highlighters which return empty tokens.
|
|
|
|
if (sToken.isEmpty()) {
|
|
|
|
edit->mHighlighter->next();
|
|
|
|
if (edit->mHighlighter->eol())
|
|
|
|
break;
|
|
|
|
sToken = edit->mHighlighter->getToken();
|
|
|
|
// Maybe should also test whether GetTokenPos changed...
|
2021-06-20 09:27:37 +08:00
|
|
|
if (sToken.isEmpty()) {
|
|
|
|
qDebug()<<SynEdit::tr("The highlighter seems to be in an infinite loop");
|
2021-05-18 15:49:58 +08:00
|
|
|
throw BaseError(SynEdit::tr("The highlighter seems to be in an infinite loop"));
|
2021-06-20 09:27:37 +08:00
|
|
|
}
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
2021-10-05 00:42:35 +08:00
|
|
|
nTokenColumnsBefore = edit->charToColumn(sLine,edit->mHighlighter->getTokenPos()+1)-1;
|
2021-05-27 01:05:49 +08:00
|
|
|
nTokenColumnLen = edit->stringColumns(sToken, nTokenColumnsBefore);
|
2021-05-26 18:32:17 +08:00
|
|
|
if (nTokenColumnsBefore + nTokenColumnLen >= vFirstChar) {
|
|
|
|
if (nTokenColumnsBefore + nTokenColumnLen >= vLastChar) {
|
|
|
|
if (nTokenColumnsBefore >= vLastChar)
|
2021-05-18 15:49:58 +08:00
|
|
|
break; //*** BREAK ***
|
2021-05-26 18:32:17 +08:00
|
|
|
nTokenColumnLen = vLastChar - nTokenColumnsBefore - 1;
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
|
|
|
// It's at least partially visible. Get the token attributes now.
|
|
|
|
attr = edit->mHighlighter->getTokenAttribute();
|
2021-10-20 11:14:49 +08:00
|
|
|
if (sToken == "["
|
|
|
|
|| sToken == "("
|
|
|
|
|| sToken == "{"
|
|
|
|
) {
|
|
|
|
SynRangeState rangeState = edit->mHighlighter->getRangeState();
|
|
|
|
GetBraceColorAttr(rangeState.bracketLevel
|
|
|
|
+rangeState.braceLevel
|
|
|
|
+rangeState.parenthesisLevel
|
|
|
|
,attr);
|
|
|
|
} else if (sToken == "]"
|
|
|
|
|| sToken == ")"
|
|
|
|
|| sToken == "}"
|
|
|
|
){
|
|
|
|
SynRangeState rangeState = edit->mHighlighter->getRangeState();
|
|
|
|
GetBraceColorAttr(rangeState.bracketLevel
|
|
|
|
+rangeState.braceLevel
|
|
|
|
+rangeState.parenthesisLevel+1,
|
|
|
|
attr);
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
2021-10-05 21:25:23 +08:00
|
|
|
if (bCurrentLine && edit->mInputPreeditString.length()>0) {
|
|
|
|
int startPos = edit->mHighlighter->getTokenPos()+1;
|
|
|
|
int endPos = edit->mHighlighter->getTokenPos() + sToken.length();
|
2021-10-29 12:29:21 +08:00
|
|
|
//qDebug()<<startPos<<":"<<endPos<<" - "+sToken+" - "<<edit->mCaretX<<":"<<edit->mCaretX+edit->mInputPreeditString.length();
|
2021-10-05 21:25:23 +08:00
|
|
|
if (!(endPos < edit->mCaretX
|
|
|
|
|| startPos >= edit->mCaretX+edit->mInputPreeditString.length())) {
|
|
|
|
if (!preeditAttr) {
|
|
|
|
preeditAttr = attr;
|
|
|
|
} else {
|
|
|
|
attr = preeditAttr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-26 18:32:17 +08:00
|
|
|
AddHighlightToken(sToken, nTokenColumnsBefore - (vFirstChar - FirstCol),
|
2021-05-21 23:33:53 +08:00
|
|
|
nTokenColumnLen, vLine,attr);
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
|
|
|
// Let the highlighter scan the next token.
|
|
|
|
edit->mHighlighter->next();
|
|
|
|
}
|
|
|
|
// Don't assume HL.GetTokenPos is valid after HL.GetEOL == True.
|
2021-05-26 18:32:17 +08:00
|
|
|
nTokenColumnsBefore += edit->stringColumns(sToken,nTokenColumnsBefore-1);
|
|
|
|
if (edit->mHighlighter->eol() && (nTokenColumnsBefore < vLastChar)) {
|
2021-05-18 15:49:58 +08:00
|
|
|
int lineColumns = edit->mLines->lineColumns(vLine-1);
|
|
|
|
// Draw text that couldn't be parsed by the highlighter, if any.
|
2021-05-26 18:32:17 +08:00
|
|
|
if (nTokenColumnsBefore < lineColumns) {
|
|
|
|
if (nTokenColumnsBefore + 1 < vFirstChar)
|
|
|
|
nTokenColumnsBefore = vFirstChar - 1;
|
|
|
|
nTokenColumnLen = std::min(lineColumns, vLastChar) - (nTokenColumnsBefore + 1);
|
2021-05-21 23:33:53 +08:00
|
|
|
if (nTokenColumnLen > 0) {
|
2021-05-26 18:32:17 +08:00
|
|
|
sToken = edit->substringByColumns(sLine,nTokenColumnsBefore+1,nTokenColumnLen);
|
|
|
|
AddHighlightToken(sToken, nTokenColumnsBefore - (vFirstChar - FirstCol),
|
2021-05-21 23:33:53 +08:00
|
|
|
nTokenColumnLen, vLine, PSynHighlighterAttribute());
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
|
|
|
}
|
2021-05-24 18:11:07 +08:00
|
|
|
// Draw LineBreak glyph.
|
|
|
|
if (edit->mOptions.testFlag(eoShowSpecialChars) && (!bLineSelected) &&
|
2021-05-18 15:49:58 +08:00
|
|
|
(!bSpecialLine) && (edit->mLines->lineColumns(vLine-1) < vLastChar)) {
|
2021-05-24 18:11:07 +08:00
|
|
|
AddHighlightToken(SynLineBreakGlyph,
|
|
|
|
edit->mLines->lineColumns(vLine-1) - (vFirstChar - FirstCol),
|
|
|
|
edit->charColumns(SynLineBreakGlyph),vLine, edit->mHighlighter->whitespaceAttribute());
|
|
|
|
}
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paint folding
|
|
|
|
foldRange = edit->foldStartAtLine(vLine);
|
|
|
|
if ((foldRange) && foldRange->collapsed) {
|
2021-05-21 23:33:53 +08:00
|
|
|
sFold = " ... } ";
|
|
|
|
nFold = edit->stringColumns(sFold,edit->mLines->lineColumns(vLine-1));
|
|
|
|
attr = edit->mHighlighter->symbolAttribute();
|
2021-09-23 12:06:26 +08:00
|
|
|
GetBraceColorAttr(edit->mHighlighter->getRangeState().braceLevel,attr);
|
2021-05-21 23:33:53 +08:00
|
|
|
AddHighlightToken(sFold,edit->mLines->lineColumns(vLine-1)+1 - (vFirstChar - FirstCol)
|
|
|
|
, nFold, vLine, attr);
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw anything that's left in the TokenAccu record. Fill to the end
|
|
|
|
// of the invalid area with the correct colors.
|
|
|
|
PaintHighlightToken(true);
|
|
|
|
|
2021-10-05 21:25:23 +08:00
|
|
|
|
2021-05-18 15:49:58 +08:00
|
|
|
//Paint editingAreaBorders
|
2021-10-05 21:25:23 +08:00
|
|
|
foreach (const PSynEditingArea& area, areaList) {
|
|
|
|
if (bCurrentLine && edit->mInputPreeditString.length()>0) {
|
2021-10-05 23:30:34 +08:00
|
|
|
if (area->beginX > edit->mCaretX) {
|
2021-10-05 21:25:23 +08:00
|
|
|
area->beginX+=edit->mInputPreeditString.length();
|
|
|
|
}
|
|
|
|
if (area->endX > edit->mCaretX) {
|
|
|
|
area->endX+=edit->mInputPreeditString.length();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
area->beginX = edit->charToColumn(sLine, area->beginX);
|
|
|
|
area->endX = edit->charToColumn(sLine,area->endX);
|
|
|
|
}
|
2021-10-05 00:42:35 +08:00
|
|
|
if (bCurrentLine && edit->mInputPreeditString.length()>0) {
|
|
|
|
PSynEditingArea area = std::make_shared<SynEditingArea>();
|
2021-10-05 21:25:23 +08:00
|
|
|
area->beginX = edit->charToColumn(sLine, edit->mCaretX);
|
|
|
|
area->endX = edit->charToColumn(sLine, edit->mCaretX + edit->mInputPreeditString.length());
|
2021-10-05 00:42:35 +08:00
|
|
|
area->type = SynEditingAreaType::eatUnderLine;
|
2021-10-05 21:25:23 +08:00
|
|
|
if (preeditAttr) {
|
|
|
|
area->color = preeditAttr->foreground();
|
|
|
|
} else {
|
|
|
|
area->color = colFG;
|
|
|
|
}
|
2021-10-05 00:42:35 +08:00
|
|
|
areaList.append(area);
|
|
|
|
}
|
2021-05-18 15:49:58 +08:00
|
|
|
PaintEditAreas(areaList);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now paint the right edge if necessary. We do it line by line to reduce
|
|
|
|
// the flicker. Should not cost very much anyway, compared to the many
|
|
|
|
// calls to ExtTextOut.
|
|
|
|
if (bDoRightEdge) {
|
2021-05-24 22:09:14 +08:00
|
|
|
painter->setPen(edit->mRightEdgeColor);
|
2021-05-21 23:33:53 +08:00
|
|
|
painter->drawLine(nRightEdge, rcLine.top(),nRightEdge,rcLine.bottom()+1);
|
2021-05-18 15:49:58 +08:00
|
|
|
}
|
|
|
|
bCurrentLine = false;
|
|
|
|
}
|
2021-05-16 20:36:00 +08:00
|
|
|
}
|
|
|
|
|
2021-08-29 10:14:07 +08:00
|
|
|
void SynEditTextPainter::drawMark(PSynEditMark , int &, int )
|
2021-05-21 23:33:53 +08:00
|
|
|
{
|
|
|
|
//todo
|
|
|
|
}
|