1/* 2 * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23package com.sun.swingset3.demos.textfield; 24 25import java.awt.*; 26import java.awt.event.*; 27import java.util.*; 28import java.util.List; 29import javax.swing.*; 30import javax.swing.border.LineBorder; 31import javax.swing.event.DocumentEvent; 32import javax.swing.event.DocumentListener; 33 34/** 35 * JHistoryTextField 36 * 37 * @author Pavel Porvatov 38 */ 39public class JHistoryTextField extends JTextField { 40 41 private static final int MAX_VISIBLE_ROWS = 8; 42 43 private final List<String> history = new ArrayList<String>(); 44 45 private final JPopupMenu popup = new JPopupMenu() { 46 @Override 47 public Dimension getPreferredSize() { 48 Dimension dimension = super.getPreferredSize(); 49 50 dimension.width = JHistoryTextField.this.getWidth(); 51 52 return dimension; 53 } 54 }; 55 56 private final JList<String> list = new JList<>(new DefaultListModel<>()); 57 58 private String userText; 59 60 private boolean notificationDenied; 61 62 public JHistoryTextField() { 63 JScrollPane scrollPane = new JScrollPane(list, 64 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 65 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 66 scrollPane.setHorizontalScrollBar(null); 67 scrollPane.setBorder(null); 68 69 list.setFocusable(false); 70 71 popup.add(scrollPane); 72 popup.setFocusable(false); 73 popup.setBorder(new LineBorder(Color.BLACK, 1)); 74 75 getDocument().addDocumentListener(new DocumentListener() { 76 @Override 77 public void insertUpdate(DocumentEvent e) { 78 onTextChanged(); 79 } 80 81 @Override 82 public void removeUpdate(DocumentEvent e) { 83 onTextChanged(); 84 } 85 86 @Override 87 public void changedUpdate(DocumentEvent e) { 88 onTextChanged(); 89 } 90 }); 91 92 list.addMouseMotionListener(new MouseAdapter() { 93 @Override 94 public void mouseMoved(MouseEvent e) { 95 int index = list.locationToIndex(e.getPoint()); 96 97 if (index >= 0 && list.getSelectedIndex() != index) { 98 list.setSelectedIndex(index); 99 } 100 } 101 }); 102 103 list.addMouseListener(new MouseAdapter() { 104 @Override 105 public void mouseReleased(MouseEvent e) { 106 if (SwingUtilities.isLeftMouseButton(e)) { 107 setTextWithoutNotification(list.getSelectedValue()); 108 109 popup.setVisible(false); 110 } 111 } 112 }); 113 114 addFocusListener(new FocusAdapter() { 115 @Override 116 public void focusLost(FocusEvent e) { 117 popup.setVisible(false); 118 } 119 }); 120 121 addKeyListener(new KeyAdapter() { 122 @Override 123 public void keyPressed(KeyEvent e) { 124 if (popup.isShowing()) { 125 switch (e.getKeyCode()) { 126 case KeyEvent.VK_UP: { 127 changeListSelectedIndex(-1); 128 129 break; 130 } 131 132 case KeyEvent.VK_PAGE_UP: { 133 changeListSelectedIndex(-list.getVisibleRowCount()); 134 135 break; 136 } 137 138 case KeyEvent.VK_DOWN: { 139 changeListSelectedIndex(1); 140 141 break; 142 } 143 144 case KeyEvent.VK_PAGE_DOWN: { 145 changeListSelectedIndex(list.getVisibleRowCount()); 146 147 break; 148 } 149 150 case KeyEvent.VK_ESCAPE: { 151 popup.setVisible(false); 152 153 setTextWithoutNotification(userText); 154 155 break; 156 } 157 158 case KeyEvent.VK_ENTER: 159 case KeyEvent.VK_LEFT: 160 case KeyEvent.VK_RIGHT: { 161 popup.setVisible(false); 162 163 break; 164 } 165 } 166 } else if (e.getKeyCode() == KeyEvent.VK_DOWN 167 || e.getKeyCode() == KeyEvent.VK_UP 168 || e.getKeyCode() == KeyEvent.VK_PAGE_UP 169 || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) { 170 userText = getText(); 171 172 showFilteredHistory(); 173 } 174 } 175 }); 176 } 177 178 private void changeListSelectedIndex(int delta) { 179 int size = list.getModel().getSize(); 180 int index = list.getSelectedIndex(); 181 182 int newIndex; 183 184 if (index < 0) { 185 newIndex = delta > 0 ? 0 : size - 1; 186 } else { 187 newIndex = index + delta; 188 } 189 190 if (newIndex >= size || newIndex < 0) { 191 newIndex = newIndex < 0 ? 0 : size - 1; 192 193 if (index == newIndex) { 194 newIndex = -1; 195 } 196 } 197 198 if (newIndex < 0) { 199 list.getSelectionModel().clearSelection(); 200 list.ensureIndexIsVisible(0); 201 202 setTextWithoutNotification(userText); 203 } else { 204 list.setSelectedIndex(newIndex); 205 list.ensureIndexIsVisible(newIndex); 206 207 setTextWithoutNotification(list.getSelectedValue()); 208 } 209 } 210 211 private void setTextWithoutNotification(String text) { 212 notificationDenied = true; 213 214 try { 215 setText(text); 216 } finally { 217 notificationDenied = false; 218 } 219 } 220 221 private void onTextChanged() { 222 if (!notificationDenied) { 223 userText = getText(); 224 225 showFilteredHistory(); 226 } 227 } 228 229 private void showFilteredHistory() { 230 list.getSelectionModel().clearSelection(); 231 232 DefaultListModel<String> model = (DefaultListModel<String>) list.getModel(); 233 234 model.clear(); 235 236 for (String s : history) { 237 if (s.contains(userText)) { 238 model.addElement(s); 239 } 240 } 241 242 int size = model.size(); 243 244 if (size == 0) { 245 popup.setVisible(false); 246 } else { 247 list.setVisibleRowCount(size < MAX_VISIBLE_ROWS ? size : MAX_VISIBLE_ROWS); 248 249 popup.pack(); 250 251 if (!popup.isShowing()) { 252 popup.show(JHistoryTextField.this, 0, getHeight()); 253 } 254 } 255 } 256 257 public List<String> getHistory() { 258 return Collections.unmodifiableList(history); 259 } 260 261 public void setHistory(List<? extends String> history) { 262 this.history.clear(); 263 this.history.addAll(history); 264 } 265} 266