JAL-2316 Adjusted unit tests. Tidied UrlLinkProviderI interface.
[jalview.git] / src / jalview / gui / Preferences.java
index be0ac05..d129898 100755 (executable)
  */
 package jalview.gui;
 
-import static jalview.util.UrlConstants.EMBLEBI_STRING;
-import static jalview.util.UrlConstants.SEQUENCE_ID;
-import static jalview.util.UrlConstants.SEQUENCE_NAME;
-import static jalview.util.UrlConstants.SRS_STRING;
-
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.bin.Cache;
 import jalview.gui.Help.HelpId;
@@ -34,12 +29,17 @@ import jalview.io.JalviewFileView;
 import jalview.jbgui.GPreferences;
 import jalview.jbgui.GSequenceLink;
 import jalview.schemes.ColourSchemeProperty;
+import jalview.urls.UrlLinkTableModel;
+import jalview.urls.UrlProvider;
+import jalview.urls.UrlProviderI;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.UrlConstants;
 import jalview.ws.sifts.SiftsSettings;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
+import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
@@ -48,8 +48,6 @@ import java.awt.event.MouseEvent;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.StringTokenizer;
-import java.util.Vector;
 
 import javax.help.HelpSetException;
 import javax.swing.JColorChooser;
@@ -57,6 +55,18 @@ import javax.swing.JFileChooser;
 import javax.swing.JInternalFrame;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.ListSelectionModel;
+import javax.swing.RowFilter;
+import javax.swing.RowSorter;
+import javax.swing.SortOrder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableModel;
+import javax.swing.table.TableRowSorter;
 
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 
@@ -102,7 +112,9 @@ public class Preferences extends GPreferences
    * Holds name and link separated with | character. Sequence ID must be
    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
    */
-  public static Vector<String> sequenceURLLinks;
+  public static UrlProviderI sequenceUrlLinks;
+
+  public static UrlLinkTableModel dataModel;
 
   /**
    * Holds name and link separated with | character. Sequence IDS and Sequences
@@ -115,40 +127,10 @@ public class Preferences extends GPreferences
   public static List<String> groupURLLinks;
   static
   {
-    String string = Cache.getDefault("SEQUENCE_LINKS", EMBLEBI_STRING);
-    sequenceURLLinks = new Vector<String>();
-
-    try
-    {
-      StringTokenizer st = new StringTokenizer(string, "|");
-      while (st.hasMoreElements())
-      {
-        String name = st.nextToken();
-        String url = st.nextToken();
-        // check for '|' within a regex
-        int rxstart = url.indexOf("$" + SEQUENCE_ID + "$");
-        if (rxstart == -1)
-        {
-          rxstart = url.indexOf("$" + SEQUENCE_NAME + "$");
-        }
-        while (rxstart == -1 && url.indexOf("/=$") == -1)
-        {
-          url = url + "|" + st.nextToken();
-        }
-        sequenceURLLinks.addElement(name + "|" + url);
-      }
-    } catch (Exception ex)
-    {
-      System.out.println(ex + "\nError parsing sequence links");
-    }
-    {
-      // upgrade old SRS link
-      int srsPos = sequenceURLLinks.indexOf(SRS_STRING);
-      if (srsPos > -1)
-      {
-        sequenceURLLinks.setElementAt(EMBLEBI_STRING, srsPos);
-      }
-    }
+    String string = Cache.getDefault("SEQUENCE_LINKS",
+            UrlConstants.DEFAULT_STRING);
+    sequenceUrlLinks = new UrlProvider(UrlConstants.DEFAULT_LABEL, string);
+    dataModel = new UrlLinkTableModel(sequenceUrlLinks);
 
     /**
      * TODO: reformulate groupURL encoding so two or more can be stored in the
@@ -158,8 +140,6 @@ public class Preferences extends GPreferences
     groupURLLinks = new ArrayList<String>();
   }
 
-  Vector<String> nameLinks, urlLinks;
-
   JInternalFrame frame;
 
   DasSourceBrowser dasSource;
@@ -343,18 +323,121 @@ public class Preferences extends GPreferences
     /*
      * Set Connections tab defaults
      */
