Files
ByteEdit/src/org/fife/ui/rsyntaxtextarea/FoldingAwareIconRowHeader.java
T
2021-03-07 15:22:55 +01:00

223 lines
7.4 KiB
Java

/*
* 03/07/2012
* FoldingAwareIconRowHeader - Icon row header that paints itself correctly
* even when code folding is enabled.
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.Icon;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
import org.fife.ui.rtextarea.GutterIconInfo;
import org.fife.ui.rtextarea.IconRowHeader;
/**
* A row header component that takes code folding into account when painting
* itself.
*
* @author Robert Futrell
* @version 1.0
*/
public class FoldingAwareIconRowHeader extends IconRowHeader {
/**
* Constructor.
*
* @param textArea The parent text area.
*/
public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
super(textArea);
}
/**
* {@inheritDoc}
*/
@Override
protected void paintComponent(Graphics g) {
// When line wrap is not enabled, take the faster code path.
if (textArea == null) {
return;
}
RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
FoldManager fm = rsta.getFoldManager();
if (!fm.isCodeFoldingSupportedAndEnabled()) {
super.paintComponent(g);
return;
}
visibleRect = g.getClipBounds(visibleRect);
if (visibleRect == null) { // ???
visibleRect = getVisibleRect();
}
// System.out.println("IconRowHeader repainting: " + visibleRect);
if (visibleRect == null) {
return;
}
paintBackgroundImpl(g, visibleRect);
if (textArea.getLineWrap()) {
paintComponentWrapped(g);
return;
}
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
textAreaInsets = textArea.getInsets(textAreaInsets);
if (visibleRect.y < textAreaInsets.top) {
visibleRect.height -= (textAreaInsets.top - visibleRect.y);
visibleRect.y = textAreaInsets.top;
}
// Get the first line to paint.
int cellHeight = textArea.getLineHeight();
int topLine = (visibleRect.y - textAreaInsets.top) / cellHeight;
// Get where to start painting (top of the row).
// We need to be "scrolled up" up just enough for the missing part of
// the first line.
int y = topLine * cellHeight + textAreaInsets.top;
// AFTER calculating visual offset to paint at, account for folding.
topLine += fm.getHiddenLineCountAbove(topLine, true);
// Paint the active line range.
if (activeLineRangeStart > -1 && activeLineRangeEnd > -1) {
Color activeLineRangeColor = getActiveLineRangeColor();
g.setColor(activeLineRangeColor);
try {
int realY1 = rsta.yForLine(activeLineRangeStart);
if (realY1 > -1) { // Not in a collapsed fold...
int y1 = realY1;// Math.max(y, realY1);
int y2 = rsta.yForLine(activeLineRangeEnd);
if (y2 == -1) { // In a collapsed fold
y2 = y1;
}
y2 += cellHeight - 1;
if (y2 < visibleRect.y || y1 > visibleRect.y + visibleRect.height) {
// System.out.println("... nothing to paint,
// bailing...");
return;
}
y1 = Math.max(y, realY1);
y2 = Math.min(y2, visibleRect.y + visibleRect.height);
// System.out.println(y1 + "... " + y2 + "; " + realY1 + ",
// " + visibleRect);
int j = y1;
while (j <= y2) {
int yEnd = Math.min(y2, j + getWidth());
int xEnd = yEnd - j;
g.drawLine(0, j, xEnd, yEnd);
j += 2;
}
int i = 2;
while (i < getWidth()) {
int yEnd = y1 + getWidth() - i;
g.drawLine(i, y1, getWidth(), yEnd);
i += 2;
}
if (realY1 >= y && realY1 < visibleRect.y + visibleRect.height) {
g.drawLine(0, realY1, getWidth(), realY1);
}
if (y2 >= y && y2 < visibleRect.y + visibleRect.height) {
g.drawLine(0, y2, getWidth(), y2);
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
// Paint icons
if (trackingIcons != null) {
int lastLine = textArea.getLineCount() - 1;
for (int i = trackingIcons.size() - 1; i >= 0; i--) { // Last to
// first
GutterIconInfo ti = getTrackingIcon(i);
int offs = ti.getMarkedOffset();
if (offs >= 0 && offs <= doc.getLength()) {
int line = root.getElementIndex(offs);
if (line <= lastLine && line >= topLine) {
try {
Icon icon = ti.getIcon();
if (icon != null) {
int lineY = rsta.yForLine(line);
if (lineY >= y && lineY <= visibleRect.y + visibleRect.height) {
int y2 = lineY + (cellHeight - icon.getIconHeight()) / 2;
icon.paintIcon(this, g, 0, y2);
lastLine = line - 1; // Paint only 1 icon
// per line
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
} else if (line < topLine) {
break; // All other lines are above us, so quit now
}
}
}
}
}
/**
* Paints icons when line wrapping is enabled. Note that this does not override
* the parent class's implementation to avoid this version being called when
* line wrapping is disabled.
*/
private void paintComponentWrapped(Graphics g) {
// The variables we use are as follows:
// - visibleRect is the "visible" area of the text area; e.g.
// [0,100, 300,100+(lineCount*cellHeight)-1].
// actualTop.y is the topmost-pixel in the first logical line we
// paint. Note that we may well not paint this part of the logical
// line, as it may be broken into many physical lines, with the first
// few physical lines scrolled past. Note also that this is NOT the
// visible rect of this line number list; this line number list has
// visible rect == [0,0, insets.left-1,visibleRect.height-1].
// We avoid using modelToView/viewToModel where possible, as these
// methods trigger a parsing of the line into syntax tokens, which is
// costly. It's cheaper to just grab the child views' bounds.
RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
// boolean currentLineHighlighted = textArea.getHighlightCurrentLine();
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
int topPosition = textArea.viewToModel(new Point(visibleRect.x, visibleRect.y));
int topLine = root.getElementIndex(topPosition);
int topY = visibleRect.y;
int bottomY = visibleRect.y + visibleRect.height;
int cellHeight = textArea.getLineHeight();
// Paint icons
if (trackingIcons != null) {
int lastLine = textArea.getLineCount() - 1;
for (int i = trackingIcons.size() - 1; i >= 0; i--) { // Last to
// first
GutterIconInfo ti = getTrackingIcon(i);
Icon icon = ti.getIcon();
if (icon != null) {
int iconH = icon.getIconHeight();
int offs = ti.getMarkedOffset();
if (offs >= 0 && offs <= doc.getLength()) {
int line = root.getElementIndex(offs);
if (line <= lastLine && line >= topLine) {
try {
int lineY = rsta.yForLine(line);
if (lineY <= bottomY && (lineY + iconH >= topY)) {
int y2 = lineY + (cellHeight - iconH) / 2;
ti.getIcon().paintIcon(this, g, 0, y2);
lastLine = line - 1; // Paint only 1 icon
// per line
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
} else if (line < topLine) {
break; // All other lines are above us, so quit now
}
}
}
}
}
}
}