001package jmri.jmrix.can.cbus.swing.nodeconfig;
002
003import java.awt.*;
004import java.text.DateFormat;
005import java.text.SimpleDateFormat;
006
007import javax.swing.*;
008import javax.swing.event.TableModelEvent;
009import javax.swing.table.TableCellRenderer;
010import javax.swing.table.TableRowSorter;
011
012import jmri.jmrix.can.CanSystemConnectionMemo;
013import jmri.jmrix.can.cbus.CbusConfigurationManager;
014import jmri.jmrix.can.cbus.node.CbusNodeTableDataModel;
015import jmri.jmrix.can.cbus.swing.CbusCommonSwing;
016import jmri.util.ThreadingUtil;
017import jmri.util.swing.XTableColumnModel;
018import jmri.util.table.*;
019
020/**
021 * Pane providing a CBUS node table.
022 *
023 * @author Steve Young (C) 2019
024 * @see CbusNodeTableDataModel
025 *
026 * @since 2.99.2
027 */
028public class CbusNodeTablePane extends JPanel {
029
030    private CbusNodeTableDataModel nodeModel;
031    protected JTable nodeTable;
032
033    private final DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm EEE d MMM");
034
035    public void initComponents(CanSystemConnectionMemo memo) {
036        nodeModel = memo.get(CbusConfigurationManager.class)
037            .provide(CbusNodeTableDataModel.class);
038
039        init();
040
041    }
042
043    public CbusNodeTablePane() {
044        super();
045    }
046
047    public void init() {
048
049        nodeTable = new JTableWithColumnToolTips(nodeModel,CbusNodeTableDataModel.COLUMNTOOLTIPS){
050
051            @Override
052            public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
053                int modelRow = convertRowIndexToModel(row);
054                int modelColumn = convertColumnIndexToModel(column);
055                Component comp = super.prepareRenderer(renderer, row, column);
056                if (!isRowSelected(modelRow) &&
057                        ( modelColumn ==  CbusNodeTableDataModel.NODE_IN_LEARN_MODE_COLUMN
058                        || modelColumn ==  CbusNodeTableDataModel.NODE_EVENT_INDEX_VALID_COLUMN
059                        )) {
060                    comp.setBackground(( row % 2 == 0 ) ? this.getBackground() : jmri.jmrix.can.cbus.swing.CbusCommonSwing.WHITE_GREEN );
061                }
062                return comp;
063            }
064        };
065
066        // Use XTableColumnModel so we can control which columns are visible
067        XTableColumnModel tcm = new XTableColumnModel();
068        nodeTable.setColumnModel(tcm);
069
070        TableRowSorter<CbusNodeTableDataModel>sorter = new TableRowSorter<>(nodeModel);
071        nodeTable.setRowSorter(sorter);
072
073        // configure items for GUI
074        CbusCommonSwing.configureTable(nodeTable);
075
076        tcm.getColumn(CbusNodeTableDataModel.NODE_NUMBER_COLUMN).setCellRenderer(getRenderer());
077        tcm.getColumn(CbusNodeTableDataModel.NODE_TYPE_NAME_COLUMN).setCellRenderer(getRenderer());
078        tcm.getColumn(CbusNodeTableDataModel.NODE_USER_NAME_COLUMN).setCellRenderer(getRenderer());
079        tcm.getColumn(CbusNodeTableDataModel.NODE_EVENTS_COLUMN).setCellRenderer(getRenderer());
080        tcm.getColumn(CbusNodeTableDataModel.COMMAND_STAT_NUMBER_COLUMN).setCellRenderer(getRenderer());
081        tcm.getColumn(CbusNodeTableDataModel.CANID_COLUMN).setCellRenderer(getRenderer());
082        tcm.getColumn(CbusNodeTableDataModel.NODE_TOTAL_BYTES_COLUMN).setCellRenderer(getRenderer());
083        tcm.getColumn(CbusNodeTableDataModel.BYTES_REMAINING_COLUMN).setCellRenderer(new ProgressCellRender(CbusCommonSwing.WHITE_GREEN));
084        tcm.getColumn(CbusNodeTableDataModel.NUMBER_BACKUPS_COLUMN).setCellRenderer(getRenderer());
085        tcm.getColumn(CbusNodeTableDataModel.SESSION_BACKUP_STATUS_COLUMN).setCellRenderer(getRenderer());
086        tcm.getColumn(CbusNodeTableDataModel.LAST_BACKUP_COLUMN).setCellRenderer(getRenderer());
087
088        tcm.getColumn(CbusNodeTableDataModel.NODE_RESYNC_BUTTON_COLUMN).setCellEditor(new ButtonEditor(new JButton()));
089        tcm.getColumn(CbusNodeTableDataModel.NODE_RESYNC_BUTTON_COLUMN).setCellRenderer(new ButtonRenderer());
090
091        tcm.getColumn(CbusNodeTableDataModel.NODE_EDIT_BUTTON_COLUMN).setCellEditor(new ButtonEditor(new JButton()));
092        tcm.getColumn(CbusNodeTableDataModel.NODE_EDIT_BUTTON_COLUMN).setCellRenderer(new ButtonRenderer());
093
094       ((JComponent) tcm.getColumn(CbusNodeTableDataModel.NODE_RESYNC_BUTTON_COLUMN).getCellRenderer()).setOpaque(false);
095       ((JComponent) tcm.getColumn(CbusNodeTableDataModel.NODE_EDIT_BUTTON_COLUMN).getCellRenderer()).setOpaque(false);
096
097        setLayout(new BorderLayout());
098        JScrollPane eventScroll = new JScrollPane(nodeTable);
099        eventScroll.setVisible(true);
100        eventScroll.setPreferredSize(new Dimension(300, 40));
101        add(eventScroll);
102
103        validate();
104        repaint();
105
106        nodeModel.addTableModelListener((TableModelEvent e) -> {
107            if (nodeModel.getRequestNodeRowToDisplay()>-1) {
108                setRowToNode();
109            }
110        });
111        nodeModel.fireTableDataChanged();
112    }
113
114    private void setRowToNode(){
115        ThreadingUtil.runOnGUIEventually(() -> {
116            int newRow = nodeModel.getRequestNodeRowToDisplay();
117            if ( newRow>-1 ) {
118                nodeTable.setRowSelectionInterval(newRow, newRow);
119                nodeTable.scrollRectToVisible(nodeTable.getCellRect(newRow, 0, true));
120                nodeModel.setRequestNodeDisplay(-1);
121            }
122        });
123    }
124
125    /**
126     * Cell Renderer for string table columns
127     */
128    private TableCellRenderer getRenderer() {
129        return new TableCellRenderer() {
130            JTextField f = new JTextField();
131
132            @Override
133            public Component getTableCellRendererComponent(
134                JTable table, Object arg1, boolean isSelected, boolean hasFocus,
135                int row, int col) {
136                String string;
137                if(arg1 != null){
138                    string = arg1.toString();
139                    f.setText(string);
140                    f.setHorizontalAlignment(SwingConstants.CENTER);
141                    CbusCommonSwing.hideNumbersLessThan(0, string, f);
142                    CbusCommonSwing.setCellFromDate(arg1,f,DATE_FORMAT);
143                    CbusCommonSwing.setCellFromBackupEnum(arg1,f);
144
145                } else {
146                    f.setText("");
147                }
148
149                CbusCommonSwing.setCellBackground(isSelected, f, table,row);
150                CbusCommonSwing.setCellFocus(hasFocus, f, table);
151
152                checkDuplicateCanId(f, table, arg1, col);
153
154                return f;
155            }
156
157            private void checkDuplicateCanId(JTextField f, JTable table, Object val, int col) {
158
159                int modelCol = table.convertColumnIndexToModel(col);
160                if (( modelCol == CbusNodeTableDataModel.CANID_COLUMN ) && (val instanceof Integer )) {
161                    int numCanIds = nodeModel.getNumberNodesWithCanId((int)val);
162                    if ( numCanIds > 1 ) {
163                        f.setBackground(CbusCommonSwing.VERY_LIGHT_RED);
164                        f.setToolTipText(Bundle.getMessage("DuplicateCanIdTip"));
165                    } else {
166                        f.setToolTipText(null);
167                    }
168                }
169            }
170        };
171    }
172
173    public void dispose() {
174        nodeTable = null;
175    }
176
177    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusNodeTablePane.class);
178
179}