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}