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 <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <string.h>
9 #include <math.h>
10
11 #include <vector>
12 #include <map>
13
14 #include "Platform.h"
15
16 #include "Scintilla.h"
17 #include "XPM.h"
18 #include "LineMarker.h"
19
20 #ifdef SCI_NAMESPACE
21 using namespace Scintilla;
22 #endif
23
SetXPM(const char * textForm)24 void LineMarker::SetXPM(const char *textForm) {
25 delete pxpm;
26 pxpm = new XPM(textForm);
27 markType = SC_MARK_PIXMAP;
28 }
29
SetXPM(const char * const * linesForm)30 void LineMarker::SetXPM(const char *const *linesForm) {
31 delete pxpm;
32 pxpm = new XPM(linesForm);
33 markType = SC_MARK_PIXMAP;
34 }
35
SetRGBAImage(Point sizeRGBAImage,float scale,const unsigned char * pixelsRGBAImage)36 void LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) {
37 delete image;
38 image = new RGBAImage(sizeRGBAImage.x, sizeRGBAImage.y, scale, pixelsRGBAImage);
39 markType = SC_MARK_RGBAIMAGE;
40 }
41
DrawBox(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore,ColourDesired back)42 static void DrawBox(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
43 PRectangle rc;
44 rc.left = centreX - armSize;
45 rc.top = centreY - armSize;
46 rc.right = centreX + armSize + 1;
47 rc.bottom = centreY + armSize + 1;
48 surface->RectangleDraw(rc, back, fore);
49 }
50
DrawCircle(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore,ColourDesired back)51 static void DrawCircle(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
52 PRectangle rcCircle;
53 rcCircle.left = centreX - armSize;
54 rcCircle.top = centreY - armSize;
55 rcCircle.right = centreX + armSize + 1;
56 rcCircle.bottom = centreY + armSize + 1;
57 surface->Ellipse(rcCircle, back, fore);
58 }
59
DrawPlus(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore)60 static void DrawPlus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
61 PRectangle rcV(centreX, centreY - armSize + 2, centreX + 1, centreY + armSize - 2 + 1);
62 surface->FillRectangle(rcV, fore);
63 PRectangle rcH(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY+1);
64 surface->FillRectangle(rcH, fore);
65 }
66
DrawMinus(Surface * surface,int centreX,int centreY,int armSize,ColourDesired fore)67 static void DrawMinus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
68 PRectangle rcH(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY+1);
69 surface->FillRectangle(rcH, fore);
70 }
71
Draw(Surface * surface,PRectangle & rcWhole,Font & fontForCharacter,typeOfFold tFold,int marginStyle)72 void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, typeOfFold tFold, int marginStyle) {
73 ColourDesired head = back;
74 ColourDesired body = back;
75 ColourDesired tail = back;
76
77 switch (tFold) {
78 case LineMarker::head :
79 case LineMarker::headWithTail :
80 head = backSelected;
81 tail = backSelected;
82 break;
83 case LineMarker::body :
84 head = backSelected;
85 body = backSelected;
86 break;
87 case LineMarker::tail :
88 body = backSelected;
89 tail = backSelected;
90 break;
91 default :
92 // LineMarker::undefined
93 break;
94 }
95
96 if ((markType == SC_MARK_PIXMAP) && (pxpm)) {
97 pxpm->Draw(surface, rcWhole);
98 return;
99 }
100 if ((markType == SC_MARK_RGBAIMAGE) && (image)) {
101 // Make rectangle just large enough to fit image centred on centre of rcWhole
102 PRectangle rcImage;
103 rcImage.top = static_cast<int>(((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2);
104 rcImage.bottom = rcImage.top + image->GetScaledHeight();
105 rcImage.left = static_cast<int>(((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2);
106 rcImage.right = rcImage.left + image->GetScaledWidth();
107 surface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels());
108 return;
109 }
110 // Restrict most shapes a bit
111 PRectangle rc = rcWhole;
112 rc.top++;
113 rc.bottom--;
114 int minDim = Platform::Minimum(rc.Width(), rc.Height());
115 minDim--; // Ensure does not go beyond edge
116 int centreX = floor((rc.right + rc.left) / 2.0);
117 int centreY = floor((rc.bottom + rc.top) / 2.0);
118 int dimOn2 = minDim / 2;
119 int dimOn4 = minDim / 4;
120 int blobSize = dimOn2-1;
121 int armSize = dimOn2-2;
122 if (marginStyle == SC_MARGIN_NUMBER || marginStyle == SC_MARGIN_TEXT || marginStyle == SC_MARGIN_RTEXT) {
123 // On textual margins move marker to the left to try to avoid overlapping the text
124 centreX = rc.left + dimOn2 + 1;
125 }
126 if (markType == SC_MARK_ROUNDRECT) {
127 PRectangle rcRounded = rc;
128 rcRounded.left = rc.left + 1;
129 rcRounded.right = rc.right - 1;
130 surface->RoundedRectangle(rcRounded, fore, back);
131 } else if (markType == SC_MARK_CIRCLE) {
132 PRectangle rcCircle;
133 rcCircle.left = centreX - dimOn2;
134 rcCircle.top = centreY - dimOn2;
135 rcCircle.right = centreX + dimOn2;
136 rcCircle.bottom = centreY + dimOn2;
137 surface->Ellipse(rcCircle, fore, back);
138 } else if (markType == SC_MARK_ARROW) {
139 Point pts[] = {
140 Point(centreX - dimOn4, centreY - dimOn2),
141 Point(centreX - dimOn4, centreY + dimOn2),
142 Point(centreX + dimOn2 - dimOn4, centreY),
143 };
144 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
145 fore, back);
146
147 } else if (markType == SC_MARK_ARROWDOWN) {
148 Point pts[] = {
149 Point(centreX - dimOn2, centreY - dimOn4),
150 Point(centreX + dimOn2, centreY - dimOn4),
151 Point(centreX, centreY + dimOn2 - dimOn4),
152 };
153 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
154 fore, back);
155
156 } else if (markType == SC_MARK_PLUS) {
157 Point pts[] = {
158 Point(centreX - armSize, centreY - 1),
159 Point(centreX - 1, centreY - 1),
160 Point(centreX - 1, centreY - armSize),
161 Point(centreX + 1, centreY - armSize),
162 Point(centreX + 1, centreY - 1),
163 Point(centreX + armSize, centreY -1),
164 Point(centreX + armSize, centreY +1),
165 Point(centreX + 1, centreY + 1),
166 Point(centreX + 1, centreY + armSize),
167 Point(centreX - 1, centreY + armSize),
168 Point(centreX - 1, centreY + 1),
169 Point(centreX - armSize, centreY + 1),
170 };
171 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
172 fore, back);
173
174 } else if (markType == SC_MARK_MINUS) {
175 Point pts[] = {
176 Point(centreX - armSize, centreY - 1),
177 Point(centreX + armSize, centreY -1),
178 Point(centreX + armSize, centreY +1),
179 Point(centreX - armSize, centreY + 1),
180 };
181 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
182 fore, back);
183
184 } else if (markType == SC_MARK_SMALLRECT) {
185 PRectangle rcSmall;
186 rcSmall.left = rc.left + 1;
187 rcSmall.top = rc.top + 2;
188 rcSmall.right = rc.right - 1;
189 rcSmall.bottom = rc.bottom - 2;
190 surface->RectangleDraw(rcSmall, fore, back);
191
192 } else if (markType == SC_MARK_EMPTY || markType == SC_MARK_BACKGROUND ||
193 markType == SC_MARK_UNDERLINE || markType == SC_MARK_AVAILABLE) {
194 // An invisible marker so don't draw anything
195
196 } else if (markType == SC_MARK_VLINE) {
197 surface->PenColour(body);
198 surface->MoveTo(centreX, rcWhole.top);
199 surface->LineTo(centreX, rcWhole.bottom);
200
201 } else if (markType == SC_MARK_LCORNER) {
202 surface->PenColour(tail);
203 surface->MoveTo(centreX, rcWhole.top);
204 surface->LineTo(centreX, centreY);
205 surface->LineTo(rc.right - 1, centreY);
206
207 } else if (markType == SC_MARK_TCORNER) {
208 surface->PenColour(tail);
209 surface->MoveTo(centreX, centreY);
210 surface->LineTo(rc.right - 1, centreY);
211
212 surface->PenColour(body);
213 surface->MoveTo(centreX, rcWhole.top);
214 surface->LineTo(centreX, centreY + 1);
215
216 surface->PenColour(head);
217 surface->LineTo(centreX, rcWhole.bottom);
218
219 } else if (markType == SC_MARK_LCORNERCURVE) {
220 surface->PenColour(tail);
221 surface->MoveTo(centreX, rcWhole.top);
222 surface->LineTo(centreX, centreY-3);
223 surface->LineTo(centreX+3, centreY);
224 surface->LineTo(rc.right - 1, centreY);
225
226 } else if (markType == SC_MARK_TCORNERCURVE) {
227 surface->PenColour(tail);
228 surface->MoveTo(centreX, centreY-3);
229 surface->LineTo(centreX+3, centreY);
230 surface->LineTo(rc.right - 1, centreY);
231
232 surface->PenColour(body);
233 surface->MoveTo(centreX, rcWhole.top);
234 surface->LineTo(centreX, centreY-2);
235
236 surface->PenColour(head);
237 surface->LineTo(centreX, rcWhole.bottom);
238
239 } else if (markType == SC_MARK_BOXPLUS) {
240 DrawBox(surface, centreX, centreY, blobSize, fore, head);
241 DrawPlus(surface, centreX, centreY, blobSize, tail);
242
243 } else if (markType == SC_MARK_BOXPLUSCONNECTED) {
244 if (tFold == LineMarker::headWithTail)
245 surface->PenColour(tail);
246 else
247 surface->PenColour(body);
248 surface->MoveTo(centreX, centreY + blobSize);
249 surface->LineTo(centreX, rcWhole.bottom);
250
251 surface->PenColour(body);
252 surface->MoveTo(centreX, rcWhole.top);
253 surface->LineTo(centreX, centreY - blobSize);
254
255 DrawBox(surface, centreX, centreY, blobSize, fore, head);
256 DrawPlus(surface, centreX, centreY, blobSize, tail);
257
258 if (tFold == LineMarker::body) {
259 surface->PenColour(tail);
260 surface->MoveTo(centreX + 1, centreY + blobSize);
261 surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
262
263 surface->MoveTo(centreX + blobSize, centreY + blobSize);
264 surface->LineTo(centreX + blobSize, centreY - blobSize);
265
266 surface->MoveTo(centreX + 1, centreY - blobSize);
267 surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
268 }
269 } else if (markType == SC_MARK_BOXMINUS) {
270 DrawBox(surface, centreX, centreY, blobSize, fore, head);
271 DrawMinus(surface, centreX, centreY, blobSize, tail);
272
273 surface->PenColour(head);
274 surface->MoveTo(centreX, centreY + blobSize);
275 surface->LineTo(centreX, rcWhole.bottom);
276
277 } else if (markType == SC_MARK_BOXMINUSCONNECTED) {
278 DrawBox(surface, centreX, centreY, blobSize, fore, head);
279 DrawMinus(surface, centreX, centreY, blobSize, tail);
280
281 surface->PenColour(head);
282 surface->MoveTo(centreX, centreY + blobSize);
283 surface->LineTo(centreX, rcWhole.bottom);
284
285 surface->PenColour(body);
286 surface->MoveTo(centreX, rcWhole.top);
287 surface->LineTo(centreX, centreY - blobSize);
288
289 if (tFold == LineMarker::body) {
290 surface->PenColour(tail);
291 surface->MoveTo(centreX + 1, centreY + blobSize);
292 surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
293
294 surface->MoveTo(centreX + blobSize, centreY + blobSize);
295 surface->LineTo(centreX + blobSize, centreY - blobSize);
296
297 surface->MoveTo(centreX + 1, centreY - blobSize);
298 surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
299 }
300 } else if (markType == SC_MARK_CIRCLEPLUS) {
301 DrawCircle(surface, centreX, centreY, blobSize, fore, head);
302 DrawPlus(surface, centreX, centreY, blobSize, tail);
303
304 } else if (markType == SC_MARK_CIRCLEPLUSCONNECTED) {
305 if (tFold == LineMarker::headWithTail)
306 surface->PenColour(tail);
307 else
308 surface->PenColour(body);
309 surface->MoveTo(centreX, centreY + blobSize);
310 surface->LineTo(centreX, rcWhole.bottom);
311
312 surface->PenColour(body);
313 surface->MoveTo(centreX, rcWhole.top);
314 surface->LineTo(centreX, centreY - blobSize);
315
316 DrawCircle(surface, centreX, centreY, blobSize, fore, head);
317 DrawPlus(surface, centreX, centreY, blobSize, tail);
318
319 } else if (markType == SC_MARK_CIRCLEMINUS) {
320 DrawCircle(surface, centreX, centreY, blobSize, fore, head);
321 DrawMinus(surface, centreX, centreY, blobSize, tail);
322
323 surface->PenColour(head);
324 surface->MoveTo(centreX, centreY + blobSize);
325 surface->LineTo(centreX, rcWhole.bottom);
326
327 } else if (markType == SC_MARK_CIRCLEMINUSCONNECTED) {
328 DrawCircle(surface, centreX, centreY, blobSize, fore, head);
329 DrawMinus(surface, centreX, centreY, blobSize, tail);
330
331 surface->PenColour(head);
332 surface->MoveTo(centreX, centreY + blobSize);
333 surface->LineTo(centreX, rcWhole.bottom);
334
335 surface->PenColour(body);
336 surface->MoveTo(centreX, rcWhole.top);
337 surface->LineTo(centreX, centreY - blobSize);
338
339 } else if (markType >= SC_MARK_CHARACTER) {
340 char character[1];
341 character[0] = static_cast<char>(markType - SC_MARK_CHARACTER);
342 XYPOSITION width = surface->WidthText(fontForCharacter, character, 1);
343 rc.left += (rc.Width() - width) / 2;
344 rc.right = rc.left + width;
345 surface->DrawTextClipped(rc, fontForCharacter, rc.bottom - 2,
346 character, 1, fore, back);
347
348 } else if (markType == SC_MARK_DOTDOTDOT) {
349 int right = centreX - 6;
350 for (int b=0; b<3; b++) {
351 PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom-2);
352 surface->FillRectangle(rcBlob, fore);
353 right += 5;
354 }
355 } else if (markType == SC_MARK_ARROWS) {
356 surface->PenColour(fore);
357 int right = centreX - 2;
358 for (int b=0; b<3; b++) {
359 surface->MoveTo(right - 4, centreY - 4);
360 surface->LineTo(right, centreY);
361 surface->LineTo(right - 5, centreY + 5);
362 right += 4;
363 }
364 } else if (markType == SC_MARK_SHORTARROW) {
365 Point pts[] = {
366 Point(centreX, centreY + dimOn2),
367 Point(centreX + dimOn2, centreY),
368 Point(centreX, centreY - dimOn2),
369 Point(centreX, centreY - dimOn4),
370 Point(centreX - dimOn4, centreY - dimOn4),
371 Point(centreX - dimOn4, centreY + dimOn4),
372 Point(centreX, centreY + dimOn4),
373 Point(centreX, centreY + dimOn2),
374 };
375 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
376 fore, back);
377 } else if (markType == SC_MARK_LEFTRECT) {
378 PRectangle rcLeft = rcWhole;
379 rcLeft.right = rcLeft.left + 4;
380 surface->FillRectangle(rcLeft, back);
381 } else { // SC_MARK_FULLRECT
382 surface->FillRectangle(rcWhole, back);
383 }
384 }
385