OtsSearchPanel.java

package org.opentrafficsim.swing.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.Optional;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import org.djutils.base.Identifiable;
import org.opentrafficsim.core.gtu.Gtu;
import org.opentrafficsim.core.network.Link;
import org.opentrafficsim.core.network.Network;
import org.opentrafficsim.core.network.Node;

import nl.tudelft.simulation.dsol.animation.Locatable;

/**
 * The OTS search panel.
 * <p>
 * Copyright (c) 2020-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
 * </p>
 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
 */
public class OtsSearchPanel extends JPanel implements ActionListener, FocusListener, DocumentListener
{
    /** ... */
    private static final long serialVersionUID = 20200127L;

    /** The animation panel. */
    private final OtsAnimationPanel otsAnimationPanel;

    /** The type-of-object-to-search-for selector. */
    private final JComboBox<ObjectKind<?>> typeToSearch;

    /** Id of the object to search for. */
    private final JTextField idTextField;

    /** Track object check box. */
    private final JCheckBox trackObject;

    /**
     * Construct a new OtsSearchPanel.
     * @param otsAnimationPanel the animation panel
     */
    public OtsSearchPanel(final OtsAnimationPanel otsAnimationPanel)
    {
        this.otsAnimationPanel = otsAnimationPanel;
        this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        this.add(new JLabel(OtsControlPanel.loadIcon("/View.png").get()));
        this.add(Box.createHorizontalStrut(5));
        ObjectKind<?>[] objectKinds = new ObjectKind[] {new ObjectKind<Gtu>("GTU")
        {
            @Override
            Optional<Gtu> searchNetwork(final Network network, final String id)
            {
                return network.getGTU(id);
            }
        }, new ObjectKind<Node>("Node")
        {
            @Override
            Optional<Node> searchNetwork(final Network network, final String id)
            {
                return network.getNode(id);
            }
        }, new ObjectKind<Link>("Link")
        {
            @Override
            Optional<Link> searchNetwork(final Network network, final String id)
            {
                return network.getLink(id);
            }
        }};
        this.typeToSearch = new JComboBox<ObjectKind<?>>(objectKinds);
        this.typeToSearch.setPreferredSize(new Dimension(55, 25));
        this.add(this.typeToSearch);

        /** Text field with appearance control. */
        this.idTextField = new AppearanceControlTextField();
        this.idTextField.setPreferredSize(new Dimension(100, 25));
        this.add(this.idTextField);
        this.trackObject = new JCheckBox("Track");
        this.add(this.trackObject);
        this.trackObject.setActionCommand("Tracking status changed");
        this.idTextField.setActionCommand("Id changed");
        this.typeToSearch.setActionCommand("Type changed");
        this.trackObject.addActionListener(this);
        this.idTextField.addActionListener(this);
        this.typeToSearch.addActionListener(this);
        this.idTextField.addFocusListener(this);
        this.idTextField.getDocument().addDocumentListener(this);
        new GhostText(this.idTextField, "Id...").setGhostColor(Color.GRAY);
    }

    /**
     * Update all values at once.
     * @param objectKey key of the object type to search
     * @param id id of object to search
     * @param track if true; track continuously; if false; center on it, but do not track
     */
    public void selectAndTrackObject(final String objectKey, final String id, final boolean track)
    {
        for (int index = this.typeToSearch.getItemCount(); --index >= 0;)
        {
            if (this.typeToSearch.getItemAt(index).getKey().equals(objectKey))
            {
                this.typeToSearch.setSelectedIndex(index);
            }
        }
        this.trackObject.setSelected(track);
        this.idTextField.setText(id);
        actionPerformed(null);
    }

    @Override
    public void actionPerformed(final ActionEvent e)
    {
        this.otsAnimationPanel.setAutoPan(this.idTextField.getText(), (ObjectKind<?>) this.typeToSearch.getSelectedItem(),
                this.trackObject.isSelected());
    }

    @Override
    public final void focusGained(final FocusEvent e)
    {
        actionPerformed(null);
    }

    @Override
    public final void focusLost(final FocusEvent e)
    {
        // Do nothing
    }

    @Override
    public final void insertUpdate(final DocumentEvent e)
    {
        actionPerformed(null);
    }

    @Override
    public final void removeUpdate(final DocumentEvent e)
    {
        actionPerformed(null);
    }

    @Override
    public final void changedUpdate(final DocumentEvent e)
    {
        actionPerformed(null);
    }

    /**
     * Entries in the typeToSearch JComboBox of the OTS search panel.
     * @param <T> Type of object identified by key
     */
    abstract static class ObjectKind<T extends Locatable & Identifiable>
    {
        /** The key of this ObjectKind. */
        private final String key;

        /**
         * Construct a new ObjectKind (entry in the combo box).
         * @param key the key of the new ObjectKind
         */
        ObjectKind(final String key)
        {
            this.key = key;
        }

        /**
         * Retrieve the key.
         * @return the key
         */
        public Object getKey()
        {
            return this.key;
        }

        /**
         * Lookup an object of type T in an OTS network.
         * @param network the OTS network
         * @param id id of the object to return
         * @return the object in the network of the correct type and matching id, empty if no matching object was found.
         */
        abstract Optional<T> searchNetwork(Network network, String id);

        /**
         * Produce the text that will appear in the combo box. This method should be overridden to implement localization.
         */
        @Override
        public String toString()
        {
            return this.key;
        }
    }
}