001/** 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 * Created on Sep 14, 2011 021 * Created by Andreas Prlic 022 * 023 * @since 3.0.2 024 */ 025package org.biojava.nbio.structure.align.gui.autosuggest; 026 027import javax.swing.*; 028import java.awt.*; 029import java.awt.event.*; 030import java.util.Vector; 031import java.util.concurrent.atomic.AtomicBoolean; 032 033 034 035 036/** A JTextField that can make suggestions for auto-complete. 037 * 038 * @author Andreas Prlic 039 * 040 */ 041public class JAutoSuggest extends JTextField{ 042 043 /** 044 * 045 */ 046 private static final long serialVersionUID = 8591734727984365156L; 047 048 private static final String DEFAULT_TEXT= "Please enter text ..."; 049 050 String defaultText; 051 private JDialog dialog; 052 private Point location; 053 private JList list; 054 055 private Vector<String> suggestions; 056 057 /** last word that was entered by user */ 058 private String lastWord ; 059 060 AutoSuggestProvider autoSuggestProvider; 061 062 Font regular; 063 Font busy; 064 065 SuggestionFetcher matcher; 066 067 public JAutoSuggest(){ 068 super(); 069 070 init(); 071 } 072 073 public JAutoSuggest(int size){ 074 super(size); 075 init(); 076 } 077 078 public JAutoSuggest(Frame owner){ 079 owner.addComponentListener(new ComponentListener() { 080 @Override 081 public void componentShown(ComponentEvent e) { 082 updateLocation(); 083 } 084 085 @Override 086 public void componentResized(ComponentEvent e) { 087 updateLocation(); 088 } 089 090 @Override 091 public void componentMoved(ComponentEvent e) { 092 updateLocation(); 093 } 094 095 @Override 096 public void componentHidden(ComponentEvent e) { 097 updateLocation(); 098 } 099 }); 100 owner.addWindowListener(new WindowListener() { 101 @Override 102 public void windowOpened(WindowEvent e) { 103 } 104 105 @Override 106 public void windowIconified(WindowEvent e) { 107 dialog.setVisible(false); 108 } 109 110 @Override 111 public void windowDeiconified(WindowEvent e) { 112 } 113 114 @Override 115 public void windowDeactivated(WindowEvent e) { 116 } 117 118 @Override 119 public void windowClosing(WindowEvent e) { 120 dialog.dispose(); 121 } 122 123 @Override 124 public void windowClosed(WindowEvent e) { 125 dialog.dispose(); 126 } 127 128 @Override 129 public void windowActivated(WindowEvent e) { 130 } 131 }); 132 addFocusListener(new FocusListener() { 133 @Override 134 public void focusLost(FocusEvent e) { 135 System.out.println("Lost Focus"); 136 dialog.setVisible(false); 137 138 if (getText().trim().equals("") && e.getOppositeComponent() != null && e.getOppositeComponent().getName() != null) { 139 if (!e.getOppositeComponent().getName().equals("suggestFieldDropdownButton")) { 140 setText(defaultText); 141 } 142 } else if (getText().trim().equals("")) { 143 setText(defaultText); 144 } 145 } 146 147 @Override 148 public void focusGained(FocusEvent e) { 149 System.out.println("Lost Gained"); 150 if (getText().trim().equals(defaultText)) { 151 setText(""); 152 } 153 154 showSuggest(); 155 } 156 }); 157 158 159 160 161 init(); 162 163 // set dialog owner... 164 //dialog.setD 165 166 } 167 168 169 private void initSuggestionList() { 170 list = new JList(); 171 list.addMouseListener(new MouseListener() { 172 private int selected; 173 174 @Override 175 public void mousePressed(MouseEvent e) { 176 } 177 178 @Override 179 public void mouseReleased(MouseEvent e) { 180 if (selected == list.getSelectedIndex()) { 181 // provide double-click for selecting a suggestion 182 setText((String) list.getSelectedValue()); 183 184 dialog.setVisible(false); 185 } 186 selected = list.getSelectedIndex(); 187 } 188 189 @Override 190 public void mouseExited(MouseEvent e) { 191 } 192 193 @Override 194 public void mouseEntered(MouseEvent e) { 195 } 196 197 @Override 198 public void mouseClicked(MouseEvent e) { 199 } 200 }); 201 dialog.add(new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 202 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)); 203 dialog.pack(); 204 addKeyListener(new KeyListener() { 205 @Override 206 public void keyTyped(KeyEvent e) { 207 } 208 209 @Override 210 public void keyPressed(KeyEvent e) { 211 updateLocation(); 212 } 213 214 @Override 215 public void keyReleased(KeyEvent e) { 216 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { 217 dialog.setVisible(false); 218 return; 219 } else if (e.getKeyCode() == KeyEvent.VK_DOWN) { 220 if (dialog.isVisible()) { 221 list.setSelectedIndex(list.getSelectedIndex() + 1); 222 list.ensureIndexIsVisible(list.getSelectedIndex() + 1); 223 return; 224 } else { 225 showSuggest(); 226 } 227 } else if (e.getKeyCode() == KeyEvent.VK_UP) { 228 list.setSelectedIndex(list.getSelectedIndex() - 1); 229 list.ensureIndexIsVisible(list.getSelectedIndex() - 1); 230 return; 231 } else if (e.getKeyCode() == KeyEvent.VK_ENTER 232 && list.getSelectedIndex() != -1 && suggestions.size() > 0) { 233 setText((String) list.getSelectedValue()); 234 235 236 dialog.setVisible(false); 237 return; 238 } 239 showSuggest(); 240 } 241 }); 242 243 } 244 245 private void init(){ 246 autoSuggestProvider = new DefaultAutoSuggestProvider(); 247 lastWord = ""; 248 regular = getFont(); 249 busy = new Font(getFont().getName(), Font.ITALIC, getFont().getSize()); 250 suggestions = new Vector<String>(); 251 defaultText = DEFAULT_TEXT; 252 253 254 dialog = new JDialog(); 255 dialog.setUndecorated(true); 256 dialog.setFocusableWindowState(false); 257 dialog.setFocusable(false); 258 259 initSuggestionList(); 260 } 261 262 public String getDefaultText() { 263 return defaultText; 264 } 265 266 public void setDefaultText(String defaultText) { 267 this.defaultText = defaultText; 268 } 269 270 public AutoSuggestProvider getAutoSuggestProvider() { 271 return autoSuggestProvider; 272 } 273 274 public void setAutoSuggestProvider(AutoSuggestProvider autoSuggestProvider) { 275 this.autoSuggestProvider = autoSuggestProvider; 276 } 277 278 /** 279 * Force the suggestions to be displayed (Useful for buttons 280 * e.g. for using JSuggestionField like a ComboBox) 281 */ 282 public void showSuggest() { 283 284 assert(getText() != null); 285 lastWord = getText().trim(); 286 //autoSuggestProvider.getSuggestion(lastWord); 287 288 289 290 if (!getText().toLowerCase().contains(lastWord.toLowerCase())) { 291 suggestions.clear(); 292 } 293 294 if (matcher != null) { 295 matcher.setStop(); 296 } 297 matcher = new SuggestionFetcher(); 298 299 //SwingUtilities.invokeLater(matcher); 300 matcher.execute(); 301 lastWord = getText().trim(); 302 updateLocation(); 303 } 304 305 /** 306 * Force the suggestions to be hidden (Useful for buttons, e.g. to use 307 * JSuggestionField like a ComboBox) 308 */ 309 public void hideSuggest() { 310 dialog.setVisible(false); 311 } 312 313 /** 314 * @return boolean Visibility of the suggestion window 315 */ 316 public boolean isSuggestVisible() { 317 return dialog.isVisible(); 318 } 319 320 321 322 /** 323 * Place the suggestion window under the JTextField. 324 */ 325 private void updateLocation() { 326 try { 327 location = getLocationOnScreen(); 328 location.y += getHeight(); 329 dialog.setLocation(location); 330 } catch (IllegalComponentStateException e) { 331 return; // might happen on window creation 332 } 333 } 334 335 336 /** fetch suggestions from SuggestionProvider 337 * 338 * 339 * 340 */ 341 private class SuggestionFetcher extends SwingWorker<String, Object> { 342 /** flag used to stop the thread */ 343 private AtomicBoolean stop = new AtomicBoolean(false); 344 345 String previousWord; 346 /** 347 * Standard run method used in threads 348 * responsible for the actual search 349 */ 350 @Override 351 public String doInBackground() { 352 try { 353 setFont(busy); 354 String userInput = getText().trim(); 355 if ( userInput == null || userInput.equals("")) 356 return ""; 357 358 if ( previousWord != null){ 359 if ( userInput.equals(previousWord)) 360 return ""; 361 } 362 previousWord = userInput; 363 364 suggestions = autoSuggestProvider.getSuggestion(userInput); 365 366 setFont(regular); 367 368 if (suggestions.size() > 0) { 369 list.setListData(suggestions); 370 list.setSelectedIndex(0); 371 list.ensureIndexIsVisible(0); 372 dialog.setVisible(true); 373 } else { 374 dialog.setVisible(false); 375 } 376 } catch (Exception e) { 377 //e.printStackTrace(); 378 // ignore... 379 380 } 381 return "Done."; 382 } 383 384 public void setStop(){ 385 stop.set(true); 386 } 387 388 389 } 390}