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