/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2017 RomRaider.com
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.romraider.maps;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.Serializable;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.naming.NameNotFoundException;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.InputMap;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.apache.log4j.Logger;
import com.romraider.Settings;
import com.romraider.editor.ecu.ECUEditorManager;
import com.romraider.swing.TableToolBar;
import com.romraider.util.JEPUtil;
import com.romraider.util.NumberUtil;
import com.romraider.util.SettingsManager;
import com.romraider.xml.RomAttributeParser;
public abstract class Table extends JPanel implements Serializable {
private static final long serialVersionUID = 6559256489995552645L;
protected static final Logger LOGGER = Logger.getLogger(Table.class);
protected static final String ST_DELIMITER = "\t\n\r\f";
protected static int memModelEndian;
protected String name;
protected int type;
protected String category = "Other";
protected String description = Settings.BLANK;
protected Vector<Scale> scales = new Vector<Scale>();
protected Scale curScale;
protected int storageAddress;
protected int storageType;
protected boolean signed;
protected int endian;
protected boolean flip;
protected DataCell[] data = new DataCell[0];
protected boolean beforeRam = false;
protected int ramOffset = 0;
protected BorderLayout borderLayout = new BorderLayout();
protected GridLayout centerLayout = new GridLayout(1, 1, 0, 0);
protected JPanel centerPanel = new JPanel(centerLayout);
protected JLabel tableLabel;
protected int verticalOverhead = 103;
protected int horizontalOverhead = 2;
protected int cellHeight = (int) getSettings().getCellSize().getHeight();
protected int cellWidth = (int) getSettings().getCellSize().getWidth();
protected int minHeight = 100;
protected int minWidthNoOverlay = 465;
protected int minWidthOverlay = 700;
protected int highlightX;
protected int highlightY;
protected boolean highlight = false;
protected int userLevel = 0;
protected boolean locked = false;
protected String logParam = Settings.BLANK;
protected boolean overlayLog = false;
protected CopyTableWorker copyTableWorker;
protected CopySelectionWorker copySelectionWorker;
protected double minAllowedBin = 0.0;
protected double maxAllowedBin = 0.0;
protected double maxBin;
protected double minBin;
protected double maxCompare = 0.0;
protected double minCompare = 0.0;
protected int compareDisplay = Settings.COMPARE_DISPLAY_ABSOLUTE;
protected int compareValueType = Settings.DATA_TYPE_BIN;
protected boolean staticDataTable = false;
protected String liveAxisValue = Settings.BLANK;
protected int liveDataIndex = 0;
private Table compareTable = null;
public Table() {
scales.clear();
this.setLayout(borderLayout);
this.add(centerPanel, BorderLayout.CENTER);
centerPanel.setVisible(true);
// key binding actions
Action rightAction = new AbstractAction() {
private static final long serialVersionUID = 1042884198300385041L;
@Override
public void actionPerformed(ActionEvent e) {
cursorRight();
}
};
Action leftAction = new AbstractAction() {
private static final long serialVersionUID = -4970441255677214171L;
@Override
public void actionPerformed(ActionEvent e) {
cursorLeft();
}
};
Action downAction = new AbstractAction() {
private static final long serialVersionUID = -7898502951121825984L;
@Override
public void actionPerformed(ActionEvent e) {
cursorDown();
}
};
Action upAction = new AbstractAction() {
private static final long serialVersionUID = 6937621541727666631L;
@Override
public void actionPerformed(ActionEvent e) {
cursorUp();
}
};
Action shiftRightAction = new AbstractAction() {
private static final long serialVersionUID = 1042888914300385041L;
@Override
public void actionPerformed(ActionEvent e) {
shiftCursorRight();
}
};
Action shiftLeftAction = new AbstractAction() {
private static final long serialVersionUID = -4970441655277214171L;
@Override
public void actionPerformed(ActionEvent e) {
shiftCursorLeft();
}
};
Action shiftDownAction = new AbstractAction() {
private static final long serialVersionUID = -7898502951812125984L;
@Override
public void actionPerformed(ActionEvent e) {
shiftCursorDown();
}
};
Action shiftUpAction = new AbstractAction() {
private static final long serialVersionUID = 6937621527147666631L;
@Override
public void actionPerformed(ActionEvent e) {
shiftCursorUp();
}
};
Action incCoarseAction = new AbstractAction() {
private static final long serialVersionUID = -8308522736529183148L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().incrementCoarse();
}
};
Action decCoarseAction = new AbstractAction() {
private static final long serialVersionUID = -7407628920997400915L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().decrementCoarse();
}
};
Action incFineAction = new AbstractAction() {
private static final long serialVersionUID = 7261463425941761433L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().incrementFine();
}
};
Action decFineAction = new AbstractAction() {
private static final long serialVersionUID = 8929400237520608035L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().decrementFine();
}
};
Action num0Action = new AbstractAction() {
private static final long serialVersionUID = -6310984176739090034L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('0');
}
};
Action num1Action = new AbstractAction() {
private static final long serialVersionUID = -6187220355403883499L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('1');
}
};
Action num2Action = new AbstractAction() {
private static final long serialVersionUID = -8745505977907325720L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('2');
}
};
Action num3Action = new AbstractAction() {
private static final long serialVersionUID = 4694872385823448942L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('3');
}
};
Action num4Action = new AbstractAction() {
private static final long serialVersionUID = 4005741329254221678L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('4');
}
};
Action num5Action = new AbstractAction() {
private static final long serialVersionUID = -5846094949106279884L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('5');
}
};
Action num6Action = new AbstractAction() {
private static final long serialVersionUID = -5338656374925334150L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('6');
}
};
Action num7Action = new AbstractAction() {
private static final long serialVersionUID = 1959983381590509303L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('7');
}
};
Action num8Action = new AbstractAction() {
private static final long serialVersionUID = 7442763278699460648L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('8');
}
};
Action num9Action = new AbstractAction() {
private static final long serialVersionUID = 7475171864584215094L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('9');
}
};
Action numPointAction = new AbstractAction() {
private static final long serialVersionUID = -4729135055857591830L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('.');
}
};
Action copyAction = new AbstractAction() {
private static final long serialVersionUID = -6978981449261938672L;
@Override
public void actionPerformed(ActionEvent e) {
copySelection();
}
};
Action pasteAction = new AbstractAction() {
private static final long serialVersionUID = 2026817603236490899L;
@Override
public void actionPerformed(ActionEvent e) {
paste();
}
};
Action interpolate = new AbstractAction() {
private static final long serialVersionUID = -2357532575392447149L;
@Override
public void actionPerformed(ActionEvent e) {
interpolate();
}
};
Action verticalInterpolate = new AbstractAction() {
private static final long serialVersionUID = -2375322575392447149L;
@Override
public void actionPerformed(ActionEvent e) {
verticalInterpolate();
}
};
Action horizontalInterpolate = new AbstractAction() {
private static final long serialVersionUID = -6346750245035640773L;
@Override
public void actionPerformed(ActionEvent e) {
horizontalInterpolate();
}
};
Action multiplyAction = new AbstractAction() {
private static final long serialVersionUID = -2753212575392447149L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().multiply();
}
};
Action numNegAction = new AbstractAction() {
private static final long serialVersionUID = -7532750245035640773L;
@Override
public void actionPerformed(ActionEvent e) {
getToolbar().focusSetValue('-');
}
};
// set input mapping
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
KeyStroke right = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
KeyStroke left = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
KeyStroke up = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
KeyStroke down = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
KeyStroke shiftRight = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK);
KeyStroke shiftLeft = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK);
KeyStroke shiftUp = KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK);
KeyStroke shiftDown = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK);
KeyStroke decrement = KeyStroke.getKeyStroke('-');
KeyStroke increment = KeyStroke.getKeyStroke('+');
KeyStroke decrement2 = KeyStroke.getKeyStroke("control DOWN");
KeyStroke increment2 = KeyStroke.getKeyStroke("control UP");
KeyStroke decrement3 = KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_DOWN_MASK);
KeyStroke increment3 = KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_DOWN_MASK);
KeyStroke decrement4 = KeyStroke.getKeyStroke("control shift DOWN");
KeyStroke increment4 = KeyStroke.getKeyStroke("control shift UP");
KeyStroke num0 = KeyStroke.getKeyStroke('0');
KeyStroke num1 = KeyStroke.getKeyStroke('1');
KeyStroke num2 = KeyStroke.getKeyStroke('2');
KeyStroke num3 = KeyStroke.getKeyStroke('3');
KeyStroke num4 = KeyStroke.getKeyStroke('4');
KeyStroke num5 = KeyStroke.getKeyStroke('5');
KeyStroke num6 = KeyStroke.getKeyStroke('6');
KeyStroke num7 = KeyStroke.getKeyStroke('7');
KeyStroke num8 = KeyStroke.getKeyStroke('8');
KeyStroke num9 = KeyStroke.getKeyStroke('9');
KeyStroke mulKey = KeyStroke.getKeyStroke('*');
KeyStroke mulKeys = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK);
KeyStroke numPoint = KeyStroke.getKeyStroke('.');
KeyStroke copy = KeyStroke.getKeyStroke("control C");
KeyStroke paste = KeyStroke.getKeyStroke("control V");
KeyStroke interp = KeyStroke.getKeyStroke("shift I");
KeyStroke vinterp = KeyStroke.getKeyStroke("shift V");
KeyStroke hinterp = KeyStroke.getKeyStroke("shift H");
KeyStroke numNeg = KeyStroke.getKeyStroke('-');
im.put(right, "right");
im.put(left, "left");
im.put(up, "up");
im.put(down, "down");
im.put(shiftRight, "shiftRight");
im.put(shiftLeft, "shiftLeft");
im.put(shiftUp, "shiftUp");
im.put(shiftDown, "shiftDown");
im.put(increment, "incCoarseAction");
im.put(decrement, "decCoarseAction");
im.put(increment2, "incCoarseAction");
im.put(decrement2, "decCoarseAction");
im.put(increment3, "incFineAction");
im.put(decrement3, "decFineAction");
im.put(increment4, "incFineAction");
im.put(decrement4, "decFineAction");
im.put(num0, "num0Action");
im.put(num1, "num1Action");
im.put(num2, "num2Action");
im.put(num3, "num3Action");
im.put(num4, "num4Action");
im.put(num5, "num5Action");
im.put(num6, "num6Action");
im.put(num7, "num7Action");
im.put(num8, "num8Action");
im.put(num9, "num9Action");
im.put(numPoint, "numPointAction");
im.put(copy, "copyAction");
im.put(paste, "pasteAction");
im.put(interp, "interpolate");
im.put(vinterp, "verticalInterpolate");
im.put(hinterp, "horizontalInterpolate");
im.put(mulKey, "mulAction");
im.put(mulKeys, "mulAction");
im.put(numNeg, "numNeg");
getActionMap().put(im.get(right), rightAction);
getActionMap().put(im.get(left), leftAction);
getActionMap().put(im.get(up), upAction);
getActionMap().put(im.get(down), downAction);
getActionMap().put(im.get(shiftRight), shiftRightAction);
getActionMap().put(im.get(shiftLeft), shiftLeftAction);
getActionMap().put(im.get(shiftUp), shiftUpAction);
getActionMap().put(im.get(shiftDown), shiftDownAction);
getActionMap().put(im.get(increment), incCoarseAction);
getActionMap().put(im.get(decrement), decCoarseAction);
getActionMap().put(im.get(increment2), incCoarseAction);
getActionMap().put(im.get(decrement2), decCoarseAction);
getActionMap().put(im.get(increment3), incFineAction);
getActionMap().put(im.get(decrement3), decFineAction);
getActionMap().put(im.get(increment4), incFineAction);
getActionMap().put(im.get(decrement4), decFineAction);
getActionMap().put(im.get(num0), num0Action);
getActionMap().put(im.get(num1), num1Action);
getActionMap().put(im.get(num2), num2Action);
getActionMap().put(im.get(num3), num3Action);
getActionMap().put(im.get(num4), num4Action);
getActionMap().put(im.get(num5), num5Action);
getActionMap().put(im.get(num6), num6Action);
getActionMap().put(im.get(num7), num7Action);
getActionMap().put(im.get(num8), num8Action);
getActionMap().put(im.get(num9), num9Action);
getActionMap().put(im.get(numPoint), numPointAction);
getActionMap().put(im.get(mulKey), multiplyAction);
getActionMap().put(im.get(mulKeys), multiplyAction);
getActionMap().put(im.get(copy), copyAction);
getActionMap().put(im.get(paste), pasteAction);
getActionMap().put(im.get(interp), interpolate);
getActionMap().put(im.get(vinterp), verticalInterpolate);
getActionMap().put(im.get(hinterp), horizontalInterpolate);
getActionMap().put(im.get(numNeg), numNegAction);
this.setInputMap(WHEN_FOCUSED, im);
}
public DataCell[] getData() {
return data;
}
public void setData(DataCell[] data) {
this.data = data;
}
public void populateTable(byte[] input, int romRamOffset) throws ArrayIndexOutOfBoundsException, IndexOutOfBoundsException {
// temporarily remove lock
boolean tempLock = locked;
locked = false;
if (!beforeRam) {
this.ramOffset = romRamOffset;
}
for (int i = 0; i < data.length; i++) {
if (data[i] == null) {
double dataValue = 0.0;
// populate data cells
if (storageType == Settings.STORAGE_TYPE_FLOAT) { //float storage type
byte[] byteValue = new byte[4];
byteValue[0] = input[getStorageAddress() + i * 4 - ramOffset];
byteValue[1] = input[getStorageAddress() + i * 4 - ramOffset + 1];
byteValue[2] = input[getStorageAddress() + i * 4 - ramOffset + 2];
byteValue[3] = input[getStorageAddress() + i * 4 - ramOffset + 3];
dataValue = RomAttributeParser.byteToFloat(byteValue, endian, memModelEndian);
} else if (storageType == Settings.STORAGE_TYPE_MOVI20 ||
storageType == Settings.STORAGE_TYPE_MOVI20S) { // when data is in MOVI20 instruction
dataValue = RomAttributeParser.parseByteValue(input,
endian,
getStorageAddress() + i * 3 - ramOffset,
storageType,
signed);
} else { // integer storage type
dataValue = RomAttributeParser.parseByteValue(input,
endian,
getStorageAddress() + i * storageType - ramOffset,
storageType,
signed);
}
data[i] = new DataCell(this, dataValue, 0, i);
data[i].setPreferredSize(new Dimension(cellWidth, cellHeight));
centerPanel.add(data[i]);
// show locked cell
if (tempLock) {
data[i].setForeground(Color.GRAY);
}
}
}
// reset locked status
locked = tempLock;
calcCellRanges();
}
public int getType() {
return type;
}
public DataCell getDataCell(int location) {
return data[location];
}
public void setType(int type) {
this.type = type;
}
@Override
public String getName() {
if(null == name || name.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append(Settings.DEFAULT_TABLE_NAME);
if(0 != this.getStorageAddress()) {
sb.append(" ("+this.getStorageAddress() + ")");
}
if(null != this.getLogParam() && !this.getLogParam().isEmpty()) {
sb.append(" - " + this.getLogParam());
}
return sb.toString();
}
return name;
}
@Override
public void setName(String name) {
if(null != name && !name.isEmpty()) {
this.name = name;
}
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Scale getCurrentScale() {
return this.curScale;
}
public Scale getScale(String scaleName) throws NameNotFoundException {
for (Scale scale : scales) {
if (scale.getName().equalsIgnoreCase(scaleName)) {
return scale;
}
}
return new Scale();
}
public Vector<Scale> getScales() {
return scales;
}
public void addScale(Scale scale) {
// look for scale, replace or add new
for (int i = 0; i < scales.size(); i++) {
if (scales.get(i).getName().equalsIgnoreCase(scale.getName())) {
scales.remove(i);
break;
}
}
scales.add(scale);
if(null == curScale) {
this.curScale = scale;
}
if(SettingsManager.getSettings().getDefaultScale().equalsIgnoreCase(scale.getName())) {
this.curScale = scale;
}
validateScaling();
}
public int getStorageAddress() {
return storageAddress;
}
public void setStorageAddress(int storageAddress) {
this.storageAddress = storageAddress;
}
public int getStorageType() {
return storageType;
}
public void setStorageType(int storageType) {
this.storageType = storageType;
calcValueRange();
}
public boolean isSignedData() {
return signed;
}
public void setSignedData(boolean signed) {
this.signed = signed;
}
public int getEndian() {
return endian;
}
public void setEndian(int endian) {
this.endian = endian;
}
public void setDataSize(int size) {
data = new DataCell[size];
}
public int getDataSize() {
return data.length;
}
public boolean getFlip() {
return flip;
}
public void setFlip(boolean flip) {
this.flip = flip;
}
public void setLogParam(String logParam) {
this.logParam = logParam;
}
public String getLogParam() {
return logParam;
}
@Override
public String toString() {
/*String output = "\n ---- Table " + name + " ----" +
scale +
"\n Category: " + category +
"\n Type: " + type +
"\n Description: " + description +
"\n Storage Address: " + Integer.toHexString(storageAddress) +
"\n Storage Type: " + storageType +
"\n Endian: " + endian +
"\n Flip: " + flip +
"\n ---- End Table " + name + " ----";
for (int i = 0; i < data.length; i++) {
if (data[i] != null) {
output = output + "\nData: " + data[i];
}
}
return output;*/
return getName();
}
@Override
public boolean equals(Object other) {
try {
if(null == other) {
return false;
}
if(other == this) {
return true;
}
if(!(other instanceof Table)) {
return false;
}
Table otherTable = (Table)other;
if( (null == this.getName() && null == otherTable.getName())
|| (this.getName().isEmpty() && otherTable.getName().isEmpty()) ) {
;// Skip name compare if name is null or empty.
} else {
if(!this.getName().equalsIgnoreCase(otherTable.getName())) {
return false;
}
}
if(this.data.length != otherTable.data.length)
{
return false;
}
if(this.data.equals(otherTable.data))
{
return true;
}
// Compare Bin Values
for(int i=0 ; i < this.data.length ; i++) {
if(! this.data[i].equals(otherTable.data[i])) {
return false;
}
}
return true;
} catch(Exception ex) {
// TODO: Log Exception.
return false;
}
}
public double getMaxAllowedBin() {
return maxAllowedBin;
}
public double getMinAllowedBin() {
return minAllowedBin;
}
public double getMaxAllowedReal() {
return JEPUtil.evaluate(getCurrentScale().getExpression(), getMaxAllowedBin());
}
public double getMinAllowedReal() {
return JEPUtil.evaluate(getCurrentScale().getExpression(), getMinAllowedBin());
}
private void calcValueRange() {
if (getStorageType() != Settings.STORAGE_TYPE_FLOAT) {
if (isSignedData()) {
switch (getStorageType()) {
case 1:
minAllowedBin = Byte.MIN_VALUE;
maxAllowedBin = Byte.MAX_VALUE;
break;
case 2:
minAllowedBin = Short.MIN_VALUE;
maxAllowedBin = Short.MAX_VALUE;
break;
case 4:
minAllowedBin = Integer.MIN_VALUE;
maxAllowedBin = Integer.MAX_VALUE;
break;
case Settings.STORAGE_TYPE_MOVI20:
minAllowedBin = Settings.MOVI20_MIN_VALUE;
maxAllowedBin = Settings.MOVI20_MAX_VALUE;
break;
case Settings.STORAGE_TYPE_MOVI20S:
minAllowedBin = Settings.MOVI20S_MIN_VALUE;
maxAllowedBin = Settings.MOVI20S_MAX_VALUE;
break;
}
}
else {
maxAllowedBin = (Math.pow(256, getStorageType()) - 1);
minAllowedBin = 0.0;
}
} else {
maxAllowedBin = Float.MAX_VALUE;
if(isSignedData()) {
minAllowedBin = 0.0;
} else {
minAllowedBin = -Float.MAX_VALUE;
}
}
}
public void calcCellRanges() {
double binMax = data[0].getBinValue();
double binMin = data[0].getBinValue();
double compareMax = data[0].getCompareValue();
double compareMin = data[0].getCompareValue();
for(DataCell cell : data) {
// Calc bin
if(binMax < cell.getBinValue()) {
binMax = cell.getBinValue();
}
if(binMin > cell.getBinValue()) {
binMin = cell.getBinValue();
}
// Calc compare
double compareValue = cell.getCompareValue();
if(compareMax < compareValue) {
compareMax = compareValue;
}
if(compareMin > compareValue) {
compareMin = compareValue;
}
}
setMaxBin(binMax);
setMinBin(binMin);
setMaxCompare(compareMax);
setMinCompare(compareMin);
}
public double getMaxBin() {
return this.maxBin;
}
public double getMinBin() {
return this.minBin;
}
public double getMaxReal() {
double minReal = JEPUtil.evaluate(getCurrentScale().getExpression(), getMinBin());
double maxReal = JEPUtil.evaluate(getCurrentScale().getExpression(), getMaxBin());
if(minReal > maxReal) {
return minReal;
} else {
return maxReal;
}
}
public double getMinReal() {
double minReal = JEPUtil.evaluate(getCurrentScale().getExpression(), getMinBin());
double maxReal = JEPUtil.evaluate(getCurrentScale().getExpression(), getMaxBin());
if(minReal < maxReal) {
return minReal;
} else {
return maxReal;
}
}
public void setMaxBin(double maxBin) {
this.maxBin = maxBin;
}
public void setMinBin(double minBin) {
this.minBin = minBin;
}
public double getMaxCompare() {
return this.maxCompare;
}
public void setMaxCompare(double maxCompare) {
this.maxCompare = maxCompare;
}
public double getMinCompare() {
return this.minCompare;
}
public void setMinCompare(double minCompare) {
this.minCompare = minCompare;
}
public void drawTable() {
for(DataCell cell : data) {
if(null != cell) {
cell.drawCell();
}
}
}
public Dimension getFrameSize() {
int height = verticalOverhead + cellHeight;
int width = horizontalOverhead + data.length * cellWidth;
if (height < minHeight) {
height = minHeight;
}
int minWidth = isLiveDataSupported() ? minWidthOverlay : minWidthNoOverlay;
if (width < minWidth) {
width = minWidth;
}
return new Dimension(width, height);
}
public void increment(double increment) {
if (!locked && !(userLevel > getSettings().getUserLevel())) {
for (DataCell cell : data) {
if (cell.isSelected()) {
cell.increment(increment);
}
}
} else if (userLevel > getSettings().getUserLevel()) {
JOptionPane.showMessageDialog(this, "This table can only be modified by users with a userlevel of \n" +
userLevel + " or greater. Click View->User Level to change your userlevel.",
"Table cannot be modified",
JOptionPane.INFORMATION_MESSAGE);
}
}
public void multiply(double factor) {
if (!locked && !(userLevel > getSettings().getUserLevel())) {
for (DataCell cell : data) {
if (cell.isSelected()) {
cell.multiply(factor);
}
}
} else if (userLevel > getSettings().getUserLevel()) {
JOptionPane.showMessageDialog(this, "This table can only be modified by users with a userlevel of \n" +
userLevel + " or greater. Click View->User Level to change your userlevel.",
"Table cannot be modified",
JOptionPane.INFORMATION_MESSAGE);
}
}
public void setRealValue(String realValue) {
if (!locked && userLevel <= getSettings().getUserLevel()) {
for(DataCell cell : data) {
if (cell.isSelected()) {
cell.setRealValue(realValue);
}
}
} else if (userLevel > getSettings().getUserLevel()) {
JOptionPane.showMessageDialog(this, "This table can only be modified by users with a userlevel of \n" +
userLevel + " or greater. Click View->User Level to change your userlevel.",
"Table cannot be modified",
JOptionPane.INFORMATION_MESSAGE);
}
}
public void clearSelection() {
clearSelectedData();
}
public void clearSelectedData() {
for (DataCell cell : data) {
if(cell.isSelected()) {
cell.setSelected(false);
}
}
}
public void startHighlight(int x, int y) {
this.highlightY = y;
this.highlightX = x;
highlight = true;
highlight(x, y);
}
public void highlight(int x, int y) {
if (highlight) {
for (int i = 0; i < data.length; i++) {
if ((i >= highlightY && i <= y) || (i <= highlightY && i >= y)) {
data[i].setHighlighted(true);
} else {
data[i].setHighlighted(false);
}
}
}
}
public void stopHighlight() {
highlight = false;
// loop through, selected and un-highlight
for (DataCell cell : data) {
if (cell.isHighlighted()) {
cell.setHighlighted(false);
if(!cell.isSelected()) {
cell.setSelected(true);
}
}
}
}
public abstract void cursorUp();
public abstract void cursorDown();
public abstract void cursorLeft();
public abstract void cursorRight();
public abstract void shiftCursorUp();
public abstract void shiftCursorDown();
public abstract void shiftCursorLeft();
public abstract void shiftCursorRight();
public void setRevertPoint() {
for (DataCell cell : data) {
cell.setRevertPoint();
}
}
public void undoAll() {
clearLiveDataTrace();
for (DataCell cell : data) {
cell.undo();
}
}
public void undoSelected() {
clearLiveDataTrace();
for (DataCell cell : data) {
// reset current value to original value
if (cell.isSelected()) {
cell.undo();
}
}
}
public byte[] saveFile(byte[] binData) {
if (userLevel <= getSettings().getUserLevel() && (userLevel < 5 || getSettings().isSaveDebugTables()) ) {
for (int i = 0; i < data.length; i++) {
// determine output byte values
byte[] output;
if(this.isStaticDataTable() && storageType > 0) {
LOGGER.warn("Static data table: " +this.getName()+ ", storageType: "+storageType);
}
if (storageType != Settings.STORAGE_TYPE_FLOAT) {
// convert byte values
if(this.isStaticDataTable() && storageType > 0) {
try {
int parsedValue = Integer.parseInt(data[i].getStaticText());
output = RomAttributeParser.parseIntegerValue(parsedValue, endian, storageType);
} catch (NumberFormatException ex) {
LOGGER.error("Error parsing static data table value: " + data[i].getStaticText(), ex);
LOGGER.error("Validate the table definition storageType and data value.");
continue;
}
} else if(this.isStaticDataTable() && storageType < 1) {
// Do not save the value.
//LOGGER.debug("The static data table value will not be saved.");
continue;
} else {
output = RomAttributeParser.parseIntegerValue((int) data[i].getBinValue(), endian, storageType);
}
int byteLength = storageType;
if (storageType == Settings.STORAGE_TYPE_MOVI20 ||
storageType == Settings.STORAGE_TYPE_MOVI20S) { // when data is in MOVI20 instruction
byteLength = 3;
}
for (int z = 0; z < byteLength; z++) { // insert into file
binData[i * byteLength + z + getStorageAddress() - ramOffset] = output[z];
}
} else { // float
// convert byte values
output = RomAttributeParser.floatToByte((float) data[i].getBinValue(), endian, memModelEndian);
for (int z = 0; z < 4; z++) { // insert in to file
binData[i * 4 + z + getStorageAddress() - ramOffset] = output[z];
}
}
}
}
return binData;
}
public boolean isBeforeRam() {
return beforeRam;
}
public void setBeforeRam(boolean beforeRam) {
this.beforeRam = beforeRam;
}
@Override
public void addKeyListener(KeyListener listener) {
super.addKeyListener(listener);
for (DataCell cell : data) {
for (int z = 0; z < storageType; z++) {
cell.addKeyListener(listener);
}
}
}
public void selectCellAt(int y) {
if(y >= 0 && y < data.length) {
clearSelection();
data[y].setSelected(true);
highlightY = y;
ECUEditorManager.getECUEditor().getTableToolBar().updateTableToolBar(this);
}
}
public void selectCellAtWithoutClear(int y) {
if(y >= 0 && y < data.length) {
data[y].setSelected(true);
highlightY = y;
ECUEditorManager.getECUEditor().getTableToolBar().updateTableToolBar(this);
}
}
public void copySelection() {
Window ancestorWindow = SwingUtilities.getWindowAncestor(this);
if(null != ancestorWindow) {
ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
copySelectionWorker = new CopySelectionWorker(this);
copySelectionWorker.execute();
}
public StringBuffer getTableAsString() {
StringBuffer output = new StringBuffer(Settings.BLANK);
for (int i = 0; i < data.length; i++) {
if (overlayLog) {
output.append(data[i].getCellText());
}
else {
output.append(NumberUtil.stringValue(data[i].getRealValue()));
}
if (i < data.length - 1) {
output.append(Settings.TAB);
}
}
return output;
}
public void copyTable() {
Window ancestorWindow = SwingUtilities.getWindowAncestor(this);
if(null != ancestorWindow) {
ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
copyTableWorker = new CopyTableWorker(this);
copyTableWorker.execute();
}
public String getCellAsString(int index) {
return data[index].getText();
}
public void pasteValues(String[] input) {
//set real values
for (int i = 0; i < input.length; i++) {
try {
Double.parseDouble(input[i]);
data[i].setRealValue(input[i]);
} catch (NumberFormatException ex) { /* not a number, do nothing */ }
}
}
public void paste() {
// TODO: This sounds like desearialize.
if (!staticDataTable) {
StringTokenizer st = new StringTokenizer(Settings.BLANK);
try {
String input = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor);
st = new StringTokenizer(input, ST_DELIMITER);
} catch (UnsupportedFlavorException ex) { /* wrong paste type -- do nothing */
} catch (IOException ex) {
}
String pasteType = st.nextToken();
if ("[Table1D]".equalsIgnoreCase(pasteType)) { // copied entire table
int i = 0;
while (st.hasMoreTokens()) {
String currentToken = st.nextToken();
try {
if (!data[i].getText().equalsIgnoreCase(currentToken)) {
data[i].setRealValue(currentToken);
}
} catch (ArrayIndexOutOfBoundsException ex) { /* table larger than target, ignore*/ }
i++;
}
} else if ("[Selection1D]".equalsIgnoreCase(pasteType)) { // copied selection
if (data[highlightY].isSelected()) {
int i = 0;
while (st.hasMoreTokens()) {
String currentToken = st.nextToken();
try {
if (!data[highlightY + i].getText().equalsIgnoreCase(currentToken)) {
data[highlightY + i].setRealValue(currentToken);
}
} catch (ArrayIndexOutOfBoundsException ex) { /* paste larger than target, ignore */ }
i++;
}
}
}
}
}
public void verticalInterpolate() {
}
public void horizontalInterpolate() {
}
public void interpolate() {
horizontalInterpolate();
}
public double linearInterpolation(double x, double x1, double x2, double y1, double y2) {
return (x1 == x2) ? 0.0 : (y1 + (x - x1) * (y2 - y1) / (x2 - x1));
}
public void validateScaling() {
if (type != Settings.TABLE_SWITCH) {
// make sure a scale is present
if (scales.isEmpty()) {
scales.add(new Scale());
}
for(Scale scale : scales) {
double startValue = 5;
double toReal = JEPUtil.evaluate(scale.getExpression(), startValue); // convert real world value of "5"
double endValue = JEPUtil.evaluate(scale.getByteExpression(), toReal);
// if real to byte doesn't equal 5, report conflict
if (Math.abs(endValue - startValue) > .001) {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(4, 1));
panel.add(new JLabel("The real value and byte value conversion expressions for table " + getName() + " are invalid."));
panel.add(new JLabel("To real value: " + scale.getExpression()));
panel.add(new JLabel("To byte: " + scale.getByteExpression()));
JCheckBox check = new JCheckBox("Always display this message", true);
check.setHorizontalAlignment(JCheckBox.RIGHT);
panel.add(check);
check.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
getSettings().setCalcConflictWarning(((JCheckBox) e.getSource()).isSelected());
}
}
);
JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(this), panel,
"Warning", JOptionPane.ERROR_MESSAGE);
}
}
}
}
public void populateCompareValues(Table otherTable) {
if(null == otherTable) {
return;
}
DataCell[] compareData = otherTable.getData();
if(data.length != compareData.length) {
return;
}
clearLiveDataTrace();
int i = 0;
for(DataCell cell : data) {
cell.setCompareValue(compareData[i]);
i++;
}
calcCellRanges();
drawTable();
}
public void setCompareDisplay(int compareDisplay) {
this.compareDisplay = compareDisplay;
drawTable();
}
public int getCompareDisplay() {
return this.compareDisplay;
}
public void setCompareValueType(int compareValueType) {
this.compareValueType = compareValueType;
drawTable();
}
public int getCompareValueType() {
return this.compareValueType;
}
public int getUserLevel() {
return userLevel;
}
public void setUserLevel(int userLevel) {
this.userLevel = userLevel;
if (userLevel > 5) {
userLevel = 5;
} else if (userLevel < 1) {
userLevel = 1;
}
}
public void setScaleByName(String scaleName) throws NameNotFoundException {
for(Scale scale : scales) {
if(scale.getName().equalsIgnoreCase(scaleName)) {
Scale currentScale = getCurrentScale();
if(currentScale == null || !currentScale.equals(scale)) {
this.setCurrentScale(scale);
}
return;
}
}
throw new NameNotFoundException();
}
public void setCurrentScale(Scale curScale) {
this.curScale = curScale;
updateTableLabel();
drawTable();
}
public Settings getSettings()
{
return SettingsManager.getSettings();
}
public TableToolBar getToolbar()
{
return ECUEditorManager.getECUEditor().getTableToolBar();
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public void setOverlayLog(boolean overlayLog) {
this.overlayLog = overlayLog;
}
public boolean getOverlayLog()
{
return this.overlayLog;
}
public double getLiveAxisValue() {
try {
return Double.parseDouble(liveAxisValue);
} catch (NumberFormatException e) {
return 0.0;
}
}
public abstract boolean isLiveDataSupported();
public abstract boolean isButtonSelected();
public void highlightLiveData(String liveVal) {
if (getOverlayLog()) {
double liveValue = 0.0;
try {
liveValue = NumberUtil.doubleValue(liveVal);
} catch (Exception ex) {
LOGGER.error("Table - live data highlight parsing error for value: " + liveVal);
return;
}
int startIdx = data.length;
for (int i = 0; i < data.length; i++) {
double currentValue = data[i].getRealValue();
if (liveValue == currentValue) {
startIdx = i;
break;
} else if (liveValue < currentValue){
startIdx = i-1;
break;
}
}
setLiveDataIndex(startIdx);
DataCell cell = data[getLiveDataIndex()];
cell.setLiveDataTrace(true);
cell.setLiveDataTraceValue(liveVal);
getToolbar().setLiveDataValue(liveVal);
}
}
public void updateLiveDataHighlight() {
if (getOverlayLog()) {
data[getLiveDataIndex()].setLiveDataTrace(true);
}
}
public int getLiveDataIndex() {
return liveDataIndex;
}
public void setLiveDataIndex(int index) {
if (index < 0) {
index = 0;
}
if (index >= data.length) {
index = data.length - 1;
}
this.liveDataIndex = index;
}
public void clearLiveDataTrace() {
for (DataCell cell : data) {
cell.setLiveDataTrace(false);
}
}
public String getLogParamString() {
return getName()+ ":" + getLogParam();
}
public Table getCompareTable() {
return compareTable;
}
public void setCompareTable(Table compareTable) {
this.compareTable = compareTable;
}
public void updateTableLabel() {
if(null == name || name.isEmpty()) {
;// Do not update label.
} else if(null == getCurrentScale () || "0x" == getCurrentScale().getUnit()) {
// static or no scale exists.
tableLabel.setText(getName());
} else {
tableLabel.setText(getName() + " (" + getCurrentScale().getUnit() + ")");
}
}
public void colorCells() {
calcCellRanges();
drawTable();
}
public void refreshCompare() {
populateCompareValues(getCompareTable());
}
public boolean isStaticDataTable() {
return staticDataTable;
}
public void setStaticDataTable(boolean staticDataTable) {
this.staticDataTable = staticDataTable;
}
public void setMemModelEndian(int endian) {
memModelEndian = endian;
}
public int getMemModelEndian() {
return memModelEndian;
}
}
class CopySelectionWorker extends SwingWorker<Void, Void> {
Table table;
public CopySelectionWorker(Table table) {
this.table = table;
}
@Override
protected Void doInBackground() throws Exception {
// find bounds of selection
// coords[0] = x min, y min, x max, y max
String output = "[Selection1D]" + Settings.NEW_LINE;
boolean copy = false;
int[] coords = new int[2];
coords[0] = table.getDataSize();
for (int i = 0; i < table.getDataSize(); i++) {
if (table.getData()[i].isSelected()) {
if (i < coords[0]) {
coords[0] = i;
copy = true;
}
if (i > coords[1]) {
coords[1] = i;
copy = true;
}
}
}
//make a string of the selection
for (int i = coords[0]; i <= coords[1]; i++) {
if (table.getData()[i].isSelected()) {
output = output + NumberUtil.stringValue(table.getData()[i].getRealValue());
} else {
output = output + "x"; // x represents non-selected cell
}
if (i < coords[1]) {
output = output + "\t";
}
}
//copy to clipboard
if (copy) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(output), null);
}
return null;
}
@Override
public void done() {
Window ancestorWindow = SwingUtilities.getWindowAncestor(table);
if(null != ancestorWindow) {
ancestorWindow.setCursor(null);
}
table.setCursor(null);
ECUEditorManager.getECUEditor().setCursor(null);
}
}
class CopyTableWorker extends SwingWorker<Void, Void> {
Table table;
public CopyTableWorker(Table table) {
this.table = table;
}
@Override
protected Void doInBackground() throws Exception {
String tableHeader = table.getSettings().getTableHeader();
StringBuffer output = new StringBuffer(tableHeader);
output.append(table.getTableAsString());
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(String.valueOf(output)), null);
return null;
}
@Override
public void done() {
Window ancestorWindow = SwingUtilities.getWindowAncestor(table);
if(null != ancestorWindow) {
ancestorWindow.setCursor(null);
}
table.setCursor(null);
ECUEditorManager.getECUEditor().setCursor(null);
}
}