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 /** 036 * Constructs a new {@code SearchTextResultListPanel}. 037 */ 038 public SearchTextResultListPanel() { 039 super(new BorderLayout()); 040 041 edSearchText = new JosmTextField(); 042 edSearchText.getDocument().addDocumentListener(new DocumentListener() { 043 @Override 044 public void removeUpdate(DocumentEvent e) { 045 filterItems(); 046 } 047 048 @Override 049 public void insertUpdate(DocumentEvent e) { 050 filterItems(); 051 } 052 053 @Override 054 public void changedUpdate(DocumentEvent e) { 055 filterItems(); 056 } 057 }); 058 edSearchText.addKeyListener(new KeyAdapter() { 059 @Override 060 public void keyPressed(KeyEvent e) { 061 switch (e.getKeyCode()) { 062 case KeyEvent.VK_DOWN: 063 selectItem(lsResult.getSelectedIndex() + 1); 064 break; 065 case KeyEvent.VK_UP: 066 selectItem(lsResult.getSelectedIndex() - 1); 067 break; 068 case KeyEvent.VK_PAGE_DOWN: 069 selectItem(lsResult.getSelectedIndex() + 10); 070 break; 071 case KeyEvent.VK_PAGE_UP: 072 selectItem(lsResult.getSelectedIndex() - 10); 073 break; 074 case KeyEvent.VK_HOME: 075 selectItem(0); 076 break; 077 case KeyEvent.VK_END: 078 selectItem(lsResultModel.getSize()); 079 break; 080 default: // Do nothing 081 } 082 } 083 }); 084 add(edSearchText, BorderLayout.NORTH); 085 086 lsResult = new JList<>(lsResultModel); 087 lsResult.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 088 lsResult.addMouseListener(new MouseAdapter() { 089 @Override 090 public void mouseClicked(MouseEvent e) { 091 if (e.getClickCount() > 1) { 092 if (dblClickListener != null) 093 dblClickListener.actionPerformed(null); 094 } else { 095 if (clickListener != null) 096 clickListener.actionPerformed(null); 097 } 098 } 099 }); 100 add(new JScrollPane(lsResult), BorderLayout.CENTER); 101 } 102 103 protected static class ResultListModel<T> extends AbstractListModel<T> { 104 105 private transient List<T> items = new ArrayList<>(); 106 107 public synchronized void setItems(List<T> items) { 108 this.items = items; 109 fireContentsChanged(this, 0, Integer.MAX_VALUE); 110 } 111 112 @Override 113 public synchronized T getElementAt(int index) { 114 return items.get(index); 115 } 116 117 @Override 118 public synchronized int getSize() { 119 return items.size(); 120 } 121 122 public synchronized boolean isEmpty() { 123 return items.isEmpty(); 124 } 125 } 126 127 public synchronized void init() { 128 listSelectionListeners.clear(); 129 edSearchText.setText(""); 130 filterItems(); 131 } 132 133 private synchronized void selectItem(int newIndex) { 134 if (newIndex < 0) { 135 newIndex = 0; 136 } 137 if (newIndex > lsResultModel.getSize() - 1) { 138 newIndex = lsResultModel.getSize() - 1; 139 } 140 lsResult.setSelectedIndex(newIndex); 141 lsResult.ensureIndexIsVisible(newIndex); 142 } 143 144 public synchronized void clearSelection() { 145 lsResult.clearSelection(); 146 } 147 148 public synchronized int getItemCount() { 149 return lsResultModel.getSize(); 150 } 151 152 public void setDblClickListener(ActionListener dblClickListener) { 153 this.dblClickListener = dblClickListener; 154 } 155 156 public void setClickListener(ActionListener clickListener) { 157 this.clickListener = clickListener; 158 } 159 160 /** 161 * Adds a selection listener to the presets list. 162 * 163 * @param selectListener The list selection listener 164 * @since 7412 165 */ 166 public synchronized void addSelectionListener(ListSelectionListener selectListener) { 167 lsResult.getSelectionModel().addListSelectionListener(selectListener); 168 listSelectionListeners.add(selectListener); 169 } 170 171 /** 172 * Removes a selection listener from the presets list. 173 * 174 * @param selectListener The list selection listener 175 * @since 7412 176 */ 177 public synchronized void removeSelectionListener(ListSelectionListener selectListener) { 178 listSelectionListeners.remove(selectListener); 179 lsResult.getSelectionModel().removeListSelectionListener(selectListener); 180 } 181}