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.
17 #include "Scintilla.h"
19 #include "StringCopy.h"
21 #include "LineMarker.h"
24 using namespace Scintilla
;
27 void LineMarker::SetXPM(const char *textForm
) {
29 pxpm
= new XPM(textForm
);
30 markType
= SC_MARK_PIXMAP
;
33 void LineMarker::SetXPM(const char *const *linesForm
) {
35 pxpm
= new XPM(linesForm
);
36 markType
= SC_MARK_PIXMAP
;
39 void LineMarker::SetRGBAImage(Point sizeRGBAImage
, float scale
, const unsigned char *pixelsRGBAImage
) {
41 image
= new RGBAImage(static_cast<int>(sizeRGBAImage
.x
), static_cast<int>(sizeRGBAImage
.y
), scale
, pixelsRGBAImage
);
42 markType
= SC_MARK_RGBAIMAGE
;
45 static void DrawBox(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
, ColourDesired back
) {
46 PRectangle rc
= PRectangle::FromInts(
49 centreX
+ armSize
+ 1,
50 centreY
+ armSize
+ 1);
51 surface
->RectangleDraw(rc
, back
, fore
);
54 static void DrawCircle(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
, ColourDesired back
) {
55 PRectangle rcCircle
= PRectangle::FromInts(
58 centreX
+ armSize
+ 1,
59 centreY
+ armSize
+ 1);
60 surface
->Ellipse(rcCircle
, back
, fore
);
63 static void DrawPlus(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
) {
64 PRectangle rcV
= PRectangle::FromInts(centreX
, centreY
- armSize
+ 2, centreX
+ 1, centreY
+ armSize
- 2 + 1);
65 surface
->FillRectangle(rcV
, fore
);
66 PRectangle rcH
= PRectangle::FromInts(centreX
- armSize
+ 2, centreY
, centreX
+ armSize
- 2 + 1, centreY
+ 1);
67 surface
->FillRectangle(rcH
, fore
);
70 static void DrawMinus(Surface
*surface
, int centreX
, int centreY
, int armSize
, ColourDesired fore
) {
71 PRectangle rcH
= PRectangle::FromInts(centreX
- armSize
+ 2, centreY
, centreX
+ armSize
- 2 + 1, centreY
+ 1);
72 surface
->FillRectangle(rcH
, fore
);
75 void LineMarker::Draw(Surface
*surface
, PRectangle
&rcWhole
, Font
&fontForCharacter
, typeOfFold tFold
, int marginStyle
) const {
76 if (customDraw
!= NULL
) {
77 customDraw(surface
, rcWhole
, fontForCharacter
, tFold
, marginStyle
, this);
81 ColourDesired colourHead
= back
;
82 ColourDesired colourBody
= back
;
83 ColourDesired colourTail
= back
;
86 case LineMarker::head
:
87 case LineMarker::headWithTail
:
88 colourHead
= backSelected
;
89 colourTail
= backSelected
;
91 case LineMarker::body
:
92 colourHead
= backSelected
;
93 colourBody
= backSelected
;
95 case LineMarker::tail
:
96 colourBody
= backSelected
;
97 colourTail
= backSelected
;
100 // LineMarker::undefined
104 if ((markType
== SC_MARK_PIXMAP
) && (pxpm
)) {
105 pxpm
->Draw(surface
, rcWhole
);
108 if ((markType
== SC_MARK_RGBAIMAGE
) && (image
)) {
109 // Make rectangle just large enough to fit image centred on centre of rcWhole
111 rcImage
.top
= ((rcWhole
.top
+ rcWhole
.bottom
) - image
->GetScaledHeight()) / 2;
112 rcImage
.bottom
= rcImage
.top
+ image
->GetScaledHeight();
113 rcImage
.left
= ((rcWhole
.left
+ rcWhole
.right
) - image
->GetScaledWidth()) / 2;
114 rcImage
.right
= rcImage
.left
+ image
->GetScaledWidth();
115 surface
->DrawRGBAImage(rcImage
, image
->GetWidth(), image
->GetHeight(), image
->Pixels());
118 // Restrict most shapes a bit
119 PRectangle rc
= rcWhole
;
122 int minDim
= Platform::Minimum(static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()));
123 minDim
--; // Ensure does not go beyond edge
124 int centreX
= static_cast<int>(floor((rc
.right
+ rc
.left
) / 2.0));
125 int centreY
= static_cast<int>(floor((rc
.bottom
+ rc
.top
) / 2.0));
126 int dimOn2
= minDim
/ 2;
127 int dimOn4
= minDim
/ 4;
128 int blobSize
= dimOn2
-1;
129 int armSize
= dimOn2
-2;
130 if (marginStyle
== SC_MARGIN_NUMBER
|| marginStyle
== SC_MARGIN_TEXT
|| marginStyle
== SC_MARGIN_RTEXT
) {
131 // On textual margins move marker to the left to try to avoid overlapping the text
132 centreX
= static_cast<int>(rc
.left
) + dimOn2
+ 1;
134 if (markType
== SC_MARK_ROUNDRECT
) {
135 PRectangle rcRounded
= rc
;
136 rcRounded
.left
= rc
.left
+ 1;
137 rcRounded
.right
= rc
.right
- 1;
138 surface
->RoundedRectangle(rcRounded
, fore
, back
);
139 } else if (markType
== SC_MARK_CIRCLE
) {
140 PRectangle rcCircle
= PRectangle::FromInts(
145 surface
->Ellipse(rcCircle
, fore
, back
);
146 } else if (markType
== SC_MARK_ARROW
) {
148 Point::FromInts(centreX
- dimOn4
, centreY
- dimOn2
),
149 Point::FromInts(centreX
- dimOn4
, centreY
+ dimOn2
),
150 Point::FromInts(centreX
+ dimOn2
- dimOn4
, centreY
),
152 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
154 } else if (markType
== SC_MARK_ARROWDOWN
) {
156 Point::FromInts(centreX
- dimOn2
, centreY
- dimOn4
),
157 Point::FromInts(centreX
+ dimOn2
, centreY
- dimOn4
),
158 Point::FromInts(centreX
, centreY
+ dimOn2
- dimOn4
),
160 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
162 } else if (markType
== SC_MARK_PLUS
) {
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),
170 Point::FromInts(centreX
+ armSize
, centreY
+1),
171 Point::FromInts(centreX
+ 1, centreY
+ 1),
172 Point::FromInts(centreX
+ 1, centreY
+ armSize
),
173 Point::FromInts(centreX
- 1, centreY
+ armSize
),
174 Point::FromInts(centreX
- 1, centreY
+ 1),
175 Point::FromInts(centreX
- armSize
, centreY
+ 1),
177 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
179 } else if (markType
== SC_MARK_MINUS
) {
181 Point::FromInts(centreX
- armSize
, centreY
- 1),
182 Point::FromInts(centreX
+ armSize
, centreY
-1),
183 Point::FromInts(centreX
+ armSize
, centreY
+1),
184 Point::FromInts(centreX
- armSize
, centreY
+ 1),
186 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
188 } else if (markType
== SC_MARK_SMALLRECT
) {
190 rcSmall
.left
= rc
.left
+ 1;
191 rcSmall
.top
= rc
.top
+ 2;
192 rcSmall
.right
= rc
.right
- 1;
193 rcSmall
.bottom
= rc
.bottom
- 2;
194 surface
->RectangleDraw(rcSmall
, fore
, back
);
196 } else if (markType
== SC_MARK_EMPTY
|| markType
== SC_MARK_BACKGROUND
||
197 markType
== SC_MARK_UNDERLINE
|| markType
== SC_MARK_AVAILABLE
) {
198 // An invisible marker so don't draw anything
200 } else if (markType
== SC_MARK_VLINE
) {
201 surface
->PenColour(colourBody
);
202 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
203 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
205 } else if (markType
== SC_MARK_LCORNER
) {
206 surface
->PenColour(colourTail
);
207 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
208 surface
->LineTo(centreX
, centreY
);
209 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
211 } else if (markType
== SC_MARK_TCORNER
) {
212 surface
->PenColour(colourTail
);
213 surface
->MoveTo(centreX
, centreY
);
214 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
216 surface
->PenColour(colourBody
);
217 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
218 surface
->LineTo(centreX
, centreY
+ 1);
220 surface
->PenColour(colourHead
);
221 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
223 } else if (markType
== SC_MARK_LCORNERCURVE
) {
224 surface
->PenColour(colourTail
);
225 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
226 surface
->LineTo(centreX
, centreY
-3);
227 surface
->LineTo(centreX
+3, centreY
);
228 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
230 } else if (markType
== SC_MARK_TCORNERCURVE
) {
231 surface
->PenColour(colourTail
);
232 surface
->MoveTo(centreX
, centreY
-3);
233 surface
->LineTo(centreX
+3, centreY
);
234 surface
->LineTo(static_cast<int>(rc
.right
) - 1, centreY
);
236 surface
->PenColour(colourBody
);
237 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
238 surface
->LineTo(centreX
, centreY
-2);
240 surface
->PenColour(colourHead
);
241 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
243 } else if (markType
== SC_MARK_BOXPLUS
) {
244 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
245 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
247 } else if (markType
== SC_MARK_BOXPLUSCONNECTED
) {
248 if (tFold
== LineMarker::headWithTail
)
249 surface
->PenColour(colourTail
);
251 surface
->PenColour(colourBody
);
252 surface
->MoveTo(centreX
, centreY
+ blobSize
);
253 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
255 surface
->PenColour(colourBody
);
256 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
257 surface
->LineTo(centreX
, centreY
- blobSize
);
259 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
260 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
262 if (tFold
== LineMarker::body
) {
263 surface
->PenColour(colourTail
);
264 surface
->MoveTo(centreX
+ 1, centreY
+ blobSize
);
265 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
+ blobSize
);
267 surface
->MoveTo(centreX
+ blobSize
, centreY
+ blobSize
);
268 surface
->LineTo(centreX
+ blobSize
, centreY
- blobSize
);
270 surface
->MoveTo(centreX
+ 1, centreY
- blobSize
);
271 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
- blobSize
);
273 } else if (markType
== SC_MARK_BOXMINUS
) {
274 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
275 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
277 surface
->PenColour(colourHead
);
278 surface
->MoveTo(centreX
, centreY
+ blobSize
);
279 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
281 } else if (markType
== SC_MARK_BOXMINUSCONNECTED
) {
282 DrawBox(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
283 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
285 surface
->PenColour(colourHead
);
286 surface
->MoveTo(centreX
, centreY
+ blobSize
);
287 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
289 surface
->PenColour(colourBody
);
290 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
291 surface
->LineTo(centreX
, centreY
- blobSize
);
293 if (tFold
== LineMarker::body
) {
294 surface
->PenColour(colourTail
);
295 surface
->MoveTo(centreX
+ 1, centreY
+ blobSize
);
296 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
+ blobSize
);
298 surface
->MoveTo(centreX
+ blobSize
, centreY
+ blobSize
);
299 surface
->LineTo(centreX
+ blobSize
, centreY
- blobSize
);
301 surface
->MoveTo(centreX
+ 1, centreY
- blobSize
);
302 surface
->LineTo(centreX
+ blobSize
+ 1, centreY
- blobSize
);
304 } else if (markType
== SC_MARK_CIRCLEPLUS
) {
305 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
306 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
308 } else if (markType
== SC_MARK_CIRCLEPLUSCONNECTED
) {
309 if (tFold
== LineMarker::headWithTail
)
310 surface
->PenColour(colourTail
);
312 surface
->PenColour(colourBody
);
313 surface
->MoveTo(centreX
, centreY
+ blobSize
);
314 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
316 surface
->PenColour(colourBody
);
317 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
318 surface
->LineTo(centreX
, centreY
- blobSize
);
320 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
321 DrawPlus(surface
, centreX
, centreY
, blobSize
, colourTail
);
323 } else if (markType
== SC_MARK_CIRCLEMINUS
) {
324 surface
->PenColour(colourHead
);
325 surface
->MoveTo(centreX
, centreY
+ blobSize
);
326 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
328 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
329 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
331 } else if (markType
== SC_MARK_CIRCLEMINUSCONNECTED
) {
332 surface
->PenColour(colourHead
);
333 surface
->MoveTo(centreX
, centreY
+ blobSize
);
334 surface
->LineTo(centreX
, static_cast<int>(rcWhole
.bottom
));
336 surface
->PenColour(colourBody
);
337 surface
->MoveTo(centreX
, static_cast<int>(rcWhole
.top
));
338 surface
->LineTo(centreX
, centreY
- blobSize
);
340 DrawCircle(surface
, centreX
, centreY
, blobSize
, fore
, colourHead
);
341 DrawMinus(surface
, centreX
, centreY
, blobSize
, colourTail
);
343 } else if (markType
>= SC_MARK_CHARACTER
) {
345 character
[0] = static_cast<char>(markType
- SC_MARK_CHARACTER
);
346 XYPOSITION width
= surface
->WidthText(fontForCharacter
, character
, 1);
347 rc
.left
+= (rc
.Width() - width
) / 2;
348 rc
.right
= rc
.left
+ width
;
349 surface
->DrawTextClipped(rc
, fontForCharacter
, rc
.bottom
- 2,
350 character
, 1, fore
, back
);
352 } else if (markType
== SC_MARK_DOTDOTDOT
) {
353 XYPOSITION right
= static_cast<XYPOSITION
>(centreX
- 6);
354 for (int b
=0; b
<3; b
++) {
355 PRectangle
rcBlob(right
, rc
.bottom
- 4, right
+ 2, rc
.bottom
-2);
356 surface
->FillRectangle(rcBlob
, fore
);
359 } else if (markType
== SC_MARK_ARROWS
) {
360 surface
->PenColour(fore
);
361 int right
= centreX
- 2;
362 const int armLength
= dimOn2
- 1;
363 for (int b
= 0; b
<3; b
++) {
364 surface
->MoveTo(right
, centreY
);
365 surface
->LineTo(right
- armLength
, centreY
- armLength
);
366 surface
->MoveTo(right
, centreY
);
367 surface
->LineTo(right
- armLength
, centreY
+ armLength
);
370 } else if (markType
== SC_MARK_SHORTARROW
) {
372 Point::FromInts(centreX
, centreY
+ dimOn2
),
373 Point::FromInts(centreX
+ dimOn2
, centreY
),
374 Point::FromInts(centreX
, centreY
- dimOn2
),
375 Point::FromInts(centreX
, centreY
- dimOn4
),
376 Point::FromInts(centreX
- dimOn4
, centreY
- dimOn4
),
377 Point::FromInts(centreX
- dimOn4
, centreY
+ dimOn4
),
378 Point::FromInts(centreX
, centreY
+ dimOn4
),
379 Point::FromInts(centreX
, centreY
+ dimOn2
),
381 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
382 } else if (markType
== SC_MARK_LEFTRECT
) {
383 PRectangle rcLeft
= rcWhole
;
384 rcLeft
.right
= rcLeft
.left
+ 4;
385 surface
->FillRectangle(rcLeft
, back
);
386 } else if (markType
== SC_MARK_BOOKMARK
) {
387 int halfHeight
= minDim
/ 3;
389 Point::FromInts(static_cast<int>(rc
.left
), centreY
-halfHeight
),
390 Point::FromInts(static_cast<int>(rc
.right
) - 3, centreY
- halfHeight
),
391 Point::FromInts(static_cast<int>(rc
.right
) - 3 - halfHeight
, centreY
),
392 Point::FromInts(static_cast<int>(rc
.right
) - 3, centreY
+ halfHeight
),
393 Point::FromInts(static_cast<int>(rc
.left
), centreY
+ halfHeight
),
395 surface
->Polygon(pts
, ELEMENTS(pts
), fore
, back
);
396 } else { // SC_MARK_FULLRECT
397 surface
->FillRectangle(rcWhole
, back
);