adds protype size to JTextArea hack for nonexistent SwingJS editable
[jalview.git] / src / jalview / io / cache / JvCacheableInputBox.java
index 2cf1678..934ba9e 100644 (file)
@@ -1,31 +1,69 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.io.cache;
 
+import jalview.bin.Cache;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
-import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
-import javax.swing.BorderFactory;
 import javax.swing.JComboBox;
-import javax.swing.JLabel;
+import javax.swing.JComponent;
 import javax.swing.JMenuItem;
-import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JTextField;
 import javax.swing.SwingUtilities;
-import javax.swing.text.AttributeSet;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.PlainDocument;
+import javax.swing.event.CaretListener;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.JTextComponent;
 
-public class JvCacheableInputBox<E> extends JComboBox<String>
+/**
+ * A class that provides an editable combobox with a memory of previous entries
+ * that may be persisted
+ * 
+ * @author tcofoegbu
+ *
+ * @param <E>
+ */
+/*
+ * (temporary?) patches to wrap a JTextField instead when running as Javascript
+ */
+public class JvCacheableInputBox<E>
 {
+  private JComboBox<String> comboBox; // used for Jalview
+
+  private JTextField textField; // used for JalviewJS
 
   private static final long serialVersionUID = 5774610435079326695L;
 
@@ -33,63 +71,110 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
 
   private AppCache appCache;
 
-  JPanel pnlDefaultCache = new JPanel();
-
-  JLabel lblDefaultCacheSize = new JLabel();
+  private JPopupMenu popup = new JPopupMenu();
 
-  JTextField txtDefaultCacheSize = new JTextField();
+  private JMenuItem menuItemClearCache = new JMenuItem();
 
-  JPopupMenu popup = new JPopupMenu();
+  volatile boolean enterWasPressed = false;
 
-  JMenuItem menuItemClearCache = new JMenuItem();
+private String prototypeDisplayValue;
 
-  final static int INPUT_LIMIT = 2;
+private boolean isJS;
 
-
-  public JvCacheableInputBox(String cacheKey)
+  /**
+   * @return flag indicating if the most recent keypress was enter
+   */
+  public boolean wasEnterPressed()
   {
-    super();
-    this.cacheKey = cacheKey;
-    setEditable(true);
-    setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
-    appCache = AppCache.getInstance();
-    initCachePopupMenu();
-    appCache.initCache(cacheKey);
-    updateCache();
+    return enterWasPressed;
   }
 
-
   /**
-   * Initialise this cache's pop-up menu
+   * Constructor
+   * 
+   * @param newCacheKey
    */
-  private void initCachePopupMenu()
+  public JvCacheableInputBox(String newCacheKey)
   {
-    pnlDefaultCache.setBackground(Color.WHITE);
-    // pad panel so as to align with other menu items
-    pnlDefaultCache.setBorder(BorderFactory.createEmptyBorder(0, 19, 0, 0));
-    txtDefaultCacheSize.setPreferredSize(new Dimension(45, 20));
-    lblDefaultCacheSize.setText(MessageManager
-            .getString("label.default_cache_size"));
-    // Force input to accept only Integer entries up to length - INPUT_LIMIT
-    txtDefaultCacheSize.setDocument(new PlainDocument()
+    super();
+    isJS = true;//Platform.isJS();
+    this.prototypeDisplayValue = 
+            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
+    if (isJS)
     {
-      private static final long serialVersionUID = 1L;
+      textField = new JTextField() {
+         public Dimension getPreferredSize() {
+                 return super.getPreferredSize();
+//                FontMetrics fm = getFontMetrics(getFont());
+//                     return new Dimension(fm.stringWidth(prototypeDisplayValue), fm.getHeight());
+         }
+      };
+      return;
+    }
 
+    this.cacheKey = newCacheKey;
+    comboBox = new JComboBox<String>();
+    comboBox.setEditable(true);
+    comboBox.addKeyListener(new KeyAdapter()
+    {
       @Override
-      public void insertString(int offs, String str, AttributeSet a)
-              throws BadLocationException
+      public void keyTyped(KeyEvent e)
       {
-        if (getLength() + str.length() <= INPUT_LIMIT && isInteger(str))
+        enterWasPressed = false;
+        if (e.getKeyCode() == KeyEvent.VK_ENTER)
         {
-          super.insertString(offs, str, a);
+          enterWasPressed = true;
         }
+        // let event bubble up
       }
     });
-    txtDefaultCacheSize.setText(appCache.getCacheLmit(cacheKey));
-    pnlDefaultCache.add(lblDefaultCacheSize);
-    pnlDefaultCache.add(txtDefaultCacheSize);
-    menuItemClearCache.setText(MessageManager
-            .getString("action.clear_cached_items"));
+    comboBox.setPrototypeDisplayValue(prototypeDisplayValue);
+    appCache = AppCache.getInstance();
+    initCachePopupMenu();
+    initCache(newCacheKey);
+    updateCache();
+  }
+
+  /**
+   * Method for initialising cache items for a given cache key and populating the
+   * in-memory cache with persisted cache items
+   * 
+   * @param cacheKey
+   */
+  private void initCache(String cacheKey)
+  {
+    // obtain persisted cache items from properties file as a delimited string
+    String delimitedCacheStr = Cache.getProperty(cacheKey);
+    if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
+    {
+      return;
+    }
+    // convert delimited cache items to a list of strings
+    List<String> persistedCacheItems = Arrays
+            .asList(delimitedCacheStr.split(AppCache.CACHE_DELIMITER));
+
+    LinkedHashSet<String> foundCacheItems = appCache
+            .getAllCachedItemsFor(cacheKey);
+    if (foundCacheItems == null)
+    {
+      foundCacheItems = new LinkedHashSet<>();
+    }
+    // populate memory cache
+    for (String cacheItem : persistedCacheItems)
+    {
+      foundCacheItems.add(cacheItem);
+    }
+    appCache.putCache(cacheKey, foundCacheItems);
+  }
+
+  /**
+   * Initialise this cache's pop-up menu
+   */
+  private void initCachePopupMenu()
+  {
+    menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
+    menuItemClearCache
+            .setText(MessageManager.getString("action.clear_cached_items"));
     menuItemClearCache.addActionListener(new ActionListener()
     {
       @Override
@@ -102,10 +187,9 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
       }
     });
 
-    popup.insert(pnlDefaultCache, 0);
     popup.add(menuItemClearCache);
-    setComponentPopupMenu(popup);
-    add(popup);
+    comboBox.setComponentPopupMenu(popup);
+    comboBox.add(popup);
   }
 
   /**
@@ -131,13 +215,16 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
    */
   public void updateCache()
   {
+    if (isJS)
+    {
+      return;
+    }
     SwingUtilities.invokeLater(new Runnable()
     {
       @Override
       public void run()
       {
-        int cacheLimit = appCache.updateCacheLimit(cacheKey,
-                txtDefaultCacheSize.getText());
+        int cacheLimit = Integer.parseInt(appCache.getCacheLimit(cacheKey));
         String userInput = getUserInput();
         if (userInput != null && !userInput.isEmpty())
         {
@@ -151,18 +238,18 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
         }
 
         String lastSearch = userInput;
-        if (getItemCount() > 0)
+        if (comboBox.getItemCount() > 0)
         {
-          removeAllItems();
+          comboBox.removeAllItems();
         }
         Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
-        List<String> reversedCacheItems = new ArrayList<String>();
+        List<String> reversedCacheItems = new ArrayList<>();
         reversedCacheItems.addAll(cacheItems);
         cacheItems = null;
         Collections.reverse(reversedCacheItems);
         if (lastSearch.isEmpty())
         {
-          addItem("");
+          comboBox.addItem("");
         }
 
         if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
@@ -183,12 +270,12 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
               }
               else
               {
-                addItem(cacheItem);
+                comboBox.addItem(cacheItem);
               }
             }
             else
             {
-              addItem(cacheItem);
+              comboBox.addItem(cacheItem);
             }
           }
           appCache.putCache(cacheKey, foundCache);
@@ -198,26 +285,128 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
     });
   }
 
