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.util.MessageManager;
26 import java.awt.Color;
27 import java.awt.Dimension;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.KeyEvent;
31 import java.awt.event.KeyListener;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.LinkedHashSet;
36 import java.util.List;
39 import javax.swing.BorderFactory;
40 import javax.swing.JComboBox;
41 import javax.swing.JLabel;
42 import javax.swing.JMenuItem;
43 import javax.swing.JPanel;
44 import javax.swing.JPopupMenu;
45 import javax.swing.JTextField;
46 import javax.swing.SwingUtilities;
47 import javax.swing.text.AttributeSet;
48 import javax.swing.text.BadLocationException;
49 import javax.swing.text.PlainDocument;
51 public class JvCacheableInputBox<E> extends JComboBox<String>
54 private static final long serialVersionUID = 5774610435079326695L;
56 private static final int INPUT_LIMIT = 2;
58 private static final int LEFT_BOARDER_WIDTH = 16;
60 private String cacheKey;
62 private AppCache appCache;
64 private JPanel pnlDefaultCache = new JPanel();
66 private JLabel lblDefaultCacheSize = new JLabel();
68 private JTextField txtDefaultCacheSize = new JTextField();
70 private JPopupMenu popup = new JPopupMenu();
72 private JMenuItem menuItemClearCache = new JMenuItem();
74 volatile boolean enterWasPressed = false;
77 * @return flag indicating if the most recent keypress was enter
79 public boolean wasEnterPressed()
81 return enterWasPressed;
84 public JvCacheableInputBox(String newCacheKey)
87 this.cacheKey = newCacheKey;
89 addKeyListener(new KeyListener()
93 public void keyTyped(KeyEvent e)
95 enterWasPressed = false;
96 if (e.getKeyCode() == KeyEvent.VK_ENTER)
98 enterWasPressed = true;
100 // let event bubble up
104 public void keyReleased(KeyEvent e)
106 // TODO Auto-generated method stub
111 public void keyPressed(KeyEvent e)
113 // TODO Auto-generated method stub
117 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
127 * the 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<String>();
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 pnlDefaultCache.setBackground(Color.WHITE);
163 // pad panel so as to align with other menu items
164 pnlDefaultCache.setBorder(
165 BorderFactory.createEmptyBorder(0, LEFT_BOARDER_WIDTH, 0, 0));
166 txtDefaultCacheSize.setPreferredSize(new Dimension(45, 20));
167 txtDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
169 .setText(MessageManager.getString("label.default_cache_size"));
170 lblDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
171 // Force input to accept only Integer entries up to length - INPUT_LIMIT
172 txtDefaultCacheSize.setDocument(new PlainDocument()
174 private static final long serialVersionUID = 1L;
177 public void insertString(int offs, String str, AttributeSet a)
178 throws BadLocationException
180 if (getLength() + str.length() <= INPUT_LIMIT && isInteger(str))
182 super.insertString(offs, str, a);
186 txtDefaultCacheSize.addKeyListener(new java.awt.event.KeyAdapter()
189 public void keyPressed(KeyEvent e)
191 if (e.getKeyCode() == KeyEvent.VK_ENTER)
200 txtDefaultCacheSize.setText(appCache.getCacheLimit(cacheKey));
201 pnlDefaultCache.add(lblDefaultCacheSize);
202 menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
203 pnlDefaultCache.add(txtDefaultCacheSize);
205 .setText(MessageManager.getString("action.clear_cached_items"));
206 menuItemClearCache.addActionListener(new ActionListener()
209 public void actionPerformed(ActionEvent e)
211 // System.out.println(">>>>> Clear cache items");
213 appCache.deleteCacheItems(cacheKey);
218 popup.insert(pnlDefaultCache, 0);
219 popup.add(menuItemClearCache);
220 setComponentPopupMenu(popup);
224 private void closePopup()
226 popup.setVisible(false);
227 popup.transferFocus();
231 * Answers true if input text is an integer
236 static boolean isInteger(String text)
240 Integer.parseInt(text);
242 } catch (NumberFormatException e)
249 * Method called to update the cache with the last user input
251 public void updateCache()
253 SwingUtilities.invokeLater(new Runnable()
258 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty()
259 ? Integer.valueOf(AppCache.DEFAULT_LIMIT)
260 : Integer.valueOf(txtDefaultCacheSize.getText());
261 int cacheLimit = appCache.updateCacheLimit(cacheKey, userLimit);
262 String userInput = getUserInput();
263 if (userInput != null && !userInput.isEmpty())
265 LinkedHashSet<String> foundCache = appCache
266 .getAllCachedItemsFor(cacheKey);
267 // remove old cache item so as to place current input at the top of
269 foundCache.remove(userInput);
270 foundCache.add(userInput);
271 appCache.putCache(cacheKey, foundCache);
274 String lastSearch = userInput;
275 if (getItemCount() > 0)
279 Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
280 List<String> reversedCacheItems = new ArrayList<String>();
281 reversedCacheItems.addAll(cacheItems);
283 Collections.reverse(reversedCacheItems);
284 if (lastSearch.isEmpty())
289 if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
291 LinkedHashSet<String> foundCache = appCache
292 .getAllCachedItemsFor(cacheKey);
293 boolean prune = reversedCacheItems.size() > cacheLimit;
295 boolean limitExceeded = false;
296 for (String cacheItem : reversedCacheItems)
298 limitExceeded = (count++ > cacheLimit);
303 foundCache.remove(cacheItem);
315 appCache.putCache(cacheKey, foundCache);
317 setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
323 * This method should be called to persist the in-memory cache when this
324 * components parent frame is closed / exited
326 public void persistCache()
328 appCache.persistCache(cacheKey);
329 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty()
330 ? Integer.valueOf(AppCache.DEFAULT_LIMIT)
331 : Integer.valueOf(txtDefaultCacheSize.getText());
332 appCache.updateCacheLimit(cacheKey, userLimit);
336 * Method to obtain input text from the cache box
340 public String getUserInput()
342 return getEditor().getItem() == null ? ""
343 : getEditor().getItem().toString().trim();