notepad-plus-plus-legacy/scintilla/src/LineMarker.cxx

399 lines
15 KiB
C++

// Scintilla source code edit control
/** @file LineMarker.cxx
** Defines the look of a line marker in the margin.
**/
// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <string.h>
#include <math.h>
#include <vector>
#include <map>
#include "Platform.h"
#include "Scintilla.h"
#include "StringCopy.h"
#include "XPM.h"
#include "LineMarker.h"
#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif
void LineMarker::SetXPM(const char *textForm) {
delete pxpm;
pxpm = new XPM(textForm);
markType = SC_MARK_PIXMAP;
}
void LineMarker::SetXPM(const char *const *linesForm) {
delete pxpm;
pxpm = new XPM(linesForm);
markType = SC_MARK_PIXMAP;
}
void LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) {
delete image;
image = new RGBAImage(static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y), scale, pixelsRGBAImage);
markType = SC_MARK_RGBAIMAGE;
}
static void DrawBox(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
PRectangle rc = PRectangle::FromInts(
centreX - armSize,
centreY - armSize,
centreX + armSize + 1,
centreY + armSize + 1);
surface->RectangleDraw(rc, back, fore);
}
static void DrawCircle(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
PRectangle rcCircle = PRectangle::FromInts(
centreX - armSize,
centreY - armSize,
centreX + armSize + 1,
centreY + armSize + 1);
surface->Ellipse(rcCircle, back, fore);
}
static void DrawPlus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
PRectangle rcV = PRectangle::FromInts(centreX, centreY - armSize + 2, centreX + 1, centreY + armSize - 2 + 1);
surface->FillRectangle(rcV, fore);
PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1);
surface->FillRectangle(rcH, fore);
}
static void DrawMinus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1);
surface->FillRectangle(rcH, fore);
}
void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, typeOfFold tFold, int marginStyle) const {
if (customDraw != NULL) {
customDraw(surface, rcWhole, fontForCharacter, tFold, marginStyle, this);
return;
}
ColourDesired colourHead = back;
ColourDesired colourBody = back;
ColourDesired colourTail = back;
switch (tFold) {
case LineMarker::head :
case LineMarker::headWithTail :
colourHead = backSelected;
colourTail = backSelected;
break;
case LineMarker::body :
colourHead = backSelected;
colourBody = backSelected;
break;
case LineMarker::tail :
colourBody = backSelected;
colourTail = backSelected;
break;
default :
// LineMarker::undefined
break;
}
if ((markType == SC_MARK_PIXMAP) && (pxpm)) {
pxpm->Draw(surface, rcWhole);
return;
}
if ((markType == SC_MARK_RGBAIMAGE) && (image)) {
// Make rectangle just large enough to fit image centred on centre of rcWhole
PRectangle rcImage;
rcImage.top = ((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2;
rcImage.bottom = rcImage.top + image->GetScaledHeight();
rcImage.left = ((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2;
rcImage.right = rcImage.left + image->GetScaledWidth();
surface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels());
return;
}
// Restrict most shapes a bit
PRectangle rc = rcWhole;
rc.top++;
rc.bottom--;
int minDim = Platform::Minimum(static_cast<int>(rc.Width()), static_cast<int>(rc.Height()));
minDim--; // Ensure does not go beyond edge
int centreX = static_cast<int>(floor((rc.right + rc.left) / 2.0));
int centreY = static_cast<int>(floor((rc.bottom + rc.top) / 2.0));
int dimOn2 = minDim / 2;
int dimOn4 = minDim / 4;
int blobSize = dimOn2-1;
int armSize = dimOn2-2;
if (marginStyle == SC_MARGIN_NUMBER || marginStyle == SC_MARGIN_TEXT || marginStyle == SC_MARGIN_RTEXT) {
// On textual margins move marker to the left to try to avoid overlapping the text
centreX = static_cast<int>(rc.left) + dimOn2 + 1;
}
if (markType == SC_MARK_ROUNDRECT) {
PRectangle rcRounded = rc;
rcRounded.left = rc.left + 1;
rcRounded.right = rc.right - 1;
surface->RoundedRectangle(rcRounded, fore, back);
} else if (markType == SC_MARK_CIRCLE) {
PRectangle rcCircle = PRectangle::FromInts(
centreX - dimOn2,
centreY - dimOn2,
centreX + dimOn2,
centreY + dimOn2);
surface->Ellipse(rcCircle, fore, back);
} else if (markType == SC_MARK_ARROW) {
Point pts[] = {
Point::FromInts(centreX - dimOn4, centreY - dimOn2),
Point::FromInts(centreX - dimOn4, centreY + dimOn2),
Point::FromInts(centreX + dimOn2 - dimOn4, centreY),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_ARROWDOWN) {
Point pts[] = {
Point::FromInts(centreX - dimOn2, centreY - dimOn4),
Point::FromInts(centreX + dimOn2, centreY - dimOn4),
Point::FromInts(centreX, centreY + dimOn2 - dimOn4),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_PLUS) {
Point pts[] = {
Point::FromInts(centreX - armSize, centreY - 1),
Point::FromInts(centreX - 1, centreY - 1),
Point::FromInts(centreX - 1, centreY - armSize),
Point::FromInts(centreX + 1, centreY - armSize),
Point::FromInts(centreX + 1, centreY - 1),
Point::FromInts(centreX + armSize, centreY -1),
Point::FromInts(centreX + armSize, centreY +1),
Point::FromInts(centreX + 1, centreY + 1),
Point::FromInts(centreX + 1, centreY + armSize),
Point::FromInts(centreX - 1, centreY + armSize),
Point::FromInts(centreX - 1, centreY + 1),
Point::FromInts(centreX - armSize, centreY + 1),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_MINUS) {
Point pts[] = {
Point::FromInts(centreX - armSize, centreY - 1),
Point::FromInts(centreX + armSize, centreY -1),
Point::FromInts(centreX + armSize, centreY +1),
Point::FromInts(centreX - armSize, centreY + 1),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_SMALLRECT) {
PRectangle rcSmall;
rcSmall.left = rc.left + 1;
rcSmall.top = rc.top + 2;
rcSmall.right = rc.right - 1;
rcSmall.bottom = rc.bottom - 2;
surface->RectangleDraw(rcSmall, fore, back);
} else if (markType == SC_MARK_EMPTY || markType == SC_MARK_BACKGROUND ||
markType == SC_MARK_UNDERLINE || markType == SC_MARK_AVAILABLE) {
// An invisible marker so don't draw anything
} else if (markType == SC_MARK_VLINE) {
surface->PenColour(colourBody);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
} else if (markType == SC_MARK_LCORNER) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY);
surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
} else if (markType == SC_MARK_TCORNER) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, centreY);
surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
surface->PenColour(colourBody);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY + 1);
surface->PenColour(colourHead);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
} else if (markType == SC_MARK_LCORNERCURVE) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY-3);
surface->LineTo(centreX+3, centreY);
surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
} else if (markType == SC_MARK_TCORNERCURVE) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, centreY-3);
surface->LineTo(centreX+3, centreY);
surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
surface->PenColour(colourBody);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY-2);
surface->PenColour(colourHead);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
} else if (markType == SC_MARK_BOXPLUS) {
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_BOXPLUSCONNECTED) {
if (tFold == LineMarker::headWithTail)
surface->PenColour(colourTail);
else
surface->PenColour(colourBody);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
surface->PenColour(colourBody);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY - blobSize);
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
if (tFold == LineMarker::body) {
surface->PenColour(colourTail);
surface->MoveTo(centreX + 1, centreY + blobSize);
surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
surface->MoveTo(centreX + blobSize, centreY + blobSize);
surface->LineTo(centreX + blobSize, centreY - blobSize);
surface->MoveTo(centreX + 1, centreY - blobSize);
surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
}
} else if (markType == SC_MARK_BOXMINUS) {
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
} else if (markType == SC_MARK_BOXMINUSCONNECTED) {
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
surface->PenColour(colourBody);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY - blobSize);
if (tFold == LineMarker::body) {
surface->PenColour(colourTail);
surface->MoveTo(centreX + 1, centreY + blobSize);
surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
surface->MoveTo(centreX + blobSize, centreY + blobSize);
surface->LineTo(centreX + blobSize, centreY - blobSize);
surface->MoveTo(centreX + 1, centreY - blobSize);
surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
}
} else if (markType == SC_MARK_CIRCLEPLUS) {
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_CIRCLEPLUSCONNECTED) {
if (tFold == LineMarker::headWithTail)
surface->PenColour(colourTail);
else
surface->PenColour(colourBody);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
surface->PenColour(colourBody);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY - blobSize);
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_CIRCLEMINUS) {
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_CIRCLEMINUSCONNECTED) {
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
surface->PenColour(colourBody);
surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
surface->LineTo(centreX, centreY - blobSize);
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType >= SC_MARK_CHARACTER) {
char character[1];
character[0] = static_cast<char>(markType - SC_MARK_CHARACTER);
XYPOSITION width = surface->WidthText(fontForCharacter, character, 1);
rc.left += (rc.Width() - width) / 2;
rc.right = rc.left + width;
surface->DrawTextClipped(rc, fontForCharacter, rc.bottom - 2,
character, 1, fore, back);
} else if (markType == SC_MARK_DOTDOTDOT) {
XYPOSITION right = static_cast<XYPOSITION>(centreX - 6);
for (int b=0; b<3; b++) {
PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom-2);
surface->FillRectangle(rcBlob, fore);
right += 5.0f;
}
} else if (markType == SC_MARK_ARROWS) {
surface->PenColour(fore);
int right = centreX - 2;
const int armLength = dimOn2 - 1;
for (int b = 0; b<3; b++) {
surface->MoveTo(right, centreY);
surface->LineTo(right - armLength, centreY - armLength);
surface->MoveTo(right, centreY);
surface->LineTo(right - armLength, centreY + armLength);
right += 4;
}
} else if (markType == SC_MARK_SHORTARROW) {
Point pts[] = {
Point::FromInts(centreX, centreY + dimOn2),
Point::FromInts(centreX + dimOn2, centreY),
Point::FromInts(centreX, centreY - dimOn2),
Point::FromInts(centreX, centreY - dimOn4),
Point::FromInts(centreX - dimOn4, centreY - dimOn4),
Point::FromInts(centreX - dimOn4, centreY + dimOn4),
Point::FromInts(centreX, centreY + dimOn4),
Point::FromInts(centreX, centreY + dimOn2),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_LEFTRECT) {
PRectangle rcLeft = rcWhole;
rcLeft.right = rcLeft.left + 4;
surface->FillRectangle(rcLeft, back);
} else if (markType == SC_MARK_BOOKMARK) {
int halfHeight = minDim / 3;
Point pts[] = {
Point::FromInts(static_cast<int>(rc.left), centreY-halfHeight),
Point::FromInts(static_cast<int>(rc.right) - 3, centreY - halfHeight),
Point::FromInts(static_cast<int>(rc.right) - 3 - halfHeight, centreY),
Point::FromInts(static_cast<int>(rc.right) - 3, centreY + halfHeight),
Point::FromInts(static_cast<int>(rc.left), centreY + halfHeight),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else { // SC_MARK_FULLRECT
surface->FillRectangle(rcWhole, back);
}
}