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