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