001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.widgets;
003
004import java.awt.BorderLayout;
005import java.awt.event.ActionListener;
006import java.awt.event.KeyAdapter;
007import java.awt.event.KeyEvent;
008import java.awt.event.MouseAdapter;
009import java.awt.event.MouseEvent;
010import java.util.ArrayList;
011import java.util.List;
012
013import javax.swing.AbstractListModel;
014import javax.swing.JList;
015import javax.swing.JPanel;
016import javax.swing.JScrollPane;
017import javax.swing.ListSelectionModel;
018import javax.swing.event.DocumentEvent;
019import javax.swing.event.DocumentListener;
020import javax.swing.event.ListSelectionListener;
021
022public abstract class SearchTextResultListPanel<T> extends JPanel {
023
024    protected final JosmTextField edSearchText;
025    protected final JList<T> lsResult;
026    protected final ResultListModel<T> lsResultModel = new ResultListModel<>();
027
028    protected final transient List<ListSelectionListener> listSelectionListeners = new ArrayList<>();
029
030    private transient ActionListener dblClickListener;
031    private transient ActionListener clickListener;
032
033    protected abstract void filterItems();
034
035    public SearchTextResultListPanel() {
036        super(new BorderLayout());
037
038        edSearchText = new JosmTextField();
039        edSearchText.getDocument().addDocumentListener(new DocumentListener() {
040            @Override
041            public void removeUpdate(DocumentEvent e) {
042                filterItems();
043            }
044
045            @Override
046            public void insertUpdate(DocumentEvent e) {
047                filterItems();
048            }
049
050            @Override
051            public void changedUpdate(DocumentEvent e) {
052                filterItems();
053            }
054        });
055        edSearchText.addKeyListener(new KeyAdapter() {
056            @Override
057            public void keyPressed(KeyEvent e) {
058                switch (e.getKeyCode()) {
059                    case KeyEvent.VK_DOWN:
060                        selectItem(lsResult.getSelectedIndex() + 1);
061                        break;
062                    case KeyEvent.VK_UP:
063                        selectItem(lsResult.getSelectedIndex() - 1);
064                        break;
065                    case KeyEvent.VK_PAGE_DOWN:
066                        selectItem(lsResult.getSelectedIndex() + 10);
067                        break;
068                    case KeyEvent.VK_PAGE_UP:
069                        selectItem(lsResult.getSelectedIndex() - 10);
070                        break;
071                    case KeyEvent.VK_HOME:
072                        selectItem(0);
073                        break;
074                    case KeyEvent.VK_END:
075                        selectItem(lsResultModel.getSize());
076                        break;
077                }
078            }
079        });
080        add(edSearchText, BorderLayout.NORTH);
081
082        lsResult = new JList<>(lsResultModel);
083        lsResult.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
084        lsResult.addMouseListener(new MouseAdapter() {
085            @Override
086            public void mouseClicked(MouseEvent e) {
087                if (e.getClickCount() > 1) {
088                    if (dblClickListener != null)
089                        dblClickListener.actionPerformed(null);
090                } else {
091                    if (clickListener != null)
092                        clickListener.actionPerformed(null);
093                }
094            }
095        });
096        add(new JScrollPane(lsResult), BorderLayout.CENTER);
097    }
098
099    protected static class ResultListModel<T> extends AbstractListModel<T> {
100
101        private transient List<T> items = new ArrayList<>();
102
103        public synchronized void setItems(List<T> items) {
104            this.items = items;
105            fireContentsChanged(this, 0, Integer.MAX_VALUE);
106        }
107
108        @Override
109        public synchronized T getElementAt(int index) {
110            return items.get(index);
111        }
112
113        @Override
114        public synchronized int getSize() {
115            return items.size();
116        }
117
118        public synchronized boolean isEmpty() {
119            return items.isEmpty();
120        }
121    }
122
123    public synchronized void init() {
124        listSelectionListeners.clear();
125        edSearchText.setText("");
126        filterItems();
127    }
128
129    private synchronized void selectItem(int newIndex) {
130        if (newIndex < 0) {
131            newIndex = 0;
132        }
133        if (newIndex > lsResultModel.getSize() - 1) {
134            newIndex = lsResultModel.getSize() - 1;
135        }
136        lsResult.setSelectedIndex(newIndex);
137        lsResult.ensureIndexIsVisible(newIndex);
138    }
139
140    public synchronized void clearSelection() {
141        lsResult.clearSelection();
142    }
143
144    public synchronized int getItemCount() {
145        return lsResultModel.getSize();
146    }
147
148    public void setDblClickListener(ActionListener dblClickListener) {
149        this.dblClickListener = dblClickListener;
150    }
151
152    public void setClickListener(ActionListener clickListener) {
153        this.clickListener = clickListener;
154    }
155
156    /**
157     * Adds a selection listener to the presets list.
158     *
159     * @param selectListener The list selection listener
160     * @since 7412
161     */
162    public synchronized void addSelectionListener(ListSelectionListener selectListener) {
163        lsResult.getSelectionModel().addListSelectionListener(selectListener);
164        listSelectionListeners.add(selectListener);
165    }
166
167    /**
168     * Removes a selection listener from the presets list.
169     *
170     * @param selectListener The list selection listener
171     * @since 7412
172     */
173    public synchronized void removeSelectionListener(ListSelectionListener selectListener) {
174        listSelectionListeners.remove(selectListener);
175        lsResult.getSelectionModel().removeListSelectionListener(selectListener);
176    }
177}