/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.gui.log;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JScrollBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.util.GraphicsUtil;
class TablePanel extends LogPanel {
private class MyListener implements ModelListener {
private void computeRowCount() {
Model model = getModel();
Selection sel = model.getSelection();
int rows = 0;
for (int i = sel.size() - 1; i >= 0; i--) {
int x = model.getValueLog(sel.get(i)).size();
if (x > rows)
rows = x;
}
if (rowCount != rows) {
rowCount = rows;
computePreferredSize();
}
}
public void entryAdded(ModelEvent event, Value[] values) {
int oldCount = rowCount;
computeRowCount();
if (oldCount == rowCount) {
int value = vsb.getValue();
if (value > vsb.getMinimum()
&& value < vsb.getMaximum() - vsb.getVisibleAmount()) {
vsb.setValue(vsb.getValue() - vsb.getUnitIncrement(-1));
} else {
repaint();
}
}
}
public void filePropertyChanged(ModelEvent event) {
}
public void selectionChanged(ModelEvent event) {
computeRowCount();
}
}
private class VerticalScrollBar extends JScrollBar implements
ChangeListener {
private static final long serialVersionUID = 1L;
private int oldMaximum = -1;
private int oldExtent = -1;
public VerticalScrollBar() {
getModel().addChangeListener(this);
}
@Override
public int getBlockIncrement(int direction) {
int curY = getValue();
int curHeight = getVisibleAmount();
int numCells = curHeight / cellHeight - 1;
if (numCells <= 0)
numCells = 1;
if (direction > 0) {
return curY > 0 ? numCells * cellHeight : numCells * cellHeight
+ HEADER_SEP;
} else {
return curY > cellHeight + HEADER_SEP ? numCells * cellHeight
: numCells * cellHeight + HEADER_SEP;
}
}
@Override
public int getUnitIncrement(int direction) {
int curY = getValue();
if (direction > 0) {
return curY > 0 ? cellHeight : cellHeight + HEADER_SEP;
} else {
return curY > cellHeight + HEADER_SEP ? cellHeight : cellHeight
+ HEADER_SEP;
}
}
public void stateChanged(ChangeEvent event) {
int newMaximum = getMaximum();
int newExtent = getVisibleAmount();
if (oldMaximum != newMaximum || oldExtent != newExtent) {
if (getValue() + oldExtent >= oldMaximum) {
setValue(newMaximum - newExtent);
}
oldMaximum = newMaximum;
oldExtent = newExtent;
}
}
}
private static final long serialVersionUID = 1L;
private static final Font HEAD_FONT = new Font("Serif", Font.BOLD, 14);
private static final Font BODY_FONT = new Font("Serif", Font.PLAIN, 14);
private static final int COLUMN_SEP = 8;
private static final int HEADER_SEP = 4;
private MyListener myListener = new MyListener();
private int cellWidth = 25; // reasonable start values
private int cellHeight = 15;
private int rowCount = 0;
private int tableWidth;
private int tableHeight;
private VerticalScrollBar vsb;
public TablePanel(LogFrame frame) {
super(frame);
vsb = new VerticalScrollBar();
modelChanged(null, getModel());
}
private void computePreferredSize() {
Model model = getModel();
Selection sel = model.getSelection();
int columns = sel.size();
if (columns == 0) {
setPreferredSize(new Dimension(0, 0));
return;
}
Graphics g = getGraphics();
if (g == null) {
cellHeight = 16;
cellWidth = 24;
} else {
FontMetrics fm = g.getFontMetrics(HEAD_FONT);
cellHeight = fm.getHeight();
cellWidth = 24;
for (int i = 0; i < columns; i++) {
String header = sel.get(i).toShortString();
cellWidth = Math.max(cellWidth, fm.stringWidth(header));
}
}
tableWidth = (cellWidth + COLUMN_SEP) * columns - COLUMN_SEP;
tableHeight = cellHeight * (1 + rowCount) + HEADER_SEP;
setPreferredSize(new Dimension(tableWidth, tableHeight));
revalidate();
repaint();
}
public int getColumn(MouseEvent event) {
int x = event.getX() - (getWidth() - tableWidth) / 2;
if (x < 0)
return -1;
Selection sel = getModel().getSelection();
int ret = (x + COLUMN_SEP / 2) / (cellWidth + COLUMN_SEP);
return ret >= 0 && ret < sel.size() ? ret : -1;
}
@Override
public String getHelpText() {
return Strings.get("tableHelp");
}
public int getRow(MouseEvent event) {
int y = event.getY() - (getHeight() - tableHeight) / 2;
if (y < cellHeight + HEADER_SEP)
return -1;
int ret = (y - cellHeight - HEADER_SEP) / cellHeight;
return ret >= 0 && ret < rowCount ? ret : -1;
}
@Override
public String getTitle() {
return Strings.get("tableTab");
}
JScrollBar getVerticalScrollBar() {
return vsb;
}
@Override
public void localeChanged() {
computePreferredSize();
repaint();
}
@Override
public void modelChanged(Model oldModel, Model newModel) {
if (oldModel != null)
oldModel.removeModelListener(myListener);
if (newModel != null)
newModel.addModelListener(myListener);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension sz = getSize();
int top = Math.max(0, (sz.height - tableHeight) / 2);
int left = Math.max(0, (sz.width - tableWidth) / 2);
Model model = getModel();
if (model == null)
return;
Selection sel = model.getSelection();
int columns = sel.size();
if (columns == 0) {
g.setFont(BODY_FONT);
GraphicsUtil.drawCenteredText(g, Strings.get("tableEmptyMessage"),
sz.width / 2, sz.height / 2);
return;
}
g.setColor(Color.GRAY);
int lineY = top + cellHeight + HEADER_SEP / 2;
g.drawLine(left, lineY, left + tableWidth, lineY);
g.setColor(Color.BLACK);
g.setFont(HEAD_FONT);
FontMetrics headerMetric = g.getFontMetrics();
int x = left;
int y = top + headerMetric.getAscent() + 1;
for (int i = 0; i < columns; i++) {
x = paintHeader(sel.get(i).toShortString(), x, y, g, headerMetric);
}
g.setFont(BODY_FONT);
FontMetrics bodyMetric = g.getFontMetrics();
Rectangle clip = g.getClipBounds();
int firstRow = Math.max(0, (clip.y - y) / cellHeight - 1);
int lastRow = Math.min(rowCount, 2 + (clip.y + clip.height - y)
/ cellHeight);
int y0 = top + cellHeight + HEADER_SEP;
x = left;
for (int col = 0; col < columns; col++) {
SelectionItem item = sel.get(col);
ValueLog log = model.getValueLog(item);
int radix = item.getRadix();
int offs = rowCount - log.size();
y = y0 + Math.max(offs, firstRow) * cellHeight;
for (int row = Math.max(offs, firstRow); row < lastRow; row++) {
Value val = log.get(row - offs);
String label = val.toDisplayString(radix);
int width = bodyMetric.stringWidth(label);
g.drawString(label, x + (cellWidth - width) / 2,
y + bodyMetric.getAscent());
y += cellHeight;
}
x += cellWidth + COLUMN_SEP;
}
}
private int paintHeader(String header, int x, int y, Graphics g,
FontMetrics fm) {
int width = fm.stringWidth(header);
g.drawString(header, x + (cellWidth - width) / 2, y);
return x + cellWidth + COLUMN_SEP;
}
}