/** * Copyright (c) 2014 by Brainwy Software LTDA. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.shared_ui.editor; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.eclipse.core.runtime.Assert; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.python.pydev.shared_core.string.TextSelectionUtils; public class TextVerticalLinesIndentGuide implements IVerticalLinesIndentGuideComputer { private final IVerticalIndentGuidePreferencesProvider verticalIndentPrefs; public TextVerticalLinesIndentGuide(IVerticalIndentGuidePreferencesProvider verticalIndentPrefs) { Assert.isNotNull(verticalIndentPrefs); this.verticalIndentPrefs = verticalIndentPrefs; } @Override public int getTabWidth() { return verticalIndentPrefs.getTabWidth(); } @Override public boolean getShowIndentGuide() { return verticalIndentPrefs.getShowIndentGuide(); } @Override public Color getColor(StyledText styledText) { return verticalIndentPrefs.getColor(styledText); } @Override public int getTransparency() { return verticalIndentPrefs.getTransparency(); } @Override public void dispose() { verticalIndentPrefs.dispose(); } @Override public SortedMap<Integer, List<VerticalLinesToDraw>> computeVerticalLinesToDrawInRegion( StyledText styledText, int topIndex, int bottomIndex) { final int originalTopIndex = topIndex; SortedMap<Integer, List<VerticalLinesToDraw>> lineToVerticalLinesToDraw; lineToVerticalLinesToDraw = new TreeMap<Integer, List<VerticalLinesToDraw>>(); int lineHeight = styledText.getLineHeight(); int lineCount = styledText.getLineCount(); if (bottomIndex > lineCount - 1) { bottomIndex = lineCount - 1; } // lineHeight = styledText.getLinePixel(1) - styledText.getLinePixel(0); // Note: if the top index is an all whitespace line, we have to start computing earlier to have something valid at the all whitespaces line while (topIndex > 0) { final String string = styledText.getLine(topIndex); int firstCharPosition = TextSelectionUtils.getFirstCharPosition(string); if (firstCharPosition == string.length()) { // All whitespaces... go back until we find one that is not only whitespaces. topIndex--; } else { break; } } for (int line = topIndex; line <= bottomIndex; line++) { // Only draw in visible range... (topIndex/bottomIndex) final String string = styledText.getLine(line); int firstCharPosition = TextSelectionUtils.getFirstCharPosition(string); if (firstCharPosition == string.length()) { // The line only has whitespaces... Let's copy the indentation guide from the previous line (if any) // just updating the y. List<VerticalLinesToDraw> previousLine = lineToVerticalLinesToDraw.get(line - 1); if (previousLine != null) { ArrayList<VerticalLinesToDraw> newLst = new ArrayList<>(previousLine.size()); for (VerticalLinesToDraw verticalLinesToDraw : previousLine) { newLst.add(verticalLinesToDraw.copyChangingYOffset(lineHeight)); } lineToVerticalLinesToDraw.put(line, newLst); } continue; } if (firstCharPosition == 0) { continue; } computeLine(string, firstCharPosition, styledText, line, lineHeight, lineToVerticalLinesToDraw); } if (originalTopIndex != topIndex) { // Remove the entries we created just because we had to generate based on previous lines (those shouldn't be drawn: // we only want the visible region in the return). Set<Entry<Integer, List<VerticalLinesToDraw>>> entrySet = lineToVerticalLinesToDraw.entrySet(); Iterator<Entry<Integer, List<VerticalLinesToDraw>>> iterator = entrySet.iterator(); while (iterator.hasNext()) { Entry<Integer, List<VerticalLinesToDraw>> next = iterator.next(); if (next.getKey() < originalTopIndex) { iterator.remove(); } else { break; //As it's sorted, we know we can bail out early. } } } return lineToVerticalLinesToDraw; } private void computeLine(String string, int firstCharPosition, StyledText styledText, int line, int lineHeight, SortedMap<Integer, List<VerticalLinesToDraw>> lineToVerticalLinesToDraw) { int lineOffset = -1; String spaces = string.substring(0, firstCharPosition); int level = 0; int whitespacesFound = 0; int tabWidthUsed = getTabWidth(); for (int j = 0; j < firstCharPosition - 1; j++) { //-1 because we don't want to cover for the column where a non whitespace char is. char c = spaces.charAt(j); if (c == '\t') { level++; whitespacesFound = 0; } else { //whitespace (not tab) whitespacesFound++; if (whitespacesFound % tabWidthUsed == 0) { level++; whitespacesFound = 0; } } if (level > 0) { Point point1; if (lineOffset == -1) { lineOffset = styledText.getOffsetAtLine(line); } point1 = styledText.getLocationAtOffset(lineOffset + j + 1); int xCoord = point1.x + 3; VerticalLinesToDraw verticalLinesToDraw = new VerticalLinesToDraw(xCoord, point1.y, xCoord, point1.y + lineHeight); List<VerticalLinesToDraw> lst = lineToVerticalLinesToDraw.get(line); if (lst == null) { lst = new ArrayList<VerticalLinesToDraw>(); lineToVerticalLinesToDraw.put(line, lst); } lst.add(verticalLinesToDraw); level--; } } } }