001package jmri.jmrit.beantable;
002
003import jmri.jmrit.beantable.oblock.*;
004import jmri.swing.RowSorterUtil;
005import jmri.util.swing.XTableColumnModel;
006import jmri.util.table.ButtonEditor;
007import jmri.util.table.ButtonRenderer;
008import jmri.util.table.ToggleButtonEditor;
009import jmri.util.table.ToggleButtonRenderer;
010
011import javax.annotation.CheckForNull;
012import javax.annotation.Nonnull;
013
014import javax.swing.*;
015import javax.swing.table.TableModel;
016import javax.swing.table.TableRowSorter;
017import java.awt.*;
018import java.util.Objects;
019
020/**
021 * GUI for tabbed OBlock editing since 2020. Based on AudioTablePanel.
022 * OBlock parts adapted from {@link jmri.jmrit.beantable.oblock.TableFrames}
023 * Which interface will be presented is user settable in Display prefs.
024 *
025 * @author Bob Jacobsen Copyright (C) 2003
026 * @author Matthew Harris copyright (c) 2009
027 * @author Egbert Broerse copyright (c) 2020
028 */
029public class OBlockTablePanel extends JPanel {
030
031    private OBlockTableModel oblockDataModel;
032    private PortalTableModel portalDataModel;
033    private SignalTableModel signalDataModel;
034    private BlockPortalTableModel blockportalDataModel;
035
036    private JTable oblockTable;
037    private JTable portalTable;
038    private JTable signalTable;
039    private JTable blockportalTable;
040
041    private JScrollPane oblockDataScroll;
042    private JScrollPane portalDataScroll;
043    private JScrollPane signalDataScroll;
044    private JScrollPane blockportalDataScroll;
045
046    private final JTabbedPane oblockTabs;
047    TableFrames _tf;
048    private final JPanel bottomBox; // panel at bottom for extra buttons etc
049
050//    @SuppressWarnings("OverridableMethodCallInConstructor")
051    public OBlockTablePanel(OBlockTableModel oblocks,
052                            PortalTableModel portals,
053                            SignalTableModel signals,
054                            BlockPortalTableModel blockportals,
055                            TableFrames tf,
056                            String helpTarget) {
057
058        super(); // required? nothing set
059        _tf = tf;
060
061        log.debug("Building tables");
062
063        // OBlock Table
064        oblockDataModel = oblocks;
065        TableRowSorter<OBlockTableModel> sorter = new TableRowSorter<>(oblockDataModel);
066        // use NamedBean's built-in Comparator interface for sorting the system name column
067        RowSorterUtil.setSortOrder(sorter, OBlockTableModel.SYSNAMECOL, SortOrder.ASCENDING);
068        oblockTable = makeJTable(OBlockTableAction.class.getName(), oblockDataModel, sorter); // use our own
069        // style table, check overlap with configureWarrantTable done next
070        oblockTable.setDefaultEditor(JButton.class, new ButtonEditor(new JButton()));
071        oblockTable.setDefaultRenderer(JButton.class, new ButtonRenderer());
072        oblockTable.getColumnModel().getColumn(OBlockTableModel.UNITSCOL).setCellRenderer(
073                new ToggleButtonRenderer(Bundle.getMessage("cm"), Bundle.getMessage("in")));
074        oblockTable.getColumnModel().getColumn(OBlockTableModel.UNITSCOL).setCellEditor(
075                new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("cm"), Bundle.getMessage("in")));
076        oblocks.configCurveColumn(oblockTable); // use real combo
077        oblockTable.getColumnModel().getColumn(OBlockTableModel.REPORT_CURRENTCOL).setCellRenderer(
078                new ToggleButtonRenderer(Bundle.getMessage("Current"), Bundle.getMessage("Last")));
079        oblockTable.getColumnModel().getColumn(OBlockTableModel.REPORT_CURRENTCOL).setCellEditor(
080                new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Current"), Bundle.getMessage("Last")));
081        oblocks.configSpeedColumn(oblockTable); // use real combo
082        oblockTable.getColumnModel().getColumn(OBlockTableModel.PERMISSIONCOL).setCellRenderer(
083                new ToggleButtonRenderer(Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute")));
084        oblockTable.getColumnModel().getColumn(OBlockTableModel.PERMISSIONCOL).setCellEditor(
085                new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute")));
086        // Use XTableColumnModel so we can control which columns are visible
087        XTableColumnModel tcm = new XTableColumnModel();
088        oblockTable.setColumnModel(tcm);
089        oblockTable.getTableHeader().setReorderingAllowed(true); // makeJTable not used for oblockTable
090        oblockTable.createDefaultColumnsFromModel();
091        tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.REPORTERCOL), false); // doesn't hide them?
092        tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.REPORT_CURRENTCOL), false);
093        tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.PERMISSIONCOL), false);
094        tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.WARRANTCOL), false);
095        tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.ERR_SENSORCOL), false);
096        tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.CURVECOL), false);
097        for (int i = 0; i < tcm.getColumnCount(); i++) {
098            int width = oblockDataModel.getPreferredWidth(i);
099            tcm.getColumn(i).setPreferredWidth(width);
100        }
101        oblockDataModel.addHeaderListener(oblockTable); // HeaderListeners not set up for the other 3 small tables
102        oblockTable.setPreferredScrollableViewportSize(new java.awt.Dimension(550, 300)); // a wide table
103        oblockDataScroll = new JScrollPane(oblockTable);
104
105        // Portal Table
106        portalDataModel = portals;
107        TableRowSorter<PortalTableModel> portalsorter = new TableRowSorter<>(portalDataModel);
108        RowSorterUtil.setSortOrder(portalsorter, portalDataModel.NAME_COLUMN, SortOrder.ASCENDING);
109        portalTable = makeJTable("Portal", portalDataModel, portalsorter);
110        // style table
111        portalTable.setDefaultEditor(JButton.class, new ButtonEditor(new JButton()));
112        portalTable.setDefaultRenderer(JButton.class, new ButtonRenderer());
113        portalTable.doLayout();
114        //portalTable.setColumnModel(new XTableColumnModel());
115        portalTable.createDefaultColumnsFromModel();
116        for (int i = 0; i < portalDataModel.getColumnCount(); i++) {
117            int width = portalDataModel.getPreferredWidth(i);
118            portalTable.getColumnModel().getColumn(i).setPreferredWidth(width);
119        }
120        portalDataScroll = new JScrollPane(portalTable);
121
122        // Signal Table
123        signalDataModel = signals;
124        TableRowSorter<SignalTableModel> sigsorter = new TableRowSorter<>(signalDataModel);
125        RowSorterUtil.setSortOrder(sigsorter, SignalTableModel.NAME_COLUMN, SortOrder.ASCENDING);
126        signalTable = makeJTable("Signals", signalDataModel, sigsorter);
127        // style table
128        signalTable.setDefaultEditor(JButton.class, new ButtonEditor(new JButton()));
129        signalTable.setDefaultRenderer(JButton.class, new ButtonRenderer());
130        signalTable.getColumnModel().getColumn(SignalTableModel.UNITSCOL).setCellRenderer(
131                new ToggleButtonRenderer(Bundle.getMessage("cm"), Bundle.getMessage("in")));
132        signalTable.getColumnModel().getColumn(SignalTableModel.UNITSCOL).setCellEditor(
133                new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("cm"), Bundle.getMessage("in")));
134        signalTable.doLayout();
135        //signalTable.setColumnModel(new XTableColumnModel());
136        signalTable.createDefaultColumnsFromModel();
137        for (int i = 0; i < signalDataModel.getColumnCount(); i++) {
138            int width = SignalTableModel.getPreferredWidth(i);
139            signalTable.getColumnModel().getColumn(i).setPreferredWidth(width);
140        }
141        signalDataScroll = new JScrollPane(signalTable);
142
143        // Block-Portal Xreference table
144        blockportalDataModel = blockportals; // cross-reference (not editable)
145        //sorter = new TableRowSorter<>(blockportalDataModel);
146        RowSorterUtil.setSortOrder(sorter, BlockPortalTableModel.BLOCK_NAME_COLUMN, SortOrder.ASCENDING);
147        blockportalTable = makeJTable("Block-Portal X-ref", blockportalDataModel, sorter); // cannot directly access
148        // style table
149        blockportalTable.setDefaultRenderer(String.class, new jmri.jmrit.symbolicprog.ValueRenderer());
150        blockportalTable.doLayout();
151        //blockportalTable.setColumnModel(new XTableColumnModel());
152        blockportalTable.createDefaultColumnsFromModel();
153        for (int i = 0; i < blockportalDataModel.getColumnCount(); i++) {
154            int width = blockportalDataModel.getPreferredWidth(i);
155            blockportalTable.getColumnModel().getColumn(i).setPreferredWidth(width);
156        }
157        blockportalDataScroll = new JScrollPane(blockportalTable);
158
159        // configure items for GUI
160        configureWarrantTable(oblockTable); // only class to extend BeanTableDataModel
161        //oblockDataModel.configEditColumn(oblockTable);
162        for (int i = 0; i < oblockTable.getColumnCount(); i++) {
163            // copied from TableFrames#makeOBlockTable() l729 as it needs table so can't copy to oblockDataModel
164            int width = oblockDataModel.getPreferredWidth(i);
165            oblockTable.getColumnModel().getColumn(i).setPreferredWidth(width);
166        }
167        oblockDataModel.persistTable(oblockTable); // only oblockDataModel contains this method
168
169        configureWarrantTable(signalTable);
170        // pathDataModel.configEditColumn(pathTable);
171        //oblockDataModel.persistTable(signalTable);
172
173        configureWarrantTable(portalTable);
174        // portalDataModel.configEditColumn(portalTable);
175        //oblockDataModel.persistTable(portalTable);
176
177        configureWarrantTable(blockportalTable);
178        // portalDataModel.configEditColumn(blockportalTable);
179        //oblockDataModel.persistTable(blockportalTable);
180
181        // add more changeListeners for table (example load, created) to update tables?
182
183        // general GUI config
184        this.setLayout(new BorderLayout());
185
186        // install the four items in GUI as tabs
187        oblockTabs = new JTabbedPane();
188        oblockTabs.addTab(Bundle.getMessage("BeanNameOBlocks"), oblockDataScroll);
189        oblockTabs.addTab(Bundle.getMessage("BeanNamePortals"), portalDataScroll);
190        oblockTabs.addTab(Bundle.getMessage("Signals"), signalDataScroll);
191        oblockTabs.addTab(Bundle.getMessage("TitleBlockPortalXRef"), blockportalDataScroll);
192        // turnouts not on a tab: via Edit button in Path Edit pane (or a Tables submenu)
193
194        add(oblockTabs, BorderLayout.CENTER);
195        log.debug("tabs complete");
196
197        bottomBox = new JPanel();
198        bottomBox.setLayout(new jmri.util.swing.WrapLayout(jmri.util.swing.WrapLayout.LEFT,20,5));
199
200        add(bottomBox, BorderLayout.SOUTH);
201
202        log.debug("bottomBox complete");
203        // set preferred scrolling options
204        oblockDataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
205//        portalDataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
206//        signalDataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
207//        blockportalDataScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
208    }
209
210    public JMenuItem getPrintItem() { // copied from AudioTablePanel
211        log.debug("OBLOCK TABBED getPrintItem() called");
212        return _tf.getPrintMenuItems(oblockTable, portalTable, signalTable, blockportalTable);
213    }
214
215    public JMenu getOptionMenu() {
216        log.debug("OBLOCK TABBED getOptionMenu() called");
217        return _tf.getOptionMenu();
218    }
219
220    public JMenu getTablesMenu() {
221        log.debug("OBLOCK TABBED getTablesMenu() called");
222        return _tf.getTablesMenu();
223    }
224
225    /**
226     * Add a component to the bottom box.
227     * @param comp {@link Component} to add
228     */
229    protected void addToBottomBox(Component comp) {
230        bottomBox.add(comp);
231    }
232
233    public void dispose() {
234        if (oblockDataModel != null) {
235            oblockDataModel.stopPersistingTable(oblockTable);
236            oblockDataModel.dispose();
237        }
238        oblockDataModel = null;
239        oblockTable = null;
240        oblockDataScroll = null;
241
242        //if (portalDataModel != null) {
243            // portalDataModel.stopPersistingTable(portalTable);
244            // portalDataModel.dispose();
245        //}
246        portalDataModel = null;
247        portalTable = null;
248        portalDataScroll = null;
249
250        //if (signalDataModel != null) {
251            // signalDataModel.stopPersistingTable(signalTable);
252            // signalDataModel.dispose();
253        //}
254        signalDataModel = null;
255        signalTable = null;
256        signalDataScroll = null;
257
258        //if (blockportalDataModel != null) {
259            // blockportalDataModel.stopPersistingTable(blockportalTable);
260            // blockportalDataModel.dispose();
261        //}
262        blockportalDataModel = null;
263        blockportalTable = null;
264        blockportalDataScroll = null;
265    }
266
267    /**
268     * Create and configure a new table using the given model and row sorter.
269     *
270     * @param name   the name of the table
271     * @param model  the data model for the table
272     * @param sorter the row sorter for the table; if null, the table will not
273     *               be sortable
274     * @return the table
275     * @throws NullPointerException if name or model is null
276     */
277    public JTable makeJTable(@Nonnull String name, @Nonnull TableModel model, @CheckForNull RowSorter<? extends TableModel> sorter) {
278        Objects.requireNonNull(name, "the table name must be nonnull " + name);
279        Objects.requireNonNull(model, "the table model must be nonnull " + name);
280        JTable table = this.configureJTable(name, new JTable(model), sorter);
281        //model.addHeaderListener(table);
282        return table;
283    }
284
285    /**
286     * Configure a new table using the given model and row sorter.
287     *
288     * @param table  the table to configure
289     * @param name   the table name
290     * @param sorter the row sorter for the table; if null, the table will not
291     *               be sortable
292     * @return the table
293     * @throws NullPointerException if table or the table name is null
294     */
295    protected JTable configureJTable(@Nonnull String name, @Nonnull JTable table, @CheckForNull RowSorter<? extends TableModel> sorter) {
296        Objects.requireNonNull(table, "the table must be nonnull");
297        Objects.requireNonNull(name, "the table name must be nonnull");
298        table.setRowSorter(sorter);
299        table.setName(name);
300        //table.getTableHeader().setReorderingAllowed(true); // already assigned per table above
301        //table.setColumnModel(new XTableColumnModel());
302        //table.createDefaultColumnsFromModel();
303        return table;
304    }
305
306    /**
307     * Configure a table to have our standard rows and columns.
308     * This also persists the table user interface state.
309     * Adapted from {@link BeanTableDataModel} for tables 1-4, EBR 2020
310     *
311     * @param table {@link JTable} to configure
312     */
313    public void configureWarrantTable(JTable table) {
314        // ignore Property columns
315        table.setDefaultRenderer(JButton.class, new ButtonRenderer());
316        table.setDefaultEditor(JButton.class, new ButtonEditor(new JButton()));
317        table.setDefaultRenderer(JToggleButton.class, new ToggleButtonRenderer(Bundle.getMessage("cm"), Bundle.getMessage("in"))); // overrides
318        table.setDefaultEditor(JToggleButton.class, new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("cm"), Bundle.getMessage("in")));
319        table.setDefaultRenderer(JRadioButton.class, new ToggleButtonRenderer(Bundle.getMessage("Current"), Bundle.getMessage("Last"))); // overrides
320        table.setDefaultEditor(JRadioButton.class, new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Current"), Bundle.getMessage("Last")));
321        table.setDefaultRenderer(JCheckBox.class, new ToggleButtonRenderer(Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute")));
322        table.setDefaultEditor(JCheckBox.class, new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute")));
323        table.setDefaultEditor(OBlockTableModel.SpeedComboBoxPanel.class, new OBlockTableModel.SpeedComboBoxPanel());
324        table.setDefaultRenderer(OBlockTableModel.SpeedComboBoxPanel.class, new OBlockTableModel.SpeedComboBoxPanel());
325        table.setDefaultEditor(OBlockTableModel.CurveComboBoxPanel.class, new OBlockTableModel.CurveComboBoxPanel());
326        table.setDefaultRenderer(OBlockTableModel.CurveComboBoxPanel.class, new OBlockTableModel.CurveComboBoxPanel());
327        // allow reordering of the columns
328        //table.getTableHeader().setReorderingAllowed(true);
329        // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541)
330        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
331        table.setRowHeight(TableFrames.ROW_HEIGHT);
332        // resize columns per table
333//        table.doLayout();
334        // resize columns as requested (for OBlocks tabbed: throws java.lang.IllegalArgumentException: "Identifier not found")
335//        for (int i = 0; i < table.getColumnCount(); i++) {
336//            int width = table.getColumn(i).getPreferredWidth();
337//            table.getColumnModel().getColumn(i).setPreferredWidth(width);
338//        }
339    }
340
341    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OBlockTablePanel.class);
342
343}