Fix action icons in the log dialog being clipped on High-DPI displays
[TortoiseGit.git] / ext / scintilla / src / MarginView.cxx
blob0ca707a8b4b21ac48f01d625e3e514ef3d71f356
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 <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <math.h>
12 #include <assert.h>
13 #include <ctype.h>
15 #include <stdexcept>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
20 #include <memory>
22 #include "Platform.h"
24 #include "ILexer.h"
25 #include "Scintilla.h"
27 #include "StringCopy.h"
28 #include "Position.h"
29 #include "SplitVector.h"
30 #include "Partitioning.h"
31 #include "RunStyles.h"
32 #include "ContractionState.h"
33 #include "CellBuffer.h"
34 #include "KeyMap.h"
35 #include "Indicator.h"
36 #include "XPM.h"
37 #include "LineMarker.h"
38 #include "Style.h"
39 #include "ViewStyle.h"
40 #include "CharClassify.h"
41 #include "Decoration.h"
42 #include "CaseFolder.h"
43 #include "Document.h"
44 #include "UniConversion.h"
45 #include "Selection.h"
46 #include "PositionCache.h"
47 #include "EditModel.h"
48 #include "MarginView.h"
49 #include "EditView.h"
51 #ifdef SCI_NAMESPACE
52 using namespace Scintilla;
53 #endif
55 #ifdef SCI_NAMESPACE
56 namespace Scintilla {
57 #endif
59 void DrawWrapMarker(Surface *surface, PRectangle rcPlace,
60 bool isEndMarker, ColourDesired wrapColour) {
61 surface->PenColour(wrapColour);
63 enum { xa = 1 }; // gap before start
64 int w = static_cast<int>(rcPlace.right - rcPlace.left) - xa - 1;
66 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
68 int x0 = static_cast<int>(xStraight ? rcPlace.left : rcPlace.right - 1);
69 int y0 = static_cast<int>(rcPlace.top);
71 int dy = static_cast<int>(rcPlace.bottom - rcPlace.top) / 5;
72 int y = static_cast<int>(rcPlace.bottom - rcPlace.top) / 2 + dy;
74 struct Relative {
75 Surface *surface;
76 int xBase;
77 int xDir;
78 int yBase;
79 int yDir;
80 void MoveTo(int xRelative, int yRelative) {
81 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
83 void LineTo(int xRelative, int yRelative) {
84 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
87 Relative rel = { surface, x0, xStraight ? 1 : -1, y0, 1 };
89 // arrow head
90 rel.MoveTo(xa, y);
91 rel.LineTo(xa + 2 * w / 3, y - dy);
92 rel.MoveTo(xa, y);
93 rel.LineTo(xa + 2 * w / 3, y + dy);
95 // arrow body
96 rel.MoveTo(xa, y);
97 rel.LineTo(xa + w, y);
98 rel.LineTo(xa + w, y - 2 * dy);
99 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
100 y - 2 * dy);
103 MarginView::MarginView() {
104 pixmapSelMargin = 0;
105 pixmapSelPattern = 0;
106 pixmapSelPatternOffset1 = 0;
107 wrapMarkerPaddingRight = 3;
108 customDrawWrapMarker = NULL;
111 void MarginView::DropGraphics(bool freeObjects) {
112 if (freeObjects) {
113 delete pixmapSelMargin;
114 pixmapSelMargin = 0;
115 delete pixmapSelPattern;
116 pixmapSelPattern = 0;
117 delete pixmapSelPatternOffset1;
118 pixmapSelPatternOffset1 = 0;
119 } else {
120 if (pixmapSelMargin)
121 pixmapSelMargin->Release();
122 if (pixmapSelPattern)
123 pixmapSelPattern->Release();
124 if (pixmapSelPatternOffset1)
125 pixmapSelPatternOffset1->Release();
129 void MarginView::AllocateGraphics(const ViewStyle &vsDraw) {
130 if (!pixmapSelMargin)
131 pixmapSelMargin = Surface::Allocate(vsDraw.technology);
132 if (!pixmapSelPattern)
133 pixmapSelPattern = Surface::Allocate(vsDraw.technology);
134 if (!pixmapSelPatternOffset1)
135 pixmapSelPatternOffset1 = Surface::Allocate(vsDraw.technology);
138 void MarginView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
139 if (!pixmapSelPattern->Initialised()) {
140 const int patternSize = 8;
141 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
142 pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
143 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
144 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
145 // way between the chrome colour and the chrome highlight colour making a nice transition
146 // between the window chrome and the content area. And it works in low colour depths.
147 PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);
149 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
150 ColourDesired colourFMFill = vsDraw.selbar;
151 ColourDesired colourFMStripes = vsDraw.selbarlight;
153 if (!(vsDraw.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
154 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
155 // (Typically, the highlight colour is white.)
156 colourFMFill = vsDraw.selbarlight;
159 if (vsDraw.foldmarginColour.isSet) {
160 // override default fold margin colour
161 colourFMFill = vsDraw.foldmarginColour;
163 if (vsDraw.foldmarginHighlightColour.isSet) {
164 // override default fold margin highlight colour
165 colourFMStripes = vsDraw.foldmarginHighlightColour;
168 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
169 pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
170 for (int y = 0; y < patternSize; y++) {
171 for (int x = y % 2; x < patternSize; x += 2) {
172 PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);
173 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
174 pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
180 static int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault, const ViewStyle &vs) {
181 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
182 return markerDefault;
183 return markerCheck;
186 void MarginView::PaintMargin(Surface *surface, int topLine, PRectangle rc, PRectangle rcMargin,
187 const EditModel &model, const ViewStyle &vs) {
189 PRectangle rcSelMargin = rcMargin;
190 rcSelMargin.right = rcMargin.left;
191 if (rcSelMargin.bottom < rc.bottom)
192 rcSelMargin.bottom = rc.bottom;
194 Point ptOrigin = model.GetVisibleOriginInMain();
195 FontAlias fontLineNumber = vs.styles[STYLE_LINENUMBER].font;
196 for (size_t margin = 0; margin < vs.ms.size(); margin++) {
197 if (vs.ms[margin].width > 0) {
199 rcSelMargin.left = rcSelMargin.right;
200 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
202 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
203 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
204 // Required because of special way brush is created for selection margin
205 // Ensure patterns line up when scrolling with separate margin view
206 // by choosing correctly aligned variant.
207 bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
208 surface->FillRectangle(rcSelMargin,
209 invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
210 } else {
211 ColourDesired colour;
212 switch (vs.ms[margin].style) {
213 case SC_MARGIN_BACK:
214 colour = vs.styles[STYLE_DEFAULT].back;
215 break;
216 case SC_MARGIN_FORE:
217 colour = vs.styles[STYLE_DEFAULT].fore;
218 break;
219 case SC_MARGIN_COLOUR:
220 colour = vs.ms[margin].back;
221 break;
222 default:
223 colour = vs.styles[STYLE_LINENUMBER].back;
224 break;
226 surface->FillRectangle(rcSelMargin, colour);
228 } else {
229 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
232 const int lineStartPaint = static_cast<int>(rcMargin.top + ptOrigin.y) / vs.lineHeight;
233 int visibleLine = model.TopLineOfMain() + lineStartPaint;
234 int yposScreen = lineStartPaint * vs.lineHeight - static_cast<int>(ptOrigin.y);
235 // Work out whether the top line is whitespace located after a
236 // lessening of fold level which implies a 'fold tail' but which should not
237 // be displayed until the last of a sequence of whitespace.
238 bool needWhiteClosure = false;
239 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
240 int level = model.pdoc->GetLevel(model.cs.DocFromDisplay(visibleLine));
241 if (level & SC_FOLDLEVELWHITEFLAG) {
242 int lineBack = model.cs.DocFromDisplay(visibleLine);
243 int levelPrev = level;
244 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
245 lineBack--;
246 levelPrev = model.pdoc->GetLevel(lineBack);
248 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
249 if (LevelNumber(level) < LevelNumber(levelPrev))
250 needWhiteClosure = true;
253 if (highlightDelimiter.isEnabled) {
254 int lastLine = model.cs.DocFromDisplay(topLine + model.LinesOnScreen()) + 1;
255 model.pdoc->GetHighlightDelimiters(highlightDelimiter, model.pdoc->LineFromPosition(model.sel.MainCaret()), lastLine);
259 // Old code does not know about new markers needed to distinguish all cases
260 const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
261 SC_MARKNUM_FOLDEROPEN, vs);
262 const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
263 SC_MARKNUM_FOLDER, vs);
265 while ((visibleLine < model.cs.LinesDisplayed()) && yposScreen < rc.bottom) {
267 PLATFORM_ASSERT(visibleLine < model.cs.LinesDisplayed());
268 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
269 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
270 const int firstVisibleLine = model.cs.DisplayFromDoc(lineDoc);
271 const int lastVisibleLine = model.cs.DisplayLastFromDoc(lineDoc);
272 const bool firstSubLine = visibleLine == firstVisibleLine;
273 const bool lastSubLine = visibleLine == lastVisibleLine;
275 int marks = model.pdoc->GetMark(lineDoc);
276 if (!firstSubLine)
277 marks = 0;
279 bool headWithTail = false;
281 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
282 // Decide which fold indicator should be displayed
283 const int level = model.pdoc->GetLevel(lineDoc);
284 const int levelNext = model.pdoc->GetLevel(lineDoc + 1);
285 const int levelNum = LevelNumber(level);
286 const int levelNextNum = LevelNumber(levelNext);
287 if (level & SC_FOLDLEVELHEADERFLAG) {
288 if (firstSubLine) {
289 if (levelNum < levelNextNum) {
290 if (model.cs.GetExpanded(lineDoc)) {
291 if (levelNum == SC_FOLDLEVELBASE)
292 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
293 else
294 marks |= 1 << folderOpenMid;
295 } else {
296 if (levelNum == SC_FOLDLEVELBASE)
297 marks |= 1 << SC_MARKNUM_FOLDER;
298 else
299 marks |= 1 << folderEnd;
301 } else if (levelNum > SC_FOLDLEVELBASE) {
302 marks |= 1 << SC_MARKNUM_FOLDERSUB;
304 } else {
305 if (levelNum < levelNextNum) {
306 if (model.cs.GetExpanded(lineDoc)) {
307 marks |= 1 << SC_MARKNUM_FOLDERSUB;
308 } else if (levelNum > SC_FOLDLEVELBASE) {
309 marks |= 1 << SC_MARKNUM_FOLDERSUB;
311 } else if (levelNum > SC_FOLDLEVELBASE) {
312 marks |= 1 << SC_MARKNUM_FOLDERSUB;
315 needWhiteClosure = false;
316 const int firstFollowupLine = model.cs.DocFromDisplay(model.cs.DisplayFromDoc(lineDoc + 1));
317 const int firstFollowupLineLevel = model.pdoc->GetLevel(firstFollowupLine);
318 const int secondFollowupLineLevelNum = LevelNumber(model.pdoc->GetLevel(firstFollowupLine + 1));
319 if (!model.cs.GetExpanded(lineDoc)) {
320 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
321 (levelNum > secondFollowupLineLevelNum))
322 needWhiteClosure = true;
324 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
325 headWithTail = true;
327 } else if (level & SC_FOLDLEVELWHITEFLAG) {
328 if (needWhiteClosure) {
329 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
330 marks |= 1 << SC_MARKNUM_FOLDERSUB;
331 } else if (levelNextNum > SC_FOLDLEVELBASE) {
332 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
333 needWhiteClosure = false;
334 } else {
335 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
336 needWhiteClosure = false;
338 } else if (levelNum > SC_FOLDLEVELBASE) {
339 if (levelNextNum < levelNum) {
340 if (levelNextNum > SC_FOLDLEVELBASE) {
341 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
342 } else {
343 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
345 } else {
346 marks |= 1 << SC_MARKNUM_FOLDERSUB;
349 } else if (levelNum > SC_FOLDLEVELBASE) {
350 if (levelNextNum < levelNum) {
351 needWhiteClosure = false;
352 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
353 marks |= 1 << SC_MARKNUM_FOLDERSUB;
354 needWhiteClosure = true;
355 } else if (lastSubLine) {
356 if (levelNextNum > SC_FOLDLEVELBASE) {
357 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
358 } else {
359 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
361 } else {
362 marks |= 1 << SC_MARKNUM_FOLDERSUB;
364 } else {
365 marks |= 1 << SC_MARKNUM_FOLDERSUB;
370 marks &= vs.ms[margin].mask;
372 PRectangle rcMarker = rcSelMargin;
373 rcMarker.top = static_cast<XYPOSITION>(yposScreen);
374 rcMarker.bottom = static_cast<XYPOSITION>(yposScreen + vs.lineHeight);
375 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
376 if (firstSubLine) {
377 char number[100] = "";
378 if (lineDoc >= 0)
379 sprintf(number, "%d", lineDoc + 1);
380 if (model.foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) {
381 if (model.foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
382 int lev = model.pdoc->GetLevel(lineDoc);
383 sprintf(number, "%c%c %03X %03X",
384 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
385 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
386 LevelNumber(lev),
387 lev >> 16
389 } else {
390 int state = model.pdoc->GetLineState(lineDoc);
391 sprintf(number, "%0X", state);
394 PRectangle rcNumber = rcMarker;
395 // Right justify
396 XYPOSITION width = surface->WidthText(fontLineNumber, number, static_cast<int>(strlen(number)));
397 XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
398 rcNumber.left = xpos;
399 DrawTextNoClipPhase(surface, rcNumber, vs.styles[STYLE_LINENUMBER],
400 rcNumber.top + vs.maxAscent, number, static_cast<int>(strlen(number)), drawAll);
401 } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
402 PRectangle rcWrapMarker = rcMarker;
403 rcWrapMarker.right -= wrapMarkerPaddingRight;
404 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
405 if (customDrawWrapMarker == NULL) {
406 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
407 } else {
408 customDrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
411 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
412 const StyledText stMargin = model.pdoc->MarginStyledText(lineDoc);
413 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
414 if (firstSubLine) {
415 surface->FillRectangle(rcMarker,
416 vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
417 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
418 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
419 rcMarker.left = rcMarker.right - width - 3;
421 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker,
422 stMargin, 0, stMargin.length, drawAll);
423 } else {
424 // if we're displaying annotation lines, color the margin to match the associated document line
425 const int annotationLines = model.pdoc->AnnotationLines(lineDoc);
426 if (annotationLines && (visibleLine > lastVisibleLine - annotationLines)) {
427 surface->FillRectangle(rcMarker, vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
433 if (marks) {
434 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
435 if (marks & 1) {
436 LineMarker::typeOfFold tFold = LineMarker::undefined;
437 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
438 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
439 tFold = LineMarker::body;
440 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
441 if (firstSubLine) {
442 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
443 } else {
444 if (model.cs.GetExpanded(lineDoc) || headWithTail) {
445 tFold = LineMarker::body;
446 } else {
447 tFold = LineMarker::undefined;
450 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
451 tFold = LineMarker::tail;
454 vs.markers[markBit].Draw(surface, rcMarker, fontLineNumber, tFold, vs.ms[margin].style);
456 marks >>= 1;
460 visibleLine++;
461 yposScreen += vs.lineHeight;
466 PRectangle rcBlankMargin = rcMargin;
467 rcBlankMargin.left = rcSelMargin.right;
468 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
471 #ifdef SCI_NAMESPACE
473 #endif