blob: 86deeac7974316f0730b0177a2dd879ba449d2d3 [file] [log] [blame]
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.swingset3.demos.table;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
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;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.UIManager;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;
import javax.swing.text.Document;
import com.sun.swingset3.DemoProperties;
import com.sun.swingset3.demos.DemoUtilities;
/**
*
* @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());
public static final String DEMO_TITLE = TableDemo.class.getAnnotation(DemoProperties.class).value();
public static final int ROW_HEIGHT = 26;
public static final String COLUMN1_NAME = "Year";
public static final String COLUMN2_NAME = "Award Category";
public static final String COLUMN3_NAME = "Movie Title";
public static final String COLUMN4_NAME = "Nominees";
private OscarTableModel oscarModel;
private JPanel controlPanel;
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(ROW_HEIGHT);
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);
add(scrollpane, 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(COLUMN1_NAME);
column.setPreferredWidth(26);
column.setCellRenderer(new OscarCellRenderers.YearRenderer(getTableRowColors()));
columnModel.addColumn(column);
column = new TableColumn();
column.setModelIndex(OscarTableModel.CATEGORY_COLUMN);
column.setHeaderValue(COLUMN2_NAME);
column.setPreferredWidth(100);
column.setCellRenderer(cellRenderer);
columnModel.addColumn(column);
column = new TableColumn();
column.setModelIndex(OscarTableModel.MOVIE_COLUMN);
column.setHeaderValue(COLUMN3_NAME);
column.setPreferredWidth(180);
column.setCellRenderer(cellRenderer);
columnModel.addColumn(column);
column = new TableColumn();
column.setModelIndex(OscarTableModel.PERSONS_COLUMN);
column.setHeaderValue(COLUMN4_NAME);
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<OscarTableModel, Integer> comboFilter = RowFilter.andFilter(filters);
sorter.setRowFilter(comboFilter);
} 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() / 524);
}
};
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)));
}
@Override
protected void done() {
setProgress(100);
}
}
//</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();
}
});
}
}