// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.widgets; import java.awt.BorderLayout; 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.util.ArrayList; import java.util.List; import javax.swing.AbstractListModel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionListener; public abstract class SearchTextResultListPanel<T> extends JPanel { protected final JosmTextField edSearchText; protected final JList<T> lsResult; protected final ResultListModel<T> lsResultModel = new ResultListModel<>(); protected final transient List<ListSelectionListener> listSelectionListeners = new ArrayList<>(); private transient ActionListener dblClickListener; private transient ActionListener clickListener; protected abstract void filterItems(); /** * Constructs a new {@code SearchTextResultListPanel}. */ public SearchTextResultListPanel() { super(new BorderLayout()); edSearchText = new JosmTextField(); edSearchText.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { filterItems(); } @Override public void insertUpdate(DocumentEvent e) { filterItems(); } @Override public void changedUpdate(DocumentEvent e) { filterItems(); } }); edSearchText.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_DOWN: selectItem(lsResult.getSelectedIndex() + 1); break; case KeyEvent.VK_UP: selectItem(lsResult.getSelectedIndex() - 1); break; case KeyEvent.VK_PAGE_DOWN: selectItem(lsResult.getSelectedIndex() + 10); break; case KeyEvent.VK_PAGE_UP: selectItem(lsResult.getSelectedIndex() - 10); break; case KeyEvent.VK_HOME: selectItem(0); break; case KeyEvent.VK_END: selectItem(lsResultModel.getSize()); break; default: // Do nothing } } }); add(edSearchText, BorderLayout.NORTH); lsResult = new JList<>(lsResultModel); lsResult.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); lsResult.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() > 1) { if (dblClickListener != null) dblClickListener.actionPerformed(null); } else { if (clickListener != null) clickListener.actionPerformed(null); } } }); add(new JScrollPane(lsResult), BorderLayout.CENTER); } protected static class ResultListModel<T> extends AbstractListModel<T> { private transient List<T> items = new ArrayList<>(); public synchronized void setItems(List<T> items) { this.items = items; fireContentsChanged(this, 0, Integer.MAX_VALUE); } @Override public synchronized T getElementAt(int index) { return items.get(index); } @Override public synchronized int getSize() { return items.size(); } public synchronized boolean isEmpty() { return items.isEmpty(); } } public synchronized void init() { listSelectionListeners.clear(); edSearchText.setText(""); filterItems(); } private synchronized void selectItem(int newIndex) { if (newIndex < 0) { newIndex = 0; } if (newIndex > lsResultModel.getSize() - 1) { newIndex = lsResultModel.getSize() - 1; } lsResult.setSelectedIndex(newIndex); lsResult.ensureIndexIsVisible(newIndex); } public synchronized void clearSelection() { lsResult.clearSelection(); } public synchronized int getItemCount() { return lsResultModel.getSize(); } public void setDblClickListener(ActionListener dblClickListener) { this.dblClickListener = dblClickListener; } public void setClickListener(ActionListener clickListener) { this.clickListener = clickListener; } /** * Adds a selection listener to the presets list. * * @param selectListener The list selection listener * @since 7412 */ public synchronized void addSelectionListener(ListSelectionListener selectListener) { lsResult.getSelectionModel().addListSelectionListener(selectListener); listSelectionListeners.add(selectListener); } /** * Removes a selection listener from the presets list. * * @param selectListener The list selection listener * @since 7412 */ public synchronized void removeSelectionListener(ListSelectionListener selectListener) { listSelectionListeners.remove(selectListener); lsResult.getSelectionModel().removeListSelectionListener(selectListener); } }