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 <[email protected]>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <cstring>
9 #include <cmath>
10
11 #include <stdexcept>
12 #include <string>
13 #include <string_view>
14 #include <vector>
15 #include <map>
16 #include <algorithm>
17 #include <memory>
18
19 #include "Platform.h"
20
21 #include "Scintilla.h"
22
23 #include "IntegerRectangle.h"
24 #include "XPM.h"
25 #include "LineMarker.h"
26
27 using namespace Scintilla;
28
LineMarker(const LineMarker & other)29 LineMarker::LineMarker(const LineMarker &other) {
30 // Defined to avoid pxpm and image being blindly copied, not as a complete copy constructor.
31 markType = other.markType;
32 fore = other.fore;
33 back = other.back;
34 backSelected = other.backSelected;
35 alpha = other.alpha;
36 if (other.pxpm)
37 pxpm = std::make_unique<XPM>(*other.pxpm);
38 else
39 pxpm = nullptr;
40 if (other.image)
41 image = std::make_unique<RGBAImage>(*other.image);
42 else
43 image = nullptr;
44 customDraw = other.customDraw;
45 }
46
operator =(const LineMarker & other)47 LineMarker &LineMarker::operator=(const LineMarker &other) {
48 // Defined to avoid pxpm and image being blindly copied, not as a complete assignment operator.
49 if (this != &other) {
50 markType = other.markType;
51 fore = other.fore;
52 back = other.back;
53 backSelected = other.backSelected;
54 alpha = other.alpha;
55 if (other.pxpm)
56 pxpm = std::make_unique<XPM>(*other.pxpm);
57 else
58 pxpm = nullptr;
59 if (other.image)
60 image = std::make_unique<RGBAImage>(*other.image);
61 else
62 image = nullptr;
63 customDraw = other.customDraw;
64 }
65 return *this;
66 }
67
SetXPM(const char * textForm)68 void LineMarker::SetXPM(const char *textForm) {
69 pxpm = std::make_unique<XPM>(textForm);
70 markType = SC_MARK_PIXMAP;
71 }
72
SetXPM(const char * const * linesForm)73 void LineMarker::SetXPM(const char *const *linesForm) {
74 pxpm = std::make_unique<XPM>(linesForm);
75 markType = SC_MARK_PIXMAP;
76 }
77
SetRGBAImage(Point sizeRGBAImage,float scale,const unsigned char * pixelsRGBAImage)78 void LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) {
79 image = std::make_unique<RGBAImage>(static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y), scale, pixelsRGBAImage);
80 markType = SC_MARK_RGBAIMAGE;
81 }
82
DrawBox(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore,ColourDesired back)83 static void DrawBox(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
84 const PRectangle rc = PRectangle::FromInts(
85 centreX - armSize,
86 centreY - armSize,
87 centreX + armSize + 1,
88 centreY + armSize + 1);
89 surface->RectangleDraw(rc, back, fore);
90 }
91
DrawCircle(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore,ColourDesired back)92 static void DrawCircle(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
93 const PRectangle rcCircle = PRectangle::FromInts(
94 centreX - armSize,
95 centreY - armSize,
96 centreX + armSize + 1,
97 centreY + armSize + 1);
98 surface->Ellipse(rcCircle, back, fore);
99 }
100
DrawPlus(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore)101 static void DrawPlus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
102 const PRectangle rcV = PRectangle::FromInts(centreX, centreY - armSize + 2, centreX + 1, centreY + armSize - 2 + 1);
103 surface->FillRectangle(rcV, fore);
104 const PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1);
105 surface->FillRectangle(rcH, fore);
106 }
107
DrawMinus(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore)108 static void DrawMinus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
109 const PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1);
110 surface->FillRectangle(rcH, fore);
111 }
112
Draw(Surface * surface,PRectangle & rcWhole,Font & fontForCharacter,FoldPart part,int marginStyle) const113 void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, FoldPart part, int marginStyle) const {
114 if (customDraw) {
115 customDraw(surface, rcWhole, fontForCharacter, static_cast<int>(part), marginStyle, this);
116 return;
117 }
118
119 ColourDesired colourHead = back;
120 ColourDesired colourBody = back;
121 ColourDesired colourTail = back;
122
123 switch (part) {
124 case FoldPart::head:
125 case FoldPart::headWithTail:
126 colourHead = backSelected;
127 colourTail = backSelected;
128 break;
129 case FoldPart::body:
130 colourHead = backSelected;
131 colourBody = backSelected;
132 break;
133 case FoldPart::tail:
134 colourBody = backSelected;
135 colourTail = backSelected;
136 break;
137 default:
138 // FoldPart::undefined
139 break;
140 }
141
142 if ((markType == SC_MARK_PIXMAP) && (pxpm)) {
143 pxpm->Draw(surface, rcWhole);
144 return;
145 }
146 if ((markType == SC_MARK_RGBAIMAGE) && (image)) {
147 // Make rectangle just large enough to fit image centred on centre of rcWhole
148 PRectangle rcImage;
149 rcImage.top = ((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2;
150 rcImage.bottom = rcImage.top + image->GetScaledHeight();
151 rcImage.left = ((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2;
152 rcImage.right = rcImage.left + image->GetScaledWidth();
153 surface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels());
154 return;
155 }
156
157 const IntegerRectangle ircWhole(rcWhole);
158 // Restrict most shapes a bit
159 const PRectangle rc(rcWhole.left, rcWhole.top + 1, rcWhole.right, rcWhole.bottom - 1);
160 // Ensure does not go beyond edge
161 const int minDim = std::min(ircWhole.Width(), ircWhole.Height() - 2) - 1;
162 int centreX = (ircWhole.right + ircWhole.left) / 2;
163 const int centreY = (ircWhole.bottom + ircWhole.top) / 2;
164 const int dimOn2 = minDim / 2;
165 const int dimOn4 = minDim / 4;
166 const int blobSize = dimOn2 - 1;
167 const int armSize = dimOn2 - 2;
168 if (marginStyle == SC_MARGIN_NUMBER || marginStyle == SC_MARGIN_TEXT || marginStyle == SC_MARGIN_RTEXT) {
169 // On textual margins move marker to the left to try to avoid overlapping the text
170 centreX = ircWhole.left + dimOn2 + 1;
171 }
172
173 switch (markType) {
174 case SC_MARK_ROUNDRECT: {
175 PRectangle rcRounded = rc;
176 rcRounded.left = rc.left + 1;
177 rcRounded.right = rc.right - 1;
178 surface->RoundedRectangle(rcRounded, fore, back);
179 }
180 break;
181
182 case SC_MARK_CIRCLE: {
183 const PRectangle rcCircle = PRectangle::FromInts(
184 centreX - dimOn2,
185 centreY - dimOn2,
186 centreX + dimOn2,
187 centreY + dimOn2);
188 surface->Ellipse(rcCircle, fore, back);
189 }
190 break;
191
192 case SC_MARK_ARROW: {
193 Point pts[] = {
194 Point::FromInts(centreX - dimOn4, centreY - dimOn2),
195 Point::FromInts(centreX - dimOn4, centreY + dimOn2),
196 Point::FromInts(centreX + dimOn2 - dimOn4, centreY),
197 };
198 surface->Polygon(pts, std::size(pts), fore, back);
199 }
200 break;
201
202 case SC_MARK_ARROWDOWN: {
203 Point pts[] = {
204 Point::FromInts(centreX - dimOn2, centreY - dimOn4),
205 Point::FromInts(centreX + dimOn2, centreY - dimOn4),
206 Point::FromInts(centreX, centreY + dimOn2 - dimOn4),
207 };
208 surface->Polygon(pts, std::size(pts), fore, back);
209 }
210 break;
211
212 case SC_MARK_PLUS: {
213 Point pts[] = {
214 Point::FromInts(centreX - armSize, centreY - 1),
215 Point::FromInts(centreX - 1, centreY - 1),
216 Point::FromInts(centreX - 1, centreY - armSize),
217 Point::FromInts(centreX + 1, centreY - armSize),
218 Point::FromInts(centreX + 1, centreY - 1),
219 Point::FromInts(centreX + armSize, centreY - 1),
220 Point::FromInts(centreX + armSize, centreY + 1),
221 Point::FromInts(centreX + 1, centreY + 1),
222 Point::FromInts(centreX + 1, centreY + armSize),
223 Point::FromInts(centreX - 1, centreY + armSize),
224 Point::FromInts(centreX - 1, centreY + 1),
225 Point::FromInts(centreX - armSize, centreY + 1),
226 };
227 surface->Polygon(pts, std::size(pts), fore, back);
228 }
229 break;
230
231 case SC_MARK_MINUS: {
232 Point pts[] = {
233 Point::FromInts(centreX - armSize, centreY - 1),
234 Point::FromInts(centreX + armSize, centreY - 1),
235 Point::FromInts(centreX + armSize, centreY + 1),
236 Point::FromInts(centreX - armSize, centreY + 1),
237 };
238 surface->Polygon(pts, std::size(pts), fore, back);
239 }
240 break;
241
242 case SC_MARK_SMALLRECT: {
243 PRectangle rcSmall;
244 rcSmall.left = rc.left + 1;
245 rcSmall.top = rc.top + 2;
246 rcSmall.right = rc.right - 1;
247 rcSmall.bottom = rc.bottom - 2;
248 surface->RectangleDraw(rcSmall, fore, back);
249 }
250 break;
251
252 case SC_MARK_EMPTY:
253 case SC_MARK_BACKGROUND:
254 case SC_MARK_UNDERLINE:
255 case SC_MARK_AVAILABLE:
256 // An invisible marker so don't draw anything
257 break;
258
259 case SC_MARK_VLINE: {
260 surface->PenColour(colourBody);
261 surface->MoveTo(centreX, ircWhole.top);
262 surface->LineTo(centreX, ircWhole.bottom);
263 }
264 break;
265
266 case SC_MARK_LCORNER: {
267 surface->PenColour(colourTail);
268 surface->MoveTo(centreX, ircWhole.top);
269 surface->LineTo(centreX, centreY);
270 surface->LineTo(ircWhole.right - 1, centreY);
271 }
272 break;
273
274 case SC_MARK_TCORNER: {
275 surface->PenColour(colourTail);
276 surface->MoveTo(centreX, centreY);
277 surface->LineTo(ircWhole.right - 1, centreY);
278
279 surface->PenColour(colourBody);
280 surface->MoveTo(centreX, ircWhole.top);
281 surface->LineTo(centreX, centreY + 1);
282
283 surface->PenColour(colourHead);
284 surface->LineTo(centreX, ircWhole.bottom);
285 }
286 break;
287
288 case SC_MARK_LCORNERCURVE: {
289 surface->PenColour(colourTail);
290 surface->MoveTo(centreX, ircWhole.top);
291 surface->LineTo(centreX, centreY - 3);
292 surface->LineTo(centreX + 3, centreY);
293 surface->LineTo(ircWhole.right - 1, centreY);
294 }
295 break;
296
297 case SC_MARK_TCORNERCURVE: {
298 surface->PenColour(colourTail);
299 surface->MoveTo(centreX, centreY - 3);
300 surface->LineTo(centreX + 3, centreY);
301 surface->LineTo(ircWhole.right - 1, centreY);
302
303 surface->PenColour(colourBody);
304 surface->MoveTo(centreX, ircWhole.top);
305 surface->LineTo(centreX, centreY - 2);
306
307 surface->PenColour(colourHead);
308 surface->LineTo(centreX, ircWhole.bottom);
309 }
310 break;
311
312 case SC_MARK_BOXPLUS: {
313 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
314 DrawPlus(surface, centreX, centreY, blobSize, fore);
315 }
316 break;
317
318 case SC_MARK_BOXPLUSCONNECTED: {
319 if (part == FoldPart::headWithTail)
320 surface->PenColour(colourTail);
321 else
322 surface->PenColour(colourBody);
323 surface->MoveTo(centreX, centreY + blobSize);
324 surface->LineTo(centreX, ircWhole.bottom);
325
326 surface->PenColour(colourBody);
327 surface->MoveTo(centreX, ircWhole.top);
328 surface->LineTo(centreX, centreY - blobSize);
329
330 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
331 DrawPlus(surface, centreX, centreY, blobSize, fore);
332
333 if (part == FoldPart::body) {
334 surface->PenColour(colourTail);
335 surface->MoveTo(centreX + 1, centreY + blobSize);
336 surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
337
338 surface->MoveTo(centreX + blobSize, centreY + blobSize);
339 surface->LineTo(centreX + blobSize, centreY - blobSize);
340
341 surface->MoveTo(centreX + 1, centreY - blobSize);
342 surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
343 }
344 }
345 break;
346
347 case SC_MARK_BOXMINUS: {
348 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
349 DrawMinus(surface, centreX, centreY, blobSize, fore);
350
351 surface->PenColour(fore);
352 surface->MoveTo(centreX, centreY + blobSize);
353 surface->LineTo(centreX, ircWhole.bottom);
354 }
355 break;
356
357 case SC_MARK_BOXMINUSCONNECTED: {
358 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
359 DrawMinus(surface, centreX, centreY, blobSize, fore);
360
361 surface->PenColour(fore);
362 surface->MoveTo(centreX, centreY + blobSize);
363 surface->LineTo(centreX, ircWhole.bottom);
364
365 surface->PenColour(colourBody);
366 surface->MoveTo(centreX, ircWhole.top);
367 surface->LineTo(centreX, centreY - blobSize);
368
369 if (part == FoldPart::body) {
370 surface->PenColour(colourTail);
371 surface->MoveTo(centreX + 1, centreY + blobSize);
372 surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
373
374 surface->MoveTo(centreX + blobSize, centreY + blobSize);
375 surface->LineTo(centreX + blobSize, centreY - blobSize);
376
377 surface->MoveTo(centreX + 1, centreY - blobSize);
378 surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
379 }
380 }
381 break;
382
383 case SC_MARK_CIRCLEPLUS: {
384 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
385 DrawPlus(surface, centreX, centreY, blobSize, colourTail);
386 }
387 break;
388
389 case SC_MARK_CIRCLEPLUSCONNECTED: {
390 if (part == FoldPart::headWithTail)
391 surface->PenColour(colourTail);
392 else
393 surface->PenColour(colourBody);
394 surface->MoveTo(centreX, centreY + blobSize);
395 surface->LineTo(centreX, ircWhole.bottom);
396
397 surface->PenColour(colourBody);
398 surface->MoveTo(centreX, ircWhole.top);
399 surface->LineTo(centreX, centreY - blobSize);
400
401 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
402 DrawPlus(surface, centreX, centreY, blobSize, colourTail);
403 }
404 break;
405
406 case SC_MARK_CIRCLEMINUS: {
407 surface->PenColour(colourHead);
408 surface->MoveTo(centreX, centreY + blobSize);
409 surface->LineTo(centreX, ircWhole.bottom);
410
411 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
412 DrawMinus(surface, centreX, centreY, blobSize, colourTail);
413 }
414 break;
415
416 case SC_MARK_CIRCLEMINUSCONNECTED: {
417 surface->PenColour(colourHead);
418 surface->MoveTo(centreX, centreY + blobSize);
419 surface->LineTo(centreX, ircWhole.bottom);
420
421 surface->PenColour(colourBody);
422 surface->MoveTo(centreX, ircWhole.top);
423 surface->LineTo(centreX, centreY - blobSize);
424
425 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
426 DrawMinus(surface, centreX, centreY, blobSize, colourTail);
427 }
428 break;
429
430 case SC_MARK_DOTDOTDOT: {
431 XYPOSITION right = static_cast<XYPOSITION>(centreX - 6);
432 for (int b = 0; b < 3; b++) {
433 const PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom - 2);
434 surface->FillRectangle(rcBlob, fore);
435 right += 5.0f;
436 }
437 }
438 break;
439
440 case SC_MARK_ARROWS: {
441 surface->PenColour(fore);
442 int right = centreX - 2;
443 const int armLength = dimOn2 - 1;
444 for (int b = 0; b < 3; b++) {
445 surface->MoveTo(right, centreY);
446 surface->LineTo(right - armLength, centreY - armLength);
447 surface->MoveTo(right, centreY);
448 surface->LineTo(right - armLength, centreY + armLength);
449 right += 4;
450 }
451 }
452 break;
453
454 case SC_MARK_SHORTARROW: {
455 Point pts[] = {
456 Point::FromInts(centreX, centreY + dimOn2),
457 Point::FromInts(centreX + dimOn2, centreY),
458 Point::FromInts(centreX, centreY - dimOn2),
459 Point::FromInts(centreX, centreY - dimOn4),
460 Point::FromInts(centreX - dimOn4, centreY - dimOn4),
461 Point::FromInts(centreX - dimOn4, centreY + dimOn4),
462 Point::FromInts(centreX, centreY + dimOn4),
463 Point::FromInts(centreX, centreY + dimOn2),
464 };
465 surface->Polygon(pts, std::size(pts), fore, back);
466 }
467 break;
468
469 case SC_MARK_FULLRECT:
470 surface->FillRectangle(rcWhole, back);
471 break;
472
473 case SC_MARK_LEFTRECT: {
474 PRectangle rcLeft = rcWhole;
475 rcLeft.right = rcLeft.left + 4;
476 surface->FillRectangle(rcLeft, back);
477 }
478 break;
479
480 case SC_MARK_BOOKMARK: {
481 const int halfHeight = minDim / 3;
482 Point pts[] = {
483 Point::FromInts(ircWhole.left, centreY - halfHeight),
484 Point::FromInts(ircWhole.right - 3, centreY - halfHeight),
485 Point::FromInts(ircWhole.right - 3 - halfHeight, centreY),
486 Point::FromInts(ircWhole.right - 3, centreY + halfHeight),
487 Point::FromInts(ircWhole.left, centreY + halfHeight),
488 };
489 surface->Polygon(pts, std::size(pts), fore, back);
490 }
491 break;
492
493 case SC_MARK_VERTICALBOOKMARK: {
494 const int halfWidth = minDim / 3;
495 Point pts[] = {
496 Point::FromInts(centreX - halfWidth, centreY - dimOn2),
497 Point::FromInts(centreX + halfWidth, centreY - dimOn2),
498 Point::FromInts(centreX + halfWidth, centreY + dimOn2),
499 Point::FromInts(centreX, centreY + dimOn2 - halfWidth),
500 Point::FromInts(centreX - halfWidth, centreY + dimOn2),
501 };
502 surface->Polygon(pts, std::size(pts), fore, back);
503 }
504 break;
505
506 default:
507 if (markType >= SC_MARK_CHARACTER) {
508 std::string character(1, static_cast<char>(markType - SC_MARK_CHARACTER));
509 const XYPOSITION width = surface->WidthText(fontForCharacter, character);
510 PRectangle rcText = rc;
511 rcText.left += (rc.Width() - width) / 2;
512 rcText.right = rc.left + width;
513 surface->DrawTextClipped(rcText, fontForCharacter, rcText.bottom - 2,
514 character, fore, back);
515 } else {
516 // treat as SC_MARK_FULLRECT
517 surface->FillRectangle(rcWhole, back);
518 }
519 break;
520 }
521 }
522