Add utils_get_real_path() and use it
[geany-mirror.git] / scintilla / src / MarginView.cxx
blob7ecc85c7be478c681698c58827463bf996f6f684
1 // Scintilla source code edit control
2 /** @file MarginView.cxx
3 ** Defines the appearance of the editor margin.
4 **/
5 // Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cassert>
11 #include <cstring>
12 #include <cctype>
13 #include <cstdio>
14 #include <cmath>
16 #include <stdexcept>
17 #include <string>
18 #include <vector>
19 #include <map>
20 #include <algorithm>
21 #include <memory>
23 #include "Platform.h"
25 #include "ILexer.h"
26 #include "Scintilla.h"
28 #include "StringCopy.h"
29 #include "Position.h"
30 #include "UniqueString.h"
31 #include "SplitVector.h"
32 #include "Partitioning.h"
33 #include "RunStyles.h"
34 #include "ContractionState.h"
35 #include "CellBuffer.h"
36 #include "KeyMap.h"
37 #include "Indicator.h"
38 #include "XPM.h"
39 #include "LineMarker.h"
40 #include "Style.h"
41 #include "ViewStyle.h"
42 #include "CharClassify.h"
43 #include "Decoration.h"
44 #include "CaseFolder.h"
45 #include "Document.h"
46 #include "UniConversion.h"
47 #include "Selection.h"
48 #include "PositionCache.h"
49 #include "EditModel.h"
50 #include "MarginView.h"
51 #include "EditView.h"
53 #ifdef SCI_NAMESPACE
54 using namespace Scintilla;
55 #endif
57 #ifdef SCI_NAMESPACE
58 namespace Scintilla {
59 #endif
61 void DrawWrapMarker(Surface *surface, PRectangle rcPlace,
62 bool isEndMarker, ColourDesired wrapColour) {
63 surface->PenColour(wrapColour);
65 enum { xa = 1 }; // gap before start
66 int w = static_cast<int>(rcPlace.right - rcPlace.left) - xa - 1;
68 const bool xStraight = isEndMarker; // x-mirrored symbol for start marker
70 const int x0 = static_cast<int>(xStraight ? rcPlace.left : rcPlace.right - 1);
71 const int y0 = static_cast<int>(rcPlace.top);
73 int dy = static_cast<int>(rcPlace.bottom - rcPlace.top) / 5;
74 int y = static_cast<int>(rcPlace.bottom - rcPlace.top) / 2 + dy;
76 struct Relative {
77 Surface *surface;
78 int xBase;
79 int xDir;
80 int yBase;
81 int yDir;
82 void MoveTo(int xRelative, int yRelative) {
83 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
85 void LineTo(int xRelative, int yRelative) {
86 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
89 Relative rel = { surface, x0, xStraight ? 1 : -1, y0, 1 };
91 // arrow head
92 rel.MoveTo(xa, y);
93 rel.LineTo(xa + 2 * w / 3, y - dy);
94 rel.MoveTo(xa, y);
95 rel.LineTo(xa + 2 * w / 3, y + dy);
97 // arrow body
98 rel.MoveTo(xa, y);
99 rel.LineTo(xa + w, y);
100 rel.LineTo(xa + w, y - 2 * dy);
101 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
102 y - 2 * dy);
105 MarginView::MarginView() {
106 wrapMarkerPaddingRight = 3;
107 customDrawWrapMarker = NULL;
110 void MarginView::DropGraphics(bool freeObjects) {
111 if (freeObjects) {
112 pixmapSelMargin.reset();
113 pixmapSelPattern.reset();
114 pixmapSelPatternOffset1.reset();
115 } else {
116 if (pixmapSelMargin)
117 pixmapSelMargin->Release();
118 if (pixmapSelPattern)
119 pixmapSelPattern->Release();
120 if (pixmapSelPatternOffset1)
121 pixmapSelPatternOffset1->Release();
125 void MarginView::AllocateGraphics(const ViewStyle &vsDraw) {
126 if (!pixmapSelMargin)
127 pixmapSelMargin.reset(Surface::Allocate(vsDraw.technology));
128 if (!pixmapSelPattern)
129 pixmapSelPattern.reset(Surface::Allocate(vsDraw.technology));
130 if (!pixmapSelPatternOffset1)
131 pixmapSelPatternOffset1.reset(Surface::Allocate(vsDraw.technology));
134 void MarginView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
135 if (!pixmapSelPattern->Initialised()) {
136 const int patternSize = 8;
137 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
138 pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
139 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
140 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
141 // way between the chrome colour and the chrome highlight colour making a nice transition
142 // between the window chrome and the content area. And it works in low colour depths.
143 PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);
145 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
146 ColourDesired colourFMFill = vsDraw.selbar;
147 ColourDesired colourFMStripes = vsDraw.selbarlight;
149 if (!(vsDraw.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
150 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
151 // (Typically, the highlight colour is white.)
152 colourFMFill = vsDraw.selbarlight;
155 if (vsDraw.foldmarginColour.isSet) {
156 // override default fold margin colour
157 colourFMFill = vsDraw.foldmarginColour;
159 if (vsDraw.foldmarginHighlightColour.isSet) {
160 // override default fold margin highlight colour
161 colourFMStripes = vsDraw.foldmarginHighlightColour;
164 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
165 pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
166 for (int y = 0; y < patternSize; y++) {
167 for (int x = y % 2; x < patternSize; x += 2) {
168 PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);
169 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
170 pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
176 static int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault, const ViewStyle &vs) {
177 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
178 return markerDefault;
179 return markerCheck;
182 void MarginView::PaintMargin(Surface *surface, Sci::Line topLine, PRectangle rc, PRectangle rcMargin,
183 const EditModel &model, const ViewStyle &vs) {
185 PRectangle rcSelMargin = rcMargin;
186 rcSelMargin.right = rcMargin.left;
187 if (rcSelMargin.bottom < rc.bottom)
188 rcSelMargin.bottom = rc.bottom;
190 Point ptOrigin = model.GetVisibleOriginInMain();
191 FontAlias fontLineNumber = vs.styles[STYLE_LINENUMBER].font;
192 for (size_t margin = 0; margin < vs.ms.size(); margin++) {
193 if (vs.ms[margin].width > 0) {
195 rcSelMargin.left = rcSelMargin.right;
196 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
198 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
199 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
200 // Required because of special way brush is created for selection margin
201 // Ensure patterns line up when scrolling with separate margin view
202 // by choosing correctly aligned variant.
203 const bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
204 surface->FillRectangle(rcSelMargin,
205 invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
206 } else {
207 ColourDesired colour;
208 switch (vs.ms[margin].style) {
209 case SC_MARGIN_BACK:
210 colour = vs.styles[STYLE_DEFAULT].back;
211 break;
212 case SC_MARGIN_FORE:
213 colour = vs.styles[STYLE_DEFAULT].fore;
214 break;
215 case SC_MARGIN_COLOUR:
216 colour = vs.ms[margin].back;
217 break;
218 default:
219 colour = vs.styles[STYLE_LINENUMBER].back;
220 break;
222 surface->FillRectangle(rcSelMargin, colour);
224 } else {
225 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
228 const int lineStartPaint = static_cast<int>(rcMargin.top + ptOrigin.y) / vs.lineHeight;
229 Sci::Line visibleLine = model.TopLineOfMain() + lineStartPaint;
230 Sci::Position yposScreen = lineStartPaint * vs.lineHeight - static_cast<Sci::Position>(ptOrigin.y);
231 // Work out whether the top line is whitespace located after a
232 // lessening of fold level which implies a 'fold tail' but which should not
233 // be displayed until the last of a sequence of whitespace.
234 bool needWhiteClosure = false;
235 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
236 const int level = model.pdoc->GetLevel(model.cs.DocFromDisplay(visibleLine));
237 if (level & SC_FOLDLEVELWHITEFLAG) {
238 Sci::Line lineBack = model.cs.DocFromDisplay(visibleLine);
239 int levelPrev = level;
240 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
241 lineBack--;
242 levelPrev = model.pdoc->GetLevel(lineBack);
244 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
245 if (LevelNumber(level) < LevelNumber(levelPrev))
246 needWhiteClosure = true;
249 if (highlightDelimiter.isEnabled) {
250 Sci::Line lastLine = model.cs.DocFromDisplay(topLine + model.LinesOnScreen()) + 1;
251 model.pdoc->GetHighlightDelimiters(highlightDelimiter, model.pdoc->LineFromPosition(model.sel.MainCaret()), lastLine);
255 // Old code does not know about new markers needed to distinguish all cases
256 const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
257 SC_MARKNUM_FOLDEROPEN, vs);
258 const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
259 SC_MARKNUM_FOLDER, vs);
261 while ((visibleLine < model.cs.LinesDisplayed()) && yposScreen < rc.bottom) {
263 PLATFORM_ASSERT(visibleLine < model.cs.LinesDisplayed());
264 const Sci::Line lineDoc = model.cs.DocFromDisplay(visibleLine);
265 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
266 const Sci::Line firstVisibleLine = model.cs.DisplayFromDoc(lineDoc);
267 const Sci::Line lastVisibleLine = model.cs.DisplayLastFromDoc(lineDoc);
268 const bool firstSubLine = visibleLine == firstVisibleLine;
269 const bool lastSubLine = visibleLine == lastVisibleLine;
271 int marks = model.pdoc->GetMark(lineDoc);
272 if (!firstSubLine)
273 marks = 0;
275 bool headWithTail = false;
277 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
278 // Decide which fold indicator should be displayed
279 const int level = model.pdoc->GetLevel(lineDoc);
280 const int levelNext = model.pdoc->GetLevel(lineDoc + 1);
281 const int levelNum = LevelNumber(level);
282 const int levelNextNum = LevelNumber(levelNext);
283 if (level & SC_FOLDLEVELHEADERFLAG) {
284 if (firstSubLine) {
285 if (levelNum < levelNextNum) {
286 if (model.cs.GetExpanded(lineDoc)) {
287 if (levelNum == SC_FOLDLEVELBASE)
288 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
289 else
290 marks |= 1 << folderOpenMid;
291 } else {
292 if (levelNum == SC_FOLDLEVELBASE)
293 marks |= 1 << SC_MARKNUM_FOLDER;
294 else
295 marks |= 1 << folderEnd;
297 } else if (levelNum > SC_FOLDLEVELBASE) {
298 marks |= 1 << SC_MARKNUM_FOLDERSUB;
300 } else {
301 if (levelNum < levelNextNum) {
302 if (model.cs.GetExpanded(lineDoc)) {
303 marks |= 1 << SC_MARKNUM_FOLDERSUB;
304 } else if (levelNum > SC_FOLDLEVELBASE) {
305 marks |= 1 << SC_MARKNUM_FOLDERSUB;
307 } else if (levelNum > SC_FOLDLEVELBASE) {
308 marks |= 1 << SC_MARKNUM_FOLDERSUB;
311 needWhiteClosure = false;
312 const Sci::Line firstFollowupLine = model.cs.DocFromDisplay(model.cs.DisplayFromDoc(lineDoc + 1));
313 const int firstFollowupLineLevel = model.pdoc->GetLevel(firstFollowupLine);
314 const int secondFollowupLineLevelNum = LevelNumber(model.pdoc->GetLevel(firstFollowupLine + 1));
315 if (!model.cs.GetExpanded(lineDoc)) {
316 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
317 (levelNum > secondFollowupLineLevelNum))
318 needWhiteClosure = true;
320 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
321 headWithTail = true;
323 } else if (level & SC_FOLDLEVELWHITEFLAG) {
324 if (needWhiteClosure) {
325 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
326 marks |= 1 << SC_MARKNUM_FOLDERSUB;
327 } else if (levelNextNum > SC_FOLDLEVELBASE) {
328 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
329 needWhiteClosure = false;
330 } else {
331 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
332 needWhiteClosure = false;
334 } else if (levelNum > SC_FOLDLEVELBASE) {
335 if (levelNextNum < levelNum) {
336 if (levelNextNum > SC_FOLDLEVELBASE) {
337 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
338 } else {
339 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
341 } else {
342 marks |= 1 << SC_MARKNUM_FOLDERSUB;
345 } else if (levelNum > SC_FOLDLEVELBASE) {
346 if (levelNextNum < levelNum) {
347 needWhiteClosure = false;
348 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
349 marks |= 1 << SC_MARKNUM_FOLDERSUB;
350 needWhiteClosure = true;
351 } else if (lastSubLine) {
352 if (levelNextNum > SC_FOLDLEVELBASE) {
353 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
354 } else {
355 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
357 } else {
358 marks |= 1 << SC_MARKNUM_FOLDERSUB;
360 } else {
361 marks |= 1 << SC_MARKNUM_FOLDERSUB;
366 marks &= vs.ms[margin].mask;
368 PRectangle rcMarker = rcSelMargin;
369 rcMarker.top = static_cast<XYPOSITION>(yposScreen);
370 rcMarker.bottom = static_cast<XYPOSITION>(yposScreen + vs.lineHeight);
371 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
372 if (firstSubLine) {
373 char number[100] = "";
374 if (lineDoc >= 0)
375 sprintf(number, "%d", lineDoc + 1);
376 if (model.foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) {
377 if (model.foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
378 const int lev = model.pdoc->GetLevel(lineDoc);
379 sprintf(number, "%c%c %03X %03X",
380 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
381 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
382 LevelNumber(lev),
383 lev >> 16
385 } else {
386 const int state = model.pdoc->GetLineState(lineDoc);
387 sprintf(number, "%0X", state);
390 PRectangle rcNumber = rcMarker;
391 // Right justify
392 XYPOSITION width = surface->WidthText(fontLineNumber, number, static_cast<int>(strlen(number)));
393 XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
394 rcNumber.left = xpos;
395 DrawTextNoClipPhase(surface, rcNumber, vs.styles[STYLE_LINENUMBER],
396 rcNumber.top + vs.maxAscent, number, static_cast<int>(strlen(number)), drawAll);
397 } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
398 PRectangle rcWrapMarker = rcMarker;
399 rcWrapMarker.right -= wrapMarkerPaddingRight;
400 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
401 if (customDrawWrapMarker == NULL) {
402 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
403 } else {
404 customDrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
407 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
408 const StyledText stMargin = model.pdoc->MarginStyledText(lineDoc);
409 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
410 if (firstSubLine) {
411 surface->FillRectangle(rcMarker,
412 vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
413 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
414 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
415 rcMarker.left = rcMarker.right - width - 3;
417 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker,
418 stMargin, 0, stMargin.length, drawAll);
419 } else {
420 // if we're displaying annotation lines, color the margin to match the associated document line
421 const int annotationLines = model.pdoc->AnnotationLines(lineDoc);
422 if (annotationLines && (visibleLine > lastVisibleLine - annotationLines)) {
423 surface->FillRectangle(rcMarker, vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
429 if (marks) {
430 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
431 if (marks & 1) {
432 LineMarker::typeOfFold tFold = LineMarker::undefined;
433 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
434 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
435 tFold = LineMarker::body;
436 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
437 if (firstSubLine) {
438 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
439 } else {
440 if (model.cs.GetExpanded(lineDoc) || headWithTail) {
441 tFold = LineMarker::body;
442 } else {
443 tFold = LineMarker::undefined;
446 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
447 tFold = LineMarker::tail;
450 vs.markers[markBit].Draw(surface, rcMarker, fontLineNumber, tFold, vs.ms[margin].style);
452 marks >>= 1;
456 visibleLine++;
457 yposScreen += vs.lineHeight;
462 PRectangle rcBlankMargin = rcMargin;
463 rcBlankMargin.left = rcSelMargin.right;
464 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
467 #ifdef SCI_NAMESPACE
469 #endif