/* * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Sun Microsystems nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.sun.swingset3.demos.table; import com.sun.swingset3.DemoProperties; import com.sun.swingset3.demos.DemoUtilities; import com.sun.swingset3.demos.Stacker; import javax.swing.*; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; import javax.swing.event.*; import javax.swing.table.*; import javax.swing.text.Document; import java.awt.*; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; /** * * @author aim */ @DemoProperties( value = "JTable Demo", category = "Data", description = "Demonstrates use of Swing's data grid component, JTable, including asynchronous loading and sorting/filtering.", sourceFiles = { "com/sun/swingset3/demos/table/TableDemo.java", "com/sun/swingset3/demos/table/HyperlinkCellRenderer.java", "com/sun/swingset3/demos/table/IMDBLink.java", "com/sun/swingset3/demos/table/Link.java", "com/sun/swingset3/demos/table/OscarCandidate.java", "com/sun/swingset3/demos/table/OscarCellRenderers.java", "com/sun/swingset3/demos/table/OscarDataParser.java", "com/sun/swingset3/demos/table/OscarTableModel.java", "com/sun/swingset3/demos/DemoUtilities.java", "com/sun/swingset3/demos/JHyperlink.java", "com/sun/swingset3/demos/table/resources/bestpicture", //"com/sun/swingset3/demos/table/resources/oscars.xml", file too large!! "com/sun/swingset3/demos/table/resources/TableDemo.properties", "com/sun/swingset3/demos/table/resources/images/goldstar.png", "com/sun/swingset3/demos/table/resources/images/nominee.png", "com/sun/swingset3/demos/table/resources/images/TableDemo.gif" } ) public class TableDemo extends JPanel { static final Logger logger = Logger.getLogger(TableDemo.class.getName()); private OscarTableModel oscarModel; private JPanel controlPanel; private Stacker dataPanel; private JTable oscarTable; private JCheckBox winnersCheckbox; private JTextField filterField; private Box statusBarLeft; private JLabel actionStatus; private JLabel tableStatus; private Color[] rowColors; private String statusLabelString; private String searchLabelString; private boolean showOnlyWinners = false; private String filterString = null; private TableRowSorter sorter; private RowFilter<OscarTableModel, Integer> winnerFilter; private RowFilter<OscarTableModel, Integer> searchFilter; // Resource bundle for internationalized and accessible text private ResourceBundle bundle = null; public TableDemo() { initModel(); initComponents(); initSortingFiltering(); } protected void initModel() { oscarModel = new OscarTableModel(); } protected void initComponents() { setLayout(new BorderLayout()); controlPanel = createControlPanel(); add(controlPanel, BorderLayout.NORTH); //<snip>Create JTable oscarTable = new JTable(oscarModel); //</snip> //</snip>Set JTable display properties oscarTable.setColumnModel(createColumnModel()); oscarTable.setAutoCreateRowSorter(true); oscarTable.setRowHeight(26); oscarTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); oscarTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); oscarTable.setIntercellSpacing(new Dimension(0, 0)); //</snip> //<snip>Initialize preferred size for table's viewable area Dimension viewSize = new Dimension(); viewSize.width = oscarTable.getColumnModel().getTotalColumnWidth(); viewSize.height = 10 * oscarTable.getRowHeight(); oscarTable.setPreferredScrollableViewportSize(viewSize); //</snip> //<snip>Customize height and alignment of table header JTableHeader header = oscarTable.getTableHeader(); header.setPreferredSize(new Dimension(30, 26)); TableCellRenderer headerRenderer = header.getDefaultRenderer(); if (headerRenderer instanceof JLabel) { ((JLabel) headerRenderer).setHorizontalAlignment(JLabel.CENTER); } //</snip> JScrollPane scrollpane = new JScrollPane(oscarTable); dataPanel = new Stacker(scrollpane); add(dataPanel, BorderLayout.CENTER); add(createStatusBar(), BorderLayout.SOUTH); } protected JPanel createControlPanel() { JPanel controlPanel = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); controlPanel.setLayout(gridbag); c.gridx = 0; c.gridy = 1; c.gridheight = 1; c.insets = new Insets(20, 10, 0, 10); c.anchor = GridBagConstraints.SOUTHWEST; JLabel searchLabel = new JLabel(getString("TableDemo.searchLabel", "Search Titles and Recipients")); controlPanel.add(searchLabel, c); c.gridx = 0; c.gridy = 2; c.weightx = 1.0; c.insets.top = 0; c.insets.bottom = 12; c.anchor = GridBagConstraints.SOUTHWEST; //c.fill = GridBagConstraints.HORIZONTAL; filterField = new JTextField(24); filterField.getDocument().addDocumentListener(new SearchFilterListener()); controlPanel.add(filterField, c); c.gridx = 1; c.gridy = 2; c.gridwidth = GridBagConstraints.REMAINDER; //c.insets.right = 24; //c.insets.left = 12; c.weightx = 0.0; c.anchor = GridBagConstraints.EAST; c.fill = GridBagConstraints.NONE; winnersCheckbox = new JCheckBox(getString("TableDemo.winnersLabel", "Show Only Winners")); winnersCheckbox.addChangeListener(new ShowWinnersListener()); controlPanel.add(winnersCheckbox, c); return controlPanel; } protected Container createStatusBar() { statusLabelString = getString("TableDemo.rowCountLabel", "Showing "); searchLabelString = getString("TableDemo.searchCountLabel", "Search found "); Box statusBar = Box.createHorizontalBox(); // Left status area statusBar.add(Box.createRigidArea(new Dimension(10, 22))); statusBarLeft = Box.createHorizontalBox(); statusBar.add(statusBarLeft); actionStatus = new JLabel(getString("TableDemo.noDataStatusLabel", "No data loaded")); actionStatus.setHorizontalAlignment(JLabel.LEADING); statusBarLeft.add(actionStatus); // Middle (should stretch) statusBar.add(Box.createHorizontalGlue()); statusBar.add(Box.createHorizontalGlue()); statusBar.add(Box.createVerticalGlue()); // Right status area tableStatus = new JLabel(statusLabelString + "0"); statusBar.add(tableStatus); statusBar.add(Box.createHorizontalStrut(12)); //<snip>Track number of rows currently displayed oscarModel.addTableModelListener(new TableModelListener() { public void tableChanged(TableModelEvent e) { // Get rowCount from *table*, not model, as the view row count // may be different from the model row count due to filtering tableStatus.setText((hasFilterString() ? searchLabelString : statusLabelString) + oscarTable.getRowCount()); } }); //</snip> return statusBar; } private Color[] getTableRowColors() { if (rowColors == null) { rowColors = new Color[2]; rowColors[0] = UIManager.getColor("Table.background"); rowColors[1] = new Color((int) (rowColors[0].getRed() * .9), (int) (rowColors[0].getGreen() * .9), (int) (rowColors[0].getBlue() * .9)); } return rowColors; } // returns appropriate string from resource bundle protected String getString(String key, String fallback) { String value = fallback; if (bundle == null) { String bundleName = getClass().getPackage().getName() + ".resources." + getClass().getSimpleName(); bundle = ResourceBundle.getBundle(bundleName); } try { value = bundle != null ? bundle.getString(key) : key; } catch (MissingResourceException ex) { logger.log(Level.WARNING, "couldn't find resource value for: " + key, ex); } return value; } public void start() { if (oscarModel.getRowCount() == 0) { loadData("resources/oscars.xml"); } } //<snip>Initialize table columns protected TableColumnModel createColumnModel() { DefaultTableColumnModel columnModel = new DefaultTableColumnModel(); TableCellRenderer cellRenderer = new OscarCellRenderers.RowRenderer(getTableRowColors()); TableColumn column = new TableColumn(); column.setModelIndex(OscarTableModel.YEAR_COLUMN); column.setHeaderValue(getString("TableDemo.yearColumnTitle", "Year")); column.setPreferredWidth(26); column.setCellRenderer(new OscarCellRenderers.YearRenderer(getTableRowColors())); columnModel.addColumn(column); column = new TableColumn(); column.setModelIndex(OscarTableModel.CATEGORY_COLUMN); column.setHeaderValue(getString("TableDemo.categoryColumnTitle", "Award Category")); column.setPreferredWidth(100); column.setCellRenderer(cellRenderer); columnModel.addColumn(column); column = new TableColumn(); column.setModelIndex(OscarTableModel.MOVIE_COLUMN); column.setHeaderValue(getString("TableDemo.movieTitleColumnTitle", "Movie Title")); column.setPreferredWidth(180); HyperlinkCellRenderer hyperlinkRenderer = new OscarCellRenderers.MovieRenderer(new IMDBLinkAction(), true, getTableRowColors()); hyperlinkRenderer.setRowColors(getTableRowColors()); column.setCellRenderer(hyperlinkRenderer); columnModel.addColumn(column); column = new TableColumn(); column.setModelIndex(OscarTableModel.PERSONS_COLUMN); column.setHeaderValue(getString("TableDemo.nomineesColumnTitle", "Nominees")); column.setPreferredWidth(120); column.setCellRenderer(new OscarCellRenderers.NomineeRenderer(getTableRowColors())); columnModel.addColumn(column); return columnModel; } //</snip> protected void initSortingFiltering() { //<snip>Setup filtering for winners sorter = new TableRowSorter(oscarModel); oscarTable.setRowSorter(sorter); winnerFilter = new RowFilter<OscarTableModel, Integer>() { public boolean include(Entry<? extends OscarTableModel, ? extends Integer> entry) { OscarTableModel oscarModel = entry.getModel(); OscarCandidate candidate = oscarModel.getCandidate(entry.getIdentifier().intValue()); if (candidate.isWinner()) { // Returning true indicates this row should be shown. return true; } // loser return false; } }; //</snip> //<snip>Setup search filter searchFilter = new RowFilter<OscarTableModel, Integer>() { public boolean include(Entry<? extends OscarTableModel, ? extends Integer> entry) { OscarTableModel oscarModel = entry.getModel(); OscarCandidate candidate = oscarModel.getCandidate(entry.getIdentifier().intValue()); boolean matches = false; Pattern p = Pattern.compile(filterString + ".*", Pattern.CASE_INSENSITIVE); String movie = candidate.getMovieTitle(); if (movie != null) { if (movie.startsWith("The ")) { movie = movie.replace("The ", ""); } else if (movie.startsWith("A ")) { movie = movie.replace("A ", ""); } // Returning true indicates this row should be shown. matches = p.matcher(movie).matches(); } List<String> persons = candidate.getPersons(); for (String person : persons) { if (p.matcher(person).matches()) { matches = true; } } return matches; } }; //</snip> } public void setShowOnlyWinners(boolean showOnlyWinners) { boolean oldShowOnlyWinners = this.showOnlyWinners; this.showOnlyWinners = showOnlyWinners; configureFilters(); tableStatus.setText(statusLabelString + oscarTable.getRowCount()); firePropertyChange("showOnlyWinners", oldShowOnlyWinners, showOnlyWinners); } public boolean getShowOnlyWinners() { return showOnlyWinners; } public void setFilterString(String filterString) { String oldFilterString = this.filterString; this.filterString = filterString; configureFilters(); firePropertyChange("filterString", oldFilterString, filterString); } protected boolean hasFilterString() { return filterString != null && !filterString.equals(""); } protected void configureFilters() { if (showOnlyWinners && hasFilterString()) { List<RowFilter<OscarTableModel, Integer>> filters = new ArrayList<RowFilter<OscarTableModel, Integer>>(2); filters.add(winnerFilter); filters.add(searchFilter); // RowFilter<? super Object, ? super Object> comboFilter = RowFilter.andFilter((Iterable<? extends RowFilter<? super Object, ? super Object>>) filters); sorter.setRowFilter(RowFilter.andFilter(filters)); } else if (showOnlyWinners) { sorter.setRowFilter(winnerFilter); } else if (hasFilterString()) { sorter.setRowFilter(searchFilter); } else { sorter.setRowFilter(null); } tableStatus.setText((hasFilterString() ? searchLabelString : statusLabelString) + oscarTable.getRowCount()); } private class ShowWinnersListener implements ChangeListener { public void stateChanged(ChangeEvent event) { setShowOnlyWinners(winnersCheckbox.isSelected()); } } //<snip>Setup search filter protected class SearchFilterListener implements DocumentListener { protected void changeFilter(DocumentEvent event) { Document document = event.getDocument(); try { setFilterString(document.getText(0, document.getLength())); } catch (Exception ex) { ex.printStackTrace(); System.err.println(ex); } } public void changedUpdate(DocumentEvent e) { changeFilter(e); } public void insertUpdate(DocumentEvent e) { changeFilter(e); } public void removeUpdate(DocumentEvent e) { changeFilter(e); } } //</snip> //<snip>Use SwingWorker to asynchronously load the data public void loadData(String dataPath) { // create SwingWorker which will load the data on a separate thread OscarDataLoader loader = new OscarDataLoader( TableDemo.class.getResource(dataPath), oscarModel); actionStatus.setText(getString("TableDemo.loadingStatusLabel", "Loading data: ")); // display progress bar while data loads final JProgressBar progressBar = new JProgressBar(); statusBarLeft.add(progressBar); loader.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName().equals("progress")) { int progress = ((Integer) event.getNewValue()).intValue(); progressBar.setValue(progress); if (progress == 100) { statusBarLeft.remove(progressBar); actionStatus.setText(""); revalidate(); } } } }); loader.execute(); } //</snip> protected void showMessage(String title, String message) { JOptionPane.showMessageDialog(this, message, title, JOptionPane.INFORMATION_MESSAGE); } //<snip>Use SwingWorker to asynchronously load the data private class OscarDataLoader extends javax.swing.SwingWorker<List<OscarCandidate>, OscarCandidate> { private final URL oscarData; private final OscarTableModel oscarModel; private final List<OscarCandidate> candidates = new ArrayList<OscarCandidate>(); private JLabel credits; private OscarDataLoader(URL oscarURL, OscarTableModel oscarTableModel) { this.oscarData = oscarURL; this.oscarModel = oscarTableModel; } @Override public List<OscarCandidate> doInBackground() { OscarDataParser parser = new OscarDataParser() { @Override protected void addCandidate(OscarCandidate candidate) { candidates.add(candidate); if (candidates.size() % 3 == 0) { try { // slow it down so we can see progress :-) Thread.sleep(1); } catch (Exception ex) { } } publish(candidate); setProgress(100 * candidates.size() / 8545); } }; parser.parseDocument(oscarData); return candidates; } // for OS X older Java 6 /* protected void process(List<OscarCandidate>... moreCandidates) { for(List<OscarCandidate> newCandidates: moreCandidates) { oscarModel.add(newCandidates); } }*/ //@Override protected void process(List<OscarCandidate> moreCandidates) { if (credits == null) { showCredits(); } oscarModel.add(moreCandidates); } // For older Java 6 on OS X protected void process(OscarCandidate... moreCandidates) { for (OscarCandidate candidate : moreCandidates) { oscarModel.add(candidate); } } private void showCredits() { credits = new JLabel(getString("TableDemo.credits", "<html><p align=\"center\">Academy Award data<br>courtesy of Howard Katz</p></html>")); credits.setFont(UIManager.getFont("Table.font").deriveFont(24f)); credits.setHorizontalAlignment(JLabel.CENTER); credits.setBorder(new CompoundBorder(new TitledBorder(""), new EmptyBorder(20,20,20,20))); dataPanel.showMessageLayer(credits, .75f); } @Override protected void done() { setProgress(100); dataPanel.hideMessageLayer(); } } //</snip> private class IMDBLinkAction extends AbstractAction { public void actionPerformed(ActionEvent event) { int row = ((Integer) event.getSource()).intValue(); OscarCandidate candidate = oscarModel.getCandidate(oscarTable.convertRowIndexToModel(row)); try { URI imdbURI = candidate.getIMDBMovieURI(); if (imdbURI == null) { String imdbString = IMDBLink.getMovieURIString(candidate.getMovieTitle(), candidate.getYear()); if (imdbString != null) { imdbURI = new URI(imdbString); candidate.setIMDBMovieURI(imdbURI); } } if (imdbURI != null) { DemoUtilities.browse(imdbURI); } else { showMessage("IMDB Link", getString("TableDemo.imdbLinkNotFound", "Unable to locate IMDB URL for") + "\n" + candidate.getMovieTitle()); } } catch (Exception ex) { ex.printStackTrace(); } } } public static void main(String args[]) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame frame = new JFrame("JTable Demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); TableDemo demo = new TableDemo(); frame.add(demo); frame.setSize(700, 400); frame.setVisible(true); demo.start(); } }); } }