[9347] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
| 2 | package org.openstreetmap.josm.gui.widgets;
|
---|
| 3 |
|
---|
[9372] | 4 | import java.awt.BorderLayout;
|
---|
[9347] | 5 | import java.awt.event.ActionListener;
|
---|
| 6 | import java.awt.event.KeyAdapter;
|
---|
| 7 | import java.awt.event.KeyEvent;
|
---|
| 8 | import java.awt.event.MouseAdapter;
|
---|
| 9 | import java.awt.event.MouseEvent;
|
---|
[9372] | 10 | import java.util.ArrayList;
|
---|
[9347] | 11 | import java.util.List;
|
---|
| 12 |
|
---|
[9372] | 13 | import javax.swing.AbstractListModel;
|
---|
| 14 | import javax.swing.JList;
|
---|
| 15 | import javax.swing.JPanel;
|
---|
| 16 | import javax.swing.JScrollPane;
|
---|
[9390] | 17 | import javax.swing.ListSelectionModel;
|
---|
[9372] | 18 | import javax.swing.event.DocumentEvent;
|
---|
| 19 | import javax.swing.event.DocumentListener;
|
---|
| 20 | import javax.swing.event.ListSelectionListener;
|
---|
| 21 |
|
---|
[12304] | 22 | /**
|
---|
| 23 | * A panel containing a search text field and a list of results for that search text.
|
---|
| 24 | * @param <T> The class of the things that are searched
|
---|
| 25 | */
|
---|
[9347] | 26 | public abstract class SearchTextResultListPanel<T> extends JPanel {
|
---|
| 27 |
|
---|
| 28 | protected final JosmTextField edSearchText;
|
---|
| 29 | protected final JList<T> lsResult;
|
---|
| 30 | protected final ResultListModel<T> lsResultModel = new ResultListModel<>();
|
---|
| 31 |
|
---|
| 32 | protected final transient List<ListSelectionListener> listSelectionListeners = new ArrayList<>();
|
---|
| 33 |
|
---|
| 34 | private transient ActionListener dblClickListener;
|
---|
| 35 | private transient ActionListener clickListener;
|
---|
| 36 |
|
---|
| 37 | protected abstract void filterItems();
|
---|
| 38 |
|
---|
[10217] | 39 | /**
|
---|
| 40 | * Constructs a new {@code SearchTextResultListPanel}.
|
---|
| 41 | */
|
---|
[9347] | 42 | public SearchTextResultListPanel() {
|
---|
| 43 | super(new BorderLayout());
|
---|
| 44 |
|
---|
| 45 | edSearchText = new JosmTextField();
|
---|
| 46 | edSearchText.getDocument().addDocumentListener(new DocumentListener() {
|
---|
| 47 | @Override
|
---|
| 48 | public void removeUpdate(DocumentEvent e) {
|
---|
| 49 | filterItems();
|
---|
| 50 | }
|
---|
| 51 |
|
---|
| 52 | @Override
|
---|
| 53 | public void insertUpdate(DocumentEvent e) {
|
---|
| 54 | filterItems();
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | @Override
|
---|
| 58 | public void changedUpdate(DocumentEvent e) {
|
---|
| 59 | filterItems();
|
---|
| 60 | }
|
---|
| 61 | });
|
---|
| 62 | edSearchText.addKeyListener(new KeyAdapter() {
|
---|
| 63 | @Override
|
---|
| 64 | public void keyPressed(KeyEvent e) {
|
---|
| 65 | switch (e.getKeyCode()) {
|
---|
| 66 | case KeyEvent.VK_DOWN:
|
---|
| 67 | selectItem(lsResult.getSelectedIndex() + 1);
|
---|
| 68 | break;
|
---|
| 69 | case KeyEvent.VK_UP:
|
---|
| 70 | selectItem(lsResult.getSelectedIndex() - 1);
|
---|
| 71 | break;
|
---|
| 72 | case KeyEvent.VK_PAGE_DOWN:
|
---|
| 73 | selectItem(lsResult.getSelectedIndex() + 10);
|
---|
| 74 | break;
|
---|
| 75 | case KeyEvent.VK_PAGE_UP:
|
---|
| 76 | selectItem(lsResult.getSelectedIndex() - 10);
|
---|
| 77 | break;
|
---|
| 78 | case KeyEvent.VK_HOME:
|
---|
| 79 | selectItem(0);
|
---|
| 80 | break;
|
---|
| 81 | case KeyEvent.VK_END:
|
---|
| 82 | selectItem(lsResultModel.getSize());
|
---|
| 83 | break;
|
---|
[10217] | 84 | default: // Do nothing
|
---|
[9347] | 85 | }
|
---|
| 86 | }
|
---|
| 87 | });
|
---|
| 88 | add(edSearchText, BorderLayout.NORTH);
|
---|
| 89 |
|
---|
| 90 | lsResult = new JList<>(lsResultModel);
|
---|
[9390] | 91 | lsResult.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
---|
[9347] | 92 | lsResult.addMouseListener(new MouseAdapter() {
|
---|
| 93 | @Override
|
---|
| 94 | public void mouseClicked(MouseEvent e) {
|
---|
| 95 | if (e.getClickCount() > 1) {
|
---|
| 96 | if (dblClickListener != null)
|
---|
| 97 | dblClickListener.actionPerformed(null);
|
---|
| 98 | } else {
|
---|
| 99 | if (clickListener != null)
|
---|
| 100 | clickListener.actionPerformed(null);
|
---|
| 101 | }
|
---|
| 102 | }
|
---|
| 103 | });
|
---|
| 104 | add(new JScrollPane(lsResult), BorderLayout.CENTER);
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | protected static class ResultListModel<T> extends AbstractListModel<T> {
|
---|
| 108 |
|
---|
| 109 | private transient List<T> items = new ArrayList<>();
|
---|
| 110 |
|
---|
| 111 | public synchronized void setItems(List<T> items) {
|
---|
| 112 | this.items = items;
|
---|
| 113 | fireContentsChanged(this, 0, Integer.MAX_VALUE);
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 | @Override
|
---|
| 117 | public synchronized T getElementAt(int index) {
|
---|
| 118 | return items.get(index);
|
---|
| 119 | }
|
---|
| 120 |
|
---|
| 121 | @Override
|
---|
| 122 | public synchronized int getSize() {
|
---|
| 123 | return items.size();
|
---|
| 124 | }
|
---|
| 125 |
|
---|
| 126 | public synchronized boolean isEmpty() {
|
---|
| 127 | return items.isEmpty();
|
---|
| 128 | }
|
---|
| 129 | }
|
---|
| 130 |
|
---|
[12304] | 131 | /**
|
---|
| 132 | * Initializes and clears the panel.
|
---|
| 133 | */
|
---|
[9347] | 134 | public synchronized void init() {
|
---|
| 135 | listSelectionListeners.clear();
|
---|
| 136 | edSearchText.setText("");
|
---|
| 137 | filterItems();
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | private synchronized void selectItem(int newIndex) {
|
---|
| 141 | if (newIndex < 0) {
|
---|
| 142 | newIndex = 0;
|
---|
| 143 | }
|
---|
| 144 | if (newIndex > lsResultModel.getSize() - 1) {
|
---|
| 145 | newIndex = lsResultModel.getSize() - 1;
|
---|
| 146 | }
|
---|
| 147 | lsResult.setSelectedIndex(newIndex);
|
---|
| 148 | lsResult.ensureIndexIsVisible(newIndex);
|
---|
| 149 | }
|
---|
| 150 |
|
---|
[12304] | 151 | /**
|
---|
| 152 | * Clear the selected result
|
---|
| 153 | */
|
---|
[9347] | 154 | public synchronized void clearSelection() {
|
---|
| 155 | lsResult.clearSelection();
|
---|
| 156 | }
|
---|
| 157 |
|
---|
[12304] | 158 | /**
|
---|
| 159 | * Get the number of items available
|
---|
| 160 | * @return The number of search result items available
|
---|
| 161 | */
|
---|
[9347] | 162 | public synchronized int getItemCount() {
|
---|
| 163 | return lsResultModel.getSize();
|
---|
| 164 | }
|
---|
| 165 |
|
---|
[12304] | 166 | /**
|
---|
| 167 | * Sets a listener to be invoked on double click
|
---|
| 168 | * @param dblClickListener The double click listener
|
---|
| 169 | */
|
---|
[9347] | 170 | public void setDblClickListener(ActionListener dblClickListener) {
|
---|
| 171 | this.dblClickListener = dblClickListener;
|
---|
| 172 | }
|
---|
| 173 |
|
---|
[12304] | 174 | /**
|
---|
| 175 | * Sets a listener to be invoked on ssingle click
|
---|
| 176 | * @param clickListener The click listener
|
---|
| 177 | */
|
---|
[9347] | 178 | public void setClickListener(ActionListener clickListener) {
|
---|
| 179 | this.clickListener = clickListener;
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | /**
|
---|
| 183 | * Adds a selection listener to the presets list.
|
---|
| 184 | *
|
---|
| 185 | * @param selectListener The list selection listener
|
---|
| 186 | * @since 7412
|
---|
| 187 | */
|
---|
| 188 | public synchronized void addSelectionListener(ListSelectionListener selectListener) {
|
---|
| 189 | lsResult.getSelectionModel().addListSelectionListener(selectListener);
|
---|
| 190 | listSelectionListeners.add(selectListener);
|
---|
| 191 | }
|
---|
| 192 |
|
---|
| 193 | /**
|
---|
| 194 | * Removes a selection listener from the presets list.
|
---|
| 195 | *
|
---|
| 196 | * @param selectListener The list selection listener
|
---|
| 197 | * @since 7412
|
---|
| 198 | */
|
---|
| 199 | public synchronized void removeSelectionListener(ListSelectionListener selectListener) {
|
---|
| 200 | listSelectionListeners.remove(selectListener);
|
---|
| 201 | lsResult.getSelectionModel().removeListSelectionListener(selectListener);
|
---|
| 202 | }
|
---|
| 203 | }
|
---|