/************************************************************** | |
* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
* | |
*************************************************************/ | |
package org.apache.openoffice.ooxml.viewer.tokenview; | |
import java.awt.Color; | |
import java.awt.Container; | |
import java.awt.Font; | |
import java.awt.FontMetrics; | |
import java.awt.Graphics; | |
import java.awt.Graphics2D; | |
import java.awt.Point; | |
import java.awt.RenderingHints; | |
import java.awt.event.ComponentEvent; | |
import java.awt.event.ComponentListener; | |
import java.awt.event.MouseEvent; | |
import java.awt.event.MouseMotionListener; | |
import java.util.Iterator; | |
import java.util.Vector; | |
import javax.swing.JPanel; | |
import javax.swing.JScrollPane; | |
import javax.swing.UIManager; | |
/** A simple view of tokenized content. | |
* | |
* Create the content by calling GetDocumentFactory() and using the returned | |
* factory to add tokenized text. | |
*/ | |
@SuppressWarnings("serial") | |
public class TokenView<TokenType> | |
extends JPanel | |
implements MouseMotionListener, DocumentFactory.IRepaintTarget, ComponentListener | |
{ | |
public TokenView () | |
{ | |
maLines = new LineContainer<TokenType>(); | |
maFormatter = new Formatter<TokenType>(); | |
addMouseMotionListener(this); | |
addComponentListener(this); | |
} | |
@Override | |
public void paintComponent (final Graphics aGraphics) | |
{ | |
super.paintComponent(aGraphics); | |
final Graphics2D aG2 = (Graphics2D)aGraphics; | |
aG2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); | |
final FormatState<TokenType> aState; | |
synchronized(maLines) | |
{ | |
aState = maFormatter.FormatText(aG2, maLines); | |
} | |
setPreferredSize(aState.GetTextBoundingSize()); | |
for (final Line<TokenType> aLine : aState) | |
{ | |
PaintLineHighlight(aG2, aLine); | |
PaintLineNumber(aG2, aLine); | |
int nX = mnTextStart; | |
for (final Run<TokenType> aRun : aLine) | |
{ | |
final Color aRunColor; | |
if (aRun == maRunUnderMouse) | |
aRunColor = maRunUnderMouseColor; | |
else if (aRun == maHighlightedErrorRun) | |
aRunColor = maErrorHighlightColor; | |
else | |
aRunColor = null; | |
aRun.Paint( | |
(Graphics2D)aGraphics, | |
nX, | |
aLine.GetBottom(), | |
aRunColor); | |
nX += aRun.GetWidth(); | |
} | |
} | |
aGraphics.setColor(maSeparatorColor); | |
final int nTop = aGraphics.getClipBounds().y; | |
final int nBottom = aGraphics.getClipBounds().y+aGraphics.getClipBounds().height; | |
aGraphics.drawLine( | |
mnBarPosition0, | |
nTop, | |
mnBarPosition0, | |
nBottom); | |
aGraphics.drawLine( | |
mnBarPosition1, | |
nTop, | |
mnBarPosition1, | |
nBottom); | |
} | |
/** Paint a line with a highlight. | |
* There are different kinds of highlight: | |
* - the current line | |
* - one of three groups of enclosing parent elements | |
*/ | |
private void PaintLineHighlight ( | |
final Graphics2D aG2, | |
final Line<TokenType> aLine) | |
{ | |
final Color aBackgroundColor; | |
if (aLine == maHighlightedLine) | |
aBackgroundColor = maHighlightColor; | |
else | |
aBackgroundColor = maBackgroundColor; | |
final Color aBarColor; | |
if (maLines.IsLineInGroup(aLine, maHighlightedGroup0)) | |
aBarColor = maGroupHighlightColor0; | |
else if (maLines.IsLineInGroup(aLine, maHighlightedGroup1)) | |
aBarColor = maGroupHighlightColor1; | |
else if (maLines.IsLineInGroup(aLine, maHighlightedGroup2)) | |
aBarColor = maGroupHighlightColor2; | |
else | |
aBarColor = maBackgroundColor; | |
aG2.setColor(aBarColor); | |
aG2.fillRect( | |
0, | |
aLine.GetTop(), | |
mnLeftBarWidth, | |
aLine.GetHeight()); | |
aG2.setColor(aBackgroundColor); | |
aG2.fillRect( | |
mnLeftBarWidth, | |
aLine.GetTop(), | |
getWidth() - mnLeftBarWidth, | |
aLine.GetHeight()); | |
} | |
private void PaintLineNumber ( | |
final Graphics2D aG2, | |
final Line<TokenType> aLine) | |
{ | |
final String sNumber = Integer.toString(aLine.GetStartOffset()); | |
final FontMetrics aMetrics = aG2.getFontMetrics(); | |
final int nWidth = aMetrics.stringWidth(sNumber); | |
final int nHeight = aMetrics.getHeight(); | |
aG2.setColor(maLineNumberColor); | |
aG2.setFont(maLineNumberFont); | |
aG2.drawString( | |
sNumber, | |
mnBarPosition0+1 + mnNumberBarWidth-nWidth, | |
aLine.GetBottom() - (aLine.GetHeight()-nHeight)/2 - aMetrics.getDescent()); | |
} | |
@Override | |
public void mouseDragged (final MouseEvent aEvent) | |
{ | |
} | |
@Override | |
public void mouseMoved (final MouseEvent aEvent) | |
{ | |
final Line<TokenType> aLine = maLines.GetLineForY(aEvent.getY()); | |
if (aLine != null) | |
{ | |
UpdateHighlightedLine(aLine); | |
final Run<TokenType> aRun = aLine.GetRunForX(aEvent.getX() - mnTextStart); | |
SetRunUnderMouse(aRun); | |
} | |
} | |
private void UpdateHighlightedLine (final Line<TokenType> aLine) | |
{ | |
HighlightLine(aLine); | |
final Iterator<Run<TokenType>> aRunIterator = aLine.iterator(); | |
if (aRunIterator.hasNext()) | |
{ | |
final Run<TokenType> aRun = aRunIterator.next(); | |
if (aRun.IsGroup()) | |
HighlightGroup(aRun); | |
else | |
HighlightGroup(aRun.GetParent()); | |
} | |
} | |
@Override | |
public void RequestRepaint() | |
{ | |
repaint(); | |
} | |
public DocumentFactory<TokenType> GetDocumentFactory() | |
{ | |
return new DocumentFactory<TokenType>(maLines, this); | |
} | |
private void HighlightLine (final Line<TokenType> aLine) | |
{ | |
if (aLine != maHighlightedLine) | |
{ | |
maHighlightedLine = aLine; | |
repaint(); | |
} | |
} | |
private void HighlightGroup (final Run<TokenType> aRun) | |
{ | |
if (maHighlightedGroup0 != aRun) | |
{ | |
maHighlightedGroup0 = aRun; | |
if (aRun != null) | |
{ | |
final Run<TokenType> aGroup1 = aRun.GetParent(); | |
maHighlightedGroup1 = aGroup1; | |
if (aGroup1 != null) | |
{ | |
final Run<TokenType> aGroup2 = aGroup1.GetParent(); | |
maHighlightedGroup2 = aGroup2; | |
} | |
} | |
repaint(); | |
} | |
} | |
@Override | |
public void componentHidden(ComponentEvent e) | |
{ | |
} | |
@Override | |
public void componentMoved (final ComponentEvent aEvent) | |
{ | |
final Point aPoint = getMousePosition(); | |
if (aPoint != null) | |
UpdateHighlightedLine(maLines.GetLineForY(aPoint.y)); | |
} | |
@Override | |
public void componentResized(ComponentEvent e) | |
{ | |
} | |
@Override | |
public void componentShown(ComponentEvent e) | |
{ | |
} | |
public Run<TokenType> GetRun (final int nOffset) | |
{ | |
final Line<TokenType> aLine = maLines.GetLineForOffset(nOffset); | |
if (aLine != null) | |
return aLine.GetRunForOffset(nOffset); | |
else | |
return null; | |
} | |
/** Return all runs that completely or partially lie in the range from | |
* start offset (including) and end offset (excluding). | |
*/ | |
public RunRange<TokenType> GetRuns (final int nStartOffset, final int nEndOffset) | |
{ | |
final Vector<Run<TokenType>> aRuns = new Vector<>(); | |
for (final Line<TokenType> aLine : maLines.GetLinesForOffsets(nStartOffset, nEndOffset)) | |
for (final Run<TokenType> aRun : aLine.GetRunsForOffsets(nStartOffset, nEndOffset)) | |
aRuns.add(aRun); | |
return new RunRange<TokenType>(aRuns); | |
} | |
public void MarkError (final Run<TokenType> aRun) | |
{ | |
maHighlightedErrorRun = aRun; | |
repaint(); | |
} | |
public void ShowRun (final Run<TokenType> aRun) | |
{ | |
final Container aComponent = getParent().getParent(); | |
if (aComponent instanceof JScrollPane) | |
((JScrollPane)aComponent).getVerticalScrollBar().setValue( | |
aRun.GetLine().GetTop()); | |
} | |
private void SetRunUnderMouse (final Run<TokenType> aRun) | |
{ | |
if (maRunUnderMouse != aRun) | |
{ | |
maRunUnderMouse = aRun; | |
if (maRunUnderMouse != null) | |
setToolTipText(maRunUnderMouse.GetToolTipText()); | |
else | |
setToolTipText(null); | |
repaint(); | |
} | |
} | |
private final LineContainer<TokenType> maLines; | |
private final Formatter<TokenType> maFormatter; | |
private Line<TokenType> maHighlightedLine; | |
private Run<TokenType> maHighlightedGroup0; | |
private Run<TokenType> maHighlightedGroup1; | |
private Run<TokenType> maHighlightedGroup2; | |
private Run<TokenType> maHighlightedErrorRun; | |
private Run<TokenType> maRunUnderMouse; | |
private final static int mnLeftBarWidth = 10; | |
private final static int mnNumberBarWidth = 30; | |
private final static int mnBarPosition0 = mnLeftBarWidth; | |
private final static int mnBarPosition1 = mnBarPosition0 + mnNumberBarWidth ; | |
private final static int mnTextStart = mnBarPosition1 + 2; | |
private final static Color maSeparatorColor = Color.GRAY; | |
private final static Color maBackgroundColor = Color.WHITE; | |
private final static Color maHighlightColor = new Color(0xB0E0E6); // Powder Blue | |
private final static Color maGroupHighlightColor0 = new Color(0x32CD32); // Lime Green | |
private final static Color maGroupHighlightColor1 = new Color(0x90EE90); // Light Green | |
private final static Color maGroupHighlightColor2 = new Color(0xbbFfbb); | |
private final static Color maErrorHighlightColor = new Color(0xff3020); | |
private final static Color maLineNumberColor = new Color(0x808080); | |
private final static Color maRunUnderMouseColor = maGroupHighlightColor2; | |
private final static Font maLineNumberFont = UIManager.getDefaults().getFont("TextField.font").deriveFont(9.0f); | |
} |