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