JAL-2316 GUI updates to Connections tab in Preferences dialog
authorkiramt <k.mourao@dundee.ac.uk>
Fri, 2 Dec 2016 13:49:27 +0000 (13:49 +0000)
committerkiramt <k.mourao@dundee.ac.uk>
Fri, 2 Dec 2016 13:49:27 +0000 (13:49 +0000)
12 files changed:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/gui/Preferences.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GSequenceLink.java
src/jalview/urls/CustomUrlProvider.java
src/jalview/urls/IdentifiersUrlProvider.java
src/jalview/urls/UrlLinkDisplay.java [new file with mode: 0644]
src/jalview/urls/UrlLinkTableModel.java [new file with mode: 0644]
src/jalview/urls/UrlProvider.java
src/jalview/urls/UrlProviderI.java
src/jalview/urls/UrlProviderImpl.java

index 7582568..045f58c 100644 (file)
@@ -1273,4 +1273,6 @@ label.SEQUENCE_ID_for_DB_ACCESSION1 = Please review your URL links in the 'Conne
 label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'.
 label.do_not_display_again = Do not display this message again
 exception.url_cannot_have_miriam_id = {0} is a MIRIAM id and cannot be used as a custom url name
-exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one link
\ No newline at end of file
+exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one line
+label.filter = Filter text:
+action.customfilter = Custom only
\ No newline at end of file
index f3d782c..9922e46 100644 (file)
@@ -1274,4 +1274,6 @@ label.SEQUENCE_ID_for_DB_ACCESSION1 = Por favor, revise sus URLs en la pesta
 label.SEQUENCE_ID_for_DB_ACCESSION2 = URL enlaza usando '$SEQUENCE_ID$' para accesiones DB ahora usar '$DB_ACCESSION$'.
 label.do_not_display_again = No mostrar este mensaje de nuevo
 exception.url_cannot_have_miriam_id = {0} is a MIRIAM id and cannot be used as a custom url name
-exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one link
\ No newline at end of file
+exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one link
+label.filter = Filter text:
+action.customfilter = Custom only
\ No newline at end of file
index 56e076e..7a24aeb 100755 (executable)
@@ -29,6 +29,7 @@ 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;
@@ -37,6 +38,7 @@ 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;
@@ -45,7 +47,6 @@ import java.awt.event.MouseEvent;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 import javax.help.HelpSetException;
 import javax.swing.JColorChooser;
@@ -53,6 +54,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;
 
@@ -100,6 +113,8 @@ public class Preferences extends GPreferences
    */
   public static UrlProviderI sequenceUrlLinks;
 
+  public static UrlLinkTableModel dataModel;
+
   /**
    * Holds name and link separated with | character. Sequence IDS and Sequences
    * must be $SEQUENCEIDS$ or $SEQUENCEIDS=/.possible | chars ./=$ and
@@ -115,6 +130,14 @@ public class Preferences extends GPreferences
             UrlProviderI.DEFAULT_STRING);
     sequenceUrlLinks = new UrlProvider(UrlProviderI.DEFAULT_LABEL, string);
 
+    List<String> colNames = new ArrayList<String>();
+    // colNames.add("ID");
+    // TODO KM add to properties file
+    colNames.add("URL");
+    colNames.add("In Menu");
+    colNames.add("Default");
+    dataModel = new UrlLinkTableModel(sequenceUrlLinks, colNames, "ID");
+
     /**
      * TODO: reformulate groupURL encoding so two or more can be stored in the
      * .properties file as '|' separated strings
@@ -123,8 +146,6 @@ public class Preferences extends GPreferences
     groupURLLinks = new ArrayList<String>();
   }
 
-  Vector<String> nameLinks, urlLinks;
-
   JInternalFrame frame;
 
   DasSourceBrowser dasSource;
@@ -308,13 +329,121 @@ public class Preferences extends GPreferences
     /*
      * Set Connections tab defaults
      */
-    nameLinks = new Vector<String>();
-    urlLinks = new Vector<String>();
 
-    resetStoredLinks();
-    updateLinkData();
+    // 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()
+    {
+      @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);
+      }
+    };
+
+    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", ""));
@@ -509,7 +638,7 @@ public class Preferences extends GPreferences
     jalview.util.BrowserLauncher.resetBrowser();
 
     // save user-defined and selected links
-    String links = sequenceUrlLinks.writeUrlsAsString();
+    String links = linkUrlTable.getModel().toString();
     if (links.isEmpty())
     {
       Cache.applicationProperties.remove("SEQUENCE_LINKS");
@@ -710,16 +839,9 @@ public class Preferences extends GPreferences
       {
         if (link.checkValid())
         {
-          nameLinks.addElement(link.getName());
-          urlLinks.addElement(link.getURL());
-          if (updateLinkData())
-          {
-            valid = true;
-          }
-          else
-          {
-            break;
-          }
+          ((UrlLinkTableModel) linkUrlTable.getModel()).insertRow(
+                  link.getName(), link.getURL());
+          valid = true;
         }
       }
       else
@@ -734,9 +856,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"),
@@ -744,8 +867,8 @@ 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)
@@ -757,19 +880,12 @@ public class Preferences extends GPreferences
       {
         if (link.checkValid())
         {
-          nameLinks.setElementAt(link.getName(), index);
-          urlLinks.setElementAt(link.getURL(), index);
-          if (updateLinkData())
-          {
-            valid = true;
-          }
-          else
-          {
-            break;
-          }
+
+          linkUrlTable.setValueAt(link.getName(), index, 0);
+          linkUrlTable.setValueAt(link.getURL(), index, 1);
+          valid = true;
         }
       }
