ArduinoTableFilter.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.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.SwingConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;
import net.sourceforge.jarduino.message.ArduinoMessage;
import net.sourceforge.jarduino.message.ArduinoMsgFilter;
import net.sourceforge.jarduino.message.ArduinoNamedObject;
import net.sourceforge.jarduino.message.ArduinoSystem;
import net.sourceforge.jarduino.util.ArduinoLogManager;
import net.sourceforge.jarduino.util.ArduinoLogger;
/**
* Arduino Message Table.
*/
public class ArduinoTableFilter {
/**
* The LOGGER.
*/
private static final ArduinoLogger LOGGER = ArduinoLogManager.getLogger(ArduinoTableFilter.class);
/**
* The tick icon.
*/
private static final Icon TICKICON = loadIcon("GreenJellyTick.png");
/**
* The cross icon.
*/
private static final Icon CROSSICON = loadIcon("OrangeJellyCross.png");
/**
* Icon Size.
*/
private static final int ICON_SIZE = 20;
/**
* Small width.
*/
private static final int WIDTH_SMALL = 60;
/**
* Medium width.
*/
private static final int WIDTH_MEDIUM = 80;
/**
* Large width.
*/
private static final int WIDTH_LARGE = 150;
/**
* The parent panel.
*/
private final ArduinoPanelFilter theParent;
/**
* The table.
*/
private final JTable theTable;
/**
* The table model.
*/
private final ArduinoFilterModel theModel;
/**
* The message list.
*/
private final ArduinoMsgFilter theMsgFilter;
/**
* The filter.
*/
private List<ArduinoMessage> theMessages;
/**
* Are we using a bespoke filter.
*/
private boolean isBespoke;
/**
* Constructor.
* @param pParent the parent panel
*/
ArduinoTableFilter(final ArduinoPanelFilter pParent) {
/* Store parameters */
theParent = pParent;
/* Create the filter */
theMsgFilter = new ArduinoMsgFilter();
/* Create the table and model */
theTable = new JTable();
theModel = new ArduinoFilterModel();
theTable.setModel(theModel);
theTable.addMouseListener(new MouseListener());
/* Set the row sorter */
theModel.setSorter();
/* Configure the columns */
configureColumns();
}
/**
* Obtain the component.
* @return the component
*/
JComponent getComponent() {
return theTable;
}
/**
* Obtain the filter.
* @return the filter
*/
ArduinoMsgFilter getFilter() {
return theMsgFilter;
}
/**
* Configure the columns.
*/
private void configureColumns() {
/* Create renderers */
final TableCellRenderer myRightRenderer = new AlignmentRenderer(SwingConstants.RIGHT);
final TableCellRenderer myCenterRenderer = new AlignmentRenderer(SwingConstants.CENTER);
final TableCellRenderer myIconRenderer = new IconRenderer();
/* Access the column model */
final TableColumnModel myModel = theTable.getColumnModel();
/* Set the name column */
TableColumn myColumn = myModel.getColumn(ArduinoFilterModel.COL_NAME);
myColumn.setPreferredWidth(WIDTH_LARGE);
myColumn.setCellRenderer(myRightRenderer);
/* Set the id column */
myColumn = myModel.getColumn(ArduinoFilterModel.COL_ID);
myColumn.setPreferredWidth(WIDTH_MEDIUM);
myColumn.setCellRenderer(myCenterRenderer);
/* Set the received column */
myColumn = myModel.getColumn(ArduinoFilterModel.COL_RECEIVED);
myColumn.setPreferredWidth(WIDTH_SMALL);
myColumn.setMaxWidth(WIDTH_SMALL);
myColumn.setCellRenderer(myIconRenderer);
/* Set the sent column */
myColumn = myModel.getColumn(ArduinoFilterModel.COL_SENT);
myColumn.setPreferredWidth(WIDTH_SMALL);
myColumn.setMaxWidth(WIDTH_SMALL);
myColumn.setCellRenderer(myIconRenderer);
}
/**
* Configure the table.
*
* @param pSystem the system
*/
void configureForSystem(final ArduinoSystem pSystem) {
/* store the details */
theMessages = pSystem.getMessages();
/* Configure for the system node */
configureForNode(pSystem);
}
/**
* Configure the table.
*
* @param pNode the node
*/
void configureForNode(final ArduinoNamedObject pNode) {
/* Update the filter */
theMsgFilter.resetSelection(pNode);
/* Reset bespoke flag */
isBespoke = false;
/* update the table model */
theModel.fireTableDataChanged();
theParent.updateFilter();
}
/**
* Is the filter bespoke?
* @return true/false
*/
boolean isBespoke() {
return isBespoke;
}
/**
* Load icon.
* @param pName the name of the icon
* @return the icon
*/
private static Icon loadIcon(final String pName) {
/* Load the icon */
try {
final Image myImage = ImageIO.read(ArduinoTableFilter.class.getResourceAsStream("icons/" + pName));
final Image myNewImage = myImage.getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_SMOOTH);
return new ImageIcon(myNewImage);
} catch (IOException e) {
LOGGER.error("Failed to load icon", e);
return null;
}
}
/**
* Table Model.
*/
private class ArduinoFilterModel
extends AbstractTableModel {
/**
* Serial Id.
*/
private static final long serialVersionUID = 240324436537632666L;
/**
* The name column.
*/
private static final int COL_NAME = 0;
/**
* The id column.
*/
private static final int COL_ID = COL_NAME + 1;
/**
* The sent column.
*/
private static final int COL_SENT = COL_ID + 1;
/**
* The received column.
*/
private static final int COL_RECEIVED = COL_SENT + 1;
/**
* Constructor.
*/
ArduinoFilterModel() {
}
@Override
public int getRowCount() {
return theMessages != null ? theMessages.size() : 0;
}
@Override
public String getColumnName(final int pColIndex) {
switch (pColIndex) {
case COL_NAME:
return "Name";
case COL_ID:
return "Id";
case COL_SENT:
return "Sent";
case COL_RECEIVED:
return "Received";
default:
return null;
}
}
@Override
public int getColumnCount() {
return COL_RECEIVED + 1;
}
@Override
public boolean isCellEditable(final int pRowIndex,
final int pColIndex) {
return false;
}
@Override
public Object getValueAt(final int pRowIndex,
final int pColIndex) {
switch (pColIndex) {
case COL_NAME:
return theMessages.get(pRowIndex).getName();
case COL_ID:
return getId(pRowIndex);
case COL_SENT:
return theMsgFilter.isBuilt(getId(pRowIndex)) ? TICKICON : CROSSICON;
case COL_RECEIVED:
return theMsgFilter.isParsed(getId(pRowIndex)) ? TICKICON : CROSSICON;
default:
return null;
}
}
/**
* Obtain the id for a row.
* @param pRowIndex the rowIndex
* @return the id.
*/
private String getId(final int pRowIndex) {
return theMessages.get(pRowIndex).getId();
}
/**
* Set row sorter.
*/
void setSorter() {
/* Create a sorter */
final TableRowSorter<ArduinoFilterModel> mySorter = new TableRowSorter<>(this);
/* Create the standard sort order */
final List<SortKey> mySortKeys = new ArrayList<>();
mySortKeys.add(new SortKey(COL_SENT, SortOrder.ASCENDING));
mySortKeys.add(new SortKey(COL_RECEIVED, SortOrder.ASCENDING));
mySorter.setSortKeys(mySortKeys);
/* Sort on update and set sorter */
mySorter.setSortsOnUpdates(true);
theTable.setRowSorter(mySorter);
}
}
/**
* Simple Alignment Renderer.
*/
private static class AlignmentRenderer extends DefaultTableCellRenderer {
/**
* Constructor.
*
* @param pAlignment the alignment
*/
AlignmentRenderer(final int pAlignment) {
setHorizontalAlignment(pAlignment);
}
@Override
public JComponent getTableCellRendererComponent(final JTable pTable,
final Object pValue,
final boolean pSelected,
final boolean hasFocus,
final int pRow,
final int pCol) {
/* Obtain the string representation */
final String myValue = pValue == null
? null
: pValue.toString();
/* Set the value */
setText(myValue);
/* return this as the component */
return this;
}
}
/**
* Simple Icon Renderer.
*/
private static class IconRenderer extends DefaultTableCellRenderer {
/**
* Constructor.
*/
IconRenderer() {
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public JComponent getTableCellRendererComponent(final JTable pTable,
final Object pValue,
final boolean pSelected,
final boolean hasFocus,
final int pRow,
final int pCol) {
/* Access the icon */
final Icon myIcon = pValue instanceof Icon
? (Icon) pValue
: null;
/* Set the icon */
setIcon(myIcon);
/* return this as the component */
return this;
}
}
/**
* Mouse Adapter class.
* <p>
* Required to handle button clicked, dragged, and released in different place
*/
private class MouseListener
extends MouseAdapter {
@Override
public void mouseClicked(final MouseEvent e) {
/* Determine the row and column that the click has occurred in */
final int myRow = theTable.convertRowIndexToModel(theTable.rowAtPoint(e.getPoint()));
final int myCol = theTable.convertColumnIndexToModel(theTable.columnAtPoint(e.getPoint()));
/* If it was in the table and is a column of interest */
if (myRow != -1
&& (myCol == ArduinoFilterModel.COL_RECEIVED || myCol == ArduinoFilterModel.COL_SENT)) {
/* Process the click */
final ArduinoMessage myMessage = theMessages.get(myRow);
processClick(myMessage, myCol);
theModel.fireTableCellUpdated(myRow, myCol);
theParent.updateFilter();
}
}
/**
* Process click.
* @param pMessage the message
* @param pColumn the column index
*/
void processClick(final ArduinoMessage pMessage,
final int pColumn) {
final int myFlag = pColumn == ArduinoFilterModel.COL_RECEIVED
? ArduinoMsgFilter.PARSEMSG
: ArduinoMsgFilter.BUILDMSG;
theMsgFilter.toggleFlag(pMessage.getId(), myFlag);
isBespoke = true;
}
}
}