dk.dma.ais.virtualnet.transponder.gui.SelectTargetList.java Source code

Java tutorial

Introduction

Here is the source code for dk.dma.ais.virtualnet.transponder.gui.SelectTargetList.java

Source

/* 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();
        }
    }
}