-
       else
       {
         break;
@@ -780,58 +896,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();
-  }
-
-  private boolean updateLinkData()
-  {
-    try
-    {
-      sequenceUrlLinks.setUrlLinks(nameLinks, urlLinks);
-      linkNameList.setListData(nameLinks);
-      linkURLList.setListData(urlLinks);
-    } catch (IllegalArgumentException e)
+    else
     {
-
-      // put back the old links
-      resetStoredLinks();
-
-      linkNameList.setListData(nameLinks);
-      linkURLList.setListData(urlLinks);
-
-      JOptionPane.showInternalMessageDialog(Desktop.desktop,
-              e.getMessage(), MessageManager.getString("label.link_name"),
-              JOptionPane.WARNING_MESSAGE);
-
-      return false;
+      modelIndex = linkUrlTable.convertRowIndexToModel(index);
     }
-    return true;
-  }
 
-  private void resetStoredLinks()
-  {
-    Vector<String> nlinks = sequenceUrlLinks.getLinksForDisplay();
-
-    nameLinks.clear();
-    urlLinks.clear();
-    for (String entry : nlinks)
-    {
-      nameLinks.addElement(entry.split("\\|")[0]);
-      urlLinks.addElement(entry.split("\\|")[1]);
-    }
+    // 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)
   {
@@ -1048,4 +1133,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);
+      }
+    }
+}
 }
index 90053f5..3fe1939 100755 (executable)
@@ -45,6 +45,7 @@ import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 
+import javax.swing.AbstractCellEditor;
 import javax.swing.BorderFactory;
 import javax.swing.ButtonGroup;
 import javax.swing.DefaultListCellRenderer;
@@ -53,11 +54,11 @@ import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
-import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
 import javax.swing.JTabbedPane;
+import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
 import javax.swing.SwingConstants;
@@ -67,8 +68,8 @@ import javax.swing.border.EtchedBorder;
 import javax.swing.border.TitledBorder;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
 
 /**
  * Base class for the Preferences panel.
@@ -180,7 +181,21 @@ public class GPreferences extends JPanel
   /*
    * Connections tab components
    */
-  protected JList linkURLList = new JList();
+  protected JTable linkUrlTable = new JTable();
+
+  protected JButton editLink = new JButton();
+
+  protected JButton deleteLink = new JButton();
+
+  protected JTextField filterTB = new JTextField();
+
+  protected JButton doReset = new JButton();
+
+  protected JButton userOnly = new JButton();
+
+  protected JLabel portLabel = new JLabel();
+
+  protected JLabel serverLabel = new JLabel();
 
   protected JTextField proxyServerTB = new JTextField();
 
@@ -188,8 +203,6 @@ public class GPreferences extends JPanel
 
   protected JTextField defaultBrowser = new JTextField();
 
-  protected JList linkNameList = new JList();
-
   protected JCheckBox useProxy = new JCheckBox();
 
   protected JCheckBox usagestats = new JCheckBox();
