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}