ArduinoPanelSignal.java
/*******************************************************************************
* jArduino: Arduino C++ Code Generation From Java
* Copyright 2020 Tony Washer
*
* Licensed 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 net.sourceforge.jarduino.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import net.sourceforge.jarduino.message.ArduinoMessage;
import net.sourceforge.jarduino.message.ArduinoSystem;
import net.sourceforge.jarduino.util.ArduinoHex;
/**
* Message Signals display panel.
*/
public class ArduinoPanelSignal {
/**
* Font.
*/
static final Font FONT = new Font("monospaced", Font.BOLD, 16);
/**
* Font.
*/
static final Font FONTX = new Font("SansSerif", Font.BOLD, 16);
/**
* The Panel.
*/
private final JPanel thePanel;
/**
* The Message table.
*/
private final ArduinoTableSignal theTable;
/**
* The select button.
*/
private final ArduinoScrollButton<ArduinoMessage> theSelect;
/**
* The Hex Data.
*/
private final JLabel theHex;
/**
* The MsgId.
*/
private final JLabel theMsgId;
/**
* The Binary Data.
*/
private final BinaryData theBinary;
/**
* The message data.
*/
private byte[] theData;
/**
* Constructor.
* @param pFrame the frame
*/
ArduinoPanelSignal(final JFrame pFrame) {
/* Create scroll button */
theSelect = new ArduinoScrollButton<>(pFrame);
theSelect.setFormatter(ArduinoMessage::getName);
theSelect.onSelect(this::selectMessage);
/* Create Panel for messageId */
theMsgId = new JLabel();
theMsgId.setFont(FONTX);
final JLabel myIdPrompt = new JLabel("MessageId:");
myIdPrompt.setFont(FONTX);
final JPanel myIdPanel = new JPanel();
myIdPanel.setLayout(new BoxLayout(myIdPanel, BoxLayout.X_AXIS));
myIdPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
myIdPanel.add(myIdPrompt);
myIdPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
myIdPanel.add(theMsgId);
myIdPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
/* Create Panel for selection */
final JPanel mySelectPanel = new JPanel();
mySelectPanel.setLayout(new BoxLayout(mySelectPanel, BoxLayout.X_AXIS));
mySelectPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
mySelectPanel.add(theSelect.getComponent());
mySelectPanel.add(Box.createHorizontalGlue());
mySelectPanel.add(myIdPanel);
Border myBorder = BorderFactory.createTitledBorder("Select Message");
mySelectPanel.setBorder(myBorder);
/* Create the table */
theTable = new ArduinoTableSignal(this);
/* Create Panel for Data */
final JPanel myDataPanel = new JPanel(new GridBagLayout());
final GridBagConstraints myC = new GridBagConstraints();
myBorder = BorderFactory.createTitledBorder("Message Data");
myDataPanel.setBorder(myBorder);
/* Add hexData */
myC.gridx = 0;
myC.gridy = 0;
myC.anchor = GridBagConstraints.LINE_END;
myC.weightx = 0.0;
myC.fill = GridBagConstraints.NONE;
myDataPanel.add(new JLabel("Hex Data: "), myC);
myC.gridx = 1;
myC.gridy = 0;
myC.anchor = GridBagConstraints.CENTER;
myC.weightx = 1.0;
myC.fill = GridBagConstraints.HORIZONTAL;
theHex = new JLabel();
theHex.setFont(FONT);
myDataPanel.add(theHex, myC);
/* Add binaryData */
myC.gridx = 0;
myC.gridy = 1;
myC.anchor = GridBagConstraints.LINE_END;
myC.weightx = 0.0;
myC.fill = GridBagConstraints.NONE;
myDataPanel.add(new JLabel("Binary Data: "), myC);
myC.gridx = 1;
myC.gridy = 1;
myC.anchor = GridBagConstraints.CENTER;
myC.weightx = 1.0;
myC.fill = GridBagConstraints.HORIZONTAL;
theBinary = new BinaryData();
myDataPanel.add(theBinary.getComponent(), myC);
/* Create the message panel */
final JPanel myMsgPanel = new JPanel(new BorderLayout());
myMsgPanel.add(new JScrollPane(theTable.getComponent()), BorderLayout.CENTER);
myBorder = BorderFactory.createTitledBorder("Message Signals");
myMsgPanel.setBorder(myBorder);
/* Create the panel */
thePanel = new JPanel(new BorderLayout());
thePanel.add(mySelectPanel, BorderLayout.PAGE_START);
thePanel.add(myMsgPanel, BorderLayout.CENTER);
thePanel.add(myDataPanel, BorderLayout.PAGE_END);
/* Set Dimensions */
thePanel.setPreferredSize(new Dimension(ArduinoPanelMain.WIDTH, ArduinoPanelMain.HEIGHT));
}
/**
* Obtain the component.
* @return the component
*/
JComponent getComponent() {
return thePanel;
}
/**
* Obtain the data.
* @return the data
*/
byte[] getData() {
return theData;
}
/**
* Set the system.
* @param pSystem the system
*/
void setSystem(final ArduinoSystem pSystem) {
/* Build the new message selection list */
theSelect.removeAll();
for (ArduinoMessage myMessage : pSystem.getMessages()) {
theSelect.add(myMessage);
}
}
/**
* Select the message.
* @param pMessage the message
*/
private void selectMessage(final ArduinoMessage pMessage) {
/* Allocate the data */
theData = new byte[pMessage.getLength()];
/* Configure the table */
theTable.configureTable(pMessage);
/* Update the hex and binary */
updateHexAndBinary();
updateBitColors();
updateMessageId(pMessage);
}
/**
* Update messageId.
* @param pMessage the message
*/
void updateMessageId(final ArduinoMessage pMessage) {
final int myNum = Integer.parseUnsignedInt(pMessage.getId());
theMsgId.setText(pMessage.getId() + String.format(" 0x%08X", myNum));
}
/**
* Update hex and binary.
*/
void updateHexAndBinary() {
updateHexData();
theBinary.refreshData();
}
/**
* Update Hex Data.
*/
private void updateHexData() {
theHex.setText(ArduinoHex.bytesToHexString(theData));
}
/**
* Update BitColors.
*/
void updateBitColors() {
/* Reset the colours */
theBinary.resetColors();
/* Loop through the rows */
final int myNumRows = theTable.getNumRows();
for (int i = 0; i < myNumRows; i++) {
/* Obtain the colour for the signal */
final Color myColor = theTable.getColorForRow(i);
/* If the signal is active */
if (myColor != null) {
/* Access the bitMap for the row */
final byte[] myBitMap = theTable.getBitMapForRow(i);
/* Update the colours */
theBinary.updateColor(myColor, myBitMap);
}
}
}
/**
* The binaryData panel.
*/
private class BinaryData {
/**
* The Panel.
*/
private final JPanel thePanel;
/**
* The Binary Bit Panels.
*/
private final BinaryBit[] theBits;
/**
* Constructor.
*/
BinaryData() {
/* Create the panel */
thePanel = new JPanel();
thePanel.setLayout(new BoxLayout(thePanel, BoxLayout.X_AXIS));
/* Create the bit label array */
theBits = new BinaryBit[Long.SIZE];
/* Create the bit panels */
for (int i = 0; i < Long.SIZE; i++) {
/* Create the binary bit */
final BinaryBit myBit = new BinaryBit(i);
thePanel.add(myBit.getComponent());
theBits[i] = myBit;
/* If we are on a byte boundary */
if ((i + 1) % Byte.SIZE == 0) {
final JLabel mySpacer = new JLabel();
mySpacer.setText(" ");
thePanel.add(mySpacer);
}
}
}
/**
* Obtain the component.
* @return the component
*/
JComponent getComponent() {
return thePanel;
}
/**
* Refresh the data.
*/
void refreshData() {
/* determine the maxBit */
final int maxBit = theData.length * Byte.SIZE;
/* Loop through the bits */
for (int i = 0; i < Long.SIZE; i++) {
final boolean visible = i < maxBit;
final BinaryBit myBit = theBits[i];
myBit.refreshData(visible);
}
}
/**
* Reset colors.
*/
void resetColors() {
/* determine the maxBit */
final int maxBit = theData.length * Byte.SIZE;
/* Loop through the bytes */
for (int bitNo = 0; bitNo < maxBit; bitNo++) {
/* Set the colour */
theBits[bitNo].setColor(thePanel.getBackground());
}
}
/**
* Update color.
* @param pColour the colour
* @param pBitMap the bitMap
*/
void updateColor(final Color pColour,
final byte[] pBitMap) {
/* determine the maxBit */
final int maxBit = theData.length * Byte.SIZE;
/* Loop through the bytes */
for (int bitNo = 0; bitNo < maxBit; bitNo++) {
/* Access details */
final int myByteNo = bitNo / Byte.SIZE;
final int myShift = Byte.SIZE - (bitNo % Byte.SIZE) - 1;
final int myMask = 1 << myShift;
/* If the bit is set */
if ((pBitMap[myByteNo] & myMask) != 0) {
/* Set the colour */
theBits[bitNo].setColor(pColour);
}
}
}
}
/**
* The binaryBit panel.
*/
private class BinaryBit {
/**
* The index.
*/
private final int theIndex;
/**
* The Panel.
*/
private final JLabel theLabel;
/**
* Constructor.
* @param pIndex the index
*/
BinaryBit(final int pIndex) {
/* Store parameter */
theIndex = pIndex;
/* Create the label */
theLabel = new JLabel();
theLabel.setFont(FONT);
theLabel.setHorizontalAlignment(SwingConstants.CENTER);
theLabel.setOpaque(true);
/* Add the mouse adapter */
theLabel.addMouseListener(new LabelMouse());
}
/**
* Obtain the component.
* @return the component
*/
JComponent getComponent() {
return theLabel;
}
/**
* Set color.
* @param pColour the colour
*/
void setColor(final Color pColour) {
theLabel.setForeground(Color.BLACK);
theLabel.setBackground(pColour);
}
/**
* Refresh the data.
* @param pVisible is the bit visible?
*/
void refreshData(final boolean pVisible) {
theLabel.setVisible(pVisible);
if (pVisible) {
showBitFromData();
}
}
/**
* Display the bit value.
*/
void showBitFromData() {
final int myByteNo = theIndex / Byte.SIZE;
final int myShift = Byte.SIZE - (theIndex % Byte.SIZE) - 1;
final int myMask = 1 << myShift;
theLabel.setText((theData[myByteNo] & myMask) != 0 ? "1" : "0");
}
/**
* Toggle bit value.
*/
void toggleBitInData() {
/* Toggle the bit */
final int myByteNo = theIndex / Byte.SIZE;
final int myShift = Byte.SIZE - (theIndex % Byte.SIZE) - 1;
final int myMask = 1 << myShift;
theData[myByteNo] ^= myMask;
theLabel.setText((theData[myByteNo] & myMask) != 0 ? "1" : "0");
refreshOnToggle();
}
/**
* Refresh On Toggle.
*/
private void refreshOnToggle() {
theTable.loadValues();
updateHexData();
updateBitColors();
}
/**
* LabelMouse.
*/
private final class LabelMouse
extends MouseAdapter {
@Override
public void mouseClicked(final MouseEvent e) {
toggleBitInData();
}
}
}
}