@@ -483,38 +496,255 @@ public class GPreferences extends JPanel
   {
     JPanel connectTab = new JPanel();
     connectTab.setLayout(new GridBagLayout());
-    JLabel serverLabel = new JLabel();
+
+    // Label for browser text box
+    JLabel browserLabel = new JLabel();
+    browserLabel.setFont(LABEL_FONT);
+    browserLabel.setHorizontalAlignment(SwingConstants.TRAILING);
+    browserLabel.setText(MessageManager
+            .getString("label.default_browser_unix"));
+    defaultBrowser.setFont(LABEL_FONT);
+    defaultBrowser.setText("");
+
+    defaultBrowser.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        if (e.getClickCount() > 1)
+        {
+          defaultBrowser_mouseClicked(e);
+        }
+      }
+    });
+
+    JPanel proxyPanel = initConnTabProxyPanel();
+    JPanel linkPanel = initConnTabUrlLinks();
+    initConnTabCheckboxes();
+
+    // Add URL link panel
+    connectTab.add(linkPanel, new GridBagConstraints(0, 0, 2, 1, 2.0, 2.0,
+            GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(
+                    16, 0, 0, 12), 359, 32));
+
+    // Add default Browser text box
+    connectTab.add(browserLabel, new GridBagConstraints(0, 1, 1, 1, 0.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+            new Insets(10, 0, 0, 0), 5, 1));
+
+    connectTab.add(defaultBrowser, new GridBagConstraints(1, 1, 1, 1, 1.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(10, 0, 0, 10), 307, 1));
+
+    // Add proxy server panel
+    connectTab.add(proxyPanel, new GridBagConstraints(0, 2, 2, 1, 1.0, 0.0,
+            GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+            new Insets(10, 0, 0, 12), 4, 10));
+
+    // Add usage stats, version check and questionnaire checkboxes
+    connectTab.add(usagestats, new GridBagConstraints(0, 3, 1, 1, 1.0, 0.0,
+            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 4, 2), 70, 1));
+    connectTab.add(questionnaire, new GridBagConstraints(1, 3, 1, 1, 1.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 4, 10), 70, 1));
+    connectTab.add(versioncheck, new GridBagConstraints(0, 4, 1, 1, 1.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 4, 2), 70, 1));
+    return connectTab;
+  }
+
+  /**
+   * Initialises the proxy server panel in the Connections tab
+   * 
+   * @return the proxy server panel
+   */
+  private JPanel initConnTabProxyPanel()
+  {
+    // Label for server text box
     serverLabel.setText(MessageManager.getString("label.address"));
     serverLabel.setHorizontalAlignment(SwingConstants.RIGHT);
     serverLabel.setFont(LABEL_FONT);
+
+    // Proxy server and port text boxes
     proxyServerTB.setFont(LABEL_FONT);
     proxyPortTB.setFont(LABEL_FONT);
-    JLabel portLabel = new JLabel();
+
+    // Label for Port text box
     portLabel.setFont(LABEL_FONT);
     portLabel.setHorizontalAlignment(SwingConstants.RIGHT);
     portLabel.setText(MessageManager.getString("label.port"));
-    JLabel browserLabel = new JLabel();
-    browserLabel.setFont(new java.awt.Font("SansSerif", 0, 11));
-    browserLabel.setHorizontalAlignment(SwingConstants.TRAILING);
-    browserLabel.setText(MessageManager
-            .getString("label.default_browser_unix"));
-    defaultBrowser.setFont(LABEL_FONT);
-    defaultBrowser.setText("");
+
+    // Use proxy server checkbox
+    useProxy.setFont(LABEL_FONT);
+    useProxy.setHorizontalAlignment(SwingConstants.RIGHT);
+    useProxy.setHorizontalTextPosition(SwingConstants.LEADING);
+    useProxy.setText(MessageManager.getString("label.use_proxy_server"));
+    useProxy.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        useProxy_actionPerformed();
+      }
+    });
+
+    // Make proxy server panel
+    JPanel proxyPanel = new JPanel();
+    TitledBorder titledBorder1 = new TitledBorder(
+            MessageManager.getString("label.proxy_server"));
+    proxyPanel.setBorder(titledBorder1);
+    proxyPanel.setLayout(new GridBagLayout());
+    proxyPanel.add(serverLabel, new GridBagConstraints(0, 1, 1, 1, 0.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+            new Insets(0, 2, 2, 0), 5, 0));
+    proxyPanel.add(portLabel, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
+            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
+                    0, 2, 0), 11, 0));
+    proxyPanel.add(useProxy, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,
+            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
+                    2, 5, 185), 2, -4));
+    proxyPanel.add(proxyPortTB, new GridBagConstraints(3, 1, 1, 1, 1.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 2, 2), 54, 1));
+    proxyPanel.add(proxyServerTB, new GridBagConstraints(1, 1, 1, 1, 1.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 2, 0), 263, 1));
+
+    return proxyPanel;
+  }
+
+  /**
+   * Initialises the checkboxes in the Connections tab
+   */
+  private void initConnTabCheckboxes()
+  {
+    // Usage stats checkbox label
     usagestats.setText(MessageManager
             .getString("label.send_usage_statistics"));
     usagestats.setFont(LABEL_FONT);
     usagestats.setHorizontalAlignment(SwingConstants.RIGHT);
     usagestats.setHorizontalTextPosition(SwingConstants.LEADING);
+
+    // Questionnaire checkbox label
     questionnaire.setText(MessageManager
             .getString("label.check_for_questionnaires"));
     questionnaire.setFont(LABEL_FONT);
     questionnaire.setHorizontalAlignment(SwingConstants.RIGHT);
     questionnaire.setHorizontalTextPosition(SwingConstants.LEADING);
+
+    // Check for latest version checkbox label
     versioncheck.setText(MessageManager
             .getString("label.check_for_latest_version"));
     versioncheck.setFont(LABEL_FONT);
     versioncheck.setHorizontalAlignment(SwingConstants.RIGHT);
     versioncheck.setHorizontalTextPosition(SwingConstants.LEADING);
+  }
+
+  /**
+   * Initialises the URL links panel in the Connection tab
+   * 
+   * @return the URL links panel
+   */
+  private JPanel initConnTabUrlLinks()
+  {
+    // Set up table for Url links
+    linkUrlTable.setFillsViewportHeight(true);
+    linkUrlTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+    linkUrlTable.setAutoCreateRowSorter(true);
+    linkUrlTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+    // Table in scrollpane so that the table is given a scrollbar
+    JScrollPane linkScrollPane = new JScrollPane(linkUrlTable);
+    linkScrollPane.setBorder(null);
+
+    // Panel for links functionality
+    JPanel linkPanel = new JPanel(new GridBagLayout());
+    linkPanel.setBorder(new TitledBorder(MessageManager
+            .getString("label.url_linkfrom_sequence_id")));
+
+    // Put the Url links panel together
+
+    // Buttons go at top right, resizing only resizes the blank space vertically
+    JPanel buttonPanel = initConnTabUrlButtons();
+    GridBagConstraints linkConstraints1 = new GridBagConstraints();
+    linkConstraints1.gridx = 1;
+    linkConstraints1.gridy = 0;
+    linkConstraints1.fill = GridBagConstraints.VERTICAL;
+    linkPanel.add(buttonPanel, linkConstraints1);
+
+    // Links table goes at top left, resizing resizes the table
+    GridBagConstraints linkConstraints2 = new GridBagConstraints();
+    linkConstraints2.gridx = 0;
+    linkConstraints2.gridy = 0;
+    linkConstraints2.weightx = 1.0;
+    linkConstraints2.weighty = 1.0;
+    linkConstraints2.fill = GridBagConstraints.BOTH;
+    linkPanel.add(linkScrollPane, linkConstraints2);
+
+    // Filter box and buttons goes at bottom left, resizing resizes the text box
+    JPanel filterPanel = initConnTabFilterPanel();
+    GridBagConstraints linkConstraints3 = new GridBagConstraints();
+    linkConstraints3.gridx = 0;
+    linkConstraints3.gridy = 1;
+    linkConstraints3.weightx = 1.0;
+    linkConstraints3.fill = GridBagConstraints.HORIZONTAL;
+    linkPanel.add(filterPanel, linkConstraints3);
+
+    return linkPanel;
+  }
+
+  private JPanel initConnTabFilterPanel()
+  {
+    // Filter textbox and reset button
+    JLabel filterLabel = new JLabel(
+            MessageManager.getString("label.filter"));
+    filterLabel.setFont(LABEL_FONT);
+    filterLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    filterLabel.setHorizontalTextPosition(SwingConstants.LEADING);
+
+    filterTB.setFont(LABEL_FONT);
+    filterTB.setText("");
+
+    doReset.setText(MessageManager.getString("action.reset"));
+    userOnly.setText(MessageManager.getString("action.customfilter"));
+
+    // Panel for filter functionality
+    JPanel filterPanel = new JPanel(new GridBagLayout());
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.gridx = 0;
+    gbc.gridy = 0;
+    gbc.fill = GridBagConstraints.NONE;
+    gbc.anchor = GridBagConstraints.WEST;
+
+    filterPanel.add(filterLabel, gbc);
+
+    GridBagConstraints gbc1 = new GridBagConstraints();
+    gbc1.gridx = 0;
+    gbc1.gridx = 1;
+    gbc1.fill = GridBagConstraints.HORIZONTAL;
+    gbc1.anchor = GridBagConstraints.WEST;
+    gbc1.weightx = 1.0;
+    filterPanel.add(filterTB, gbc1);
+
+    GridBagConstraints gbc2 = new GridBagConstraints();
+    gbc2.gridx = 2;
+    gbc2.fill = GridBagConstraints.NONE;
+    gbc2.anchor = GridBagConstraints.WEST;
+    filterPanel.add(doReset, gbc2);
+
+    GridBagConstraints gbc3 = new GridBagConstraints();
+    gbc3.gridx = 3;
+    gbc3.fill = GridBagConstraints.NONE;
+    gbc3.anchor = GridBagConstraints.WEST;
+    filterPanel.add(userOnly, gbc3);
+
+    return filterPanel;
+  }
+
+  private JPanel initConnTabUrlButtons()
+  {
+    // Buttons for new / edit / delete Url links
     JButton newLink = new JButton();
     newLink.setText(MessageManager.getString("action.new"));
     newLink.addActionListener(new java.awt.event.ActionListener()
@@ -525,7 +755,7 @@ public class GPreferences extends JPanel
         newLink_actionPerformed(e);
       }
     });