-    nameLinks = new Vector<String>();
-    urlLinks = new Vector<String>();
-    for (int i = 0; i < sequenceURLLinks.size(); i++)
+
+    // set up sorting
+    linkUrlTable.setModel(dataModel);
+    final TableRowSorter<TableModel> sorter = new TableRowSorter<>(
+            linkUrlTable.getModel());
+    linkUrlTable.setRowSorter(sorter);
+    List<RowSorter.SortKey> sortKeys = new ArrayList<>();
+
+    sortKeys.add(new RowSorter.SortKey(3,
+            SortOrder.DESCENDING));
+    sortKeys.add(new RowSorter.SortKey(2, SortOrder.DESCENDING));
+    sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));
+
+    sorter.setSortKeys(sortKeys);
+    sorter.sort();
+    
+    // set up filtering
+    ActionListener onReset;
+    onReset = new ActionListener()
     {
-      String link = sequenceURLLinks.elementAt(i).toString();
-      nameLinks.addElement(link.substring(0, link.indexOf("|")));
-      urlLinks.addElement(link.substring(link.indexOf("|") + 1));
-    }
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        filterTB.setText("");
+        sorter.setRowFilter(RowFilter.regexFilter(""));
+      }
+
+    };
+    doReset.addActionListener(onReset);
+
+    // filter to display only custom urls
+    final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<TableModel, Object>()
+    {
+      @Override
+      public boolean include(
+              Entry<? extends TableModel, ? extends Object> entry)
+      {
+        String id = entry.getStringValue(4);
+        return sequenceUrlLinks.isUserEntry(id);
+      }
+    };
 
-    updateLinkData();
+    final TableRowSorter<TableModel> customSorter = new TableRowSorter<>(
+            linkUrlTable.getModel());
+    customSorter.setRowFilter(customUrlFilter);
+
+    ActionListener onCustomOnly;
+    onCustomOnly = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        filterTB.setText("");
+        sorter.setRowFilter(customUrlFilter);
+      }
+    };
+    userOnly.addActionListener(onCustomOnly);
+
+    filterTB.getDocument().addDocumentListener(new DocumentListener()
+    {
+      @Override
+      public void changedUpdate(DocumentEvent e)
+      {
+        sorter.setRowFilter(RowFilter.regexFilter("(?i)"
+                + filterTB.getText()));
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e)
+      {
+        sorter.setRowFilter(RowFilter.regexFilter("(?i)"
+                + filterTB.getText()));
+      }
+
+      @Override
+      public void insertUpdate(DocumentEvent e)
+      {
+        sorter.setRowFilter(RowFilter.regexFilter("(?i)"
+                + filterTB.getText()));
+      }
+    });
+
+    // set up list selection functionality
+    linkUrlTable.getSelectionModel().addListSelectionListener(
+            new UrlListSelectionHandler());
+
+    // set up radio buttons
+    linkUrlTable.getColumn("Default").setCellRenderer(
+            new RadioButtonRenderer());
+    linkUrlTable.getColumn("Default")
+            .setCellEditor(new RadioButtonEditor());
+
+    // get boolean columns and resize those to min possible
+    for (int column = 0; column < linkUrlTable.getColumnCount(); column++)
+    {
+      if (linkUrlTable.getModel().getColumnClass(column)
+              .equals(Boolean.class))
+      {
+        TableColumn tableColumn = linkUrlTable.getColumnModel().getColumn(
+                column);
+        int preferredWidth = tableColumn.getMinWidth();
+
+        TableCellRenderer cellRenderer = linkUrlTable.getCellRenderer(0,
+                column);
+        Component c = linkUrlTable.prepareRenderer(cellRenderer, 0, column);
+        int cwidth = c.getPreferredSize().width
+                + linkUrlTable.getIntercellSpacing().width;
+        preferredWidth = Math.max(preferredWidth, cwidth);
+
+        tableColumn.setPreferredWidth(preferredWidth);
+      }
+    }
 
     useProxy.setSelected(Cache.getDefault("USE_PROXY", false));
+    useProxy_actionPerformed(); // make sure useProxy is correctly initialised
     proxyServerTB.setEnabled(useProxy.isSelected());
     proxyPortTB.setEnabled(useProxy.isSelected());
     proxyServerTB.setText(Cache.getDefault("PROXY_SERVER", ""));
@@ -548,25 +631,16 @@ public class Preferences extends GPreferences
 
     jalview.util.BrowserLauncher.resetBrowser();
 
