blob: fb307f86ff33aa0cd18aaff63e35f603d3b69678 [file] [log] [blame]
/**************************************************************
*
* 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);
}