-    JButton editLink = new JButton();
+
     editLink.setText(MessageManager.getString("action.edit"));
     editLink.addActionListener(new java.awt.event.ActionListener()
     {
@@ -535,7 +765,7 @@ public class GPreferences extends JPanel
         editLink_actionPerformed(e);
       }
     });
-    JButton deleteLink = new JButton();
+
     deleteLink.setText(MessageManager.getString("action.delete"));
     deleteLink.addActionListener(new java.awt.event.ActionListener()
     {
@@ -546,117 +776,21 @@ public class GPreferences extends JPanel
       }
     });
 
-    linkURLList.addListSelectionListener(new ListSelectionListener()
-    {
-      @Override
-      public void valueChanged(ListSelectionEvent e)
-      {
-        int index = linkURLList.getSelectedIndex();
-        linkNameList.setSelectedIndex(index);
-      }
-    });
+    // no current selection, so initially disable delete/edit buttons
+    editLink.setEnabled(false);
+    deleteLink.setEnabled(false);
 
-    linkNameList.addListSelectionListener(new ListSelectionListener()
-    {
-      @Override
-      public void valueChanged(ListSelectionEvent e)
-      {
-        int index = linkNameList.getSelectedIndex();
-        linkURLList.setSelectedIndex(index);
-      }
-    });
+    // Panels for new/edit/delete link buttons
+    // buttonContent prevents the buttons from being resized when the window is
+    JPanel buttonContent = new JPanel(new GridLayout(0, 1, 0, 0));
+    JPanel buttonPanel = new JPanel(new BorderLayout());
 
-    JScrollPane linkScrollPane = new JScrollPane();
-    linkScrollPane.setBorder(null);
-    JPanel linkPanel = new JPanel();
-    linkPanel.setBorder(new TitledBorder(MessageManager
-            .getString("label.url_linkfrom_sequence_id")));
-    linkPanel.setLayout(new BorderLayout());
-    GridLayout gridLayout1 = new GridLayout();
-    JPanel editLinkButtons = new JPanel();
-    editLinkButtons.setLayout(gridLayout1);
-    gridLayout1.setRows(3);
-    linkNameList.setFont(LABEL_FONT);
-    linkNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-    BorderLayout borderLayout3 = new BorderLayout();
-    JPanel linkPanel2 = new JPanel();
-    linkPanel2.setLayout(borderLayout3);
-    linkURLList.setFont(LABEL_FONT);
-    linkURLList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+    buttonContent.add(newLink, null);
+    buttonContent.add(editLink, null);
+    buttonContent.add(deleteLink, null);
+    buttonPanel.add(buttonContent, BorderLayout.NORTH);
 
