1 // Scintilla source code edit control
2 /** @file LineMarker.cxx
3 ** Defines the look of a line marker in the margin.
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.
18 #include "Scintilla.h"
20 #include "StringCopy.h"
22 #include "LineMarker.h"
25 using namespace Scintilla
;
28 void LineMarker::SetXPM(const char *textForm
) {
29 pxpm
.reset(new XPM(textForm
));
30 markType
= SC_MARK_PIXMAP
;
33 void LineMarker::SetXPM(const char *const *linesForm
) {
34 pxpm
.reset(new XPM(linesForm
));
35 markType
= SC_MARK_PIXMAP
;
38 void LineMarker::SetRGBAImage(Point sizeRGBAImage
, float scale
, const unsigned char *pixelsRGBAImage
) {
39 image
.reset(new RGBAImage(static_cast<int>(sizeRGBAImage
.x
), static_cast<int>(sizeRGBAImage
.y
), scale
, pixelsRGBAImage
));
40 markType
= SC_MARK_RGBAIMAGE
;
43 static void DrawBox(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
, ColourDesired back
) {
44 PRectangle rc
= PRectangle::FromInts(
47 centreX
+ armSize
+ 1,
48 centreY
+ armSize
+ 1);
49 surface
->RectangleDraw(rc
, back
, fore
);
52 static void DrawCircle(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
, ColourDesired back
) {
53 PRectangle rcCircle
= PRectangle::FromInts(
56 centreX
+ armSize
+ 1,
57 centreY
+ armSize
+ 1);
58 surface
->Ellipse(rcCircle
, back
, fore
);
61 static void DrawPlus(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
) {
62 PRectangle rcV
= PRectangle::FromInts(centreX
, centreY
- armSize
+ 2, centreX
+ 1, centreY
+ armSize
- 2 + 1);
63 surface
->FillRectangle(rcV
, fore
);
64 PRectangle rcH
= PRectangle::FromInts(centreX
- armSize
+ 2, centreY
, centreX
+ armSize
- 2 + 1, centreY
+ 1);
65 surface
->FillRectangle(rcH
, fore
);
68 static void DrawMinus(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
) {
69 PRectangle rcH
= PRectangle::FromInts(centreX
- armSize
+ 2, centreY
, centreX
+ armSize
- 2 + 1, centreY
+ 1);
70 surface
->FillRectangle(rcH
, fore
);
73 void LineMarker::Draw(Surface
*surface
, PRectangle
&rcWhole
, Font
&fontForCharacter
, typeOfFold tFold
, int marginStyle
) const {
75 customDraw(surface
, rcWhole
, fontForCharacter
, tFold
, marginStyle
, this);
79 ColourDesired colourHead
= back
;
80 ColourDesired colourBody
= back
;
81 ColourDesired colourTail
= back
;
84 case LineMarker::head
:
85 case LineMarker::headWithTail
:
86 colourHead
= backSelected
;
87 colourTail
= backSelected
;
89 case LineMarker::body
:
90 colourHead
= backSelected
;
91 colourBody
= backSelected
;
93 case LineMarker::tail
:
94 colourBody
= backSelected
;
95 colourTail
= backSelected
;
98 // LineMarker::undefined
102 if ((markType
== SC_MARK_PIXMAP
) && (pxpm
)) {
103 pxpm
->Draw(surface
, rcWhole
);
106 if ((markType
== SC_MARK_RGBAIMAGE
) && (image
)) {
107 // Make rectangle just large enough to fit image centred on centre of rcWhole
109 rcImage
.top
= ((rcWhole
.top
+ rcWhole
.bottom
) - image
->GetScaledHeight()) / 2;
110 rcImage
.bottom
= rcImage
.top
+ image
->GetScaledHeight();
111 rcImage
.left
= ((rcWhole
.left
+ rcWhole
.right
) - image
->GetScaledWidth()) / 2;
112 rcImage
.right
= rcImage
.left
+ image
->GetScaledWidth();
113 surface
->DrawRGBAImage(rcImage
, image
->GetWidth(), image
->GetHeight(), image
->Pixels());
116 // Restrict most shapes a bit
117 PRectangle rc
= rcWhole
;
120 int minDim
= Platform::Minimum(static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()));
121 minDim
--; // Ensure does not go beyond edge
122 int centreX
= static_cast<int>(floor((rc
.right
+ rc
.left
) / 2.0));
123 const int centreY
= static_cast<int>(floor((rc
.bottom
+ rc
.top
) / 2.0));
124 const int dimOn2
= minDim
/ 2;
125 const int dimOn4
= minDim
/ 4;
126 int blobSize
= dimOn2
-1;
127 const int armSize
= dimOn2
-2;
128 if (marginStyle
== SC_MARGIN_NUMBER
|| marginStyle
== SC_MARGIN_TEXT
|| marginStyle
== SC_MARGIN_RTEXT
) {
129 // On textual margins move marker to the left to try to avoid overlapping the text
130 centreX
= static_cast<int>(rc
.left
) + dimOn2
+ 1;
132 if (markType
== SC_MARK_ROUNDRECT
) {
133 PRectangle rcRounded
= rc
;
134 rcRounded
.left
= rc
.left
+ 1;
135 rcRounded
.right
= rc
.right
- 1;
136 surface
->RoundedRectangle(rcRounded
, fore
, back
);
137 } else if (markType
== SC_MARK_CIRCLE
) {
138 PRectangle rcCircle
= PRectangle::FromInts(
143 surface
->Ellipse(rcCircle
, fore
, back
);
144 } else if (markType
== SC_MARK_ARROW
) {
146 Point::FromInts(centreX
- dimOn4
, centreY
- dimOn2
),
147 Point::FromInts(centreX
- dimOn4
, centreY
+ dimOn2
),
148 Point::FromInts(centreX
+ dimOn2
- dimOn4
, centreY
),
150 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
152 } else if (markType
== SC_MARK_ARROWDOWN
) {
154 Point::FromInts(centreX
- dimOn2
, centreY
- dimOn4
),
155 Point::FromInts(centreX
+ dimOn2
, centreY
- dimOn4
),
156 Point::FromInts(centreX
, centreY
+ dimOn2
- dimOn4
),
158 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
160 } else if (markType
== SC_MARK_PLUS
) {
162 Point::FromInts(centreX
- armSize
, centreY
- 1),
163 Point::FromInts(centreX
- 1, centreY
- 1),
164 Point::FromInts(centreX
- 1, centreY
- armSize
),
165 Point::FromInts(centreX
+ 1, centreY
- armSize
),
166 Point::FromInts(centreX
+ 1, centreY
- 1),
167 Point::FromInts(centreX
+ armSize
, centreY
-1),
168 Point::FromInts(centreX
+ armSize
, centreY
+1),
169 Point::FromInts(centreX
+ 1, centreY
+ 1),
170 Point::FromInts(centreX
+ 1, centreY
+ armSize
),
171 Point::FromInts(centreX
- 1, centreY
+ armSize
),
172 Point::FromInts(centreX
- 1, centreY
+ 1),
173 Point::FromInts(centreX
- armSize
, centreY
+ 1),
175 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
177 } else if (markType
== SC_MARK_MINUS
) {
179 Point::FromInts(centreX
- armSize
, centreY
- 1),
180 Point::FromInts(centreX
+ armSize
, centreY
-1),
181 Point::FromInts(centreX
+ armSize
, centreY
+1),
182 Point::FromInts(centreX
- armSize
, centreY
+ 1),
184 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
186 } else if (markType
== SC_MARK_SMALLRECT
) {
188 rcSmall
.left
= rc
.left
+ 1;
189 rcSmall
.top
= rc
.top
+ 2;
190 rcSmall
.right
= rc
.right
- 1;
191 rcSmall
.bottom
= rc
.bottom
- 2;
192 surface
->RectangleDraw(rcSmall
, fore
, back
);
194 } else if (markType
== SC_MARK_EMPTY
|| markType
== SC_MARK_BACKGROUND
||
195 markType
== SC_MARK_UNDERLINE
|| markType
== SC_MARK_AVAILABLE
) {
196 // An invisible marker so don't draw anything
198 } else if (markType
== SC_MARK_VLINE
) {
199 surface
->PenColour(colourBody
);
200 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
201 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
203 } else if (markType
== SC_MARK_LCORNER
) {
204 surface
->PenColour(colourTail
);
205 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
206 surface
->LineTo(centreX
, centreY
);
207 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
209 } else if (markType
== SC_MARK_TCORNER
) {
210 surface
->PenColour(colourTail
);
211 surface
->MoveTo(centreX
, centreY
);
212 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
214 surface
->PenColour(colourBody
);
215 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
216 surface
->LineTo(centreX
, centreY
+ 1);
218 surface
->PenColour(colourHead
);
219 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
221 } else if (markType
== SC_MARK_LCORNERCURVE
) {
222 surface
->PenColour(colourTail
);
223 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
224 surface
->LineTo(centreX
, centreY
-3);
225 surface
->LineTo(centreX
+3, centreY
);
226 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
228 } else if (markType
== SC_MARK_TCORNERCURVE
) {
229 surface
->PenColour(colourTail
);
230 surface
->MoveTo(centreX
, centreY
-3);
231 surface
->LineTo(centreX
+3, centreY
);
232 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
234 surface
->PenColour(colourBody
);
235 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
236 surface
->LineTo(centreX
, centreY
-2);
238 surface
->PenColour(colourHead
);
239 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
241 } else if (markType
== SC_MARK_BOXPLUS
) {
242 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
243 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
245 } else if (markType
== SC_MARK_BOXPLUSCONNECTED
) {
246 if (tFold
== LineMarker::headWithTail
)
247 surface
->PenColour(colourTail
);
249 surface
->PenColour(colourBody
);
250 surface
->MoveTo(centreX
, centreY
+ blobSize
);
251 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
253 surface
->PenColour(colourBody
);
254 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
255 surface
->LineTo(centreX
, centreY
- blobSize
);
257 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
258 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
260 if (tFold
== LineMarker::body
) {
261 surface
->PenColour(colourTail
);
262 surface
->MoveTo(centreX
+ 1, centreY
+ blobSize
);
263 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
+ blobSize
);
265 surface
->MoveTo(centreX
+ blobSize
, centreY
+ blobSize
);
266 surface
->LineTo(centreX
+ blobSize
, centreY
- blobSize
);
268 surface
->MoveTo(centreX
+ 1, centreY
- blobSize
);
269 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
- blobSize
);
271 } else if (markType
== SC_MARK_BOXMINUS
) {
272 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
273 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
275 surface
->PenColour(colourHead
);
276 surface
->MoveTo(centreX
, centreY
+ blobSize
);
277 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
279 } else if (markType
== SC_MARK_BOXMINUSCONNECTED
) {
280 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
281 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
283 surface
->PenColour(colourHead
);
284 surface
->MoveTo(centreX
, centreY
+ blobSize
);
285 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
287 surface
->PenColour(colourBody
);
288 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
289 surface
->LineTo(centreX
, centreY
- blobSize
);
291 if (tFold
== LineMarker::body
) {
292 surface
->PenColour(colourTail
);
293 surface
->MoveTo(centreX
+ 1, centreY
+ blobSize
);
294 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
+ blobSize
);
296 surface
->MoveTo(centreX
+ blobSize
, centreY
+ blobSize
);
297 surface
->LineTo(centreX
+ blobSize
, centreY
- blobSize
);
299 surface
->MoveTo(centreX
+ 1, centreY
- blobSize
);
300 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
- blobSize
);
302 } else if (markType
== SC_MARK_CIRCLEPLUS
) {
303 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
304 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
306 } else if (markType
== SC_MARK_CIRCLEPLUSCONNECTED
) {
307 if (tFold
== LineMarker::headWithTail
)
308 surface
->PenColour(colourTail
);
310 surface
->PenColour(colourBody
);
311 surface
->MoveTo(centreX
, centreY
+ blobSize
);
312 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
314 surface
->PenColour(colourBody
);
315 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
316 surface
->LineTo(centreX
, centreY
- blobSize
);
318 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
319 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
321 } else if (markType
== SC_MARK_CIRCLEMINUS
) {
322 surface
->PenColour(colourHead
);
323 surface
->MoveTo(centreX
, centreY
+ blobSize
);
324 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
326 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
327 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
329 } else if (markType
== SC_MARK_CIRCLEMINUSCONNECTED
) {
330 surface
->PenColour(colourHead
);
331 surface
->MoveTo(centreX
, centreY
+ blobSize
);
332 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
334 surface
->PenColour(colourBody
);
335 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
336 surface
->LineTo(centreX
, centreY
- blobSize
);
338 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
339 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
341 } else if (markType
>= SC_MARK_CHARACTER
) {
343 character
[0] = static_cast<char>(markType
- SC_MARK_CHARACTER
);
344 XYPOSITION width
= surface
->WidthText(fontForCharacter
, character
, 1);
345 rc
.left
+= (rc
.Width() - width
) / 2;
346 rc
.right
= rc
.left
+ width
;
347 surface
->DrawTextClipped(rc
, fontForCharacter
, rc
.bottom
- 2,
348 character
, 1, fore
, back
);
350 } else if (markType
== SC_MARK_DOTDOTDOT
) {
351 XYPOSITION right
= static_cast<XYPOSITION
>(centreX
- 6);
352 for (int b
=0; b
<3; b
++) {
353 PRectangle
rcBlob(right
, rc
.bottom
- 4, right
+ 2, rc
.bottom
-2);
354 surface
->FillRectangle(rcBlob
, fore
);
357 } else if (markType
== SC_MARK_ARROWS
) {
358 surface
->PenColour(fore
);
359 int right
= centreX
- 2;
360 const int armLength
= dimOn2
- 1;
361 for (int b
= 0; b
<3; b
++) {
362 surface
->MoveTo(right
, centreY
);
363 surface
->LineTo(right
- armLength
, centreY
- armLength
);
364 surface
->MoveTo(right
, centreY
);
365 surface
->LineTo(right
- armLength
, centreY
+ armLength
);
368 } else if (markType
== SC_MARK_SHORTARROW
) {
370 Point::FromInts(centreX
, centreY
+ dimOn2
),
371 Point::FromInts(centreX
+ dimOn2
, centreY
),
372 Point::FromInts(centreX
, centreY
- dimOn2
),
373 Point::FromInts(centreX
, centreY
- dimOn4
),
374 Point::FromInts(centreX
- dimOn4
, centreY
- dimOn4
),
375 Point::FromInts(centreX
- dimOn4
, centreY
+ dimOn4
),
376 Point::FromInts(centreX
, centreY
+ dimOn4
),
377 Point::FromInts(centreX
, centreY
+ dimOn2
),
379 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
380 } else if (markType
== SC_MARK_LEFTRECT
) {
381 PRectangle rcLeft
= rcWhole
;
382 rcLeft
.right
= rcLeft
.left
+ 4;
383 surface
->FillRectangle(rcLeft
, back
);
384 } else if (markType
== SC_MARK_BOOKMARK
) {
385 const int halfHeight
= minDim
/ 3;
387 Point::FromInts(static_cast<int>(rc
.left
), centreY
-halfHeight
),
388 Point::FromInts(static_cast<int>(rc
.right
) - 3, centreY
- halfHeight
),
389 Point::FromInts(static_cast<int>(rc
.right
) - 3 - halfHeight
, centreY
),
390 Point::FromInts(static_cast<int>(rc
.right
) - 3, centreY
+ halfHeight
),
391 Point::FromInts(static_cast<int>(rc
.left
), centreY
+ halfHeight
),
393 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
394 } else { // SC_MARK_FULLRECT
395 surface
->FillRectangle(rcWhole
, back
);