-
   /**
    * This method should be called to persist the in-memory cache when this
    * components parent frame is closed / exited
    */
   public void persistCache()
   {
-    appCache.persistCache(cacheKey);
-    appCache.updateCacheLimit(cacheKey, txtDefaultCacheSize.getText());
+    if (!isJS)
+    {
+      appCache.persistCache(cacheKey);
+    }
   }
 
   /**
-   * Method to obtain input text from the cache box
+   * Returns the trimmed text in the input field
    * 
    * @return
    */
   public String getUserInput()
   {
-    return getEditor().getItem() == null ? "" : getEditor().getItem()
-            .toString().trim();
+    if (isJS)
+    {
+      return textField.getText().trim();
+    }
+    Object item = comboBox.getEditor().getItem();
+    return item == null ? "" : item.toString().trim();
+  }
+
+  public JComponent getComponent()
+  {
+    return isJS ? textField : comboBox;
+  }
+
+  public void addActionListener(ActionListener actionListener)
+  {
+    if (isJS)
+    {
+      textField.addActionListener(actionListener);
+    }
+    else
+    {
+      comboBox.addActionListener(actionListener);
+    }
+  }
+
+  public void addDocumentListener(DocumentListener listener)
+  {
+    if (!isJS)
+    {
+      ((JTextComponent) comboBox.getEditor().getEditorComponent())
+              .getDocument().addDocumentListener(listener);
+    }
+  }
+
+  public void addFocusListener(FocusListener focusListener)
+  {
+    if (isJS)
+    {
+      textField.addFocusListener(focusListener);
+    }
+    else
+    {
+      comboBox.addFocusListener(focusListener);
+    }
+  }
+
+  public void addKeyListener(KeyListener kl)
+  {
+    if (!isJS)
+    {
+      comboBox.getEditor().getEditorComponent().addKeyListener(kl);
+    }
+  }
+
+  public void setEditable(boolean b)
+  {
+    if (!isJS)
+    {
+      comboBox.setEditable(b);
+    }
+  }
+
+  public void setPrototypeDisplayValue(String string)
+  {
+       this.prototypeDisplayValue = string;
+    if (!isJS)
+    {
+      comboBox.setPrototypeDisplayValue(string);
+    } 
+  }
+
+  public void setSelectedItem(String userInput)
+  {
+    if (!isJS)
+    {
+      comboBox.setSelectedItem(userInput);
+    }
+  }
+
+  public boolean isPopupVisible()
+  {
+    if (!isJS)
+    {
+      return comboBox.isPopupVisible();
+    }
+    return false;
+  }
+
+  public void addCaretListener(CaretListener caretListener)
+  {
+    if (!isJS)
+    {
+      ((JTextComponent) comboBox.getEditor().getEditorComponent())
+              .addCaretListener(caretListener);
+    }
+  }
+
+  public void addItem(String item)
+  {
+    if (!isJS)
+    {
+      comboBox.addItem(item);
+    }
   }
 
 }