xref: /MusicPlayer2/scintilla/src/Indicator.cxx (revision 8af74909132ed5e696cb05b6689ae4baf14c1c96)
1*8af74909SZhong Yang // Scintilla source code edit control
2*8af74909SZhong Yang /** @file Indicator.cxx
3*8af74909SZhong Yang  ** Defines the style of indicators which are text decorations such as underlining.
4*8af74909SZhong Yang  **/
5*8af74909SZhong Yang // Copyright 1998-2001 by Neil Hodgson <[email protected]>
6*8af74909SZhong Yang // The License.txt file describes the conditions under which this software may be distributed.
7*8af74909SZhong Yang 
8*8af74909SZhong Yang #include <cmath>
9*8af74909SZhong Yang 
10*8af74909SZhong Yang #include <stdexcept>
11*8af74909SZhong Yang #include <string_view>
12*8af74909SZhong Yang #include <vector>
13*8af74909SZhong Yang #include <map>
14*8af74909SZhong Yang #include <algorithm>
15*8af74909SZhong Yang #include <memory>
16*8af74909SZhong Yang 
17*8af74909SZhong Yang #include "Platform.h"
18*8af74909SZhong Yang 
19*8af74909SZhong Yang #include "Scintilla.h"
20*8af74909SZhong Yang #include "IntegerRectangle.h"
21*8af74909SZhong Yang #include "Indicator.h"
22*8af74909SZhong Yang #include "XPM.h"
23*8af74909SZhong Yang 
24*8af74909SZhong Yang using namespace Scintilla;
25*8af74909SZhong Yang 
PixelGridAlign(const PRectangle & rc)26*8af74909SZhong Yang static PRectangle PixelGridAlign(const PRectangle &rc) noexcept {
27*8af74909SZhong Yang 	// Move left and right side to nearest pixel to avoid blurry visuals
28*8af74909SZhong Yang 	return PRectangle(std::round(rc.left), std::floor(rc.top),
29*8af74909SZhong Yang 		std::round(rc.right), std::floor(rc.bottom));
30*8af74909SZhong Yang }
31*8af74909SZhong Yang 
Draw(Surface * surface,const PRectangle & rc,const PRectangle & rcLine,const PRectangle & rcCharacter,State state,int value) const32*8af74909SZhong Yang void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, State state, int value) const {
33*8af74909SZhong Yang 	StyleAndColour sacDraw = sacNormal;
34*8af74909SZhong Yang 	if (Flags() & SC_INDICFLAG_VALUEFORE) {
35*8af74909SZhong Yang 		sacDraw.fore = ColourDesired(value & SC_INDICVALUEMASK);
36*8af74909SZhong Yang 	}
37*8af74909SZhong Yang 	if (state == State::hover) {
38*8af74909SZhong Yang 		sacDraw = sacHover;
39*8af74909SZhong Yang 	}
40*8af74909SZhong Yang 	const IntegerRectangle irc(rc);
41*8af74909SZhong Yang 	surface->PenColour(sacDraw.fore);
42*8af74909SZhong Yang 	const int ymid = (irc.bottom + irc.top) / 2;
43*8af74909SZhong Yang 
44*8af74909SZhong Yang 	switch (sacDraw.style) {
45*8af74909SZhong Yang 	case INDIC_SQUIGGLE: {
46*8af74909SZhong Yang 			const IntegerRectangle ircSquiggle(PixelGridAlign(rc));
47*8af74909SZhong Yang 			int x = ircSquiggle.left;
48*8af74909SZhong Yang 			const int xLast = ircSquiggle.right;
49*8af74909SZhong Yang 			int y = 0;
50*8af74909SZhong Yang 			surface->MoveTo(x, irc.top + y);
51*8af74909SZhong Yang 			while (x < xLast) {
52*8af74909SZhong Yang 				if ((x + 2) > xLast) {
53*8af74909SZhong Yang 					y = 1;
54*8af74909SZhong Yang 					x = xLast;
55*8af74909SZhong Yang 				} else {
56*8af74909SZhong Yang 					x += 2;
57*8af74909SZhong Yang 					y = 2 - y;
58*8af74909SZhong Yang 				}
59*8af74909SZhong Yang 				surface->LineTo(x, irc.top + y);
60*8af74909SZhong Yang 			}
61*8af74909SZhong Yang 		}
62*8af74909SZhong Yang 		break;
63*8af74909SZhong Yang 
64*8af74909SZhong Yang 	case INDIC_SQUIGGLEPIXMAP: {
65*8af74909SZhong Yang 			const PRectangle rcSquiggle = PixelGridAlign(rc);
66*8af74909SZhong Yang 
67*8af74909SZhong Yang 			const int width = std::min(4000, static_cast<int>(rcSquiggle.Width()));
68*8af74909SZhong Yang 			RGBAImage image(width, 3, 1.0, nullptr);
69*8af74909SZhong Yang 			enum { alphaFull = 0xff, alphaSide = 0x2f, alphaSide2=0x5f };
70*8af74909SZhong Yang 			for (int x = 0; x < width; x++) {
71*8af74909SZhong Yang 				if (x%2) {
72*8af74909SZhong Yang 					// Two halfway columns have a full pixel in middle flanked by light pixels
73*8af74909SZhong Yang 					image.SetPixel(x, 0, sacDraw.fore, alphaSide);
74*8af74909SZhong Yang 					image.SetPixel(x, 1, sacDraw.fore, alphaFull);
75*8af74909SZhong Yang 					image.SetPixel(x, 2, sacDraw.fore, alphaSide);
76*8af74909SZhong Yang 				} else {
77*8af74909SZhong Yang 					// Extreme columns have a full pixel at bottom or top and a mid-tone pixel in centre
78*8af74909SZhong Yang 					image.SetPixel(x, (x % 4) ? 0 : 2, sacDraw.fore, alphaFull);
79*8af74909SZhong Yang 					image.SetPixel(x, 1, sacDraw.fore, alphaSide2);
80*8af74909SZhong Yang 				}
81*8af74909SZhong Yang 			}
82*8af74909SZhong Yang 			surface->DrawRGBAImage(rcSquiggle, image.GetWidth(), image.GetHeight(), image.Pixels());
83*8af74909SZhong Yang 		}
84*8af74909SZhong Yang 		break;
85*8af74909SZhong Yang 
86*8af74909SZhong Yang 	case INDIC_SQUIGGLELOW: {
87*8af74909SZhong Yang 			surface->MoveTo(irc.left, irc.top);
88*8af74909SZhong Yang 			int x = irc.left + 3;
89*8af74909SZhong Yang 			int y = 0;
90*8af74909SZhong Yang 			while (x < rc.right) {
91*8af74909SZhong Yang 				surface->LineTo(x - 1, irc.top + y);
92*8af74909SZhong Yang 				y = 1 - y;
93*8af74909SZhong Yang 				surface->LineTo(x, irc.top + y);
94*8af74909SZhong Yang 				x += 3;
95*8af74909SZhong Yang 			}
96*8af74909SZhong Yang 			surface->LineTo(irc.right, irc.top + y);	// Finish the line
97*8af74909SZhong Yang 		}
98*8af74909SZhong Yang 		break;
99*8af74909SZhong Yang 
100*8af74909SZhong Yang 	case INDIC_TT: {
101*8af74909SZhong Yang 			surface->MoveTo(irc.left, ymid);
102*8af74909SZhong Yang 			int x = irc.left + 5;
103*8af74909SZhong Yang 			while (x < rc.right) {
104*8af74909SZhong Yang 				surface->LineTo(x, ymid);
105*8af74909SZhong Yang 				surface->MoveTo(x-3, ymid);
106*8af74909SZhong Yang 				surface->LineTo(x-3, ymid+2);
107*8af74909SZhong Yang 				x++;
108*8af74909SZhong Yang 				surface->MoveTo(x, ymid);
109*8af74909SZhong Yang 				x += 5;
110*8af74909SZhong Yang 			}
111*8af74909SZhong Yang 			surface->LineTo(irc.right, ymid);	// Finish the line
112*8af74909SZhong Yang 			if (x - 3 <= rc.right) {
113*8af74909SZhong Yang 				surface->MoveTo(x-3, ymid);
114*8af74909SZhong Yang 				surface->LineTo(x-3, ymid+2);
115*8af74909SZhong Yang 			}
116*8af74909SZhong Yang 		}
117*8af74909SZhong Yang 		break;
118*8af74909SZhong Yang 
119*8af74909SZhong Yang 	case INDIC_DIAGONAL: {
120*8af74909SZhong Yang 			int x = irc.left;
121*8af74909SZhong Yang 			while (x < rc.right) {
122*8af74909SZhong Yang 				surface->MoveTo(x, irc.top + 2);
123*8af74909SZhong Yang 				int endX = x+3;
124*8af74909SZhong Yang 				int endY = irc.top - 1;
125*8af74909SZhong Yang 				if (endX > rc.right) {
126*8af74909SZhong Yang 					endY += endX - irc.right;
127*8af74909SZhong Yang 					endX = irc.right;
128*8af74909SZhong Yang 				}
129*8af74909SZhong Yang 				surface->LineTo(endX, endY);
130*8af74909SZhong Yang 				x += 4;
131*8af74909SZhong Yang 			}
132*8af74909SZhong Yang 		}
133*8af74909SZhong Yang 		break;
134*8af74909SZhong Yang 
135*8af74909SZhong Yang 	case INDIC_STRIKE: {
136*8af74909SZhong Yang 			surface->MoveTo(irc.left, irc.top - 4);
137*8af74909SZhong Yang 			surface->LineTo(irc.right, irc.top - 4);
138*8af74909SZhong Yang 		}
139*8af74909SZhong Yang 		break;
140*8af74909SZhong Yang 
141*8af74909SZhong Yang 	case INDIC_HIDDEN:
142*8af74909SZhong Yang 	case INDIC_TEXTFORE:
143*8af74909SZhong Yang 		// Draw nothing
144*8af74909SZhong Yang 		break;
145*8af74909SZhong Yang 
146*8af74909SZhong Yang 	case INDIC_BOX: {
147*8af74909SZhong Yang 			surface->MoveTo(irc.left, ymid + 1);
148*8af74909SZhong Yang 			surface->LineTo(irc.right, ymid + 1);
149*8af74909SZhong Yang 			const int lineTop = static_cast<int>(rcLine.top) + 1;
150*8af74909SZhong Yang 			surface->LineTo(irc.right, lineTop);
151*8af74909SZhong Yang 			surface->LineTo(irc.left, lineTop);
152*8af74909SZhong Yang 			surface->LineTo(irc.left, ymid + 1);
153*8af74909SZhong Yang 		}
154*8af74909SZhong Yang 		break;
155*8af74909SZhong Yang 
156*8af74909SZhong Yang 	case INDIC_ROUNDBOX:
157*8af74909SZhong Yang 	case INDIC_STRAIGHTBOX:
158*8af74909SZhong Yang 	case INDIC_FULLBOX: {
159*8af74909SZhong Yang 			PRectangle rcBox = rcLine;
160*8af74909SZhong Yang 			if (sacDraw.style != INDIC_FULLBOX)
161*8af74909SZhong Yang 				rcBox.top = rcLine.top + 1;
162*8af74909SZhong Yang 			rcBox.left = rc.left;
163*8af74909SZhong Yang 			rcBox.right = rc.right;
164*8af74909SZhong Yang 			surface->AlphaRectangle(rcBox, (sacDraw.style == INDIC_ROUNDBOX) ? 1 : 0,
165*8af74909SZhong Yang 						sacDraw.fore, fillAlpha, sacDraw.fore, outlineAlpha, 0);
166*8af74909SZhong Yang 		}
167*8af74909SZhong Yang 		break;
168*8af74909SZhong Yang 
169*8af74909SZhong Yang 	case INDIC_GRADIENT:
170*8af74909SZhong Yang 	case INDIC_GRADIENTCENTRE: {
171*8af74909SZhong Yang 			PRectangle rcBox = rc;
172*8af74909SZhong Yang 			rcBox.top = rcLine.top + 1;
173*8af74909SZhong Yang 			rcBox.bottom = rcLine.bottom;
174*8af74909SZhong Yang 			const Surface::GradientOptions options = Surface::GradientOptions::topToBottom;
175*8af74909SZhong Yang 			const ColourAlpha start(sacDraw.fore, fillAlpha);
176*8af74909SZhong Yang 			const ColourAlpha end(sacDraw.fore, 0);
177*8af74909SZhong Yang 			std::vector<ColourStop> stops;
178*8af74909SZhong Yang 			switch (sacDraw.style) {
179*8af74909SZhong Yang 			case INDIC_GRADIENT:
180*8af74909SZhong Yang 				stops.push_back(ColourStop(0.0, start));
181*8af74909SZhong Yang 				stops.push_back(ColourStop(1.0, end));
182*8af74909SZhong Yang 				break;
183*8af74909SZhong Yang 			case INDIC_GRADIENTCENTRE:
184*8af74909SZhong Yang 				stops.push_back(ColourStop(0.0, end));
185*8af74909SZhong Yang 				stops.push_back(ColourStop(0.5, start));
186*8af74909SZhong Yang 				stops.push_back(ColourStop(1.0, end));
187*8af74909SZhong Yang 				break;
188*8af74909SZhong Yang 			}
189*8af74909SZhong Yang 			surface->GradientRectangle(rcBox, stops, options);
190*8af74909SZhong Yang 		}
191*8af74909SZhong Yang 		break;
192*8af74909SZhong Yang 
193*8af74909SZhong Yang 	case INDIC_DOTBOX: {
194*8af74909SZhong Yang 			PRectangle rcBox = PixelGridAlign(rc);
195*8af74909SZhong Yang 			rcBox.top = rcLine.top + 1;
196*8af74909SZhong Yang 			rcBox.bottom = rcLine.bottom;
197*8af74909SZhong Yang 			const IntegerRectangle ircBox(rcBox);
198*8af74909SZhong Yang 			// Cap width at 4000 to avoid large allocations when mistakes made
199*8af74909SZhong Yang 			const int width = std::min(ircBox.Width(), 4000);
200*8af74909SZhong Yang 			RGBAImage image(width, ircBox.Height(), 1.0, nullptr);
201*8af74909SZhong Yang 			// Draw horizontal lines top and bottom
202*8af74909SZhong Yang 			for (int x=0; x<width; x++) {
203*8af74909SZhong Yang 				for (int y = 0; y<ircBox.Height(); y += ircBox.Height() - 1) {
204*8af74909SZhong Yang 					image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
205*8af74909SZhong Yang 				}
206*8af74909SZhong Yang 			}
207*8af74909SZhong Yang 			// Draw vertical lines left and right
208*8af74909SZhong Yang 			for (int y = 1; y<ircBox.Height(); y++) {
209*8af74909SZhong Yang 				for (int x=0; x<width; x += width-1) {
210*8af74909SZhong Yang 					image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
211*8af74909SZhong Yang 				}
212*8af74909SZhong Yang 			}
213*8af74909SZhong Yang 			surface->DrawRGBAImage(rcBox, image.GetWidth(), image.GetHeight(), image.Pixels());
214*8af74909SZhong Yang 		}
215*8af74909SZhong Yang 		break;
216*8af74909SZhong Yang 
217*8af74909SZhong Yang 	case INDIC_DASH: {
218*8af74909SZhong Yang 			int x = irc.left;
219*8af74909SZhong Yang 			while (x < rc.right) {
220*8af74909SZhong Yang 				surface->MoveTo(x, ymid);
221*8af74909SZhong Yang 				surface->LineTo(std::min(x + 4, irc.right), ymid);
222*8af74909SZhong Yang 				x += 7;
223*8af74909SZhong Yang 			}
224*8af74909SZhong Yang 		}
225*8af74909SZhong Yang 		break;
226*8af74909SZhong Yang 
227*8af74909SZhong Yang 	case INDIC_DOTS: {
228*8af74909SZhong Yang 			int x = irc.left;
229*8af74909SZhong Yang 			while (x < irc.right) {
230*8af74909SZhong Yang 				const PRectangle rcDot = PRectangle::FromInts(x, ymid, x + 1, ymid + 1);
231*8af74909SZhong Yang 				surface->FillRectangle(rcDot, sacDraw.fore);
232*8af74909SZhong Yang 				x += 2;
233*8af74909SZhong Yang 			}
234*8af74909SZhong Yang 		}
235*8af74909SZhong Yang 		break;
236*8af74909SZhong Yang 
237*8af74909SZhong Yang 	case INDIC_COMPOSITIONTHICK: {
238*8af74909SZhong Yang 			const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom);
239*8af74909SZhong Yang 			surface->FillRectangle(rcComposition, sacDraw.fore);
240*8af74909SZhong Yang 		}
241*8af74909SZhong Yang 		break;
242*8af74909SZhong Yang 
243*8af74909SZhong Yang 	case INDIC_COMPOSITIONTHIN: {
244*8af74909SZhong Yang 			const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom-1);
245*8af74909SZhong Yang 			surface->FillRectangle(rcComposition, sacDraw.fore);
246*8af74909SZhong Yang 		}
247*8af74909SZhong Yang 		break;
248*8af74909SZhong Yang 
249*8af74909SZhong Yang 	case INDIC_POINT:
250*8af74909SZhong Yang 	case INDIC_POINTCHARACTER:
251*8af74909SZhong Yang 		if (rcCharacter.Width() >= 0.1) {
252*8af74909SZhong Yang 			const XYPOSITION pixelHeight = std::floor(rc.Height() - 1.0f);	// 1 pixel onto next line if multiphase
253*8af74909SZhong Yang 			const XYPOSITION x = (sacDraw.style == INDIC_POINT) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2);
254*8af74909SZhong Yang 			const XYPOSITION ix = std::round(x);
255*8af74909SZhong Yang 			const XYPOSITION iy = std::floor(rc.top + 1.0f);
256*8af74909SZhong Yang 			Point pts[] = {
257*8af74909SZhong Yang 				Point(ix - pixelHeight, iy + pixelHeight),	// Left
258*8af74909SZhong Yang 				Point(ix + pixelHeight, iy + pixelHeight),	// Right
259*8af74909SZhong Yang 				Point(ix, iy)								// Top
260*8af74909SZhong Yang 			};
261*8af74909SZhong Yang 			surface->Polygon(pts, std::size(pts), sacDraw.fore, sacDraw.fore);
262*8af74909SZhong Yang 		}
263*8af74909SZhong Yang 		break;
264*8af74909SZhong Yang 
265*8af74909SZhong Yang 	default:
266*8af74909SZhong Yang 		// Either INDIC_PLAIN or unknown
267*8af74909SZhong Yang 		surface->MoveTo(irc.left, ymid);
268*8af74909SZhong Yang 		surface->LineTo(irc.right, ymid);
269*8af74909SZhong Yang 	}
270*8af74909SZhong Yang }
271*8af74909SZhong Yang 
SetFlags(int attributes_)272*8af74909SZhong Yang void Indicator::SetFlags(int attributes_) noexcept {
273*8af74909SZhong Yang 	attributes = attributes_;
274*8af74909SZhong Yang }
275