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.
16 #include "Scintilla.h"
18 #include "StringCopy.h"
20 #include "LineMarker.h"
23 using namespace Scintilla
;
26 void LineMarker::SetXPM(const char *textForm
) {
28 pxpm
= new XPM(textForm
);
29 markType
= SC_MARK_PIXMAP
;
32 void LineMarker::SetXPM(const char *const *linesForm
) {
34 pxpm
= new XPM(linesForm
);
35 markType
= SC_MARK_PIXMAP
;
38 void LineMarker::SetRGBAImage(Point sizeRGBAImage
, float scale
, const unsigned char *pixelsRGBAImage
) {
40 image
= new RGBAImage(static_cast<int>(sizeRGBAImage
.x
), static_cast<int>(sizeRGBAImage
.y
), scale
, pixelsRGBAImage
);
41 markType
= SC_MARK_RGBAIMAGE
;
44 static void DrawBox(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
, ColourDesired back
) {
45 PRectangle rc
= PRectangle::FromInts(
48 centreX
+ armSize
+ 1,
49 centreY
+ armSize
+ 1);
50 surface
->RectangleDraw(rc
, back
, fore
);
53 static void DrawCircle(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
, ColourDesired back
) {
54 PRectangle rcCircle
= PRectangle::FromInts(
57 centreX
+ armSize
+ 1,
58 centreY
+ armSize
+ 1);
59 surface
->Ellipse(rcCircle
, back
, fore
);
62 static void DrawPlus(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
) {
63 PRectangle rcV
= PRectangle::FromInts(centreX
, centreY
- armSize
+ 2, centreX
+ 1, centreY
+ armSize
- 2 + 1);
64 surface
->FillRectangle(rcV
, fore
);
65 PRectangle rcH
= PRectangle::FromInts(centreX
- armSize
+ 2, centreY
, centreX
+ armSize
- 2 + 1, centreY
+ 1);
66 surface
->FillRectangle(rcH
, fore
);
69 static void DrawMinus(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
) {
70 PRectangle rcH
= PRectangle::FromInts(centreX
- armSize
+ 2, centreY
, centreX
+ armSize
- 2 + 1, centreY
+ 1);
71 surface
->FillRectangle(rcH
, fore
);
74 void LineMarker::Draw(Surface
*surface
, PRectangle
&rcWhole
, Font
&fontForCharacter
, typeOfFold tFold
, int marginStyle
) const {
75 ColourDesired head
= back
;
76 ColourDesired body
= back
;
77 ColourDesired tail
= back
;
80 case LineMarker::head
:
81 case LineMarker::headWithTail
:
85 case LineMarker::body
:
89 case LineMarker::tail
:
94 // LineMarker::undefined
98 if ((markType
== SC_MARK_PIXMAP
) && (pxpm
)) {
99 pxpm
->Draw(surface
, rcWhole
);
102 if ((markType
== SC_MARK_RGBAIMAGE
) && (image
)) {
103 // Make rectangle just large enough to fit image centred on centre of rcWhole
105 rcImage
.top
= ((rcWhole
.top
+ rcWhole
.bottom
) - image
->GetScaledHeight()) / 2;
106 rcImage
.bottom
= rcImage
.top
+ image
->GetScaledHeight();
107 rcImage
.left
= ((rcWhole
.left
+ rcWhole
.right
) - image
->GetScaledWidth()) / 2;
108 rcImage
.right
= rcImage
.left
+ image
->GetScaledWidth();
109 surface
->DrawRGBAImage(rcImage
, image
->GetWidth(), image
->GetHeight(), image
->Pixels());
112 // Restrict most shapes a bit
113 PRectangle rc
= rcWhole
;
116 int minDim
= Platform::Minimum(static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()));
117 minDim
--; // Ensure does not go beyond edge
118 int centreX
= static_cast<int>(floor((rc
.right
+ rc
.left
) / 2.0));
119 int centreY
= static_cast<int>(floor((rc
.bottom
+ rc
.top
) / 2.0));
120 int dimOn2
= minDim
/ 2;
121 int dimOn4
= minDim
/ 4;
122 int blobSize
= dimOn2
-1;
123 int armSize
= dimOn2
-2;
124 if (marginStyle
== SC_MARGIN_NUMBER
|| marginStyle
== SC_MARGIN_TEXT
|| marginStyle
== SC_MARGIN_RTEXT
) {
125 // On textual margins move marker to the left to try to avoid overlapping the text
126 centreX
= static_cast<int>(rc
.left
) + dimOn2
+ 1;
128 if (markType
== SC_MARK_ROUNDRECT
) {
129 PRectangle rcRounded
= rc
;
130 rcRounded
.left
= rc
.left
+ 1;
131 rcRounded
.right
= rc
.right
- 1;
132 surface
->RoundedRectangle(rcRounded
, fore
, back
);
133 } else if (markType
== SC_MARK_CIRCLE
) {
134 PRectangle rcCircle
= PRectangle::FromInts(
139 surface
->Ellipse(rcCircle
, fore
, back
);
140 } else if (markType
== SC_MARK_ARROW
) {
142 Point::FromInts(centreX
- dimOn4
, centreY
- dimOn2
),
143 Point::FromInts(centreX
- dimOn4
, centreY
+ dimOn2
),
144 Point::FromInts(centreX
+ dimOn2
- dimOn4
, centreY
),
146 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
148 } else if (markType
== SC_MARK_ARROWDOWN
) {
150 Point::FromInts(centreX
- dimOn2
, centreY
- dimOn4
),
151 Point::FromInts(centreX
+ dimOn2
, centreY
- dimOn4
),
152 Point::FromInts(centreX
, centreY
+ dimOn2
- dimOn4
),
154 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
156 } else if (markType
== SC_MARK_PLUS
) {
158 Point::FromInts(centreX
- armSize
, centreY
- 1),
159 Point::FromInts(centreX
- 1, centreY
- 1),
160 Point::FromInts(centreX
- 1, centreY
- armSize
),
161 Point::FromInts(centreX
+ 1, centreY
- armSize
),
162 Point::FromInts(centreX
+ 1, centreY
- 1),
163 Point::FromInts(centreX
+ armSize
, centreY
-1),
164 Point::FromInts(centreX
+ armSize
, centreY
+1),
165 Point::FromInts(centreX
+ 1, centreY
+ 1),
166 Point::FromInts(centreX
+ 1, centreY
+ armSize
),
167 Point::FromInts(centreX
- 1, centreY
+ armSize
),
168 Point::FromInts(centreX
- 1, centreY
+ 1),
169 Point::FromInts(centreX
- armSize
, centreY
+ 1),
171 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
173 } else if (markType
== SC_MARK_MINUS
) {
175 Point::FromInts(centreX
- armSize
, centreY
- 1),
176 Point::FromInts(centreX
+ armSize
, centreY
-1),
177 Point::FromInts(centreX
+ armSize
, centreY
+1),
178 Point::FromInts(centreX
- armSize
, centreY
+ 1),
180 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
182 } else if (markType
== SC_MARK_SMALLRECT
) {
184 rcSmall
.left
= rc
.left
+ 1;
185 rcSmall
.top
= rc
.top
+ 2;
186 rcSmall
.right
= rc
.right
- 1;
187 rcSmall
.bottom
= rc
.bottom
- 2;
188 surface
->RectangleDraw(rcSmall
, fore
, back
);
190 } else if (markType
== SC_MARK_EMPTY
|| markType
== SC_MARK_BACKGROUND
||
191 markType
== SC_MARK_UNDERLINE
|| markType
== SC_MARK_AVAILABLE
) {
192 // An invisible marker so don't draw anything
194 } else if (markType
== SC_MARK_VLINE
) {
195 surface
->PenColour(body
);
196 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
197 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
199 } else if (markType
== SC_MARK_LCORNER
) {
200 surface
->PenColour(tail
);
201 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
202 surface
->LineTo(centreX
, centreY
);
203 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
205 } else if (markType
== SC_MARK_TCORNER
) {
206 surface
->PenColour(tail
);
207 surface
->MoveTo(centreX
, centreY
);
208 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
210 surface
->PenColour(body
);
211 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
212 surface
->LineTo(centreX
, centreY
+ 1);
214 surface
->PenColour(head
);
215 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
217 } else if (markType
== SC_MARK_LCORNERCURVE
) {
218 surface
->PenColour(tail
);
219 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
220 surface
->LineTo(centreX
, centreY
-3);
221 surface
->LineTo(centreX
+3, centreY
);
222 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
224 } else if (markType
== SC_MARK_TCORNERCURVE
) {
225 surface
->PenColour(tail
);
226 surface
->MoveTo(centreX
, centreY
-3);
227 surface
->LineTo(centreX
+3, centreY
);
228 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
230 surface
->PenColour(body
);
231 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
232 surface
->LineTo(centreX
, centreY
-2);
234 surface
->PenColour(head
);
235 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
237 } else if (markType
== SC_MARK_BOXPLUS
) {
238 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, head
);
239 DrawPlus(surface
, centreX
, centreY
, blobSize
, tail
);
241 } else if (markType
== SC_MARK_BOXPLUSCONNECTED
) {
242 if (tFold
== LineMarker::headWithTail
)
243 surface
->PenColour(tail
);
245 surface
->PenColour(body
);
246 surface
->MoveTo(centreX
, centreY
+ blobSize
);
247 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
249 surface
->PenColour(body
);
250 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
251 surface
->LineTo(centreX
, centreY
- blobSize
);
253 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, head
);
254 DrawPlus(surface
, centreX
, centreY
, blobSize
, tail
);
256 if (tFold
== LineMarker::body
) {
257 surface
->PenColour(tail
);
258 surface
->MoveTo(centreX
+ 1, centreY
+ blobSize
);
259 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
+ blobSize
);
261 surface
->MoveTo(centreX
+ blobSize
, centreY
+ blobSize
);
262 surface
->LineTo(centreX
+ blobSize
, centreY
- blobSize
);
264 surface
->MoveTo(centreX
+ 1, centreY
- blobSize
);
265 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
- blobSize
);
267 } else if (markType
== SC_MARK_BOXMINUS
) {
268 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, head
);
269 DrawMinus(surface
, centreX
, centreY
, blobSize
, tail
);
271 surface
->PenColour(head
);
272 surface
->MoveTo(centreX
, centreY
+ blobSize
);
273 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
275 } else if (markType
== SC_MARK_BOXMINUSCONNECTED
) {
276 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, head
);
277 DrawMinus(surface
, centreX
, centreY
, blobSize
, tail
);
279 surface
->PenColour(head
);
280 surface
->MoveTo(centreX
, centreY
+ blobSize
);
281 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
283 surface
->PenColour(body
);
284 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
285 surface
->LineTo(centreX
, centreY
- blobSize
);
287 if (tFold
== LineMarker::body
) {
288 surface
->PenColour(tail
);
289 surface
->MoveTo(centreX
+ 1, centreY
+ blobSize
);
290 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
+ blobSize
);
292 surface
->MoveTo(centreX
+ blobSize
, centreY
+ blobSize
);
293 surface
->LineTo(centreX
+ blobSize
, centreY
- blobSize
);
295 surface
->MoveTo(centreX
+ 1, centreY
- blobSize
);
296 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
- blobSize
);
298 } else if (markType
== SC_MARK_CIRCLEPLUS
) {
299 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, head
);
300 DrawPlus(surface
, centreX
, centreY
, blobSize
, tail
);
302 } else if (markType
== SC_MARK_CIRCLEPLUSCONNECTED
) {
303 if (tFold
== LineMarker::headWithTail
)
304 surface
->PenColour(tail
);
306 surface
->PenColour(body
);
307 surface
->MoveTo(centreX
, centreY
+ blobSize
);
308 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
310 surface
->PenColour(body
);
311 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
312 surface
->LineTo(centreX
, centreY
- blobSize
);
314 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, head
);
315 DrawPlus(surface
, centreX
, centreY
, blobSize
, tail
);
317 } else if (markType
== SC_MARK_CIRCLEMINUS
) {
318 surface
->PenColour(head
);
319 surface
->MoveTo(centreX
, centreY
+ blobSize
);
320 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
322 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, head
);
323 DrawMinus(surface
, centreX
, centreY
, blobSize
, tail
);
325 } else if (markType
== SC_MARK_CIRCLEMINUSCONNECTED
) {
326 surface
->PenColour(head
);
327 surface
->MoveTo(centreX
, centreY
+ blobSize
);
328 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
330 surface
->PenColour(body
);
331 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
332 surface
->LineTo(centreX
, centreY
- blobSize
);
334 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, head
);
335 DrawMinus(surface
, centreX
, centreY
, blobSize
, tail
);
337 } else if (markType
>= SC_MARK_CHARACTER
) {
339 character
[0] = static_cast<char>(markType
- SC_MARK_CHARACTER
);
340 XYPOSITION width
= surface
->WidthText(fontForCharacter
, character
, 1);
341 rc
.left
+= (rc
.Width() - width
) / 2;
342 rc
.right
= rc
.left
+ width
;
343 surface
->DrawTextClipped(rc
, fontForCharacter
, rc
.bottom
- 2,
344 character
, 1, fore
, back
);
346 } else if (markType
== SC_MARK_DOTDOTDOT
) {
347 XYPOSITION right
= static_cast<XYPOSITION
>(centreX
- 6);
348 for (int b
=0; b
<3; b
++) {
349 PRectangle
rcBlob(right
, rc
.bottom
- 4, right
+ 2, rc
.bottom
-2);
350 surface
->FillRectangle(rcBlob
, fore
);
353 } else if (markType
== SC_MARK_ARROWS
) {
354 surface
->PenColour(fore
);
355 int right
= centreX
- 2;
356 const int armLength
= dimOn2
- 1;
357 for (int b
= 0; b
<3; b
++) {
358 surface
->MoveTo(right
, centreY
);
359 surface
->LineTo(right
- armLength
, centreY
- armLength
);
360 surface
->MoveTo(right
, centreY
);
361 surface
->LineTo(right
- armLength
, centreY
+ armLength
);
364 } else if (markType
== SC_MARK_SHORTARROW
) {
366 Point::FromInts(centreX
, centreY
+ dimOn2
),
367 Point::FromInts(centreX
+ dimOn2
, centreY
),
368 Point::FromInts(centreX
, centreY
- dimOn2
),
369 Point::FromInts(centreX
, centreY
- dimOn4
),
370 Point::FromInts(centreX
- dimOn4
, centreY
- dimOn4
),
371 Point::FromInts(centreX
- dimOn4
, centreY
+ dimOn4
),
372 Point::FromInts(centreX
, centreY
+ dimOn4
),
373 Point::FromInts(centreX
, centreY
+ dimOn2
),
375 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
376 } else if (markType
== SC_MARK_LEFTRECT
) {
377 PRectangle rcLeft
= rcWhole
;
378 rcLeft
.right
= rcLeft
.left
+ 4;
379 surface
->FillRectangle(rcLeft
, back
);
380 } else if (markType
== SC_MARK_BOOKMARK
) {
381 int halfHeight
= minDim
/ 3;
383 Point::FromInts(static_cast<int>(rc
.left
), centreY
-halfHeight
),
384 Point::FromInts(static_cast<int>(rc
.right
) - 3, centreY
- halfHeight
),
385 Point::FromInts(static_cast<int>(rc
.right
) - 3 - halfHeight
, centreY
),
386 Point::FromInts(static_cast<int>(rc
.right
) - 3, centreY
+ halfHeight
),
387 Point::FromInts(static_cast<int>(rc
.left
), centreY
+ halfHeight
),
389 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
390 } else { // SC_MARK_FULLRECT
391 surface
->FillRectangle(rcWhole
, back
);