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}