ArduinoPanelMeta.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.CardLayout;
import java.awt.Dimension;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.border.Border;
import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrObject;
import net.sourceforge.jarduino.message.ArduinoChar;
import net.sourceforge.jarduino.message.ArduinoMessage;
import net.sourceforge.jarduino.message.ArduinoNamedObject;
import net.sourceforge.jarduino.message.ArduinoNode;
import net.sourceforge.jarduino.message.ArduinoSignal;
import net.sourceforge.jarduino.message.ArduinoSystem;
import net.sourceforge.jarduino.message.ArduinoValues;
/**
* Configuration display.
*/
public class ArduinoPanelMeta {
/**
* Attributes Text.
*/
private static final String VIEW_ATTR = "Attributes";
/**
* Values Text.
*/
private static final String VIEW_VALUES = "Values";
/**
* Connections Text.
*/
private static final String VIEW_CONNECTS = "Connections";
/**
* The panel.
*/
private final JPanel thePanel;
/**
* The Node Select button.
*/
private final ArduinoScrollButton<ArduinoNode> theNode;
/**
* The Message Select button.
*/
private final ArduinoScrollButton<ArduinoMessage> theMessage;
/**
* The Signal Select button.
*/
private final ArduinoScrollButton<ArduinoSignal> theSignal;
/**
* The View button.
*/
private final JButton theViewButton;
/**
* The button Panel map.
*/
private final Map<ArduinoConfigMode, JPanel> theButtons;
/**
* The Select Panel.
*/
private final JPanel theSelect;
/**
* The Filler Panel.
*/
private final JPanel theFiller;
/**
* The View Panel.
*/
private final JPanel theView;
/**
* The Comment label.
*/
private final JLabel theComment;
/**
* The Attributes table.
*/
private final ArduinoTableAttr theAttrs;
/**
* The Values table.
*/
private final ArduinoTableValues theValues;
/**
* The NodeConnect table.
*/
private final ArduinoTableNodeConnect theNodeConnect;
/**
* The MsgConnect table.
*/
private final ArduinoTableMsgConnect theMsgConnect;
/**
* The TablePanel.
*/
private final JPanel theTablePanel;
/**
* The mode.
*/
private ArduinoConfigMode theMode;
/**
* The system.
*/
private ArduinoSystem theSystem;
/**
* The selected object.
*/
private ArduinoNamedObject theSelected;
/**
* Do we show the valuesTable?
*/
private boolean showValues;
/**
* Constructor.
* @param pFrame the frame
*/
ArduinoPanelMeta(final JFrame pFrame) {
/* Create the Select buttons */
theNode = new ArduinoScrollButton<>(pFrame);
theNode.setFormatter(ArduinoNode::getName);
theNode.onSelect(this::setSelected);
theMessage = new ArduinoScrollButton<>(pFrame);
theMessage.setFormatter(ArduinoMessage::getName);
theMessage.onSelect(this::setSelected);
theSignal = new ArduinoScrollButton<>(pFrame);
theSignal.setFormatter(ArduinoSignal::getName);
theSignal.onSelect(this::setSelected);
/* Create the mode button */
final ArduinoScrollButton<ArduinoConfigMode> myModes = new ArduinoScrollButton<>(pFrame);
for (ArduinoConfigMode myMode : ArduinoConfigMode.values()) {
myModes.add(myMode);
}
myModes.setSelectedItem(ArduinoConfigMode.SYSTEM);
myModes.onSelect(this::selectMode);
/* Create the button map */
theButtons = new EnumMap<>(ArduinoConfigMode.class);
/* Create the mode panel */
final JPanel myModePanel = new JPanel();
myModePanel.setLayout(new BoxLayout(myModePanel, BoxLayout.X_AXIS));
myModePanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
myModePanel.add(myModes.getComponent());
myModePanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
Border myBorder = BorderFactory.createTitledBorder("Mode");
myModePanel.setBorder(myBorder);
/* Create the item panel */
theSelect = new JPanel();
theSelect.setLayout(new BoxLayout(theSelect, BoxLayout.X_AXIS));
theSelect.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
theSelect.add(buildMsgPanel(ArduinoConfigMode.NODE, theNode));
theSelect.add(buildMsgPanel(ArduinoConfigMode.MESSAGE, theMessage));
theSelect.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
theSelect.add(buildMsgPanel(ArduinoConfigMode.SIGNAL, theSignal));
theSelect.add(Box.createHorizontalGlue());
myBorder = BorderFactory.createTitledBorder("Selection");
theSelect.setBorder(myBorder);
/* Create the view button */
theViewButton = new JButton(VIEW_ATTR);
theViewButton.addActionListener(e -> {
showValues = !showValues;
theViewButton.setText(getViewButtonText());
updateView();
});
/* Create the view panel */
theView = new JPanel();
theView.setLayout(new BoxLayout(theView, BoxLayout.X_AXIS));
theView.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
theView.add(theViewButton);
theView.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
myBorder = BorderFactory.createTitledBorder("View");
theView.setBorder(myBorder);
/* Create the filler panel */
theFiller = new JPanel();
theFiller.setLayout(new BoxLayout(theFiller, BoxLayout.X_AXIS));
theFiller.add(Box.createHorizontalGlue());
/* Create the selection panel */
final JPanel mySelectPanel = new JPanel();
mySelectPanel.setLayout(new BoxLayout(mySelectPanel, BoxLayout.X_AXIS));
mySelectPanel.add(myModePanel);
mySelectPanel.add(theSelect);
mySelectPanel.add(theFiller);
mySelectPanel.add(theView);
/* Create the comment field */
theComment = new JLabel();
myBorder = BorderFactory.createTitledBorder("Comment");
theComment.setBorder(myBorder);
final JPanel myComment = new JPanel(new BorderLayout());
myComment.add(theComment);
/* Create the header panel */
final JPanel myHeader = new JPanel();
myHeader.setLayout(new BoxLayout(myHeader, BoxLayout.Y_AXIS));
myHeader.add(mySelectPanel);
myHeader.add(new JSeparator());
myHeader.add(myComment);
/* Create the attributes table */
theAttrs = new ArduinoTableAttr();
final JScrollPane myAttrScroll = new JScrollPane(theAttrs.getComponent());
myBorder = BorderFactory.createTitledBorder(VIEW_ATTR);
myAttrScroll.setBorder(myBorder);
/* Create the values table */
theValues = new ArduinoTableValues();
final JScrollPane myValueScroll = new JScrollPane(theValues.getComponent());
myBorder = BorderFactory.createTitledBorder(VIEW_VALUES);
myValueScroll.setBorder(myBorder);
/* Create the nodeConnect table */
theNodeConnect = new ArduinoTableNodeConnect();
myBorder = BorderFactory.createTitledBorder(VIEW_CONNECTS);
theNodeConnect.getComponent().setBorder(myBorder);
/* Create the values table */
theMsgConnect = new ArduinoTableMsgConnect();
myBorder = BorderFactory.createTitledBorder(VIEW_CONNECTS);
theMsgConnect.getComponent().setBorder(myBorder);
/* Create the table card */
theTablePanel = new JPanel(new CardLayout());
theTablePanel.add(myAttrScroll, VIEW_ATTR);
theTablePanel.add(myValueScroll, VIEW_VALUES);
theTablePanel.add(theNodeConnect.getComponent(), VIEW_CONNECTS + ArduinoConfigMode.NODE.toString());
theTablePanel.add(theMsgConnect.getComponent(), VIEW_CONNECTS + ArduinoConfigMode.MESSAGE.toString());
/* Create the panel */
thePanel = new JPanel(new BorderLayout());
thePanel.add(myHeader, BorderLayout.PAGE_START);
thePanel.add(theTablePanel, BorderLayout.CENTER);
/* Default to system */
theMode = ArduinoConfigMode.SYSTEM;
}
/**
* Obtain the component.
* @return the component
*/
JComponent getComponent() {
return thePanel;
}
/**
* Select Modes.
* @param pMode the mode
*/
private void selectMode(final ArduinoConfigMode pMode) {
/* If this is the existing mode, just return */
if (theMode == pMode) {
return;
}
/* Set visibility */
setVisibility(pMode);
/* Switch on mode */
theMode = pMode;
switch (theMode) {
case NODE:
setSelected(theNode.getSelectedItem());
break;
case MESSAGE:
setSelected(theMessage.getSelectedItem());
break;
case SIGNAL:
/* Careful if signal is selected before any message */
final ArduinoSignal mySignal = theSignal.getSelectedItem();
setSelected(mySignal == null ? theMessage.getSelectedItem() : mySignal);
break;
case SYSTEM:
default:
setSelected(theSystem);
break;
}
/* Update the view button */
theViewButton.setText(getViewButtonText());
}
/**
* Set selected Object.
* @param pObject the selected object
*/
private void setSelected(final ArduinoNamedObject pObject) {
/* Ignore if the object is irrelevant */
if (!theMode.checkObject(pObject)
|| Objects.equals(theSelected, pObject)) {
return;
}
/* If we have a message when we are in signal Mode */
if (pObject instanceof ArduinoMessage) {
/* Ignore if the object is the owner of the selected */
final ArduinoMessage myMessage = (ArduinoMessage) pObject;
if (theMode == ArduinoConfigMode.SIGNAL
&& theSelected instanceof ArduinoSignal
&& pObject.equals(((ArduinoSignal) theSelected).getOwner())) {
return;
}
/* Populate the signal list */
theSignal.removeAll();
for (ArduinoSignal mySignal : myMessage.getAllSignals()) {
/* Add to the list */
theSignal.add(mySignal);
}
/* Return if we are in signal mode */
if (theMode == ArduinoConfigMode.SIGNAL) {
return;
}
}
/* Set the details for the object */
setComment(pObject);
theAttrs.configureTable(theSystem, theMode, (ArduinoAttrObject) pObject);
theValues.getComponent().setVisible(false);
theView.setVisible(false);
if (pObject instanceof ArduinoSignal) {
final ArduinoValues myValues = ((ArduinoSignal) pObject).getValues();
if (myValues != null) {
theValues.configureTable(myValues);
theValues.getComponent().setVisible(true);
theView.setVisible(true);
}
}
if (pObject instanceof ArduinoNode) {
theNodeConnect.configureTable((ArduinoNode) pObject);
theView.setVisible(true);
}
if (pObject instanceof ArduinoMessage) {
theMsgConnect.configureTable((ArduinoMessage) pObject);
theView.setVisible(true);
}
theSelected = pObject;
updateView();
/* Set Dimensions */
thePanel.setPreferredSize(new Dimension(ArduinoPanelMain.WIDTH, ArduinoPanelMain.HEIGHT));
}
/**
* Set comment.
* @param pObject the selected object
*/
private void setComment(final ArduinoNamedObject pObject) {
/* Access the comment for the object */
String myComment = theSystem.getCommentForObject(pObject);
/* Clean the comment */
myComment = cleanseLabelText(myComment);
/* Set text and visibility */
theComment.setText(myComment);
theComment.setVisible(myComment != null && !showValues);
}
/**
* Clean label text to handle embedded new line.
* @param pText the text
* @return the cleansed text
*/
static String cleanseLabelText(final String pText) {
/* Access the text */
String myText = pText;
/* If the text includes a linefeed */
if (myText != null
&& myText.indexOf(ArduinoChar.LF) != -1) {
/* Convert to html */
myText = "<html>"
+ myText.replace("&", "&")
.replace(">", ">")
.replace("<", "<")
.replace(Character.toString(ArduinoChar.LF), "<br>");
}
/* Return the text */
return myText;
}
/**
* Create a labelled button panel.
* @param pMode the mode
* @param pButton the button
* @return the panel
*/
private JPanel buildMsgPanel(final ArduinoConfigMode pMode,
final ArduinoScrollButton<?> pButton) {
/* Create the panel */
final JPanel myPanel = new JPanel();
myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.X_AXIS));
myPanel.add(new JLabel(pMode.toString() + ArduinoChar.COLON));
myPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
myPanel.add(pButton.getComponent());
/* Store into map and return */
theButtons.put(pMode, myPanel);
return myPanel;
}
/**
* Set visibility.
* @param pMode the mode
*/
private void setVisibility(final ArduinoConfigMode pMode) {
/* Determine visibility */
final boolean bNodeVisible = pMode == ArduinoConfigMode.NODE;
final boolean bSignalVisible = pMode == ArduinoConfigMode.SIGNAL;
final boolean bMessageVisible = pMode == ArduinoConfigMode.MESSAGE || bSignalVisible;
final boolean bSelectVisible = pMode != ArduinoConfigMode.SYSTEM;
/* Update the button panels */
theButtons.get(ArduinoConfigMode.NODE).setVisible(bNodeVisible);
theButtons.get(ArduinoConfigMode.MESSAGE).setVisible(bMessageVisible);
theButtons.get(ArduinoConfigMode.SIGNAL).setVisible(bSignalVisible);
/* Update selection/filler */
theSelect.setVisible(bSelectVisible);
theFiller.setVisible(!bSelectVisible);
}
/**
* Update the view.
*/
private void updateView() {
/* Determine the card to show */
String myCard = VIEW_ATTR;
if (showValues) {
if (theSelected instanceof ArduinoNode) {
myCard = VIEW_CONNECTS + ArduinoConfigMode.NODE.toString();
} else if (theSelected instanceof ArduinoMessage) {
myCard = VIEW_CONNECTS + ArduinoConfigMode.MESSAGE.toString();
} else if (theSelected instanceof ArduinoSignal
&& ((ArduinoSignal) theSelected).getValues() != null) {
myCard = VIEW_VALUES;
}
}
/* Determine if we are showing connections */
final CardLayout myLayout = (CardLayout) theTablePanel.getLayout();
myLayout.show(theTablePanel, myCard);
}
/**
* Determine the viewButton Text.
* @return the text
*/
private String getViewButtonText() {
/* Determine the card to show */
if (showValues) {
switch (theMode) {
case NODE:
case MESSAGE:
return VIEW_CONNECTS;
case SIGNAL:
return VIEW_VALUES;
default:
break;
}
}
return VIEW_ATTR;
}
/**
* Set the system.
* @param pSystem the system
*/
void setSystem(final ArduinoSystem pSystem) {
/* Store system */
theSystem = pSystem;
/* clear the selection lists */
theSelected = null;
theNode.removeAll();
theMessage.removeAll();
theSignal.removeAll();
/* Loop through the nodes */
for (ArduinoNode myNode : theSystem.getNodes()) {
/* Add to the list (unless it is the nullNode) */
if (!myNode.getName().equals(ArduinoNode.NULL_NODE)) {
theNode.add(myNode);
}
/* Loop through the messages */
for (ArduinoMessage myMessage : myNode.getMessages()) {
/* Add to the list */
theMessage.add(myMessage);
}
}
/* Reset the mode */
final ArduinoConfigMode myMode = theMode;
theMode = null;
selectMode(myMode);
}
/**
* The modes.
*/
enum ArduinoConfigMode {
/**
* System.
*/
SYSTEM("System"),
/**
* Node.
*/
NODE("Node"),
/**
* Message.
*/
MESSAGE("Message"),
/**
* Signal.
*/
SIGNAL("Signal");
/**
* The name.
*/
private final String theName;
/**
* Constructor.
* @param pName the Name
*/
ArduinoConfigMode(final String pName) {
theName = pName;
}
/**
* Check that object is of the correct type.
* @param pObject the object
* @return true/false
*/
boolean checkObject(final ArduinoNamedObject pObject) {
switch (this) {
case NODE:
return pObject instanceof ArduinoNode;
case MESSAGE:
return pObject instanceof ArduinoMessage;
case SIGNAL:
return pObject instanceof ArduinoMessage
|| pObject instanceof ArduinoSignal;
case SYSTEM:
return pObject instanceof ArduinoSystem;
default:
return false;
}
}
@Override
public String toString() {
return theName;
}
}
}