2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.io.cache;
23 import jalview.bin.Cache;
24 import jalview.bin.Jalview;
25 import jalview.util.MessageManager;
27 import java.awt.event.ActionEvent;
28 import java.awt.event.ActionListener;
29 import java.awt.event.FocusListener;
30 import java.awt.event.KeyAdapter;
31 import java.awt.event.KeyEvent;
32 import java.awt.event.KeyListener;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.LinkedHashSet;
37 import java.util.List;
40 import javax.swing.JComboBox;
41 import javax.swing.JComponent;
42 import javax.swing.JMenuItem;
43 import javax.swing.JPopupMenu;
44 import javax.swing.JTextField;
45 import javax.swing.SwingUtilities;
46 import javax.swing.event.CaretListener;
47 import javax.swing.event.DocumentListener;
48 import javax.swing.text.JTextComponent;
51 * A class that provides an editable combobox with a memory of previous entries
52 * that may be persisted
59 * (temporary?) patches to wrap a JTextField instead when running as Javascript
61 public class JvCacheableInputBox<E>
63 private JComboBox<String> comboBox; // used for Jalview
65 private JTextField textField; // used for JalviewJS
67 private static final long serialVersionUID = 5774610435079326695L;
69 private String cacheKey;
71 private AppCache appCache;
73 private JPopupMenu popup = new JPopupMenu();
75 private JMenuItem menuItemClearCache = new JMenuItem();
77 volatile boolean enterWasPressed = false;
80 * @return flag indicating if the most recent keypress was enter
82 public boolean wasEnterPressed()
84 return enterWasPressed;
92 public JvCacheableInputBox(String newCacheKey)
97 textField = new JTextField();
101 this.cacheKey = newCacheKey;
102 comboBox = new JComboBox<String>();
103 comboBox.setEditable(true);
104 comboBox.addKeyListener(new KeyAdapter()
107 public void keyTyped(KeyEvent e)
109 enterWasPressed = false;
110 if (e.getKeyCode() == KeyEvent.VK_ENTER)
112 enterWasPressed = true;
114 // let event bubble up
117 comboBox.setPrototypeDisplayValue(
118 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
119 appCache = AppCache.getInstance();
120 initCachePopupMenu();
121 initCache(newCacheKey);
126 * Method for initialising cache items for a given cache key and populating the
127 * in-memory cache with persisted cache items
131 private void initCache(String cacheKey)
133 // obtain persisted cache items from properties file as a delimited string
134 String delimitedCacheStr = Cache.getProperty(cacheKey);
135 if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
139 // convert delimited cache items to a list of strings
140 List<String> persistedCacheItems = Arrays
141 .asList(delimitedCacheStr.split(AppCache.CACHE_DELIMITER));
143 LinkedHashSet<String> foundCacheItems = appCache
144 .getAllCachedItemsFor(cacheKey);
145 if (foundCacheItems == null)
147 foundCacheItems = new LinkedHashSet<>();
149 // populate memory cache
150 for (String cacheItem : persistedCacheItems)
152 foundCacheItems.add(cacheItem);
154 appCache.putCache(cacheKey, foundCacheItems);
158 * Initialise this cache's pop-up menu
160 private void initCachePopupMenu()
162 menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
164 .setText(MessageManager.getString("action.clear_cached_items"));
165 menuItemClearCache.addActionListener(new ActionListener()
168 public void actionPerformed(ActionEvent e)
170 // System.out.println(">>>>> Clear cache items");
172 appCache.deleteCacheItems(cacheKey);
177 popup.add(menuItemClearCache);
178 comboBox.setComponentPopupMenu(popup);
183 * Answers true if input text is an integer
188 static boolean isInteger(String text)
192 Integer.parseInt(text);
194 } catch (NumberFormatException e)
201 * Method called to update the cache with the last user input
203 public void updateCache()
209 SwingUtilities.invokeLater(new Runnable()
214 int cacheLimit = Integer.parseInt(appCache.getCacheLimit(cacheKey));
215 String userInput = getUserInput();
216 if (userInput != null && !userInput.isEmpty())
218 LinkedHashSet<String> foundCache = appCache
219 .getAllCachedItemsFor(cacheKey);
220 // remove old cache item so as to place current input at the top of
222 foundCache.remove(userInput);
223 foundCache.add(userInput);
224 appCache.putCache(cacheKey, foundCache);
227 String lastSearch = userInput;
228 if (comboBox.getItemCount() > 0)
230 comboBox.removeAllItems();
232 Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
233 List<String> reversedCacheItems = new ArrayList<>();
234 reversedCacheItems.addAll(cacheItems);
236 Collections.reverse(reversedCacheItems);
237 if (lastSearch.isEmpty())
239 comboBox.addItem("");
242 if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
244 LinkedHashSet<String> foundCache = appCache
245 .getAllCachedItemsFor(cacheKey);
246 boolean prune = reversedCacheItems.size() > cacheLimit;
248 boolean limitExceeded = false;
249 for (String cacheItem : reversedCacheItems)
251 limitExceeded = (count++ > cacheLimit);
256 foundCache.remove(cacheItem);
260 comboBox.addItem(cacheItem);
265 comboBox.addItem(cacheItem);
268 appCache.putCache(cacheKey, foundCache);
270 setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
276 * This method should be called to persist the in-memory cache when this
277 * components parent frame is closed / exited
279 public void persistCache()
283 appCache.persistCache(cacheKey);
288 * Returns the trimmed text in the input field
292 public String getUserInput()
296 return textField.getText().trim();
298 Object item = comboBox.getEditor().getItem();
299 return item == null ? "" : item.toString().trim();
302 public JComponent getComponent()
304 return Jalview.isJS() ? textField : comboBox;
307 public void addActionListener(ActionListener actionListener)
311 comboBox.addActionListener(actionListener);
315 public void addDocumentListener(DocumentListener listener)
319 ((JTextComponent) comboBox.getEditor().getEditorComponent())
320 .getDocument().addDocumentListener(listener);
324 public void addFocusListener(FocusListener focusListener)
328 comboBox.addFocusListener(focusListener);
332 public void addKeyListener(KeyListener kl)
336 comboBox.getEditor().getEditorComponent().addKeyListener(kl);
340 public void setEditable(boolean b)
344 comboBox.setEditable(b);
348 public void setPrototypeDisplayValue(String string)
352 comboBox.setPrototypeDisplayValue(string);
356 public void setSelectedItem(String userInput)
360 comboBox.setSelectedItem(userInput);
364 public boolean isPopupVisible()
368 return comboBox.isPopupVisible();
373 public void addCaretListener(CaretListener caretListener)
377 ((JTextComponent) comboBox.getEditor().getEditorComponent())
378 .addCaretListener(caretListener);
382 public void addItem(String item)
386 comboBox.addItem(item);