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