Java tutorial
/* Copyright (c) 2011 Danish Maritime Authority. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dk.dma.ais.virtualnet.transponder.gui; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractListModel; import javax.swing.JList; import javax.swing.JTextField; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.Timer; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.lang.StringUtils; import dk.dma.ais.virtualnet.common.table.TargetTableEntry; /** * This class defines a list of {@linkplain TargetTableEntry} elements * that can be filtered using a text field returned by * {@linkplain #getFilterField()} */ public class SelectTargetList extends JList<TargetTableEntry> { private static final long serialVersionUID = 1L; private static final int DEFAULT_FIELD_WIDTH = 20; private TargetFilterField filterField; /** * Constructor */ public SelectTargetList() { super(); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); setLayoutOrientation(JList.VERTICAL); setModel(new TargetFilterModel()); filterField = new TargetFilterField(DEFAULT_FIELD_WIDTH); } /** * Sets the list model, ensuring that it is of type {@code FilterModel} * @param model the list mode to set */ @Override public void setModel(ListModel<TargetTableEntry> model) { if (!(model instanceof TargetFilterModel)) { throw new IllegalArgumentException(); } super.setModel(model); } /** * Returns the list model cast as a {@code FilterModel} * @return the list model cast as a {@code FilterModel} */ public TargetFilterModel getFilterModel() { return (TargetFilterModel) getModel(); } /** * Adds a new target to the list * @param target the target to add */ public void addTarget(TargetTableEntry target) { getFilterModel().addTarget(target); } /** * Returns a reference to the associated {@code FilterField} * @return a reference to the associated {@code FilterField} */ public JTextField getFilterField() { return filterField; } /** * Sets the first target in the list as the selected one, and requests focus */ protected void setlectFirstTarget() { setSelectedIndex(0); requestFocus(); } /** * The {@code TargetFilterModel} class. */ class TargetFilterModel extends AbstractListModel<TargetTableEntry> implements ActionListener { private static final long serialVersionUID = 1L; private static final int FILTER_UPDATE_DELAY = 300; List<TargetTableEntry> targets; List<TargetTableEntry> filterTargets; Timer timer = new Timer(FILTER_UPDATE_DELAY, this); /** * Constructor */ public TargetFilterModel() { super(); timer.setRepeats(false); targets = new ArrayList<>(); filterTargets = new ArrayList<>(); } /** * Returns the target at the given index * @param index the index * @return the target at the given index */ @Override public TargetTableEntry getElementAt(int index) { if (index < filterTargets.size()) { return filterTargets.get(index); } else { return null; } } /** * Returns the number of target in the filtered list * @return the number of target in the filtered list */ @Override public int getSize() { return filterTargets.size(); } /** * Adds a new element to the list * @param target the element to add */ public void addTarget(TargetTableEntry target) { targets.add(target); refilter(); } /** * Schedules a timed update of the list */ private void refilter() { timer.restart(); } /** * Called when the timer has timed out. * Updates the filtered list of targets based on the * text in the filter text field. * @param e the action event */ @Override public void actionPerformed(ActionEvent e) { String term = getFilterField().getText().toLowerCase(); filterTargets.clear(); for (TargetTableEntry target : targets) { if (StringUtils.containsIgnoreCase(target.getName(), term) || StringUtils.containsIgnoreCase(Integer.toString(target.getMmsi()), term)) { filterTargets.add(target); } } fireContentsChanged(this, 0, getSize()); } } /** * inner class provides filter-by-keystroke field */ class TargetFilterField extends JTextField implements DocumentListener { private static final long serialVersionUID = 1L; private String hint = "Filter name/mmsi"; /** * Constructor * @param width */ public TargetFilterField(int width) { super(width); getDocument().addDocumentListener(this); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { if (showClearButton() && getClearRect().contains(e.getPoint())) { setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); } } }); addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (showClearButton() && getClearRect().contains(e.getPoint())) { setText(""); } } }); // If the user presses the down key, transfer focus to the // first target in the list addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_DOWN) { setlectFirstTarget(); } } }); } /** * Returns the hint text to display in an empty text field * @return the hint text to display in an empty text field */ public String getHint() { return hint; } /** * Sets the hint text to display in an empty text field * @param hint the hint text to display in an empty text field */ public void setHint(String hint) { this.hint = hint; } /** * Computes the rectangle to display a clear button in * @return the rectangle to display a clear button in */ Rectangle getClearRect() { int padding = 6; int size = getHeight() - 2 * padding; return new Rectangle(getWidth() - size - padding, padding, size, size); } /** * Returns whether to display the clear text button or not * @return whether to display the clear text button or not */ boolean showClearButton() { return getText().trim().length() > 0; } /** * {@inheritDoc} */ @Override public void paint(Graphics g) { super.paint(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Paint a hint text if the field is blank if (getText().trim().length() == 0 && !hasFocus()) { FontMetrics fm = g.getFontMetrics(); Rectangle2D rect = fm.getStringBounds(hint, g); g2.setColor(getForeground()); g2.drawString(hint, 8, (getHeight() - (int) rect.getHeight()) / 2 + fm.getAscent()); } // Draw a clear text button if there is text present if (showClearButton()) { Rectangle r = getClearRect(); g2.setColor(Color.lightGray); g2.fillArc((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight(), 0, 360); g2.setColor(getBackground()); g2.setStroke(new BasicStroke(2)); g2.draw(new Line2D.Double(r.getCenterX() - 3, r.getCenterY() - 3, r.getCenterX() + 2, r.getCenterY() + 2)); g2.draw(new Line2D.Double(r.getCenterX() + 2, r.getCenterY() - 3, r.getCenterX() - 3, r.getCenterY() + 2)); } } /** * {@inheritDoc} */ @Override public void changedUpdate(DocumentEvent e) { getFilterModel().refilter(); } /** * {@inheritDoc} */ @Override public void insertUpdate(DocumentEvent e) { getFilterModel().refilter(); } /** * {@inheritDoc} */ @Override public void removeUpdate(DocumentEvent e) { getFilterModel().refilter(); } } }