/* * RomRaider Open-Source Tuning, Logging and Reflashing * Copyright (C) 2006-2016 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 static com.romraider.util.ColorScaler.getScaledColor; import static com.romraider.util.ParamChecker.isNullOrEmpty; import static javax.swing.BorderFactory.createLineBorder; import java.awt.Color; import java.awt.Font; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.Serializable; import java.text.DecimalFormat; import java.text.ParseException; import java.util.StringTokenizer; import javax.swing.JLabel; import javax.swing.border.Border; import org.apache.log4j.Logger; import com.romraider.Settings; import com.romraider.editor.ecu.ECUEditorManager; import com.romraider.util.JEPUtil; import com.romraider.util.NumberUtil; import com.romraider.util.SettingsManager; public class DataCell extends JLabel implements MouseListener, Serializable { private static final long serialVersionUID = -2904293227148940937L; private static final Logger LOGGER = Logger.getLogger(DataCell.class); private static final Font DEFAULT_FONT = new Font("Arial", Font.BOLD, 12); private static final String ST_DELIMITER = "\t\n\r\f"; private static final DecimalFormat FORMATTER = new DecimalFormat(); private static final String PERCENT_FORMAT = "#,##0.0%"; private static final String TT_FORMAT = "#,##0.##########"; private static final String TT_PERCENT_FORMAT = "#,##0.0#########%"; private static final String REPLACE_TEXT = "\u0020|\u00a0"; private static int UNSELECT_MASK1 = MouseEvent.BUTTON1_DOWN_MASK + MouseEvent.CTRL_DOWN_MASK + MouseEvent.ALT_DOWN_MASK; private static int UNSELECT_MASK2 = MouseEvent.BUTTON3_DOWN_MASK + MouseEvent.CTRL_DOWN_MASK + MouseEvent.ALT_DOWN_MASK; private final Table table; private boolean selected = false; private boolean highlighted = false; private boolean traced = false; private int x = 0; private int y = 0; private double binValue = 0.0; private double originalValue = 0.0; private double compareToValue = 0.0; private String liveValue = Settings.BLANK; private static final Color DEFAULT_BORDER_COLOR = new Color(0, 0, 0); private final Color increaseBorderColor = getSettings().getIncreaseBorder(); private final Color decreaseBorderColor = getSettings().getDecreaseBorder(); private String staticText = null; public DataCell(Table table) { this.table = table; this.setHorizontalAlignment(CENTER); this.setVerticalAlignment(CENTER); this.setFont(DEFAULT_FONT); this.setOpaque(true); this.setVisible(true); this.addMouseListener(this); } public DataCell(Table table, String staticText) { this(table); final StringTokenizer st = new StringTokenizer(staticText, ST_DELIMITER); if (st.hasMoreTokens()) { this.staticText = st.nextToken(); } table.setStaticDataTable(true); } public DataCell(Table table, double originalValue, int x, int y) { this(table); this.originalValue = originalValue; this.binValue = originalValue; this.x = x; this.y = y; this.setPreferredSize(getSettings().getCellSize()); } public double getBinValue() { return binValue; } public double getRealValue() { return JEPUtil.evaluate(table.getCurrentScale().getExpression(), binValue); } public void setRealValue(String input) { // create parser input = input.replaceAll(REPLACE_TEXT, Settings.BLANK); try { double result = 0.0; if (!"x".equalsIgnoreCase(input)) { result = JEPUtil.evaluate(table.getCurrentScale().getByteExpression(), NumberUtil.doubleValue(input)); if (table.getStorageType() != Settings.STORAGE_TYPE_FLOAT) { result = (int) Math.round(result); } if(binValue != result) { this.setBinValue(result); } } } catch (ParseException e) { // Do nothing. input is null or not a valid number. } } public double getCompareValue() { return binValue - compareToValue; } public double getRealCompareValue() { return JEPUtil.evaluate(table.getCurrentScale().getExpression(), binValue) - JEPUtil.evaluate(table.getCurrentScale().getExpression(), compareToValue); } public double getRealCompareChangeValue() { double realBinValue = JEPUtil.evaluate(table.getCurrentScale().getExpression(), binValue); double realCompareValue = JEPUtil.evaluate(table.getCurrentScale().getExpression(), compareToValue); if(realCompareValue != 0.0) { // Compare change formula ((V2 - V1) / |V1|). return ((realBinValue - realCompareValue) / Math.abs(realCompareValue)); } else { // Use this to avoid divide by 0 or infinite increase. return realBinValue - realCompareValue; } } public Color getCompareColor() { if(table instanceof Table1D) { Table1D checkTable = (Table1D)table; if(checkTable.isAxis() && !getSettings().isColorAxis()) { return getSettings().getAxisColor(); } } double compareScale; if (0.0 == getCompareValue()) { return Settings.UNCHANGED_VALUE_COLOR; }else if(table.getMinCompare() == table.getMaxCompare()) { return getSettings().getMaxColor(); } else { compareScale = (getCompareValue() - table.getMinCompare()) / (table.getMaxCompare() - table.getMinCompare()); } return getScaledColor(compareScale); } public Color getBinColor() { if(table instanceof Table1D) { Table1D checkTable = (Table1D)table; if(checkTable.isAxis() && !getSettings().isColorAxis()) { return getSettings().getAxisColor(); } } if (table.getMaxAllowedBin() < getBinValue()) { return getSettings().getWarningColor(); } else if (table.getMinAllowedBin() > getBinValue()) { return getSettings().getWarningColor(); } else { // limits not set, scale based on table values double colorScale; if (table.getMaxBin() - table.getMinBin() == 0.0) { // if all values are the same, color will be middle value colorScale = .5; } else { colorScale = (getRealValue() - table.getMinReal()) / (table.getMaxReal() - table.getMinReal()); } return getScaledColor(colorScale); } } public void drawCell() { if(table == null) { // Table will be null in the static case. return; } this.invalidate(); setFont(getSettings().getTableFont()); setText(getCellText()); setToolTipText(getCellToolTip()); setBackground(getCellBackgroundColor()); setForeground(getCellTextColor()); setBorder(getCellBorder()); this.validate(); table.validate(); table.repaint(); } private Color getCellBackgroundColor() { Settings settings = getSettings(); Color backgroundColor; if(highlighted) { backgroundColor = settings.getHighlightColor(); } else if(selected) { backgroundColor = settings.getSelectColor(); } else if(null == table.getCompareTable()) { backgroundColor = getBinColor(); }else { backgroundColor = getCompareColor(); } return backgroundColor; } private Color getCellTextColor() { Color textColor; if(traced) { if(!getLiveValue().isEmpty()) { if(table instanceof Table1D) { textColor = Settings.scaleTextColor; } else { textColor = Settings.liveDataTraceTextColor; } } else { textColor = Settings.scaleTextColor; } } else if (highlighted) { textColor = Settings.highlightTextColor; } else if (selected) { textColor = Settings.selectTextColor; } else { textColor = Settings.scaleTextColor; } return textColor; } private Border getCellBorder() { Border border; if(traced) { border = createLineBorder(getSettings().getliveValueColor(), 2); } else { double checkValue; if(null == table.getCompareTable()) { checkValue= originalValue; } else { checkValue = compareToValue; } if (checkValue < binValue) { border = createLineBorder(increaseBorderColor, 2); } else if (checkValue > binValue) { border = createLineBorder(decreaseBorderColor, 2); } else { border = createLineBorder(DEFAULT_BORDER_COLOR, 1); } } return border; } public String getCellText() { if(table.isStaticDataTable()) { return getStaticText(); } FORMATTER.applyPattern(table.getCurrentScale().getFormat()); String displayString = ""; if (null == table.getCompareTable()) { displayString = FORMATTER.format(getRealValue()); } else if (table.getCompareDisplay() == Settings.COMPARE_DISPLAY_ABSOLUTE) { displayString = FORMATTER.format(getRealCompareValue()); } else if (table.getCompareDisplay() == Settings.COMPARE_DISPLAY_PERCENT) { FORMATTER.applyPattern(PERCENT_FORMAT); if (getCompareValue() == 0.0) { displayString = FORMATTER.format(0.0); } else { displayString = FORMATTER.format(getRealCompareChangeValue()); } } if(traced) { if(!(table instanceof Table1D)) { displayString = getLiveValueString(displayString); } } return displayString; } private String getCellToolTip() { if(table.isStaticDataTable()) { return getStaticText(); } String ttString = null; FORMATTER.applyPattern(TT_FORMAT); if (null == table.getCompareTable()) { ttString = FORMATTER.format(getRealValue()); } else if (table.getCompareDisplay() == Settings.COMPARE_DISPLAY_ABSOLUTE) { ttString = FORMATTER.format(getRealCompareValue()); } else if (table.getCompareDisplay() == Settings.COMPARE_DISPLAY_PERCENT) { FORMATTER.applyPattern(TT_PERCENT_FORMAT); if (getCompareValue() == 0.0) { ttString = FORMATTER.format(0.0); } else { ttString = FORMATTER.format(getRealCompareChangeValue()); } } if(traced) { if(!(table instanceof Table1D)) { ttString = getLiveValueString(ttString); } } return ttString; } private String getLiveValue() { return this.liveValue; } private String getLiveValueString(String currentValue) { return currentValue + (isNullOrEmpty(getLiveValue()) ? Settings.BLANK : (':' + getLiveValue())); } public void setBinValue(double newBinValue) { if(binValue == newBinValue) { return; } double checkedValue = newBinValue; // make sure it's in range if(checkedValue < table.getMinAllowedBin()) { checkedValue = table.getMinAllowedBin(); } if(checkedValue > table.getMaxAllowedBin()) { checkedValue = table.getMaxAllowedBin(); } if(binValue == checkedValue) { return; } // set bin. binValue = checkedValue; drawCell(); } @Override public String toString() { return getCellText(); } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { if(!table.isStaticDataTable() && this.selected != selected) { this.selected = selected; drawCell(); } } public void setHighlighted(boolean highlighted) { if(!table.isStaticDataTable() && this.highlighted != highlighted) { this.highlighted = highlighted; drawCell(); } } public boolean isHighlighted() { return highlighted; } @Override public void mouseEntered(MouseEvent e) { if (UNSELECT_MASK1 == (e.getModifiersEx() & UNSELECT_MASK1)) { clearCell(); } else if (UNSELECT_MASK2 == (e.getModifiersEx() & UNSELECT_MASK2)) { clearCell(); } else { table.highlight(x, y); } } @Override public void mousePressed(MouseEvent e) { if (!e.isControlDown()) { table.clearSelection(); } if (e.isControlDown() && e.isAltDown()) { clearCell(); } else { table.startHighlight(x, y); } requestFocus(); ECUEditorManager.getECUEditor().getTableToolBar().updateTableToolBar(table); } @Override public void mouseReleased(MouseEvent e) { table.stopHighlight(); } @Override public void mouseClicked(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } private void clearCell() { if(isHighlighted()) { setHighlighted(false); } if(isSelected()) { setSelected(false); } } public void increment(double increment) { double oldValue = getRealValue(); if (table.getCurrentScale().getCoarseIncrement() < 0.0) { increment = 0.0 - increment; } double incResult = JEPUtil.evaluate(table.getCurrentScale().getByteExpression(), (oldValue + increment)); if (table.getStorageType() == Settings.STORAGE_TYPE_FLOAT) { if(binValue != incResult) { this.setBinValue(incResult); } } else { int roundResult = (int) Math.round(incResult); if(binValue != roundResult) { this.setBinValue(roundResult); } } // make sure table is incremented if change isn't great enough int maxValue = (int) Math.pow(8, table.getStorageType()); if (table.getStorageType() != Settings.STORAGE_TYPE_FLOAT && oldValue == getRealValue() && binValue > 0.0 && binValue < maxValue) { LOGGER.debug(maxValue + " " + binValue); increment(increment * 2); } } public void undo() { this.setBinValue(originalValue); } public void setRevertPoint() { this.setOriginalValue(binValue); this.drawCell(); } public void setOriginalValue(double originalValue) { this.originalValue = originalValue; } public void setCompareValue(DataCell compareCell) { if(Settings.DATA_TYPE_BIN == table.getCompareValueType()) { if(this.compareToValue == compareCell.binValue) { return; } this.compareToValue = compareCell.binValue; } else { if(this.compareToValue == compareCell.originalValue) { return; } this.compareToValue = compareCell.originalValue; } } public void multiply(double factor) { setBinValue(binValue * factor); } public void setLiveDataTrace(boolean trace) { if(traced != trace) { traced = trace; drawCell(); } } public void setLiveDataTraceValue(String liveValue) { if(this.liveValue != liveValue) { this.liveValue = liveValue; drawCell(); } } private Settings getSettings() { return SettingsManager.getSettings(); } @Override public boolean equals(Object other) { if(other == null) { return false; } if(!(other instanceof DataCell)) { return false; } DataCell otherCell = (DataCell) other; if(this.table.isStaticDataTable() != otherCell.table.isStaticDataTable()) { return false; } return binValue == otherCell.binValue; } public String getStaticText() { String displayString = null; try { FORMATTER.applyPattern(table.getCurrentScale().getFormat()); double staticDouble = NumberUtil.doubleValue(staticText); displayString = FORMATTER.format(JEPUtil.evaluate(table.getCurrentScale().getExpression(), staticDouble)); } catch (Exception ex) { displayString = this.staticText; } return displayString; } public void setY(int y) { this.y = y; } }