-    defaultBrowser.addMouseListener(new MouseAdapter()
-    {
-      @Override
-      public void mouseClicked(MouseEvent e)
-      {
-        if (e.getClickCount() > 1)
-        {
-          defaultBrowser_mouseClicked(e);
-        }
-      }
-    });
-    useProxy.setFont(LABEL_FONT);
-    useProxy.setHorizontalAlignment(SwingConstants.RIGHT);
-    useProxy.setHorizontalTextPosition(SwingConstants.LEADING);
-    useProxy.setText(MessageManager.getString("label.use_proxy_server"));
-    useProxy.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        useProxy_actionPerformed();
-      }
-    });
-    linkPanel.add(editLinkButtons, BorderLayout.EAST);
-    editLinkButtons.add(newLink, null);
-    editLinkButtons.add(editLink, null);
-    editLinkButtons.add(deleteLink, null);
-    linkPanel.add(linkScrollPane, BorderLayout.CENTER);
-    linkScrollPane.getViewport().add(linkPanel2, null);
-    linkPanel2.add(linkURLList, BorderLayout.CENTER);
-    linkPanel2.add(linkNameList, BorderLayout.WEST);
-    JPanel jPanel1 = new JPanel();
-    TitledBorder titledBorder1 = new TitledBorder(
-            MessageManager.getString("label.proxy_server"));
-    jPanel1.setBorder(titledBorder1);
-    jPanel1.setLayout(new GridBagLayout());
-    jPanel1.add(serverLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
-                    2, 4, 0), 5, 0));
-    jPanel1.add(portLabel, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
-                    0, 4, 0), 11, 6));
-    connectTab.add(linkPanel, new GridBagConstraints(0, 0, 2, 1, 1.0, 1.0,
-            GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
-                    16, 0, 0, 12), 359, -17));
-    connectTab.add(jPanel1, new GridBagConstraints(0, 2, 2, 1, 1.0, 1.0,
-            GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
-                    21, 0, 35, 12), 4, 6));
-    connectTab.add(browserLabel, new GridBagConstraints(0, 1, 1, 1, 0.0,
-            0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,
-            new Insets(16, 0, 0, 0), 5, 1));
-    jPanel1.add(useProxy, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
-                    2, 5, 185), 2, -4));
-    jPanel1.add(proxyPortTB, new GridBagConstraints(3, 1, 1, 1, 1.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 54, 1));
-    jPanel1.add(proxyServerTB, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 0), 263, 1));
-    connectTab.add(defaultBrowser, new GridBagConstraints(1, 1, 1, 1, 1.0,
-            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(15, 0, 0, 15), 307, 1));
-    connectTab.add(usagestats, new GridBagConstraints(0, 4, 1, 1, 1.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 70, 1));
-    connectTab.add(questionnaire, new GridBagConstraints(1, 4, 1, 1, 1.0,
-            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 70, 1));
-    connectTab.add(versioncheck, new GridBagConstraints(0, 5, 1, 1, 1.0,
-            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 70, 1));
-    return connectTab;
+    return buttonPanel;
   }
 
   /**
@@ -1357,8 +1491,78 @@ public class GPreferences extends JPanel
 
   public void useProxy_actionPerformed()
   {
-    proxyServerTB.setEnabled(useProxy.isSelected());
-    proxyPortTB.setEnabled(useProxy.isSelected());
+    boolean enabled = useProxy.isSelected();
+    portLabel.setEnabled(enabled);
+    serverLabel.setEnabled(enabled);
+    proxyServerTB.setEnabled(enabled);
+    proxyPortTB.setEnabled(enabled);
   }
 
+  public class RadioButtonRenderer extends JRadioButton implements
+          TableCellRenderer
+  {
+    public RadioButtonRenderer()
+    {
+      setHorizontalAlignment(CENTER);
+    }
+
+    @Override
+    public Component getTableCellRendererComponent(JTable table,
+            Object value, boolean isSelected, boolean hasFocus, int row,
+            int column)
+    {
+      setSelected((boolean) value);
+
+      // set colours to match rest of table
+      if (isSelected)
+      {
+        setBackground(table.getSelectionBackground());
+        setForeground(table.getSelectionForeground());
+      }
+      else
+      {
+        setBackground(table.getBackground());
+        setForeground(table.getForeground());
+      }
+
+      // adjust row height so radio buttons actually fit
+      table.setRowHeight(row, getMinimumSize().height);
+
+      return this;
+    }
+  }
+
+  public class RadioButtonEditor extends AbstractCellEditor implements
+          TableCellEditor
+  {
+    private JRadioButton button = new JRadioButton();
+
+    public RadioButtonEditor()
+    {
+      this.button.setHorizontalAlignment(SwingConstants.CENTER);
+      this.button.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          fireEditingStopped();
+        }
+      });
+    }
+
+    @Override
+    public Component getTableCellEditorComponent(JTable table,
+            Object value, boolean isSelected, int row, int column)
+    {
+      button.setSelected((boolean) value);
+      return button;
+    }
+
+    @Override
+    public Object getCellEditorValue()
+    {
+      return button.isSelected();
+    }
+
+  }
 }
index 2689946..f7914bf 100755 (executable)
@@ -28,7 +28,6 @@ import java.awt.Font;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
-import java.awt.Panel;
 import java.awt.Rectangle;
 import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
@@ -40,7 +39,7 @@ import javax.swing.JPanel;
 import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
-public class GSequenceLink extends Panel
+public class GSequenceLink extends JPanel
 {
   public GSequenceLink()
   {
index d88c571..fb0d676 100644 (file)
@@ -29,9 +29,12 @@ import static jalview.util.UrlConstants.SEQUENCE_ID;
 import jalview.util.MessageManager;
 import jalview.util.UrlLink;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
@@ -73,19 +76,24 @@ public class CustomUrlProvider extends UrlProviderImpl
       while (st.hasMoreElements())
       {
         String name = st.nextToken();
-        String url = st.nextToken();
-        // check for '|' within a regex
-        int rxstart = url.indexOf(DELIM + DB_ACCESSION + DELIM);
-        if (rxstart == -1)
-        {
-          rxstart = url.indexOf(DELIM + SEQUENCE_ID + DELIM);
-        }
-        while (rxstart == -1 && url.indexOf("/=" + DELIM) == -1
-                && st.hasMoreTokens())
+
+        if (!isMiriamId(name))
         {
-          url = url + SEP + st.nextToken();
+          // this one of our custom urls
+          String url = st.nextToken();
+          // check for '|' within a regex
+          int rxstart = url.indexOf(DELIM + DB_ACCESSION + DELIM);
+          if (rxstart == -1)
+          {
+            rxstart = url.indexOf(DELIM + SEQUENCE_ID + DELIM);
+          }
+          while (rxstart == -1 && url.indexOf("/=" + DELIM) == -1
+                  && st.hasMoreTokens())
+          {
+            url = url + SEP + st.nextToken();
+          }
+          urls.put(name, new UrlLink(name + SEP + url));
         }
-        urls.put(name, new UrlLink(name + SEP + url));
       }
     } catch (Exception ex)
     {
@@ -148,6 +156,27 @@ public class CustomUrlProvider extends UrlProviderImpl
   }
 
   @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<UrlLinkDisplay>();
+    for (Entry<String, UrlLink> entry : urls.entrySet())
+    {
+      String key = entry.getKey();
+      boolean isDefault = (key.equals(defaultUrl));
+      boolean isSelected = true; // custom urls always selected
+      String displayLink = entry.getValue().toString().split("\\|")[1]; // TODO
+                                                                        // sort
+                                                                        // this
+                                                                        // out
+                                                                        // properly!
+      displayLinks.add(new UrlLinkDisplay(key, key, displayLink,
+              isSelected,
+              isDefault));
+    }
+    return displayLinks;
+  }
+
+  @Override
   public boolean setDefaultUrl(String id)
   {
     if (urls.containsKey(id))
@@ -248,6 +277,45 @@ public class CustomUrlProvider extends UrlProviderImpl
   }
 
   @Override
+  public void setUrlData(List<UrlLinkDisplay> links)
+  {
+    HashMap<String, UrlLink> newurls = new HashMap<String, UrlLink>();
+
+    Iterator<UrlLinkDisplay> it = links.iterator();
+    while (it.hasNext())
+    {
+      UrlLinkDisplay link = it.next();
+
+      // MIRIAM ids will be handled by a different UrlProvider class
+      if (!isMiriamId(link.getId()))
+      {
+
+        // don't allow duplicate key names as entries will be overwritten
+        if (newurls.containsKey(link.getId()))
+        {
+          throw new IllegalArgumentException(MessageManager.formatMessage(
+                  "exception.url_cannot_have_duplicate_id", link.getId()));
+        }
+        if (link.getIsSelected())
+        {
+          newurls.put(link.getId(),
+                  new UrlLink(link.getId() + SEP + link.getUrl()));
+
+          // sort out default and selected ids
+          if (link.getIsDefault())
+          {
+            setDefaultUrl(link.getId());
+          }
+        }
+          // TODO KM make it possible to set and save selected custom urls
+
+      }
+
+    }
+    urls = newurls;
+  }
+
+  @Override
   public String chooseDefaultUrl()
   {
     // unilaterally set the default id to the EMBL_EBI link
index 7819136..a74ad07 100644 (file)
@@ -35,6 +35,9 @@ import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
 import java.util.Vector;
 
 import org.json.simple.JSONArray;
@@ -65,11 +68,13 @@ public class IdentifiersUrlProvider extends UrlProviderImpl
     {
       // File idFile = getIdentifiers();
       urls = readIdentifiers(new FileReader(idFileName));
+      selectedUrls = new ArrayList<String>();
       checkSelectionMatchesUrls(cachedUrlList);
 
     } catch (IOException e)
     {
-
+      System.out.println("Exception reading URLs from identifiers.org");
+      System.out.println(e.getMessage());
     }
   }
 
@@ -129,7 +134,7 @@ public class IdentifiersUrlProvider extends UrlProviderImpl
 
   private void checkSelectionMatchesUrls(String cachedUrlList)
   {
-    selectedUrls = new ArrayList<String>();
+
     String[] prevSelected = cachedUrlList.split("\\" + SEP);
     for (String id : prevSelected)
     {
@@ -145,7 +150,6 @@ public class IdentifiersUrlProvider extends UrlProviderImpl
 
   private void checkSelectionMatchesUrls(Vector<String> idList)
   {
-    selectedUrls = new ArrayList<String>();
     String[] prevSelected = new String[idList.size()];
     idList.toArray(prevSelected);
     for (String id : prevSelected)
@@ -183,12 +187,19 @@ public class IdentifiersUrlProvider extends UrlProviderImpl
   @Override
   public String writeUrlsAsString()
   {
-    StringBuffer links = new StringBuffer();
-    for (String k : selectedUrls)
+    if (!selectedUrls.isEmpty())
     {
-      links.append(k);
+      StringBuffer links = new StringBuffer();
+      for (String k : selectedUrls)
+      {
+        links.append(k);
+        links.append(SEP);
+      }
+      // remove last SEP
+      links.setLength(links.length() - 1);
+      return links.toString();
     }
-    return links.toString();
+    return "";
   }
 
   @Override
@@ -197,13 +208,63 @@ public class IdentifiersUrlProvider extends UrlProviderImpl
     Vector<String> links = new Vector<String>();
     for (String key : selectedUrls)
     {
-      links.add(key + SEP + urls.get(key).get("url") + "/" + DELIM
+      links.add(urls.get(key).get("name") + SEP + urls.get(key).get("url")
+              + "/" + DELIM
               + DB_ACCESSION + DELIM);
     }
     return links;
   }
 
   @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<UrlLinkDisplay>();
+    for (Entry<String, HashMap<String, String>> entry : urls.entrySet())
+    {
+      String key = entry.getKey();
+      boolean isDefault = (key == defaultUrl);
+      boolean isSelected = (selectedUrls.contains(key));
+      displayLinks.add(new UrlLinkDisplay(key,
+              entry.getValue().get("name"),
+              entry.getValue().get("url")
+              + "/" + DELIM + DB_ACCESSION + DELIM, isSelected, isDefault));
+    }
+    return displayLinks;
+  }
+
+  @Override
+  public void setUrlData(List<UrlLinkDisplay> links)
+  {
+    selectedUrls = new ArrayList<String>();
+
+    Iterator<UrlLinkDisplay> it = links.iterator();
+    while (it.hasNext())
+    {
+      UrlLinkDisplay link = it.next();
+
+      // Handle links with MIRIAM ids only
+      if (isMiriamId(link.getId())) // TODO separate use of name and id
+      {
+        // select/deselect links accordingly and set default url
+
+        if (link.getIsSelected())
+        {
+          if (urls.containsKey(link.getId()))
+          {
+            selectedUrls.add(link.getId());
+          }
+          if (link.getIsDefault())
+          {
+            setDefaultUrl(link.getId());
+          }
+
+        }
+      }
+
+    }
+  }
+
+  @Override
   public String getDefaultUrl(String seqid)
   {
     return urls.get(defaultUrl).get("url") + "/" + seqid;
diff --git a/src/jalview/urls/UrlLinkDisplay.java b/src/jalview/urls/UrlLinkDisplay.java
new file mode 100644 (file)
index 0000000..9afe6d3
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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.urls;
+
+
+/**
+ * UrlLink table row definition
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
+public class UrlLinkDisplay
+{
+  private String id; // id is not supplied to display, but used to identify
+                     // entries when saved
+
+  private String name;
+
+  private String url;
+
+  private boolean isDefault;
+
+  private boolean isSelected;
+
+  public final static int ID = 4;
+
+  public final static int URL = 1;
+
+  public final static int SELECTED = 2;
+
+  public final static int DEFAULT = 3;
+
+  public final static int NAME = 0;
+
+  public UrlLinkDisplay(String rowId, String rowName, String rowUrl,
+          boolean rowSelected, boolean rowDefault)
+  {
+    id = rowId;
+    name = rowName;
+    url = rowUrl;
+    isDefault = rowDefault;
+    isSelected = rowSelected;
+  }
+
+  public String getId()
+  {
+    return id;
+  }
+
+  public String getName()
+  {
+    return name;
+  }
+
+  public String getUrl()
+  {
+    return url;
+  }
+
+  public boolean getIsDefault()
+  {
+    return isDefault;
+  }
+
+  public boolean getIsSelected()
+  {
+    return isSelected;
+  }
+
+  public void setUrl(String rowUrl)
+  {
+    url = rowUrl;
+  }
+
+  public void setIsDefault(boolean rowDefault)
+  {
+    isDefault = rowDefault;
+  }
+
+  public void setIsSelected(boolean rowSelected)
+  {
+    isSelected = rowSelected;
+  }
+
+  public Object getValue(int index)
+  {
+    switch (index)
+    {
+    case ID:
+      return id;
+    case URL:
+      return url;
+    case DEFAULT:
+      return isDefault;
+    case SELECTED:
+      return isSelected;
+    case NAME:
+      return name;
+    default:
+      return null; // TODO
+    }
+  }
+
+  public void setValue(int index, Object value)
+  {
+    switch (index)
+    {
+    case ID:
+      id = (String) value;
+      break;
+    case URL:
+      url = (String) value;
+      break;
+    case DEFAULT:
+      isDefault = (boolean) value;
+      break;
+    case SELECTED:
+      isSelected = (boolean) value;
+      break;
+    case NAME:
+      name = (String) value;
+      break;
+    default:
+      // TODO
+    }
+  }
+
+  public boolean isEditable(int index)
+  {
+    return ((index == DEFAULT) || (index == SELECTED));
+  }
+}
diff --git a/src/jalview/urls/UrlLinkTableModel.java b/src/jalview/urls/UrlLinkTableModel.java
new file mode 100644 (file)
index 0000000..103454b
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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.urls;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+/**
+ * TableModel for UrlLinks table
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
+public class UrlLinkTableModel extends AbstractTableModel
+{
+  // local storage of data
+  // use LinkedHashMap to guarantee ordering remains the same, as
+  // we need to maintain a row/col mapping into the HashMap
+  private List<UrlLinkDisplay> data;
+
+  private UrlProviderI dataProvider;
+
+  private List<String> displayColumns;
+
+  // row in table which is currently the default
+  private int defaultRow;
+
+  /**
+   * UrlLinkTableModel constructor
+   * 
+   * @param baseData
+   *          base data set to be presented in table
+   * @param entryNames
+   *          keys of entries in baseData's nested hashmap. Should match order
+   *          in displayColNames
+   * @param displayColNames
+   *          names of columns to display in order.
+   * @param keyColName
+   *          name of column corresponding to keys in baseData
+   */
+  public UrlLinkTableModel(UrlProviderI baseData,
+          List<String> displayColNames,
+          String keyColName)
+  {
+    dataProvider = baseData;
+    data = baseData.getLinksForTable();
+    displayColumns = new ArrayList<String>();
+    displayColumns.add(keyColName);
+    displayColumns.addAll(displayColNames);
+
+    // find the default row
+    defaultRow = 0;
+    Iterator<UrlLinkDisplay> it = data.iterator();
+    while (it.hasNext())
+    {
+      if (it.next().getIsDefault())
+      {
+        break;
+      }
+      else
+      {
+        defaultRow++;
+      }
+    }
+  }
+
+  @Override
+  public int getRowCount()
+  {
+    if (data == null)
+    {
+      return 0;
+    }
+    else
+    {
+      return data.size();
+    }
+  }
+
+  @Override
+  public int getColumnCount()
+  {
+    return displayColumns.size();
+  }
+
+  @Override
+  public Object getValueAt(int rowIndex, int columnIndex)
+  {
+    return data.get(rowIndex).getValue(columnIndex);
+  }
+
+  @Override
+  public boolean isCellEditable(int rowIndex, int columnIndex)
+  {
+    return data.get(rowIndex).isEditable(columnIndex);
+  }
+
+  @Override
+  public void setValueAt(Object aValue, int rowIndex, int columnIndex)
+  {
+    if ((columnIndex == UrlLinkDisplay.SELECTED)
+            && (rowIndex == defaultRow))
+    {
+      // Selected urls column: can't deselect default URL
+      // refuse to edit: TODO show message box here
+
+    }
+    else if (columnIndex == UrlLinkDisplay.DEFAULT)
+    {
+      // Default url column: exactly one row must always be true
+      if (rowIndex != defaultRow)
+      {
+        // selected row is not currently the default
+        // set the current default to false
+        data.get(defaultRow).setValue(columnIndex, false);
+        fireTableRowsUpdated(defaultRow, defaultRow);
+
+        // set the default to be the selected row
+        defaultRow = rowIndex;
+        data.get(rowIndex).setValue(columnIndex, aValue);
+
+        // default row must also be selected
+        if (!data.get(rowIndex).getIsSelected())
+        {
+          data.get(rowIndex).setValue(UrlLinkDisplay.SELECTED, true);
+        }
+        fireTableRowsUpdated(rowIndex, rowIndex);
+      }
+    }
+    else
+    {
+      data.get(rowIndex).setValue(columnIndex, aValue);
+      fireTableRowsUpdated(rowIndex, rowIndex);
+    }
+  }
+
+  @Override
+  public Class<?> getColumnClass(int columnIndex)
+  {
+    return getValueAt(0, columnIndex).getClass();
+  }
+
+  @Override
+  public String getColumnName(int columnIndex)
+  {
+    return displayColumns.get(columnIndex);
+  }
+
+  @Override
+  public String toString()
+  {
+    // update the UrlProvider from data list
+    dataProvider.setUrlData(data);
+
+    return dataProvider.writeUrlsAsString();
+  }
+
+  public void removeRow(int rowIndex)
+  {
+    // remove the row from data
+    data.remove(rowIndex);
+
+    // update default row
+    if (defaultRow > rowIndex)
+    {
+      defaultRow--;
+    }
+
+    fireTableRowsDeleted(rowIndex, rowIndex);
+  }
+
+  public int insertRow(String name, String url)
+  {
+    // add a row to the data
+    UrlLinkDisplay u = new UrlLinkDisplay(name, name, url, true, false);
+    int index = data.size();
+    data.add(u);
+    fireTableRowsInserted(index, index);
+    return index;
+  }
+}
index af618f0..37e95e2 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.urls;
 
