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