-    if (nameLinks.size() > 0)
+    // save user-defined and selected links
+    String links = linkUrlTable.getModel().toString();
+    if (links.isEmpty())
     {
-      StringBuffer links = new StringBuffer();
-      sequenceURLLinks = new Vector<String>();
-      for (int i = 0; i < nameLinks.size(); i++)
-      {
-        sequenceURLLinks.addElement(nameLinks.elementAt(i) + "|"
-                + urlLinks.elementAt(i));
-        links.append(sequenceURLLinks.elementAt(i).toString());
-        links.append("|");
-      }
-      // remove last "|"
-      links.setLength(links.length() - 1);
-      Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
-              links.toString());
+      Cache.applicationProperties.remove("SEQUENCE_LINKS");
     }
     else
     {
-      Cache.applicationProperties.remove("SEQUENCE_LINKS");
+      Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
+              links.toString());
     }
 
     Cache.applicationProperties.setProperty("USE_PROXY",
@@ -748,7 +822,6 @@ public class Preferences extends GPreferences
   @Override
   public void newLink_actionPerformed(ActionEvent e)
   {
-
     GSequenceLink link = new GSequenceLink();
     boolean valid = false;
     while (!valid)
@@ -759,9 +832,8 @@ public class Preferences extends GPreferences
       {
         if (link.checkValid())
         {
-          nameLinks.addElement(link.getName());
-          urlLinks.addElement(link.getURL());
-          updateLinkData();
+          ((UrlLinkTableModel) linkUrlTable.getModel()).insertRow(
+                  link.getName(), link.getURL());
           valid = true;
         }
       }
@@ -777,9 +849,10 @@ public class Preferences extends GPreferences
   {
     GSequenceLink link = new GSequenceLink();
 
-    int index = linkNameList.getSelectedIndex();
+    int index = linkUrlTable.getSelectedRow();
     if (index == -1)
     {
+      // no row was selected
       JOptionPane.showInternalMessageDialog(Desktop.desktop,
               MessageManager.getString("label.no_link_selected"),
               MessageManager.getString("label.no_link_selected"),
@@ -787,26 +860,23 @@ public class Preferences extends GPreferences
       return;
     }
 
-    link.setName(nameLinks.elementAt(index).toString());
-    link.setURL(urlLinks.elementAt(index).toString());
+    link.setName(linkUrlTable.getValueAt(index, 0).toString());
+    link.setURL(linkUrlTable.getValueAt(index, 1).toString());
 
     boolean valid = false;
     while (!valid)
     {
-
       if (JOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
               MessageManager.getString("label.new_sequence_url_link"),
               JOptionPane.OK_CANCEL_OPTION, -1, null) == JOptionPane.OK_OPTION)
       {
         if (link.checkValid())
         {
-          nameLinks.setElementAt(link.getName(), index);
-          urlLinks.setElementAt(link.getURL(), index);
-          updateLinkData();
+          linkUrlTable.setValueAt(link.getName(), index, 0);
+          linkUrlTable.setValueAt(link.getURL(), index, 1);
           valid = true;
         }
       }
-
       else
       {
         break;
@@ -817,26 +887,27 @@ public class Preferences extends GPreferences
   @Override
   public void deleteLink_actionPerformed(ActionEvent e)
   {
-    int index = linkNameList.getSelectedIndex();
+    int index = linkUrlTable.getSelectedRow();
+    int modelIndex = -1;
     if (index == -1)
     {
+      // no row is selected
       JOptionPane.showInternalMessageDialog(Desktop.desktop,
               MessageManager.getString("label.no_link_selected"),
               MessageManager.getString("label.no_link_selected"),
               JOptionPane.WARNING_MESSAGE);
       return;
     }
-    nameLinks.removeElementAt(index);
-    urlLinks.removeElementAt(index);
-    updateLinkData();
-  }
+    else
+    {
+      modelIndex = linkUrlTable.convertRowIndexToModel(index);
+    }
 
-  void updateLinkData()
-  {
-    linkNameList.setListData(nameLinks);
-    linkURLList.setListData(urlLinks);
+    // make sure we use the model index to delete, and not the table index
+    ((UrlLinkTableModel) linkUrlTable.getModel()).removeRow(modelIndex);
   }
 
+
   @Override
   public void defaultBrowser_mouseClicked(MouseEvent e)
   {
@@ -1053,4 +1124,46 @@ public class Preferences extends GPreferences
       return name.hashCode() + code.hashCode();
     }
   }
+  
+  private class UrlListSelectionHandler implements ListSelectionListener
+  {
+
+    @Override
+    public void valueChanged(ListSelectionEvent e)
+    {
+      ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+
+      int index = lsm.getMinSelectionIndex();
+      if (index == -1)
+      {
+        // no selection, so disable delete/edit buttons
+        editLink.setEnabled(false);
+        deleteLink.setEnabled(false);
+        return;
+      }
+      int modelIndex = linkUrlTable.convertRowIndexToModel(index);
+
+      // determine if the new selection is a custom url or not
+      if (!sequenceUrlLinks.isUserEntry((String) linkUrlTable
+.getModel()
+              .getValueAt(modelIndex, 4))) // KM TODO do this better
+      {
+        // entry is not a user-defined url and so should not be edited
+        // disable edit and delete buttons
+        deleteLink.setEnabled(false);
+        editLink.setEnabled(false);
+      }
+      else
+      {
+        deleteLink.setEnabled(true);
+        editLink.setEnabled(true);
+      }
+
+      // BUT it's the default url, don't allow deletion
+      if ((boolean) linkUrlTable.getValueAt(index, 3))
+      {
+        deleteLink.setEnabled(false);
+      }
+    }
+}
 }