+import static jalview.util.UrlConstants.SEP;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -57,7 +59,7 @@ public class UrlProvider implements UrlProviderI
     UrlProviderI idProvider = new IdentifiersUrlProvider(cachedUrlList,
             ID_ORG_FILE);
     customProvider = new CustomUrlProvider(cachedUrlList);
-    // providers.add(idProvider);
+    providers.add(idProvider);
     providers.add(customProvider);
 
     // check that the defaultUrl still exists
@@ -134,7 +136,10 @@ public class UrlProvider implements UrlProviderI
     for (UrlProviderI p : providers)
     {
       result += p.writeUrlsAsString();
+      result += SEP;
     }
+    // remove last sep
+    result = result.substring(0, result.length() - 1);
     return result;
   }
 
@@ -156,6 +161,26 @@ public class UrlProvider implements UrlProviderI
   }
 
   @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<UrlLinkDisplay>();
+    for (UrlProviderI p : providers)
+    {
+      displayLinks.addAll(p.getLinksForTable());
+    }
+    return displayLinks;
+  }
+
+  @Override
+  public void setUrlData(List<UrlLinkDisplay> links)
+  {
+    for (UrlProviderI p : providers)
+    {
+      p.setUrlData(links);
+    }
+  }
+
+  @Override
   public String getDefaultUrl(String seqid)
   {
     String link = null;
@@ -207,4 +232,9 @@ public class UrlProvider implements UrlProviderI
     return customProvider.chooseDefaultUrl();
   }
 
