Update Scintilla to 4.0.4
[TortoiseGit.git] / ext / scintilla / src / MarginView.cxx
blob19211b49b1b3a7ccb3f824754de1ec042516fb79
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 "ILoader.h"
26 #include "ILexer.h"
27 #include "Scintilla.h"
29 #include "StringCopy.h"
30 #include "Position.h"
31 #include "UniqueString.h"
32 #include "SplitVector.h"
33 #include "Partitioning.h"
34 #include "RunStyles.h"
35 #include "ContractionState.h"
36 #include "CellBuffer.h"
37 #include "KeyMap.h"
38 #include "Indicator.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 using namespace Scintilla;
55 namespace Scintilla {
57 void DrawWrapMarker(Surface *surface, PRectangle rcPlace,
58 bool isEndMarker, ColourDesired wrapColour) {
59 surface->PenColour(wrapColour);
61 enum { xa = 1 }; // gap before start
62 const int w = static_cast<int>(rcPlace.right - rcPlace.left) - xa - 1;
64 const bool xStraight = isEndMarker; // x-mirrored symbol for start marker
66 const int x0 = static_cast<int>(xStraight ? rcPlace.left : rcPlace.right - 1);
67 const int y0 = static_cast<int>(rcPlace.top);
69 const int dy = static_cast<int>(rcPlace.bottom - rcPlace.top) / 5;
70 const int y = static_cast<int>(rcPlace.bottom - rcPlace.top) / 2 + dy;
72 struct Relative {
73 Surface *surface;
74 int xBase;
75 int xDir;
76 int yBase;
77 int yDir;
78 void MoveTo(int xRelative, int yRelative) {
79 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
81 void LineTo(int xRelative, int yRelative) {
82 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
85 Relative rel = { surface, x0, xStraight ? 1 : -1, y0, 1 };
87 // arrow head
88 rel.MoveTo(xa, y);
89 rel.LineTo(xa + 2 * w / 3, y - dy);
90 rel.MoveTo(xa, y);
91 rel.LineTo(xa + 2 * w / 3, y + dy);
93 // arrow body
94 rel.MoveTo(xa, y);
95 rel.LineTo(xa + w, y);
96 rel.LineTo(xa + w, y - 2 * dy);
97 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
98 y - 2 * dy);
101 MarginView::MarginView() {
102 wrapMarkerPaddingRight = 3;
103 customDrawWrapMarker = NULL;
106 void MarginView::DropGraphics(bool freeObjects) {
107 if (freeObjects) {
108 pixmapSelMargin.reset();
109 pixmapSelPattern.reset();
110 pixmapSelPatternOffset1.reset();
111 } else {
112 if (pixmapSelMargin)
113 pixmapSelMargin->Release();
114 if (pixmapSelPattern)
115 pixmapSelPattern->Release();
116 if (pixmapSelPatternOffset1)
117 pixmapSelPatternOffset1->Release();
121 void MarginView::AllocateGraphics(const ViewStyle &vsDraw) {
122 if (!pixmapSelMargin)
123 pixmapSelMargin.reset(Surface::Allocate(vsDraw.technology));
124 if (!pixmapSelPattern)
125 pixmapSelPattern.reset(Surface::Allocate(vsDraw.technology));
126 if (!pixmapSelPatternOffset1)
127 pixmapSelPatternOffset1.reset(Surface::Allocate(vsDraw.technology));
130 void MarginView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
131 if (!pixmapSelPattern->Initialised()) {
132 const int patternSize = 8;
133 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
134 pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
135 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
136 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
137 // way between the chrome colour and the chrome highlight colour making a nice transition
138 // between the window chrome and the content area. And it works in low colour depths.
139 const PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);
141 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
142 ColourDesired colourFMFill = vsDraw.selbar;
143 ColourDesired colourFMStripes = vsDraw.selbarlight;
145 if (!(vsDraw.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
146 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
147 // (Typically, the highlight colour is white.)
148 colourFMFill = vsDraw.selbarlight;
151 if (vsDraw.foldmarginColour.isSet) {
152 // override default fold margin colour
153 colourFMFill = vsDraw.foldmarginColour;
155 if (vsDraw.foldmarginHighlightColour.isSet) {
156 // override default fold margin highlight colour
157 colourFMStripes = vsDraw.foldmarginHighlightColour;
160 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
161 pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
162 for (int y = 0; y < patternSize; y++) {
163 for (int x = y % 2; x < patternSize; x += 2) {
164 const PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);
165 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
166 pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
172 static int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault, const ViewStyle &vs) {
173 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
174 return markerDefault;
175 return markerCheck;
178 void MarginView::PaintMargin(Surface *surface, Sci::Line topLine, PRectangle rc, PRectangle rcMargin,
179 const EditModel &model, const ViewStyle &vs) {
181 PRectangle rcSelMargin = rcMargin;
182 rcSelMargin.right = rcMargin.left;
183 if (rcSelMargin.bottom < rc.bottom)
184 rcSelMargin.bottom = rc.bottom;
186 const Point ptOrigin = model.GetVisibleOriginInMain();
187 FontAlias fontLineNumber = vs.styles[STYLE_LINENUMBER].font;
188 for (size_t margin = 0; margin < vs.ms.size(); margin++) {
189 if (vs.ms[margin].width > 0) {
191 rcSelMargin.left = rcSelMargin.right;
192 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
194 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
195 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
196 // Required because of special way brush is created for selection margin
197 // Ensure patterns line up when scrolling with separate margin view
198 // by choosing correctly aligned variant.
199 const bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
200 surface->FillRectangle(rcSelMargin,
201 invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
202 } else {
203 ColourDesired colour;
204 switch (vs.ms[margin].style) {
205 case SC_MARGIN_BACK:
206 colour = vs.styles[STYLE_DEFAULT].back;
207 break;
208 case SC_MARGIN_FORE:
209 colour = vs.styles[STYLE_DEFAULT].fore;
210 break;
211 case SC_MARGIN_COLOUR:
212 colour = vs.ms[margin].back;
213 break;
214 default:
215 colour = vs.styles[STYLE_LINENUMBER].back;
216 break;
218 surface->FillRectangle(rcSelMargin, colour);
220 } else {
221 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
224 const int lineStartPaint = static_cast<int>(rcMargin.top + ptOrigin.y) / vs.lineHeight;
225 Sci::Line visibleLine = model.TopLineOfMain() + lineStartPaint;
226 Sci::Position yposScreen = lineStartPaint * vs.lineHeight - static_cast<Sci::Position>(ptOrigin.y);
227 // Work out whether the top line is whitespace located after a
228 // lessening of fold level which implies a 'fold tail' but which should not
229 // be displayed until the last of a sequence of whitespace.
230 bool needWhiteClosure = false;
231 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
232 const int level = model.pdoc->GetLevel(model.pcs->DocFromDisplay(visibleLine));
233 if (level & SC_FOLDLEVELWHITEFLAG) {
234 Sci::Line lineBack = model.pcs->DocFromDisplay(visibleLine);
235 int levelPrev = level;
236 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
237 lineBack--;
238 levelPrev = model.pdoc->GetLevel(lineBack);
240 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
241 if (LevelNumber(level) < LevelNumber(levelPrev))
242 needWhiteClosure = true;
245 if (highlightDelimiter.isEnabled) {
246 const Sci::Line lastLine = model.pcs->DocFromDisplay(topLine + model.LinesOnScreen()) + 1;
247 model.pdoc->GetHighlightDelimiters(highlightDelimiter,
248 static_cast<Sci::Line>(model.pdoc->LineFromPosition(model.sel.MainCaret())), lastLine);
252 // Old code does not know about new markers needed to distinguish all cases
253 const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
254 SC_MARKNUM_FOLDEROPEN, vs);
255 const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
256 SC_MARKNUM_FOLDER, vs);
258 while ((visibleLine < model.pcs->LinesDisplayed()) && yposScreen < rc.bottom) {
260 PLATFORM_ASSERT(visibleLine < model.pcs->LinesDisplayed());
261 const Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);
262 PLATFORM_ASSERT(model.pcs->GetVisible(lineDoc));
263 const Sci::Line firstVisibleLine = model.pcs->DisplayFromDoc(lineDoc);
264 const Sci::Line lastVisibleLine = model.pcs->DisplayLastFromDoc(lineDoc);
265 const bool firstSubLine = visibleLine == firstVisibleLine;
266 const bool lastSubLine = visibleLine == lastVisibleLine;
268 int marks = model.pdoc->GetMark(lineDoc);
269 if (!firstSubLine)
270 marks = 0;
272 bool headWithTail = false;
274 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
275 // Decide which fold indicator should be displayed
276 const int level = model.pdoc->GetLevel(lineDoc);
277 const int levelNext = model.pdoc->GetLevel(lineDoc + 1);
278 const int levelNum = LevelNumber(level);
279 const int levelNextNum = LevelNumber(levelNext);
280 if (level & SC_FOLDLEVELHEADERFLAG) {
281 if (firstSubLine) {
282 if (levelNum < levelNextNum) {
283 if (model.pcs->GetExpanded(lineDoc)) {
284 if (levelNum == SC_FOLDLEVELBASE)
285 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
286 else
287 marks |= 1 << folderOpenMid;
288 } else {
289 if (levelNum == SC_FOLDLEVELBASE)
290 marks |= 1 << SC_MARKNUM_FOLDER;
291 else
292 marks |= 1 << folderEnd;
294 } else if (levelNum > SC_FOLDLEVELBASE) {
295 marks |= 1 << SC_MARKNUM_FOLDERSUB;
297 } else {
298 if (levelNum < levelNextNum) {
299 if (model.pcs->GetExpanded(lineDoc)) {
300 marks |= 1 << SC_MARKNUM_FOLDERSUB;
301 } else if (levelNum > SC_FOLDLEVELBASE) {
302 marks |= 1 << SC_MARKNUM_FOLDERSUB;
304 } else if (levelNum > SC_FOLDLEVELBASE) {
305 marks |= 1 << SC_MARKNUM_FOLDERSUB;
308 needWhiteClosure = false;
309 const Sci::Line firstFollowupLine = model.pcs->DocFromDisplay(model.pcs->DisplayFromDoc(lineDoc + 1));
310 const int firstFollowupLineLevel = model.pdoc->GetLevel(firstFollowupLine);
311 const int secondFollowupLineLevelNum = LevelNumber(model.pdoc->GetLevel(firstFollowupLine + 1));
312 if (!model.pcs->GetExpanded(lineDoc)) {
313 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
314 (levelNum > secondFollowupLineLevelNum))
315 needWhiteClosure = true;
317 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
318 headWithTail = true;
320 } else if (level & SC_FOLDLEVELWHITEFLAG) {
321 if (needWhiteClosure) {
322 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
323 marks |= 1 << SC_MARKNUM_FOLDERSUB;
324 } else if (levelNextNum > SC_FOLDLEVELBASE) {
325 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
326 needWhiteClosure = false;
327 } else {
328 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
329 needWhiteClosure = false;
331 } else if (levelNum > SC_FOLDLEVELBASE) {
332 if (levelNextNum < levelNum) {
333 if (levelNextNum > SC_FOLDLEVELBASE) {
334 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
335 } else {
336 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
338 } else {
339 marks |= 1 << SC_MARKNUM_FOLDERSUB;
342 } else if (levelNum > SC_FOLDLEVELBASE) {
343 if (levelNextNum < levelNum) {
344 needWhiteClosure = false;
345 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
346 marks |= 1 << SC_MARKNUM_FOLDERSUB;
347 needWhiteClosure = true;
348 } else if (lastSubLine) {
349 if (levelNextNum > SC_FOLDLEVELBASE) {
350 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
351 } else {
352 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
354 } else {
355 marks |= 1 << SC_MARKNUM_FOLDERSUB;
357 } else {
358 marks |= 1 << SC_MARKNUM_FOLDERSUB;
363 marks &= vs.ms[margin].mask;
365 PRectangle rcMarker = rcSelMargin;
366 rcMarker.top = static_cast<XYPOSITION>(yposScreen);
367 rcMarker.bottom = static_cast<XYPOSITION>(yposScreen + vs.lineHeight);
368 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
369 if (firstSubLine) {
370 char number[100] = "";
371 if (lineDoc >= 0)
372 sprintf(number, "%d", lineDoc + 1);
373 if (model.foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) {
374 if (model.foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
375 const int lev = model.pdoc->GetLevel(lineDoc);
376 sprintf(number, "%c%c %03X %03X",
377 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
378 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
379 LevelNumber(lev),
380 lev >> 16
382 } else {
383 const int state = model.pdoc->GetLineState(lineDoc);
384 sprintf(number, "%0X", state);
387 PRectangle rcNumber = rcMarker;
388 // Right justify
389 const XYPOSITION width = surface->WidthText(fontLineNumber, number, static_cast<int>(strlen(number)));
390 const XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
391 rcNumber.left = xpos;
392 DrawTextNoClipPhase(surface, rcNumber, vs.styles[STYLE_LINENUMBER],
393 rcNumber.top + vs.maxAscent, number, static_cast<int>(strlen(number)), drawAll);
394 } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
395 PRectangle rcWrapMarker = rcMarker;
396 rcWrapMarker.right -= wrapMarkerPaddingRight;
397 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
398 if (customDrawWrapMarker == NULL) {
399 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
400 } else {
401 customDrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
404 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
405 const StyledText stMargin = model.pdoc->MarginStyledText(lineDoc);
406 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
407 if (firstSubLine) {
408 surface->FillRectangle(rcMarker,
409 vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
410 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
411 const int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
412 rcMarker.left = rcMarker.right - width - 3;
414 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker,
415 stMargin, 0, stMargin.length, drawAll);
416 } else {
417 // if we're displaying annotation lines, color the margin to match the associated document line
418 const int annotationLines = model.pdoc->AnnotationLines(lineDoc);
419 if (annotationLines && (visibleLine > lastVisibleLine - annotationLines)) {
420 surface->FillRectangle(rcMarker, vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
426 if (marks) {
427 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
428 if (marks & 1) {
429 LineMarker::typeOfFold tFold = LineMarker::undefined;
430 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
431 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
432 tFold = LineMarker::body;
433 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
434 if (firstSubLine) {
435 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
436 } else {
437 if (model.pcs->GetExpanded(lineDoc) || headWithTail) {
438 tFold = LineMarker::body;
439 } else {
440 tFold = LineMarker::undefined;
443 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
444 tFold = LineMarker::tail;
447 vs.markers[markBit].Draw(surface, rcMarker, fontLineNumber, tFold, vs.ms[margin].style);
449 marks >>= 1;
453 visibleLine++;
454 yposScreen += vs.lineHeight;
459 PRectangle rcBlankMargin = rcMargin;
460 rcBlankMargin.left = rcSelMargin.right;
461 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);