Scintilla 4.0.3
[TortoiseGit.git] / ext / scintilla / src / LineMarker.cxx
blob3c214edcd8b2a02df737088ec961291ecc903423
1 // Scintilla source code edit control
2 /** @file LineMarker.cxx
3 ** Defines the look of a line marker in the margin.
4 **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <cstring>
9 #include <cmath>
11 #include <stdexcept>
12 #include <vector>
13 #include <map>
14 #include <algorithm>
15 #include <memory>
17 #include "Platform.h"
19 #include "Scintilla.h"
21 #include "StringCopy.h"
22 #include "XPM.h"
23 #include "LineMarker.h"
25 using namespace Scintilla;
27 void LineMarker::SetXPM(const char *textForm) {
28 pxpm.reset(new XPM(textForm));
29 markType = SC_MARK_PIXMAP;
32 void LineMarker::SetXPM(const char *const *linesForm) {
33 pxpm.reset(new XPM(linesForm));
34 markType = SC_MARK_PIXMAP;
37 void LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) {
38 image.reset(new RGBAImage(static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y), scale, pixelsRGBAImage));
39 markType = SC_MARK_RGBAIMAGE;
42 static void DrawBox(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
43 PRectangle rc = PRectangle::FromInts(
44 centreX - armSize,
45 centreY - armSize,
46 centreX + armSize + 1,
47 centreY + armSize + 1);
48 surface->RectangleDraw(rc, back, fore);
51 static void DrawCircle(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
52 PRectangle rcCircle = PRectangle::FromInts(
53 centreX - armSize,
54 centreY - armSize,
55 centreX + armSize + 1,
56 centreY + armSize + 1);
57 surface->Ellipse(rcCircle, back, fore);
60 static void DrawPlus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
61 PRectangle rcV = PRectangle::FromInts(centreX, centreY - armSize + 2, centreX + 1, centreY + armSize - 2 + 1);
62 surface->FillRectangle(rcV, fore);
63 PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1);
64 surface->FillRectangle(rcH, fore);
67 static void DrawMinus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
68 PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1);
69 surface->FillRectangle(rcH, fore);
72 void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, typeOfFold tFold, int marginStyle) const {
73 if (customDraw) {
74 customDraw(surface, rcWhole, fontForCharacter, tFold, marginStyle, this);
75 return;
78 ColourDesired colourHead = back;
79 ColourDesired colourBody = back;
80 ColourDesired colourTail = back;
82 switch (tFold) {
83 case LineMarker::head :
84 case LineMarker::headWithTail :
85 colourHead = backSelected;
86 colourTail = backSelected;
87 break;
88 case LineMarker::body :
89 colourHead = backSelected;
90 colourBody = backSelected;
91 break;
92 case LineMarker::tail :
93 colourBody = backSelected;
94 colourTail = backSelected;
95 break;
96 default :
97 // LineMarker::undefined
98 break;
101 if ((markType == SC_MARK_PIXMAP) && (pxpm)) {
102 pxpm->Draw(surface, rcWhole);
103 return;
105 if ((markType == SC_MARK_RGBAIMAGE) && (image)) {
106 // Make rectangle just large enough to fit image centred on centre of rcWhole
107 PRectangle rcImage;
108 rcImage.top = ((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2;
109 rcImage.bottom = rcImage.top + image->GetScaledHeight();
110 rcImage.left = ((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2;
111 rcImage.right = rcImage.left + image->GetScaledWidth();
112 surface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels());
113 return;
115 // Restrict most shapes a bit
116 PRectangle rc = rcWhole;
117 rc.top++;
118 rc.bottom--;
119 int minDim = std::min(static_cast<int>(rc.Width()), static_cast<int>(rc.Height()));
120 minDim--; // Ensure does not go beyond edge
121 int centreX = static_cast<int>(floor((rc.right + rc.left) / 2.0));
122 const int centreY = static_cast<int>(floor((rc.bottom + rc.top) / 2.0));
123 const int dimOn2 = minDim / 2;
124 const int dimOn4 = minDim / 4;
125 int blobSize = dimOn2-1;
126 const int armSize = dimOn2-2;
127 if (marginStyle == SC_MARGIN_NUMBER || marginStyle == SC_MARGIN_TEXT || marginStyle == SC_MARGIN_RTEXT) {
128 // On textual margins move marker to the left to try to avoid overlapping the text
129 centreX = static_cast<int>(rc.left) + dimOn2 + 1;
131 if (markType == SC_MARK_ROUNDRECT) {
132 PRectangle rcRounded = rc;
133 rcRounded.left = rc.left + 1;
134 rcRounded.right = rc.right - 1;
135 surface->RoundedRectangle(rcRounded, fore, back);
136 } else if (markType == SC_MARK_CIRCLE) {
137 PRectangle rcCircle = PRectangle::FromInts(
138 centreX - dimOn2,
139 centreY - dimOn2,
140 centreX + dimOn2,
141 centreY + dimOn2);
142 surface->Ellipse(rcCircle, fore, back);
143 } else if (markType == SC_MARK_ARROW) {
144 Point pts[] = {
145 Point::FromInts(centreX - dimOn4, centreY - dimOn2),
146 Point::FromInts(centreX - dimOn4, centreY + dimOn2),
147 Point::FromInts(centreX + dimOn2 - dimOn4, centreY),
149 surface->Polygon(pts, ELEMENTS(pts), fore, back);
151 } else if (markType == SC_MARK_ARROWDOWN) {
152 Point pts[] = {
153 Point::FromInts(centreX - dimOn2, centreY - dimOn4),
154 Point::FromInts(centreX + dimOn2, centreY - dimOn4),
155 Point::FromInts(centreX, centreY + dimOn2 - dimOn4),
157 surface->Polygon(pts, ELEMENTS(pts), fore, back);
159 } else if (markType == SC_MARK_PLUS) {
160 Point pts[] = {
161 Point::FromInts(centreX - armSize, centreY - 1),
162 Point::FromInts(centreX - 1, centreY - 1),
163 Point::FromInts(centreX - 1, centreY - armSize),
164 Point::FromInts(centreX + 1, centreY - armSize),
165 Point::FromInts(centreX + 1, centreY - 1),
166 Point::FromInts(centreX + armSize, centreY -1),
167 Point::FromInts(centreX + armSize, centreY +1),
168 Point::FromInts(centreX + 1, centreY + 1),
169 Point::FromInts(centreX + 1, centreY + armSize),
170 Point::FromInts(centreX - 1, centreY + armSize),
171 Point::FromInts(centreX - 1, centreY + 1),
172 Point::FromInts(centreX - armSize, centreY + 1),
174 surface->Polygon(pts, ELEMENTS(pts), fore, back);
176 } else if (markType == SC_MARK_MINUS) {
177 Point pts[] = {
178 Point::FromInts(centreX - armSize, centreY - 1),
179 Point::FromInts(centreX + armSize, centreY -1),
180 Point::FromInts(centreX + armSize, centreY +1),
181 Point::FromInts(centreX - armSize, centreY + 1),
183 surface->Polygon(pts, ELEMENTS(pts), fore, back);
185 } else if (markType == SC_MARK_SMALLRECT) {
186 PRectangle rcSmall;
187 rcSmall.left = rc.left + 1;
188 rcSmall.top = rc.top + 2;
189 rcSmall.right = rc.right - 1;
190 rcSmall.bottom = rc.bottom - 2;
191 surface->RectangleDraw(rcSmall, fore, back);
193 } else if (markType == SC_MARK_EMPTY || markType == SC_MARK_BACKGROUND ||
194 markType == SC_MARK_UNDERLINE || markType == SC_MARK_AVAILABLE) {
195 // An invisible marker so don't draw anything
197 } else if (markType == SC_MARK_VLINE) {
198 surface->PenColour(colourBody);
199 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
200 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
202 } else if (markType == SC_MARK_LCORNER) {
203 surface->PenColour(colourTail);
204 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
205 surface->LineTo(centreX, centreY);
206 surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
208 } else if (markType == SC_MARK_TCORNER) {
209 surface->PenColour(colourTail);
210 surface->MoveTo(centreX, centreY);
211 surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
213 surface->PenColour(colourBody);
214 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
215 surface->LineTo(centreX, centreY + 1);
217 surface->PenColour(colourHead);
218 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
220 } else if (markType == SC_MARK_LCORNERCURVE) {
221 surface->PenColour(colourTail);
222 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
223 surface->LineTo(centreX, centreY-3);
224 surface->LineTo(centreX+3, centreY);
225 surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
227 } else if (markType == SC_MARK_TCORNERCURVE) {
228 surface->PenColour(colourTail);
229 surface->MoveTo(centreX, centreY-3);
230 surface->LineTo(centreX+3, centreY);
231 surface->LineTo(static_cast<int>(rc.right) - 1, centreY);
233 surface->PenColour(colourBody);
234 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
235 surface->LineTo(centreX, centreY-2);
237 surface->PenColour(colourHead);
238 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
240 } else if (markType == SC_MARK_BOXPLUS) {
241 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
242 DrawPlus(surface, centreX, centreY, blobSize, colourTail);
244 } else if (markType == SC_MARK_BOXPLUSCONNECTED) {
245 if (tFold == LineMarker::headWithTail)
246 surface->PenColour(colourTail);
247 else
248 surface->PenColour(colourBody);
249 surface->MoveTo(centreX, centreY + blobSize);
250 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
252 surface->PenColour(colourBody);
253 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
254 surface->LineTo(centreX, centreY - blobSize);
256 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
257 DrawPlus(surface, centreX, centreY, blobSize, colourTail);
259 if (tFold == LineMarker::body) {
260 surface->PenColour(colourTail);
261 surface->MoveTo(centreX + 1, centreY + blobSize);
262 surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
264 surface->MoveTo(centreX + blobSize, centreY + blobSize);
265 surface->LineTo(centreX + blobSize, centreY - blobSize);
267 surface->MoveTo(centreX + 1, centreY - blobSize);
268 surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
270 } else if (markType == SC_MARK_BOXMINUS) {
271 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
272 DrawMinus(surface, centreX, centreY, blobSize, colourTail);
274 surface->PenColour(colourHead);
275 surface->MoveTo(centreX, centreY + blobSize);
276 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
278 } else if (markType == SC_MARK_BOXMINUSCONNECTED) {
279 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
280 DrawMinus(surface, centreX, centreY, blobSize, colourTail);
282 surface->PenColour(colourHead);
283 surface->MoveTo(centreX, centreY + blobSize);
284 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
286 surface->PenColour(colourBody);
287 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
288 surface->LineTo(centreX, centreY - blobSize);
290 if (tFold == LineMarker::body) {
291 surface->PenColour(colourTail);
292 surface->MoveTo(centreX + 1, centreY + blobSize);
293 surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
295 surface->MoveTo(centreX + blobSize, centreY + blobSize);
296 surface->LineTo(centreX + blobSize, centreY - blobSize);
298 surface->MoveTo(centreX + 1, centreY - blobSize);
299 surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
301 } else if (markType == SC_MARK_CIRCLEPLUS) {
302 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
303 DrawPlus(surface, centreX, centreY, blobSize, colourTail);
305 } else if (markType == SC_MARK_CIRCLEPLUSCONNECTED) {
306 if (tFold == LineMarker::headWithTail)
307 surface->PenColour(colourTail);
308 else
309 surface->PenColour(colourBody);
310 surface->MoveTo(centreX, centreY + blobSize);
311 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
313 surface->PenColour(colourBody);
314 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
315 surface->LineTo(centreX, centreY - blobSize);
317 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
318 DrawPlus(surface, centreX, centreY, blobSize, colourTail);
320 } else if (markType == SC_MARK_CIRCLEMINUS) {
321 surface->PenColour(colourHead);
322 surface->MoveTo(centreX, centreY + blobSize);
323 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
325 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
326 DrawMinus(surface, centreX, centreY, blobSize, colourTail);
328 } else if (markType == SC_MARK_CIRCLEMINUSCONNECTED) {
329 surface->PenColour(colourHead);
330 surface->MoveTo(centreX, centreY + blobSize);
331 surface->LineTo(centreX, static_cast<int>(rcWhole.bottom));
333 surface->PenColour(colourBody);
334 surface->MoveTo(centreX, static_cast<int>(rcWhole.top));
335 surface->LineTo(centreX, centreY - blobSize);
337 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
338 DrawMinus(surface, centreX, centreY, blobSize, colourTail);
340 } else if (markType >= SC_MARK_CHARACTER) {
341 char character[1];
342 character[0] = static_cast<char>(markType - SC_MARK_CHARACTER);
343 XYPOSITION width = surface->WidthText(fontForCharacter, character, 1);
344 rc.left += (rc.Width() - width) / 2;
345 rc.right = rc.left + width;
346 surface->DrawTextClipped(rc, fontForCharacter, rc.bottom - 2,
347 character, 1, fore, back);
349 } else if (markType == SC_MARK_DOTDOTDOT) {
350 XYPOSITION right = static_cast<XYPOSITION>(centreX - 6);
351 for (int b=0; b<3; b++) {
352 PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom-2);
353 surface->FillRectangle(rcBlob, fore);
354 right += 5.0f;
356 } else if (markType == SC_MARK_ARROWS) {
357 surface->PenColour(fore);
358 int right = centreX - 2;
359 const int armLength = dimOn2 - 1;
360 for (int b = 0; b<3; b++) {
361 surface->MoveTo(right, centreY);
362 surface->LineTo(right - armLength, centreY - armLength);
363 surface->MoveTo(right, centreY);
364 surface->LineTo(right - armLength, centreY + armLength);
365 right += 4;
367 } else if (markType == SC_MARK_SHORTARROW) {
368 Point pts[] = {
369 Point::FromInts(centreX, centreY + dimOn2),
370 Point::FromInts(centreX + dimOn2, centreY),
371 Point::FromInts(centreX, centreY - dimOn2),
372 Point::FromInts(centreX, centreY - dimOn4),
373 Point::FromInts(centreX - dimOn4, centreY - dimOn4),
374 Point::FromInts(centreX - dimOn4, centreY + dimOn4),
375 Point::FromInts(centreX, centreY + dimOn4),
376 Point::FromInts(centreX, centreY + dimOn2),
378 surface->Polygon(pts, ELEMENTS(pts), fore, back);
379 } else if (markType == SC_MARK_LEFTRECT) {
380 PRectangle rcLeft = rcWhole;
381 rcLeft.right = rcLeft.left + 4;
382 surface->FillRectangle(rcLeft, back);
383 } else if (markType == SC_MARK_BOOKMARK) {
384 const int halfHeight = minDim / 3;
385 Point pts[] = {
386 Point::FromInts(static_cast<int>(rc.left), centreY-halfHeight),
387 Point::FromInts(static_cast<int>(rc.right) - 3, centreY - halfHeight),
388 Point::FromInts(static_cast<int>(rc.right) - 3 - halfHeight, centreY),
389 Point::FromInts(static_cast<int>(rc.right) - 3, centreY + halfHeight),
390 Point::FromInts(static_cast<int>(rc.left), centreY + halfHeight),
392 surface->Polygon(pts, ELEMENTS(pts), fore, back);
393 } else { // SC_MARK_FULLRECT
394 surface->FillRectangle(rcWhole, back);