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.
19 #include "Scintilla.h"
21 #include "StringCopy.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(
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(
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 {
74 customDraw(surface
, rcWhole
, fontForCharacter
, tFold
, marginStyle
, this);
78 ColourDesired colourHead
= back
;
79 ColourDesired colourBody
= back
;
80 ColourDesired colourTail
= back
;
83 case LineMarker::head
:
84 case LineMarker::headWithTail
:
85 colourHead
= backSelected
;
86 colourTail
= backSelected
;
88 case LineMarker::body
:
89 colourHead
= backSelected
;
90 colourBody
= backSelected
;
92 case LineMarker::tail
:
93 colourBody
= backSelected
;
94 colourTail
= backSelected
;
97 // LineMarker::undefined
101 if ((markType
== SC_MARK_PIXMAP
) && (pxpm
)) {
102 pxpm
->Draw(surface
, rcWhole
);
105 if ((markType
== SC_MARK_RGBAIMAGE
) && (image
)) {
106 // Make rectangle just large enough to fit image centred on centre of rcWhole
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());
115 // Restrict most shapes a bit
116 PRectangle rc
= rcWhole
;
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(
142 surface
->Ellipse(rcCircle
, fore
, back
);
143 } else if (markType
== SC_MARK_ARROW
) {
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
) {
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
) {
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
) {
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
) {
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
);
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
);
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
) {
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
);
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
);
367 } else if (markType
== SC_MARK_SHORTARROW
) {
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;
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
);