001package jmri.jmrit.beantable;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import java.awt.Component;
006import java.awt.event.ActionEvent;
007import java.awt.event.ActionListener;
008import java.text.MessageFormat;
009
010import javax.swing.BoxLayout;
011import javax.swing.JMenu;
012import javax.swing.JMenuBar;
013import javax.swing.JMenuItem;
014import javax.swing.JPanel;
015import javax.swing.JScrollPane;
016import javax.swing.JTable;
017import javax.swing.SortOrder;
018import javax.swing.table.TableRowSorter;
019
020import jmri.NamedBean;
021import jmri.swing.RowSorterUtil;
022import jmri.util.AlphanumComparator;
023
024/**
025 * Provide a JFrame to display a table of NamedBeans.
026 * <p>
027 * This is used when a table is opened by itself, without
028 * being embedded in a selection frame.  Typically, this 
029 * happens when the table is opened from a startup action.
030 * <p>
031 * This frame includes the table itself at the top, plus a "bottom area" for
032 * things like an Add... button and checkboxes that control display options.
033 * <p>
034 * The usual menus are also provided here.
035 * <p>
036 * Specific uses are customized via the BeanTableDataModel implementation they
037 * provide, and by providing a {@link #extras} implementation that can in turn
038 * invoke {@link #addToBottomBox} as needed.
039 *
040 * @author Bob Jacobsen Copyright (C) 2003
041 */
042public class BeanTableFrame<E extends NamedBean> extends jmri.util.JmriJFrame {
043
044    BeanTableDataModel<E> dataModel;
045    JTable dataTable;
046    final JPanel bottomBox;  // panel at bottom for extra buttons etc
047
048    public BeanTableFrame() {
049        super();
050        bottomBox = new JPanel();
051        bottomBox.setLayout(new jmri.util.swing.WrapLayout( jmri.util.swing.WrapLayout.LEFT, 20, 5));
052    }
053
054    public BeanTableFrame(String s) {
055        super(s);
056        bottomBox = new JPanel();
057        bottomBox.setLayout(new jmri.util.swing.WrapLayout( jmri.util.swing.WrapLayout.LEFT, 20, 5));
058    }
059
060    public BeanTableFrame(BeanTableDataModel<E> model, String helpTarget, JTable dataTab) {
061
062        this();
063        dataModel = model;
064        this.dataTable = dataTab;
065
066        JScrollPane dataScroll = new JScrollPane(dataTable);
067
068        // give system name column as smarter sorter and use it initially
069        TableRowSorter<BeanTableDataModel<?>> sorter = new TableRowSorter<>(dataModel);
070
071        // use NamedBean's built-in Comparator interface for sorting the system name column
072        RowSorterUtil.setSortOrder(sorter, BeanTableDataModel.SYSNAMECOL, SortOrder.ASCENDING);
073
074        sorter.setComparator(BeanTableDataModel.USERNAMECOL, new AlphanumComparator());
075        RowSorterUtil.setSortOrder(sorter, BeanTableDataModel.USERNAMECOL, SortOrder.ASCENDING);
076
077        this.dataTable.setRowSorter(sorter);
078
079        // configure items for GUI
080        dataModel.configureTable(dataTable);
081
082        // general GUI config
083        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
084
085        // add save menu item
086        JMenuBar menuBar = new JMenuBar();
087        JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile"));
088        menuBar.add(fileMenu);
089        fileMenu.add(new jmri.configurexml.StoreMenu());
090
091        JMenuItem printItem = new JMenuItem(Bundle.getMessage("PrintTable"));
092        fileMenu.add(printItem);
093        printItem.addActionListener(new ActionListener() {
094            @Override
095            public void actionPerformed(ActionEvent e) {
096                try {
097                    // MessageFormat headerFormat = new MessageFormat(getTitle());  // not used below
098                    MessageFormat footerFormat = new MessageFormat(getTitle() + " page {0,number}");
099                    dataTable.print(JTable.PrintMode.FIT_WIDTH, null, footerFormat);
100                } catch (java.awt.print.PrinterException e1) {
101                    log.warn("error printing: {}", e1, e1);
102                }
103            }
104        });
105
106        JMenuItem exportItem = new JMenuItem(Bundle.getMessage("ExportTable"));
107        fileMenu.add(exportItem);
108        exportItem.addActionListener((ActionEvent e) -> {
109            dataModel.exportToCSV(null);
110        });
111
112        setJMenuBar(menuBar);
113
114        addHelpMenu(helpTarget, true);
115
116        // install items in GUI
117        getContentPane().add(dataScroll);
118        getContentPane().add(bottomBox);
119
120        // add extras, if desired by subclass
121        extras();
122
123        // set Viewport preferred size from size of table
124        java.awt.Dimension dataTableSize = dataTable.getPreferredSize();
125        // width is right, but if table is empty, it's not high
126        // enough to reserve much space.
127        dataTableSize.height = Math.max(dataTableSize.height, 400);
128        dataScroll.getViewport().setPreferredSize(dataTableSize);
129
130        // set preferred scrolling options
131        dataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
132        dataScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
133        dataModel.persistTable(dataTable);
134    }
135
136    /**
137     * Hook to allow sub-types to install more items in GUI
138     */
139    void extras() {
140    }
141
142    /**
143     * Add a component to the bottom box. Takes care of organising glue, struts
144     * etc
145     *
146     * @param comp {@link Component} to add
147     * @param c    Class name
148     */
149    @SuppressFBWarnings(value = "UUF_UNUSED_FIELD",
150            justification = "param c is required in the listedtableframe")
151    protected void addToBottomBox(Component comp, String c) {
152       bottomBox.add(comp);
153    }
154
155    public JTable getTable() {
156        return dataTable;
157    }
158    
159    @Override
160    public void dispose() {
161        if (dataModel != null) {
162            dataModel.stopPersistingTable(dataTable);
163            dataModel.dispose();
164        }
165        dataModel = null;
166        dataTable = null;
167        super.dispose();
168    }
169
170    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BeanTableFrame.class);
171
172}