+  @Override
+  public boolean isUserEntry(String id)
+  {
+    return customProvider.isUserEntry(id);
+  }
 }
index 4ade3bb..3841a50 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.urls;
 
 import java.io.File;
+import java.util.List;
 import java.util.Vector;
 
 /**
@@ -56,6 +57,17 @@ public interface UrlProviderI
   Vector<String> getLinksForDisplay();
 
   /**
+   * Get names and urls as strings for display
+   * 
+   */
+  List<UrlLinkDisplay> getLinksForTable();
+
+  /**
+   * Set names and urls from display settings
+   */
+  void setUrlData(List<UrlLinkDisplay> links);
+
+  /**
    * Get the id of the default URL
    * 
    * @return id of the default URL
@@ -110,4 +122,9 @@ public interface UrlProviderI
    * @return id of chosen default url
    */
   String chooseDefaultUrl();
+
+  /**
+   * Determine if id is for a user-defined URL
+   */
+  boolean isUserEntry(String id);
 }
index 554145d..4511919 100644 (file)
  */
 package jalview.urls;
 
+import java.util.List;
 import java.util.Vector;
 import java.util.regex.Pattern;
 
+/**
+ * Leaf node of UrlProvider composite
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
 public class UrlProviderImpl implements UrlProviderI
 {
   private static final Pattern MIRIAM_PATTERN = Pattern
@@ -84,11 +92,27 @@ public class UrlProviderImpl implements UrlProviderI
     return null;
   }
 
+  @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    return null;
+  }
+
+  @Override
+  public void setUrlData(List<UrlLinkDisplay> links)
+  {
+    // deliberately left empty
+  }
+
   protected boolean isMiriamId(String id)
   {
     return MIRIAM_PATTERN.matcher(id).matches();
   }
 
-
+  @Override
+  public boolean isUserEntry(String id)
+  {
+    return !isMiriamId(id);
+  }
 
 }